From 8ab97a89ce5af4a50995d2d3078dc957331983a7 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Fri, 12 Jan 2018 15:12:04 -0800 Subject: [PATCH 001/749] Remove skip for testModifiers001 Bug: 66906055 Test: ./art/tools/run-libjdwp-tests.sh --mode=host Change-Id: I07bc6a1cbbe2d858488e07da8c66e399a26be79d --- tools/external_oj_libjdwp_art_failures.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tools/external_oj_libjdwp_art_failures.txt b/tools/external_oj_libjdwp_art_failures.txt index c96830a592..c6f1422e70 100644 --- a/tools/external_oj_libjdwp_art_failures.txt +++ b/tools/external_oj_libjdwp_art_failures.txt @@ -12,12 +12,6 @@ bug: 66906414, name: "org.apache.harmony.jpda.tests.jdwp.ThreadReference.ThreadGroup002Test#testThreadGroup002" }, -{ - description: "Test fails due to modifiers not including ACC_SUPER", - result: EXEC_FAILED, - bug: 66906055, - name: "org.apache.harmony.jpda.tests.jdwp.ReferenceType.ModifiersTest#testModifiers001" -}, { description: "Test fails due to static values not being set correctly.", result: EXEC_FAILED, -- GitLab From a98a28262f645d100e2dee9587e7822d35ade6f9 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 24 May 2017 16:14:10 -0700 Subject: [PATCH 002/749] Do fewer GCs shortly after zygote fork After zygote fork, increase heap limit (max_allowed_footprint_) to maximum growth limit and concurrent GC threshold (concurrent_start_bytes_) to half of heap limit for 2s. This means there will be, most likely, no GCs that happen during launch for most apps. This should reduce startup time of apps, as well as, save some power. After the 2s windows is done, a concurrent GC is done to free up RAM and adjust the counters back to normal, if no GC took place so far. Not measured: Boot time, transient RAM usage increase. ----------------------------------------------------- App | Avg speed | Heap size | GC count ----------------------------------------------------- Camera | 567ms / 588ms | 4.3MB / 2.7MB | 2 / 4 ----------------------------------------------------- Chrome | 350ms / 394ms | 2.5MB / 1.5MB | 0 / 2 ----------------------------------------------------- Photos | 447ms / 516ms | 6MB / 4MB | 0 / 3 ----------------------------------------------------- Maps |1419ms / 1440ms| 19.5MB / 11MB | 0 / 5 ----------------------------------------------------- Gmail | 148ms / 156ms | 3.5MB / 2.5MB | 0 / 1 ----------------------------------------------------- Youtube | 721ms / 761ms | 8MB / 4.5MB | 0 / 3 ----------------------------------------------------- Notes: 1) Format: 2) For Camera app, 2 GCs are caused by native allocations 3) Speed is averaged over 100 runs 4) Heap size is at end of the launch Bug: 36727951 Test: test-art-host Change-Id: I4ca9b5be7433097851560f8738fbc8cae733d85e --- runtime/gc/heap.cc | 37 +++++++++++++++++++++ runtime/gc/heap.h | 3 ++ runtime/native/dalvik_system_ZygoteHooks.cc | 2 ++ 3 files changed, 42 insertions(+) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index b1932d1a29..9c21cba071 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -143,6 +143,10 @@ static constexpr bool kLogAllGCs = false; static constexpr size_t kPartialTlabSize = 16 * KB; static constexpr bool kUsePartialTlabs = true; +// Use Max heap for 2 seconds, this is smaller than the usual 5s window since we don't want to leave +// allocate with relaxed ergonomics for that long. +static constexpr size_t kPostForkMaxHeapDurationMS = 2000; + #if defined(__LP64__) || !defined(ADDRESS_SANITIZER) // 300 MB (0x12c00000) - (default non-moving space capacity). static uint8_t* const kPreferredAllocSpaceBegin = @@ -3523,6 +3527,12 @@ void Heap::ClampGrowthLimit() { } void Heap::ClearGrowthLimit() { + if (max_allowed_footprint_ == growth_limit_ && growth_limit_ < capacity_) { + max_allowed_footprint_ = capacity_; + concurrent_start_bytes_ = + std::max(max_allowed_footprint_, kMinConcurrentRemainingBytes) - + kMinConcurrentRemainingBytes; + } growth_limit_ = capacity_; ScopedObjectAccess soa(Thread::Current()); for (const auto& space : continuous_spaces_) { @@ -4094,5 +4104,32 @@ void Heap::VlogHeapGrowth(size_t max_allowed_footprint, size_t new_footprint, si << PrettySize(new_footprint) << " for a " << PrettySize(alloc_size) << " allocation"; } +class Heap::TriggerPostForkCCGcTask : public HeapTask { + public: + explicit TriggerPostForkCCGcTask(uint64_t target_time) : HeapTask(target_time) {} + void Run(Thread* self) OVERRIDE { + gc::Heap* heap = Runtime::Current()->GetHeap(); + // Trigger a GC, if not already done. The first GC after fork, whenever + // takes place, will adjust the thresholds to normal levels. + if (heap->max_allowed_footprint_ == heap->growth_limit_) { + heap->RequestConcurrentGC(self, kGcCauseBackground, false); + } + } +}; + +void Heap::PostForkChildAction(Thread* self) { + // Temporarily increase max_allowed_footprint_ and concurrent_start_bytes_ to + // max values to avoid GC during app launch. + if (collector_type_ == kCollectorTypeCC && !IsLowMemoryMode()) { + // Set max_allowed_footprint_ to the largest allowed value. + SetIdealFootprint(growth_limit_); + // Set concurrent_start_bytes_ to half of the heap size. + concurrent_start_bytes_ = std::max(max_allowed_footprint_ / 2, GetBytesAllocated()); + + GetTaskProcessor()->AddTask( + self, new TriggerPostForkCCGcTask(NanoTime() + MsToNs(kPostForkMaxHeapDurationMS))); + } +} + } // namespace gc } // namespace art diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index faa6195259..d4daee8cb4 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -832,10 +832,13 @@ class Heap { const Verification* GetVerification() const; + void PostForkChildAction(Thread* self); + private: class ConcurrentGCTask; class CollectorTransitionTask; class HeapTrimTask; + class TriggerPostForkCCGcTask; // Compact source space to target space. Returns the collector used. collector::GarbageCollector* Compact(space::ContinuousMemMapAllocSpace* target_space, diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index 779a6a6450..ef8cece5ae 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -297,6 +297,8 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, LOG(ERROR) << StringPrintf("Unknown bits set in runtime_flags: %#x", runtime_flags); } + Runtime::Current()->GetHeap()->PostForkChildAction(thread); + // Update tracing. if (Trace::GetMethodTracingMode() != TracingMode::kTracingInactive) { Trace::TraceOutputMode output_mode = Trace::GetOutputMode(); -- GitLab From 1d894d90634b3d685a5a1143dd84f0272b4a7dcd Mon Sep 17 00:00:00 2001 From: Yi Kong Date: Fri, 19 Jan 2018 22:01:14 -0800 Subject: [PATCH 003/749] Enable ThinLTO for dex2oat This helps reducing binary size as well as improve performance of dex2oat. On Marlin (lower clocked cores) With PGO dex2oat binary + libraries size: - no-lto: 9.90MB - thin-lto: 9.81MB dex2oat run time for top 25 apps (with speed filter): Average case: -2.83% Best case : -4.65% Worst case : 0.25%, likely noise Without PGO dex2oat binary + libraries size: - no-lto: 9.67MB - thin-lto: 9.60MB dex2oat run time for top 25 apps (with speed filter): Average case : -0.96% Best case : -2.30% Worst case : 0.52%, likely noise Test: Benchmark on Marlin (low-clock cores) Bug: 62839002 Change-Id: Iad39eae4c222b753dd2dab949a928151ea13e630 --- compiler/Android.bp | 9 ++++++++- dex2oat/Android.bp | 9 ++++++++- dexlayout/Android.bp | 9 ++++++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/compiler/Android.bp b/compiler/Android.bp index ba08d7975b..23d5c14678 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -256,7 +256,14 @@ art_cc_library { instrumentation: true, profile_file: "art/dex2oat.profdata", benchmarks: ["dex2oat"], - } + }, + target: { + android: { + lto: { + thin: true, + }, + }, + }, } art_cc_library { diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index dd16ba4909..d933e0561a 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -139,7 +139,14 @@ art_cc_binary { "-Wno-frame-larger-than=", "-DART_PGO_INSTRUMENTATION", ], - } + }, + target: { + android: { + lto: { + thin: true, + }, + }, + }, } art_cc_binary { diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp index bea61d0c71..24be25bd26 100644 --- a/dexlayout/Android.bp +++ b/dexlayout/Android.bp @@ -44,7 +44,14 @@ art_cc_library { instrumentation: true, profile_file: "art/dex2oat.profdata", benchmarks: ["dex2oat"], - } + }, + target: { + android: { + lto: { + thin: true, + }, + }, + }, } art_cc_library { -- GitLab From 3dad341ed027b760d9b4ee402cb2c93ac484a07a Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Wed, 28 Feb 2018 12:01:46 -0800 Subject: [PATCH 004/749] Introduce ABS as HIR nodes. NOTE: step 1 of 2 for "Introduce MIN/MAX/ABS as HIR nodes." Rationale: Having explicit MIN/MAX/ABS operations (in contrast with intrinsics) simplifies recognition and optimization of these common operations (e.g. constant folding, hoisting, detection of saturation arithmetic). Furthermore, mapping conditionals, selectors, intrinsics, etc. (some still TBD) onto these operations generalizes the way they are optimized downstream substantially. Bug: b/65164101 Test: test-art-host,target Change-Id: I9c93987197216158ba02c8aca2385086adedabc4 --- compiler/optimizing/code_generator_arm64.cc | 40 +++++ .../optimizing/code_generator_arm_vixl.cc | 55 +++++++ compiler/optimizing/code_generator_mips.cc | 111 +++++++++++++ compiler/optimizing/code_generator_mips.h | 2 + compiler/optimizing/code_generator_mips64.cc | 54 ++++++ compiler/optimizing/code_generator_x86.cc | 90 ++++++++++ compiler/optimizing/code_generator_x86_64.cc | 64 ++++++++ compiler/optimizing/induction_var_range.cc | 11 +- compiler/optimizing/instruction_simplifier.cc | 52 +++--- compiler/optimizing/intrinsics.h | 4 + compiler/optimizing/intrinsics_arm64.cc | 69 +------- compiler/optimizing/intrinsics_arm_vixl.cc | 77 --------- compiler/optimizing/intrinsics_mips.cc | 116 ------------- compiler/optimizing/intrinsics_mips64.cc | 69 -------- compiler/optimizing/intrinsics_x86.cc | 154 ------------------ compiler/optimizing/intrinsics_x86_64.cc | 96 ----------- compiler/optimizing/nodes.h | 42 +++++ compiler/optimizing/pc_relative_fixups_x86.cc | 2 + compiler/optimizing/scheduler.cc | 3 +- test/441-checker-inliner/src/Main.java | 8 +- test/445-checker-licm/src/Main.java | 10 +- test/449-checker-bce/src/Main.java | 6 +- test/455-checker-gvn/src/Main.java | 12 +- test/645-checker-abs-simd/src/Main.java | 148 ++++++++--------- test/660-checker-sad-byte/src/Main.java | 16 +- test/660-checker-sad-char/src/Main.java | 16 +- test/660-checker-sad-int/src/Main.java | 16 +- test/660-checker-sad-long/src/Main.java | 8 +- test/660-checker-sad-short/src/Main.java | 16 +- test/660-checker-simd-sad-byte/src/Main.java | 10 +- test/660-checker-simd-sad-char/src/Main.java | 10 +- test/660-checker-simd-sad-int/src/Main.java | 8 +- test/660-checker-simd-sad-long/src/Main.java | 6 +- test/660-checker-simd-sad-short/src/Main.java | 10 +- .../660-checker-simd-sad-short2/src/Main.java | 26 +-- .../660-checker-simd-sad-short3/src/Main.java | 16 +- 36 files changed, 675 insertions(+), 778 deletions(-) diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 60f8f98757..1f9c5546e2 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -5462,6 +5462,46 @@ void InstructionCodeGeneratorARM64::VisitRem(HRem* rem) { } } +void LocationsBuilderARM64::VisitAbs(HAbs* abs) { + LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); + break; + default: + LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType(); + } +} + +void InstructionCodeGeneratorARM64::VisitAbs(HAbs* abs) { + switch (abs->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: { + Register in_reg = InputRegisterAt(abs, 0); + Register out_reg = OutputRegister(abs); + __ Cmp(in_reg, Operand(0)); + __ Cneg(out_reg, in_reg, lt); + break; + } + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: { + FPRegister in_reg = InputFPRegisterAt(abs, 0); + FPRegister out_reg = OutputFPRegister(abs); + __ Fabs(out_reg, in_reg); + break; + } + default: + LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType(); + } +} + void LocationsBuilderARM64::VisitConstructorFence(HConstructorFence* constructor_fence) { constructor_fence->SetLocations(nullptr); } diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 2f495fc15f..13518adf7d 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -4690,6 +4690,61 @@ void InstructionCodeGeneratorARMVIXL::VisitRem(HRem* rem) { } } +void LocationsBuilderARMVIXL::VisitAbs(HAbs* abs) { + LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + locations->AddTemp(Location::RequiresRegister()); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); + break; + default: + LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType(); + } +} + +void InstructionCodeGeneratorARMVIXL::VisitAbs(HAbs* abs) { + LocationSummary* locations = abs->GetLocations(); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: { + vixl32::Register in_reg = RegisterFrom(locations->InAt(0)); + vixl32::Register out_reg = RegisterFrom(locations->Out()); + vixl32::Register mask = RegisterFrom(locations->GetTemp(0)); + __ Asr(mask, in_reg, 31); + __ Add(out_reg, in_reg, mask); + __ Eor(out_reg, out_reg, mask); + break; + } + case DataType::Type::kInt64: { + Location in = locations->InAt(0); + vixl32::Register in_reg_lo = LowRegisterFrom(in); + vixl32::Register in_reg_hi = HighRegisterFrom(in); + Location output = locations->Out(); + vixl32::Register out_reg_lo = LowRegisterFrom(output); + vixl32::Register out_reg_hi = HighRegisterFrom(output); + DCHECK(!out_reg_lo.Is(in_reg_hi)) << "Diagonal overlap unexpected."; + vixl32::Register mask = RegisterFrom(locations->GetTemp(0)); + __ Asr(mask, in_reg_hi, 31); + __ Adds(out_reg_lo, in_reg_lo, mask); + __ Adc(out_reg_hi, in_reg_hi, mask); + __ Eor(out_reg_lo, out_reg_lo, mask); + __ Eor(out_reg_hi, out_reg_hi, mask); + break; + } + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + __ Vabs(OutputVRegister(abs), InputVRegisterAt(abs, 0)); + break; + default: + LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType(); + } +} void LocationsBuilderARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) { LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction); diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index d01b895446..1c24918698 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -8780,6 +8780,117 @@ void InstructionCodeGeneratorMIPS::VisitRem(HRem* instruction) { } } +void LocationsBuilderMIPS::VisitAbs(HAbs* abs) { + LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); + break; + default: + LOG(FATAL) << "Unexpected abs type " << abs->GetResultType(); + } +} + +void InstructionCodeGeneratorMIPS::GenerateAbsFP(LocationSummary* locations, + DataType::Type type, + bool isR2OrNewer, + bool isR6) { + FRegister in = locations->InAt(0).AsFpuRegister(); + FRegister out = locations->Out().AsFpuRegister(); + + // Note, as a "quality of implementation", rather than pure "spec compliance", we require that + // Math.abs() clears the sign bit (but changes nothing else) for all numbers, including NaN + // (signaling NaN may become quiet though). + // + // The ABS.fmt instructions (abs.s and abs.d) do exactly that when NAN2008=1 (R6). For this case, + // both regular floating point numbers and NAN values are treated alike, only the sign bit is + // affected by this instruction. + // But when NAN2008=0 (R2 and before), the ABS.fmt instructions can't be used. For this case, any + // NaN operand signals invalid operation. This means that other bits (not just sign bit) might be + // changed when doing abs(NaN). Because of that, we clear sign bit in a different way. + if (isR6) { + if (type == DataType::Type::kFloat64) { + __ AbsD(out, in); + } else { + DCHECK_EQ(type, DataType::Type::kFloat32); + __ AbsS(out, in); + } + } else { + if (type == DataType::Type::kFloat64) { + if (in != out) { + __ MovD(out, in); + } + __ MoveFromFpuHigh(TMP, in); + // ins instruction is not available for R1. + if (isR2OrNewer) { + __ Ins(TMP, ZERO, 31, 1); + } else { + __ Sll(TMP, TMP, 1); + __ Srl(TMP, TMP, 1); + } + __ MoveToFpuHigh(TMP, out); + } else { + DCHECK_EQ(type, DataType::Type::kFloat32); + __ Mfc1(TMP, in); + // ins instruction is not available for R1. + if (isR2OrNewer) { + __ Ins(TMP, ZERO, 31, 1); + } else { + __ Sll(TMP, TMP, 1); + __ Srl(TMP, TMP, 1); + } + __ Mtc1(TMP, out); + } + } +} + +void InstructionCodeGeneratorMIPS::VisitAbs(HAbs* abs) { + LocationSummary* locations = abs->GetLocations(); + bool isR2OrNewer = codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2(); + bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: { + Register in = locations->InAt(0).AsRegister(); + Register out = locations->Out().AsRegister(); + __ Sra(AT, in, 31); + __ Xor(out, in, AT); + __ Subu(out, out, AT); + break; + } + case DataType::Type::kInt64: { + Register in_lo = locations->InAt(0).AsRegisterPairLow(); + Register in_hi = locations->InAt(0).AsRegisterPairHigh(); + Register out_lo = locations->Out().AsRegisterPairLow(); + Register out_hi = locations->Out().AsRegisterPairHigh(); + // The comments in this section show the analogous operations which would + // be performed if we had 64-bit registers "in", and "out". + // __ Dsra32(AT, in, 31); + __ Sra(AT, in_hi, 31); + // __ Xor(out, in, AT); + __ Xor(TMP, in_lo, AT); + __ Xor(out_hi, in_hi, AT); + // __ Dsubu(out, out, AT); + __ Subu(out_lo, TMP, AT); + __ Sltu(TMP, out_lo, TMP); + __ Addu(out_hi, out_hi, TMP); + break; + } + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + GenerateAbsFP(locations, abs->GetResultType(), isR2OrNewer, isR6); + break; + default: + LOG(FATAL) << "Unexpected abs type " << abs->GetResultType(); + } +} + void LocationsBuilderMIPS::VisitConstructorFence(HConstructorFence* constructor_fence) { constructor_fence->SetLocations(nullptr); } diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index d87cfc0786..d09ab7cce0 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -246,6 +246,8 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator { bool value_can_be_null); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info, uint32_t dex_pc); + void GenerateAbsFP(LocationSummary* locations, DataType::Type type, bool isR2OrNewer, bool isR6); + // Generate a heap reference load using one register `out`: // // out <- *(out + offset) diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index e3529f178a..a45c68e0ac 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -6665,6 +6665,60 @@ void InstructionCodeGeneratorMIPS64::VisitRem(HRem* instruction) { } } +void LocationsBuilderMIPS64::VisitAbs(HAbs* abs) { + LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); + break; + default: + LOG(FATAL) << "Unexpected abs type " << abs->GetResultType(); + } +} + +void InstructionCodeGeneratorMIPS64::VisitAbs(HAbs* abs) { + LocationSummary* locations = abs->GetLocations(); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: { + GpuRegister in = locations->InAt(0).AsRegister(); + GpuRegister out = locations->Out().AsRegister(); + __ Sra(AT, in, 31); + __ Xor(out, in, AT); + __ Subu(out, out, AT); + break; + } + case DataType::Type::kInt64: { + GpuRegister in = locations->InAt(0).AsRegister(); + GpuRegister out = locations->Out().AsRegister(); + __ Dsra32(AT, in, 31); + __ Xor(out, in, AT); + __ Dsubu(out, out, AT); + break; + } + case DataType::Type::kFloat32: { + FpuRegister in = locations->InAt(0).AsFpuRegister(); + FpuRegister out = locations->Out().AsFpuRegister(); + __ AbsS(out, in); + break; + } + case DataType::Type::kFloat64: { + FpuRegister in = locations->InAt(0).AsFpuRegister(); + FpuRegister out = locations->Out().AsFpuRegister(); + __ AbsD(out, in); + break; + } + default: + LOG(FATAL) << "Unexpected abs type " << abs->GetResultType(); + } +} + void LocationsBuilderMIPS64::VisitConstructorFence(HConstructorFence* constructor_fence) { constructor_fence->SetLocations(nullptr); } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 6bf045885d..51b96be0f8 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -3802,6 +3802,96 @@ void InstructionCodeGeneratorX86::VisitRem(HRem* rem) { } } +void LocationsBuilderX86::VisitAbs(HAbs* abs) { + LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: + locations->SetInAt(0, Location::RegisterLocation(EAX)); + locations->SetOut(Location::SameAsFirstInput()); + locations->AddTemp(Location::RegisterLocation(EDX)); + break; + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); + locations->AddTemp(Location::RequiresRegister()); + break; + case DataType::Type::kFloat32: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + locations->AddTemp(Location::RequiresFpuRegister()); + locations->AddTemp(Location::RequiresRegister()); + break; + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + locations->AddTemp(Location::RequiresFpuRegister()); + break; + default: + LOG(FATAL) << "Unexpected type for HAbs " << abs->GetResultType(); + } +} + +void InstructionCodeGeneratorX86::VisitAbs(HAbs* abs) { + LocationSummary* locations = abs->GetLocations(); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: { + Register out = locations->Out().AsRegister(); + DCHECK_EQ(out, EAX); + Register temp = locations->GetTemp(0).AsRegister(); + DCHECK_EQ(temp, EDX); + // Sign extend EAX into EDX. + __ cdq(); + // XOR EAX with sign. + __ xorl(EAX, EDX); + // Subtract out sign to correct. + __ subl(EAX, EDX); + // The result is in EAX. + break; + } + case DataType::Type::kInt64: { + Location input = locations->InAt(0); + Register input_lo = input.AsRegisterPairLow(); + Register input_hi = input.AsRegisterPairHigh(); + Location output = locations->Out(); + Register output_lo = output.AsRegisterPairLow(); + Register output_hi = output.AsRegisterPairHigh(); + Register temp = locations->GetTemp(0).AsRegister(); + // Compute the sign into the temporary. + __ movl(temp, input_hi); + __ sarl(temp, Immediate(31)); + // Store the sign into the output. + __ movl(output_lo, temp); + __ movl(output_hi, temp); + // XOR the input to the output. + __ xorl(output_lo, input_lo); + __ xorl(output_hi, input_hi); + // Subtract the sign. + __ subl(output_lo, temp); + __ sbbl(output_hi, temp); + break; + } + case DataType::Type::kFloat32: { + XmmRegister out = locations->Out().AsFpuRegister(); + XmmRegister temp = locations->GetTemp(0).AsFpuRegister(); + Register constant = locations->GetTemp(1).AsRegister(); + __ movl(constant, Immediate(INT32_C(0x7FFFFFFF))); + __ movd(temp, constant); + __ andps(out, temp); + break; + } + case DataType::Type::kFloat64: { + XmmRegister out = locations->Out().AsFpuRegister(); + XmmRegister temp = locations->GetTemp(0).AsFpuRegister(); + // TODO: Use a constant from the constant table (requires extra input). + __ LoadLongConstant(temp, INT64_C(0x7FFFFFFFFFFFFFFF)); + __ andpd(out, temp); + break; + } + default: + LOG(FATAL) << "Unexpected type for HAbs " << abs->GetResultType(); + } +} + void LocationsBuilderX86::VisitDivZeroCheck(HDivZeroCheck* instruction) { LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction); switch (instruction->GetType()) { diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 7be360536b..0bb56a2b4a 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -3821,6 +3821,70 @@ void InstructionCodeGeneratorX86_64::VisitRem(HRem* rem) { } } +void LocationsBuilderX86_64::VisitAbs(HAbs* abs) { + LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + locations->AddTemp(Location::RequiresRegister()); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + locations->AddTemp(Location::RequiresFpuRegister()); + break; + default: + LOG(FATAL) << "Unexpected type for HAbs " << abs->GetResultType(); + } +} + +void InstructionCodeGeneratorX86_64::VisitAbs(HAbs* abs) { + LocationSummary* locations = abs->GetLocations(); + switch (abs->GetResultType()) { + case DataType::Type::kInt32: { + CpuRegister out = locations->Out().AsRegister(); + CpuRegister mask = locations->GetTemp(0).AsRegister(); + // Create mask. + __ movl(mask, out); + __ sarl(mask, Immediate(31)); + // Add mask. + __ addl(out, mask); + __ xorl(out, mask); + break; + } + case DataType::Type::kInt64: { + CpuRegister out = locations->Out().AsRegister(); + CpuRegister mask = locations->GetTemp(0).AsRegister(); + // Create mask. + __ movq(mask, out); + __ sarq(mask, Immediate(63)); + // Add mask. + __ addq(out, mask); + __ xorq(out, mask); + break; + } + case DataType::Type::kFloat32: { + XmmRegister out = locations->Out().AsFpuRegister(); + XmmRegister mask = locations->GetTemp(0).AsFpuRegister(); + __ movss(mask, codegen_->LiteralInt32Address(INT32_C(0x7FFFFFFF))); + __ andps(out, mask); + break; + } + case DataType::Type::kFloat64: { + XmmRegister out = locations->Out().AsFpuRegister(); + XmmRegister mask = locations->GetTemp(0).AsFpuRegister(); + __ movsd(mask, codegen_->LiteralInt64Address(INT64_C(0x7FFFFFFFFFFFFFFF))); + __ andpd(out, mask); + break; + } + default: + LOG(FATAL) << "Unexpected type for HAbs " << abs->GetResultType(); + } +} + void LocationsBuilderX86_64::VisitDivZeroCheck(HDivZeroCheck* instruction) { LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction); locations->SetInAt(0, Location::Any()); diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index 99dec11240..d699d01ecb 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -85,15 +85,14 @@ static bool IsGEZero(HInstruction* instruction) { // Instruction MIN(>=0, >=0) is >= 0. return IsGEZero(instruction->InputAt(0)) && IsGEZero(instruction->InputAt(1)); - case Intrinsics::kMathAbsInt: - case Intrinsics::kMathAbsLong: - // Instruction ABS(>=0) is >= 0. - // NOTE: ABS(minint) = minint prevents assuming - // >= 0 without looking at the argument. - return IsGEZero(instruction->InputAt(0)); default: break; } + } else if (instruction->IsAbs()) { + // Instruction ABS(>=0) is >= 0. + // NOTE: ABS(minint) = minint prevents assuming + // >= 0 without looking at the argument. + return IsGEZero(instruction->InputAt(0)); } int64_t value = -1; return IsInt64AndGet(instruction, &value) && value >= 0; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index a42a85dc1d..eac8d2f593 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -120,6 +120,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void SimplifyReturnThis(HInvoke* invoke); void SimplifyAllocationIntrinsic(HInvoke* invoke); void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind); + void SimplifyAbs(HInvoke* invoke, DataType::Type type); CodeGenerator* codegen_; CompilerDriver* compiler_driver_; @@ -850,34 +851,10 @@ static HInstruction* NewIntegralAbs(ArenaAllocator* allocator, HInstruction* x, HInstruction* cursor) { DataType::Type type = x->GetType(); - DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64); - // Construct a fake intrinsic with as much context as is needed to allocate one. - // The intrinsic will always be lowered into code later anyway. - // TODO: b/65164101 : moving towards a real HAbs node makes more sense. - HInvokeStaticOrDirect::DispatchInfo dispatch_info = { - HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress, - HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod, - 0u - }; - HInvokeStaticOrDirect* invoke = new (allocator) HInvokeStaticOrDirect( - allocator, - 1, - type, - x->GetDexPc(), - /*method_idx*/ -1, - /*resolved_method*/ nullptr, - dispatch_info, - kStatic, - MethodReference(nullptr, dex::kDexNoIndex), - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - invoke->SetArgumentAt(0, x); - invoke->SetIntrinsic(type == DataType::Type::kInt32 ? Intrinsics::kMathAbsInt - : Intrinsics::kMathAbsLong, - kNoEnvironmentOrCache, - kNoSideEffects, - kNoThrow); - cursor->GetBlock()->InsertInstructionBefore(invoke, cursor); - return invoke; + DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64); + HAbs* abs = new (allocator) HAbs(type, x, x->GetDexPc()); + cursor->GetBlock()->InsertInstructionBefore(abs, cursor); + return abs; } // Returns true if operands a and b consists of widening type conversions @@ -2430,6 +2407,13 @@ void InstructionSimplifierVisitor::SimplifyMemBarrier(HInvoke* invoke, invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, mem_barrier); } +void InstructionSimplifierVisitor::SimplifyAbs(HInvoke* invoke, DataType::Type type) { + DCHECK(invoke->IsInvokeStaticOrDirect()); + HAbs* abs = new (GetGraph()->GetAllocator()) + HAbs(type, invoke->InputAt(0), invoke->GetDexPc()); + invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, abs); +} + void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { switch (instruction->GetIntrinsic()) { case Intrinsics::kStringEquals: @@ -2513,6 +2497,18 @@ void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { case Intrinsics::kVarHandleStoreStoreFence: SimplifyMemBarrier(instruction, MemBarrierKind::kStoreStore); break; + case Intrinsics::kMathAbsInt: + SimplifyAbs(instruction, DataType::Type::kInt32); + break; + case Intrinsics::kMathAbsLong: + SimplifyAbs(instruction, DataType::Type::kInt64); + break; + case Intrinsics::kMathAbsFloat: + SimplifyAbs(instruction, DataType::Type::kFloat32); + break; + case Intrinsics::kMathAbsDouble: + SimplifyAbs(instruction, DataType::Type::kFloat64); + break; default: break; } diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index 62991435c7..24aff226ca 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -266,6 +266,10 @@ void IntrinsicCodeGenerator ## Arch::Visit ## Name(HInvoke* invoke) { \ << " should have been converted to HIR"; \ } #define UNREACHABLE_INTRINSICS(Arch) \ +UNREACHABLE_INTRINSIC(Arch, MathAbsInt) \ +UNREACHABLE_INTRINSIC(Arch, MathAbsLong) \ +UNREACHABLE_INTRINSIC(Arch, MathAbsFloat) \ +UNREACHABLE_INTRINSIC(Arch, MathAbsDouble) \ UNREACHABLE_INTRINSIC(Arch, FloatFloatToIntBits) \ UNREACHABLE_INTRINSIC(Arch, DoubleDoubleToLongBits) \ UNREACHABLE_INTRINSIC(Arch, FloatIsNaN) \ diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 2f8e33f941..0b04fff927 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -529,68 +529,6 @@ void IntrinsicCodeGeneratorARM64::VisitLongLowestOneBit(HInvoke* invoke) { GenLowestOneBit(invoke, DataType::Type::kInt64, GetVIXLAssembler()); } -static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); -} - -static void MathAbsFP(LocationSummary* locations, bool is64bit, MacroAssembler* masm) { - Location in = locations->InAt(0); - Location out = locations->Out(); - - FPRegister in_reg = is64bit ? DRegisterFrom(in) : SRegisterFrom(in); - FPRegister out_reg = is64bit ? DRegisterFrom(out) : SRegisterFrom(out); - - __ Fabs(out_reg, in_reg); -} - -void IntrinsicLocationsBuilderARM64::VisitMathAbsDouble(HInvoke* invoke) { - CreateFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathAbsDouble(HInvoke* invoke) { - MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler()); -} - -void IntrinsicLocationsBuilderARM64::VisitMathAbsFloat(HInvoke* invoke) { - CreateFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathAbsFloat(HInvoke* invoke) { - MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler()); -} - -static void GenAbsInteger(LocationSummary* locations, - bool is64bit, - MacroAssembler* masm) { - Location in = locations->InAt(0); - Location output = locations->Out(); - - Register in_reg = is64bit ? XRegisterFrom(in) : WRegisterFrom(in); - Register out_reg = is64bit ? XRegisterFrom(output) : WRegisterFrom(output); - - __ Cmp(in_reg, Operand(0)); - __ Cneg(out_reg, in_reg, lt); -} - -void IntrinsicLocationsBuilderARM64::VisitMathAbsInt(HInvoke* invoke) { - CreateIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathAbsInt(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler()); -} - -void IntrinsicLocationsBuilderARM64::VisitMathAbsLong(HInvoke* invoke) { - CreateIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathAbsLong(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler()); -} - static void GenMinMaxFP(LocationSummary* locations, bool is_min, bool is_double, @@ -698,6 +636,13 @@ void IntrinsicCodeGeneratorARM64::VisitMathMaxLongLong(HInvoke* invoke) { GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetVIXLAssembler()); } +static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { + LocationSummary* locations = + new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); +} + void IntrinsicLocationsBuilderARM64::VisitMathSqrt(HInvoke* invoke) { CreateFPToFPLocations(allocator_, invoke); } diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index 830d0403e4..e351fcc706 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -432,83 +432,6 @@ void IntrinsicCodeGeneratorARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* invo GenNumberOfTrailingZeros(invoke, DataType::Type::kInt64, codegen_); } -static void MathAbsFP(HInvoke* invoke, ArmVIXLAssembler* assembler) { - __ Vabs(OutputVRegister(invoke), InputVRegisterAt(invoke, 0)); -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathAbsDouble(HInvoke* invoke) { - CreateFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathAbsDouble(HInvoke* invoke) { - MathAbsFP(invoke, GetAssembler()); -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathAbsFloat(HInvoke* invoke) { - CreateFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathAbsFloat(HInvoke* invoke) { - MathAbsFP(invoke, GetAssembler()); -} - -static void CreateIntToIntPlusTemp(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); - - locations->AddTemp(Location::RequiresRegister()); -} - -static void GenAbsInteger(LocationSummary* locations, - bool is64bit, - ArmVIXLAssembler* assembler) { - Location in = locations->InAt(0); - Location output = locations->Out(); - - vixl32::Register mask = RegisterFrom(locations->GetTemp(0)); - - if (is64bit) { - vixl32::Register in_reg_lo = LowRegisterFrom(in); - vixl32::Register in_reg_hi = HighRegisterFrom(in); - vixl32::Register out_reg_lo = LowRegisterFrom(output); - vixl32::Register out_reg_hi = HighRegisterFrom(output); - - DCHECK(!out_reg_lo.Is(in_reg_hi)) << "Diagonal overlap unexpected."; - - __ Asr(mask, in_reg_hi, 31); - __ Adds(out_reg_lo, in_reg_lo, mask); - __ Adc(out_reg_hi, in_reg_hi, mask); - __ Eor(out_reg_lo, mask, out_reg_lo); - __ Eor(out_reg_hi, mask, out_reg_hi); - } else { - vixl32::Register in_reg = RegisterFrom(in); - vixl32::Register out_reg = RegisterFrom(output); - - __ Asr(mask, in_reg, 31); - __ Add(out_reg, in_reg, mask); - __ Eor(out_reg, mask, out_reg); - } -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathAbsInt(HInvoke* invoke) { - CreateIntToIntPlusTemp(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathAbsInt(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetAssembler()); -} - - -void IntrinsicLocationsBuilderARMVIXL::VisitMathAbsLong(HInvoke* invoke) { - CreateIntToIntPlusTemp(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathAbsLong(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); -} - static void GenMinMaxFloat(HInvoke* invoke, bool is_min, CodeGeneratorARMVIXL* codegen) { ArmVIXLAssembler* assembler = codegen->GetAssembler(); Location op1_loc = invoke->GetLocations()->InAt(0); diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index cafa5228d9..6d6ff7576b 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -742,122 +742,6 @@ void IntrinsicCodeGeneratorMIPS::VisitLongBitCount(HInvoke* invoke) { GenBitCount(invoke->GetLocations(), DataType::Type::kInt64, IsR6(), GetAssembler()); } -static void MathAbsFP(LocationSummary* locations, - bool is64bit, - bool isR2OrNewer, - bool isR6, - MipsAssembler* assembler) { - FRegister in = locations->InAt(0).AsFpuRegister(); - FRegister out = locations->Out().AsFpuRegister(); - - // Note, as a "quality of implementation", rather than pure "spec compliance", we require that - // Math.abs() clears the sign bit (but changes nothing else) for all numbers, including NaN - // (signaling NaN may become quiet though). - // - // The ABS.fmt instructions (abs.s and abs.d) do exactly that when NAN2008=1 (R6). For this case, - // both regular floating point numbers and NAN values are treated alike, only the sign bit is - // affected by this instruction. - // But when NAN2008=0 (R2 and before), the ABS.fmt instructions can't be used. For this case, any - // NaN operand signals invalid operation. This means that other bits (not just sign bit) might be - // changed when doing abs(NaN). Because of that, we clear sign bit in a different way. - if (isR6) { - if (is64bit) { - __ AbsD(out, in); - } else { - __ AbsS(out, in); - } - } else { - if (is64bit) { - if (in != out) { - __ MovD(out, in); - } - __ MoveFromFpuHigh(TMP, in); - // ins instruction is not available for R1. - if (isR2OrNewer) { - __ Ins(TMP, ZERO, 31, 1); - } else { - __ Sll(TMP, TMP, 1); - __ Srl(TMP, TMP, 1); - } - __ MoveToFpuHigh(TMP, out); - } else { - __ Mfc1(TMP, in); - // ins instruction is not available for R1. - if (isR2OrNewer) { - __ Ins(TMP, ZERO, 31, 1); - } else { - __ Sll(TMP, TMP, 1); - __ Srl(TMP, TMP, 1); - } - __ Mtc1(TMP, out); - } - } -} - -// double java.lang.Math.abs(double) -void IntrinsicLocationsBuilderMIPS::VisitMathAbsDouble(HInvoke* invoke) { - CreateFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathAbsDouble(HInvoke* invoke) { - MathAbsFP(invoke->GetLocations(), /* is64bit */ true, IsR2OrNewer(), IsR6(), GetAssembler()); -} - -// float java.lang.Math.abs(float) -void IntrinsicLocationsBuilderMIPS::VisitMathAbsFloat(HInvoke* invoke) { - CreateFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathAbsFloat(HInvoke* invoke) { - MathAbsFP(invoke->GetLocations(), /* is64bit */ false, IsR2OrNewer(), IsR6(), GetAssembler()); -} - -static void GenAbsInteger(LocationSummary* locations, bool is64bit, MipsAssembler* assembler) { - if (is64bit) { - Register in_lo = locations->InAt(0).AsRegisterPairLow(); - Register in_hi = locations->InAt(0).AsRegisterPairHigh(); - Register out_lo = locations->Out().AsRegisterPairLow(); - Register out_hi = locations->Out().AsRegisterPairHigh(); - - // The comments in this section show the analogous operations which would - // be performed if we had 64-bit registers "in", and "out". - // __ Dsra32(AT, in, 31); - __ Sra(AT, in_hi, 31); - // __ Xor(out, in, AT); - __ Xor(TMP, in_lo, AT); - __ Xor(out_hi, in_hi, AT); - // __ Dsubu(out, out, AT); - __ Subu(out_lo, TMP, AT); - __ Sltu(TMP, out_lo, TMP); - __ Addu(out_hi, out_hi, TMP); - } else { - Register in = locations->InAt(0).AsRegister(); - Register out = locations->Out().AsRegister(); - - __ Sra(AT, in, 31); - __ Xor(out, in, AT); - __ Subu(out, out, AT); - } -} - -// int java.lang.Math.abs(int) -void IntrinsicLocationsBuilderMIPS::VisitMathAbsInt(HInvoke* invoke) { - CreateIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathAbsInt(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetAssembler()); -} - -// long java.lang.Math.abs(long) -void IntrinsicLocationsBuilderMIPS::VisitMathAbsLong(HInvoke* invoke) { - CreateIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathAbsLong(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); -} - static void GenMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type, diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 89f1818be2..5debd26c5a 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -470,75 +470,6 @@ void IntrinsicCodeGeneratorMIPS64::VisitLongBitCount(HInvoke* invoke) { GenBitCount(invoke->GetLocations(), DataType::Type::kInt64, GetAssembler()); } -static void MathAbsFP(LocationSummary* locations, bool is64bit, Mips64Assembler* assembler) { - FpuRegister in = locations->InAt(0).AsFpuRegister(); - FpuRegister out = locations->Out().AsFpuRegister(); - - if (is64bit) { - __ AbsD(out, in); - } else { - __ AbsS(out, in); - } -} - -// double java.lang.Math.abs(double) -void IntrinsicLocationsBuilderMIPS64::VisitMathAbsDouble(HInvoke* invoke) { - CreateFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathAbsDouble(HInvoke* invoke) { - MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); -} - -// float java.lang.Math.abs(float) -void IntrinsicLocationsBuilderMIPS64::VisitMathAbsFloat(HInvoke* invoke) { - CreateFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathAbsFloat(HInvoke* invoke) { - MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler()); -} - -static void CreateIntToInt(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - -static void GenAbsInteger(LocationSummary* locations, bool is64bit, Mips64Assembler* assembler) { - GpuRegister in = locations->InAt(0).AsRegister(); - GpuRegister out = locations->Out().AsRegister(); - - if (is64bit) { - __ Dsra32(AT, in, 31); - __ Xor(out, in, AT); - __ Dsubu(out, out, AT); - } else { - __ Sra(AT, in, 31); - __ Xor(out, in, AT); - __ Subu(out, out, AT); - } -} - -// int java.lang.Math.abs(int) -void IntrinsicLocationsBuilderMIPS64::VisitMathAbsInt(HInvoke* invoke) { - CreateIntToInt(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathAbsInt(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetAssembler()); -} - -// long java.lang.Math.abs(long) -void IntrinsicLocationsBuilderMIPS64::VisitMathAbsLong(HInvoke* invoke) { - CreateIntToInt(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathAbsLong(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); -} - static void GenMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type, diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 46b7f3f1ce..0edc061e97 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -333,160 +333,6 @@ void IntrinsicCodeGeneratorX86::VisitShortReverseBytes(HInvoke* invoke) { GenReverseBytes(invoke->GetLocations(), DataType::Type::kInt16, GetAssembler()); } - -// TODO: Consider Quick's way of doing Double abs through integer operations, as the immediate we -// need is 64b. - -static void CreateFloatToFloat(ArenaAllocator* allocator, HInvoke* invoke) { - // TODO: Enable memory operations when the assembler supports them. - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetOut(Location::SameAsFirstInput()); - HInvokeStaticOrDirect* static_or_direct = invoke->AsInvokeStaticOrDirect(); - DCHECK(static_or_direct != nullptr); - if (static_or_direct->HasSpecialInput() && - invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) { - // We need addressibility for the constant area. - locations->SetInAt(1, Location::RequiresRegister()); - // We need a temporary to hold the constant. - locations->AddTemp(Location::RequiresFpuRegister()); - } -} - -static void MathAbsFP(HInvoke* invoke, - bool is64bit, - X86Assembler* assembler, - CodeGeneratorX86* codegen) { - LocationSummary* locations = invoke->GetLocations(); - Location output = locations->Out(); - - DCHECK(output.IsFpuRegister()); - if (locations->GetInputCount() == 2 && locations->InAt(1).IsValid()) { - HX86ComputeBaseMethodAddress* method_address = - invoke->InputAt(1)->AsX86ComputeBaseMethodAddress(); - DCHECK(locations->InAt(1).IsRegister()); - // We also have a constant area pointer. - Register constant_area = locations->InAt(1).AsRegister(); - XmmRegister temp = locations->GetTemp(0).AsFpuRegister(); - if (is64bit) { - __ movsd(temp, codegen->LiteralInt64Address( - INT64_C(0x7FFFFFFFFFFFFFFF), method_address, constant_area)); - __ andpd(output.AsFpuRegister(), temp); - } else { - __ movss(temp, codegen->LiteralInt32Address( - INT32_C(0x7FFFFFFF), method_address, constant_area)); - __ andps(output.AsFpuRegister(), temp); - } - } else { - // Create the right constant on an aligned stack. - if (is64bit) { - __ subl(ESP, Immediate(8)); - __ pushl(Immediate(0x7FFFFFFF)); - __ pushl(Immediate(0xFFFFFFFF)); - __ andpd(output.AsFpuRegister(), Address(ESP, 0)); - } else { - __ subl(ESP, Immediate(12)); - __ pushl(Immediate(0x7FFFFFFF)); - __ andps(output.AsFpuRegister(), Address(ESP, 0)); - } - __ addl(ESP, Immediate(16)); - } -} - -void IntrinsicLocationsBuilderX86::VisitMathAbsDouble(HInvoke* invoke) { - CreateFloatToFloat(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathAbsDouble(HInvoke* invoke) { - MathAbsFP(invoke, /* is64bit */ true, GetAssembler(), codegen_); -} - -void IntrinsicLocationsBuilderX86::VisitMathAbsFloat(HInvoke* invoke) { - CreateFloatToFloat(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathAbsFloat(HInvoke* invoke) { - MathAbsFP(invoke, /* is64bit */ false, GetAssembler(), codegen_); -} - -static void CreateAbsIntLocation(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RegisterLocation(EAX)); - locations->SetOut(Location::SameAsFirstInput()); - locations->AddTemp(Location::RegisterLocation(EDX)); -} - -static void GenAbsInteger(LocationSummary* locations, X86Assembler* assembler) { - Location output = locations->Out(); - Register out = output.AsRegister(); - DCHECK_EQ(out, EAX); - Register temp = locations->GetTemp(0).AsRegister(); - DCHECK_EQ(temp, EDX); - - // Sign extend EAX into EDX. - __ cdq(); - - // XOR EAX with sign. - __ xorl(EAX, EDX); - - // Subtract out sign to correct. - __ subl(EAX, EDX); - - // The result is in EAX. -} - -static void CreateAbsLongLocation(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); - locations->AddTemp(Location::RequiresRegister()); -} - -static void GenAbsLong(LocationSummary* locations, X86Assembler* assembler) { - Location input = locations->InAt(0); - Register input_lo = input.AsRegisterPairLow(); - Register input_hi = input.AsRegisterPairHigh(); - Location output = locations->Out(); - Register output_lo = output.AsRegisterPairLow(); - Register output_hi = output.AsRegisterPairHigh(); - Register temp = locations->GetTemp(0).AsRegister(); - - // Compute the sign into the temporary. - __ movl(temp, input_hi); - __ sarl(temp, Immediate(31)); - - // Store the sign into the output. - __ movl(output_lo, temp); - __ movl(output_hi, temp); - - // XOR the input to the output. - __ xorl(output_lo, input_lo); - __ xorl(output_hi, input_hi); - - // Subtract the sign. - __ subl(output_lo, temp); - __ sbbl(output_hi, temp); -} - -void IntrinsicLocationsBuilderX86::VisitMathAbsInt(HInvoke* invoke) { - CreateAbsIntLocation(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathAbsInt(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), GetAssembler()); -} - -void IntrinsicLocationsBuilderX86::VisitMathAbsLong(HInvoke* invoke) { - CreateAbsLongLocation(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathAbsLong(HInvoke* invoke) { - GenAbsLong(invoke->GetLocations(), GetAssembler()); -} - static void GenMinMaxFP(HInvoke* invoke, bool is_min, bool is_double, diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 6483b7cb2a..9d378d6b59 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -236,102 +236,6 @@ void IntrinsicCodeGeneratorX86_64::VisitShortReverseBytes(HInvoke* invoke) { GenReverseBytes(invoke->GetLocations(), DataType::Type::kInt16, GetAssembler()); } - -// TODO: Consider Quick's way of doing Double abs through integer operations, as the immediate we -// need is 64b. - -static void CreateFloatToFloatPlusTemps(ArenaAllocator* allocator, HInvoke* invoke) { - // TODO: Enable memory operations when the assembler supports them. - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetOut(Location::SameAsFirstInput()); - locations->AddTemp(Location::RequiresFpuRegister()); // FP reg to hold mask. -} - -static void MathAbsFP(LocationSummary* locations, - bool is64bit, - X86_64Assembler* assembler, - CodeGeneratorX86_64* codegen) { - Location output = locations->Out(); - - DCHECK(output.IsFpuRegister()); - XmmRegister xmm_temp = locations->GetTemp(0).AsFpuRegister(); - - // TODO: Can mask directly with constant area using pand if we can guarantee - // that the literal is aligned on a 16 byte boundary. This will avoid a - // temporary. - if (is64bit) { - __ movsd(xmm_temp, codegen->LiteralInt64Address(INT64_C(0x7FFFFFFFFFFFFFFF))); - __ andpd(output.AsFpuRegister(), xmm_temp); - } else { - __ movss(xmm_temp, codegen->LiteralInt32Address(INT32_C(0x7FFFFFFF))); - __ andps(output.AsFpuRegister(), xmm_temp); - } -} - -void IntrinsicLocationsBuilderX86_64::VisitMathAbsDouble(HInvoke* invoke) { - CreateFloatToFloatPlusTemps(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathAbsDouble(HInvoke* invoke) { - MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler(), codegen_); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathAbsFloat(HInvoke* invoke) { - CreateFloatToFloatPlusTemps(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathAbsFloat(HInvoke* invoke) { - MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler(), codegen_); -} - -static void CreateIntToIntPlusTemp(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); - locations->AddTemp(Location::RequiresRegister()); -} - -static void GenAbsInteger(LocationSummary* locations, bool is64bit, X86_64Assembler* assembler) { - Location output = locations->Out(); - CpuRegister out = output.AsRegister(); - CpuRegister mask = locations->GetTemp(0).AsRegister(); - - if (is64bit) { - // Create mask. - __ movq(mask, out); - __ sarq(mask, Immediate(63)); - // Add mask. - __ addq(out, mask); - __ xorq(out, mask); - } else { - // Create mask. - __ movl(mask, out); - __ sarl(mask, Immediate(31)); - // Add mask. - __ addl(out, mask); - __ xorl(out, mask); - } -} - -void IntrinsicLocationsBuilderX86_64::VisitMathAbsInt(HInvoke* invoke) { - CreateIntToIntPlusTemp(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathAbsInt(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetAssembler()); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathAbsLong(HInvoke* invoke) { - CreateIntToIntPlusTemp(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathAbsLong(HInvoke* invoke) { - GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); -} - static void GenMinMaxFP(LocationSummary* locations, bool is_min, bool is_double, diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 0534685e5f..99d80d77c5 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1337,6 +1337,7 @@ class HLoopInformationOutwardIterator : public ValueObject { #define FOR_EACH_CONCRETE_INSTRUCTION_COMMON(M) \ M(Above, Condition) \ M(AboveOrEqual, Condition) \ + M(Abs, UnaryOperation) \ M(Add, BinaryOperation) \ M(And, BinaryOperation) \ M(ArrayGet, Instruction) \ @@ -5015,6 +5016,47 @@ class HRem FINAL : public HBinaryOperation { DEFAULT_COPY_CONSTRUCTOR(Rem); }; +class HAbs FINAL : public HUnaryOperation { + public: + HAbs(DataType::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc) + : HUnaryOperation(kAbs, result_type, input, dex_pc) {} + + // Evaluation for integral values. + template static T ComputeIntegral(T x) { + return x < 0 ? -x : x; + } + + // Evaluation for floating-point values. + // Note, as a "quality of implementation", rather than pure "spec compliance", + // we require that Math.abs() clears the sign bit (but changes nothing else) + // for all floating-point numbers, including NaN (signaling NaN may become quiet though). + // http://b/30758343 + template static T ComputeFP(T x) { + S bits = bit_cast(x); + return bit_cast(bits & std::numeric_limits::max()); + } + + HConstant* Evaluate(HIntConstant* x) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant(ComputeIntegral(x->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HLongConstant* x) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant(ComputeIntegral(x->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HFloatConstant* x) const OVERRIDE { + return GetBlock()->GetGraph()->GetFloatConstant( + ComputeFP(x->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HDoubleConstant* x) const OVERRIDE { + return GetBlock()->GetGraph()->GetDoubleConstant( + ComputeFP(x->GetValue()), GetDexPc()); + } + + DECLARE_INSTRUCTION(Abs); + + protected: + DEFAULT_COPY_CONSTRUCTOR(Abs); +}; + class HDivZeroCheck FINAL : public HExpression<1> { public: // `HDivZeroCheck` can trigger GC, as it may call the `ArithmeticException` diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc index f92f4b274a..a3ca6311c2 100644 --- a/compiler/optimizing/pc_relative_fixups_x86.cc +++ b/compiler/optimizing/pc_relative_fixups_x86.cc @@ -234,6 +234,8 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { switch (invoke->GetIntrinsic()) { case Intrinsics::kMathAbsDouble: case Intrinsics::kMathAbsFloat: + LOG(FATAL) << "Unreachable abs"; + UNREACHABLE(); case Intrinsics::kMathMaxDoubleDouble: case Intrinsics::kMathMaxFloatFloat: case Intrinsics::kMathMinDoubleDouble: diff --git a/compiler/optimizing/scheduler.cc b/compiler/optimizing/scheduler.cc index bb28d50b56..cdbe48342d 100644 --- a/compiler/optimizing/scheduler.cc +++ b/compiler/optimizing/scheduler.cc @@ -667,7 +667,8 @@ bool HScheduler::IsSchedulable(const HInstruction* instruction) const { // HUnaryOperation (or HBinaryOperation), check in debug mode that we have // the exhaustive lists here. if (instruction->IsUnaryOperation()) { - DCHECK(instruction->IsBooleanNot() || + DCHECK(instruction->IsAbs() || + instruction->IsBooleanNot() || instruction->IsNot() || instruction->IsNeg()) << "unexpected instruction " << instruction->DebugName(); return true; diff --git a/test/441-checker-inliner/src/Main.java b/test/441-checker-inliner/src/Main.java index aff4a0717e..3ccfce4de1 100644 --- a/test/441-checker-inliner/src/Main.java +++ b/test/441-checker-inliner/src/Main.java @@ -152,10 +152,12 @@ public class Main { /// CHECK-DAG: <> InvokeStaticOrDirect /// CHECK-DAG: Return [<>] + // + // Intrinsic directly simplified into Abs and evaluated! + // /// CHECK-START: int Main.InlinedIntrinsicsAreStillIntrinsic() inliner (after) - /// CHECK-DAG: <> IntConstant -1 - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsInt - /// CHECK-DAG: Return [<>] + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: Return [<>] public static int InlinedIntrinsicsAreStillIntrinsic() { return returnAbs(-1); diff --git a/test/445-checker-licm/src/Main.java b/test/445-checker-licm/src/Main.java index 9635e70278..bd5d9e20c7 100644 --- a/test/445-checker-licm/src/Main.java +++ b/test/445-checker-licm/src/Main.java @@ -153,14 +153,18 @@ public class Main { return result; } - /// CHECK-START: int Main.invariantBoundIntrinsic(int) licm (before) + + /// CHECK-START: int Main.invariantBoundIntrinsic(int) instruction_simplifier (before) /// CHECK-DAG: InvokeStaticOrDirect loop:{{B\d+}} + // + /// CHECK-START: int Main.invariantBoundIntrinsic(int) licm (before) + /// CHECK-DAG: Abs loop:{{B\d+}} /// CHECK-START: int Main.invariantBoundIntrinsic(int) licm (after) - /// CHECK-NOT: InvokeStaticOrDirect loop:{{B\d+}} + /// CHECK-NOT: Abs loop:{{B\d+}} /// CHECK-START: int Main.invariantBoundIntrinsic(int) licm (after) - /// CHECK-DAG: InvokeStaticOrDirect loop:none + /// CHECK-DAG: Abs loop:none public static int invariantBoundIntrinsic(int x) { int result = 0; diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java index 4868355b90..1144366dfb 100644 --- a/test/449-checker-bce/src/Main.java +++ b/test/449-checker-bce/src/Main.java @@ -1151,8 +1151,7 @@ public class Main { /// CHECK-DAG: <> NullCheck [<>] loop:<> /// CHECK-DAG: <> ArrayLength [<>] loop:<> /// CHECK-DAG: <> BoundsCheck [<>,<>] loop:<> - // Note: The ArtMethod* (typed as int or long) is optional after sharpening. - /// CHECK-DAG: InvokeStaticOrDirect [<>{{(,[ij]\d+)?}}] loop:<> + /// CHECK-DAG: Abs [<>] loop:<> /// CHECK-DAG: <> Phi loop:<> /// CHECK-DAG: <> Phi loop:<> /// CHECK-DAG: <> StaticFieldGet loop:none @@ -1165,8 +1164,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> // Array reference ..[j] still in inner loop, with a direct index. /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> - // Note: The ArtMethod* (typed as int or long) is optional after sharpening. - /// CHECK-DAG: InvokeStaticOrDirect [<>{{(,[ij]\d+)?}}] loop:<> + /// CHECK-DAG: Abs [<>] loop:<> /// CHECK-DAG: <> Phi loop:<> /// CHECK-DAG: <> Phi loop:<> // Synthetic phi. diff --git a/test/455-checker-gvn/src/Main.java b/test/455-checker-gvn/src/Main.java index 6df57fdeef..74b1839808 100644 --- a/test/455-checker-gvn/src/Main.java +++ b/test/455-checker-gvn/src/Main.java @@ -40,7 +40,7 @@ public class Main { /// CHECK: StaticFieldGet /// CHECK: StaticFieldGet /// CHECK: Mul - /// CHECK: InvokeStaticOrDirect + /// CHECK: Abs /// CHECK: StaticFieldGet /// CHECK: StaticFieldGet /// CHECK: Mul @@ -50,7 +50,7 @@ public class Main { /// CHECK: StaticFieldGet /// CHECK: StaticFieldGet /// CHECK: Mul - /// CHECK: InvokeStaticOrDirect + /// CHECK: Abs /// CHECK-NOT: StaticFieldGet /// CHECK-NOT: StaticFieldGet /// CHECK-NOT: Mul @@ -66,13 +66,13 @@ public class Main { } /// CHECK-START: int Main.directIntrinsic(int) GVN (before) - /// CHECK: InvokeStaticOrDirect - /// CHECK: InvokeStaticOrDirect + /// CHECK: Abs + /// CHECK: Abs /// CHECK: Add /// CHECK-START: int Main.directIntrinsic(int) GVN (after) - /// CHECK: InvokeStaticOrDirect - /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK: Abs + /// CHECK-NOT: Abs /// CHECK: Add public static int directIntrinsic(int x) { diff --git a/test/645-checker-abs-simd/src/Main.java b/test/645-checker-abs-simd/src/Main.java index d498bda052..4c69e58004 100644 --- a/test/645-checker-abs-simd/src/Main.java +++ b/test/645-checker-abs-simd/src/Main.java @@ -23,18 +23,18 @@ public class Main { private static final long DPQUIET = 1L << 51; /// CHECK-START: void Main.doitByte(byte[]) loop_optimization (before) - /// CHECK-DAG: Phi loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Phi loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: Abs loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none // /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitByte(byte[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAbs loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: VecLoad loop:<> outer_loop:none + /// CHECK-DAG: VecAbs loop:<> outer_loop:none + /// CHECK-DAG: VecStore loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: Abs loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none // /// CHECK-EVAL: "<>" != "<>" // @@ -45,10 +45,10 @@ public class Main { } /// CHECK-START: void Main.doitChar(char[]) loop_optimization (before) - /// CHECK-DAG: Phi loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Phi loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: Abs loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none // /// CHECK-START: void Main.doitChar(char[]) loop_optimization (after) /// CHECK-NOT: VecAbs @@ -60,18 +60,18 @@ public class Main { } /// CHECK-START: void Main.doitShort(short[]) loop_optimization (before) - /// CHECK-DAG: Phi loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Phi loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: Abs loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none // /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitShort(short[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAbs loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: VecLoad loop:<> outer_loop:none + /// CHECK-DAG: VecAbs loop:<> outer_loop:none + /// CHECK-DAG: VecStore loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: Abs loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none // /// CHECK-EVAL: "<>" != "<>" // @@ -82,18 +82,18 @@ public class Main { } /// CHECK-START: void Main.doitCastedChar(char[]) loop_optimization (before) - /// CHECK-DAG: Phi loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Phi loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: Abs loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none // /// CHECK-START-ARM64: void Main.doitCastedChar(char[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAbs loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: VecLoad loop:<> outer_loop:none + /// CHECK-DAG: VecAbs loop:<> outer_loop:none + /// CHECK-DAG: VecStore loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: Abs loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none // /// CHECK-EVAL: "<>" != "<>" // @@ -104,18 +104,18 @@ public class Main { } /// CHECK-START: void Main.doitInt(int[]) loop_optimization (before) - /// CHECK-DAG: Phi loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Phi loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: Abs loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none // /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitInt(int[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAbs loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: VecLoad loop:<> outer_loop:none + /// CHECK-DAG: VecAbs loop:<> outer_loop:none + /// CHECK-DAG: VecStore loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: Abs loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none // /// CHECK-EVAL: "<>" != "<>" // @@ -126,18 +126,18 @@ public class Main { } /// CHECK-START: void Main.doitLong(long[]) loop_optimization (before) - /// CHECK-DAG: Phi loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsLong loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Phi loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: Abs loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none // /// CHECK-START-{ARM64,MIPS64}: void Main.doitLong(long[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAbs loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsLong loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: VecLoad loop:<> outer_loop:none + /// CHECK-DAG: VecAbs loop:<> outer_loop:none + /// CHECK-DAG: VecStore loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: Abs loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none // /// CHECK-EVAL: "<>" != "<>" // @@ -148,18 +148,18 @@ public class Main { } /// CHECK-START: void Main.doitFloat(float[]) loop_optimization (before) - /// CHECK-DAG: Phi loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsFloat loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Phi loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: Abs loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none // /// CHECK-START-{ARM64,MIPS64}: void Main.doitFloat(float[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAbs loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsFloat loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: VecLoad loop:<> outer_loop:none + /// CHECK-DAG: VecAbs loop:<> outer_loop:none + /// CHECK-DAG: VecStore loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: Abs loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none // /// CHECK-EVAL: "<>" != "<>" // @@ -170,18 +170,18 @@ public class Main { } /// CHECK-START: void Main.doitDouble(double[]) loop_optimization (before) - /// CHECK-DAG: Phi loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsDouble loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Phi loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: Abs loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none // /// CHECK-START-{ARM64,MIPS64}: void Main.doitDouble(double[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAbs loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsDouble loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: VecLoad loop:<> outer_loop:none + /// CHECK-DAG: VecAbs loop:<> outer_loop:none + /// CHECK-DAG: VecStore loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: Abs loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none // /// CHECK-EVAL: "<>" != "<>" // diff --git a/test/660-checker-sad-byte/src/Main.java b/test/660-checker-sad-byte/src/Main.java index 6bcd046bfc..cd7fbcbdb9 100644 --- a/test/660-checker-sad-byte/src/Main.java +++ b/test/660-checker-sad-byte/src/Main.java @@ -24,7 +24,7 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: int Main.sad2(byte, byte) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsInt + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad2(byte x, byte y) { int diff = x - y; @@ -48,7 +48,7 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: int Main.sad3Alt(byte, byte) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsInt + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad3Alt(byte x, byte y) { int diff = x - y; @@ -72,7 +72,7 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: long Main.sadL2(byte, byte) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL2(byte x, byte y) { long diff = x - y; @@ -98,7 +98,7 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: long Main.sadL3Alt(byte, byte) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL3Alt(byte x, byte y) { long diff = x - y; diff --git a/test/660-checker-sad-char/src/Main.java b/test/660-checker-sad-char/src/Main.java index 3033509cdf..ecf748ae07 100644 --- a/test/660-checker-sad-char/src/Main.java +++ b/test/660-checker-sad-char/src/Main.java @@ -24,7 +24,7 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: int Main.sad2(char, char) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsInt + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad2(char x, char y) { int diff = x - y; @@ -48,7 +48,7 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: int Main.sad3Alt(char, char) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsInt + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad3Alt(char x, char y) { int diff = x - y; @@ -72,7 +72,7 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: long Main.sadL2(char, char) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL2(char x, char y) { long diff = x - y; @@ -98,7 +98,7 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: long Main.sadL3Alt(char, char) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL3Alt(char x, char y) { long diff = x - y; diff --git a/test/660-checker-sad-int/src/Main.java b/test/660-checker-sad-int/src/Main.java index 42ecea68da..280dd66a51 100644 --- a/test/660-checker-sad-int/src/Main.java +++ b/test/660-checker-sad-int/src/Main.java @@ -28,7 +28,7 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: int Main.sad2(int, int) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsInt + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad2(int x, int y) { int diff = x - y; @@ -53,7 +53,7 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: int Main.sad3Alt(int, int) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsInt + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad3Alt(int x, int y) { int diff = x - y; @@ -77,7 +77,7 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: long Main.sadL2(int, int) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL2(int x, int y) { long diff = x - y; @@ -103,7 +103,7 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: long Main.sadL3Alt(int, int) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL3Alt(int x, int y) { long diff = x - y; diff --git a/test/660-checker-sad-long/src/Main.java b/test/660-checker-sad-long/src/Main.java index d2e32acffc..ca0f4b71dd 100644 --- a/test/660-checker-sad-long/src/Main.java +++ b/test/660-checker-sad-long/src/Main.java @@ -28,7 +28,7 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: long Main.sad2(long, long) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sad2(long x, long y) { long diff = x - y; @@ -53,7 +53,7 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: long Main.sad3Alt(long, long) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sad3Alt(long x, long y) { long diff = x - y; diff --git a/test/660-checker-sad-short/src/Main.java b/test/660-checker-sad-short/src/Main.java index 182fe8a029..b712a146f4 100644 --- a/test/660-checker-sad-short/src/Main.java +++ b/test/660-checker-sad-short/src/Main.java @@ -24,7 +24,7 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: int Main.sad2(short, short) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsInt + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad2(short x, short y) { int diff = x - y; @@ -48,7 +48,7 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: int Main.sad3Alt(short, short) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsInt + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad3Alt(short x, short y) { int diff = x - y; @@ -72,7 +72,7 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: long Main.sadL2(short, short) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL2(short x, short y) { long diff = x - y; @@ -98,7 +98,7 @@ public class Main { /// CHECK-DAG: Return [<>] // /// CHECK-START: long Main.sadL3Alt(short, short) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathAbsLong + /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL3Alt(short x, short y) { long diff = x - y; diff --git a/test/660-checker-simd-sad-byte/src/Main.java b/test/660-checker-simd-sad-byte/src/Main.java index 594948b7f9..778d55c3ce 100644 --- a/test/660-checker-simd-sad-byte/src/Main.java +++ b/test/660-checker-simd-sad-byte/src/Main.java @@ -95,7 +95,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -126,7 +126,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -159,7 +159,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -197,7 +197,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsLong loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -234,7 +234,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsLong loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // diff --git a/test/660-checker-simd-sad-char/src/Main.java b/test/660-checker-simd-sad-char/src/Main.java index ba226149b4..91c92f1179 100644 --- a/test/660-checker-simd-sad-char/src/Main.java +++ b/test/660-checker-simd-sad-char/src/Main.java @@ -64,7 +64,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -87,7 +87,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -112,7 +112,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -142,7 +142,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsLong loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -170,7 +170,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsLong loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // diff --git a/test/660-checker-simd-sad-int/src/Main.java b/test/660-checker-simd-sad-int/src/Main.java index aa8431c1f5..29415fd2cf 100644 --- a/test/660-checker-simd-sad-int/src/Main.java +++ b/test/660-checker-simd-sad-int/src/Main.java @@ -27,7 +27,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -84,7 +84,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -120,7 +120,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsLong loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -157,7 +157,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsLong loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // diff --git a/test/660-checker-simd-sad-long/src/Main.java b/test/660-checker-simd-sad-long/src/Main.java index 8281812b5b..360e7234eb 100644 --- a/test/660-checker-simd-sad-long/src/Main.java +++ b/test/660-checker-simd-sad-long/src/Main.java @@ -28,7 +28,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsLong loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -90,7 +90,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsLong loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -127,7 +127,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsLong loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // diff --git a/test/660-checker-simd-sad-short/src/Main.java b/test/660-checker-simd-sad-short/src/Main.java index 16bcabac95..8a44d9ed12 100644 --- a/test/660-checker-simd-sad-short/src/Main.java +++ b/test/660-checker-simd-sad-short/src/Main.java @@ -62,7 +62,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -93,7 +93,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -126,7 +126,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -164,7 +164,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsLong loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -201,7 +201,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsLong loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // diff --git a/test/660-checker-simd-sad-short2/src/Main.java b/test/660-checker-simd-sad-short2/src/Main.java index 274892d001..a1f98297c5 100644 --- a/test/660-checker-simd-sad-short2/src/Main.java +++ b/test/660-checker-simd-sad-short2/src/Main.java @@ -61,10 +61,10 @@ public class Main { /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none @@ -80,7 +80,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -130,7 +130,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -182,7 +182,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -215,8 +215,8 @@ public class Main { /// CHECK-DAG: <> LongConstant 0 loop:none /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> Phi [<>,{{j\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none @@ -239,7 +239,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsLong loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -271,8 +271,8 @@ public class Main { /// CHECK-DAG: <> LongConstant 1 loop:none /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> Phi [<>,{{j\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none @@ -295,7 +295,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsLong loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // diff --git a/test/660-checker-simd-sad-short3/src/Main.java b/test/660-checker-simd-sad-short3/src/Main.java index 5016b658e5..877a5362ce 100644 --- a/test/660-checker-simd-sad-short3/src/Main.java +++ b/test/660-checker-simd-sad-short3/src/Main.java @@ -29,7 +29,7 @@ public class Main { /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -60,7 +60,7 @@ public class Main { /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -91,7 +91,7 @@ public class Main { /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -122,7 +122,7 @@ public class Main { /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -153,7 +153,7 @@ public class Main { /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -185,7 +185,7 @@ public class Main { /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -219,7 +219,7 @@ public class Main { /// CHECK-DAG: <> [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // @@ -254,7 +254,7 @@ public class Main { /// CHECK-DAG: <> [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // -- GitLab From b8e7c370ccfc53cca8c7caeee7535693668f70fe Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 20 Feb 2018 18:24:55 -0800 Subject: [PATCH 005/749] ART: Refactor known-classloader visits Refactor visiting dex Elements and DexFiles in known classloaders, unifying the walking code. Test: m test-art-host Change-Id: I4203ac4fbb0ee68660aadc0dfbf8affacbc03b8b --- openjdkjvmti/ti_class.cc | 22 ++-- runtime/class_linker.cc | 192 +++++++++++---------------------- runtime/class_loader_utils.h | 116 +++++++++++++++++++- runtime/common_runtime_test.cc | 58 +++------- 4 files changed, 194 insertions(+), 194 deletions(-) diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc index 7a9432656a..e289eb090e 100644 --- a/openjdkjvmti/ti_class.cc +++ b/openjdkjvmti/ti_class.cc @@ -40,6 +40,7 @@ #include "base/array_ref.h" #include "base/macros.h" #include "class_linker.h" +#include "class_loader_utils.h" #include "class_table-inl.h" #include "common_throws.h" #include "dex/art_dex_file_loader.h" @@ -942,23 +943,12 @@ jvmtiError ClassUtil::GetClassLoaderClassDescriptors(jvmtiEnv* env, art::Handle class_loader( hs.NewHandle(soa.Decode(loader))); std::vector dex_files; - ClassLoaderHelper::VisitDexFileObjects( - self, + art::VisitClassLoaderDexFiles( + soa, class_loader, - [&] (art::ObjPtr dex_file) REQUIRES_SHARED(art::Locks::mutator_lock_) { - art::StackHandleScope<2> hs(self); - art::Handle h_dex_file(hs.NewHandle(dex_file)); - art::Handle cookie( - hs.NewHandle(ClassLoaderHelper::GetDexFileCookie(h_dex_file))); - size_t num_elements = cookie->GetLength(); - // We need to skip over the oat_file that's the first element. The other elements are all - // dex files. - for (size_t i = 1; i < num_elements; i++) { - dex_files.push_back( - reinterpret_cast(static_cast(cookie->Get(i)))); - } - // Iterate over all dex files. - return true; + [&](const art::DexFile* dex_file) { + dex_files.push_back(dex_file); + return true; // Continue with other dex files. }); // We hold the loader so the dex files won't go away until after this call at worst. return CopyClassDescriptors(env, dex_files, count_ptr, classes); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index c667fe2f30..ebac5c1661 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1122,12 +1122,8 @@ static bool FlattenPathClassLoader(ObjPtr class_loader, DCHECK(out_dex_file_names != nullptr); DCHECK(error_msg != nullptr); ScopedObjectAccessUnchecked soa(Thread::Current()); - ArtField* const dex_path_list_field = - jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList); - ArtField* const dex_elements_field = - jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements); - CHECK(dex_path_list_field != nullptr); - CHECK(dex_elements_field != nullptr); + StackHandleScope<1> hs(soa.Self()); + Handle handle(hs.NewHandle(class_loader)); while (!ClassLinker::IsBootClassLoader(soa, class_loader)) { if (soa.Decode(WellKnownClasses::dalvik_system_PathClassLoader) != class_loader->GetClass()) { @@ -1136,32 +1132,29 @@ static bool FlattenPathClassLoader(ObjPtr class_loader, // Unsupported class loader. return false; } - ObjPtr dex_path_list = dex_path_list_field->GetObject(class_loader); - if (dex_path_list != nullptr) { - // DexPathList has an array dexElements of Elements[] which each contain a dex file. - ObjPtr dex_elements_obj = dex_elements_field->GetObject(dex_path_list); - // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look - // at the mCookie which is a DexFile vector. - if (dex_elements_obj != nullptr) { - ObjPtr> dex_elements = - dex_elements_obj->AsObjectArray(); - // Reverse order since we insert the parent at the front. - for (int32_t i = dex_elements->GetLength() - 1; i >= 0; --i) { - ObjPtr element = dex_elements->GetWithoutChecks(i); - if (element == nullptr) { - *error_msg = StringPrintf("Null dex element at index %d", i); - return false; - } - ObjPtr name; - if (!GetDexPathListElementName(element, &name)) { - *error_msg = StringPrintf("Invalid dex path list element at index %d", i); - return false; - } - if (name != nullptr) { - out_dex_file_names->push_front(name.Ptr()); - } - } + // Get element names. Sets error to true on failure. + auto add_element_names = [&](ObjPtr element, bool* error) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (element == nullptr) { + *error_msg = "Null dex element"; + *error = true; // Null element is a critical error. + return false; // Had an error, stop the visit. + } + ObjPtr name; + if (!GetDexPathListElementName(element, &name)) { + *error_msg = "Invalid dex path list element"; + *error = false; // Invalid element is not a critical error. + return false; // Stop the visit. } + if (name != nullptr) { + out_dex_file_names->push_front(name.Ptr()); + } + return true; // Continue with the next Element. + }; + bool error = VisitClassLoaderDexElements(soa, handle, add_element_names, /* error */ false); + if (error) { + // An error occurred during DexPathList Element visiting. + return false; } class_loader = class_loader->GetParent(); } @@ -2444,71 +2437,33 @@ ObjPtr ClassLinker::FindClassInBaseDexClassLoaderClassPath( const char* descriptor, size_t hash, Handle class_loader) { - CHECK(IsPathOrDexClassLoader(soa, class_loader) || IsDelegateLastClassLoader(soa, class_loader)) + DCHECK(IsPathOrDexClassLoader(soa, class_loader) || IsDelegateLastClassLoader(soa, class_loader)) << "Unexpected class loader for descriptor " << descriptor; - Thread* self = soa.Self(); - ArtField* const cookie_field = - jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie); - ArtField* const dex_file_field = - jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); - ObjPtr dex_path_list = - jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)-> - GetObject(class_loader.Get()); - if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) { - // DexPathList has an array dexElements of Elements[] which each contain a dex file. - ObjPtr dex_elements_obj = - jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)-> - GetObject(dex_path_list); - // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look - // at the mCookie which is a DexFile vector. - if (dex_elements_obj != nullptr) { - StackHandleScope<1> hs(self); - Handle> dex_elements = - hs.NewHandle(dex_elements_obj->AsObjectArray()); - for (int32_t i = 0; i < dex_elements->GetLength(); ++i) { - ObjPtr element = dex_elements->GetWithoutChecks(i); - if (element == nullptr) { - // Should never happen, fall back to java code to throw a NPE. - break; - } - ObjPtr dex_file = dex_file_field->GetObject(element); - if (dex_file != nullptr) { - ObjPtr long_array = cookie_field->GetObject(dex_file)->AsLongArray(); - if (long_array == nullptr) { - // This should never happen so log a warning. - LOG(WARNING) << "Null DexFile::mCookie for " << descriptor; - break; - } - int32_t long_array_size = long_array->GetLength(); - // First element is the oat file. - for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) { - const DexFile* cp_dex_file = reinterpret_cast(static_cast( - long_array->GetWithoutChecks(j))); - const DexFile::ClassDef* dex_class_def = - OatDexFile::FindClassDef(*cp_dex_file, descriptor, hash); - if (dex_class_def != nullptr) { - ObjPtr klass = DefineClass(self, - descriptor, - hash, - class_loader, - *cp_dex_file, - *dex_class_def); - if (klass == nullptr) { - CHECK(self->IsExceptionPending()) << descriptor; - self->ClearException(); - // TODO: Is it really right to break here, and not check the other dex files? - return nullptr; - } - return klass; - } - } - } - } - } - self->AssertNoPendingException(); - } - return nullptr; + ObjPtr ret; + auto define_class = [&](const DexFile* cp_dex_file) REQUIRES_SHARED(Locks::mutator_lock_) { + const DexFile::ClassDef* dex_class_def = + OatDexFile::FindClassDef(*cp_dex_file, descriptor, hash); + if (dex_class_def != nullptr) { + ObjPtr klass = DefineClass(soa.Self(), + descriptor, + hash, + class_loader, + *cp_dex_file, + *dex_class_def); + if (klass == nullptr) { + CHECK(soa.Self()->IsExceptionPending()) << descriptor; + soa.Self()->ClearException(); + // TODO: Is it really right to break here, and not check the other dex files? + } + ret = klass; + return false; // Found a Class (or error == nullptr), stop visit. + } + return true; // Continue with the next DexFile. + }; + + VisitClassLoaderDexFiles(soa, class_loader, define_class); + return ret; } mirror::Class* ClassLinker::FindClass(Thread* self, @@ -7901,42 +7856,19 @@ std::string DescribeLoaders(ObjPtr loader, const char* clas if (loader->GetClass() == path_class_loader || loader->GetClass() == dex_class_loader || loader->GetClass() == delegate_last_class_loader) { - ArtField* const cookie_field = - jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie); - ArtField* const dex_file_field = - jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); - ObjPtr dex_path_list = - jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)-> - GetObject(loader); - if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) { - ObjPtr dex_elements_obj = - jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)-> - GetObject(dex_path_list); - if (dex_elements_obj != nullptr) { - ObjPtr> dex_elements = - dex_elements_obj->AsObjectArray(); - oss << "("; - const char* path_separator = ""; - for (int32_t i = 0; i != dex_elements->GetLength(); ++i) { - ObjPtr element = dex_elements->GetWithoutChecks(i); - ObjPtr dex_file = - (element != nullptr) ? dex_file_field->GetObject(element) : nullptr; - ObjPtr long_array = - (dex_file != nullptr) ? cookie_field->GetObject(dex_file)->AsLongArray() : nullptr; - if (long_array != nullptr) { - int32_t long_array_size = long_array->GetLength(); - // First element is the oat file. - for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) { - const DexFile* cp_dex_file = reinterpret_cast( - static_cast(long_array->GetWithoutChecks(j))); - oss << path_separator << cp_dex_file->GetLocation(); - path_separator = ":"; - } - } - } - oss << ")"; - } - } + oss << "("; + ScopedObjectAccessUnchecked soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Handle handle(hs.NewHandle(loader)); + const char* path_separator = ""; + VisitClassLoaderDexFiles(soa, + handle, + [&](const DexFile* dex_file) { + oss << path_separator << dex_file->GetLocation(); + path_separator = ":"; + return true; // Continue with the next DexFile. + }); + oss << ")"; } } diff --git a/runtime/class_loader_utils.h b/runtime/class_loader_utils.h index d160a511de..1439f11636 100644 --- a/runtime/class_loader_utils.h +++ b/runtime/class_loader_utils.h @@ -17,8 +17,12 @@ #ifndef ART_RUNTIME_CLASS_LOADER_UTILS_H_ #define ART_RUNTIME_CLASS_LOADER_UTILS_H_ +#include "art_field-inl.h" +#include "base/mutex.h" #include "handle_scope.h" +#include "jni_internal.h" #include "mirror/class_loader.h" +#include "native/dalvik_system_DexFile.h" #include "scoped_thread_state_change-inl.h" #include "well_known_classes.h" @@ -26,7 +30,7 @@ namespace art { // Returns true if the given class loader is either a PathClassLoader or a DexClassLoader. // (they both have the same behaviour with respect to class lockup order) -static bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa, +inline bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa, Handle class_loader) REQUIRES_SHARED(Locks::mutator_lock_) { mirror::Class* class_loader_class = class_loader->GetClass(); @@ -37,7 +41,7 @@ static bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa, soa.Decode(WellKnownClasses::dalvik_system_DexClassLoader)); } -static bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa, +inline bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa, Handle class_loader) REQUIRES_SHARED(Locks::mutator_lock_) { mirror::Class* class_loader_class = class_loader->GetClass(); @@ -45,6 +49,114 @@ static bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa, soa.Decode(WellKnownClasses::dalvik_system_DelegateLastClassLoader); } +// Visit the DexPathList$Element instances in the given classloader with the given visitor. +// Constraints on the visitor: +// * The visitor should return true to continue visiting more Elements. +// * The last argument of the visitor is an out argument of RetType. It will be returned +// when the visitor ends the visit (by returning false). +// This function assumes that the given classloader is a subclass of BaseDexClassLoader! +template +inline RetType VisitClassLoaderDexElements(ScopedObjectAccessAlreadyRunnable& soa, + Handle class_loader, + Visitor fn, + RetType defaultReturn) + REQUIRES_SHARED(Locks::mutator_lock_) { + Thread* self = soa.Self(); + ObjPtr dex_path_list = + jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)-> + GetObject(class_loader.Get()); + if (dex_path_list != nullptr) { + // DexPathList has an array dexElements of Elements[] which each contain a dex file. + ObjPtr dex_elements_obj = + jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)-> + GetObject(dex_path_list); + // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look + // at the mCookie which is a DexFile vector. + if (dex_elements_obj != nullptr) { + StackHandleScope<1> hs(self); + Handle> dex_elements = + hs.NewHandle(dex_elements_obj->AsObjectArray()); + for (int32_t i = 0; i < dex_elements->GetLength(); ++i) { + ObjPtr element = dex_elements->GetWithoutChecks(i); + if (element == nullptr) { + // Should never happen, fail. + break; + } + RetType ret_value; + if (!fn(element, &ret_value)) { + return ret_value; + } + } + } + self->AssertNoPendingException(); + } + return defaultReturn; +} + +// Visit the DexFiles in the given classloader with the given visitor. +// Constraints on the visitor: +// * The visitor should return true to continue visiting more DexFiles. +// * The last argument of the visitor is an out argument of RetType. It will be returned +// when the visitor ends the visit (by returning false). +// This function assumes that the given classloader is a subclass of BaseDexClassLoader! +template +inline RetType VisitClassLoaderDexFiles(ScopedObjectAccessAlreadyRunnable& soa, + Handle class_loader, + Visitor fn, + RetType defaultReturn) + REQUIRES_SHARED(Locks::mutator_lock_) { + ArtField* const cookie_field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie); + ArtField* const dex_file_field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); + if (dex_file_field == nullptr || cookie_field == nullptr) { + return defaultReturn; + } + auto visit_dex_files = [&](ObjPtr element, RetType* ret) + REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr dex_file = dex_file_field->GetObject(element); + if (dex_file != nullptr) { + ObjPtr long_array = cookie_field->GetObject(dex_file)->AsLongArray(); + if (long_array == nullptr) { + // This should never happen so log a warning. + LOG(WARNING) << "Null DexFile::mCookie"; + *ret = defaultReturn; + return true; + } + int32_t long_array_size = long_array->GetLength(); + // First element is the oat file. + for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) { + const DexFile* cp_dex_file = reinterpret_cast(static_cast( + long_array->GetWithoutChecks(j))); + RetType ret_value; + if (!fn(cp_dex_file, /* out */ &ret_value)) { + *ret = ret_value; + return false; + } + } + } + return true; + }; + + return VisitClassLoaderDexElements(soa, class_loader, visit_dex_files, defaultReturn); +} + +// Simplified version of the above, w/o out argument. +template +inline void VisitClassLoaderDexFiles(ScopedObjectAccessAlreadyRunnable& soa, + Handle class_loader, + Visitor fn) + REQUIRES_SHARED(Locks::mutator_lock_) { + auto helper = [&fn](const art::DexFile* dex_file, void** ATTRIBUTE_UNUSED) + REQUIRES_SHARED(Locks::mutator_lock_) { + return fn(dex_file); + }; + VisitClassLoaderDexFiles(soa, + class_loader, + helper, + /* default */ nullptr); +} + } // namespace art #endif // ART_RUNTIME_CLASS_LOADER_UTILS_H_ diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index e4fbc86020..e7a1374d5f 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -34,6 +34,7 @@ #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" +#include "class_loader_utils.h" #include "compiler_callbacks.h" #include "dex/art_dex_file_loader.h" #include "dex/dex_file-inl.h" @@ -541,58 +542,23 @@ std::vector CommonRuntimeTestImpl::GetDexFiles(jobject jclass_lo std::vector CommonRuntimeTestImpl::GetDexFiles( ScopedObjectAccess& soa, Handle class_loader) { - std::vector ret; - DCHECK( (class_loader->GetClass() == soa.Decode(WellKnownClasses::dalvik_system_PathClassLoader)) || (class_loader->GetClass() == soa.Decode(WellKnownClasses::dalvik_system_DelegateLastClassLoader))); - // The class loader is a PathClassLoader which inherits from BaseDexClassLoader. - // We need to get the DexPathList and loop through it. - ArtField* cookie_field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie); - ArtField* dex_file_field = - jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); - ObjPtr dex_path_list = - jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)-> - GetObject(class_loader.Get()); - if (dex_path_list != nullptr && dex_file_field!= nullptr && cookie_field != nullptr) { - // DexPathList has an array dexElements of Elements[] which each contain a dex file. - ObjPtr dex_elements_obj = - jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)-> - GetObject(dex_path_list); - // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look - // at the mCookie which is a DexFile vector. - if (dex_elements_obj != nullptr) { - StackHandleScope<1> hs(soa.Self()); - Handle> dex_elements = - hs.NewHandle(dex_elements_obj->AsObjectArray()); - for (int32_t i = 0; i < dex_elements->GetLength(); ++i) { - ObjPtr element = dex_elements->GetWithoutChecks(i); - if (element == nullptr) { - // Should never happen, fall back to java code to throw a NPE. - break; - } - ObjPtr dex_file = dex_file_field->GetObject(element); - if (dex_file != nullptr) { - ObjPtr long_array = cookie_field->GetObject(dex_file)->AsLongArray(); - DCHECK(long_array != nullptr); - int32_t long_array_size = long_array->GetLength(); - for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) { - const DexFile* cp_dex_file = reinterpret_cast(static_cast( - long_array->GetWithoutChecks(j))); - if (cp_dex_file == nullptr) { - LOG(WARNING) << "Null DexFile"; - continue; - } - ret.push_back(cp_dex_file); - } - } - } - } - } - + std::vector ret; + VisitClassLoaderDexFiles(soa, + class_loader, + [&](const DexFile* cp_dex_file) { + if (cp_dex_file == nullptr) { + LOG(WARNING) << "Null DexFile"; + } else { + ret.push_back(cp_dex_file); + } + return true; + }); return ret; } -- GitLab From d85b337856eadcb86a12b41855650005f4832c97 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 1 Mar 2018 12:59:58 +0000 Subject: [PATCH 006/749] Fix expectations of 988. To adjust with: https://android-review.googlesource.com/#/c/platform/libcore/+/628453/ test: 988-method-trace Change-Id: Ib4ca10704f55592d516b721319d74d86b54e605f --- test/988-method-trace/check | 23 ----------------------- test/988-method-trace/expected.txt | 8 ++++---- test/988-method-trace/expected_jack.diff | 10 ---------- 3 files changed, 4 insertions(+), 37 deletions(-) delete mode 100644 test/988-method-trace/check delete mode 100644 test/988-method-trace/expected_jack.diff diff --git a/test/988-method-trace/check b/test/988-method-trace/check deleted file mode 100644 index de64a3e17b..0000000000 --- a/test/988-method-trace/check +++ /dev/null @@ -1,23 +0,0 @@ -#!/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. - -# Building for libcore, this uses @hide API which gives it wrong method trace in the expected.txt -# TODO: would be nice if we could build against core_current jars in the future to avoid this. -if [[ "$NEED_DEX" == true ]]; then - patch -p0 expected.txt < expected_jack.diff >/dev/null -fi - -./default-check "$@" diff --git a/test/988-method-trace/expected.txt b/test/988-method-trace/expected.txt index 574d5b0772..7f64e23a77 100644 --- a/test/988-method-trace/expected.txt +++ b/test/988-method-trace/expected.txt @@ -107,8 +107,8 @@ fibonacci(5)=5 ......=> public static char[] java.util.Arrays.copyOf(char[],int) .......=> public static int java.lang.Math.min(int,int) .......<= public static int java.lang.Math.min(int,int) -> -.......=> public static void java.lang.System.arraycopy(char[],int,char[],int,int) -.......<= public static void java.lang.System.arraycopy(char[],int,char[],int,int) -> +.......=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) +.......<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> ......<= public static char[] java.util.Arrays.copyOf(char[],int) -> , , , , , , , , , , , , ]> .....<= private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int) -> .....=> static void java.lang.Integer.getChars(int,int,char[]) @@ -208,8 +208,8 @@ fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0 ......=> public static char[] java.util.Arrays.copyOf(char[],int) .......=> public static int java.lang.Math.min(int,int) .......<= public static int java.lang.Math.min(int,int) -> -.......=> public static void java.lang.System.arraycopy(char[],int,char[],int,int) -.......<= public static void java.lang.System.arraycopy(char[],int,char[],int,int) -> +.......=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) +.......<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> ......<= public static char[] java.util.Arrays.copyOf(char[],int) -> , , , , , , , , , , , , ]> .....<= private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int) -> .....=> static void java.lang.Integer.getChars(int,int,char[]) diff --git a/test/988-method-trace/expected_jack.diff b/test/988-method-trace/expected_jack.diff deleted file mode 100644 index 11364a0539..0000000000 --- a/test/988-method-trace/expected_jack.diff +++ /dev/null @@ -1,10 +0,0 @@ -450,453c450,453 -< .=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -< .<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> -< .=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -< .<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> ---- -> .=> public static void java.lang.System.arraycopy(int[],int,int[],int,int) -> .<= public static void java.lang.System.arraycopy(int[],int,int[],int,int) -> -> .=> public static void java.lang.System.arraycopy(char[],int,char[],int,int) -> .<= public static void java.lang.System.arraycopy(char[],int,char[],int,int) -> -- GitLab From 1c36188675779155ea145066c0ae341fbb0ace4f Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Fri, 2 Mar 2018 14:23:51 +0000 Subject: [PATCH 007/749] Honor ART_TEST_ANDROID_ROOT in libcore and JDWP test scripts. Test: Run libcore and JDWP tests on device with ART_TEST_ANDROID_ROOT defined. Change-Id: Ied7f5bff3f83bcb21d4ab66ed8e0f860aadec252 --- tools/run-jdwp-tests.sh | 9 +++++++-- tools/run-libcore-tests.sh | 7 ++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index c58411c60b..53c6fb63f9 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -26,12 +26,17 @@ if [ -z "$ANDROID_HOST_OUT" ] ; then ANDROID_HOST_OUT=${OUT_DIR-$ANDROID_BUILD_TOP/out}/host/linux-x86 fi +android_root="/system" +if [ -n "$ART_TEST_ANDROID_ROOT" ]; then + android_root="$ART_TEST_ANDROID_ROOT" +fi + java_lib_location="${ANDROID_HOST_OUT}/../common/obj/JAVA_LIBRARIES" make_target_name="apache-harmony-jdwp-tests-hostdex" vm_args="" -art="/data/local/tmp/system/bin/art" -art_debugee="sh /data/local/tmp/system/bin/art" +art="$android_root/bin/art" +art_debugee="sh $android_root/bin/art" args=$@ debuggee_args="-Xcompiler-option --debuggable" device_dir="--device-dir=/data/local/tmp" diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh index 739646a754..2b7c624a3a 100755 --- a/tools/run-libcore-tests.sh +++ b/tools/run-libcore-tests.sh @@ -28,6 +28,11 @@ else JAVA_LIBRARIES=${ANDROID_PRODUCT_OUT}/../../common/obj/JAVA_LIBRARIES fi +android_root="/system" +if [ -n "$ART_TEST_ANDROID_ROOT" ]; then + android_root="$ART_TEST_ANDROID_ROOT" +fi + function classes_jar_path { local var="$1" local suffix="jar" @@ -103,7 +108,7 @@ debug=false while true; do if [[ "$1" == "--mode=device" ]]; then vogar_args="$vogar_args --device-dir=/data/local/tmp" - vogar_args="$vogar_args --vm-command=/data/local/tmp/system/bin/art" + vogar_args="$vogar_args --vm-command=$android_root/bin/art" vogar_args="$vogar_args --vm-arg -Ximage:/data/art-test/core.art" shift elif [[ "$1" == "--mode=host" ]]; then -- GitLab From fe3e2bf0cd746347942e2a38d2d1816051ff68d9 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Fri, 2 Mar 2018 16:01:50 +0000 Subject: [PATCH 008/749] Have `art/tools/buildbot-build --target` abort on empty TARGET_PRODUCT. This is especially useful when the user has forgotten to run the `lunch` command (or forgotten to define TARGET_PRODUCT manually), to provide them with a meaningful error message instead of something cryptic like: ninja: error: unknown target 'out/target/product/system/etc/public.libraries.txt' Test: tools/buildbot-build.sh --target Change-Id: Ia7e8819eb6902db8fd8f6ac24789f1b25c3a6f8c --- tools/buildbot-build.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh index a20175531d..e447ab4cf4 100755 --- a/tools/buildbot-build.sh +++ b/tools/buildbot-build.sh @@ -74,6 +74,10 @@ if [[ $mode == "host" ]]; then make_command+=" dx-tests" mode_suffix="-host" elif [[ $mode == "target" ]]; then + if [[ -z "$TARGET_PRODUCT" ]]; then + echo 'TARGET_PRODUCT environment variable is empty; did you forget to run `lunch`?' + exit 1 + fi make_command="make $j_arg $extra_args $showcommands build-art-target-tests $common_targets" make_command+=" libjavacrypto-target libnetd_client-target linker toybox toolbox sh" make_command+=" ${out_dir}/host/linux-x86/bin/adb libstdc++ " -- GitLab From 58143d2c47734c46c1fa4855cb603c24f2d15454 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Tue, 20 Feb 2018 08:44:20 +0000 Subject: [PATCH 009/749] ART: Fixes for constructor parameter annotations Synthesize empty parameter annotations for implicit parameters on constructors. Reflective methods for recovering parameter annotations expect them to be present though they may not be present in the DEX file. Bug: b/68033708 Test: art/test/run-test --host 715 Change-Id: I0827c7e71ff7c7e044fc9dd6c5aac639a0e1a4c6 --- runtime/art_method-inl.h | 5 + runtime/art_method.h | 2 + runtime/dex/dex_file_annotations.cc | 19 +- runtime/dex/dex_file_annotations.h | 2 + runtime/mirror/class.h | 5 + .../native/java_lang_reflect_Executable.cc | 70 +++++- runtime/native/java_lang_reflect_Parameter.cc | 41 +++- .../build | 24 ++ .../expected.txt | 113 +++++++++ .../info.txt | 5 + .../src/Main.java | 215 ++++++++++++++++++ test/etc/default-build | 2 + 12 files changed, 497 insertions(+), 6 deletions(-) create mode 100644 test/715-clinit-implicit-parameter-annotations/build create mode 100644 test/715-clinit-implicit-parameter-annotations/expected.txt create mode 100644 test/715-clinit-implicit-parameter-annotations/info.txt create mode 100644 test/715-clinit-implicit-parameter-annotations/src/Main.java diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 145eb67aa9..92769942c0 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -295,6 +295,11 @@ inline const DexFile::ClassDef& ArtMethod::GetClassDef() { return GetDexFile()->GetClassDef(GetClassDefIndex()); } +inline size_t ArtMethod::GetNumberOfParameters() { + constexpr size_t return_type_count = 1u; + return strlen(GetShorty()) - return_type_count; +} + inline const char* ArtMethod::GetReturnTypeDescriptor() { DCHECK(!IsProxyMethod()); const DexFile* dex_file = GetDexFile(); diff --git a/runtime/art_method.h b/runtime/art_method.h index 013856f3fe..5d9b729847 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -602,6 +602,8 @@ class ArtMethod FINAL { const DexFile::ClassDef& GetClassDef() REQUIRES_SHARED(Locks::mutator_lock_); + ALWAYS_INLINE size_t GetNumberOfParameters() REQUIRES_SHARED(Locks::mutator_lock_); + const char* GetReturnTypeDescriptor() REQUIRES_SHARED(Locks::mutator_lock_); ALWAYS_INLINE Primitive::Type GetReturnTypePrimitive() REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc index 3431bb7efb..6f3354b724 100644 --- a/runtime/dex/dex_file_annotations.cc +++ b/runtime/dex/dex_file_annotations.cc @@ -1121,6 +1121,21 @@ mirror::ObjectArray* GetParameterAnnotations(ArtMethod* method) return ProcessAnnotationSetRefList(ClassData(method), set_ref_list, size); } +uint32_t GetNumberOfAnnotatedMethodParameters(ArtMethod* method) { + const DexFile* dex_file = method->GetDexFile(); + const DexFile::ParameterAnnotationsItem* parameter_annotations = + FindAnnotationsItemForMethod(method); + if (parameter_annotations == nullptr) { + return 0u; + } + const DexFile::AnnotationSetRefList* set_ref_list = + dex_file->GetParameterAnnotationSetRefList(parameter_annotations); + if (set_ref_list == nullptr) { + return 0u; + } + return set_ref_list->size_; +} + mirror::Object* GetAnnotationForMethodParameter(ArtMethod* method, uint32_t parameter_idx, Handle annotation_class) { @@ -1141,7 +1156,9 @@ mirror::Object* GetAnnotationForMethodParameter(ArtMethod* method, const DexFile::AnnotationSetRefItem* annotation_set_ref = &set_ref_list->list_[parameter_idx]; const DexFile::AnnotationSetItem* annotation_set = dex_file->GetSetRefItemItem(annotation_set_ref); - + if (annotation_set == nullptr) { + return nullptr; + } return GetAnnotationObjectFromAnnotationSet(ClassData(method), annotation_set, DexFile::kDexVisibilityRuntime, diff --git a/runtime/dex/dex_file_annotations.h b/runtime/dex/dex_file_annotations.h index d7ebf84b1c..4bb0d75a57 100644 --- a/runtime/dex/dex_file_annotations.h +++ b/runtime/dex/dex_file_annotations.h @@ -55,6 +55,8 @@ mirror::ObjectArray* GetExceptionTypesForMethod(ArtMethod* method REQUIRES_SHARED(Locks::mutator_lock_); mirror::ObjectArray* GetParameterAnnotations(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); +uint32_t GetNumberOfAnnotatedMethodParameters(ArtMethod* method) + REQUIRES_SHARED(Locks::mutator_lock_); mirror::Object* GetAnnotationForMethodParameter(ArtMethod* method, uint32_t parameter_idx, Handle annotation_class) diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index a1d0ff7374..6000317c85 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -186,6 +186,11 @@ class MANAGED Class FINAL : public Object { void SetAccessFlags(uint32_t new_access_flags) REQUIRES_SHARED(Locks::mutator_lock_); + // Returns true if the class is an enum. + ALWAYS_INLINE bool IsEnum() REQUIRES_SHARED(Locks::mutator_lock_) { + return (GetAccessFlags() & kAccEnum) != 0; + } + // Returns true if the class is an interface. ALWAYS_INLINE bool IsInterface() REQUIRES_SHARED(Locks::mutator_lock_) { return (GetAccessFlags() & kAccInterface) != 0; diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc index a5e70affa5..b129c66759 100644 --- a/runtime/native/java_lang_reflect_Executable.cc +++ b/runtime/native/java_lang_reflect_Executable.cc @@ -70,7 +70,6 @@ static jobjectArray Executable_getSignatureAnnotation(JNIEnv* env, jobject javaM if (method->GetDeclaringClass()->IsProxyClass()) { return nullptr; } - StackHandleScope<1> hs(soa.Self()); return soa.AddLocalReference(annotations::GetSignatureAnnotationForMethod(method)); } @@ -80,9 +79,76 @@ static jobjectArray Executable_getParameterAnnotationsNative(JNIEnv* env, jobjec ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); if (method->IsProxyMethod()) { return nullptr; + } + + StackHandleScope<4> hs(soa.Self()); + Handle> annotations = + hs.NewHandle(annotations::GetParameterAnnotations(method)); + if (annotations.IsNull()) { + return nullptr; + } + + // If the method is not a constructor, or has parameter annotations + // for each parameter, then we can return those annotations + // unmodified. Otherwise, we need to look at whether the + // constructor has implicit parameters as these may need padding + // with empty parameter annotations. + if (!method->IsConstructor() || + annotations->GetLength() == static_cast(method->GetNumberOfParameters())) { + return soa.AddLocalReference(annotations.Get()); + } + + // If declaring class is a local or an enum, do not pad parameter + // annotations, as the implicit constructor parameters are an implementation + // detail rather than required by JLS. + Handle declaring_class = hs.NewHandle(method->GetDeclaringClass()); + if (annotations::GetEnclosingMethod(declaring_class) != nullptr || + declaring_class->IsEnum()) { + return soa.AddLocalReference(annotations.Get()); + } + + // Prepare to resize the annotations so there is 1:1 correspondence + // with the constructor parameters. + Handle> resized_annotations = hs.NewHandle( + mirror::ObjectArray::Alloc( + soa.Self(), + annotations->GetClass(), + static_cast(method->GetNumberOfParameters()))); + if (resized_annotations.IsNull()) { + DCHECK(soa.Self()->IsExceptionPending()); + return nullptr; + } + + static constexpr bool kTransactionActive = false; + const int32_t offset = resized_annotations->GetLength() - annotations->GetLength(); + if (offset > 0) { + // Workaround for dexers (d8/dx) that do not insert annotations + // for implicit parameters (b/68033708). + ObjPtr annotation_array_class = + soa.Decode(WellKnownClasses::java_lang_annotation_Annotation__array); + Handle> empty_annotations = hs.NewHandle( + mirror::ObjectArray::Alloc(soa.Self(), annotation_array_class, 0)); + if (empty_annotations.IsNull()) { + DCHECK(soa.Self()->IsExceptionPending()); + return nullptr; + } + for (int i = 0; i < offset; ++i) { + resized_annotations->SetWithoutChecks(i, empty_annotations.Get()); + } + for (int i = 0; i < annotations->GetLength(); ++i) { + ObjPtr annotation = annotations->GetWithoutChecks(i); + resized_annotations->SetWithoutChecks(i + offset, annotation); + } } else { - return soa.AddLocalReference(annotations::GetParameterAnnotations(method)); + // Workaround for Jack (defunct) erroneously inserting annotations + // for local classes (b/68033708). + DCHECK_LT(offset, 0); + for (int i = 0; i < resized_annotations->GetLength(); ++i) { + ObjPtr annotation = annotations->GetWithoutChecks(i - offset); + resized_annotations->SetWithoutChecks(i, annotation); + } } + return soa.AddLocalReference(resized_annotations.Get()); } static jobjectArray Executable_getParameters0(JNIEnv* env, jobject javaMethod) { diff --git a/runtime/native/java_lang_reflect_Parameter.cc b/runtime/native/java_lang_reflect_Parameter.cc index 0b3015bda8..1ab91098d7 100644 --- a/runtime/native/java_lang_reflect_Parameter.cc +++ b/runtime/native/java_lang_reflect_Parameter.cc @@ -58,6 +58,40 @@ static jobject Parameter_getAnnotationNative(JNIEnv* env, return nullptr; } + uint32_t annotated_parameter_count = annotations::GetNumberOfAnnotatedMethodParameters(method); + if (annotated_parameter_count == 0u) { + return nullptr; + } + + // For constructors with implicit arguments, we may need to adjust + // annotation positions based on whether the implicit parameters are + // expected to known and not just a compiler implementation detail. + if (method->IsConstructor()) { + StackHandleScope<1> hs(soa.Self()); + // If declaring class is a local or an enum, do not pad parameter + // annotations, as the implicit constructor parameters are an + // implementation detail rather than required by JLS. + Handle declaring_class = hs.NewHandle(method->GetDeclaringClass()); + if (annotations::GetEnclosingMethod(declaring_class) == nullptr && !declaring_class->IsEnum()) { + // Adjust the parameter index if the number of annotations does + // not match the number of parameters. + if (annotated_parameter_count <= parameter_count) { + // Workaround for dexer not inserting annotation state for implicit parameters (b/68033708). + uint32_t skip_count = parameter_count - annotated_parameter_count; + DCHECK_GE(2u, skip_count); + if (parameterIndex < static_cast(skip_count)) { + return nullptr; + } + parameterIndex -= skip_count; + } else { + // Workaround for Jack erroneously inserting implicit parameter for local classes + // (b/68033708). + DCHECK_EQ(1u, annotated_parameter_count - parameter_count); + parameterIndex += static_cast(annotated_parameter_count - parameter_count); + } + } + } + StackHandleScope<1> hs(soa.Self()); Handle klass(hs.NewHandle(soa.Decode(annotationType))); return soa.AddLocalReference( @@ -65,9 +99,10 @@ static jobject Parameter_getAnnotationNative(JNIEnv* env, } static JNINativeMethod gMethods[] = { - FAST_NATIVE_METHOD(Parameter, - getAnnotationNative, - "(Ljava/lang/reflect/Executable;ILjava/lang/Class;)Ljava/lang/annotation/Annotation;"), + FAST_NATIVE_METHOD( + Parameter, + getAnnotationNative, + "(Ljava/lang/reflect/Executable;ILjava/lang/Class;)Ljava/lang/annotation/Annotation;"), }; void register_java_lang_reflect_Parameter(JNIEnv* env) { diff --git a/test/715-clinit-implicit-parameter-annotations/build b/test/715-clinit-implicit-parameter-annotations/build new file mode 100644 index 0000000000..4753c8c7dc --- /dev/null +++ b/test/715-clinit-implicit-parameter-annotations/build @@ -0,0 +1,24 @@ +#!/bin/bash +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Make us exit on a failure +set -e + +# Always use D8 as DX does not support propagating parameter name and +# access_flag information. +export USE_D8=true + +./default-build "$@" --experimental parameter-annotations diff --git a/test/715-clinit-implicit-parameter-annotations/expected.txt b/test/715-clinit-implicit-parameter-annotations/expected.txt new file mode 100644 index 0000000000..357eb6253e --- /dev/null +++ b/test/715-clinit-implicit-parameter-annotations/expected.txt @@ -0,0 +1,113 @@ +Main + public Main() +Main$1LocalClassStaticContext + Main$1LocalClassStaticContext(int) + Parameter [0]: Indexed : @Main$AnnotationA() + Array : @Main$AnnotationA() + Main$AnnotationA Yes + @Main$AnnotationA() + Main$AnnotationB No +Main$1LocalClassStaticContextWithCapture + Main$1LocalClassStaticContextWithCapture(java.lang.String,long) + Parameter [0]: Indexed : @Main$AnnotationA() + Array : @Main$AnnotationA() + Main$AnnotationA Yes + @Main$AnnotationA() + Main$AnnotationB No +Main$1LocalClassStaticContextWithCaptureAlternateOrdering + Main$1LocalClassStaticContextWithCaptureAlternateOrdering(java.lang.String,long) + Parameter [0]: Indexed : @Main$AnnotationA() + Array : @Main$AnnotationA() + Main$AnnotationA Yes + @Main$AnnotationA() + Main$AnnotationB No +Main$1LocalClass + Main$1LocalClass(Main,int) + Parameter [0]: Indexed : @Main$AnnotationA() + Array : @Main$AnnotationA() + Main$AnnotationA Yes + @Main$AnnotationA() + Main$AnnotationB No +Main$1LocalClassWithCapture + Main$1LocalClassWithCapture(Main,java.lang.String,long) + Parameter [0]: Indexed : @Main$AnnotationA() + Array : @Main$AnnotationA() + Main$AnnotationA Yes + @Main$AnnotationA() + Main$AnnotationB No +Main$Inner + Main$Inner(Main,int,java.lang.String) + Parameter [0]: Main$AnnotationA No + Main$AnnotationB No + Parameter [1]: Indexed : @Main$AnnotationA() + Array : @Main$AnnotationA() + Main$AnnotationA Yes + @Main$AnnotationA() + Main$AnnotationB No + Parameter [2]: Main$AnnotationA No + Main$AnnotationB No + Main$Inner(Main,int,java.lang.String,boolean) + Parameter [0]: Main$AnnotationA No + Main$AnnotationB No + Parameter [1]: Indexed : @Main$AnnotationA() + Array : @Main$AnnotationA() + Main$AnnotationA Yes + @Main$AnnotationA() + Main$AnnotationB No + Parameter [2]: Main$AnnotationA No + Main$AnnotationB No + Parameter [3]: Indexed : @Main$AnnotationB(value=x) + Array : @Main$AnnotationB(value=x) + Main$AnnotationA No + Main$AnnotationB Yes + @Main$AnnotationB(value=x) +Main$StaticInner + Main$StaticInner(int,java.lang.String) + Parameter [0]: Indexed : @Main$AnnotationA() + Array : @Main$AnnotationA() + Main$AnnotationA Yes + @Main$AnnotationA() + Main$AnnotationB No + Parameter [1]: Main$AnnotationA No + Main$AnnotationB No + Main$StaticInner(int,java.lang.String,boolean) + Parameter [0]: Indexed : @Main$AnnotationB(value=foo) + Array : @Main$AnnotationB(value=foo) + Main$AnnotationA No + Main$AnnotationB Yes + @Main$AnnotationB(value=foo) + Parameter [1]: Main$AnnotationA No + Main$AnnotationB No + Parameter [2]: Indexed : @Main$AnnotationA() + Array : @Main$AnnotationA() + Main$AnnotationA Yes + @Main$AnnotationA() + Main$AnnotationB No +Main$ImportantNumber + private Main$ImportantNumber(java.lang.String,int,double) + Parameter [0]: Indexed : @Main$AnnotationA() + Array : @Main$AnnotationA() + Main$AnnotationA Yes + @Main$AnnotationA() + Main$AnnotationB No + private Main$ImportantNumber(java.lang.String,int,double,boolean) + Parameter [0]: Indexed : @Main$AnnotationB(value=x) + Array : @Main$AnnotationB(value=x) + Main$AnnotationA No + Main$AnnotationB Yes + @Main$AnnotationB(value=x) + Parameter [1]: Indexed : @Main$AnnotationB(value=y) + Array : @Main$AnnotationB(value=y) + Main$AnnotationA No + Main$AnnotationB Yes + @Main$AnnotationB(value=y) +Main$BinaryNumber + private Main$BinaryNumber(java.lang.String,int) + Parameter [0]: Main$AnnotationA No + Main$AnnotationB No + Parameter [1]: Main$AnnotationA No + Main$AnnotationB No +Main$1 + Main$1(java.lang.String) + Parameter [0]: Main$AnnotationA No + Main$AnnotationB No diff --git a/test/715-clinit-implicit-parameter-annotations/info.txt b/test/715-clinit-implicit-parameter-annotations/info.txt new file mode 100644 index 0000000000..31afd62f27 --- /dev/null +++ b/test/715-clinit-implicit-parameter-annotations/info.txt @@ -0,0 +1,5 @@ +Tests ART synthesizes parameter annotations for implicit parameters on +constructors. Inner class and enum constructors may have implicit +parameters. If the constructor has parameter annotations, the implicit +parameters may not have annotations in the DEX file, but code that +looks at these annotations will expect them to. diff --git a/test/715-clinit-implicit-parameter-annotations/src/Main.java b/test/715-clinit-implicit-parameter-annotations/src/Main.java new file mode 100644 index 0000000000..351e3a94b3 --- /dev/null +++ b/test/715-clinit-implicit-parameter-annotations/src/Main.java @@ -0,0 +1,215 @@ +/* + * 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.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Constructor; +import java.lang.reflect.Parameter; + +public class Main { + // A simple parameter annotation + @Retention(RetentionPolicy.RUNTIME) + public @interface AnnotationA {} + + // A parameter annotation with additional state + @Retention(RetentionPolicy.RUNTIME) + public @interface AnnotationB { + String value() default "default-value"; + } + + // An inner class whose constructors with have an implicit + // argument for the enclosing instance. + public class Inner { + private final int number; + private final String text; + boolean flag; + + Inner(@AnnotationA int number, String text) { + this.number = number; + this.text = text; + this.flag = false; + } + + Inner(@AnnotationA int number, String text, @AnnotationB("x") boolean flag) { + this.number = number; + this.text = text; + this.flag = flag; + } + } + + // An inner class whose constructors with have no implicit + // arguments for the enclosing instance. + public static class StaticInner { + private final int number; + private final String text; + boolean flag; + + StaticInner(@AnnotationA int number, String text) { + this.number = number; + this.text = text; + this.flag = false; + } + + StaticInner(@AnnotationB("foo") int number, String text, @AnnotationA boolean flag) { + this.number = number; + this.text = text; + this.flag = flag; + } + } + + public enum ImportantNumber { + ONE(1.0), + TWO(2.0), + MANY(3.0, true); + + private double doubleValue; + private boolean isLarge; + + ImportantNumber(@AnnotationA double doubleValue) { + this.doubleValue = doubleValue; + this.isLarge = false; + } + + ImportantNumber(@AnnotationB("x") double doubleValue, @AnnotationB("y") boolean isLarge) { + this.doubleValue = doubleValue; + this.isLarge = isLarge; + } + } + + public enum BinaryNumber { + ZERO, + ONE; + } + + private abstract static class AnonymousBase { + public AnonymousBase(@AnnotationA String s) {} + } + + private static String annotationToNormalizedString(Annotation annotation) { + // String.replace() to accomodate different representation across VMs. + return annotation.toString().replace("\"", ""); + } + + private static void DumpConstructorParameterAnnotations(Class cls) throws Throwable { + System.out.println(cls.getName()); + for (Constructor c : cls.getDeclaredConstructors()) { + System.out.println(" " + c); + Annotation[][] annotations = c.getParameterAnnotations(); + Parameter[] parameters = c.getParameters(); + for (int i = 0; i < annotations.length; ++i) { + // Exercise java.lang.reflect.Executable.getParameterAnnotationsNative() + // which retrieves all annotations for the parameters. + System.out.print(" Parameter [" + i + "]:"); + for (Annotation annotation : parameters[i].getAnnotations()) { + System.out.println(" Indexed : " + annotationToNormalizedString(annotation)); + } + for (Annotation annotation : annotations[i]) { + System.out.println(" Array : " + annotationToNormalizedString(annotation)); + } + + // Exercise Parameter.getAnnotationNative() with + // retrieves a single parameter annotation according to type. + Object[] opaqueClasses = new Object[] {AnnotationA.class, AnnotationB.class}; + for (Object opaqueClass : opaqueClasses) { + @SuppressWarnings("unchecked") + Class annotationClass = + (Class) opaqueClass; + Annotation annotation = parameters[i].getDeclaredAnnotation(annotationClass); + String hasAnnotation = (annotation != null ? "Yes" : "No"); + System.out.println(" " + annotationClass.getName() + " " + hasAnnotation); + + Annotation[] parameterAnnotations = parameters[i].getDeclaredAnnotationsByType(annotationClass); + for (Annotation parameterAnnotation : parameterAnnotations) { + System.out.println(" " + annotationToNormalizedString(parameterAnnotation)); + } + } + } + } + } + + private Class getLocalClassWithEnclosingInstanceCapture() { + class LocalClass { + private final int integerValue; + + LocalClass(@AnnotationA int integerValue) { + this.integerValue = integerValue; + } + } + return LocalClass.class; + } + + private Class getLocalClassWithEnclosingInstanceAndLocalCapture() { + final long CAPTURED_VALUE = System.currentTimeMillis(); + class LocalClassWithCapture { + private final String value; + private final long capturedValue; + + LocalClassWithCapture(@AnnotationA String p1) { + this.value = p1; + this.capturedValue = CAPTURED_VALUE; + } + } + return LocalClassWithCapture.class; + } + + public static void main(String[] args) throws Throwable { + // A local class declared in a static context (0 implicit parameters). + class LocalClassStaticContext { + private final int value; + + LocalClassStaticContext(@AnnotationA int p0) { + this.value = p0; + } + } + + final long CAPTURED_VALUE = System.currentTimeMillis(); + // A local class declared in a static context with a capture (1 implicit parameters). + class LocalClassStaticContextWithCapture { + private final long capturedValue; + private final String argumentValue; + + LocalClassStaticContextWithCapture(@AnnotationA String p1) { + this.capturedValue = CAPTURED_VALUE; + this.argumentValue = p1; + } + } + + // Another local class declared in a static context with a capture (1 implicit parameters). + class LocalClassStaticContextWithCaptureAlternateOrdering { + private final String argumentValue; + private final long capturedValue; + + LocalClassStaticContextWithCaptureAlternateOrdering(@AnnotationA String p1) { + this.argumentValue = p1; + this.capturedValue = CAPTURED_VALUE; + } + } + + DumpConstructorParameterAnnotations(Main.class); + DumpConstructorParameterAnnotations(LocalClassStaticContext.class); + DumpConstructorParameterAnnotations(LocalClassStaticContextWithCapture.class); + DumpConstructorParameterAnnotations(LocalClassStaticContextWithCaptureAlternateOrdering.class); + Main m = new Main(); + DumpConstructorParameterAnnotations(m.getLocalClassWithEnclosingInstanceCapture()); + DumpConstructorParameterAnnotations(m.getLocalClassWithEnclosingInstanceAndLocalCapture()); + DumpConstructorParameterAnnotations(Inner.class); + DumpConstructorParameterAnnotations(StaticInner.class); + DumpConstructorParameterAnnotations(ImportantNumber.class); + DumpConstructorParameterAnnotations(BinaryNumber.class); + DumpConstructorParameterAnnotations(new AnonymousBase("") {}.getClass()); + } +} diff --git a/test/etc/default-build b/test/etc/default-build index 4ed2af6ac6..3e6577cfda 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -137,12 +137,14 @@ declare -A JAVAC_EXPERIMENTAL_ARGS JAVAC_EXPERIMENTAL_ARGS["default-methods"]="-source 1.8 -target 1.8" JAVAC_EXPERIMENTAL_ARGS["lambdas"]="-source 1.8 -target 1.8" JAVAC_EXPERIMENTAL_ARGS["method-handles"]="-source 1.8 -target 1.8" +JAVAC_EXPERIMENTAL_ARGS["parameter-annotations"]="-source 1.8 -target 1.8" JAVAC_EXPERIMENTAL_ARGS["var-handles"]="-source 1.8 -target 1.8" JAVAC_EXPERIMENTAL_ARGS[${DEFAULT_EXPERIMENT}]="-source 1.8 -target 1.8" JAVAC_EXPERIMENTAL_ARGS["agents"]="-source 1.8 -target 1.8" declare -A DX_EXPERIMENTAL_ARGS DX_EXPERIMENTAL_ARGS["method-handles"]="--min-sdk-version=26" +DX_EXPERIMENTAL_ARGS["parameter-annotations"]="--min-sdk-version=25" DX_EXPERIMENTAL_ARGS["var-handles"]="--min-sdk-version=26" while true; do -- GitLab From f39f04c536797fe23d7da827acacdb3469ba7450 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 5 Mar 2018 15:42:11 +0000 Subject: [PATCH 010/749] Be consistent with android log tags between target/host. Test: test.py Change-Id: I0f8a1a896fda5552fb85ad5e615eeee88a0a5ec0 --- test/etc/run-test-jar | 49 ++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index b8427f491b..bcb15cd4f2 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -812,6 +812,25 @@ if [ "x$RUN_TEST_ASAN_OPTIONS" != "x" ] ; then fi RUN_TEST_ASAN_OPTIONS="${RUN_TEST_ASAN_OPTIONS}detect_leaks=0" +# For running, we must turn off logging when dex2oat or patchoat are missing. Otherwise we use +# the same defaults as for prebuilt: everything when --dev, otherwise errors and above only. +if [ "$EXTERNAL_LOG_TAGS" = "n" ]; then + if [ "$DEV_MODE" = "y" ]; then + export ANDROID_LOG_TAGS='*:d' + elif [ "$USE_DEX2OAT_AND_PATCHOAT" = "n" ]; then + # All tests would log the error of failing dex2oat/patchoat. Be silent here and only + # log fatal events. + export ANDROID_LOG_TAGS='*:s' + elif [ "$HAVE_IMAGE" = "n" ]; then + # All tests would log the error of missing image. Be silent here and only log fatal + # events. + export ANDROID_LOG_TAGS='*:s' + else + # We are interested in LOG(ERROR) output. + export ANDROID_LOG_TAGS='*:e' + fi +fi + if [ "$HOST" = "n" ]; then adb root > /dev/null adb wait-for-device @@ -862,6 +881,7 @@ if [ "$HOST" = "n" ]; then export ANDROID_ADDITIONAL_PUBLIC_LIBRARIES=$PUBLIC_LIBS && \ export DEX_LOCATION=$DEX_LOCATION && \ export ANDROID_ROOT=$ANDROID_ROOT && \ + export ANDROID_LOG_TAGS=$ANDROID_LOG_TAGS && \ rm -rf ${DEX_LOCATION}/dalvik-cache/ && \ mkdir -p ${mkdir_locations} && \ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH && \ @@ -899,16 +919,6 @@ else # Host run. export ANDROID_PRINTF_LOG=brief - # By default, and for prebuild dex2oat, we are interested in errors being logged. In dev mode - # we want debug messages. - if [ "$EXTERNAL_LOG_TAGS" = "n" ]; then - if [ "$DEV_MODE" = "y" ]; then - export ANDROID_LOG_TAGS='*:d' - else - export ANDROID_LOG_TAGS='*:e' - fi - fi - export ANDROID_DATA="$DEX_LOCATION" export ANDROID_ROOT="${ANDROID_ROOT}" export LD_LIBRARY_PATH="${ANDROID_ROOT}/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}" @@ -969,25 +979,6 @@ else $strip_cmdline || { echo "Strip failed." >&2 ; exit 3; } $sync_cmdline || { echo "Sync failed." >&2 ; exit 4; } - # For running, we must turn off logging when dex2oat or patchoat are missing. Otherwise we use - # the same defaults as for prebuilt: everything when --dev, otherwise errors and above only. - if [ "$EXTERNAL_LOG_TAGS" = "n" ]; then - if [ "$DEV_MODE" = "y" ]; then - export ANDROID_LOG_TAGS='*:d' - elif [ "$USE_DEX2OAT_AND_PATCHOAT" = "n" ]; then - # All tests would log the error of failing dex2oat/patchoat. Be silent here and only - # log fatal events. - export ANDROID_LOG_TAGS='*:s' - elif [ "$HAVE_IMAGE" = "n" ]; then - # All tests would log the error of missing image. Be silent here and only log fatal - # events. - export ANDROID_LOG_TAGS='*:s' - else - # We are interested in LOG(ERROR) output. - export ANDROID_LOG_TAGS='*:e' - fi - fi - if [ "$DRY_RUN" = "y" ]; then exit 0 fi -- GitLab From 8b2f3ace473ea130e02c0eb6b899dce07839fc7c Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 5 Mar 2018 10:54:53 -0800 Subject: [PATCH 011/749] Avoid verifying output dex for dexlayout in FixedUpDexFile::Create Verification will fail if the input dex has hidden API flags. Test: test-art-host Bug: 74063493 Change-Id: Ia0f48a07ac774b0f3409817d4e8134804f9ff849 --- openjdkjvmti/fixed_up_dex_file.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc index 16bbee4037..ee83c4a4e8 100644 --- a/openjdkjvmti/fixed_up_dex_file.cc +++ b/openjdkjvmti/fixed_up_dex_file.cc @@ -115,6 +115,9 @@ std::unique_ptr FixedUpDexFile::Create(const art::DexFile& origi // this before unquickening. art::Options options; options.compact_dex_level_ = art::CompactDexLevel::kCompactDexLevelNone; + // Never verify the output since hidden API flags may cause the dex file verifier to fail. + // See b/74063493 + options.verify_output_ = false; // Add a filter to only include the class that has the matching descriptor. static constexpr bool kFilterByDescriptor = true; if (kFilterByDescriptor) { -- GitLab From 8b089742252e827d863218413e8855e1bae75af5 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 5 Mar 2018 11:47:30 -0800 Subject: [PATCH 012/749] Revert "Add an option to disable native stack dumping on SIGQUIT." This reverts commit a73280df8cac1279b6dea0424722f42ef0048613. Bug: 27185632 Bug: 74121887 Test: m test-art-host Change-Id: I24af48619577a78371c93cbad24d307d4d7a217d --- runtime/parsed_options.cc | 5 ----- runtime/runtime.cc | 2 -- runtime/runtime.h | 7 ------- runtime/runtime_common.cc | 3 +-- runtime/runtime_options.def | 1 - runtime/thread.cc | 12 ++++-------- runtime/thread.h | 2 -- runtime/thread_list.cc | 21 ++++++++------------- runtime/thread_list.h | 2 +- test/etc/run-test-jar | 4 ---- 10 files changed, 14 insertions(+), 45 deletions(-) diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index c61ecc880b..1d48817e94 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -161,10 +161,6 @@ std::unique_ptr ParsedOptions::MakeParser(bool ignore_unrecognize .Define({"-XX:EnableHSpaceCompactForOOM", "-XX:DisableHSpaceCompactForOOM"}) .WithValues({true, false}) .IntoKey(M::EnableHSpaceCompactForOOM) - .Define("-XX:DumpNativeStackOnSigQuit:_") - .WithType() - .WithValueMap({{"false", false}, {"true", true}}) - .IntoKey(M::DumpNativeStackOnSigQuit) .Define("-XX:MadviseRandomAccess:_") .WithType() .WithValueMap({{"false", false}, {"true", true}}) @@ -735,7 +731,6 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -XX:BackgroundGC=none\n"); UsageMessage(stream, " -XX:LargeObjectSpace={disabled,map,freelist}\n"); UsageMessage(stream, " -XX:LargeObjectThreshold=N\n"); - UsageMessage(stream, " -XX:DumpNativeStackOnSigQuit=booleanvalue\n"); UsageMessage(stream, " -XX:MadviseRandomAccess:booleanvalue\n"); UsageMessage(stream, " -XX:SlowDebug={false,true}\n"); UsageMessage(stream, " -Xmethod-trace\n"); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 4442fc6a54..d0a1acc072 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -269,7 +269,6 @@ Runtime::Runtime() pending_hidden_api_warning_(false), dedupe_hidden_api_warnings_(true), always_set_hidden_api_warning_flag_(false), - dump_native_stack_on_sig_quit_(true), pruned_dalvik_cache_(false), // Initially assume we perceive jank in case the process state is never updated. process_state_(kProcessStateJankPerceptible), @@ -1151,7 +1150,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { is_explicit_gc_disabled_ = runtime_options.Exists(Opt::DisableExplicitGC); dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::Dex2Oat); image_dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::ImageDex2Oat); - dump_native_stack_on_sig_quit_ = runtime_options.GetOrDefault(Opt::DumpNativeStackOnSigQuit); vfprintf_ = runtime_options.GetOrDefault(Opt::HookVfprintf); exit_ = runtime_options.GetOrDefault(Opt::HookExit); diff --git a/runtime/runtime.h b/runtime/runtime.h index c7f650ea3f..b961e7f39a 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -651,10 +651,6 @@ class Runtime { safe_mode_ = mode; } - bool GetDumpNativeStackOnSigQuit() const { - return dump_native_stack_on_sig_quit_; - } - bool GetPrunedDalvikCache() const { return pruned_dalvik_cache_; } @@ -1005,9 +1001,6 @@ class Runtime { // when there is a warning. This is only used for testing. bool always_set_hidden_api_warning_flag_; - // Whether threads should dump their native stack on SIGQUIT. - bool dump_native_stack_on_sig_quit_; - // Whether the dalvik cache was pruned when initializing the runtime. bool pruned_dalvik_cache_; diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc index 59af9187f9..41bfb58d93 100644 --- a/runtime/runtime_common.cc +++ b/runtime/runtime_common.cc @@ -41,7 +41,6 @@ namespace art { using android::base::StringPrintf; static constexpr bool kUseSigRTTimeout = true; -static constexpr bool kDumpNativeStackOnTimeout = true; const char* GetSignalName(int signal_number) { switch (signal_number) { @@ -441,7 +440,7 @@ void HandleUnexpectedSignalCommon(int signal_number, // Special timeout signal. Try to dump all threads. // Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts // are of value here. - runtime->GetThreadList()->Dump(std::cerr, kDumpNativeStackOnTimeout); + runtime->GetThreadList()->Dump(std::cerr); std::cerr << std::endl; } diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 4121ad69ed..dcb1335023 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -70,7 +70,6 @@ RUNTIME_OPTIONS_KEY (Unit, LowMemoryMode) RUNTIME_OPTIONS_KEY (bool, UseTLAB, (kUseTlab || kUseReadBarrier)) RUNTIME_OPTIONS_KEY (bool, EnableHSpaceCompactForOOM, true) RUNTIME_OPTIONS_KEY (bool, UseJitCompilation, false) -RUNTIME_OPTIONS_KEY (bool, DumpNativeStackOnSigQuit, true) RUNTIME_OPTIONS_KEY (bool, MadviseRandomAccess, false) RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold) RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold) diff --git a/runtime/thread.cc b/runtime/thread.cc index 4cdf015478..87ffcb11d2 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1161,10 +1161,9 @@ void Thread::ShortDump(std::ostream& os) const { << "]"; } -void Thread::Dump(std::ostream& os, bool dump_native_stack, BacktraceMap* backtrace_map, - bool force_dump_stack) const { +void Thread::Dump(std::ostream& os, BacktraceMap* backtrace_map, bool force_dump_stack) const { DumpState(os); - DumpStack(os, dump_native_stack, backtrace_map, force_dump_stack); + DumpStack(os, backtrace_map, force_dump_stack); } mirror::String* Thread::GetThreadName() const { @@ -1964,10 +1963,7 @@ void Thread::DumpJavaStack(std::ostream& os, bool check_suspended, bool dump_loc } } -void Thread::DumpStack(std::ostream& os, - bool dump_native_stack, - BacktraceMap* backtrace_map, - bool force_dump_stack) const { +void Thread::DumpStack(std::ostream& os, BacktraceMap* backtrace_map, bool force_dump_stack) const { // TODO: we call this code when dying but may not have suspended the thread ourself. The // IsSuspended check is therefore racy with the use for dumping (normally we inhibit // the race with the thread_suspend_count_lock_). @@ -1980,7 +1976,7 @@ void Thread::DumpStack(std::ostream& os, } if (safe_to_dump || force_dump_stack) { // If we're currently in native code, dump that stack before dumping the managed stack. - if (dump_native_stack && (dump_for_abort || force_dump_stack || ShouldShowNativeStack(this))) { + if (dump_for_abort || force_dump_stack || ShouldShowNativeStack(this)) { DumpKernelStack(os, GetTid(), " kernel: ", false); ArtMethod* method = GetCurrentMethod(nullptr, diff --git a/runtime/thread.h b/runtime/thread.h index 295685e799..87515d6206 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -207,7 +207,6 @@ class Thread { // Dumps the detailed thread state and the thread stack (used for SIGQUIT). void Dump(std::ostream& os, - bool dump_native_stack = true, BacktraceMap* backtrace_map = nullptr, bool force_dump_stack = false) const REQUIRES(!Locks::thread_suspend_count_lock_) @@ -1303,7 +1302,6 @@ class Thread { void DumpState(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_); void DumpStack(std::ostream& os, - bool dump_native_stack = true, BacktraceMap* backtrace_map = nullptr, bool force_dump_stack = false) const REQUIRES(!Locks::thread_suspend_count_lock_) diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 8095ef57c7..2e41b9f455 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -152,9 +152,8 @@ void ThreadList::DumpForSigQuit(std::ostream& os) { suspend_all_historam_.PrintConfidenceIntervals(os, 0.99, data); // Dump time to suspend. } } - bool dump_native_stack = Runtime::Current()->GetDumpNativeStackOnSigQuit(); - Dump(os, dump_native_stack); - DumpUnattachedThreads(os, dump_native_stack && kDumpUnattachedThreadNativeStackForSigQuit); + Dump(os); + DumpUnattachedThreads(os, kDumpUnattachedThreadNativeStackForSigQuit); } static void DumpUnattachedThread(std::ostream& os, pid_t tid, bool dump_native_stack) @@ -201,11 +200,10 @@ static constexpr uint32_t kDumpWaitTimeout = kIsTargetBuild ? 100000 : 20000; // A closure used by Thread::Dump. class DumpCheckpoint FINAL : public Closure { public: - DumpCheckpoint(std::ostream* os, bool dump_native_stack) + explicit DumpCheckpoint(std::ostream* os) : os_(os), barrier_(0), - backtrace_map_(dump_native_stack ? BacktraceMap::Create(getpid()) : nullptr), - dump_native_stack_(dump_native_stack) { + backtrace_map_(BacktraceMap::Create(getpid())) { if (backtrace_map_ != nullptr) { backtrace_map_->SetSuffixesToIgnore(std::vector { "oat", "odex" }); } @@ -219,7 +217,7 @@ class DumpCheckpoint FINAL : public Closure { std::ostringstream local_os; { ScopedObjectAccess soa(self); - thread->Dump(local_os, dump_native_stack_, backtrace_map_.get()); + thread->Dump(local_os, backtrace_map_.get()); } { // Use the logging lock to ensure serialization when writing to the common ostream. @@ -247,18 +245,16 @@ class DumpCheckpoint FINAL : public Closure { Barrier barrier_; // A backtrace map, so that all threads use a shared info and don't reacquire/parse separately. std::unique_ptr backtrace_map_; - // Whether we should dump the native stack. - const bool dump_native_stack_; }; -void ThreadList::Dump(std::ostream& os, bool dump_native_stack) { +void ThreadList::Dump(std::ostream& os) { Thread* self = Thread::Current(); { MutexLock mu(self, *Locks::thread_list_lock_); os << "DALVIK THREADS (" << list_.size() << "):\n"; } if (self != nullptr) { - DumpCheckpoint checkpoint(&os, dump_native_stack); + DumpCheckpoint checkpoint(&os); size_t threads_running_checkpoint; { // Use SOA to prevent deadlocks if multiple threads are calling Dump() at the same time. @@ -269,7 +265,7 @@ void ThreadList::Dump(std::ostream& os, bool dump_native_stack) { checkpoint.WaitForThreadsToRunThroughCheckpoint(threads_running_checkpoint); } } else { - DumpUnattachedThreads(os, dump_native_stack); + DumpUnattachedThreads(os, /* dump_native_stack */ true); } } @@ -491,7 +487,6 @@ void ThreadList::RunEmptyCheckpoint() { // Found a runnable thread that hasn't responded to the empty checkpoint request. // Assume it's stuck and safe to dump its stack. thread->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT), - /*dump_native_stack*/ true, /*backtrace_map*/ nullptr, /*force_dump_stack*/ true); } diff --git a/runtime/thread_list.h b/runtime/thread_list.h index 895c1a41ce..09b10d2ad3 100644 --- a/runtime/thread_list.h +++ b/runtime/thread_list.h @@ -57,7 +57,7 @@ class ThreadList { void DumpForSigQuit(std::ostream& os) REQUIRES(!Locks::thread_list_lock_, !Locks::mutator_lock_); // For thread suspend timeout dumps. - void Dump(std::ostream& os, bool dump_native_stack = true) + void Dump(std::ostream& os) REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_); pid_t GetLockOwner(); // For SignalCatcher. diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index b8427f491b..5c51aed7a2 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -775,9 +775,6 @@ if [ "$HOST" = "n" ]; then TMP_DIR_OPTION="-Djava.io.tmpdir=/data/local/tmp" fi -# We set DumpNativeStackOnSigQuit to false to avoid stressing libunwind. -# b/27185632 -# b/24664297 dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \ $GDB_ARGS \ $FLAGS \ @@ -792,7 +789,6 @@ dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \ $DEBUGGER_OPTS \ $DALVIKVM_BOOT_OPT \ $TMP_DIR_OPTION \ - -XX:DumpNativeStackOnSigQuit:false \ -cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX $MAIN $ARGS" # Remove whitespace. -- GitLab From 3b2a595f7781a5a6064c5d635454dd42fe130833 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Mon, 5 Mar 2018 13:55:28 -0800 Subject: [PATCH 013/749] Introduce ABS as HIR nodes (missing file). NOTE: includes a file that should have been there. Bug: b/65164101 Test: test-art-host,target Change-Id: Ic786b84b2635ea8f5909ad77196857f6de65bf26 --- compiler/optimizing/loop_optimization.cc | 75 ++++++++++-------------- 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 899496328e..5a483e234b 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -1297,40 +1297,35 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node, return true; } } + } else if (instruction->IsAbs()) { + // Deal with vector restrictions. + HInstruction* opa = instruction->InputAt(0); + HInstruction* r = opa; + bool is_unsigned = false; + if (HasVectorRestrictions(restrictions, kNoAbs)) { + return false; + } else if (HasVectorRestrictions(restrictions, kNoHiBits) && + (!IsNarrowerOperand(opa, type, &r, &is_unsigned) || is_unsigned)) { + return false; // reject, unless operand is sign-extension narrower + } + // Accept ABS(x) for vectorizable operand. + DCHECK(r != nullptr); + if (generate_code && vector_mode_ != kVector) { // de-idiom + r = opa; + } + if (VectorizeUse(node, r, generate_code, type, restrictions)) { + if (generate_code) { + GenerateVecOp(instruction, + vector_map_->Get(r), + nullptr, + HVecOperation::ToProperType(type, is_unsigned)); + } + return true; + } } else if (instruction->IsInvokeStaticOrDirect()) { // Accept particular intrinsics. HInvokeStaticOrDirect* invoke = instruction->AsInvokeStaticOrDirect(); switch (invoke->GetIntrinsic()) { - case Intrinsics::kMathAbsInt: - case Intrinsics::kMathAbsLong: - case Intrinsics::kMathAbsFloat: - case Intrinsics::kMathAbsDouble: { - // Deal with vector restrictions. - HInstruction* opa = instruction->InputAt(0); - HInstruction* r = opa; - bool is_unsigned = false; - if (HasVectorRestrictions(restrictions, kNoAbs)) { - return false; - } else if (HasVectorRestrictions(restrictions, kNoHiBits) && - (!IsNarrowerOperand(opa, type, &r, &is_unsigned) || is_unsigned)) { - return false; // reject, unless operand is sign-extension narrower - } - // Accept ABS(x) for vectorizable operand. - DCHECK(r != nullptr); - if (generate_code && vector_mode_ != kVector) { // de-idiom - r = opa; - } - if (VectorizeUse(node, r, generate_code, type, restrictions)) { - if (generate_code) { - GenerateVecOp(instruction, - vector_map_->Get(r), - nullptr, - HVecOperation::ToProperType(type, is_unsigned)); - } - return true; - } - return false; - } case Intrinsics::kMathMinIntInt: case Intrinsics::kMathMinLongLong: case Intrinsics::kMathMinFloatFloat: @@ -1811,18 +1806,15 @@ void HLoopOptimization::GenerateVecOp(HInstruction* org, GENERATE_VEC( new (global_allocator_) HVecUShr(global_allocator_, opa, opb, type, vector_length_, dex_pc), new (global_allocator_) HUShr(org_type, opa, opb, dex_pc)); + case HInstruction::kAbs: + DCHECK(opb == nullptr); + GENERATE_VEC( + new (global_allocator_) HVecAbs(global_allocator_, opa, type, vector_length_, dex_pc), + new (global_allocator_) HAbs(org_type, opa, dex_pc)); case HInstruction::kInvokeStaticOrDirect: { HInvokeStaticOrDirect* invoke = org->AsInvokeStaticOrDirect(); if (vector_mode_ == kVector) { switch (invoke->GetIntrinsic()) { - case Intrinsics::kMathAbsInt: - case Intrinsics::kMathAbsLong: - case Intrinsics::kMathAbsFloat: - case Intrinsics::kMathAbsDouble: - DCHECK(opb == nullptr); - vector = new (global_allocator_) - HVecAbs(global_allocator_, opa, type, vector_length_, dex_pc); - break; case Intrinsics::kMathMinIntInt: case Intrinsics::kMathMinLongLong: case Intrinsics::kMathMinFloatFloat: @@ -1998,9 +1990,7 @@ bool HLoopOptimization::VectorizeSADIdiom(LoopNode* node, HInstruction* v = instruction->InputAt(1); HInstruction* a = nullptr; HInstruction* b = nullptr; - if (v->IsInvokeStaticOrDirect() && - (v->AsInvokeStaticOrDirect()->GetIntrinsic() == Intrinsics::kMathAbsInt || - v->AsInvokeStaticOrDirect()->GetIntrinsic() == Intrinsics::kMathAbsLong)) { + if (v->GetType() == reduction_type && v->IsAbs()) { HInstruction* x = v->InputAt(0); if (x->GetType() == reduction_type) { int64_t c = 0; @@ -2054,14 +2044,13 @@ bool HLoopOptimization::VectorizeSADIdiom(LoopNode* node, VectorizeUse(node, r, generate_code, sub_type, restrictions) && VectorizeUse(node, s, generate_code, sub_type, restrictions)) { if (generate_code) { - reduction_type = HVecOperation::ToProperType(reduction_type, is_unsigned); if (vector_mode_ == kVector) { vector_map_->Put(instruction, new (global_allocator_) HVecSADAccumulate( global_allocator_, vector_map_->Get(q), vector_map_->Get(r), vector_map_->Get(s), - reduction_type, + HVecOperation::ToProperType(reduction_type, is_unsigned), GetOtherVL(reduction_type, sub_type, vector_length_), kNoDexPc)); MaybeRecordStat(stats_, MethodCompilationStat::kLoopVectorizedIdiom); -- GitLab From c431b9dc4b23cc950eb313695258df5d89f53b22 Mon Sep 17 00:00:00 2001 From: David Sehr Date: Fri, 2 Mar 2018 12:01:51 -0800 Subject: [PATCH 014/749] Move most of runtime/base to libartbase/base Enforce the layering that code in runtime/base should not depend on runtime by separating it into libartbase. Some of the code in runtime/base depends on the Runtime class, so it cannot be moved yet. Also, some of the tests depend on CommonRuntimeTest, which itself needs to be factored (in a subsequent CL). Bug: 22322814 Test: make -j 50 checkbuild make -j 50 test-art-host Change-Id: I8b096c1e2542f829eb456b4b057c71421b77d7e2 --- cmdline/cmdline.h | 2 + cmdline/cmdline_parser_test.cc | 4 +- cmdline/cmdline_result.h | 2 +- compiler/common_compiler_test.cc | 2 +- compiler/compiler.cc | 2 +- compiler/compiler.h | 2 +- compiler/debug/dwarf/dwarf_test.h | 2 +- compiler/debug/elf_symtab_writer.h | 2 +- compiler/driver/compiled_method_storage.cc | 2 +- compiler/driver/compiler_driver.h | 3 +- compiler/driver/compiler_options.h | 2 +- compiler/driver/dex_compilation_unit.cc | 2 +- compiler/jni/quick/jni_compiler.cc | 2 +- compiler/linker/file_output_stream.h | 4 +- compiler/optimizing/codegen_test.cc | 2 +- compiler/optimizing/gvn.cc | 4 +- compiler/optimizing/intrinsics.cc | 2 +- compiler/optimizing/nodes.h | 1 + .../optimizing/optimizing_compiler_stats.h | 4 +- compiler/utils/assembler_test_base.h | 2 +- compiler/utils/atomic_dex_ref_map.h | 2 +- compiler/utils/swap_space_test.cc | 2 +- compiler/utils/test_dex_file_builder_test.cc | 2 +- dex2oat/dex2oat.cc | 6 +- dex2oat/dex2oat_image_test.cc | 8 +- dex2oat/dex2oat_options.h | 1 + dex2oat/dex2oat_test.cc | 2 +- dex2oat/linker/elf_writer.h | 2 +- dex2oat/linker/elf_writer_quick.cc | 2 +- dex2oat/linker/elf_writer_quick.h | 2 +- dex2oat/linker/elf_writer_test.cc | 2 +- dex2oat/linker/image_test.h | 2 +- dex2oat/linker/image_writer.h | 4 +- dex2oat/linker/oat_writer.cc | 2 +- dex2oat/linker/oat_writer.h | 2 +- dexdump/dexdump_test.cc | 4 +- dexlayout/compact_dex_writer.h | 2 +- dexlayout/dex_writer.h | 3 +- dexlayout/dexdiag.cc | 2 + dexlayout/dexdiag_test.cc | 2 +- dexlayout/dexlayout.cc | 4 +- dexlayout/dexlayout.h | 1 - dexlayout/dexlayout_test.cc | 2 +- dexlist/dexlist_test.cc | 4 +- dexoptanalyzer/dexoptanalyzer.cc | 7 +- imgdiag/imgdiag.cc | 2 +- imgdiag/imgdiag_test.cc | 4 +- libartbase/Android.bp | 86 ++++++- {runtime => libartbase}/base/aborting.h | 6 +- {runtime => libartbase}/base/allocator.cc | 2 +- {runtime => libartbase}/base/allocator.h | 9 +- {runtime => libartbase}/base/array_ref.h | 6 +- {runtime => libartbase}/base/array_slice.h | 6 +- {runtime => libartbase/base}/atomic.h | 163 +------------ {runtime => libartbase}/base/bit_field.h | 6 +- .../base/bit_field_test.cc | 0 {runtime => libartbase}/base/bit_string.h | 6 +- .../base/bit_string_test.cc | 0 {runtime => libartbase}/base/bit_struct.h | 6 +- .../base/bit_struct_detail.h | 6 +- .../base/bit_struct_test.cc | 0 {runtime => libartbase}/base/bit_vector-inl.h | 6 +- {runtime => libartbase}/base/bit_vector.cc | 0 {runtime => libartbase}/base/bit_vector.h | 6 +- .../base/bit_vector_test.cc | 0 {runtime => libartbase}/base/bounded_fifo.h | 6 +- .../base/callee_save_type.h | 6 +- .../base/dchecked_vector.h | 6 +- {runtime => libartbase}/base/debug_stack.h | 6 +- {runtime => libartbase}/base/file_magic.cc | 0 {runtime => libartbase}/base/file_magic.h | 8 +- {runtime => libartbase}/base/hex_dump.cc | 0 {runtime => libartbase}/base/hex_dump.h | 6 +- {runtime => libartbase}/base/hex_dump_test.cc | 0 {runtime => libartbase}/base/histogram-inl.h | 8 +- {runtime => libartbase}/base/histogram.h | 6 +- .../base/histogram_test.cc | 0 .../base/length_prefixed_array.h | 8 +- {runtime => libartbase}/base/logging.cc | 4 - {runtime => libartbase}/base/logging.h | 6 +- {runtime => libartbase}/base/logging_test.cc | 0 {runtime => libartbase/base}/os.h | 13 +- {runtime => libartbase/base}/os_linux.cc | 19 +- {runtime => libartbase}/base/runtime_debug.cc | 0 {runtime => libartbase}/base/runtime_debug.h | 6 +- {runtime => libartbase}/base/safe_copy.cc | 0 {runtime => libartbase}/base/safe_copy.h | 6 +- .../base/safe_copy_test.cc | 0 {runtime => libartbase}/base/scoped_flock.cc | 0 {runtime => libartbase}/base/scoped_flock.h | 8 +- .../base/scoped_flock_test.cc | 0 .../base}/stride_iterator.h | 6 +- {runtime => libartbase}/base/strlcpy.h | 6 +- {runtime => libartbase}/base/systrace.h | 6 +- {runtime => libartbase}/base/time_utils.cc | 0 {runtime => libartbase}/base/time_utils.h | 6 +- .../base/time_utils_test.cc | 0 {runtime => libartbase}/base/to_str.h | 6 +- .../base/tracking_safe_map.h | 6 +- .../base/transform_array_ref.h | 6 +- .../base/transform_array_ref_test.cc | 0 .../base/transform_iterator.h | 6 +- .../base/transform_iterator_test.cc | 0 {runtime => libartbase}/base/unix_file/README | 0 .../base/unix_file/fd_file.cc | 0 .../base/unix_file/fd_file.h | 6 +- .../base/unix_file/fd_file_test.cc | 0 .../base/unix_file/random_access_file.h | 6 +- .../base/unix_file/random_access_file_test.h | 6 +- .../unix_file/random_access_file_utils.cc | 0 .../base/unix_file/random_access_file_utils.h | 6 +- {runtime => libartbase/base}/utils.cc | 3 +- {runtime => libartbase/base}/utils.h | 12 +- libartbase/base/utils_test.cc | 129 ++++++++++ {runtime => libartbase}/base/variant_map.h | 6 +- .../base/variant_map_test.cc | 0 libdexfile/Android.bp | 5 + .../dex/dex_file_layout.cc | 20 +- {runtime => libdexfile}/dex/dex_file_layout.h | 9 +- oatdump/oatdump.cc | 2 +- oatdump/oatdump_test.h | 4 +- openjdkjvmti/ti_class.cc | 2 +- patchoat/patchoat.cc | 6 +- patchoat/patchoat.h | 2 +- profman/profile_assistant.cc | 2 +- profman/profile_assistant_test.cc | 2 +- profman/profman.cc | 4 +- runtime/Android.bp | 58 ++--- runtime/arch/arm/quick_entrypoints_cc_arm.cc | 2 +- runtime/arch/instruction_set_features.cc | 2 +- runtime/arch/mips/entrypoints_init_mips.cc | 3 +- .../arch/mips64/entrypoints_init_mips64.cc | 3 +- runtime/art_field.cc | 2 +- runtime/art_method-inl.h | 2 +- runtime/barrier_test.cc | 2 +- runtime/base/arena_allocator.cc | 2 +- runtime/base/arena_allocator.h | 4 +- runtime/base/file_utils.cc | 38 +-- runtime/base/file_utils.h | 10 - runtime/base/mutex-inl.h | 2 +- runtime/base/mutex.cc | 2 +- runtime/base/mutex.h | 5 +- runtime/{atomic.cc => base/quasi_atomic.cc} | 3 +- runtime/base/quasi_atomic.h | 190 +++++++++++++++ runtime/base/scoped_arena_allocator.h | 4 +- runtime/class_linker.cc | 5 +- runtime/class_linker_test.cc | 57 +++++ runtime/common_runtime_test.cc | 4 +- runtime/common_runtime_test.h | 2 +- runtime/compiler_filter.cc | 2 +- runtime/dex/art_dex_file_loader_test.cc | 2 +- runtime/dex2oat_environment_test.h | 4 +- runtime/elf_file.cc | 2 +- runtime/elf_file.h | 2 +- .../quick/quick_alloc_entrypoints.cc | 1 + runtime/gc/accounting/atomic_stack.h | 2 +- runtime/gc/accounting/bitmap-inl.h | 2 +- runtime/gc/accounting/card_table-inl.h | 2 +- runtime/gc/accounting/card_table.cc | 2 +- runtime/gc/accounting/card_table_test.cc | 4 +- runtime/gc/accounting/remembered_set.h | 3 +- runtime/gc/accounting/space_bitmap-inl.h | 2 +- runtime/gc/allocator/dlmalloc.cc | 4 +- runtime/gc/collector/concurrent_copying.cc | 1 + runtime/gc/collector/garbage_collector.cc | 2 +- runtime/gc/collector/mark_compact.h | 2 +- runtime/gc/collector/mark_sweep.h | 2 +- runtime/gc/collector/semi_space.h | 2 +- runtime/gc/heap-inl.h | 3 +- runtime/gc/heap.cc | 2 +- runtime/gc/heap.h | 2 +- runtime/gc/reference_processor.cc | 2 +- runtime/gc/reference_queue.h | 2 +- runtime/gc/space/dlmalloc_space.cc | 2 +- runtime/gc/space/image_space.cc | 4 +- runtime/gc/space/image_space_fs.h | 6 +- runtime/gc/space/large_object_space.cc | 2 +- runtime/gc/space/malloc_space.cc | 2 +- runtime/gc/space/rosalloc_space.cc | 2 +- runtime/gc/space/space.h | 2 +- runtime/gc/space/zygote_space.cc | 2 +- runtime/hprof/hprof.cc | 2 +- runtime/image.cc | 2 +- runtime/indirect_reference_table.cc | 2 +- runtime/instrumentation.cc | 2 +- runtime/intern_table.h | 2 +- .../interpreter/interpreter_switch_impl.cc | 1 + runtime/interpreter/mterp/mterp.cc | 2 + runtime/interpreter/unstarted_runtime.cc | 1 + runtime/jdwp/jdwp.h | 2 +- runtime/jdwp/jdwp_handler.cc | 2 +- runtime/jdwp/jdwp_main.cc | 2 +- runtime/jit/jit.cc | 2 +- runtime/jit/jit_code_cache.cc | 1 + runtime/jit/jit_code_cache.h | 2 +- runtime/jit/profile_compilation_info.cc | 6 +- runtime/jit/profile_compilation_info.h | 2 +- runtime/jni_internal.cc | 2 +- runtime/mem_map.cc | 4 +- runtime/mirror/array.cc | 2 +- runtime/mirror/call_site.h | 2 +- runtime/mirror/class-inl.h | 2 +- runtime/mirror/class.cc | 2 +- runtime/mirror/class.h | 4 +- runtime/mirror/class_ext.cc | 2 +- runtime/mirror/emulated_stack_frame.h | 2 +- runtime/mirror/method_handles_lookup.h | 2 +- runtime/mirror/method_type.h | 2 +- runtime/mirror/object-inl.h | 2 +- runtime/mirror/object-readbarrier-inl.h | 2 +- runtime/mirror/object.h | 2 +- runtime/mirror/object_array-inl.h | 2 +- runtime/mirror/object_reference.h | 2 +- runtime/mirror/object_test.cc | 18 ++ runtime/mirror/string-inl.h | 4 +- runtime/mirror/throwable.cc | 2 +- runtime/monitor.cc | 1 + runtime/monitor.h | 2 +- runtime/monitor_pool.h | 2 +- runtime/monitor_test.cc | 2 +- runtime/native/dalvik_system_DexFile.cc | 4 +- runtime/native/java_lang_reflect_Field.cc | 2 +- runtime/native/java_lang_reflect_Parameter.cc | 2 +- .../java_util_concurrent_atomic_AtomicLong.cc | 3 +- runtime/native/sun_misc_Unsafe.cc | 1 + runtime/native_stack_dump.cc | 4 +- runtime/oat_file.cc | 10 +- runtime/oat_file.h | 4 +- runtime/oat_file_assistant.cc | 4 +- runtime/oat_file_assistant.h | 2 +- runtime/oat_file_assistant_test.cc | 4 +- runtime/oat_quick_method_header.h | 2 +- runtime/parsed_options.cc | 2 +- runtime/read_barrier-inl.h | 2 +- runtime/reference_table.cc | 2 +- runtime/reflection-inl.h | 2 +- runtime/runtime.cc | 11 +- runtime/runtime_common.h | 2 +- runtime/runtime_options.cc | 2 +- runtime/signal_catcher.cc | 4 +- runtime/thread.cc | 2 +- runtime/thread.h | 2 +- runtime/thread_linux.cc | 2 +- runtime/thread_pool.cc | 2 +- runtime/thread_pool_test.cc | 2 +- runtime/trace.cc | 4 +- runtime/trace.h | 4 +- runtime/type_lookup_table.cc | 2 +- runtime/utils_test.cc | 221 ------------------ runtime/vdex_file.h | 2 +- runtime/verifier/method_verifier.cc | 2 +- runtime/verifier/method_verifier_test.cc | 2 +- runtime/zip_archive.h | 2 +- runtime/zip_archive_test.cc | 2 +- test/137-cfi/cfi.cc | 2 +- test/ti-stress/stress.cc | 2 +- tools/hiddenapi/hiddenapi.cc | 2 +- 257 files changed, 926 insertions(+), 820 deletions(-) rename {runtime => libartbase}/base/aborting.h (88%) rename {runtime => libartbase}/base/allocator.cc (98%) rename {runtime => libartbase}/base/allocator.h (96%) rename {runtime => libartbase}/base/array_ref.h (97%) rename {runtime => libartbase}/base/array_slice.h (97%) rename {runtime => libartbase/base}/atomic.h (64%) rename {runtime => libartbase}/base/bit_field.h (95%) rename {runtime => libartbase}/base/bit_field_test.cc (100%) rename {runtime => libartbase}/base/bit_string.h (98%) rename {runtime => libartbase}/base/bit_string_test.cc (100%) rename {runtime => libartbase}/base/bit_struct.h (98%) rename {runtime => libartbase}/base/bit_struct_detail.h (96%) rename {runtime => libartbase}/base/bit_struct_test.cc (100%) rename {runtime => libartbase}/base/bit_vector-inl.h (95%) rename {runtime => libartbase}/base/bit_vector.cc (100%) rename {runtime => libartbase}/base/bit_vector.h (98%) rename {runtime => libartbase}/base/bit_vector_test.cc (100%) rename {runtime => libartbase}/base/bounded_fifo.h (92%) rename {runtime => libartbase}/base/callee_save_type.h (92%) rename {runtime => libartbase}/base/dchecked_vector.h (98%) rename {runtime => libartbase}/base/debug_stack.h (97%) rename {runtime => libartbase}/base/file_magic.cc (100%) rename {runtime => libartbase}/base/file_magic.h (87%) rename {runtime => libartbase}/base/hex_dump.cc (100%) rename {runtime => libartbase}/base/hex_dump.h (92%) rename {runtime => libartbase}/base/hex_dump_test.cc (100%) rename {runtime => libartbase}/base/histogram-inl.h (98%) rename {runtime => libartbase}/base/histogram.h (97%) rename {runtime => libartbase}/base/histogram_test.cc (100%) rename {runtime => libartbase}/base/length_prefixed_array.h (95%) rename {runtime => libartbase}/base/logging.cc (98%) rename {runtime => libartbase}/base/logging.h (96%) rename {runtime => libartbase}/base/logging_test.cc (100%) rename {runtime => libartbase/base}/os.h (84%) rename {runtime => libartbase/base}/os_linux.cc (84%) rename {runtime => libartbase}/base/runtime_debug.cc (100%) rename {runtime => libartbase}/base/runtime_debug.h (93%) rename {runtime => libartbase}/base/safe_copy.cc (100%) rename {runtime => libartbase}/base/safe_copy.h (87%) rename {runtime => libartbase}/base/safe_copy_test.cc (100%) rename {runtime => libartbase}/base/scoped_flock.cc (100%) rename {runtime => libartbase}/base/scoped_flock.h (94%) rename {runtime => libartbase}/base/scoped_flock_test.cc (100%) rename {runtime => libartbase/base}/stride_iterator.h (96%) rename {runtime => libartbase}/base/strlcpy.h (90%) rename {runtime => libartbase}/base/systrace.h (93%) rename {runtime => libartbase}/base/time_utils.cc (100%) rename {runtime => libartbase}/base/time_utils.h (96%) rename {runtime => libartbase}/base/time_utils_test.cc (100%) rename {runtime => libartbase}/base/to_str.h (90%) rename {runtime => libartbase}/base/tracking_safe_map.h (86%) rename {runtime => libartbase}/base/transform_array_ref.h (97%) rename {runtime => libartbase}/base/transform_array_ref_test.cc (100%) rename {runtime => libartbase}/base/transform_iterator.h (97%) rename {runtime => libartbase}/base/transform_iterator_test.cc (100%) rename {runtime => libartbase}/base/unix_file/README (100%) rename {runtime => libartbase}/base/unix_file/fd_file.cc (100%) rename {runtime => libartbase}/base/unix_file/fd_file.h (97%) rename {runtime => libartbase}/base/unix_file/fd_file_test.cc (100%) rename {runtime => libartbase}/base/unix_file/random_access_file.h (92%) rename {runtime => libartbase}/base/unix_file/random_access_file_test.h (96%) rename {runtime => libartbase}/base/unix_file/random_access_file_utils.cc (100%) rename {runtime => libartbase}/base/unix_file/random_access_file_utils.h (81%) rename {runtime => libartbase/base}/utils.cc (99%) rename {runtime => libartbase/base}/utils.h (97%) create mode 100644 libartbase/base/utils_test.cc rename {runtime => libartbase}/base/variant_map.h (99%) rename {runtime => libartbase}/base/variant_map_test.cc (100%) rename {runtime => libdexfile}/dex/dex_file_layout.cc (82%) rename {runtime => libdexfile}/dex/dex_file_layout.h (93%) rename runtime/{atomic.cc => base/quasi_atomic.cc} (98%) create mode 100644 runtime/base/quasi_atomic.h delete mode 100644 runtime/utils_test.cc diff --git a/cmdline/cmdline.h b/cmdline/cmdline.h index 60dfdce03b..95ab12324c 100644 --- a/cmdline/cmdline.h +++ b/cmdline/cmdline.h @@ -28,6 +28,7 @@ #include "base/file_utils.h" #include "base/logging.h" +#include "base/mutex.h" #include "base/stringpiece.h" #include "noop_compiler_callbacks.h" #include "runtime.h" @@ -303,6 +304,7 @@ struct CmdlineArgs { template struct CmdlineMain { int Main(int argc, char** argv) { + Locks::Init(); InitLogging(argv, Runtime::Abort); std::unique_ptr args = std::unique_ptr(CreateArguments()); args_ = args.get(); diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index 3cb9731a17..235a2aa90e 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -20,12 +20,13 @@ #include "gtest/gtest.h" +#include "base/mutex.h" +#include "base/utils.h" #include "jdwp_provider.h" #include "experimental_flags.h" #include "parsed_options.h" #include "runtime.h" #include "runtime_options.h" -#include "utils.h" #define EXPECT_NULL(expected) EXPECT_EQ(reinterpret_cast(expected), \ reinterpret_cast(nullptr)); @@ -126,6 +127,7 @@ class CmdlineParserTest : public ::testing::Test { using RuntimeParser = ParsedOptions::RuntimeParser; static void SetUpTestCase() { + art::Locks::Init(); art::InitLogging(nullptr, art::Runtime::Abort); // argv = null } diff --git a/cmdline/cmdline_result.h b/cmdline/cmdline_result.h index e41043abf0..0ae1145a52 100644 --- a/cmdline/cmdline_result.h +++ b/cmdline/cmdline_result.h @@ -18,7 +18,7 @@ #define ART_CMDLINE_CMDLINE_RESULT_H_ #include -#include +#include "base/utils.h" namespace art { // Result of an attempt to process the command line arguments. If fails, specifies diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index a20313374c..d3e3a51f7a 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -21,6 +21,7 @@ #include "art_method-inl.h" #include "base/callee_save_type.h" #include "base/enums.h" +#include "base/utils.h" #include "class_linker.h" #include "compiled_method-inl.h" #include "dex/descriptors_names.h" @@ -36,7 +37,6 @@ #include "oat_quick_method_header.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" -#include "utils.h" namespace art { diff --git a/compiler/compiler.cc b/compiler/compiler.cc index 7c7ae71d77..646040fd9d 100644 --- a/compiler/compiler.cc +++ b/compiler/compiler.cc @@ -19,10 +19,10 @@ #include #include "base/macros.h" +#include "base/utils.h" #include "dex/code_item_accessors-inl.h" #include "driver/compiler_driver.h" #include "optimizing/optimizing_compiler.h" -#include "utils.h" namespace art { diff --git a/compiler/compiler.h b/compiler/compiler.h index a17e2b5875..f2ec3a9fa3 100644 --- a/compiler/compiler.h +++ b/compiler/compiler.h @@ -18,8 +18,8 @@ #define ART_COMPILER_COMPILER_H_ #include "base/mutex.h" +#include "base/os.h" #include "dex/dex_file.h" -#include "os.h" namespace art { diff --git a/compiler/debug/dwarf/dwarf_test.h b/compiler/debug/dwarf/dwarf_test.h index 5405759c1f..9a7c604ca1 100644 --- a/compiler/debug/dwarf/dwarf_test.h +++ b/compiler/debug/dwarf/dwarf_test.h @@ -26,12 +26,12 @@ #include #include +#include "base/os.h" #include "base/unix_file/fd_file.h" #include "common_runtime_test.h" #include "gtest/gtest.h" #include "linker/elf_builder.h" #include "linker/file_output_stream.h" -#include "os.h" namespace art { namespace dwarf { diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h index 1310e8dabd..7a8e29191a 100644 --- a/compiler/debug/elf_symtab_writer.h +++ b/compiler/debug/elf_symtab_writer.h @@ -20,12 +20,12 @@ #include #include +#include "base/utils.h" #include "debug/debug_info.h" #include "debug/method_debug_info.h" #include "dex/dex_file-inl.h" #include "dex/code_item_accessors.h" #include "linker/elf_builder.h" -#include "utils.h" namespace art { namespace debug { diff --git a/compiler/driver/compiled_method_storage.cc b/compiler/driver/compiled_method_storage.cc index 48477abe5b..a26a985ff9 100644 --- a/compiler/driver/compiled_method_storage.cc +++ b/compiler/driver/compiled_method_storage.cc @@ -21,10 +21,10 @@ #include +#include "base/utils.h" #include "compiled_method.h" #include "linker/linker_patch.h" #include "thread-current-inl.h" -#include "utils.h" #include "utils/dedupe_set-inl.h" #include "utils/swap_space.h" diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index a3bb4ec34d..8db892bf18 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -29,6 +29,8 @@ #include "base/array_ref.h" #include "base/bit_utils.h" #include "base/mutex.h" +#include "base/os.h" +#include "base/quasi_atomic.h" #include "base/safe_map.h" #include "base/timing_logger.h" #include "class_reference.h" @@ -39,7 +41,6 @@ #include "dex/dex_to_dex_compiler.h" #include "driver/compiled_method_storage.h" #include "method_reference.h" -#include "os.h" #include "thread_pool.h" #include "utils/atomic_dex_ref_map.h" #include "utils/dex_cache_arrays_layout.h" diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 18b0913430..05d8805e81 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -22,10 +22,10 @@ #include #include "base/macros.h" +#include "base/utils.h" #include "compiler_filter.h" #include "globals.h" #include "optimizing/register_allocator.h" -#include "utils.h" namespace art { diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc index 2e315b5d12..c90c37d54a 100644 --- a/compiler/driver/dex_compilation_unit.cc +++ b/compiler/driver/dex_compilation_unit.cc @@ -16,10 +16,10 @@ #include "dex_compilation_unit.h" +#include "base/utils.h" #include "dex/code_item_accessors-inl.h" #include "dex/descriptors_names.h" #include "mirror/dex_cache.h" -#include "utils.h" namespace art { diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index fc44927231..d001cfe4fc 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -27,6 +27,7 @@ #include "base/enums.h" #include "base/logging.h" // For VLOG. #include "base/macros.h" +#include "base/utils.h" #include "calling_convention.h" #include "class_linker.h" #include "debug/dwarf/debug_frame_opcode_writer.h" @@ -37,7 +38,6 @@ #include "jni_env_ext.h" #include "memory_region.h" #include "thread.h" -#include "utils.h" #include "utils/arm/managed_register_arm.h" #include "utils/arm64/managed_register_arm64.h" #include "utils/assembler.h" diff --git a/compiler/linker/file_output_stream.h b/compiler/linker/file_output_stream.h index 28296a47fd..deb051fca4 100644 --- a/compiler/linker/file_output_stream.h +++ b/compiler/linker/file_output_stream.h @@ -17,9 +17,9 @@ #ifndef ART_COMPILER_LINKER_FILE_OUTPUT_STREAM_H_ #define ART_COMPILER_LINKER_FILE_OUTPUT_STREAM_H_ -#include "output_stream.h" +#include "base/os.h" -#include "os.h" +#include "output_stream.h" namespace art { namespace linker { diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc index ba4040acad..a0fd5ffcb1 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -18,6 +18,7 @@ #include #include "base/macros.h" +#include "base/utils.h" #include "builder.h" #include "codegen_test_utils.h" #include "dex/dex_file.h" @@ -26,7 +27,6 @@ #include "nodes.h" #include "optimizing_unit_test.h" #include "register_allocator_linear_scan.h" -#include "utils.h" #include "utils/arm/assembler_arm_vixl.h" #include "utils/arm/managed_register_arm.h" #include "utils/mips/managed_register_mips.h" diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc index 71c394ec1f..f05159b735 100644 --- a/compiler/optimizing/gvn.cc +++ b/compiler/optimizing/gvn.cc @@ -17,11 +17,11 @@ #include "gvn.h" #include "base/arena_bit_vector.h" +#include "base/bit_vector-inl.h" #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" -#include "base/bit_vector-inl.h" +#include "base/utils.h" #include "side_effects_analysis.h" -#include "utils.h" namespace art { diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index acb830e524..f8dc316e45 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -18,6 +18,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" +#include "base/utils.h" #include "class_linker.h" #include "dex/invoke_type.h" #include "driver/compiler_driver.h" @@ -26,7 +27,6 @@ #include "nodes.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" -#include "utils.h" namespace art { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 99d80d77c5..62550be526 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -26,6 +26,7 @@ #include "base/arena_object.h" #include "base/array_ref.h" #include "base/iteration_range.h" +#include "base/quasi_atomic.h" #include "base/stl_util.h" #include "base/transform_array_ref.h" #include "data_type.h" diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 0023265e50..00194ff1fe 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -22,9 +22,9 @@ #include #include -#include "atomic.h" +#include "base/atomic.h" +#include "base/globals.h" #include "base/logging.h" // For VLOG_IS_ON. -#include "globals.h" namespace art { diff --git a/compiler/utils/assembler_test_base.h b/compiler/utils/assembler_test_base.h index 1482210ac4..778a01566c 100644 --- a/compiler/utils/assembler_test_base.h +++ b/compiler/utils/assembler_test_base.h @@ -25,9 +25,9 @@ #include "android-base/strings.h" +#include "base/utils.h" #include "common_runtime_test.h" // For ScratchFile #include "exec_utils.h" -#include "utils.h" namespace art { diff --git a/compiler/utils/atomic_dex_ref_map.h b/compiler/utils/atomic_dex_ref_map.h index cabd79e9f1..fc2437999e 100644 --- a/compiler/utils/atomic_dex_ref_map.h +++ b/compiler/utils/atomic_dex_ref_map.h @@ -17,7 +17,7 @@ #ifndef ART_COMPILER_UTILS_ATOMIC_DEX_REF_MAP_H_ #define ART_COMPILER_UTILS_ATOMIC_DEX_REF_MAP_H_ -#include "atomic.h" +#include "base/atomic.h" #include "base/dchecked_vector.h" #include "base/safe_map.h" #include "dex/dex_file_reference.h" diff --git a/compiler/utils/swap_space_test.cc b/compiler/utils/swap_space_test.cc index f4bca59cb3..1650080e66 100644 --- a/compiler/utils/swap_space_test.cc +++ b/compiler/utils/swap_space_test.cc @@ -24,9 +24,9 @@ #include "gtest/gtest.h" +#include "base/os.h" #include "base/unix_file/fd_file.h" #include "common_runtime_test.h" -#include "os.h" namespace art { diff --git a/compiler/utils/test_dex_file_builder_test.cc b/compiler/utils/test_dex_file_builder_test.cc index 736a17edac..788afd8e1a 100644 --- a/compiler/utils/test_dex_file_builder_test.cc +++ b/compiler/utils/test_dex_file_builder_test.cc @@ -16,9 +16,9 @@ #include "test_dex_file_builder.h" +#include "base/utils.h" #include "dex/dex_file-inl.h" #include "gtest/gtest.h" -#include "utils.h" namespace art { diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 926575e10c..73afbad184 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -46,12 +46,15 @@ #include "base/file_utils.h" #include "base/leb128.h" #include "base/macros.h" +#include "base/mutex.h" +#include "base/os.h" #include "base/scoped_flock.h" #include "base/stl_util.h" #include "base/stringpiece.h" #include "base/time_utils.h" #include "base/timing_logger.h" #include "base/unix_file/fd_file.h" +#include "base/utils.h" #include "class_linker.h" #include "class_loader_context.h" #include "cmdline_parser.h" @@ -89,11 +92,9 @@ #include "mirror/object_array-inl.h" #include "oat_file.h" #include "oat_file_assistant.h" -#include "os.h" #include "runtime.h" #include "runtime_options.h" #include "scoped_thread_state_change-inl.h" -#include "utils.h" #include "vdex_file.h" #include "verifier/verifier_deps.h" #include "well_known_classes.h" @@ -1164,6 +1165,7 @@ class Dex2Oat FINAL { original_argc = argc; original_argv = argv; + Locks::Init(); InitLogging(argv, Runtime::Abort); compiler_options_.reset(new CompilerOptions()); diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc index 05592f1806..49b84bb0c6 100644 --- a/dex2oat/dex2oat_image_test.cc +++ b/dex2oat/dex2oat_image_test.cc @@ -29,13 +29,13 @@ #include "base/file_utils.h" #include "base/macros.h" #include "base/unix_file/fd_file.h" +#include "base/utils.h" #include "dex/art_dex_file_loader.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "jit/profile_compilation_info.h" #include "method_reference.h" #include "runtime.h" -#include "utils.h" namespace art { @@ -129,9 +129,9 @@ class Dex2oatImageTest : public CommonRuntimeTest { std::string art_file = scratch.GetFilename() + ".art"; std::string oat_file = scratch.GetFilename() + ".oat"; std::string vdex_file = scratch.GetFilename() + ".vdex"; - ret.art_size = GetFileSizeBytes(art_file); - ret.oat_size = GetFileSizeBytes(oat_file); - ret.vdex_size = GetFileSizeBytes(vdex_file); + ret.art_size = OS::GetFileSizeBytes(art_file.c_str()); + ret.oat_size = OS::GetFileSizeBytes(oat_file.c_str()); + ret.vdex_size = OS::GetFileSizeBytes(vdex_file.c_str()); CHECK_GT(ret.art_size, 0u) << art_file; CHECK_GT(ret.oat_size, 0u) << oat_file; CHECK_GT(ret.vdex_size, 0u) << vdex_file; diff --git a/dex2oat/dex2oat_options.h b/dex2oat/dex2oat_options.h index ccc85c8951..cc124c1afa 100644 --- a/dex2oat/dex2oat_options.h +++ b/dex2oat/dex2oat_options.h @@ -21,6 +21,7 @@ #include #include +#include "arch/instruction_set.h" #include "base/variant_map.h" #include "cmdline_types.h" // TODO: don't need to include this file here #include "compiler.h" diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 6b75595005..96dd319946 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -29,6 +29,7 @@ #include "base/macros.h" #include "base/mutex-inl.h" +#include "base/utils.h" #include "bytecode_utils.h" #include "dex/art_dex_file_loader.h" #include "dex/base64_test_util.h" @@ -40,7 +41,6 @@ #include "jit/profile_compilation_info.h" #include "oat.h" #include "oat_file.h" -#include "utils.h" #include "vdex_file.h" #include "ziparchive/zip_writer.h" diff --git a/dex2oat/linker/elf_writer.h b/dex2oat/linker/elf_writer.h index 7c4774038e..bcf2cd7d4b 100644 --- a/dex2oat/linker/elf_writer.h +++ b/dex2oat/linker/elf_writer.h @@ -25,8 +25,8 @@ #include "base/array_ref.h" #include "base/macros.h" #include "base/mutex.h" +#include "base/os.h" #include "debug/debug_info.h" -#include "os.h" namespace art { diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc index d2e863c2a8..07b02f1033 100644 --- a/dex2oat/linker/elf_writer_quick.cc +++ b/dex2oat/linker/elf_writer_quick.cc @@ -24,6 +24,7 @@ #include "base/casts.h" #include "base/leb128.h" +#include "base/utils.h" #include "compiled_method.h" #include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" @@ -36,7 +37,6 @@ #include "linker/file_output_stream.h" #include "thread-current-inl.h" #include "thread_pool.h" -#include "utils.h" namespace art { namespace linker { diff --git a/dex2oat/linker/elf_writer_quick.h b/dex2oat/linker/elf_writer_quick.h index e20957c5ce..274d18b858 100644 --- a/dex2oat/linker/elf_writer_quick.h +++ b/dex2oat/linker/elf_writer_quick.h @@ -20,8 +20,8 @@ #include #include "arch/instruction_set.h" +#include "base/os.h" #include "elf_writer.h" -#include "os.h" namespace art { diff --git a/dex2oat/linker/elf_writer_test.cc b/dex2oat/linker/elf_writer_test.cc index 8427e7b8ce..b2be003b5d 100644 --- a/dex2oat/linker/elf_writer_test.cc +++ b/dex2oat/linker/elf_writer_test.cc @@ -18,13 +18,13 @@ #include "base/file_utils.h" #include "base/unix_file/fd_file.h" +#include "base/utils.h" #include "common_compiler_test.h" #include "elf_file.h" #include "elf_file_impl.h" #include "elf_writer_quick.h" #include "linker/elf_builder.h" #include "oat.h" -#include "utils.h" namespace art { namespace linker { diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index c6ce951506..319c5fb675 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -28,6 +28,7 @@ #include "art_method-inl.h" #include "base/file_utils.h" #include "base/unix_file/fd_file.h" +#include "base/utils.h" #include "class_linker-inl.h" #include "common_compiler_test.h" #include "compiler_callbacks.h" @@ -46,7 +47,6 @@ #include "oat_writer.h" #include "scoped_thread_state_change-inl.h" #include "signal_catcher.h" -#include "utils.h" namespace art { namespace linker { diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h index 856edfb3bc..36bbb47786 100644 --- a/dex2oat/linker/image_writer.h +++ b/dex2oat/linker/image_writer.h @@ -33,7 +33,9 @@ #include "base/enums.h" #include "base/length_prefixed_array.h" #include "base/macros.h" +#include "base/os.h" #include "base/safe_map.h" +#include "base/utils.h" #include "class_table.h" #include "driver/compiler_driver.h" #include "image.h" @@ -43,8 +45,6 @@ #include "mirror/dex_cache.h" #include "oat_file.h" #include "obj_ptr.h" -#include "os.h" -#include "utils.h" namespace art { namespace gc { diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 2feb14a357..c72beea6ce 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -27,6 +27,7 @@ #include "base/enums.h" #include "base/file_magic.h" #include "base/logging.h" // For VLOG +#include "base/os.h" #include "base/safe_map.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" @@ -60,7 +61,6 @@ #include "mirror/dex_cache-inl.h" #include "mirror/object-inl.h" #include "oat_quick_method_header.h" -#include "os.h" #include "quicken_info.h" #include "scoped_thread_state_change-inl.h" #include "type_lookup_table.h" diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index d67e4dedfc..0cb0ef20df 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -24,6 +24,7 @@ #include "base/array_ref.h" #include "base/dchecked_vector.h" +#include "base/os.h" #include "base/safe_map.h" #include "compiler.h" #include "dex/compact_dex_level.h" @@ -33,7 +34,6 @@ #include "method_reference.h" #include "mirror/class.h" #include "oat.h" -#include "os.h" #include "string_reference.h" #include "type_reference.h" diff --git a/dexdump/dexdump_test.cc b/dexdump/dexdump_test.cc index 559dc8e781..63e0e3f20d 100644 --- a/dexdump/dexdump_test.cc +++ b/dexdump/dexdump_test.cc @@ -22,10 +22,10 @@ #include #include "arch/instruction_set.h" +#include "base/os.h" +#include "base/utils.h" #include "common_runtime_test.h" #include "exec_utils.h" -#include "os.h" -#include "utils.h" namespace art { diff --git a/dexlayout/compact_dex_writer.h b/dexlayout/compact_dex_writer.h index 0e7d65f9e4..eaf85185f1 100644 --- a/dexlayout/compact_dex_writer.h +++ b/dexlayout/compact_dex_writer.h @@ -22,8 +22,8 @@ #include // For unique_ptr #include +#include "base/utils.h" #include "dex_writer.h" -#include "utils.h" namespace art { diff --git a/dexlayout/dex_writer.h b/dexlayout/dex_writer.h index df098c0f6f..db1898bf26 100644 --- a/dexlayout/dex_writer.h +++ b/dexlayout/dex_writer.h @@ -22,13 +22,12 @@ #include #include // For unique_ptr +#include "base/os.h" #include "base/unix_file/fd_file.h" #include "dex/compact_dex_level.h" #include "dex_container.h" #include "dex/dex_file.h" #include "dex_ir.h" -#include "mem_map.h" -#include "os.h" #include diff --git a/dexlayout/dexdiag.cc b/dexlayout/dexdiag.cc index c0d6f02c00..6cb141f688 100644 --- a/dexlayout/dexdiag.cc +++ b/dexlayout/dexdiag.cc @@ -27,6 +27,7 @@ #include "android-base/stringprintf.h" #include "base/logging.h" // For InitLogging. +#include "base/mutex.h" #include "base/stringpiece.h" #include "dexlayout.h" @@ -470,6 +471,7 @@ static int DexDiagMain(int argc, char* argv[]) { } // Art specific set up. + Locks::Init(); InitLogging(argv, Runtime::Abort); MemMap::Init(); diff --git a/dexlayout/dexdiag_test.cc b/dexlayout/dexdiag_test.cc index 9927576400..068949ceba 100644 --- a/dexlayout/dexdiag_test.cc +++ b/dexlayout/dexdiag_test.cc @@ -20,9 +20,9 @@ #include "common_runtime_test.h" #include "base/file_utils.h" +#include "base/os.h" #include "exec_utils.h" #include "oat_file.h" -#include "os.h" namespace art { diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index 159a4a5ec2..ec0cbe6a60 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -34,6 +34,8 @@ #include "android-base/stringprintf.h" #include "base/logging.h" // For VLOG_IS_ON. +#include "base/os.h" +#include "base/utils.h" #include "dex/art_dex_file_loader.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" @@ -48,8 +50,6 @@ #include "dex_writer.h" #include "jit/profile_compilation_info.h" #include "mem_map.h" -#include "os.h" -#include "utils.h" namespace art { diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h index b79a59209d..2bca10ddfb 100644 --- a/dexlayout/dexlayout.h +++ b/dexlayout/dexlayout.h @@ -31,7 +31,6 @@ #include "dex_container.h" #include "dex/dex_file_layout.h" #include "dex_ir.h" -#include "mem_map.h" namespace art { diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index 981f9010ee..6719d0848c 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -22,6 +22,7 @@ #include #include "base/unix_file/fd_file.h" +#include "base/utils.h" #include "common_runtime_test.h" #include "dex/art_dex_file_loader.h" #include "dex/base64_test_util.h" @@ -31,7 +32,6 @@ #include "dexlayout.h" #include "exec_utils.h" #include "jit/profile_compilation_info.h" -#include "utils.h" namespace art { diff --git a/dexlist/dexlist_test.cc b/dexlist/dexlist_test.cc index ae44848461..0b9adbddb8 100644 --- a/dexlist/dexlist_test.cc +++ b/dexlist/dexlist_test.cc @@ -22,12 +22,12 @@ #include #include "arch/instruction_set.h" +#include "base/os.h" +#include "base/utils.h" #include "common_runtime_test.h" #include "exec_utils.h" #include "gc/heap.h" #include "gc/space/image_space.h" -#include "os.h" -#include "utils.h" namespace art { diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc index 6d4b3e3e52..febccf1950 100644 --- a/dexoptanalyzer/dexoptanalyzer.cc +++ b/dexoptanalyzer/dexoptanalyzer.cc @@ -17,19 +17,19 @@ #include #include "base/logging.h" // For InitLogging. +#include "base/mutex.h" +#include "base/os.h" +#include "base/utils.h" #include "android-base/stringprintf.h" #include "android-base/strings.h" #include "base/file_utils.h" -#include "base/logging.h" // For InitLogging. #include "compiler_filter.h" #include "class_loader_context.h" #include "dex/dex_file.h" #include "noop_compiler_callbacks.h" #include "oat_file_assistant.h" -#include "os.h" #include "runtime.h" #include "thread-inl.h" -#include "utils.h" namespace art { @@ -142,6 +142,7 @@ class DexoptAnalyzer FINAL { original_argc = argc; original_argv = argv; + Locks::Init(); InitLogging(argv, Runtime::Abort); // Skip over the command name. argv++; diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc index 8aa638a0e0..ddb8fe1302 100644 --- a/imgdiag/imgdiag.cc +++ b/imgdiag/imgdiag.cc @@ -30,6 +30,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" +#include "base/os.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" #include "gc/heap.h" @@ -40,7 +41,6 @@ #include "oat.h" #include "oat_file.h" #include "oat_file_manager.h" -#include "os.h" #include "scoped_thread_state_change-inl.h" #include "backtrace/BacktraceMap.h" diff --git a/imgdiag/imgdiag_test.cc b/imgdiag/imgdiag_test.cc index 80b0c26b7f..52096f0d7b 100644 --- a/imgdiag/imgdiag_test.cc +++ b/imgdiag/imgdiag_test.cc @@ -24,13 +24,13 @@ #include "android-base/stringprintf.h" #include "arch/instruction_set.h" +#include "base/os.h" +#include "base/utils.h" #include "common_runtime_test.h" #include "exec_utils.h" #include "gc/heap.h" #include "gc/space/image_space.h" -#include "os.h" #include "runtime.h" -#include "utils.h" namespace art { diff --git a/libartbase/Android.bp b/libartbase/Android.bp index 290d39844c..3c61944477 100644 --- a/libartbase/Android.bp +++ b/libartbase/Android.bp @@ -14,15 +14,100 @@ // limitations under the License. // +cc_defaults { + name: "libartbase_defaults", + defaults: ["art_defaults"], + host_supported: true, + srcs: [ + "base/allocator.cc", + "base/bit_vector.cc", + "base/file_magic.cc", + "base/hex_dump.cc", + "base/logging.cc", + "base/os_linux.cc", + "base/runtime_debug.cc", + "base/safe_copy.cc", + "base/scoped_flock.cc", + "base/time_utils.cc", + "base/unix_file/fd_file.cc", + "base/unix_file/random_access_file_utils.cc", + "base/utils.cc", + ], + generated_sources: ["art_libartbase_operator_srcs"], + cflags: ["-DBUILDING_LIBART=1"], + shared_libs: [ + // For common macros. + "libbase", + "liblog", + ], + export_include_dirs: ["."], + // ART's macros.h depends on libbase's macros.h. + // Note: runtime_options.h depends on cmdline. But we don't really want to export this + // generically. dex2oat takes care of it itself. + export_shared_lib_headers: ["libbase"], +} + +gensrcs { + name: "art_libartbase_operator_srcs", + cmd: "$(location generate_operator_out) art/libartbase $(in) > $(out)", + tools: ["generate_operator_out"], + srcs: [ + "base/allocator.h", + "base/callee_save_type.h", + "base/unix_file/fd_file.h", + ], + output_extension: "operator_out.cc", +} + +art_cc_library { + name: "libartbase", + defaults: ["libartbase_defaults"], + // Leave the symbols in the shared library so that stack unwinders can + // produce meaningful name resolution. + strip: { + keep_symbols: true, + }, + shared_libs: ["libbase"], + export_shared_lib_headers: ["libbase"], +} + +art_cc_library { + name: "libartbased", + defaults: [ + "art_debug_defaults", + "libartbase_defaults", + ], + shared_libs: ["libbase"], + export_shared_lib_headers: ["libbase"], +} + +// For now many of these tests still use CommonRuntimeTest, almost universally because of +// ScratchFile and related. +// TODO: Remove CommonRuntimeTest dependency from these tests. art_cc_test { name: "art_libartbase_tests", defaults: [ "art_gtest_defaults", ], srcs: [ + "base/bit_field_test.cc", + "base/bit_string_test.cc", + "base/bit_struct_test.cc", "base/bit_utils_test.cc", + "base/bit_vector_test.cc", "base/hash_set_test.cc", + "base/hex_dump_test.cc", + "base/histogram_test.cc", "base/leb128_test.cc", + "base/logging_test.cc", + "base/safe_copy_test.cc", + "base/scoped_flock_test.cc", + "base/time_utils_test.cc", + "base/transform_array_ref_test.cc", + "base/transform_iterator_test.cc", + "base/unix_file/fd_file_test.cc", + "base/utils_test.cc", + "base/variant_map_test.cc", ], shared_libs: [ "libbase", @@ -36,4 +121,3 @@ cc_library_headers { shared_libs: ["libbase"], export_shared_lib_headers: ["libbase"], } - diff --git a/runtime/base/aborting.h b/libartbase/base/aborting.h similarity index 88% rename from runtime/base/aborting.h rename to libartbase/base/aborting.h index 8906c96ea7..c7089af695 100644 --- a/runtime/base/aborting.h +++ b/libartbase/base/aborting.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_ABORTING_H_ -#define ART_RUNTIME_BASE_ABORTING_H_ +#ifndef ART_LIBARTBASE_BASE_ABORTING_H_ +#define ART_LIBARTBASE_BASE_ABORTING_H_ #include @@ -28,4 +28,4 @@ extern std::atomic gAborting; } // namespace art -#endif // ART_RUNTIME_BASE_ABORTING_H_ +#endif // ART_LIBARTBASE_BASE_ABORTING_H_ diff --git a/runtime/base/allocator.cc b/libartbase/base/allocator.cc similarity index 98% rename from runtime/base/allocator.cc rename to libartbase/base/allocator.cc index 2da88c3830..a42414507b 100644 --- a/runtime/base/allocator.cc +++ b/libartbase/base/allocator.cc @@ -21,7 +21,7 @@ #include -#include "atomic.h" +#include "base/atomic.h" namespace art { diff --git a/runtime/base/allocator.h b/libartbase/base/allocator.h similarity index 96% rename from runtime/base/allocator.h rename to libartbase/base/allocator.h index 3cedb66abe..d92fe193e6 100644 --- a/runtime/base/allocator.h +++ b/libartbase/base/allocator.h @@ -14,14 +14,13 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_ALLOCATOR_H_ -#define ART_RUNTIME_BASE_ALLOCATOR_H_ +#ifndef ART_LIBARTBASE_BASE_ALLOCATOR_H_ +#define ART_LIBARTBASE_BASE_ALLOCATOR_H_ #include -#include "atomic.h" +#include "base/atomic.h" #include "base/macros.h" -#include "base/mutex.h" namespace art { @@ -154,4 +153,4 @@ using TrackingAllocator = typename std::conditional #include @@ -206,4 +206,4 @@ bool operator!=(const ArrayRef& lhs, const ArrayRef& rhs) { } // namespace art -#endif // ART_RUNTIME_BASE_ARRAY_REF_H_ +#endif // ART_LIBARTBASE_BASE_ARRAY_REF_H_ diff --git a/runtime/base/array_slice.h b/libartbase/base/array_slice.h similarity index 97% rename from runtime/base/array_slice.h rename to libartbase/base/array_slice.h index a7bce7dc56..1ef2fbad8b 100644 --- a/runtime/base/array_slice.h +++ b/libartbase/base/array_slice.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_ARRAY_SLICE_H_ -#define ART_RUNTIME_BASE_ARRAY_SLICE_H_ +#ifndef ART_LIBARTBASE_BASE_ARRAY_SLICE_H_ +#define ART_LIBARTBASE_BASE_ARRAY_SLICE_H_ #include "base/bit_utils.h" #include "base/casts.h" @@ -149,4 +149,4 @@ class ArraySlice { } // namespace art -#endif // ART_RUNTIME_BASE_ARRAY_SLICE_H_ +#endif // ART_LIBARTBASE_BASE_ARRAY_SLICE_H_ diff --git a/runtime/atomic.h b/libartbase/base/atomic.h similarity index 64% rename from runtime/atomic.h rename to libartbase/base/atomic.h index 0e2f056d3a..fd34cc6143 100644 --- a/runtime/atomic.h +++ b/libartbase/base/atomic.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_ATOMIC_H_ -#define ART_RUNTIME_ATOMIC_H_ +#ifndef ART_LIBARTBASE_BASE_ATOMIC_H_ +#define ART_LIBARTBASE_BASE_ATOMIC_H_ #include #include @@ -24,167 +24,10 @@ #include -#include "arch/instruction_set.h" #include "base/macros.h" namespace art { -class Mutex; - -// QuasiAtomic encapsulates two separate facilities that we are -// trying to move away from: "quasiatomic" 64 bit operations -// and custom memory fences. For the time being, they remain -// exposed. Clients should be converted to use either class Atomic -// below whenever possible, and should eventually use C++11 atomics. -// The two facilities that do not have a good C++11 analog are -// ThreadFenceForConstructor and Atomic::*JavaData. -// -// NOTE: Two "quasiatomic" operations on the exact same memory address -// are guaranteed to operate atomically with respect to each other, -// but no guarantees are made about quasiatomic operations mixed with -// non-quasiatomic operations on the same address, nor about -// quasiatomic operations that are performed on partially-overlapping -// memory. -class QuasiAtomic { - static constexpr bool NeedSwapMutexes(InstructionSet isa) { - // TODO - mips64 still need this for Cas64 ??? - return (isa == InstructionSet::kMips) || (isa == InstructionSet::kMips64); - } - - public: - static void Startup(); - - static void Shutdown(); - - // Reads the 64-bit value at "addr" without tearing. - static int64_t Read64(volatile const int64_t* addr) { - if (!NeedSwapMutexes(kRuntimeISA)) { - int64_t value; -#if defined(__LP64__) - value = *addr; -#else -#if defined(__arm__) -#if defined(__ARM_FEATURE_LPAE) - // With LPAE support (such as Cortex-A15) then ldrd is defined not to tear. - __asm__ __volatile__("@ QuasiAtomic::Read64\n" - "ldrd %0, %H0, %1" - : "=r" (value) - : "m" (*addr)); -#else - // Exclusive loads are defined not to tear, clearing the exclusive state isn't necessary. - __asm__ __volatile__("@ QuasiAtomic::Read64\n" - "ldrexd %0, %H0, %1" - : "=r" (value) - : "Q" (*addr)); -#endif -#elif defined(__i386__) - __asm__ __volatile__( - "movq %1, %0\n" - : "=x" (value) - : "m" (*addr)); -#else - LOG(FATAL) << "Unsupported architecture"; -#endif -#endif // defined(__LP64__) - return value; - } else { - return SwapMutexRead64(addr); - } - } - - // Writes to the 64-bit value at "addr" without tearing. - static void Write64(volatile int64_t* addr, int64_t value) { - if (!NeedSwapMutexes(kRuntimeISA)) { -#if defined(__LP64__) - *addr = value; -#else -#if defined(__arm__) -#if defined(__ARM_FEATURE_LPAE) - // If we know that ARM architecture has LPAE (such as Cortex-A15) strd is defined not to tear. - __asm__ __volatile__("@ QuasiAtomic::Write64\n" - "strd %1, %H1, %0" - : "=m"(*addr) - : "r" (value)); -#else - // The write is done as a swap so that the cache-line is in the exclusive state for the store. - int64_t prev; - int status; - do { - __asm__ __volatile__("@ QuasiAtomic::Write64\n" - "ldrexd %0, %H0, %2\n" - "strexd %1, %3, %H3, %2" - : "=&r" (prev), "=&r" (status), "+Q"(*addr) - : "r" (value) - : "cc"); - } while (UNLIKELY(status != 0)); -#endif -#elif defined(__i386__) - __asm__ __volatile__( - "movq %1, %0" - : "=m" (*addr) - : "x" (value)); -#else - LOG(FATAL) << "Unsupported architecture"; -#endif -#endif // defined(__LP64__) - } else { - SwapMutexWrite64(addr, value); - } - } - - // Atomically compare the value at "addr" to "old_value", if equal replace it with "new_value" - // and return true. Otherwise, don't swap, and return false. - // This is fully ordered, i.e. it has C++11 memory_order_seq_cst - // semantics (assuming all other accesses use a mutex if this one does). - // This has "strong" semantics; if it fails then it is guaranteed that - // at some point during the execution of Cas64, *addr was not equal to - // old_value. - static bool Cas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) { - if (!NeedSwapMutexes(kRuntimeISA)) { - return __sync_bool_compare_and_swap(addr, old_value, new_value); - } else { - return SwapMutexCas64(old_value, new_value, addr); - } - } - - // Does the architecture provide reasonable atomic long operations or do we fall back on mutexes? - static bool LongAtomicsUseMutexes(InstructionSet isa) { - return NeedSwapMutexes(isa); - } - - static void ThreadFenceAcquire() { - std::atomic_thread_fence(std::memory_order_acquire); - } - - static void ThreadFenceRelease() { - std::atomic_thread_fence(std::memory_order_release); - } - - static void ThreadFenceForConstructor() { - #if defined(__aarch64__) - __asm__ __volatile__("dmb ishst" : : : "memory"); - #else - std::atomic_thread_fence(std::memory_order_release); - #endif - } - - static void ThreadFenceSequentiallyConsistent() { - std::atomic_thread_fence(std::memory_order_seq_cst); - } - - private: - static Mutex* GetSwapMutex(const volatile int64_t* addr); - static int64_t SwapMutexRead64(volatile const int64_t* addr); - static void SwapMutexWrite64(volatile int64_t* addr, int64_t val); - static bool SwapMutexCas64(int64_t old_value, int64_t new_value, volatile int64_t* addr); - - // We stripe across a bunch of different mutexes to reduce contention. - static constexpr size_t kSwapMutexCount = 32; - static std::vector* gSwapMutexes; - - DISALLOW_COPY_AND_ASSIGN(QuasiAtomic); -}; - template class PACKED(sizeof(T)) Atomic : public std::atomic { public: @@ -409,4 +252,4 @@ static_assert(sizeof(Atomic) == sizeof(int64_t), "Weird Atomic s } // namespace art -#endif // ART_RUNTIME_ATOMIC_H_ +#endif // ART_LIBARTBASE_BASE_ATOMIC_H_ diff --git a/runtime/base/bit_field.h b/libartbase/base/bit_field.h similarity index 95% rename from runtime/base/bit_field.h rename to libartbase/base/bit_field.h index 86007d6a35..9971735138 100644 --- a/runtime/base/bit_field.h +++ b/libartbase/base/bit_field.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_BIT_FIELD_H_ -#define ART_RUNTIME_BASE_BIT_FIELD_H_ +#ifndef ART_LIBARTBASE_BASE_BIT_FIELD_H_ +#define ART_LIBARTBASE_BASE_BIT_FIELD_H_ #include @@ -89,4 +89,4 @@ class BitField { } // namespace art -#endif // ART_RUNTIME_BASE_BIT_FIELD_H_ +#endif // ART_LIBARTBASE_BASE_BIT_FIELD_H_ diff --git a/runtime/base/bit_field_test.cc b/libartbase/base/bit_field_test.cc similarity index 100% rename from runtime/base/bit_field_test.cc rename to libartbase/base/bit_field_test.cc diff --git a/runtime/base/bit_string.h b/libartbase/base/bit_string.h similarity index 98% rename from runtime/base/bit_string.h rename to libartbase/base/bit_string.h index 7d9fb70de7..0e051f358b 100644 --- a/runtime/base/bit_string.h +++ b/libartbase/base/bit_string.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_BIT_STRING_H_ -#define ART_RUNTIME_BASE_BIT_STRING_H_ +#ifndef ART_LIBARTBASE_BASE_BIT_STRING_H_ +#define ART_LIBARTBASE_BASE_BIT_STRING_H_ #include "base/bit_struct.h" #include "base/bit_utils.h" @@ -296,4 +296,4 @@ inline std::ostream& operator<<(std::ostream& os, const BitString& bit_string) { } // namespace art -#endif // ART_RUNTIME_BASE_BIT_STRING_H_ +#endif // ART_LIBARTBASE_BASE_BIT_STRING_H_ diff --git a/runtime/base/bit_string_test.cc b/libartbase/base/bit_string_test.cc similarity index 100% rename from runtime/base/bit_string_test.cc rename to libartbase/base/bit_string_test.cc diff --git a/runtime/base/bit_struct.h b/libartbase/base/bit_struct.h similarity index 98% rename from runtime/base/bit_struct.h rename to libartbase/base/bit_struct.h index 7eb63c6b5c..386b896073 100644 --- a/runtime/base/bit_struct.h +++ b/libartbase/base/bit_struct.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_BIT_STRUCT_H_ -#define ART_RUNTIME_BASE_BIT_STRUCT_H_ +#ifndef ART_LIBARTBASE_BASE_BIT_STRUCT_H_ +#define ART_LIBARTBASE_BASE_BIT_STRUCT_H_ #include "base/bit_utils.h" #include "bit_struct_detail.h" @@ -303,4 +303,4 @@ static constexpr size_t BitStructSizeOf() { } // namespace art -#endif // ART_RUNTIME_BASE_BIT_STRUCT_H_ +#endif // ART_LIBARTBASE_BASE_BIT_STRUCT_H_ diff --git a/runtime/base/bit_struct_detail.h b/libartbase/base/bit_struct_detail.h similarity index 96% rename from runtime/base/bit_struct_detail.h rename to libartbase/base/bit_struct_detail.h index 24f6c4c85e..facfa61efb 100644 --- a/runtime/base/bit_struct_detail.h +++ b/libartbase/base/bit_struct_detail.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_BIT_STRUCT_DETAIL_H_ -#define ART_RUNTIME_BASE_BIT_STRUCT_DETAIL_H_ +#ifndef ART_LIBARTBASE_BASE_BIT_STRUCT_DETAIL_H_ +#define ART_LIBARTBASE_BASE_BIT_STRUCT_DETAIL_H_ #include "base/bit_utils.h" #include "globals.h" @@ -116,4 +116,4 @@ static constexpr bool ValidateBitStructSize() { } // namespace detail } // namespace art -#endif // ART_RUNTIME_BASE_BIT_STRUCT_DETAIL_H_ +#endif // ART_LIBARTBASE_BASE_BIT_STRUCT_DETAIL_H_ diff --git a/runtime/base/bit_struct_test.cc b/libartbase/base/bit_struct_test.cc similarity index 100% rename from runtime/base/bit_struct_test.cc rename to libartbase/base/bit_struct_test.cc diff --git a/runtime/base/bit_vector-inl.h b/libartbase/base/bit_vector-inl.h similarity index 95% rename from runtime/base/bit_vector-inl.h rename to libartbase/base/bit_vector-inl.h index e67d4e25eb..7a9f4650da 100644 --- a/runtime/base/bit_vector-inl.h +++ b/libartbase/base/bit_vector-inl.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_BIT_VECTOR_INL_H_ -#define ART_RUNTIME_BASE_BIT_VECTOR_INL_H_ +#ifndef ART_LIBARTBASE_BASE_BIT_VECTOR_INL_H_ +#define ART_LIBARTBASE_BASE_BIT_VECTOR_INL_H_ #include "bit_vector.h" @@ -97,4 +97,4 @@ inline bool BitVector::Equal(const BitVector* src) const { } // namespace art -#endif // ART_RUNTIME_BASE_BIT_VECTOR_INL_H_ +#endif // ART_LIBARTBASE_BASE_BIT_VECTOR_INL_H_ diff --git a/runtime/base/bit_vector.cc b/libartbase/base/bit_vector.cc similarity index 100% rename from runtime/base/bit_vector.cc rename to libartbase/base/bit_vector.cc diff --git a/runtime/base/bit_vector.h b/libartbase/base/bit_vector.h similarity index 98% rename from runtime/base/bit_vector.h rename to libartbase/base/bit_vector.h index 564092a1a2..2ffa2aa9c9 100644 --- a/runtime/base/bit_vector.h +++ b/libartbase/base/bit_vector.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_BIT_VECTOR_H_ -#define ART_RUNTIME_BASE_BIT_VECTOR_H_ +#ifndef ART_LIBARTBASE_BASE_BIT_VECTOR_H_ +#define ART_LIBARTBASE_BASE_BIT_VECTOR_H_ #include #include @@ -293,4 +293,4 @@ class BitVector { } // namespace art -#endif // ART_RUNTIME_BASE_BIT_VECTOR_H_ +#endif // ART_LIBARTBASE_BASE_BIT_VECTOR_H_ diff --git a/runtime/base/bit_vector_test.cc b/libartbase/base/bit_vector_test.cc similarity index 100% rename from runtime/base/bit_vector_test.cc rename to libartbase/base/bit_vector_test.cc diff --git a/runtime/base/bounded_fifo.h b/libartbase/base/bounded_fifo.h similarity index 92% rename from runtime/base/bounded_fifo.h rename to libartbase/base/bounded_fifo.h index 1520770fe6..444f31a55b 100644 --- a/runtime/base/bounded_fifo.h +++ b/libartbase/base/bounded_fifo.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_BOUNDED_FIFO_H_ -#define ART_RUNTIME_BASE_BOUNDED_FIFO_H_ +#ifndef ART_LIBARTBASE_BASE_BOUNDED_FIFO_H_ +#define ART_LIBARTBASE_BASE_BOUNDED_FIFO_H_ #include @@ -72,4 +72,4 @@ class BoundedFifoPowerOfTwo { } // namespace art -#endif // ART_RUNTIME_BASE_BOUNDED_FIFO_H_ +#endif // ART_LIBARTBASE_BASE_BOUNDED_FIFO_H_ diff --git a/runtime/base/callee_save_type.h b/libartbase/base/callee_save_type.h similarity index 92% rename from runtime/base/callee_save_type.h rename to libartbase/base/callee_save_type.h index e9cd63c3a0..3e44a3a73f 100644 --- a/runtime/base/callee_save_type.h +++ b/libartbase/base/callee_save_type.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_CALLEE_SAVE_TYPE_H_ -#define ART_RUNTIME_BASE_CALLEE_SAVE_TYPE_H_ +#ifndef ART_LIBARTBASE_BASE_CALLEE_SAVE_TYPE_H_ +#define ART_LIBARTBASE_BASE_CALLEE_SAVE_TYPE_H_ #include #include @@ -44,4 +44,4 @@ static inline constexpr CalleeSaveType GetCanonicalCalleeSaveType(CalleeSaveType } // namespace art -#endif // ART_RUNTIME_BASE_CALLEE_SAVE_TYPE_H_ +#endif // ART_LIBARTBASE_BASE_CALLEE_SAVE_TYPE_H_ diff --git a/runtime/base/dchecked_vector.h b/libartbase/base/dchecked_vector.h similarity index 98% rename from runtime/base/dchecked_vector.h rename to libartbase/base/dchecked_vector.h index 7236ac301a..e9ce6d0aaf 100644 --- a/runtime/base/dchecked_vector.h +++ b/libartbase/base/dchecked_vector.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_DCHECKED_VECTOR_H_ -#define ART_RUNTIME_BASE_DCHECKED_VECTOR_H_ +#ifndef ART_LIBARTBASE_BASE_DCHECKED_VECTOR_H_ +#define ART_LIBARTBASE_BASE_DCHECKED_VECTOR_H_ #include #include @@ -225,4 +225,4 @@ bool operator>=(const dchecked_vector& lhs, const dchecked_vector #include @@ -144,4 +144,4 @@ class DebugStackIndirectTopRefImpl { } // namespace art -#endif // ART_RUNTIME_BASE_DEBUG_STACK_H_ +#endif // ART_LIBARTBASE_BASE_DEBUG_STACK_H_ diff --git a/runtime/base/file_magic.cc b/libartbase/base/file_magic.cc similarity index 100% rename from runtime/base/file_magic.cc rename to libartbase/base/file_magic.cc diff --git a/runtime/base/file_magic.h b/libartbase/base/file_magic.h similarity index 87% rename from runtime/base/file_magic.h rename to libartbase/base/file_magic.h index e7bd706a5c..53f551cb3a 100644 --- a/runtime/base/file_magic.h +++ b/libartbase/base/file_magic.h @@ -14,13 +14,13 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_FILE_MAGIC_H_ -#define ART_RUNTIME_BASE_FILE_MAGIC_H_ +#ifndef ART_LIBARTBASE_BASE_FILE_MAGIC_H_ +#define ART_LIBARTBASE_BASE_FILE_MAGIC_H_ #include #include -#include "os.h" +#include "base/os.h" namespace art { @@ -35,4 +35,4 @@ bool IsZipMagic(uint32_t magic); } // namespace art -#endif // ART_RUNTIME_BASE_FILE_MAGIC_H_ +#endif // ART_LIBARTBASE_BASE_FILE_MAGIC_H_ diff --git a/runtime/base/hex_dump.cc b/libartbase/base/hex_dump.cc similarity index 100% rename from runtime/base/hex_dump.cc rename to libartbase/base/hex_dump.cc diff --git a/runtime/base/hex_dump.h b/libartbase/base/hex_dump.h similarity index 92% rename from runtime/base/hex_dump.h rename to libartbase/base/hex_dump.h index 2ce0aefe67..55f4d53c2e 100644 --- a/runtime/base/hex_dump.h +++ b/libartbase/base/hex_dump.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_HEX_DUMP_H_ -#define ART_RUNTIME_BASE_HEX_DUMP_H_ +#ifndef ART_LIBARTBASE_BASE_HEX_DUMP_H_ +#define ART_LIBARTBASE_BASE_HEX_DUMP_H_ #include "base/macros.h" @@ -52,4 +52,4 @@ inline std::ostream& operator<<(std::ostream& os, const HexDump& rhs) { } // namespace art -#endif // ART_RUNTIME_BASE_HEX_DUMP_H_ +#endif // ART_LIBARTBASE_BASE_HEX_DUMP_H_ diff --git a/runtime/base/hex_dump_test.cc b/libartbase/base/hex_dump_test.cc similarity index 100% rename from runtime/base/hex_dump_test.cc rename to libartbase/base/hex_dump_test.cc diff --git a/runtime/base/histogram-inl.h b/libartbase/base/histogram-inl.h similarity index 98% rename from runtime/base/histogram-inl.h rename to libartbase/base/histogram-inl.h index 3ce0140c84..26d01b2883 100644 --- a/runtime/base/histogram-inl.h +++ b/libartbase/base/histogram-inl.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_HISTOGRAM_INL_H_ -#define ART_RUNTIME_BASE_HISTOGRAM_INL_H_ +#ifndef ART_LIBARTBASE_BASE_HISTOGRAM_INL_H_ +#define ART_LIBARTBASE_BASE_HISTOGRAM_INL_H_ #include #include @@ -28,7 +28,7 @@ #include "base/bit_utils.h" #include "base/time_utils.h" -#include "utils.h" +#include "base/utils.h" namespace art { @@ -277,4 +277,4 @@ inline double Histogram::Percentile(double per, const CumulativeData& dat #pragma clang diagnostic pop } // namespace art -#endif // ART_RUNTIME_BASE_HISTOGRAM_INL_H_ +#endif // ART_LIBARTBASE_BASE_HISTOGRAM_INL_H_ diff --git a/runtime/base/histogram.h b/libartbase/base/histogram.h similarity index 97% rename from runtime/base/histogram.h rename to libartbase/base/histogram.h index 7544a9c918..39a1866b4d 100644 --- a/runtime/base/histogram.h +++ b/libartbase/base/histogram.h @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_HISTOGRAM_H_ -#define ART_RUNTIME_BASE_HISTOGRAM_H_ +#ifndef ART_LIBARTBASE_BASE_HISTOGRAM_H_ +#define ART_LIBARTBASE_BASE_HISTOGRAM_H_ #include #include @@ -128,4 +128,4 @@ template class Histogram { }; } // namespace art -#endif // ART_RUNTIME_BASE_HISTOGRAM_H_ +#endif // ART_LIBARTBASE_BASE_HISTOGRAM_H_ diff --git a/runtime/base/histogram_test.cc b/libartbase/base/histogram_test.cc similarity index 100% rename from runtime/base/histogram_test.cc rename to libartbase/base/histogram_test.cc diff --git a/runtime/base/length_prefixed_array.h b/libartbase/base/length_prefixed_array.h similarity index 95% rename from runtime/base/length_prefixed_array.h rename to libartbase/base/length_prefixed_array.h index 2df5a99352..7c09bddd55 100644 --- a/runtime/base/length_prefixed_array.h +++ b/libartbase/base/length_prefixed_array.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_LENGTH_PREFIXED_ARRAY_H_ -#define ART_RUNTIME_BASE_LENGTH_PREFIXED_ARRAY_H_ +#ifndef ART_LIBARTBASE_BASE_LENGTH_PREFIXED_ARRAY_H_ +#define ART_LIBARTBASE_BASE_LENGTH_PREFIXED_ARRAY_H_ #include // for offsetof() #include // for memset() @@ -23,7 +23,7 @@ #include "base/bit_utils.h" #include "base/casts.h" #include "base/iteration_range.h" -#include "stride_iterator.h" +#include "base/stride_iterator.h" namespace art { @@ -118,4 +118,4 @@ IterationRange> MakeIterationRangeFromLengthPrefixedArray( } // namespace art -#endif // ART_RUNTIME_BASE_LENGTH_PREFIXED_ARRAY_H_ +#endif // ART_LIBARTBASE_BASE_LENGTH_PREFIXED_ARRAY_H_ diff --git a/runtime/base/logging.cc b/libartbase/base/logging.cc similarity index 98% rename from runtime/base/logging.cc rename to libartbase/base/logging.cc index 78d54292d1..37b1f8264a 100644 --- a/runtime/base/logging.cc +++ b/libartbase/base/logging.cc @@ -21,8 +21,6 @@ #include #include "aborting.h" -#include "mutex.h" -#include "utils.h" // Headers for LogMessage::LogLine. #ifdef ART_TARGET_ANDROID @@ -59,8 +57,6 @@ void InitLogging(char* argv[], AbortFunction& abort_function) { if (gCmdLine.get() != nullptr) { return; } - // TODO: Move this to a more obvious InitART... - Locks::Init(); // Stash the command line for later use. We can use /proc/self/cmdline on Linux to recover this, // but we don't have that luxury on the Mac, and there are a couple of argv[0] variants that are diff --git a/runtime/base/logging.h b/libartbase/base/logging.h similarity index 96% rename from runtime/base/logging.h rename to libartbase/base/logging.h index c562bdf59f..fd5fc74383 100644 --- a/runtime/base/logging.h +++ b/libartbase/base/logging.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_LOGGING_H_ -#define ART_RUNTIME_BASE_LOGGING_H_ +#ifndef ART_LIBARTBASE_BASE_LOGGING_H_ +#define ART_LIBARTBASE_BASE_LOGGING_H_ #include #include @@ -110,4 +110,4 @@ class LogHelper { } // namespace art -#endif // ART_RUNTIME_BASE_LOGGING_H_ +#endif // ART_LIBARTBASE_BASE_LOGGING_H_ diff --git a/runtime/base/logging_test.cc b/libartbase/base/logging_test.cc similarity index 100% rename from runtime/base/logging_test.cc rename to libartbase/base/logging_test.cc diff --git a/runtime/os.h b/libartbase/base/os.h similarity index 84% rename from runtime/os.h rename to libartbase/base/os.h index 7130fc3732..4062d90f88 100644 --- a/runtime/os.h +++ b/libartbase/base/os.h @@ -14,8 +14,10 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_OS_H_ -#define ART_RUNTIME_OS_H_ +#ifndef ART_LIBARTBASE_BASE_OS_H_ +#define ART_LIBARTBASE_BASE_OS_H_ + +#include namespace unix_file { class FdFile; @@ -47,12 +49,15 @@ class OS { static File* OpenFileWithFlags(const char* name, int flags, bool auto_flush = true); // Check if a file exists. - static bool FileExists(const char* name); + static bool FileExists(const char* name, bool check_file_type = true); // Check if a directory exists. static bool DirectoryExists(const char* name); + + // Get the size of a file (or -1 if it does not exist). + static int64_t GetFileSizeBytes(const char* name); }; } // namespace art -#endif // ART_RUNTIME_OS_H_ +#endif // ART_LIBARTBASE_BASE_OS_H_ diff --git a/runtime/os_linux.cc b/libartbase/base/os_linux.cc similarity index 84% rename from runtime/os_linux.cc rename to libartbase/base/os_linux.cc index 1b3e0000da..6b5a604423 100644 --- a/runtime/os_linux.cc +++ b/libartbase/base/os_linux.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "os.h" +#include "base/os.h" #include #include @@ -64,10 +64,14 @@ File* OS::OpenFileWithFlags(const char* name, int flags, bool auto_flush) { return file.release(); } -bool OS::FileExists(const char* name) { +bool OS::FileExists(const char* name, bool check_file_type) { struct stat st; if (stat(name, &st) == 0) { - return S_ISREG(st.st_mode); // TODO: Deal with symlinks? + if (check_file_type) { + return S_ISREG(st.st_mode); // TODO: Deal with symlinks? + } else { + return true; + } } else { return false; } @@ -82,4 +86,13 @@ bool OS::DirectoryExists(const char* name) { } } +int64_t OS::GetFileSizeBytes(const char* name) { + struct stat st; + if (stat(name, &st) == 0) { + return -1; // TODO: Deal with symlinks? + } else { + return st.st_size; + } +} + } // namespace art diff --git a/runtime/base/runtime_debug.cc b/libartbase/base/runtime_debug.cc similarity index 100% rename from runtime/base/runtime_debug.cc rename to libartbase/base/runtime_debug.cc diff --git a/runtime/base/runtime_debug.h b/libartbase/base/runtime_debug.h similarity index 93% rename from runtime/base/runtime_debug.h rename to libartbase/base/runtime_debug.h index 89a0361fa7..7d91166aa7 100644 --- a/runtime/base/runtime_debug.h +++ b/libartbase/base/runtime_debug.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_RUNTIME_DEBUG_H_ -#define ART_RUNTIME_BASE_RUNTIME_DEBUG_H_ +#ifndef ART_LIBARTBASE_BASE_RUNTIME_DEBUG_H_ +#define ART_LIBARTBASE_BASE_RUNTIME_DEBUG_H_ namespace art { @@ -58,4 +58,4 @@ void SetRuntimeDebugFlagsEnabled(bool enabled); } // namespace art -#endif // ART_RUNTIME_BASE_RUNTIME_DEBUG_H_ +#endif // ART_LIBARTBASE_BASE_RUNTIME_DEBUG_H_ diff --git a/runtime/base/safe_copy.cc b/libartbase/base/safe_copy.cc similarity index 100% rename from runtime/base/safe_copy.cc rename to libartbase/base/safe_copy.cc diff --git a/runtime/base/safe_copy.h b/libartbase/base/safe_copy.h similarity index 87% rename from runtime/base/safe_copy.h rename to libartbase/base/safe_copy.h index d0f497c0bd..56cdfecc2d 100644 --- a/runtime/base/safe_copy.h +++ b/libartbase/base/safe_copy.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_SAFE_COPY_H_ -#define ART_RUNTIME_BASE_SAFE_COPY_H_ +#ifndef ART_LIBARTBASE_BASE_SAFE_COPY_H_ +#define ART_LIBARTBASE_BASE_SAFE_COPY_H_ #include @@ -28,4 +28,4 @@ ssize_t SafeCopy(void *dst, const void *src, size_t len); } // namespace art -#endif // ART_RUNTIME_BASE_SAFE_COPY_H_ +#endif // ART_LIBARTBASE_BASE_SAFE_COPY_H_ diff --git a/runtime/base/safe_copy_test.cc b/libartbase/base/safe_copy_test.cc similarity index 100% rename from runtime/base/safe_copy_test.cc rename to libartbase/base/safe_copy_test.cc diff --git a/runtime/base/scoped_flock.cc b/libartbase/base/scoped_flock.cc similarity index 100% rename from runtime/base/scoped_flock.cc rename to libartbase/base/scoped_flock.cc diff --git a/runtime/base/scoped_flock.h b/libartbase/base/scoped_flock.h similarity index 94% rename from runtime/base/scoped_flock.h rename to libartbase/base/scoped_flock.h index db6c819c6c..476b25748b 100644 --- a/runtime/base/scoped_flock.h +++ b/libartbase/base/scoped_flock.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_SCOPED_FLOCK_H_ -#define ART_RUNTIME_BASE_SCOPED_FLOCK_H_ +#ifndef ART_LIBARTBASE_BASE_SCOPED_FLOCK_H_ +#define ART_LIBARTBASE_BASE_SCOPED_FLOCK_H_ #include #include @@ -23,8 +23,8 @@ #include #include "base/macros.h" +#include "base/os.h" #include "base/unix_file/fd_file.h" -#include "os.h" namespace art { @@ -85,4 +85,4 @@ class LockedFileCloseNoFlush { } // namespace art -#endif // ART_RUNTIME_BASE_SCOPED_FLOCK_H_ +#endif // ART_LIBARTBASE_BASE_SCOPED_FLOCK_H_ diff --git a/runtime/base/scoped_flock_test.cc b/libartbase/base/scoped_flock_test.cc similarity index 100% rename from runtime/base/scoped_flock_test.cc rename to libartbase/base/scoped_flock_test.cc diff --git a/runtime/stride_iterator.h b/libartbase/base/stride_iterator.h similarity index 96% rename from runtime/stride_iterator.h rename to libartbase/base/stride_iterator.h index 511c2c66f2..67c0d3803e 100644 --- a/runtime/stride_iterator.h +++ b/libartbase/base/stride_iterator.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_STRIDE_ITERATOR_H_ -#define ART_RUNTIME_STRIDE_ITERATOR_H_ +#ifndef ART_LIBARTBASE_BASE_STRIDE_ITERATOR_H_ +#define ART_LIBARTBASE_BASE_STRIDE_ITERATOR_H_ #include @@ -147,4 +147,4 @@ bool operator>=(const StrideIterator& lhs, const StrideIterator& rhs) { } // namespace art -#endif // ART_RUNTIME_STRIDE_ITERATOR_H_ +#endif // ART_LIBARTBASE_BASE_STRIDE_ITERATOR_H_ diff --git a/runtime/base/strlcpy.h b/libartbase/base/strlcpy.h similarity index 90% rename from runtime/base/strlcpy.h rename to libartbase/base/strlcpy.h index de135ea990..98ea34b0a5 100644 --- a/runtime/base/strlcpy.h +++ b/libartbase/base/strlcpy.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_STRLCPY_H_ -#define ART_RUNTIME_BASE_STRLCPY_H_ +#ifndef ART_LIBARTBASE_BASE_STRLCPY_H_ +#define ART_LIBARTBASE_BASE_STRLCPY_H_ #include #include @@ -35,4 +35,4 @@ static inline size_t strlcpy(char* dst, const char* src, size_t size) { #endif -#endif // ART_RUNTIME_BASE_STRLCPY_H_ +#endif // ART_LIBARTBASE_BASE_STRLCPY_H_ diff --git a/runtime/base/systrace.h b/libartbase/base/systrace.h similarity index 93% rename from runtime/base/systrace.h rename to libartbase/base/systrace.h index dc2206e420..2ff33e8c84 100644 --- a/runtime/base/systrace.h +++ b/libartbase/base/systrace.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_SYSTRACE_H_ -#define ART_RUNTIME_BASE_SYSTRACE_H_ +#ifndef ART_LIBARTBASE_BASE_SYSTRACE_H_ +#define ART_LIBARTBASE_BASE_SYSTRACE_H_ #define ATRACE_TAG ATRACE_TAG_DALVIK #include @@ -80,4 +80,4 @@ class ScopedTraceNoStart { } // namespace art -#endif // ART_RUNTIME_BASE_SYSTRACE_H_ +#endif // ART_LIBARTBASE_BASE_SYSTRACE_H_ diff --git a/runtime/base/time_utils.cc b/libartbase/base/time_utils.cc similarity index 100% rename from runtime/base/time_utils.cc rename to libartbase/base/time_utils.cc diff --git a/runtime/base/time_utils.h b/libartbase/base/time_utils.h similarity index 96% rename from runtime/base/time_utils.h rename to libartbase/base/time_utils.h index 7648a109fa..811af5d682 100644 --- a/runtime/base/time_utils.h +++ b/libartbase/base/time_utils.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_TIME_UTILS_H_ -#define ART_RUNTIME_BASE_TIME_UTILS_H_ +#ifndef ART_LIBARTBASE_BASE_TIME_UTILS_H_ +#define ART_LIBARTBASE_BASE_TIME_UTILS_H_ #include #include @@ -101,4 +101,4 @@ void InitTimeSpec(bool absolute, int clock, int64_t ms, int32_t ns, timespec* ts } // namespace art -#endif // ART_RUNTIME_BASE_TIME_UTILS_H_ +#endif // ART_LIBARTBASE_BASE_TIME_UTILS_H_ diff --git a/runtime/base/time_utils_test.cc b/libartbase/base/time_utils_test.cc similarity index 100% rename from runtime/base/time_utils_test.cc rename to libartbase/base/time_utils_test.cc diff --git a/runtime/base/to_str.h b/libartbase/base/to_str.h similarity index 90% rename from runtime/base/to_str.h rename to libartbase/base/to_str.h index 6b1c84c411..74d9584f78 100644 --- a/runtime/base/to_str.h +++ b/libartbase/base/to_str.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_TO_STR_H_ -#define ART_RUNTIME_BASE_TO_STR_H_ +#ifndef ART_LIBARTBASE_BASE_TO_STR_H_ +#define ART_LIBARTBASE_BASE_TO_STR_H_ #include @@ -47,4 +47,4 @@ class ToStr { } // namespace art -#endif // ART_RUNTIME_BASE_TO_STR_H_ +#endif // ART_LIBARTBASE_BASE_TO_STR_H_ diff --git a/runtime/base/tracking_safe_map.h b/libartbase/base/tracking_safe_map.h similarity index 86% rename from runtime/base/tracking_safe_map.h rename to libartbase/base/tracking_safe_map.h index 2f3984d106..2750de1b94 100644 --- a/runtime/base/tracking_safe_map.h +++ b/libartbase/base/tracking_safe_map.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_TRACKING_SAFE_MAP_H_ -#define ART_RUNTIME_BASE_TRACKING_SAFE_MAP_H_ +#ifndef ART_LIBARTBASE_BASE_TRACKING_SAFE_MAP_H_ +#define ART_LIBARTBASE_BASE_TRACKING_SAFE_MAP_H_ #include "base/allocator.h" #include "base/safe_map.h" @@ -29,4 +29,4 @@ class AllocationTrackingSafeMap : public SafeMap< } // namespace art -#endif // ART_RUNTIME_BASE_TRACKING_SAFE_MAP_H_ +#endif // ART_LIBARTBASE_BASE_TRACKING_SAFE_MAP_H_ diff --git a/runtime/base/transform_array_ref.h b/libartbase/base/transform_array_ref.h similarity index 97% rename from runtime/base/transform_array_ref.h rename to libartbase/base/transform_array_ref.h index a4e0bc27ed..de2739e2fc 100644 --- a/runtime/base/transform_array_ref.h +++ b/libartbase/base/transform_array_ref.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_TRANSFORM_ARRAY_REF_H_ -#define ART_RUNTIME_BASE_TRANSFORM_ARRAY_REF_H_ +#ifndef ART_LIBARTBASE_BASE_TRANSFORM_ARRAY_REF_H_ +#define ART_LIBARTBASE_BASE_TRANSFORM_ARRAY_REF_H_ #include @@ -193,4 +193,4 @@ TransformArrayRef MakeTransformA } // namespace art -#endif // ART_RUNTIME_BASE_TRANSFORM_ARRAY_REF_H_ +#endif // ART_LIBARTBASE_BASE_TRANSFORM_ARRAY_REF_H_ diff --git a/runtime/base/transform_array_ref_test.cc b/libartbase/base/transform_array_ref_test.cc similarity index 100% rename from runtime/base/transform_array_ref_test.cc rename to libartbase/base/transform_array_ref_test.cc diff --git a/runtime/base/transform_iterator.h b/libartbase/base/transform_iterator.h similarity index 97% rename from runtime/base/transform_iterator.h rename to libartbase/base/transform_iterator.h index 9c8f822b71..82d9f9e325 100644 --- a/runtime/base/transform_iterator.h +++ b/libartbase/base/transform_iterator.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_TRANSFORM_ITERATOR_H_ -#define ART_RUNTIME_BASE_TRANSFORM_ITERATOR_H_ +#ifndef ART_LIBARTBASE_BASE_TRANSFORM_ITERATOR_H_ +#define ART_LIBARTBASE_BASE_TRANSFORM_ITERATOR_H_ #include #include @@ -175,4 +175,4 @@ auto MakeTransformRange(BaseRange& range, Function f) { } // namespace art -#endif // ART_RUNTIME_BASE_TRANSFORM_ITERATOR_H_ +#endif // ART_LIBARTBASE_BASE_TRANSFORM_ITERATOR_H_ diff --git a/runtime/base/transform_iterator_test.cc b/libartbase/base/transform_iterator_test.cc similarity index 100% rename from runtime/base/transform_iterator_test.cc rename to libartbase/base/transform_iterator_test.cc diff --git a/runtime/base/unix_file/README b/libartbase/base/unix_file/README similarity index 100% rename from runtime/base/unix_file/README rename to libartbase/base/unix_file/README diff --git a/runtime/base/unix_file/fd_file.cc b/libartbase/base/unix_file/fd_file.cc similarity index 100% rename from runtime/base/unix_file/fd_file.cc rename to libartbase/base/unix_file/fd_file.cc diff --git a/runtime/base/unix_file/fd_file.h b/libartbase/base/unix_file/fd_file.h similarity index 97% rename from runtime/base/unix_file/fd_file.h rename to libartbase/base/unix_file/fd_file.h index 3fb70f644d..fe3317f64e 100644 --- a/runtime/base/unix_file/fd_file.h +++ b/libartbase/base/unix_file/fd_file.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_UNIX_FILE_FD_FILE_H_ -#define ART_RUNTIME_BASE_UNIX_FILE_FD_FILE_H_ +#ifndef ART_LIBARTBASE_BASE_UNIX_FILE_FD_FILE_H_ +#define ART_LIBARTBASE_BASE_UNIX_FILE_FD_FILE_H_ #include @@ -192,4 +192,4 @@ std::ostream& operator<<(std::ostream& os, const FdFile::GuardState& kind); } // namespace unix_file -#endif // ART_RUNTIME_BASE_UNIX_FILE_FD_FILE_H_ +#endif // ART_LIBARTBASE_BASE_UNIX_FILE_FD_FILE_H_ diff --git a/runtime/base/unix_file/fd_file_test.cc b/libartbase/base/unix_file/fd_file_test.cc similarity index 100% rename from runtime/base/unix_file/fd_file_test.cc rename to libartbase/base/unix_file/fd_file_test.cc diff --git a/runtime/base/unix_file/random_access_file.h b/libartbase/base/unix_file/random_access_file.h similarity index 92% rename from runtime/base/unix_file/random_access_file.h rename to libartbase/base/unix_file/random_access_file.h index 31a6dbe1fc..d2124cc843 100644 --- a/runtime/base/unix_file/random_access_file.h +++ b/libartbase/base/unix_file/random_access_file.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_H_ -#define ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_H_ +#ifndef ART_LIBARTBASE_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_H_ +#define ART_LIBARTBASE_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_H_ #include @@ -65,4 +65,4 @@ class RandomAccessFile { } // namespace unix_file -#endif // ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_H_ +#endif // ART_LIBARTBASE_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_H_ diff --git a/runtime/base/unix_file/random_access_file_test.h b/libartbase/base/unix_file/random_access_file_test.h similarity index 96% rename from runtime/base/unix_file/random_access_file_test.h rename to libartbase/base/unix_file/random_access_file_test.h index 91858c21de..1de5f7b72c 100644 --- a/runtime/base/unix_file/random_access_file_test.h +++ b/libartbase/base/unix_file/random_access_file_test.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_TEST_H_ -#define ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_TEST_H_ +#ifndef ART_LIBARTBASE_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_TEST_H_ +#define ART_LIBARTBASE_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_TEST_H_ #include #include @@ -180,4 +180,4 @@ class RandomAccessFileTest : public testing::Test { } // namespace unix_file -#endif // ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_TEST_H_ +#endif // ART_LIBARTBASE_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_TEST_H_ diff --git a/runtime/base/unix_file/random_access_file_utils.cc b/libartbase/base/unix_file/random_access_file_utils.cc similarity index 100% rename from runtime/base/unix_file/random_access_file_utils.cc rename to libartbase/base/unix_file/random_access_file_utils.cc diff --git a/runtime/base/unix_file/random_access_file_utils.h b/libartbase/base/unix_file/random_access_file_utils.h similarity index 81% rename from runtime/base/unix_file/random_access_file_utils.h rename to libartbase/base/unix_file/random_access_file_utils.h index 30c81c09aa..47c4c2a57b 100644 --- a/runtime/base/unix_file/random_access_file_utils.h +++ b/libartbase/base/unix_file/random_access_file_utils.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_UTILS_H_ -#define ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_UTILS_H_ +#ifndef ART_LIBARTBASE_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_UTILS_H_ +#define ART_LIBARTBASE_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_UTILS_H_ namespace unix_file { @@ -27,4 +27,4 @@ bool CopyFile(const RandomAccessFile& src, RandomAccessFile* dst); } // namespace unix_file -#endif // ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_UTILS_H_ +#endif // ART_LIBARTBASE_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_UTILS_H_ diff --git a/runtime/utils.cc b/libartbase/base/utils.cc similarity index 99% rename from runtime/utils.cc rename to libartbase/base/utils.cc index 7246c3d279..029cffd3ab 100644 --- a/runtime/utils.cc +++ b/libartbase/base/utils.cc @@ -30,8 +30,7 @@ #include "android-base/stringprintf.h" #include "android-base/strings.h" -#include "dex/utf-inl.h" -#include "os.h" +#include "base/os.h" #if defined(__APPLE__) #include diff --git a/runtime/utils.h b/libartbase/base/utils.h similarity index 97% rename from runtime/utils.h rename to libartbase/base/utils.h index 0c3a0a231a..c8c5b72bf7 100644 --- a/runtime/utils.h +++ b/libartbase/base/utils.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_UTILS_H_ -#define ART_RUNTIME_UTILS_H_ +#ifndef ART_LIBARTBASE_BASE_UTILS_H_ +#define ART_LIBARTBASE_BASE_UTILS_H_ #include #include @@ -25,11 +25,11 @@ #include -#include "arch/instruction_set.h" #include "base/casts.h" +#include "base/enums.h" +#include "base/globals.h" +#include "base/macros.h" #include "base/stringpiece.h" -#include "dex/primitive.h" -#include "globals.h" namespace art { @@ -260,4 +260,4 @@ static inline size_t HashBytes(const uint8_t* data, size_t len) { } // namespace art -#endif // ART_RUNTIME_UTILS_H_ +#endif // ART_LIBARTBASE_BASE_UTILS_H_ diff --git a/libartbase/base/utils_test.cc b/libartbase/base/utils_test.cc new file mode 100644 index 0000000000..892d1fd5bf --- /dev/null +++ b/libartbase/base/utils_test.cc @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2011 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 "utils.h" + +#include "gtest/gtest.h" + +namespace art { + +class UtilsTest : public testing::Test {}; + +TEST_F(UtilsTest, PrettySize) { + EXPECT_EQ("1GB", PrettySize(1 * GB)); + EXPECT_EQ("2GB", PrettySize(2 * GB)); + if (sizeof(size_t) > sizeof(uint32_t)) { + EXPECT_EQ("100GB", PrettySize(100 * GB)); + } + EXPECT_EQ("1024KB", PrettySize(1 * MB)); + EXPECT_EQ("10MB", PrettySize(10 * MB)); + EXPECT_EQ("100MB", PrettySize(100 * MB)); + EXPECT_EQ("1024B", PrettySize(1 * KB)); + EXPECT_EQ("10KB", PrettySize(10 * KB)); + EXPECT_EQ("100KB", PrettySize(100 * KB)); + EXPECT_EQ("0B", PrettySize(0)); + EXPECT_EQ("1B", PrettySize(1)); + EXPECT_EQ("10B", PrettySize(10)); + EXPECT_EQ("100B", PrettySize(100)); + EXPECT_EQ("512B", PrettySize(512)); +} + +TEST_F(UtilsTest, Split) { + std::vector actual; + std::vector expected; + + expected.clear(); + + actual.clear(); + Split("", ':', &actual); + EXPECT_EQ(expected, actual); + + actual.clear(); + Split(":", ':', &actual); + EXPECT_EQ(expected, actual); + + expected.clear(); + expected.push_back("foo"); + + actual.clear(); + Split(":foo", ':', &actual); + EXPECT_EQ(expected, actual); + + actual.clear(); + Split("foo:", ':', &actual); + EXPECT_EQ(expected, actual); + + actual.clear(); + Split(":foo:", ':', &actual); + EXPECT_EQ(expected, actual); + + expected.push_back("bar"); + + actual.clear(); + Split("foo:bar", ':', &actual); + EXPECT_EQ(expected, actual); + + actual.clear(); + Split(":foo:bar", ':', &actual); + EXPECT_EQ(expected, actual); + + actual.clear(); + Split("foo:bar:", ':', &actual); + EXPECT_EQ(expected, actual); + + actual.clear(); + Split(":foo:bar:", ':', &actual); + EXPECT_EQ(expected, actual); + + expected.push_back("baz"); + + actual.clear(); + Split("foo:bar:baz", ':', &actual); + EXPECT_EQ(expected, actual); + + actual.clear(); + Split(":foo:bar:baz", ':', &actual); + EXPECT_EQ(expected, actual); + + actual.clear(); + Split("foo:bar:baz:", ':', &actual); + EXPECT_EQ(expected, actual); + + actual.clear(); + Split(":foo:bar:baz:", ':', &actual); + EXPECT_EQ(expected, actual); +} + +TEST_F(UtilsTest, ArrayCount) { + int i[64]; + EXPECT_EQ(ArrayCount(i), 64u); + char c[7]; + EXPECT_EQ(ArrayCount(c), 7u); +} + +TEST_F(UtilsTest, BoundsCheckedCast) { + char buffer[64]; + const char* buffer_end = buffer + ArrayCount(buffer); + EXPECT_EQ(BoundsCheckedCast(nullptr, buffer, buffer_end), nullptr); + EXPECT_EQ(BoundsCheckedCast(buffer, buffer, buffer_end), + reinterpret_cast(buffer)); + EXPECT_EQ(BoundsCheckedCast(buffer + 56, buffer, buffer_end), + reinterpret_cast(buffer + 56)); + EXPECT_EQ(BoundsCheckedCast(buffer - 1, buffer, buffer_end), nullptr); + EXPECT_EQ(BoundsCheckedCast(buffer + 57, buffer, buffer_end), nullptr); +} + +} // namespace art diff --git a/runtime/base/variant_map.h b/libartbase/base/variant_map.h similarity index 99% rename from runtime/base/variant_map.h rename to libartbase/base/variant_map.h index c480b5162d..4e02c54499 100644 --- a/runtime/base/variant_map.h +++ b/libartbase/base/variant_map.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_VARIANT_MAP_H_ -#define ART_RUNTIME_BASE_VARIANT_MAP_H_ +#ifndef ART_LIBARTBASE_BASE_VARIANT_MAP_H_ +#define ART_LIBARTBASE_BASE_VARIANT_MAP_H_ #include #include @@ -467,4 +467,4 @@ struct VariantMap { } // namespace art -#endif // ART_RUNTIME_BASE_VARIANT_MAP_H_ +#endif // ART_LIBARTBASE_BASE_VARIANT_MAP_H_ diff --git a/runtime/base/variant_map_test.cc b/libartbase/base/variant_map_test.cc similarity index 100% rename from runtime/base/variant_map_test.cc rename to libartbase/base/variant_map_test.cc diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp index 797b4590c4..ae4ded58e2 100644 --- a/libdexfile/Android.bp +++ b/libdexfile/Android.bp @@ -24,6 +24,7 @@ cc_defaults { "dex/descriptors_names.cc", "dex/dex_file.cc", "dex/dex_file_exception_helpers.cc", + "dex/dex_file_layout.cc", "dex/dex_file_loader.cc", "dex/dex_file_tracking_registrar.cc", "dex/dex_file_verifier.cc", @@ -54,6 +55,9 @@ cc_defaults { }, generated_sources: ["dexfile_operator_srcs"], shared_libs: [ + // Important note: relying on libartbase's header_lib is perfectly acceptable. + // However, relying on the libartbase shared library introduces further, possibly cyclic, + // dependencies for clients outside of ART. "liblog", // For common macros. "libbase", @@ -77,6 +81,7 @@ gensrcs { tools: ["generate_operator_out"], srcs: [ "dex/dex_file.h", + "dex/dex_file_layout.h", "dex/dex_instruction.h", "dex/dex_instruction_utils.h", "dex/invoke_type.h", diff --git a/runtime/dex/dex_file_layout.cc b/libdexfile/dex/dex_file_layout.cc similarity index 82% rename from runtime/dex/dex_file_layout.cc rename to libdexfile/dex/dex_file_layout.cc index d85d61d56b..1e36e05f50 100644 --- a/runtime/dex/dex_file_layout.cc +++ b/libdexfile/dex/dex_file_layout.cc @@ -18,11 +18,27 @@ #include -#include "base/file_utils.h" -#include "dex/dex_file.h" +#include "dex_file.h" namespace art { +int DexLayoutSection::MadviseLargestPageAlignedRegion(const uint8_t* begin, + const uint8_t* end, + int advice) { + DCHECK_LE(begin, end); + begin = AlignUp(begin, kPageSize); + end = AlignDown(end, kPageSize); + if (begin < end) { + // TODO: remove the direct dependency on madvise here. + int result = madvise(const_cast(begin), end - begin, advice); + if (result != 0) { + PLOG(WARNING) << "madvise failed " << result; + } + return result; + } + return 0; +} + void DexLayoutSection::Subsection::Madvise(const DexFile* dex_file, int advice) const { DCHECK(dex_file != nullptr); DCHECK_LT(start_offset_, dex_file->Size()); diff --git a/runtime/dex/dex_file_layout.h b/libdexfile/dex/dex_file_layout.h similarity index 93% rename from runtime/dex/dex_file_layout.h rename to libdexfile/dex/dex_file_layout.h index 793e3b5de7..183aefa5a4 100644 --- a/runtime/dex/dex_file_layout.h +++ b/libdexfile/dex/dex_file_layout.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DEX_DEX_FILE_LAYOUT_H_ -#define ART_RUNTIME_DEX_DEX_FILE_LAYOUT_H_ +#ifndef ART_LIBDEXFILE_DEX_DEX_FILE_LAYOUT_H_ +#define ART_LIBDEXFILE_DEX_DEX_FILE_LAYOUT_H_ #include #include @@ -96,6 +96,9 @@ class DexLayoutSection { void Madvise(const DexFile* dex_file, int advice) const; }; + // Madvise the largest page-aligned region contained in [begin, end). + static int MadviseLargestPageAlignedRegion(const uint8_t* begin, const uint8_t* end, int advice); + Subsection parts_[static_cast(LayoutType::kLayoutTypeCount)]; }; @@ -121,4 +124,4 @@ std::ostream& operator<<(std::ostream& os, const DexLayoutSections& sections); } // namespace art -#endif // ART_RUNTIME_DEX_DEX_FILE_LAYOUT_H_ +#endif // ART_LIBDEXFILE_DEX_DEX_FILE_LAYOUT_H_ diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 85c7281102..8069408b5a 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -34,6 +34,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" #include "base/bit_utils_iterator.h" +#include "base/os.h" #include "base/safe_map.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" @@ -69,7 +70,6 @@ #include "oat.h" #include "oat_file-inl.h" #include "oat_file_manager.h" -#include "os.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" #include "stack_map.h" diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h index d4bed6b3da..fac0bb234e 100644 --- a/oatdump/oatdump_test.h +++ b/oatdump/oatdump_test.h @@ -25,13 +25,13 @@ #include "arch/instruction_set.h" #include "base/file_utils.h" +#include "base/os.h" #include "base/unix_file/fd_file.h" +#include "base/utils.h" #include "common_runtime_test.h" #include "exec_utils.h" #include "gc/heap.h" #include "gc/space/image_space.h" -#include "os.h" -#include "utils.h" #include #include diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc index 7a9432656a..e55eec4c30 100644 --- a/openjdkjvmti/ti_class.cc +++ b/openjdkjvmti/ti_class.cc @@ -39,6 +39,7 @@ #include "art_jvmti.h" #include "base/array_ref.h" #include "base/macros.h" +#include "base/utils.h" #include "class_linker.h" #include "class_table-inl.h" #include "common_throws.h" @@ -72,7 +73,6 @@ #include "ti_class_loader-inl.h" #include "ti_phase.h" #include "ti_redefine.h" -#include "utils.h" #include "well_known_classes.h" namespace openjdkjvmti { diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 0115772456..3df640902a 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -35,11 +35,14 @@ #include "base/file_utils.h" #include "base/leb128.h" #include "base/logging.h" // For InitLogging. +#include "base/mutex.h" #include "base/memory_tool.h" +#include "base/os.h" #include "base/scoped_flock.h" #include "base/stringpiece.h" #include "base/unix_file/fd_file.h" #include "base/unix_file/random_access_file_utils.h" +#include "base/utils.h" #include "elf_file.h" #include "elf_file_impl.h" #include "elf_utils.h" @@ -54,11 +57,9 @@ #include "mirror/reference.h" #include "noop_compiler_callbacks.h" #include "offsets.h" -#include "os.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "thread.h" -#include "utils.h" namespace art { @@ -1177,6 +1178,7 @@ static int patchoat_verify_image(TimingLogger& timings, } static int patchoat(int argc, char **argv) { + Locks::Init(); InitLogging(argv, Runtime::Abort); MemMap::Init(); const bool debug = kIsDebugBuild; diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h index feba523f56..2b1210b5b1 100644 --- a/patchoat/patchoat.h +++ b/patchoat/patchoat.h @@ -21,12 +21,12 @@ #include "base/enums.h" #include "base/macros.h" #include "base/mutex.h" +#include "base/os.h" #include "elf_file.h" #include "elf_utils.h" #include "gc/accounting/space_bitmap.h" #include "gc/heap.h" #include "gc/space/image_space.h" -#include "os.h" #include "runtime.h" namespace art { diff --git a/profman/profile_assistant.cc b/profman/profile_assistant.cc index a00b1fa5bd..b509fb4027 100644 --- a/profman/profile_assistant.cc +++ b/profman/profile_assistant.cc @@ -16,8 +16,8 @@ #include "profile_assistant.h" +#include "base/os.h" #include "base/unix_file/fd_file.h" -#include "os.h" namespace art { diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index 6359814615..9494f04a5a 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -19,6 +19,7 @@ #include "android-base/strings.h" #include "art_method-inl.h" #include "base/unix_file/fd_file.h" +#include "base/utils.h" #include "common_runtime_test.h" #include "dex/descriptors_names.h" #include "exec_utils.h" @@ -28,7 +29,6 @@ #include "obj_ptr-inl.h" #include "profile_assistant.h" #include "scoped_thread_state_change-inl.h" -#include "utils.h" namespace art { diff --git a/profman/profman.cc b/profman/profman.cc index 5551c34b60..d1cc56389a 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -34,10 +34,12 @@ #include "base/dumpable.h" #include "base/logging.h" // For InitLogging. +#include "base/mutex.h" #include "base/scoped_flock.h" #include "base/stringpiece.h" #include "base/time_utils.h" #include "base/unix_file/fd_file.h" +#include "base/utils.h" #include "boot_image_profile.h" #include "bytecode_utils.h" #include "dex/art_dex_file_loader.h" @@ -49,7 +51,6 @@ #include "profile_assistant.h" #include "runtime.h" #include "type_reference.h" -#include "utils.h" #include "zip_archive.h" namespace art { @@ -204,6 +205,7 @@ class ProfMan FINAL { original_argc = argc; original_argv = argv; + Locks::Init(); InitLogging(argv, Runtime::Abort); // Skip over the command name. diff --git a/runtime/Android.bp b/runtime/Android.bp index c017c5fed8..daab2326c0 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -31,25 +31,14 @@ cc_defaults { "aot_class_linker.cc", "art_field.cc", "art_method.cc", - "atomic.cc", "barrier.cc", - "base/allocator.cc", "base/arena_allocator.cc", "base/arena_bit_vector.cc", - "base/bit_vector.cc", - "base/file_magic.cc", "base/file_utils.cc", - "base/hex_dump.cc", - "base/logging.cc", "base/mutex.cc", - "base/runtime_debug.cc", - "base/safe_copy.cc", + "base/quasi_atomic.cc", "base/scoped_arena_allocator.cc", - "base/scoped_flock.cc", - "base/time_utils.cc", "base/timing_logger.cc", - "base/unix_file/fd_file.cc", - "base/unix_file/random_access_file_utils.cc", "cha.cc", "check_jni.cc", "class_linker.cc", @@ -60,7 +49,6 @@ cc_defaults { "debugger.cc", "dex/art_dex_file_loader.cc", "dex/dex_file_annotations.cc", - "dex/dex_file_layout.cc", "dex_to_dex_decompiler.cc", "elf_file.cc", "exec_utils.cc", @@ -192,7 +180,6 @@ cc_defaults { "oat_quick_method_header.cc", "object_lock.cc", "offsets.cc", - "os_linux.cc", "parsed_options.cc", "plugin.cc", "quick_exception_handler.cc", @@ -215,7 +202,6 @@ cc_defaults { "trace.cc", "transaction.cc", "type_lookup_table.cc", - "utils.cc", "vdex_file.cc", "verifier/instruction_flags.cc", "verifier/method_verifier.cc", @@ -406,7 +392,6 @@ cc_defaults { ], header_libs: [ "art_cmdlineparser_headers", - "art_libartbase_headers", "libnativehelper_header_only", "jni_platform_headers", ], @@ -440,13 +425,9 @@ gensrcs { tools: ["generate_operator_out"], srcs: [ "arch/instruction_set.h", - "base/allocator.h", - "base/callee_save_type.h", "base/mutex.h", - "base/unix_file/fd_file.h", "class_status.h", "debugger.h", - "dex/dex_file_layout.h", "gc_root.h", "gc/allocator_type.h", "gc/allocator/rosalloc.h", @@ -487,8 +468,15 @@ art_cc_library { strip: { keep_symbols: true, }, - shared_libs: ["libdexfile"], - export_shared_lib_headers: ["libdexfile"], + whole_static_libs: [ + "libartbase", + ], + shared_libs: [ + "libdexfile", + ], + export_shared_lib_headers: [ + "libdexfile", + ], } art_cc_library { @@ -497,8 +485,15 @@ art_cc_library { "art_debug_defaults", "libart_defaults", ], - shared_libs: ["libdexfiled"], - export_shared_lib_headers: ["libdexfiled"], + whole_static_libs: [ + "libartbased", + ], + shared_libs: [ + "libdexfiled", + ], + export_shared_lib_headers: [ + "libdexfiled", + ], } art_cc_library { @@ -540,23 +535,9 @@ art_cc_test { "arch/x86_64/instruction_set_features_x86_64_test.cc", "barrier_test.cc", "base/arena_allocator_test.cc", - "base/bit_field_test.cc", - "base/bit_string_test.cc", - "base/bit_struct_test.cc", - "base/bit_vector_test.cc", "base/file_utils_test.cc", - "base/hex_dump_test.cc", - "base/histogram_test.cc", - "base/logging_test.cc", "base/mutex_test.cc", - "base/safe_copy_test.cc", - "base/scoped_flock_test.cc", - "base/time_utils_test.cc", "base/timing_logger_test.cc", - "base/transform_array_ref_test.cc", - "base/transform_iterator_test.cc", - "base/variant_map_test.cc", - "base/unix_file/fd_file_test.cc", "cha_test.cc", "class_linker_test.cc", "class_loader_context_test.cc", @@ -615,7 +596,6 @@ art_cc_test { "thread_pool_test.cc", "transaction_test.cc", "type_lookup_table_test.cc", - "utils_test.cc", "vdex_file_test.cc", "verifier/method_verifier_test.cc", "verifier/reg_type_test.cc", diff --git a/runtime/arch/arm/quick_entrypoints_cc_arm.cc b/runtime/arch/arm/quick_entrypoints_cc_arm.cc index 232ec3140e..987b4590b7 100644 --- a/runtime/arch/arm/quick_entrypoints_cc_arm.cc +++ b/runtime/arch/arm/quick_entrypoints_cc_arm.cc @@ -15,7 +15,7 @@ */ #include "art_method.h" -#include "utils.h" // For RoundUp(). +#include "base/utils.h" // For RoundUp(). namespace art { diff --git a/runtime/arch/instruction_set_features.cc b/runtime/arch/instruction_set_features.cc index b6b24c24fb..0c45bc9197 100644 --- a/runtime/arch/instruction_set_features.cc +++ b/runtime/arch/instruction_set_features.cc @@ -19,7 +19,7 @@ #include "android-base/strings.h" #include "base/casts.h" -#include "utils.h" +#include "base/utils.h" #include "arm/instruction_set_features_arm.h" #include "arm64/instruction_set_features_arm64.h" diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index badee59568..9418caf98c 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -17,8 +17,9 @@ #include #include "arch/mips/asm_support_mips.h" -#include "atomic.h" +#include "base/atomic.h" #include "base/logging.h" +#include "base/quasi_atomic.h" #include "entrypoints/entrypoint_utils.h" #include "entrypoints/jni/jni_entrypoints.h" #include "entrypoints/math_entrypoints.h" diff --git a/runtime/arch/mips64/entrypoints_init_mips64.cc b/runtime/arch/mips64/entrypoints_init_mips64.cc index bdfb9421df..2acfe147f8 100644 --- a/runtime/arch/mips64/entrypoints_init_mips64.cc +++ b/runtime/arch/mips64/entrypoints_init_mips64.cc @@ -18,7 +18,8 @@ #include #include "arch/mips64/asm_support_mips64.h" -#include "atomic.h" +#include "base/atomic.h" +#include "base/quasi_atomic.h" #include "entrypoints/entrypoint_utils.h" #include "entrypoints/jni/jni_entrypoints.h" #include "entrypoints/math_entrypoints.h" diff --git a/runtime/art_field.cc b/runtime/art_field.cc index 3f70958cff..b867621f02 100644 --- a/runtime/art_field.cc +++ b/runtime/art_field.cc @@ -17,6 +17,7 @@ #include "art_field.h" #include "art_field-inl.h" +#include "base/utils.h" #include "class_linker-inl.h" #include "dex/descriptors_names.h" #include "gc/accounting/card_table-inl.h" @@ -26,7 +27,6 @@ #include "mirror/object_array-inl.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" -#include "utils.h" #include "well_known_classes.h" namespace art { diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 92769942c0..8bf91d9da1 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -21,6 +21,7 @@ #include "art_field.h" #include "base/callee_save_type.h" +#include "base/utils.h" #include "class_linker-inl.h" #include "common_throws.h" #include "dex/code_item_accessors-inl.h" @@ -44,7 +45,6 @@ #include "runtime-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" -#include "utils.h" namespace art { diff --git a/runtime/barrier_test.cc b/runtime/barrier_test.cc index ecdabba8a5..04bb6bab1e 100644 --- a/runtime/barrier_test.cc +++ b/runtime/barrier_test.cc @@ -18,7 +18,7 @@ #include -#include "atomic.h" +#include "base/atomic.h" #include "common_runtime_test.h" #include "mirror/object_array-inl.h" #include "thread-current-inl.h" diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc index e87f631c2e..292bde0272 100644 --- a/runtime/base/arena_allocator.cc +++ b/runtime/base/arena_allocator.cc @@ -25,9 +25,9 @@ #include +#include "base/systrace.h" #include "mem_map.h" #include "mutex.h" -#include "systrace.h" #include "thread-current-inl.h" namespace art { diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h index 060b6fac2e..c3011091e8 100644 --- a/runtime/base/arena_allocator.h +++ b/runtime/base/arena_allocator.h @@ -21,10 +21,10 @@ #include #include "base/bit_utils.h" +#include "base/debug_stack.h" +#include "base/dchecked_vector.h" #include "base/macros.h" #include "base/memory_tool.h" -#include "dchecked_vector.h" -#include "debug_stack.h" #include "mutex.h" namespace art { diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc index dd3f8d5b3c..f9d0d12d88 100644 --- a/runtime/base/file_utils.cc +++ b/runtime/base/file_utils.cc @@ -17,10 +17,7 @@ #include "file_utils.h" #include -#include -#include // For madvise #include -#include #include #include #include @@ -47,10 +44,10 @@ #include "base/bit_utils.h" #include "base/stl_util.h" +#include "base/os.h" #include "base/unix_file/fd_file.h" #include "dex/dex_file_loader.h" #include "globals.h" -#include "os.h" #if defined(__APPLE__) #include @@ -308,19 +305,6 @@ std::string GetSystemImageFilename(const char* location, const InstructionSet is return filename; } -bool FileExists(const std::string& filename) { - struct stat buffer; - return stat(filename.c_str(), &buffer) == 0; -} - -bool FileExistsAndNotEmpty(const std::string& filename) { - struct stat buffer; - if (stat(filename.c_str(), &buffer) != 0) { - return false; - } - return buffer.st_size > 0; -} - std::string ReplaceFileExtension(const std::string& filename, const std::string& new_extension) { const size_t last_ext = filename.find_last_of('.'); if (last_ext == std::string::npos) { @@ -330,26 +314,6 @@ std::string ReplaceFileExtension(const std::string& filename, const std::string& } } -int64_t GetFileSizeBytes(const std::string& filename) { - struct stat stat_buf; - int rc = stat(filename.c_str(), &stat_buf); - return rc == 0 ? stat_buf.st_size : -1; -} - -int MadviseLargestPageAlignedRegion(const uint8_t* begin, const uint8_t* end, int advice) { - DCHECK_LE(begin, end); - begin = AlignUp(begin, kPageSize); - end = AlignDown(end, kPageSize); - if (begin < end) { - int result = madvise(const_cast(begin), end - begin, advice); - if (result != 0) { - PLOG(WARNING) << "madvise failed " << result; - } - return result; - } - return 0; -} - bool LocationIsOnSystem(const char* location) { UniqueCPtr path(realpath(location, nullptr)); return path != nullptr && android::base::StartsWith(path.get(), GetAndroidRoot().c_str()); diff --git a/runtime/base/file_utils.h b/runtime/base/file_utils.h index cac0950d9c..7f691d546b 100644 --- a/runtime/base/file_utils.h +++ b/runtime/base/file_utils.h @@ -65,10 +65,6 @@ std::string GetSystemImageFilename(const char* location, InstructionSet isa); // Returns the vdex filename for the given oat filename. std::string GetVdexFilename(const std::string& oat_filename); -// Returns true if the file exists. -bool FileExists(const std::string& filename); -bool FileExistsAndNotEmpty(const std::string& filename); - // Returns `filename` with the text after the last occurrence of '.' replaced with // `extension`. If `filename` does not contain a period, returns a string containing `filename`, // a period, and `new_extension`. @@ -76,12 +72,6 @@ bool FileExistsAndNotEmpty(const std::string& filename); // ReplaceFileExtension("foo", "abc") == "foo.abc" std::string ReplaceFileExtension(const std::string& filename, const std::string& new_extension); -// Return the file size in bytes or -1 if the file does not exists. -int64_t GetFileSizeBytes(const std::string& filename); - -// Madvise the largest page aligned region within begin and end. -int MadviseLargestPageAlignedRegion(const uint8_t* begin, const uint8_t* end, int advice); - // Return whether the location is on system (i.e. android root). bool LocationIsOnSystem(const char* location); diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h index 01adbf17e2..d6dbab4606 100644 --- a/runtime/base/mutex-inl.h +++ b/runtime/base/mutex-inl.h @@ -21,9 +21,9 @@ #include "mutex.h" +#include "base/utils.h" #include "base/value_object.h" #include "thread.h" -#include "utils.h" #if ART_USE_FUTEXES #include "linux/futex.h" diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 3d2226c1dc..a1f30b6794 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -21,7 +21,7 @@ #include "android-base/stringprintf.h" -#include "atomic.h" +#include "base/atomic.h" #include "base/logging.h" #include "base/systrace.h" #include "base/time_utils.h" diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 4f7001a101..437661798f 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -26,10 +26,10 @@ #include -#include "atomic.h" #include "base/aborting.h" +#include "base/atomic.h" +#include "base/globals.h" #include "base/macros.h" -#include "globals.h" #if defined(__APPLE__) #define ART_USE_FUTEXES 0 @@ -50,6 +50,7 @@ class SHARED_LOCKABLE ReaderWriterMutex; class SHARED_LOCKABLE MutatorMutex; class ScopedContentionRecorder; class Thread; +class Mutex; // LockLevel is used to impose a lock hierarchy [1] where acquisition of a Mutex at a higher or // equal level to a lock a thread holds is invalid. The lock hierarchy achieves a cycle free diff --git a/runtime/atomic.cc b/runtime/base/quasi_atomic.cc similarity index 98% rename from runtime/atomic.cc rename to runtime/base/quasi_atomic.cc index 07aceb7cfc..1a82812981 100644 --- a/runtime/atomic.cc +++ b/runtime/base/quasi_atomic.cc @@ -14,7 +14,8 @@ * limitations under the License. */ -#include "atomic.h" +#include "base/quasi_atomic.h" + #include "base/mutex.h" #include "base/stl_util.h" #include "thread-current-inl.h" diff --git a/runtime/base/quasi_atomic.h b/runtime/base/quasi_atomic.h new file mode 100644 index 0000000000..067d01db01 --- /dev/null +++ b/runtime/base/quasi_atomic.h @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2008 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_BASE_QUASI_ATOMIC_H_ +#define ART_RUNTIME_BASE_QUASI_ATOMIC_H_ + +#include +#include +#include +#include + +#include + +#include "arch/instruction_set.h" +#include "base/macros.h" + +namespace art { + +class Mutex; + +// QuasiAtomic encapsulates two separate facilities that we are +// trying to move away from: "quasiatomic" 64 bit operations +// and custom memory fences. For the time being, they remain +// exposed. Clients should be converted to use either class Atomic +// below whenever possible, and should eventually use C++11 atomics. +// The two facilities that do not have a good C++11 analog are +// ThreadFenceForConstructor and Atomic::*JavaData. +// +// NOTE: Two "quasiatomic" operations on the exact same memory address +// are guaranteed to operate atomically with respect to each other, +// but no guarantees are made about quasiatomic operations mixed with +// non-quasiatomic operations on the same address, nor about +// quasiatomic operations that are performed on partially-overlapping +// memory. +class QuasiAtomic { + static constexpr bool NeedSwapMutexes(InstructionSet isa) { + // TODO - mips64 still need this for Cas64 ??? + return (isa == InstructionSet::kMips) || (isa == InstructionSet::kMips64); + } + + public: + static void Startup(); + + static void Shutdown(); + + // Reads the 64-bit value at "addr" without tearing. + static int64_t Read64(volatile const int64_t* addr) { + if (!NeedSwapMutexes(kRuntimeISA)) { + int64_t value; +#if defined(__LP64__) + value = *addr; +#else +#if defined(__arm__) +#if defined(__ARM_FEATURE_LPAE) + // With LPAE support (such as Cortex-A15) then ldrd is defined not to tear. + __asm__ __volatile__("@ QuasiAtomic::Read64\n" + "ldrd %0, %H0, %1" + : "=r" (value) + : "m" (*addr)); +#else + // Exclusive loads are defined not to tear, clearing the exclusive state isn't necessary. + __asm__ __volatile__("@ QuasiAtomic::Read64\n" + "ldrexd %0, %H0, %1" + : "=r" (value) + : "Q" (*addr)); +#endif +#elif defined(__i386__) + __asm__ __volatile__( + "movq %1, %0\n" + : "=x" (value) + : "m" (*addr)); +#else + LOG(FATAL) << "Unsupported architecture"; +#endif +#endif // defined(__LP64__) + return value; + } else { + return SwapMutexRead64(addr); + } + } + + // Writes to the 64-bit value at "addr" without tearing. + static void Write64(volatile int64_t* addr, int64_t value) { + if (!NeedSwapMutexes(kRuntimeISA)) { +#if defined(__LP64__) + *addr = value; +#else +#if defined(__arm__) +#if defined(__ARM_FEATURE_LPAE) + // If we know that ARM architecture has LPAE (such as Cortex-A15) strd is defined not to tear. + __asm__ __volatile__("@ QuasiAtomic::Write64\n" + "strd %1, %H1, %0" + : "=m"(*addr) + : "r" (value)); +#else + // The write is done as a swap so that the cache-line is in the exclusive state for the store. + int64_t prev; + int status; + do { + __asm__ __volatile__("@ QuasiAtomic::Write64\n" + "ldrexd %0, %H0, %2\n" + "strexd %1, %3, %H3, %2" + : "=&r" (prev), "=&r" (status), "+Q"(*addr) + : "r" (value) + : "cc"); + } while (UNLIKELY(status != 0)); +#endif +#elif defined(__i386__) + __asm__ __volatile__( + "movq %1, %0" + : "=m" (*addr) + : "x" (value)); +#else + LOG(FATAL) << "Unsupported architecture"; +#endif +#endif // defined(__LP64__) + } else { + SwapMutexWrite64(addr, value); + } + } + + // Atomically compare the value at "addr" to "old_value", if equal replace it with "new_value" + // and return true. Otherwise, don't swap, and return false. + // This is fully ordered, i.e. it has C++11 memory_order_seq_cst + // semantics (assuming all other accesses use a mutex if this one does). + // This has "strong" semantics; if it fails then it is guaranteed that + // at some point during the execution of Cas64, *addr was not equal to + // old_value. + static bool Cas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) { + if (!NeedSwapMutexes(kRuntimeISA)) { + return __sync_bool_compare_and_swap(addr, old_value, new_value); + } else { + return SwapMutexCas64(old_value, new_value, addr); + } + } + + // Does the architecture provide reasonable atomic long operations or do we fall back on mutexes? + static bool LongAtomicsUseMutexes(InstructionSet isa) { + return NeedSwapMutexes(isa); + } + + static void ThreadFenceAcquire() { + std::atomic_thread_fence(std::memory_order_acquire); + } + + static void ThreadFenceRelease() { + std::atomic_thread_fence(std::memory_order_release); + } + + static void ThreadFenceForConstructor() { + #if defined(__aarch64__) + __asm__ __volatile__("dmb ishst" : : : "memory"); + #else + std::atomic_thread_fence(std::memory_order_release); + #endif + } + + static void ThreadFenceSequentiallyConsistent() { + std::atomic_thread_fence(std::memory_order_seq_cst); + } + + private: + static Mutex* GetSwapMutex(const volatile int64_t* addr); + static int64_t SwapMutexRead64(volatile const int64_t* addr); + static void SwapMutexWrite64(volatile int64_t* addr, int64_t val); + static bool SwapMutexCas64(int64_t old_value, int64_t new_value, volatile int64_t* addr); + + // We stripe across a bunch of different mutexes to reduce contention. + static constexpr size_t kSwapMutexCount = 32; + static std::vector* gSwapMutexes; + + DISALLOW_COPY_AND_ASSIGN(QuasiAtomic); +}; + +} // namespace art + +#endif // ART_RUNTIME_BASE_QUASI_ATOMIC_H_ diff --git a/runtime/base/scoped_arena_allocator.h b/runtime/base/scoped_arena_allocator.h index 202902e5aa..a253e2f535 100644 --- a/runtime/base/scoped_arena_allocator.h +++ b/runtime/base/scoped_arena_allocator.h @@ -20,9 +20,9 @@ #include #include "arena_allocator.h" +#include "base/debug_stack.h" +#include "base/globals.h" #include "base/macros.h" -#include "debug_stack.h" -#include "globals.h" namespace art { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index c667fe2f30..ded9f96206 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -38,12 +38,15 @@ #include "base/casts.h" #include "base/leb128.h" #include "base/logging.h" +#include "base/os.h" +#include "base/quasi_atomic.h" #include "base/scoped_arena_containers.h" #include "base/scoped_flock.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/time_utils.h" #include "base/unix_file/fd_file.h" +#include "base/utils.h" #include "base/value_object.h" #include "cha.h" #include "class_linker-inl.h" @@ -111,14 +114,12 @@ #include "oat_file_assistant.h" #include "oat_file_manager.h" #include "object_lock.h" -#include "os.h" #include "runtime.h" #include "runtime_callbacks.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" #include "thread_list.h" #include "trace.h" -#include "utils.h" #include "utils/dex_cache_arrays_layout-inl.h" #include "verifier/method_verifier.h" #include "well_known_classes.h" diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 80ef6544e8..8cd0604ac3 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -1596,6 +1596,63 @@ TEST_F(ClassLinkerTest, CreateWellKnownClassLoader) { LoadDexInDelegateLastClassLoader("Interfaces", class_loader_c); } +TEST_F(ClassLinkerTest, PrettyClass) { + ScopedObjectAccess soa(Thread::Current()); + EXPECT_EQ("null", mirror::Class::PrettyClass(nullptr)); + mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); + ASSERT_TRUE(c != nullptr); + mirror::Object* o = mirror::ObjectArray::Alloc(soa.Self(), c, 0); + EXPECT_EQ("java.lang.Class", mirror::Class::PrettyClass(o->GetClass())); +} + +TEST_F(ClassLinkerTest, PrettyClassAndClassLoader) { + ScopedObjectAccess soa(Thread::Current()); + EXPECT_EQ("null", mirror::Class::PrettyClassAndClassLoader(nullptr)); + mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); + ASSERT_TRUE(c != nullptr); + mirror::Object* o = mirror::ObjectArray::Alloc(soa.Self(), c, 0); + EXPECT_EQ("java.lang.Class", + mirror::Class::PrettyClassAndClassLoader(o->GetClass())); +} + +TEST_F(ClassLinkerTest, PrettyField) { + ScopedObjectAccess soa(Thread::Current()); + EXPECT_EQ("null", ArtField::PrettyField(nullptr)); + + mirror::Class* java_lang_String = class_linker_->FindSystemClass(soa.Self(), + "Ljava/lang/String;"); + + ArtField* f; + f = java_lang_String->FindDeclaredInstanceField("count", "I"); + EXPECT_EQ("int java.lang.String.count", f->PrettyField()); + EXPECT_EQ("java.lang.String.count", f->PrettyField(false)); +} + +TEST_F(ClassLinkerTest, JniShortName_JniLongName) { + ScopedObjectAccess soa(Thread::Current()); + mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/String;"); + ASSERT_TRUE(c != nullptr); + ArtMethod* m; + + m = c->FindClassMethod("charAt", "(I)C", kRuntimePointerSize); + ASSERT_TRUE(m != nullptr); + ASSERT_FALSE(m->IsDirect()); + EXPECT_EQ("Java_java_lang_String_charAt", m->JniShortName()); + EXPECT_EQ("Java_java_lang_String_charAt__I", m->JniLongName()); + + m = c->FindClassMethod("indexOf", "(Ljava/lang/String;I)I", kRuntimePointerSize); + ASSERT_TRUE(m != nullptr); + ASSERT_FALSE(m->IsDirect()); + EXPECT_EQ("Java_java_lang_String_indexOf", m->JniShortName()); + EXPECT_EQ("Java_java_lang_String_indexOf__Ljava_lang_String_2I", m->JniLongName()); + + m = c->FindClassMethod("copyValueOf", "([CII)Ljava/lang/String;", kRuntimePointerSize); + ASSERT_TRUE(m != nullptr); + ASSERT_TRUE(m->IsStatic()); + EXPECT_EQ("Java_java_lang_String_copyValueOf", m->JniShortName()); + EXPECT_EQ("Java_java_lang_String_copyValueOf___3CII", m->JniLongName()); +} + class ClassLinkerClassLoaderTest : public ClassLinkerTest { protected: // Verifies that the class identified by the given descriptor is loaded with diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index e4fbc86020..add300bdf3 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -30,6 +30,8 @@ #include "base/file_utils.h" #include "base/logging.h" #include "base/macros.h" +#include "base/mutex.h" +#include "base/os.h" #include "base/runtime_debug.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" @@ -51,7 +53,6 @@ #include "mirror/class_loader.h" #include "native/dalvik_system_DexFile.h" #include "noop_compiler_callbacks.h" -#include "os.h" #include "runtime-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread.h" @@ -63,6 +64,7 @@ int main(int argc, char **argv) { // everything else. In case you want to see all messages, comment out the line. setenv("ANDROID_LOG_TAGS", "*:e", 1); + art::Locks::Init(); art::InitLogging(argv, art::Runtime::Abort); LOG(INFO) << "Running main() from common_runtime_test.cc..."; testing::InitGoogleTest(&argc, argv); diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 85b0dbb43c..7fc70e294f 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -26,13 +26,13 @@ #include "arch/instruction_set.h" #include "base/mutex.h" +#include "base/os.h" #include "base/unix_file/fd_file.h" #include "dex/art_dex_file_loader.h" #include "dex/compact_dex_level.h" #include "globals.h" // TODO: Add inl file and avoid including inl. #include "obj_ptr-inl.h" -#include "os.h" #include "scoped_thread_state_change-inl.h" namespace art { diff --git a/runtime/compiler_filter.cc b/runtime/compiler_filter.cc index 7b2dd05156..bda64ebf25 100644 --- a/runtime/compiler_filter.cc +++ b/runtime/compiler_filter.cc @@ -16,7 +16,7 @@ #include "compiler_filter.h" -#include "utils.h" +#include "base/utils.h" namespace art { diff --git a/runtime/dex/art_dex_file_loader_test.cc b/runtime/dex/art_dex_file_loader_test.cc index 25d4dd0875..6e2cfec381 100644 --- a/runtime/dex/art_dex_file_loader_test.cc +++ b/runtime/dex/art_dex_file_loader_test.cc @@ -19,6 +19,7 @@ #include #include "art_dex_file_loader.h" +#include "base/os.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "common_runtime_test.h" @@ -29,7 +30,6 @@ #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "mem_map.h" -#include "os.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h index 75642fc889..00a95cc7bd 100644 --- a/runtime/dex2oat_environment_test.h +++ b/runtime/dex2oat_environment_test.h @@ -24,7 +24,9 @@ #include #include "base/file_utils.h" +#include "base/os.h" #include "base/stl_util.h" +#include "base/utils.h" #include "common_runtime_test.h" #include "compiler_callbacks.h" #include "dex/art_dex_file_loader.h" @@ -33,9 +35,7 @@ #include "gc/heap.h" #include "gc/space/image_space.h" #include "oat_file_assistant.h" -#include "os.h" #include "runtime.h" -#include "utils.h" namespace art { diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index b4661819ef..719b4af293 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -28,9 +28,9 @@ #include "base/leb128.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" +#include "base/utils.h" #include "elf_file_impl.h" #include "elf_utils.h" -#include "utils.h" namespace art { diff --git a/runtime/elf_file.h b/runtime/elf_file.h index b1c9395fb5..ab9e6fa2ec 100644 --- a/runtime/elf_file.h +++ b/runtime/elf_file.h @@ -21,9 +21,9 @@ #include #include "base/macros.h" +#include "base/os.h" // Explicitly include our own elf.h to avoid Linux and other dependencies. #include "./elf.h" -#include "os.h" namespace art { template diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc index 1ab67ec2b9..ed5885f224 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc @@ -18,6 +18,7 @@ #include "art_method-inl.h" #include "base/enums.h" +#include "base/quasi_atomic.h" #include "callee_save_frame.h" #include "dex/dex_file_types.h" #include "entrypoints/entrypoint_utils-inl.h" diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h index f8d8271335..6b103bfe1b 100644 --- a/runtime/gc/accounting/atomic_stack.h +++ b/runtime/gc/accounting/atomic_stack.h @@ -25,7 +25,7 @@ #include -#include "atomic.h" +#include "base/atomic.h" #include "base/macros.h" #include "mem_map.h" #include "stack_reference.h" diff --git a/runtime/gc/accounting/bitmap-inl.h b/runtime/gc/accounting/bitmap-inl.h index bf153f56d8..a71b212af3 100644 --- a/runtime/gc/accounting/bitmap-inl.h +++ b/runtime/gc/accounting/bitmap-inl.h @@ -23,7 +23,7 @@ #include -#include "atomic.h" +#include "base/atomic.h" #include "base/bit_utils.h" namespace art { diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h index adca5c835e..14f5d0e1c6 100644 --- a/runtime/gc/accounting/card_table-inl.h +++ b/runtime/gc/accounting/card_table-inl.h @@ -21,7 +21,7 @@ #include -#include "atomic.h" +#include "base/atomic.h" #include "base/bit_utils.h" #include "mem_map.h" #include "space_bitmap.h" diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc index 934e57a61b..4eea607c39 100644 --- a/runtime/gc/accounting/card_table.cc +++ b/runtime/gc/accounting/card_table.cc @@ -19,13 +19,13 @@ #include #include "base/systrace.h" +#include "base/utils.h" #include "card_table-inl.h" #include "gc/heap.h" #include "gc/space/space.h" #include "heap_bitmap.h" #include "mem_map.h" #include "runtime.h" -#include "utils.h" namespace art { namespace gc { diff --git a/runtime/gc/accounting/card_table_test.cc b/runtime/gc/accounting/card_table_test.cc index cb2479ffad..87965eddb8 100644 --- a/runtime/gc/accounting/card_table_test.cc +++ b/runtime/gc/accounting/card_table_test.cc @@ -18,14 +18,14 @@ #include -#include "atomic.h" +#include "base/atomic.h" +#include "base/utils.h" #include "common_runtime_test.h" #include "handle_scope-inl.h" #include "mirror/class-inl.h" #include "mirror/string-inl.h" // Strings are easiest to allocate #include "scoped_thread_state_change-inl.h" #include "thread_pool.h" -#include "utils.h" namespace art { diff --git a/runtime/gc/accounting/remembered_set.h b/runtime/gc/accounting/remembered_set.h index e9376a90ef..b96f0d318c 100644 --- a/runtime/gc/accounting/remembered_set.h +++ b/runtime/gc/accounting/remembered_set.h @@ -18,8 +18,9 @@ #define ART_RUNTIME_GC_ACCOUNTING_REMEMBERED_SET_H_ #include "base/allocator.h" +#include "base/globals.h" +#include "base/mutex.h" #include "base/safe_map.h" -#include "globals.h" #include #include diff --git a/runtime/gc/accounting/space_bitmap-inl.h b/runtime/gc/accounting/space_bitmap-inl.h index 354b9e1dbd..384e3c2f4c 100644 --- a/runtime/gc/accounting/space_bitmap-inl.h +++ b/runtime/gc/accounting/space_bitmap-inl.h @@ -23,7 +23,7 @@ #include -#include "atomic.h" +#include "base/atomic.h" #include "base/bit_utils.h" namespace art { diff --git a/runtime/gc/allocator/dlmalloc.cc b/runtime/gc/allocator/dlmalloc.cc index 4570e9c1b8..e508d5fddf 100644 --- a/runtime/gc/allocator/dlmalloc.cc +++ b/runtime/gc/allocator/dlmalloc.cc @@ -59,8 +59,8 @@ static void art_heap_usage_error(const char* function, void* p) { #include -#include "globals.h" -#include "utils.h" +#include "base/globals.h" +#include "base/utils.h" extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* arg) { // Is this chunk in use? diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index a78813bf7c..b10c504dd5 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -20,6 +20,7 @@ #include "base/enums.h" #include "base/file_utils.h" #include "base/histogram-inl.h" +#include "base/quasi_atomic.h" #include "base/stl_util.h" #include "base/systrace.h" #include "debugger.h" diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc index fa34270d95..5e3692ea9a 100644 --- a/runtime/gc/collector/garbage_collector.cc +++ b/runtime/gc/collector/garbage_collector.cc @@ -26,6 +26,7 @@ #include "base/mutex-inl.h" #include "base/systrace.h" #include "base/time_utils.h" +#include "base/utils.h" #include "gc/accounting/heap_bitmap.h" #include "gc/gc_pause_listener.h" #include "gc/heap.h" @@ -34,7 +35,6 @@ #include "runtime.h" #include "thread-current-inl.h" #include "thread_list.h" -#include "utils.h" namespace art { namespace gc { diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h index 41228099d3..e7749597cd 100644 --- a/runtime/gc/collector/mark_compact.h +++ b/runtime/gc/collector/mark_compact.h @@ -20,7 +20,7 @@ #include #include // For unique_ptr. -#include "atomic.h" +#include "base/atomic.h" #include "base/macros.h" #include "base/mutex.h" #include "garbage_collector.h" diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index 53b899e09e..5e0fe0607f 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -19,7 +19,7 @@ #include -#include "atomic.h" +#include "base/atomic.h" #include "barrier.h" #include "base/macros.h" #include "base/mutex.h" diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h index fc77c17497..d1d45c8df6 100644 --- a/runtime/gc/collector/semi_space.h +++ b/runtime/gc/collector/semi_space.h @@ -19,7 +19,7 @@ #include -#include "atomic.h" +#include "base/atomic.h" #include "base/macros.h" #include "base/mutex.h" #include "garbage_collector.h" diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 6735961591..41ee18350d 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -20,7 +20,9 @@ #include "heap.h" #include "allocation_listener.h" +#include "base/quasi_atomic.h" #include "base/time_utils.h" +#include "base/utils.h" #include "gc/accounting/atomic_stack.h" #include "gc/accounting/card_table-inl.h" #include "gc/allocation_record.h" @@ -34,7 +36,6 @@ #include "obj_ptr-inl.h" #include "runtime.h" #include "thread-inl.h" -#include "utils.h" #include "verify_object.h" namespace art { diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 19b4acd836..3dc2cb572e 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -32,6 +32,7 @@ #include "base/histogram-inl.h" #include "base/logging.h" // For VLOG. #include "base/memory_tool.h" +#include "base/os.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/time_utils.h" @@ -81,7 +82,6 @@ #include "mirror/reference-inl.h" #include "nativehelper/scoped_local_ref.h" #include "obj_ptr-inl.h" -#include "os.h" #include "reflection.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 592172f9fe..4de03318a0 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -26,7 +26,7 @@ #include "allocator_type.h" #include "arch/instruction_set.h" -#include "atomic.h" +#include "base/atomic.h" #include "base/macros.h" #include "base/mutex.h" #include "base/runtime_debug.h" diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc index c59642fe4e..356f3ecaa8 100644 --- a/runtime/gc/reference_processor.cc +++ b/runtime/gc/reference_processor.cc @@ -17,6 +17,7 @@ #include "reference_processor.h" #include "base/time_utils.h" +#include "base/utils.h" #include "collector/garbage_collector.h" #include "java_vm_ext.h" #include "mirror/class-inl.h" @@ -28,7 +29,6 @@ #include "reflection.h" #include "scoped_thread_state_change-inl.h" #include "task_processor.h" -#include "utils.h" #include "well_known_classes.h" namespace art { diff --git a/runtime/gc/reference_queue.h b/runtime/gc/reference_queue.h index c48d48c530..af7788130b 100644 --- a/runtime/gc/reference_queue.h +++ b/runtime/gc/reference_queue.h @@ -21,7 +21,7 @@ #include #include -#include "atomic.h" +#include "base/atomic.h" #include "base/mutex.h" #include "base/timing_logger.h" #include "globals.h" diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index a3eef90e3a..025c3f0ead 100644 --- a/runtime/gc/space/dlmalloc_space.cc +++ b/runtime/gc/space/dlmalloc_space.cc @@ -18,6 +18,7 @@ #include "base/logging.h" // For VLOG. #include "base/time_utils.h" +#include "base/utils.h" #include "gc/accounting/card_table.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/heap.h" @@ -30,7 +31,6 @@ #include "scoped_thread_state_change-inl.h" #include "thread.h" #include "thread_list.h" -#include "utils.h" namespace art { namespace gc { diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 366eb535f4..c100bc0c75 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -32,10 +32,12 @@ #include "base/enums.h" #include "base/file_utils.h" #include "base/macros.h" +#include "base/os.h" #include "base/scoped_flock.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/time_utils.h" +#include "base/utils.h" #include "dex/art_dex_file_loader.h" #include "dex/dex_file_loader.h" #include "exec_utils.h" @@ -46,10 +48,8 @@ #include "mirror/object-inl.h" #include "mirror/object-refvisitor-inl.h" #include "oat_file.h" -#include "os.h" #include "runtime.h" #include "space-inl.h" -#include "utils.h" namespace art { namespace gc { diff --git a/runtime/gc/space/image_space_fs.h b/runtime/gc/space/image_space_fs.h index 6ce81e9209..14deb6f001 100644 --- a/runtime/gc/space/image_space_fs.h +++ b/runtime/gc/space/image_space_fs.h @@ -23,13 +23,13 @@ #include "android-base/stringprintf.h" #include "base/file_utils.h" +#include "base/globals.h" #include "base/logging.h" // For VLOG. #include "base/macros.h" +#include "base/os.h" #include "base/unix_file/fd_file.h" -#include "globals.h" -#include "os.h" +#include "base/utils.h" #include "runtime.h" -#include "utils.h" namespace art { namespace gc { diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc index d2efb102e9..512cde484d 100644 --- a/runtime/gc/space/large_object_space.cc +++ b/runtime/gc/space/large_object_space.cc @@ -25,12 +25,12 @@ #include "base/macros.h" #include "base/memory_tool.h" #include "base/mutex-inl.h" +#include "base/os.h" #include "base/stl_util.h" #include "gc/accounting/heap_bitmap-inl.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/heap.h" #include "image.h" -#include "os.h" #include "scoped_thread_state_change-inl.h" #include "space-inl.h" #include "thread-current-inl.h" diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc index 17274b508d..0965560e2c 100644 --- a/runtime/gc/space/malloc_space.cc +++ b/runtime/gc/space/malloc_space.cc @@ -19,6 +19,7 @@ #include "android-base/stringprintf.h" #include "base/logging.h" // For VLOG +#include "base/utils.h" #include "gc/accounting/card_table-inl.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/heap.h" @@ -30,7 +31,6 @@ #include "runtime.h" #include "thread.h" #include "thread_list.h" -#include "utils.h" namespace art { namespace gc { diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc index 3a685cb82d..e7865363a1 100644 --- a/runtime/gc/space/rosalloc_space.cc +++ b/runtime/gc/space/rosalloc_space.cc @@ -19,6 +19,7 @@ #include "base/logging.h" // For VLOG. #include "base/time_utils.h" +#include "base/utils.h" #include "gc/accounting/card_table.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/heap.h" @@ -29,7 +30,6 @@ #include "scoped_thread_state_change-inl.h" #include "thread.h" #include "thread_list.h" -#include "utils.h" namespace art { namespace gc { diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h index 12bccb35e7..7af19fae61 100644 --- a/runtime/gc/space/space.h +++ b/runtime/gc/space/space.h @@ -20,7 +20,7 @@ #include #include -#include "atomic.h" +#include "base/atomic.h" #include "base/macros.h" #include "base/mutex.h" #include "gc/accounting/space_bitmap.h" diff --git a/runtime/gc/space/zygote_space.cc b/runtime/gc/space/zygote_space.cc index fddb3f2dd2..cde155fb22 100644 --- a/runtime/gc/space/zygote_space.cc +++ b/runtime/gc/space/zygote_space.cc @@ -17,12 +17,12 @@ #include "zygote_space.h" #include "base/mutex-inl.h" +#include "base/utils.h" #include "gc/accounting/card_table-inl.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/heap.h" #include "runtime.h" #include "thread-current-inl.h" -#include "utils.h" namespace art { namespace gc { diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index 52ee5169fb..aa716f12ac 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -44,6 +44,7 @@ #include "base/array_ref.h" #include "base/macros.h" #include "base/mutex.h" +#include "base/os.h" #include "base/safe_map.h" #include "base/time_utils.h" #include "base/unix_file/fd_file.h" @@ -64,7 +65,6 @@ #include "mirror/class-inl.h" #include "mirror/class.h" #include "mirror/object-refvisitor-inl.h" -#include "os.h" #include "scoped_thread_state_change-inl.h" #include "thread_list.h" diff --git a/runtime/image.cc b/runtime/image.cc index 99406229a5..0955c3a3ca 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -18,10 +18,10 @@ #include "base/bit_utils.h" #include "base/length_prefixed_array.h" +#include "base/utils.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/object_array.h" -#include "utils.h" namespace art { diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc index 51878312d9..3b9cc0f946 100644 --- a/runtime/indirect_reference_table.cc +++ b/runtime/indirect_reference_table.cc @@ -18,6 +18,7 @@ #include "base/dumpable-inl.h" #include "base/systrace.h" +#include "base/utils.h" #include "java_vm_ext.h" #include "jni_internal.h" #include "nth_caller_visitor.h" @@ -25,7 +26,6 @@ #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "thread.h" -#include "utils.h" #include diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index b6055cb6c8..7ddd17329c 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -21,7 +21,7 @@ #include "arch/context.h" #include "art_field-inl.h" #include "art_method-inl.h" -#include "atomic.h" +#include "base/atomic.h" #include "base/callee_save_type.h" #include "class_linker.h" #include "debugger.h" diff --git a/runtime/intern_table.h b/runtime/intern_table.h index 05f2794b38..cb976917e6 100644 --- a/runtime/intern_table.h +++ b/runtime/intern_table.h @@ -19,7 +19,7 @@ #include -#include "atomic.h" +#include "base/atomic.h" #include "base/allocator.h" #include "base/hash_set.h" #include "base/mutex.h" diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index d8f858e95b..e35d80f724 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -17,6 +17,7 @@ #include "interpreter_switch_impl.h" #include "base/enums.h" +#include "base/quasi_atomic.h" #include "dex/dex_file_types.h" #include "experimental_flags.h" #include "interpreter_common.h" diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 9c7645af9e..2a9ef2ce98 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -18,6 +18,8 @@ * Mterp entry point and support functions. */ #include "mterp.h" + +#include "base/quasi_atomic.h" #include "debugger.h" #include "entrypoints/entrypoint_utils-inl.h" #include "interpreter/interpreter_common.h" diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index a0b58ef29e..600561b85c 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -33,6 +33,7 @@ #include "base/casts.h" #include "base/enums.h" #include "base/macros.h" +#include "base/quasi_atomic.h" #include "class_linker.h" #include "common_throws.h" #include "dex/descriptors_names.h" diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h index b491c3ee5c..bf1d665e9d 100644 --- a/runtime/jdwp/jdwp.h +++ b/runtime/jdwp/jdwp.h @@ -17,7 +17,7 @@ #ifndef ART_RUNTIME_JDWP_JDWP_H_ #define ART_RUNTIME_JDWP_JDWP_H_ -#include "atomic.h" +#include "base/atomic.h" #include "base/logging.h" // For VLOG. #include "base/mutex.h" #include "jdwp/jdwp_bits.h" diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc index 90cac853ff..291a983e75 100644 --- a/runtime/jdwp/jdwp_handler.cc +++ b/runtime/jdwp/jdwp_handler.cc @@ -22,7 +22,7 @@ #include "android-base/stringprintf.h" -#include "atomic.h" +#include "base/atomic.h" #include "base/hex_dump.h" #include "base/logging.h" // For VLOG. #include "base/macros.h" diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc index 63f5dc8b69..557b032154 100644 --- a/runtime/jdwp/jdwp_main.cc +++ b/runtime/jdwp/jdwp_main.cc @@ -22,7 +22,7 @@ #include "android-base/stringprintf.h" -#include "atomic.h" +#include "base/atomic.h" #include "base/logging.h" // For VLOG. #include "base/time_utils.h" #include "debugger.h" diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 6d99ad0046..a757960e3e 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -23,6 +23,7 @@ #include "base/logging.h" // For VLOG. #include "base/memory_tool.h" #include "base/runtime_debug.h" +#include "base/utils.h" #include "debugger.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "interpreter/interpreter.h" @@ -38,7 +39,6 @@ #include "stack_map.h" #include "thread-inl.h" #include "thread_list.h" -#include "utils.h" namespace art { namespace jit { diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 68a3647974..51a63ddf8a 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -22,6 +22,7 @@ #include "art_method-inl.h" #include "base/enums.h" #include "base/logging.h" // For VLOG. +#include "base/quasi_atomic.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/time_utils.h" diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 0d1311fe34..16335c6911 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -19,8 +19,8 @@ #include "instrumentation.h" -#include "atomic.h" #include "base/arena_containers.h" +#include "base/atomic.h" #include "base/histogram-inl.h" #include "base/macros.h" #include "base/mutex.h" diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index 7be29c9414..1bbce4f414 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -37,16 +37,16 @@ #include "base/file_utils.h" #include "base/logging.h" // For VLOG. #include "base/mutex.h" +#include "base/os.h" #include "base/safe_map.h" #include "base/scoped_flock.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/time_utils.h" #include "base/unix_file/fd_file.h" +#include "base/utils.h" #include "dex/dex_file_loader.h" #include "jit/profiling_info.h" -#include "os.h" -#include "utils.h" #include "zip_archive.h" namespace art { @@ -278,7 +278,7 @@ bool ProfileCompilationInfo::Save(const std::string& filename, uint64_t* bytes_w // access and fail immediately if we can't. bool result = Save(fd); if (result) { - int64_t size = GetFileSizeBytes(filename); + int64_t size = OS::GetFileSizeBytes(filename.c_str()); if (size != -1) { VLOG(profiler) << "Successfully saved profile info to " << filename << " Size: " diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h index 7e09b6b833..a0f6bf8dd6 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -20,9 +20,9 @@ #include #include -#include "atomic.h" #include "base/arena_containers.h" #include "base/arena_object.h" +#include "base/atomic.h" #include "base/safe_map.h" #include "bit_memory_region.h" #include "dex/dex_cache_resolved_classes.h" diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 4c73d872f2..b78fcacc08 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -25,8 +25,8 @@ #include "art_field-inl.h" #include "art_method-inl.h" -#include "atomic.h" #include "base/allocator.h" +#include "base/atomic.h" #include "base/enums.h" #include "base/logging.h" // For VLOG. #include "base/mutex.h" diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 26acef06d6..b9d51c1125 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -35,10 +35,10 @@ #include "base/allocator.h" #include "base/bit_utils.h" #include "base/file_utils.h" +#include "base/globals.h" #include "base/logging.h" // For VLOG_IS_ON. #include "base/memory_tool.h" -#include "globals.h" -#include "utils.h" +#include "base/utils.h" #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc index 25283bc310..ea202e766f 100644 --- a/runtime/mirror/array.cc +++ b/runtime/mirror/array.cc @@ -16,6 +16,7 @@ #include "array-inl.h" +#include "base/utils.h" #include "class-inl.h" #include "class.h" #include "class_linker-inl.h" @@ -26,7 +27,6 @@ #include "object-inl.h" #include "object_array-inl.h" #include "thread.h" -#include "utils.h" namespace art { namespace mirror { diff --git a/runtime/mirror/call_site.h b/runtime/mirror/call_site.h index db244a5442..93f274808c 100644 --- a/runtime/mirror/call_site.h +++ b/runtime/mirror/call_site.h @@ -17,8 +17,8 @@ #ifndef ART_RUNTIME_MIRROR_CALL_SITE_H_ #define ART_RUNTIME_MIRROR_CALL_SITE_H_ +#include "base/utils.h" #include "mirror/method_handle_impl.h" -#include "utils.h" namespace art { diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 86d538ec80..ee7d217e8d 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -23,6 +23,7 @@ #include "art_method.h" #include "base/array_slice.h" #include "base/length_prefixed_array.h" +#include "base/utils.h" #include "class_linker.h" #include "class_loader.h" #include "common_throws.h" @@ -39,7 +40,6 @@ #include "reference-inl.h" #include "runtime.h" #include "string.h" -#include "utils.h" namespace art { namespace mirror { diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 7a09391056..3f4e841f86 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -21,6 +21,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" #include "base/logging.h" // For VLOG. +#include "base/utils.h" #include "class-inl.h" #include "class_ext.h" #include "class_linker-inl.h" @@ -40,7 +41,6 @@ #include "runtime.h" #include "thread.h" #include "throwable.h" -#include "utils.h" #include "well_known_classes.h" namespace art { diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 6000317c85..ea065676a0 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -21,6 +21,8 @@ #include "base/casts.h" #include "base/enums.h" #include "base/iteration_range.h" +#include "base/stride_iterator.h" +#include "base/utils.h" #include "class_flags.h" #include "class_status.h" #include "dex/dex_file.h" @@ -33,9 +35,7 @@ #include "object.h" #include "object_array.h" #include "read_barrier_option.h" -#include "stride_iterator.h" #include "thread.h" -#include "utils.h" namespace art { diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc index c18b219f19..081957964c 100644 --- a/runtime/mirror/class_ext.cc +++ b/runtime/mirror/class_ext.cc @@ -19,13 +19,13 @@ #include "art_method-inl.h" #include "base/casts.h" #include "base/enums.h" +#include "base/utils.h" #include "class-inl.h" #include "dex/dex_file-inl.h" #include "gc/accounting/card_table-inl.h" #include "object-inl.h" #include "object_array.h" #include "stack_trace_element.h" -#include "utils.h" #include "well_known_classes.h" namespace art { diff --git a/runtime/mirror/emulated_stack_frame.h b/runtime/mirror/emulated_stack_frame.h index 9bfa4d6098..23626f46e0 100644 --- a/runtime/mirror/emulated_stack_frame.h +++ b/runtime/mirror/emulated_stack_frame.h @@ -17,12 +17,12 @@ #ifndef ART_RUNTIME_MIRROR_EMULATED_STACK_FRAME_H_ #define ART_RUNTIME_MIRROR_EMULATED_STACK_FRAME_H_ +#include "base/utils.h" #include "dex/dex_instruction.h" #include "method_type.h" #include "object.h" #include "stack.h" #include "string.h" -#include "utils.h" namespace art { diff --git a/runtime/mirror/method_handles_lookup.h b/runtime/mirror/method_handles_lookup.h index dd8d45e66f..fefcb2ed29 100644 --- a/runtime/mirror/method_handles_lookup.h +++ b/runtime/mirror/method_handles_lookup.h @@ -17,11 +17,11 @@ #ifndef ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_ #define ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_ +#include "base/utils.h" #include "gc_root.h" #include "handle.h" #include "obj_ptr.h" #include "object.h" -#include "utils.h" namespace art { diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h index edd991092a..3627214d90 100644 --- a/runtime/mirror/method_type.h +++ b/runtime/mirror/method_type.h @@ -17,10 +17,10 @@ #ifndef ART_RUNTIME_MIRROR_METHOD_TYPE_H_ #define ART_RUNTIME_MIRROR_METHOD_TYPE_H_ +#include "base/utils.h" #include "object_array.h" #include "object.h" #include "string.h" -#include "utils.h" namespace art { diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index 7fdaa32751..55dd51427c 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -22,7 +22,7 @@ #include "array-inl.h" #include "art_field.h" #include "art_method.h" -#include "atomic.h" +#include "base/atomic.h" #include "class-inl.h" #include "class_flags.h" #include "class_linker.h" diff --git a/runtime/mirror/object-readbarrier-inl.h b/runtime/mirror/object-readbarrier-inl.h index 126cb04cf1..aeaa850abe 100644 --- a/runtime/mirror/object-readbarrier-inl.h +++ b/runtime/mirror/object-readbarrier-inl.h @@ -19,7 +19,7 @@ #include "object.h" -#include "atomic.h" +#include "base/atomic.h" #include "heap_poisoning.h" #include "lock_word-inl.h" #include "object_reference-inl.h" diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index 816ac69b29..95f82cb147 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -17,7 +17,7 @@ #ifndef ART_RUNTIME_MIRROR_OBJECT_H_ #define ART_RUNTIME_MIRROR_OBJECT_H_ -#include "atomic.h" +#include "base/atomic.h" #include "base/casts.h" #include "base/enums.h" #include "globals.h" diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h index 5cfc987e44..086d2f4672 100644 --- a/runtime/mirror/object_array-inl.h +++ b/runtime/mirror/object_array-inl.h @@ -24,6 +24,7 @@ #include "android-base/stringprintf.h" #include "array-inl.h" +#include "base/utils.h" #include "class.h" #include "gc/heap.h" #include "handle_scope-inl.h" @@ -31,7 +32,6 @@ #include "object-inl.h" #include "runtime.h" #include "thread.h" -#include "utils.h" namespace art { namespace mirror { diff --git a/runtime/mirror/object_reference.h b/runtime/mirror/object_reference.h index 7fd9c71b24..cf1f85d236 100644 --- a/runtime/mirror/object_reference.h +++ b/runtime/mirror/object_reference.h @@ -17,7 +17,7 @@ #ifndef ART_RUNTIME_MIRROR_OBJECT_REFERENCE_H_ #define ART_RUNTIME_MIRROR_OBJECT_REFERENCE_H_ -#include "atomic.h" +#include "base/atomic.h" #include "base/mutex.h" // For Locks::mutator_lock_. #include "globals.h" #include "heap_poisoning.h" diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index 32a99eb753..5306eac7f6 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -787,5 +787,23 @@ TEST_F(ObjectTest, ObjectPointer) { } } +TEST_F(ObjectTest, PrettyTypeOf) { + ScopedObjectAccess soa(Thread::Current()); + EXPECT_EQ("null", mirror::Object::PrettyTypeOf(nullptr)); + + StackHandleScope<2> hs(soa.Self()); + Handle s(hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), ""))); + EXPECT_EQ("java.lang.String", mirror::Object::PrettyTypeOf(s.Get())); + + Handle a(hs.NewHandle(mirror::ShortArray::Alloc(soa.Self(), 2))); + EXPECT_EQ("short[]", mirror::Object::PrettyTypeOf(a.Get())); + + mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); + ASSERT_TRUE(c != nullptr); + mirror::Object* o = mirror::ObjectArray::Alloc(soa.Self(), c, 0); + EXPECT_EQ("java.lang.String[]", mirror::Object::PrettyTypeOf(o)); + EXPECT_EQ("java.lang.Class", mirror::Object::PrettyTypeOf(o->GetClass())); +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index 8c2a49c5f6..a60861cc28 100644 --- a/runtime/mirror/string-inl.h +++ b/runtime/mirror/string-inl.h @@ -22,14 +22,14 @@ #include "array.h" #include "base/bit_utils.h" +#include "base/globals.h" +#include "base/utils.h" #include "class.h" #include "common_throws.h" #include "dex/utf.h" #include "gc/heap-inl.h" -#include "globals.h" #include "runtime.h" #include "thread.h" -#include "utils.h" namespace art { namespace mirror { diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc index a7a6d087e1..b6173d422b 100644 --- a/runtime/mirror/throwable.cc +++ b/runtime/mirror/throwable.cc @@ -20,6 +20,7 @@ #include "art_method-inl.h" #include "base/enums.h" +#include "base/utils.h" #include "class-inl.h" #include "dex/dex_file-inl.h" #include "gc/accounting/card_table-inl.h" @@ -28,7 +29,6 @@ #include "object_array.h" #include "stack_trace_element.h" #include "string.h" -#include "utils.h" #include "well_known_classes.h" namespace art { diff --git a/runtime/monitor.cc b/runtime/monitor.cc index 0c9c65a401..2a938da15b 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -23,6 +23,7 @@ #include "art_method-inl.h" #include "base/logging.h" // For VLOG. #include "base/mutex.h" +#include "base/quasi_atomic.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/time_utils.h" diff --git a/runtime/monitor.h b/runtime/monitor.h index f150a8c091..384ebbedaa 100644 --- a/runtime/monitor.h +++ b/runtime/monitor.h @@ -25,8 +25,8 @@ #include #include -#include "atomic.h" #include "base/allocator.h" +#include "base/atomic.h" #include "base/mutex.h" #include "gc_root.h" #include "lock_word.h" diff --git a/runtime/monitor_pool.h b/runtime/monitor_pool.h index 80bae7ff69..c6b0b0b86e 100644 --- a/runtime/monitor_pool.h +++ b/runtime/monitor_pool.h @@ -22,7 +22,7 @@ #include "base/allocator.h" #ifdef __LP64__ #include -#include "atomic.h" +#include "base/atomic.h" #include "runtime.h" #else #include "base/stl_util.h" // STLDeleteElements diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc index 7d89652ddf..bff8d7678c 100644 --- a/runtime/monitor_test.cc +++ b/runtime/monitor_test.cc @@ -18,7 +18,7 @@ #include -#include "atomic.h" +#include "base/atomic.h" #include "barrier.h" #include "base/time_utils.h" #include "class_linker-inl.h" diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 5d18b6e757..13319c4c57 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -22,7 +22,9 @@ #include "base/file_utils.h" #include "base/logging.h" +#include "base/os.h" #include "base/stl_util.h" +#include "base/utils.h" #include "class_linker.h" #include #include "common_throws.h" @@ -43,10 +45,8 @@ #include "oat_file.h" #include "oat_file_assistant.h" #include "oat_file_manager.h" -#include "os.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" -#include "utils.h" #include "well_known_classes.h" #include "zip_archive.h" diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 688ae1977e..13275d92e4 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -20,6 +20,7 @@ #include "nativehelper/jni_macros.h" #include "art_field-inl.h" +#include "base/utils.h" #include "class_linker-inl.h" #include "class_linker.h" #include "common_throws.h" @@ -31,7 +32,6 @@ #include "native_util.h" #include "reflection-inl.h" #include "scoped_fast_native_object_access-inl.h" -#include "utils.h" #include "well_known_classes.h" namespace art { diff --git a/runtime/native/java_lang_reflect_Parameter.cc b/runtime/native/java_lang_reflect_Parameter.cc index 1ab91098d7..b80b20cd8d 100644 --- a/runtime/native/java_lang_reflect_Parameter.cc +++ b/runtime/native/java_lang_reflect_Parameter.cc @@ -20,13 +20,13 @@ #include "nativehelper/jni_macros.h" #include "art_method-inl.h" +#include "base/utils.h" #include "common_throws.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_annotations.h" #include "jni_internal.h" #include "native_util.h" #include "scoped_fast_native_object_access-inl.h" -#include "utils.h" namespace art { diff --git a/runtime/native/java_util_concurrent_atomic_AtomicLong.cc b/runtime/native/java_util_concurrent_atomic_AtomicLong.cc index bd4b0fec70..c0032975ce 100644 --- a/runtime/native/java_util_concurrent_atomic_AtomicLong.cc +++ b/runtime/native/java_util_concurrent_atomic_AtomicLong.cc @@ -19,7 +19,8 @@ #include "nativehelper/jni_macros.h" #include "arch/instruction_set.h" -#include "atomic.h" +#include "base/atomic.h" +#include "base/quasi_atomic.h" #include "jni_internal.h" #include "native_util.h" diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc index 1af65a371b..25f984f6be 100644 --- a/runtime/native/sun_misc_Unsafe.cc +++ b/runtime/native/sun_misc_Unsafe.cc @@ -24,6 +24,7 @@ #include "nativehelper/jni_macros.h" +#include "base/quasi_atomic.h" #include "common_throws.h" #include "gc/accounting/card_table-inl.h" #include "jni_internal.h" diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc index 4d4bab764c..396b09abcf 100644 --- a/runtime/native_stack_dump.cc +++ b/runtime/native_stack_dump.cc @@ -44,11 +44,11 @@ #include "base/file_utils.h" #include "base/memory_tool.h" #include "base/mutex.h" +#include "base/os.h" #include "base/unix_file/fd_file.h" +#include "base/utils.h" #include "oat_quick_method_header.h" -#include "os.h" #include "thread-current-inl.h" -#include "utils.h" #endif diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 3576683fee..b0e1de2b81 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -40,9 +40,11 @@ #include "base/enums.h" #include "base/file_utils.h" #include "base/logging.h" // For VLOG_IS_ON. +#include "base/os.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/unix_file/fd_file.h" +#include "base/utils.h" #include "dex/art_dex_file_loader.h" #include "dex/dex_file_loader.h" #include "dex/dex_file_types.h" @@ -58,10 +60,8 @@ #include "oat.h" #include "oat_file-inl.h" #include "oat_file_manager.h" -#include "os.h" #include "runtime.h" #include "type_lookup_table.h" -#include "utils.h" #include "vdex_file.h" namespace art { @@ -1812,9 +1812,9 @@ void OatDexFile::MadviseDexFile(const DexFile& dex_file, MadviseState state) { // Default every dex file to MADV_RANDOM when its loaded by default for low ram devices. // Other devices have enough page cache to get performance benefits from loading more pages // into the page cache. - MadviseLargestPageAlignedRegion(dex_file.Begin(), - dex_file.Begin() + dex_file.Size(), - MADV_RANDOM); + DexLayoutSection::MadviseLargestPageAlignedRegion(dex_file.Begin(), + dex_file.Begin() + dex_file.Size(), + MADV_RANDOM); } const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); if (oat_dex_file != nullptr) { diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 255a31bba9..3c2cd00c8d 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -23,9 +23,11 @@ #include "base/array_ref.h" #include "base/mutex.h" +#include "base/os.h" #include "base/safe_map.h" #include "base/stringpiece.h" #include "base/tracking_safe_map.h" +#include "base/utils.h" #include "class_status.h" #include "compiler_filter.h" #include "dex/dex_file.h" @@ -34,9 +36,7 @@ #include "index_bss_mapping.h" #include "mirror/object.h" #include "oat.h" -#include "os.h" #include "type_lookup_table.h" -#include "utils.h" namespace art { diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 6bf05b77a8..6f079ca8c4 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -25,7 +25,9 @@ #include "base/file_utils.h" #include "base/logging.h" // For VLOG. +#include "base/os.h" #include "base/stl_util.h" +#include "base/utils.h" #include "class_linker.h" #include "compiler_filter.h" #include "dex/art_dex_file_loader.h" @@ -35,10 +37,8 @@ #include "gc/space/image_space.h" #include "image.h" #include "oat.h" -#include "os.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" -#include "utils.h" #include "vdex_file.h" #include "class_loader_context.h" diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index 6da49a9c29..8d6ec0014a 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -23,12 +23,12 @@ #include #include "arch/instruction_set.h" +#include "base/os.h" #include "base/scoped_flock.h" #include "base/unix_file/fd_file.h" #include "compiler_filter.h" #include "class_loader_context.h" #include "oat_file.h" -#include "os.h" namespace art { diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 72f7d02892..676071b247 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -27,16 +27,16 @@ #include "android-base/strings.h" #include "art_field-inl.h" +#include "base/os.h" +#include "base/utils.h" #include "class_linker-inl.h" #include "class_loader_context.h" #include "common_runtime_test.h" #include "dexopt_test.h" #include "oat_file.h" #include "oat_file_manager.h" -#include "os.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" -#include "utils.h" namespace art { diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h index 4443255f64..f0966b7bfa 100644 --- a/runtime/oat_quick_method_header.h +++ b/runtime/oat_quick_method_header.h @@ -19,10 +19,10 @@ #include "arch/instruction_set.h" #include "base/macros.h" +#include "base/utils.h" #include "method_info.h" #include "quick/quick_method_frame_info.h" #include "stack_map.h" -#include "utils.h" namespace art { diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index c61ecc880b..5518eb2c49 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -23,13 +23,13 @@ #include "base/file_utils.h" #include "base/macros.h" #include "base/stringpiece.h" +#include "base/utils.h" #include "debugger.h" #include "gc/heap.h" #include "monitor.h" #include "runtime.h" #include "ti/agent.h" #include "trace.h" -#include "utils.h" #include "cmdline_parser.h" #include "runtime_options.h" diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h index a77d100b92..58f6c04c3e 100644 --- a/runtime/read_barrier-inl.h +++ b/runtime/read_barrier-inl.h @@ -19,6 +19,7 @@ #include "read_barrier.h" +#include "base/utils.h" #include "gc/accounting/read_barrier_table.h" #include "gc/collector/concurrent_copying-inl.h" #include "gc/heap.h" @@ -26,7 +27,6 @@ #include "mirror/object_reference.h" #include "mirror/reference.h" #include "runtime.h" -#include "utils.h" namespace art { diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc index a6df27b236..d62cbdb11a 100644 --- a/runtime/reference_table.cc +++ b/runtime/reference_table.cc @@ -19,6 +19,7 @@ #include "android-base/stringprintf.h" #include "base/mutex.h" +#include "base/utils.h" #include "gc/allocation_record.h" #include "gc/heap.h" #include "indirect_reference_table.h" @@ -30,7 +31,6 @@ #include "mirror/string-inl.h" #include "runtime-inl.h" #include "thread.h" -#include "utils.h" namespace art { diff --git a/runtime/reflection-inl.h b/runtime/reflection-inl.h index 87432ab77b..26fb021903 100644 --- a/runtime/reflection-inl.h +++ b/runtime/reflection-inl.h @@ -21,13 +21,13 @@ #include "android-base/stringprintf.h" +#include "base/utils.h" #include "common_throws.h" #include "dex/descriptors_names.h" #include "dex/primitive.h" #include "jvalue-inl.h" #include "mirror/object-inl.h" #include "obj_ptr-inl.h" -#include "utils.h" namespace art { diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 4442fc6a54..8672482a8f 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -56,16 +56,20 @@ #include "art_method-inl.h" #include "asm_support.h" #include "asm_support_check.h" -#include "atomic.h" #include "base/aborting.h" #include "base/arena_allocator.h" +#include "base/atomic.h" #include "base/dumpable.h" #include "base/enums.h" #include "base/file_utils.h" #include "base/memory_tool.h" +#include "base/mutex.h" +#include "base/os.h" +#include "base/quasi_atomic.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/unix_file/fd_file.h" +#include "base/utils.h" #include "class_linker-inl.h" #include "compiler_callbacks.h" #include "debugger.h" @@ -141,7 +145,6 @@ #include "oat_file.h" #include "oat_file_manager.h" #include "object_callbacks.h" -#include "os.h" #include "parsed_options.h" #include "quick/quick_method_frame_info.h" #include "reflection.h" @@ -157,7 +160,6 @@ #include "ti/agent.h" #include "trace.h" #include "transaction.h" -#include "utils.h" #include "vdex_file.h" #include "verifier/method_verifier.h" #include "well_known_classes.h" @@ -621,6 +623,7 @@ void Runtime::SweepSystemWeaks(IsMarkedVisitor* visitor) { bool Runtime::ParseOptions(const RuntimeOptions& raw_options, bool ignore_unrecognized, RuntimeArgumentMap* runtime_options) { + Locks::Init(); InitLogging(/* argv */ nullptr, Abort); // Calls Locks::Init() as a side effect. bool parsed = ParsedOptions::Parse(raw_options, ignore_unrecognized, runtime_options); if (!parsed) { @@ -2215,7 +2218,7 @@ void Runtime::RegisterAppInfo(const std::vector& code_paths, LOG(WARNING) << "JIT profile information will not be recorded: profile filename is empty."; return; } - if (!FileExists(profile_output_filename)) { + if (!OS::FileExists(profile_output_filename.c_str(), false /*check_file_type*/)) { LOG(WARNING) << "JIT profile information will not be recorded: profile file does not exits."; return; } diff --git a/runtime/runtime_common.h b/runtime/runtime_common.h index 06d66270af..3fba441b55 100644 --- a/runtime/runtime_common.h +++ b/runtime/runtime_common.h @@ -31,8 +31,8 @@ #include #include "base/dumpable.h" +#include "base/utils.h" #include "native_stack_dump.h" -#include "utils.h" namespace art { diff --git a/runtime/runtime_options.cc b/runtime/runtime_options.cc index bce0d81cfb..f8c680db44 100644 --- a/runtime/runtime_options.cc +++ b/runtime/runtime_options.cc @@ -18,13 +18,13 @@ #include +#include "base/utils.h" #include "debugger.h" #include "gc/heap.h" #include "monitor.h" #include "runtime.h" #include "thread_list.h" #include "trace.h" -#include "utils.h" namespace art { diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc index 9c3afbb133..d590ad5cc6 100644 --- a/runtime/signal_catcher.cc +++ b/runtime/signal_catcher.cc @@ -36,18 +36,18 @@ #include "arch/instruction_set.h" #include "base/file_utils.h" #include "base/logging.h" // For GetCmdLine. +#include "base/os.h" #include "base/time_utils.h" #include "base/unix_file/fd_file.h" +#include "base/utils.h" #include "class_linker.h" #include "gc/heap.h" #include "jit/profile_saver.h" -#include "os.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "signal_set.h" #include "thread.h" #include "thread_list.h" -#include "utils.h" namespace art { diff --git a/runtime/thread.cc b/runtime/thread.cc index 4cdf015478..5b03c2d884 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -45,6 +45,7 @@ #include "base/systrace.h" #include "base/timing_logger.h" #include "base/to_str.h" +#include "base/utils.h" #include "class_linker-inl.h" #include "debugger.h" #include "dex/descriptors_names.h" @@ -90,7 +91,6 @@ #include "stack_map.h" #include "thread-inl.h" #include "thread_list.h" -#include "utils.h" #include "verifier/method_verifier.h" #include "verify_object.h" #include "well_known_classes.h" diff --git a/runtime/thread.h b/runtime/thread.h index 295685e799..6549fc1a1f 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -28,7 +28,7 @@ #include "arch/context.h" #include "arch/instruction_set.h" -#include "atomic.h" +#include "base/atomic.h" #include "base/enums.h" #include "base/macros.h" #include "base/mutex.h" diff --git a/runtime/thread_linux.cc b/runtime/thread_linux.cc index 9673eee795..d05fecf0a9 100644 --- a/runtime/thread_linux.cc +++ b/runtime/thread_linux.cc @@ -19,7 +19,7 @@ #include #include "base/logging.h" // For VLOG. -#include "utils.h" +#include "base/utils.h" namespace art { diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc index 386cdf006a..bec1150807 100644 --- a/runtime/thread_pool.cc +++ b/runtime/thread_pool.cc @@ -29,9 +29,9 @@ #include "base/casts.h" #include "base/stl_util.h" #include "base/time_utils.h" +#include "base/utils.h" #include "runtime.h" #include "thread-current-inl.h" -#include "utils.h" namespace art { diff --git a/runtime/thread_pool_test.cc b/runtime/thread_pool_test.cc index 28aa21f7a2..895a108af0 100644 --- a/runtime/thread_pool_test.cc +++ b/runtime/thread_pool_test.cc @@ -18,7 +18,7 @@ #include -#include "atomic.h" +#include "base/atomic.h" #include "common_runtime_test.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" diff --git a/runtime/trace.cc b/runtime/trace.cc index d97dcb5a3d..0f321b6591 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -24,10 +24,12 @@ #include "art_method-inl.h" #include "base/casts.h" #include "base/enums.h" +#include "base/os.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/time_utils.h" #include "base/unix_file/fd_file.h" +#include "base/utils.h" #include "class_linker.h" #include "common_throws.h" #include "debugger.h" @@ -41,12 +43,10 @@ #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "nativehelper/scoped_local_ref.h" -#include "os.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" #include "thread.h" #include "thread_list.h" -#include "utils.h" namespace art { diff --git a/runtime/trace.h b/runtime/trace.h index 7ce12dace0..86b8d00d51 100644 --- a/runtime/trace.h +++ b/runtime/trace.h @@ -26,12 +26,12 @@ #include #include -#include "atomic.h" +#include "base/atomic.h" #include "base/macros.h" +#include "base/os.h" #include "base/safe_map.h" #include "globals.h" #include "instrumentation.h" -#include "os.h" namespace art { diff --git a/runtime/type_lookup_table.cc b/runtime/type_lookup_table.cc index 925a9089cb..7e204fc03a 100644 --- a/runtime/type_lookup_table.cc +++ b/runtime/type_lookup_table.cc @@ -20,9 +20,9 @@ #include #include "base/bit_utils.h" +#include "base/utils.h" #include "dex/dex_file-inl.h" #include "dex/utf-inl.h" -#include "utils.h" namespace art { diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc deleted file mode 100644 index e67e93f9c5..0000000000 --- a/runtime/utils_test.cc +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2011 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 "utils.h" - -#include -#include - -#include "base/enums.h" -#include "base/file_utils.h" -#include "base/stl_util.h" -#include "class_linker-inl.h" -#include "common_runtime_test.h" -#include "exec_utils.h" -#include "handle_scope-inl.h" -#include "mirror/array-inl.h" -#include "mirror/array.h" -#include "mirror/object-inl.h" -#include "mirror/object_array-inl.h" -#include "mirror/string.h" -#include "scoped_thread_state_change-inl.h" - -#include "base/memory_tool.h" - -namespace art { - -class UtilsTest : public CommonRuntimeTest {}; - -TEST_F(UtilsTest, PrettyTypeOf) { - ScopedObjectAccess soa(Thread::Current()); - EXPECT_EQ("null", mirror::Object::PrettyTypeOf(nullptr)); - - StackHandleScope<2> hs(soa.Self()); - Handle s(hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), ""))); - EXPECT_EQ("java.lang.String", mirror::Object::PrettyTypeOf(s.Get())); - - Handle a(hs.NewHandle(mirror::ShortArray::Alloc(soa.Self(), 2))); - EXPECT_EQ("short[]", mirror::Object::PrettyTypeOf(a.Get())); - - mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); - ASSERT_TRUE(c != nullptr); - mirror::Object* o = mirror::ObjectArray::Alloc(soa.Self(), c, 0); - EXPECT_EQ("java.lang.String[]", mirror::Object::PrettyTypeOf(o)); - EXPECT_EQ("java.lang.Class", mirror::Object::PrettyTypeOf(o->GetClass())); -} - -TEST_F(UtilsTest, PrettyClass) { - ScopedObjectAccess soa(Thread::Current()); - EXPECT_EQ("null", mirror::Class::PrettyClass(nullptr)); - mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); - ASSERT_TRUE(c != nullptr); - mirror::Object* o = mirror::ObjectArray::Alloc(soa.Self(), c, 0); - EXPECT_EQ("java.lang.Class", mirror::Class::PrettyClass(o->GetClass())); -} - -TEST_F(UtilsTest, PrettyClassAndClassLoader) { - ScopedObjectAccess soa(Thread::Current()); - EXPECT_EQ("null", mirror::Class::PrettyClassAndClassLoader(nullptr)); - mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); - ASSERT_TRUE(c != nullptr); - mirror::Object* o = mirror::ObjectArray::Alloc(soa.Self(), c, 0); - EXPECT_EQ("java.lang.Class", - mirror::Class::PrettyClassAndClassLoader(o->GetClass())); -} - -TEST_F(UtilsTest, PrettyField) { - ScopedObjectAccess soa(Thread::Current()); - EXPECT_EQ("null", ArtField::PrettyField(nullptr)); - - mirror::Class* java_lang_String = class_linker_->FindSystemClass(soa.Self(), - "Ljava/lang/String;"); - - ArtField* f; - f = java_lang_String->FindDeclaredInstanceField("count", "I"); - EXPECT_EQ("int java.lang.String.count", f->PrettyField()); - EXPECT_EQ("java.lang.String.count", f->PrettyField(false)); -} - -TEST_F(UtilsTest, PrettySize) { - EXPECT_EQ("1GB", PrettySize(1 * GB)); - EXPECT_EQ("2GB", PrettySize(2 * GB)); - if (sizeof(size_t) > sizeof(uint32_t)) { - EXPECT_EQ("100GB", PrettySize(100 * GB)); - } - EXPECT_EQ("1024KB", PrettySize(1 * MB)); - EXPECT_EQ("10MB", PrettySize(10 * MB)); - EXPECT_EQ("100MB", PrettySize(100 * MB)); - EXPECT_EQ("1024B", PrettySize(1 * KB)); - EXPECT_EQ("10KB", PrettySize(10 * KB)); - EXPECT_EQ("100KB", PrettySize(100 * KB)); - EXPECT_EQ("0B", PrettySize(0)); - EXPECT_EQ("1B", PrettySize(1)); - EXPECT_EQ("10B", PrettySize(10)); - EXPECT_EQ("100B", PrettySize(100)); - EXPECT_EQ("512B", PrettySize(512)); -} - -TEST_F(UtilsTest, JniShortName_JniLongName) { - ScopedObjectAccess soa(Thread::Current()); - mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/String;"); - ASSERT_TRUE(c != nullptr); - ArtMethod* m; - - m = c->FindClassMethod("charAt", "(I)C", kRuntimePointerSize); - ASSERT_TRUE(m != nullptr); - ASSERT_FALSE(m->IsDirect()); - EXPECT_EQ("Java_java_lang_String_charAt", m->JniShortName()); - EXPECT_EQ("Java_java_lang_String_charAt__I", m->JniLongName()); - - m = c->FindClassMethod("indexOf", "(Ljava/lang/String;I)I", kRuntimePointerSize); - ASSERT_TRUE(m != nullptr); - ASSERT_FALSE(m->IsDirect()); - EXPECT_EQ("Java_java_lang_String_indexOf", m->JniShortName()); - EXPECT_EQ("Java_java_lang_String_indexOf__Ljava_lang_String_2I", m->JniLongName()); - - m = c->FindClassMethod("copyValueOf", "([CII)Ljava/lang/String;", kRuntimePointerSize); - ASSERT_TRUE(m != nullptr); - ASSERT_TRUE(m->IsStatic()); - EXPECT_EQ("Java_java_lang_String_copyValueOf", m->JniShortName()); - EXPECT_EQ("Java_java_lang_String_copyValueOf___3CII", m->JniLongName()); -} - -TEST_F(UtilsTest, Split) { - std::vector actual; - std::vector expected; - - expected.clear(); - - actual.clear(); - Split("", ':', &actual); - EXPECT_EQ(expected, actual); - - actual.clear(); - Split(":", ':', &actual); - EXPECT_EQ(expected, actual); - - expected.clear(); - expected.push_back("foo"); - - actual.clear(); - Split(":foo", ':', &actual); - EXPECT_EQ(expected, actual); - - actual.clear(); - Split("foo:", ':', &actual); - EXPECT_EQ(expected, actual); - - actual.clear(); - Split(":foo:", ':', &actual); - EXPECT_EQ(expected, actual); - - expected.push_back("bar"); - - actual.clear(); - Split("foo:bar", ':', &actual); - EXPECT_EQ(expected, actual); - - actual.clear(); - Split(":foo:bar", ':', &actual); - EXPECT_EQ(expected, actual); - - actual.clear(); - Split("foo:bar:", ':', &actual); - EXPECT_EQ(expected, actual); - - actual.clear(); - Split(":foo:bar:", ':', &actual); - EXPECT_EQ(expected, actual); - - expected.push_back("baz"); - - actual.clear(); - Split("foo:bar:baz", ':', &actual); - EXPECT_EQ(expected, actual); - - actual.clear(); - Split(":foo:bar:baz", ':', &actual); - EXPECT_EQ(expected, actual); - - actual.clear(); - Split("foo:bar:baz:", ':', &actual); - EXPECT_EQ(expected, actual); - - actual.clear(); - Split(":foo:bar:baz:", ':', &actual); - EXPECT_EQ(expected, actual); -} - -TEST_F(UtilsTest, ArrayCount) { - int i[64]; - EXPECT_EQ(ArrayCount(i), 64u); - char c[7]; - EXPECT_EQ(ArrayCount(c), 7u); -} - -TEST_F(UtilsTest, BoundsCheckedCast) { - char buffer[64]; - const char* buffer_end = buffer + ArrayCount(buffer); - EXPECT_EQ(BoundsCheckedCast(nullptr, buffer, buffer_end), nullptr); - EXPECT_EQ(BoundsCheckedCast(buffer, buffer, buffer_end), - reinterpret_cast(buffer)); - EXPECT_EQ(BoundsCheckedCast(buffer + 56, buffer, buffer_end), - reinterpret_cast(buffer + 56)); - EXPECT_EQ(BoundsCheckedCast(buffer - 1, buffer, buffer_end), nullptr); - EXPECT_EQ(BoundsCheckedCast(buffer + 57, buffer, buffer_end), nullptr); -} - -} // namespace art diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index d27f431cdc..72f03f266a 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -22,9 +22,9 @@ #include "base/array_ref.h" #include "base/macros.h" +#include "base/os.h" #include "dex/compact_offset_table.h" #include "mem_map.h" -#include "os.h" #include "quicken_info.h" namespace art { diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 52bd7362ef..74c2244cfe 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -30,6 +30,7 @@ #include "base/stl_util.h" #include "base/systrace.h" #include "base/time_utils.h" +#include "base/utils.h" #include "class_linker.h" #include "compiler_callbacks.h" #include "dex/descriptors_names.h" @@ -55,7 +56,6 @@ #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" -#include "utils.h" #include "verifier_compiler_binding.h" #include "verifier_deps.h" diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc index 97c1b62abe..db3f093905 100644 --- a/runtime/verifier/method_verifier_test.cc +++ b/runtime/verifier/method_verifier_test.cc @@ -21,11 +21,11 @@ #include "android-base/strings.h" +#include "base/utils.h" #include "class_linker-inl.h" #include "common_runtime_test.h" #include "dex/dex_file-inl.h" #include "scoped_thread_state_change-inl.h" -#include "utils.h" #include "verifier_enums.h" namespace art { diff --git a/runtime/zip_archive.h b/runtime/zip_archive.h index 7b45690462..aa54018574 100644 --- a/runtime/zip_archive.h +++ b/runtime/zip_archive.h @@ -23,11 +23,11 @@ #include +#include "base/os.h" #include "base/safe_map.h" #include "base/unix_file/random_access_file.h" #include "globals.h" #include "mem_map.h" -#include "os.h" // system/core/zip_archive definitions. struct ZipEntry; diff --git a/runtime/zip_archive_test.cc b/runtime/zip_archive_test.cc index 4fc7ee2e20..48ee94ce8c 100644 --- a/runtime/zip_archive_test.cc +++ b/runtime/zip_archive_test.cc @@ -22,9 +22,9 @@ #include #include +#include "base/os.h" #include "base/unix_file/fd_file.h" #include "common_runtime_test.h" -#include "os.h" namespace art { diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc index 83234f0382..b91d983e75 100644 --- a/test/137-cfi/cfi.cc +++ b/test/137-cfi/cfi.cc @@ -31,11 +31,11 @@ #include "base/file_utils.h" #include "base/macros.h" +#include "base/utils.h" #include "gc/heap.h" #include "gc/space/image_space.h" #include "oat_file.h" #include "runtime.h" -#include "utils.h" namespace art { diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc index d2da244397..bbe74656dd 100644 --- a/test/ti-stress/stress.cc +++ b/test/ti-stress/stress.cc @@ -24,9 +24,9 @@ #include +#include "base/utils.h" #include "exec_utils.h" #include "jvmti.h" -#include "utils.h" #pragma clang diagnostic push diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc index d986cf82d6..d22998ae1b 100644 --- a/tools/hiddenapi/hiddenapi.cc +++ b/tools/hiddenapi/hiddenapi.cc @@ -21,12 +21,12 @@ #include "android-base/stringprintf.h" #include "android-base/strings.h" +#include "base/os.h" #include "base/unix_file/fd_file.h" #include "dex/art_dex_file_loader.h" #include "dex/dex_file-inl.h" #include "dex/hidden_api_access_flags.h" #include "mem_map.h" -#include "os.h" namespace art { -- GitLab From 976b298a4e2d9e79983c1b131093de1a27163bf5 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Fri, 2 Mar 2018 17:54:22 -0800 Subject: [PATCH 015/749] ART: Use try-lock for interface marking Use ObjectTryLock for the performance optimization to help code that uses classes for locks. Bug: 72204414 Test: art/test/testrunner/testrunner.py -b --host -t 170 Change-Id: Ic00de7bc15cb7e8369ff7c68cefcb3375f9df140 --- runtime/class_linker.cc | 10 ++++-- test/001-HelloWorld/src/Main.java | 33 ++++++++++++++++-- test/170-interface-init/expected.txt | 1 + test/170-interface-init/info.txt | 2 ++ test/170-interface-init/src/Main.java | 50 +++++++++++++++++++++++++++ test/etc/run-test-jar | 1 - 6 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 test/170-interface-init/expected.txt create mode 100644 test/170-interface-init/info.txt create mode 100644 test/170-interface-init/src/Main.java diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index c667fe2f30..2a80e3c609 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -4977,8 +4977,14 @@ bool ClassLinker::InitializeDefaultInterfaceRecursive(Thread* self, // interface since we can still avoid the traversal. This is purely a performance optimization. if (result) { // TODO This should be done in a better way - ObjectLock lock(self, iface); - iface->SetRecursivelyInitialized(); + // Note: Use a try-lock to avoid blocking when someone else is holding the lock on this + // interface. It is bad (Java) style, but not impossible. Marking the recursive + // initialization is a performance optimization (to avoid another idempotent visit + // for other implementing classes/interfaces), and can be revisited later. + ObjectTryLock lock(self, iface); + if (lock.Acquired()) { + iface->SetRecursivelyInitialized(); + } } return result; } diff --git a/test/001-HelloWorld/src/Main.java b/test/001-HelloWorld/src/Main.java index 1ef6289559..401e8529cd 100644 --- a/test/001-HelloWorld/src/Main.java +++ b/test/001-HelloWorld/src/Main.java @@ -14,8 +14,37 @@ * limitations under the License. */ +import java.util.concurrent.*; + +interface I { +} + +class A implements I { + static int x = (int)(10*Math.random()); // Suppress initialization. +} + public class Main { - public static void main(String[] args) { - System.out.println("Hello, world!"); + public static void main(String[] args) throws Exception { + + final CountDownLatch first = new CountDownLatch(1); + final CountDownLatch second = new CountDownLatch(1); + + new Thread(new Runnable() { + public void run() { + try { + synchronized(I.class) { + first.countDown(); + second.await(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }).start(); + + first.await(); + new A(); + second.countDown(); + System.out.println("Hello, world!"); } } diff --git a/test/170-interface-init/expected.txt b/test/170-interface-init/expected.txt new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/170-interface-init/expected.txt @@ -0,0 +1 @@ +Done. diff --git a/test/170-interface-init/info.txt b/test/170-interface-init/info.txt new file mode 100644 index 0000000000..28401adf08 --- /dev/null +++ b/test/170-interface-init/info.txt @@ -0,0 +1,2 @@ +Check that holding a lock on an interface class does not block initialization of an +implementing class. diff --git a/test/170-interface-init/src/Main.java b/test/170-interface-init/src/Main.java new file mode 100644 index 0000000000..62f6c5cab2 --- /dev/null +++ b/test/170-interface-init/src/Main.java @@ -0,0 +1,50 @@ +/* + * 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.util.concurrent.CountDownLatch; + +interface I { +} + +class A implements I { + static int x = (int)(10*Math.random()); // Suppress compile-time initialization. +} + +public class Main { + public static void main(String[] args) throws Exception { + final CountDownLatch first = new CountDownLatch(1); + final CountDownLatch second = new CountDownLatch(1); + + new Thread(new Runnable() { + public void run() { + try { + synchronized(I.class) { + first.countDown(); + second.await(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }).start(); + + first.await(); + new A(); // Will initialize A. + second.countDown(); + + System.out.println("Done."); + } +} diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index b8427f491b..fa6ef54c93 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -792,7 +792,6 @@ dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \ $DEBUGGER_OPTS \ $DALVIKVM_BOOT_OPT \ $TMP_DIR_OPTION \ - -XX:DumpNativeStackOnSigQuit:false \ -cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX $MAIN $ARGS" # Remove whitespace. -- GitLab From 9ea84d0bd33694162eb27d9d06bb687f8794a6a0 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Fri, 2 Mar 2018 09:32:03 -0800 Subject: [PATCH 016/749] ART: Update dex-file fallback code Allow non-executable oat files when the boot image is out of date. Bug: 73667005 Test: m test-art-host Change-Id: Ib04339bd87fa5e268d0c636c98df62ee72d613c5 --- runtime/oat_file_assistant.cc | 37 ++++++++++++++++++++++++----------- runtime/oat_file_manager.cc | 3 +++ 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 6bf05b77a8..c5569ff909 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -1261,18 +1261,33 @@ std::unique_ptr OatFileAssistant::OatFileInfo::ReleaseFileForUse() { return ReleaseFile(); } - if (Status() == kOatRelocationOutOfDate) { - // We are loading an oat file for runtime use that needs relocation. - // Reload the file non-executable to ensure that we interpret out of the - // dex code in the oat file rather than trying to execute the unrelocated - // compiled code. - oat_file_assistant_->load_executable_ = false; - Reset(); - if (IsUseable()) { - CHECK(!IsExecutable()); - return ReleaseFile(); - } + switch (Status()) { + case kOatBootImageOutOfDate: + if (oat_file_assistant_->HasOriginalDexFiles()) { + // If there are original dex files, it is better to use them. + break; + } + FALLTHROUGH_INTENDED; + + case kOatRelocationOutOfDate: + // We are loading an oat file for runtime use that needs relocation. + // Reload the file non-executable to ensure that we interpret out of the + // dex code in the oat file rather than trying to execute the unrelocated + // compiled code. + oat_file_assistant_->load_executable_ = false; + Reset(); + if (IsUseable()) { + CHECK(!IsExecutable()); + return ReleaseFile(); + } + break; + + case kOatUpToDate: + case kOatCannotOpen: + case kOatDexOutOfDate: + break; } + return std::unique_ptr(); } diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index e4194442d3..f6fb9ded87 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -469,6 +469,9 @@ std::vector> OatFileManager::OpenDexFilesFromOat( // Get the oat file on disk. std::unique_ptr oat_file(oat_file_assistant.GetBestOatFile().release()); + VLOG(oat) << "OatFileAssistant(" << dex_location << ").GetBestOatFile()=" + << reinterpret_cast(oat_file.get()) + << " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")"; if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) { // Prevent oat files from being loaded if no class_loader or dex_elements are provided. -- GitLab From 996fabcc8296efd346b4f56b5a4a85d63445c83b Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Mon, 5 Mar 2018 16:41:03 -0800 Subject: [PATCH 017/749] Relax ABS tests when not running ART. Rationale: As a "quality of implementation", rather than pure "spec compliance", ART's version of abs() clears the sign bit (but changes nothing else) for all numbers, including NaN (signaling NaN may become quiet though). We cannot expect non-ART implementations to do the same always though. Test: 631, 645 host (ART and --jvm) Bug: 74085456 Change-Id: I2d7db301c2820c60cfb879fc19079404194e53d1 --- test/631-checker-fp-abs/src/Main.java | 28 +++++++++++++++++++++---- test/645-checker-abs-simd/src/Main.java | 17 +++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/test/631-checker-fp-abs/src/Main.java b/test/631-checker-fp-abs/src/Main.java index 0f85dc6865..2db93b8248 100644 --- a/test/631-checker-fp-abs/src/Main.java +++ b/test/631-checker-fp-abs/src/Main.java @@ -23,6 +23,9 @@ */ public class Main { + private final static boolean isDalvik = + System.getProperty("java.vm.name").equals("Dalvik"); + private static final int SPQUIET = 1 << 22; private static final long DPQUIET = 1L << 51; @@ -73,13 +76,16 @@ public class Main { // A few NaN numbers. int[] spnans = { - 0x7f800001, + 0x7f800001, // signaling 0x7fa00000, - 0x7fc00000, + 0x7fbfffff, + 0x7fc00000, // quiet + 0x7fc00001, 0x7fffffff, - 0xff800001, + 0xff800001, // signaling 0xffa00000, - 0xffc00000, + 0xffbfffff, + 0xffc00000, // quiet 0xffffffff }; for (int i = 0; i < spnans.length; i++) { @@ -142,6 +148,13 @@ public class Main { // We allow that an expected NaN result has become quiet. private static void expectEqualsNaN32(int expected, int result) { if (expected != result && (expected | SPQUIET) != result) { + if (!isDalvik) { + // If not on ART, relax the expected value more towards just + // "spec compliance" and allow sign bit to remain set for NaN. + if (expected == (result & Integer.MAX_VALUE)) { + return; + } + } throw new Error("Expected: 0x" + Integer.toHexString(expected) + ", found: 0x" + Integer.toHexString(result)); } @@ -157,6 +170,13 @@ public class Main { // We allow that an expected NaN result has become quiet. private static void expectEqualsNaN64(long expected, long result) { if (expected != result && (expected | DPQUIET) != result) { + if (!isDalvik) { + // If not on ART, relax the expected value more towards just + // "spec compliance" and allow sign bit to remain set for NaN. + if (expected == (result & Long.MAX_VALUE)) { + return; + } + } throw new Error("Expected: 0x" + Long.toHexString(expected) + ", found: 0x" + Long.toHexString(result)); } diff --git a/test/645-checker-abs-simd/src/Main.java b/test/645-checker-abs-simd/src/Main.java index 4c69e58004..870a403ff5 100644 --- a/test/645-checker-abs-simd/src/Main.java +++ b/test/645-checker-abs-simd/src/Main.java @@ -19,6 +19,9 @@ */ public class Main { + private final static boolean isDalvik = + System.getProperty("java.vm.name").equals("Dalvik"); + private static final int SPQUIET = 1 << 22; private static final long DPQUIET = 1L << 51; @@ -378,6 +381,13 @@ public class Main { // We allow that an expected NaN result has become quiet. private static void expectEqualsNaN32(int expected, int result) { if (expected != result && (expected | SPQUIET) != result) { + if (!isDalvik) { + // If not on ART, relax the expected value more towards just + // "spec compliance" and allow sign bit to remain set for NaN. + if (expected == (result & Integer.MAX_VALUE)) { + return; + } + } throw new Error("Expected: 0x" + Integer.toHexString(expected) + ", found: 0x" + Integer.toHexString(result)); } @@ -386,6 +396,13 @@ public class Main { // We allow that an expected NaN result has become quiet. private static void expectEqualsNaN64(long expected, long result) { if (expected != result && (expected | DPQUIET) != result) { + if (!isDalvik) { + // If not on ART, relax the expected value more towards just + // "spec compliance" and allow sign bit to remain set for NaN. + if (expected == (result & Long.MAX_VALUE)) { + return; + } + } throw new Error("Expected: 0x" + Long.toHexString(expected) + ", found: 0x" + Long.toHexString(result)); } -- GitLab From 8bbc11a1727bf5339905be1938c4671e34092c50 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 6 Mar 2018 14:20:13 +0000 Subject: [PATCH 018/749] Fix 562-checker-no-intermediate for HAdd. Fix breakage caused by https://android-review.googlesource.com/629998 Test: testrunner.py --target --optimizing \ -t 562-checker-no-intermediate Bug: 65164101 Change-Id: I15557d3c7c13744b999827914db8e117a56ec18a --- test/562-checker-no-intermediate/src/Main.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/test/562-checker-no-intermediate/src/Main.java b/test/562-checker-no-intermediate/src/Main.java index 104ba8bc06..d61a9b1d89 100644 --- a/test/562-checker-no-intermediate/src/Main.java +++ b/test/562-checker-no-intermediate/src/Main.java @@ -26,7 +26,7 @@ public class Main { /// CHECK-DAG: <> NullCheck /// CHECK-DAG: <> BoundsCheck /// CHECK-DAG: <> ArrayGet [<>,<>] - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt + /// CHECK-DAG: <> InvokeStaticOrDirect [<>{{(,[ij]\d+)?}}] method_name:Main.$noinline$abs /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: ArraySet [<>,<>,<>] @@ -37,7 +37,7 @@ public class Main { /// CHECK-DAG: <> BoundsCheck /// CHECK-DAG: <> IntermediateAddress [<>,<>] /// CHECK-DAG: <> ArrayGet [<>,<>] - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt + /// CHECK-DAG: <> InvokeStaticOrDirect [<>{{(,[ij]\d+)?}}] method_name:Main.$noinline$abs /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: <> IntermediateAddress [<>,<>] /// CHECK-DAG: ArraySet [<>,<>,<>] @@ -49,7 +49,7 @@ public class Main { /// CHECK-DAG: <> BoundsCheck /// CHECK-DAG: <> IntermediateAddress [<>,<>] /// CHECK-DAG: <> ArrayGet [<>,<>] - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt + /// CHECK-DAG: <> InvokeStaticOrDirect [<>{{(,[ij]\d+)?}}] method_name:Main.$noinline$abs /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: <> IntermediateAddress [<>,<>] /// CHECK-DAG: ArraySet [<>,<>,<>] @@ -60,7 +60,7 @@ public class Main { /// CHECK-DAG: <> NullCheck /// CHECK-DAG: <> BoundsCheck /// CHECK-DAG: <> ArrayGet [<>,<>] - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt + /// CHECK-DAG: <> InvokeStaticOrDirect [<>{{(,[ij]\d+)?}}] method_name:Main.$noinline$abs /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: ArraySet [<>,<>,<>] @@ -71,7 +71,7 @@ public class Main { /// CHECK-DAG: <> BoundsCheck /// CHECK-DAG: <> IntermediateAddress [<>,<>] /// CHECK-DAG: <> ArrayGet [<>,<>] - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt + /// CHECK-DAG: <> InvokeStaticOrDirect [<>{{(,[ij]\d+)?}}] method_name:Main.$noinline$abs /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: <> IntermediateAddress [<>,<>] /// CHECK-DAG: ArraySet [<>,<>,<>] @@ -83,13 +83,17 @@ public class Main { /// CHECK-DAG: <> BoundsCheck /// CHECK-DAG: <> IntermediateAddress [<>,<>] /// CHECK-DAG: <> ArrayGet [<>,<>] - /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt + /// CHECK-DAG: <> InvokeStaticOrDirect [<>{{(,[ij]\d+)?}}] method_name:Main.$noinline$abs /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: <> IntermediateAddress [<>,<>] /// CHECK-DAG: ArraySet [<>,<>,<>] public static void main(String[] args) { - array[index] += Math.abs(-42); + array[index] += $noinline$abs(-42); + } + + public static int $noinline$abs(int value) { + return Math.abs(value); } static int index = 0; -- GitLab From 8f81de5e6231fed74108a709e61136769fba44ab Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 6 Mar 2018 08:39:21 -0800 Subject: [PATCH 019/749] ART: Add to comments Follow-up to commit 9ea84d0bd33694162eb27d9d06bb687f8794a6a0. Bug: 73667005 Test: m test-art-host Change-Id: I77cc688a35fc9413eca4c352f88d2b31e8ca11d1 --- runtime/oat_file_assistant.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index c5569ff909..c96d8258f2 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -1263,10 +1263,14 @@ std::unique_ptr OatFileAssistant::OatFileInfo::ReleaseFileForUse() { switch (Status()) { case kOatBootImageOutOfDate: + // OutOfDate may be either a mismatched image, or a missing image. if (oat_file_assistant_->HasOriginalDexFiles()) { - // If there are original dex files, it is better to use them. + // If there are original dex files, it is better to use them (to avoid a potential + // quickening mismatch because the boot image changed). break; } + // If we do not accept the oat file, we may not have access to dex bytecode at all. Grudgingly + // go forward. FALLTHROUGH_INTENDED; case kOatRelocationOutOfDate: -- GitLab From 035105ff976680f11fa4fb12f1d42e2b7e250503 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 5 Mar 2018 17:48:48 -0800 Subject: [PATCH 020/749] Ensure when NotifyMethodRedefined is called the ArtMethod is valid Previously we were calling Jit::JitCodeCache::NotifyMethodRedefined with an ArtMethod that was not fully valid. This could cause OOB memory accesses if NotifyMethodRedefined attempts to access the incorrect dex-file associated with the method at that time. This occurs if the method is a native method. By looking at the wrong dex file the JIT will get an incorrect MethodID and Shorty meaning it is unable to correctly update the jit-code-cache. Test: ./test.py --host -j50 Test: Run WIP dexmaker tests that hit this issue. Bug: 74116990 Change-Id: Ied035b01b07d595df4037352b4bd20b42d285cb9 --- openjdkjvmti/ti_redefine.cc | 24 ++- test/1949-short-dex-file/expected.txt | 1 + test/1949-short-dex-file/info.txt | 30 ++++ test/1949-short-dex-file/run | 17 +++ test/1949-short-dex-file/src/Main.java | 21 +++ .../src/art/Redefinition.java | 91 +++++++++++ .../1949-short-dex-file/src/art/Test1949.java | 142 ++++++++++++++++++ 7 files changed, 319 insertions(+), 7 deletions(-) create mode 100644 test/1949-short-dex-file/expected.txt create mode 100644 test/1949-short-dex-file/info.txt create mode 100755 test/1949-short-dex-file/run create mode 100644 test/1949-short-dex-file/src/Main.java create mode 100644 test/1949-short-dex-file/src/art/Redefinition.java create mode 100644 test/1949-short-dex-file/src/art/Test1949.java diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index 46734548a8..5d430d2073 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -1402,13 +1402,6 @@ void Redefiner::ClassRedefinition::UpdateMethods(art::ObjPtr method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(class_def, dex_method_idx)); // Clear all the intrinsics related flags. method.SetNotIntrinsic(); - // Notify the jit that this method is redefined. - art::jit::Jit* jit = driver_->runtime_->GetJit(); - // Non-invokable methods don't have any JIT data associated with them so we don't need to tell - // the jit about them. - if (jit != nullptr && method.IsInvokable()) { - jit->GetCodeCache()->NotifyMethodRedefined(&method); - } } } @@ -1450,6 +1443,23 @@ void Redefiner::ClassRedefinition::UpdateClass( art::ObjPtr ext(mclass->GetExtData()); CHECK(!ext.IsNull()); ext->SetOriginalDexFile(original_dex_file); + + // Notify the jit that all the methods in this class were redefined. Need to do this last since + // the jit relies on the dex_file_ being correct (for native methods at least) to find the method + // meta-data. + art::jit::Jit* jit = driver_->runtime_->GetJit(); + if (jit != nullptr) { + art::PointerSize image_pointer_size = + driver_->runtime_->GetClassLinker()->GetImagePointerSize(); + auto code_cache = jit->GetCodeCache(); + // Non-invokable methods don't have any JIT data associated with them so we don't need to tell + // the jit about them. + for (art::ArtMethod& method : mclass->GetDeclaredMethods(image_pointer_size)) { + if (method.IsInvokable()) { + code_cache->NotifyMethodRedefined(&method); + } + } + } } // Restores the old obsolete methods maps if it turns out they weren't needed (ie there were no new diff --git a/test/1949-short-dex-file/expected.txt b/test/1949-short-dex-file/expected.txt new file mode 100644 index 0000000000..863339fb8c --- /dev/null +++ b/test/1949-short-dex-file/expected.txt @@ -0,0 +1 @@ +Passed diff --git a/test/1949-short-dex-file/info.txt b/test/1949-short-dex-file/info.txt new file mode 100644 index 0000000000..e924086e23 --- /dev/null +++ b/test/1949-short-dex-file/info.txt @@ -0,0 +1,30 @@ +Tests the fix for b/74116990 + +The JIT was reading into incorrect dex files during class redefinition if a +native method was present. + +The transformed dex file is specifically crafted to have exactly 4 methodIDs in +it. They are (in order): + (0) Ljava/lang/Object;->()V + (1) Lxyz/Transform;->()V + (2) Lxyz/Transform;->bar()V + (3) Lxyz/Transform;->foo()V + +In the transformed version of the dex file there is a new method. The new list of methodIDs is: + (0) Lart/Test1949;->doNothing()V + (1) Ljava/lang/Object;->()V + (2) Lxyz/Transform;->()V + (3) Lxyz/Transform;->bar()V + (4) Lxyz/Transform;->foo()V + +This test tries to get the JIT to read out-of-bounds on the initial dex file by getting it to +read the 5th method id of the new file (Lxyz/Transform;->foo()V) from the old dex file (which +only has 4 method ids). + +To do this we need to make sure that the class being transformed is near the end of the +alphabet (package xyz, method foo). If it is further forward than the other method-ids then the +JIT will read an incorrect (but valid) method-id from the old-dex file. This is why the error +wasn't caught in our other tests (package art is always at the front). + +The final method that causes the OOB read needs to be a native method because that is the only +method-type the jit uses dex-file information to keep track of. diff --git a/test/1949-short-dex-file/run b/test/1949-short-dex-file/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/1949-short-dex-file/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +./default-run "$@" --jvmti diff --git a/test/1949-short-dex-file/src/Main.java b/test/1949-short-dex-file/src/Main.java new file mode 100644 index 0000000000..dbbaa861c9 --- /dev/null +++ b/test/1949-short-dex-file/src/Main.java @@ -0,0 +1,21 @@ +/* + * 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. + */ + +public class Main { + public static void main(String[] args) throws Exception { + art.Test1949.run(); + } +} diff --git a/test/1949-short-dex-file/src/art/Redefinition.java b/test/1949-short-dex-file/src/art/Redefinition.java new file mode 100644 index 0000000000..56d2938a01 --- /dev/null +++ b/test/1949-short-dex-file/src/art/Redefinition.java @@ -0,0 +1,91 @@ +/* + * 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. + */ + +package art; + +import java.util.ArrayList; +// Common Redefinition functions. Placed here for use by CTS +public class Redefinition { + public static final class CommonClassDefinition { + public final Class target; + public final byte[] class_file_bytes; + public final byte[] dex_file_bytes; + + public CommonClassDefinition(Class target, byte[] class_file_bytes, byte[] dex_file_bytes) { + this.target = target; + this.class_file_bytes = class_file_bytes; + this.dex_file_bytes = dex_file_bytes; + } + } + + // A set of possible test configurations. Test should set this if they need to. + // This must be kept in sync with the defines in ti-agent/common_helper.cc + public static enum Config { + COMMON_REDEFINE(0), + COMMON_RETRANSFORM(1), + COMMON_TRANSFORM(2); + + private final int val; + private Config(int val) { + this.val = val; + } + } + + public static void setTestConfiguration(Config type) { + nativeSetTestConfiguration(type.val); + } + + private static native void nativeSetTestConfiguration(int type); + + // Transforms the class + public static native void doCommonClassRedefinition(Class target, + byte[] classfile, + byte[] dexfile); + + public static void doMultiClassRedefinition(CommonClassDefinition... defs) { + ArrayList> classes = new ArrayList<>(); + ArrayList class_files = new ArrayList<>(); + ArrayList dex_files = new ArrayList<>(); + + for (CommonClassDefinition d : defs) { + classes.add(d.target); + class_files.add(d.class_file_bytes); + dex_files.add(d.dex_file_bytes); + } + doCommonMultiClassRedefinition(classes.toArray(new Class[0]), + class_files.toArray(new byte[0][]), + dex_files.toArray(new byte[0][])); + } + + public static void addMultiTransformationResults(CommonClassDefinition... defs) { + for (CommonClassDefinition d : defs) { + addCommonTransformationResult(d.target.getCanonicalName(), + d.class_file_bytes, + d.dex_file_bytes); + } + } + + public static native void doCommonMultiClassRedefinition(Class[] targets, + byte[][] classfiles, + byte[][] dexfiles); + public static native void doCommonClassRetransformation(Class... target); + public static native void setPopRetransformations(boolean pop); + public static native void popTransformationFor(String name); + public static native void enableCommonRetransformation(boolean enable); + public static native void addCommonTransformationResult(String target_name, + byte[] class_bytes, + byte[] dex_bytes); +} diff --git a/test/1949-short-dex-file/src/art/Test1949.java b/test/1949-short-dex-file/src/art/Test1949.java new file mode 100644 index 0000000000..98fa7fc2c1 --- /dev/null +++ b/test/1949-short-dex-file/src/art/Test1949.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art; + +import java.lang.reflect.*; +import java.util.Base64; +import java.nio.ByteBuffer; + +public class Test1949 { + private final static boolean isDalvik = System.getProperty("java.vm.name").equals("Dalvik"); + + // This dex file is specifically crafted to have exactly 4 methodIDs in it. They are (in order): + // (0) Ljava/lang/Object;->()V + // (1) Lxyz/Transform;->()V + // (2) Lxyz/Transform;->bar()V + // (3) Lxyz/Transform;->foo()V + // + // In the transformed version of the dex file there is a new method. The new list of methodIDs is: + // (0) Lart/Test1949;->doNothing()V + // (1) Ljava/lang/Object;->()V + // (2) Lxyz/Transform;->()V + // (3) Lxyz/Transform;->bar()V + // (4) Lxyz/Transform;->foo()V + // + // This test tries to get the JIT to read out-of-bounds on the initial dex file by getting it to + // read the 5th method id of the new file (Lxyz/Transform;->foo()V) from the old dex file (which + // only has 4 method ids). + // + // To do this we need to make sure that the class being transformed is near the end of the + // alphabet (package xyz, method foo). If it is further forward than the other method-ids then the + // JIT will read an incorrect (but valid) method-id from the old-dex file. This is why the error + // wasn't caught in our other tests (package art is always at the front). + // + // The final method that causes the OOB read needs to be a native method because that is the only + // method-type the jit uses dex-file information to keep track of. + + /** + * base64 encoded class/dex file for + * package xyz; + * public class Transform { + * public native void foo(); + * public void bar() {} + * } + */ + private static final byte[] CLASS_BYTES_INIT = Base64.getDecoder().decode( + "yv66vgAAADUADwoAAwAMBwANBwAOAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJU" + + "YWJsZQEAA2ZvbwEAA2JhcgEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwABAAFAQANeHl6" + + "L1RyYW5zZm9ybQEAEGphdmEvbGFuZy9PYmplY3QAIQACAAMAAAAAAAMAAQAEAAUAAQAGAAAAHQAB" + + "AAEAAAAFKrcAAbEAAAABAAcAAAAGAAEAAAACAQEACAAFAAAAAQAJAAUAAQAGAAAAGQAAAAEAAAAB" + + "sQAAAAEABwAAAAYAAQAAAAQAAQAKAAAAAgAL"); + private static final byte[] DEX_BYTES_INIT = Base64.getDecoder().decode( + "ZGV4CjAzNQBDUutFJpeT+okk+aXah8NQ61q2XRtkmChwAgAAcAAAAHhWNBIAAAAAAAAAANwBAAAI" + + "AAAAcAAAAAMAAACQAAAAAQAAAJwAAAAAAAAAAAAAAAQAAACoAAAAAQAAAMgAAACIAQAA6AAAABwB" + + "AAAkAQAAOAEAAEkBAABZAQAAXAEAAGEBAABmAQAAAQAAAAIAAAAEAAAABAAAAAIAAAAAAAAAAAAA" + + "AAAAAAABAAAAAAAAAAEAAAAFAAAAAQAAAAYAAAABAAAAAQAAAAAAAAAAAAAAAwAAAAAAAADDAQAA" + + "AAAAAAEAAQABAAAAEgEAAAQAAABwEAAAAAAOAAEAAQAAAAAAFgEAAAEAAAAOAAIADgAEAA4AAAAG" + + "PGluaXQ+ABJMamF2YS9sYW5nL09iamVjdDsAD0x4eXovVHJhbnNmb3JtOwAOVHJhbnNmb3JtLmph" + + "dmEAAVYAA2JhcgADZm9vAFt+fkQ4eyJtaW4tYXBpIjoxLCJzaGEtMSI6IjkwZWYyMjkwNWMzZmVj" + + "Y2FiMjMwMzBhNmJkYzU2NTcwYTMzNWVmMDUiLCJ2ZXJzaW9uIjoidjEuMS44LWRldiJ9AAAAAQIB" + + "gYAE6AECAYACAYECAAAAAAAAAAAMAAAAAAAAAAEAAAAAAAAAAQAAAAgAAABwAAAAAgAAAAMAAACQ" + + "AAAAAwAAAAEAAACcAAAABQAAAAQAAACoAAAABgAAAAEAAADIAAAAASAAAAIAAADoAAAAAyAAAAIA" + + "AAASAQAAAiAAAAgAAAAcAQAAACAAAAEAAADDAQAAAxAAAAEAAADYAQAAABAAAAEAAADcAQAA"); + + /** + * base64 encoded class/dex file for + * package xyz; + * public class Transform { + * public native void foo(); + * public void bar() { + * // Make sure the methodID is before any of the ones in Transform + * art.Test1949.doNothing(); + * } + * } + */ + private static final byte[] CLASS_BYTES_FINAL = Base64.getDecoder().decode( + "yv66vgAAADUAFAoABAANCgAOAA8HABAHABEBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51" + + "bWJlclRhYmxlAQADZm9vAQADYmFyAQAKU291cmNlRmlsZQEADlRyYW5zZm9ybS5qYXZhDAAFAAYH" + + "ABIMABMABgEADXh5ei9UcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAMYXJ0L1Rlc3QxOTQ5" + + "AQAJZG9Ob3RoaW5nACEAAwAEAAAAAAADAAEABQAGAAEABwAAAB0AAQABAAAABSq3AAGxAAAAAQAI" + + "AAAABgABAAAAAgEBAAkABgAAAAEACgAGAAEABwAAABwAAAABAAAABLgAArEAAAABAAgAAAAGAAEA" + + "AAAEAAEACwAAAAIADA=="); + private static final byte[] DEX_BYTES_FINAL = Base64.getDecoder().decode( + "ZGV4CjAzNQBHXBiw7Hso1vnmaXE1VCV41f4+0aECixOgAgAAcAAAAHhWNBIAAAAAAAAAAAwCAAAK" + + "AAAAcAAAAAQAAACYAAAAAQAAAKgAAAAAAAAAAAAAAAUAAAC0AAAAAQAAANwAAACkAQAA/AAAADQB" + + "AAA8AQAATAEAAGABAABxAQAAgQEAAIQBAACJAQAAlAEAAJkBAAABAAAAAgAAAAMAAAAFAAAABQAA" + + "AAMAAAAAAAAAAAAAAAcAAAABAAAAAAAAAAIAAAAAAAAAAgAAAAYAAAACAAAACAAAAAIAAAABAAAA" + + "AQAAAAAAAAAEAAAAAAAAAPYBAAAAAAAAAQABAAEAAAAsAQAABAAAAHAQAQAAAA4AAQABAAAAAAAw" + + "AQAABAAAAHEAAAAAAA4AAgAOAAQADgAGPGluaXQ+AA5MYXJ0L1Rlc3QxOTQ5OwASTGphdmEvbGFu" + + "Zy9PYmplY3Q7AA9MeHl6L1RyYW5zZm9ybTsADlRyYW5zZm9ybS5qYXZhAAFWAANiYXIACWRvTm90" + + "aGluZwADZm9vAFt+fkQ4eyJtaW4tYXBpIjoxLCJzaGEtMSI6IjkwZWYyMjkwNWMzZmVjY2FiMjMw" + + "MzBhNmJkYzU2NTcwYTMzNWVmMDUiLCJ2ZXJzaW9uIjoidjEuMS44LWRldiJ9AAAAAQICgYAE/AED" + + "AZQCAYECAAAAAAAMAAAAAAAAAAEAAAAAAAAAAQAAAAoAAABwAAAAAgAAAAQAAACYAAAAAwAAAAEA" + + "AACoAAAABQAAAAUAAAC0AAAABgAAAAEAAADcAAAAASAAAAIAAAD8AAAAAyAAAAIAAAAsAQAAAiAA" + + "AAoAAAA0AQAAACAAAAEAAAD2AQAAAxAAAAEAAAAIAgAAABAAAAEAAAAMAgAA"); + + public static void run() throws Exception { + Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE); + doTest(); + } + + // A method with a methodID before anything in Transform. + public static void doNothing() {} + + private static ClassLoader CreateClassLoader(byte[] clz, byte[] dex) throws Exception { + if (isDalvik) { + Class class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader"); + Constructor ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class); + /* on Dalvik, this is a DexFile; otherwise, it's null */ + return (ClassLoader)ctor.newInstance(ByteBuffer.wrap(dex), Test1949.class.getClassLoader()); + } else { + return new ClassLoader() { + public Class findClass(String name) throws ClassNotFoundException { + if (name.equals("xyz.Transform")) { + return defineClass(name, clz, 0, clz.length); + } else { + throw new ClassNotFoundException("Couldn't find class: " + name); + } + } + }; + } + } + + public static void doTest() throws Exception { + Class c = CreateClassLoader(CLASS_BYTES_INIT, DEX_BYTES_INIT).loadClass("xyz.Transform"); + Redefinition.doCommonClassRedefinition(c, CLASS_BYTES_FINAL, DEX_BYTES_FINAL); + System.out.println("Passed"); + } +} -- GitLab From f709ba5b7f5a3802c6e216117f1202d6ee0e4591 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Tue, 6 Mar 2018 10:51:09 -0800 Subject: [PATCH 021/749] Add skip for 715 in redefine-stress This test requires annotations that slicer does not keep around. Bug: 37239009 Test: ./test.py --host -j50 Test: ./test.py --host --redefine-stress -j50 Change-Id: I340519ca74c84c6d476a3e2b8392bc51fa050e56 --- test/knownfailures.json | 1 + 1 file changed, 1 insertion(+) diff --git a/test/knownfailures.json b/test/knownfailures.json index 2bf09c3f82..5fb78191ef 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -504,6 +504,7 @@ { "tests": [ "031-class-attributes", + "715-clinit-implicit-parameter-annotations", "911-get-stack-trace" ], "description": [ -- GitLab From f5d5eb1851841abff74b2c2757c58988a5ccd3b1 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Tue, 6 Mar 2018 15:13:59 -0800 Subject: [PATCH 022/749] Remove FramePop trace listener if no outstanding events Previously we would leave the JvmtiMethodTraceListener installed forever if we even had a FramePop event requested. This was to prevent any possible UAF issues with shadow-frames. This changes it so we will check if there are any outstanding shadow-frames when the event is disabled. If there are not any left we will remove it and remove the deopts. This should improve performance after a frame-pop event is used. Bug: 74240081 Bug: 34414072 Test: ./test.py --host -j50 Test: Debug system-server a bit and use Step-Out. Change-Id: I5fdb8ccce95ba51d6113df12e01bb158d210a5b2 --- openjdkjvmti/events.cc | 31 ++++++++++++++++++++++++++----- openjdkjvmti/events.h | 3 +++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index 8b40a7e072..07b1529adb 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -1004,6 +1004,27 @@ bool EventHandler::OtherMonitorEventsEnabledAnywhere(ArtJvmtiEvent event) { return false; } +void EventHandler::SetupFramePopTraceListener(bool enable) { + if (enable) { + frame_pop_enabled = true; + SetupTraceListener(method_trace_listener_.get(), ArtJvmtiEvent::kFramePop, enable); + } else { + // remove the listener if we have no outstanding frames. + { + art::ReaderMutexLock mu(art::Thread::Current(), envs_lock_); + for (ArtJvmTiEnv* env : envs) { + art::ReaderMutexLock event_mu(art::Thread::Current(), env->event_info_mutex_); + if (!env->notify_frames.empty()) { + // Leaving FramePop listener since there are unsent FramePop events. + return; + } + } + frame_pop_enabled = false; + } + SetupTraceListener(method_trace_listener_.get(), ArtJvmtiEvent::kFramePop, enable); + } +} + // Handle special work for the given event type, if necessary. void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { switch (event) { @@ -1018,14 +1039,14 @@ void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { case ArtJvmtiEvent::kGarbageCollectionFinish: SetupGcPauseTracking(gc_pause_listener_.get(), event, enable); return; - // FramePop can never be disabled once it's been turned on since we would either need to deal - // with dangling pointers or have missed events. - // TODO We really need to make this not the case anymore. + // FramePop can never be disabled once it's been turned on if it was turned off with outstanding + // pop-events since we would either need to deal with dangling pointers or have missed events. case ArtJvmtiEvent::kFramePop: - if (!enable || (enable && frame_pop_enabled)) { + if (enable && frame_pop_enabled) { + // The frame-pop event was held on by pending events so we don't need to do anything. break; } else { - SetupTraceListener(method_trace_listener_.get(), event, enable); + SetupFramePopTraceListener(enable); break; } case ArtJvmtiEvent::kMethodEntry: diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h index 8141eff88c..bf12cb191e 100644 --- a/openjdkjvmti/events.h +++ b/openjdkjvmti/events.h @@ -247,6 +247,9 @@ class EventHandler { private: void SetupTraceListener(JvmtiMethodTraceListener* listener, ArtJvmtiEvent event, bool enable); + // Specifically handle the FramePop event which it might not always be possible to turn off. + void SetupFramePopTraceListener(bool enable); + template ALWAYS_INLINE inline std::vector> CollectEvents(art::Thread* thread, -- GitLab From 453e0e557d608d82012a49fedd1b9c83bb54daee Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Tue, 6 Mar 2018 14:02:55 -0800 Subject: [PATCH 023/749] Change Addr2line to use poll. Moving from select to poll avoids any problems with the fd being too large for the call to FD_SET. It also slightly simplifies the code. Make sure that the call to read only ever occurs if there is really data to be read. There was a case with the previous use of the select call where the read would be called when there was no data to be read which caused the process to hang forever. Test: Ran the 137 test forcing a crash and verified the addr2line Test: information is correct. Test: Ran the 141 test forcing a crash and verifying that when more than Test: two lines are returned by addr2line, it still display them all. Test: Modified the Addr2line call to pass in an emptry string for the Test: executable and verified there are no hangs. Change-Id: I3d750e7796d0c70122686aad1eb40856157c9770 --- runtime/native_stack_dump.cc | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc index 396b09abcf..530c266975 100644 --- a/runtime/native_stack_dump.cc +++ b/runtime/native_stack_dump.cc @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -145,21 +146,14 @@ static void Drain(size_t expected, bool prefix_written = false; for (;;) { - constexpr uint32_t kWaitTimeExpectedMicros = 500 * 1000; - constexpr uint32_t kWaitTimeUnexpectedMicros = 50 * 1000; - - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = expected > 0 ? kWaitTimeExpectedMicros : kWaitTimeUnexpectedMicros; - - fd_set rfds; - FD_ZERO(&rfds); - FD_SET(in, &rfds); - - int retval = TEMP_FAILURE_RETRY(select(in + 1, &rfds, nullptr, nullptr, &tv)); - - if (retval < 0) { - // Other side may have crashed or other errors. + constexpr uint32_t kWaitTimeExpectedMilli = 500; + constexpr uint32_t kWaitTimeUnexpectedMilli = 50; + + int timeout = expected > 0 ? kWaitTimeExpectedMilli : kWaitTimeUnexpectedMilli; + struct pollfd read_fd{in, POLLIN, 0}; + int retval = TEMP_FAILURE_RETRY(poll(&read_fd, 1, timeout)); + if (retval == -1) { + // An error occurred. pipe->reset(); return; } @@ -169,19 +163,23 @@ static void Drain(size_t expected, return; } - DCHECK_EQ(retval, 1); + if (!(read_fd.revents & POLLIN)) { + // addr2line call exited. + pipe->reset(); + return; + } constexpr size_t kMaxBuffer = 128; // Relatively small buffer. Should be OK as we're on an // alt stack, but just to be sure... char buffer[kMaxBuffer]; memset(buffer, 0, kMaxBuffer); int bytes_read = TEMP_FAILURE_RETRY(read(in, buffer, kMaxBuffer - 1)); - - if (bytes_read < 0) { + if (bytes_read <= 0) { // This should not really happen... pipe->reset(); return; } + buffer[bytes_read] = '\0'; char* tmp = buffer; while (*tmp != 0) { -- GitLab From edef4ba155f11d2a8544db9e560698a033831934 Mon Sep 17 00:00:00 2001 From: Goran Jakovljevic Date: Mon, 5 Mar 2018 20:03:24 +0100 Subject: [PATCH 024/749] Do not use sa_restorer if not defined Some architectures (mips) don't have sa_restorer in sigaction so don't use it if SA_RESTORER is not defined. This fix aosp_mips-eng build. Test: successful aosp_mips-eng build Change-Id: I233c21bc45fbda178fb58552428534d2ea52dccc --- sigchainlib/sigchain.cc | 6 +++++- test/004-SignalTest/signaltest.cc | 8 ++++++-- test/115-native-bridge/nativebridge.cc | 2 ++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc index 61346b1487..3127c5cfbd 100644 --- a/sigchainlib/sigchain.cc +++ b/sigchainlib/sigchain.cc @@ -80,7 +80,7 @@ static void log(const char* format, ...) { #define fatal(...) log(__VA_ARGS__); abort() -#if defined(__BIONIC__) && !defined(__LP64__) +#if defined(__BIONIC__) && !defined(__LP64__) && !defined(__mips__) static int sigismember(const sigset64_t* sigset, int signum) { return sigismember64(sigset, signum); } @@ -223,7 +223,9 @@ class SignalChain { SigactionType result; result.sa_flags = action_.sa_flags; result.sa_handler = action_.sa_handler; +#if defined(SA_RESTORER) result.sa_restorer = action_.sa_restorer; +#endif memcpy(&result.sa_mask, &action_.sa_mask, std::min(sizeof(action_.sa_mask), sizeof(result.sa_mask))); return result; @@ -237,7 +239,9 @@ class SignalChain { } else { action_.sa_flags = new_action->sa_flags; action_.sa_handler = new_action->sa_handler; +#if defined(SA_RESTORER) action_.sa_restorer = new_action->sa_restorer; +#endif sigemptyset(&action_.sa_mask); memcpy(&action_.sa_mask, &new_action->sa_mask, std::min(sizeof(action_.sa_mask), sizeof(new_action->sa_mask))); diff --git a/test/004-SignalTest/signaltest.cc b/test/004-SignalTest/signaltest.cc index 67118d5e63..49fe369b61 100644 --- a/test/004-SignalTest/signaltest.cc +++ b/test/004-SignalTest/signaltest.cc @@ -102,10 +102,14 @@ static struct sigaction oldaction; bool compare_sigaction(const struct sigaction* lhs, const struct sigaction* rhs) { // bionic's definition of `struct sigaction` has internal padding bytes, so we can't just do a // naive memcmp of the entire struct. +#if defined(SA_RESTORER) + if (lhs->sa_restorer != rhs->sa_restorer) { + return false; + } +#endif return memcmp(&lhs->sa_mask, &rhs->sa_mask, sizeof(lhs->sa_mask)) == 0 && lhs->sa_sigaction == rhs->sa_sigaction && - lhs->sa_flags == rhs->sa_flags && - lhs->sa_restorer == rhs->sa_restorer; + lhs->sa_flags == rhs->sa_flags; } extern "C" JNIEXPORT void JNICALL Java_Main_initSignalTest(JNIEnv*, jclass) { diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc index 3b352096df..a74f7638bd 100644 --- a/test/115-native-bridge/nativebridge.cc +++ b/test/115-native-bridge/nativebridge.cc @@ -229,7 +229,9 @@ static jint trampoline_Java_Main_testSignal(JNIEnv*, jclass) { struct sigaction64 tmp2; sigemptyset64(&tmp2.sa_mask); tmp2.sa_sigaction = test_sigaction_handler; +#if defined(SA_RESTORER) tmp2.sa_restorer = nullptr; +#endif sigaction64(SIGSEGV, &tmp2, nullptr); sigaction64(SIGILL, &tmp2, nullptr); -- GitLab From 14647a3e3e1f5123d71c830a92fc4f499e40ab50 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 7 Mar 2018 09:38:18 +0000 Subject: [PATCH 025/749] Attempt to fix macos build. Test: none Change-Id: I1d6cf89e0b171a6029d7e9ee60bac4d8ee61cdcf --- libartbase/base/unix_file/fd_file.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libartbase/base/unix_file/fd_file.cc b/libartbase/base/unix_file/fd_file.cc index f9da178de8..b2881b8ed0 100644 --- a/libartbase/base/unix_file/fd_file.cc +++ b/libartbase/base/unix_file/fd_file.cc @@ -31,7 +31,7 @@ #else #include #include "base/stl_util.h" -#include "globals.h" +#include "base/globals.h" #endif namespace unix_file { -- GitLab From 0d2cab5c15215eb7a7b9af0ce11f176dcbd69559 Mon Sep 17 00:00:00 2001 From: Lena Djokic Date: Tue, 6 Mar 2018 15:20:45 +0100 Subject: [PATCH 026/749] MIPS: Use PCNT to implement VisitIntegerBitCount() and VisitLongBitCount() Test: ./testrunner.py --target --optimizing in QEMU Test: mma test-art-host-gtest Change-Id: I6ce5bdc86f951094f656c2f81ae8fc836d7a0b5c --- compiler/optimizing/intrinsics_mips.cc | 152 ++++++++++-------- compiler/optimizing/intrinsics_mips.h | 1 + compiler/optimizing/intrinsics_mips64.cc | 90 ++++++----- compiler/optimizing/intrinsics_mips64.h | 2 + compiler/utils/mips/assembler_mips.cc | 20 +++ compiler/utils/mips/assembler_mips.h | 5 + .../utils/mips/assembler_mips32r6_test.cc | 16 ++ compiler/utils/mips64/assembler_mips64.cc | 20 +++ compiler/utils/mips64/assembler_mips64.h | 5 + .../utils/mips64/assembler_mips64_test.cc | 16 ++ disassembler/disassembler_mips.cc | 1 + 11 files changed, 226 insertions(+), 102 deletions(-) diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 6d6ff7576b..c268ef9052 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -58,6 +58,10 @@ inline bool IntrinsicCodeGeneratorMIPS::Is32BitFPU() const { return codegen_->GetInstructionSetFeatures().Is32BitFloatingPoint(); } +inline bool IntrinsicCodeGeneratorMIPS::HasMsa() const { + return codegen_->GetInstructionSetFeatures().HasMsa(); +} + #define __ codegen->GetAssembler()-> static void MoveFromReturnRegister(Location trg, @@ -612,6 +616,7 @@ static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { static void GenBitCount(LocationSummary* locations, DataType::Type type, bool isR6, + bool hasMsa, MipsAssembler* assembler) { Register out = locations->Out().AsRegister(); @@ -637,85 +642,102 @@ static void GenBitCount(LocationSummary* locations, // instructions compared to a loop-based algorithm which required 47 // instructions. - if (type == DataType::Type::kInt32) { - Register in = locations->InAt(0).AsRegister(); - - __ Srl(TMP, in, 1); - __ LoadConst32(AT, 0x55555555); - __ And(TMP, TMP, AT); - __ Subu(TMP, in, TMP); - __ LoadConst32(AT, 0x33333333); - __ And(out, TMP, AT); - __ Srl(TMP, TMP, 2); - __ And(TMP, TMP, AT); - __ Addu(TMP, out, TMP); - __ Srl(out, TMP, 4); - __ Addu(out, out, TMP); - __ LoadConst32(AT, 0x0F0F0F0F); - __ And(out, out, AT); - __ LoadConst32(TMP, 0x01010101); - if (isR6) { - __ MulR6(out, out, TMP); + if (hasMsa) { + if (type == DataType::Type::kInt32) { + Register in = locations->InAt(0).AsRegister(); + __ Mtc1(in, FTMP); + __ PcntW(static_cast(FTMP), static_cast(FTMP)); + __ Mfc1(out, FTMP); } else { - __ MulR2(out, out, TMP); + DCHECK_EQ(type, DataType::Type::kInt64); + Register in_lo = locations->InAt(0).AsRegisterPairLow(); + Register in_hi = locations->InAt(0).AsRegisterPairHigh(); + __ Mtc1(in_lo, FTMP); + __ Mthc1(in_hi, FTMP); + __ PcntD(static_cast(FTMP), static_cast(FTMP)); + __ Mfc1(out, FTMP); } - __ Srl(out, out, 24); } else { - DCHECK_EQ(type, DataType::Type::kInt64); - Register in_lo = locations->InAt(0).AsRegisterPairLow(); - Register in_hi = locations->InAt(0).AsRegisterPairHigh(); - Register tmp_hi = locations->GetTemp(0).AsRegister(); - Register out_hi = locations->GetTemp(1).AsRegister(); - Register tmp_lo = TMP; - Register out_lo = out; + if (type == DataType::Type::kInt32) { + Register in = locations->InAt(0).AsRegister(); + + __ Srl(TMP, in, 1); + __ LoadConst32(AT, 0x55555555); + __ And(TMP, TMP, AT); + __ Subu(TMP, in, TMP); + __ LoadConst32(AT, 0x33333333); + __ And(out, TMP, AT); + __ Srl(TMP, TMP, 2); + __ And(TMP, TMP, AT); + __ Addu(TMP, out, TMP); + __ Srl(out, TMP, 4); + __ Addu(out, out, TMP); + __ LoadConst32(AT, 0x0F0F0F0F); + __ And(out, out, AT); + __ LoadConst32(TMP, 0x01010101); + if (isR6) { + __ MulR6(out, out, TMP); + } else { + __ MulR2(out, out, TMP); + } + __ Srl(out, out, 24); + } else { + DCHECK_EQ(type, DataType::Type::kInt64); + Register in_lo = locations->InAt(0).AsRegisterPairLow(); + Register in_hi = locations->InAt(0).AsRegisterPairHigh(); + Register tmp_hi = locations->GetTemp(0).AsRegister(); + Register out_hi = locations->GetTemp(1).AsRegister(); + Register tmp_lo = TMP; + Register out_lo = out; - __ Srl(tmp_lo, in_lo, 1); - __ Srl(tmp_hi, in_hi, 1); + __ Srl(tmp_lo, in_lo, 1); + __ Srl(tmp_hi, in_hi, 1); - __ LoadConst32(AT, 0x55555555); + __ LoadConst32(AT, 0x55555555); - __ And(tmp_lo, tmp_lo, AT); - __ Subu(tmp_lo, in_lo, tmp_lo); + __ And(tmp_lo, tmp_lo, AT); + __ Subu(tmp_lo, in_lo, tmp_lo); - __ And(tmp_hi, tmp_hi, AT); - __ Subu(tmp_hi, in_hi, tmp_hi); + __ And(tmp_hi, tmp_hi, AT); + __ Subu(tmp_hi, in_hi, tmp_hi); - __ LoadConst32(AT, 0x33333333); + __ LoadConst32(AT, 0x33333333); - __ And(out_lo, tmp_lo, AT); - __ Srl(tmp_lo, tmp_lo, 2); - __ And(tmp_lo, tmp_lo, AT); - __ Addu(tmp_lo, out_lo, tmp_lo); + __ And(out_lo, tmp_lo, AT); + __ Srl(tmp_lo, tmp_lo, 2); + __ And(tmp_lo, tmp_lo, AT); + __ Addu(tmp_lo, out_lo, tmp_lo); - __ And(out_hi, tmp_hi, AT); - __ Srl(tmp_hi, tmp_hi, 2); - __ And(tmp_hi, tmp_hi, AT); - __ Addu(tmp_hi, out_hi, tmp_hi); + __ And(out_hi, tmp_hi, AT); + __ Srl(tmp_hi, tmp_hi, 2); + __ And(tmp_hi, tmp_hi, AT); + __ Addu(tmp_hi, out_hi, tmp_hi); - // Here we deviate from the original algorithm a bit. We've reached - // the stage where the bitfields holding the subtotals are large - // enough to hold the combined subtotals for both the low word, and - // the high word. This means that we can add the subtotals for the - // the high, and low words into a single word, and compute the final - // result for both the high, and low words using fewer instructions. - __ LoadConst32(AT, 0x0F0F0F0F); + // Here we deviate from the original algorithm a bit. We've reached + // the stage where the bitfields holding the subtotals are large + // enough to hold the combined subtotals for both the low word, and + // the high word. This means that we can add the subtotals for the + // the high, and low words into a single word, and compute the final + // result for both the high, and low words using fewer instructions. + __ LoadConst32(AT, 0x0F0F0F0F); - __ Addu(TMP, tmp_hi, tmp_lo); + __ Addu(TMP, tmp_hi, tmp_lo); - __ Srl(out, TMP, 4); - __ And(out, out, AT); - __ And(TMP, TMP, AT); - __ Addu(out, out, TMP); + __ Srl(out, TMP, 4); + __ And(out, out, AT); + __ And(TMP, TMP, AT); + __ Addu(out, out, TMP); - __ LoadConst32(AT, 0x01010101); + __ LoadConst32(AT, 0x01010101); - if (isR6) { - __ MulR6(out, out, AT); - } else { - __ MulR2(out, out, AT); - } + if (isR6) { + __ MulR6(out, out, AT); + } else { + __ MulR2(out, out, AT); + } - __ Srl(out, out, 24); + __ Srl(out, out, 24); + } } } @@ -725,7 +747,7 @@ void IntrinsicLocationsBuilderMIPS::VisitIntegerBitCount(HInvoke* invoke) { } void IntrinsicCodeGeneratorMIPS::VisitIntegerBitCount(HInvoke* invoke) { - GenBitCount(invoke->GetLocations(), DataType::Type::kInt32, IsR6(), GetAssembler()); + GenBitCount(invoke->GetLocations(), DataType::Type::kInt32, IsR6(), HasMsa(), GetAssembler()); } // int java.lang.Long.bitCount(int) @@ -739,7 +761,7 @@ void IntrinsicLocationsBuilderMIPS::VisitLongBitCount(HInvoke* invoke) { } void IntrinsicCodeGeneratorMIPS::VisitLongBitCount(HInvoke* invoke) { - GenBitCount(invoke->GetLocations(), DataType::Type::kInt64, IsR6(), GetAssembler()); + GenBitCount(invoke->GetLocations(), DataType::Type::kInt64, IsR6(), HasMsa(), GetAssembler()); } static void GenMinMaxFP(LocationSummary* locations, diff --git a/compiler/optimizing/intrinsics_mips.h b/compiler/optimizing/intrinsics_mips.h index 13397f11d4..1c1ba40132 100644 --- a/compiler/optimizing/intrinsics_mips.h +++ b/compiler/optimizing/intrinsics_mips.h @@ -71,6 +71,7 @@ class IntrinsicCodeGeneratorMIPS FINAL : public IntrinsicVisitor { bool IsR2OrNewer() const; bool IsR6() const; bool Is32BitFPU() const; + bool HasMsa() const; private: MipsAssembler* GetAssembler(); diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 5debd26c5a..1a7b06d206 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -46,6 +46,10 @@ ArenaAllocator* IntrinsicCodeGeneratorMIPS64::GetAllocator() { return codegen_->GetGraph()->GetAllocator(); } +inline bool IntrinsicCodeGeneratorMIPS64::HasMsa() const { + return codegen_->GetInstructionSetFeatures().HasMsa(); +} + #define __ codegen->GetAssembler()-> static void MoveFromReturnRegister(Location trg, @@ -386,6 +390,7 @@ static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { static void GenBitCount(LocationSummary* locations, const DataType::Type type, + const bool hasMsa, Mips64Assembler* assembler) { GpuRegister out = locations->Out().AsRegister(); GpuRegister in = locations->InAt(0).AsRegister(); @@ -414,41 +419,52 @@ static void GenBitCount(LocationSummary* locations, // bits are set but the algorithm here attempts to minimize the total // number of instructions executed even when a large number of bits // are set. - - if (type == DataType::Type::kInt32) { - __ Srl(TMP, in, 1); - __ LoadConst32(AT, 0x55555555); - __ And(TMP, TMP, AT); - __ Subu(TMP, in, TMP); - __ LoadConst32(AT, 0x33333333); - __ And(out, TMP, AT); - __ Srl(TMP, TMP, 2); - __ And(TMP, TMP, AT); - __ Addu(TMP, out, TMP); - __ Srl(out, TMP, 4); - __ Addu(out, out, TMP); - __ LoadConst32(AT, 0x0F0F0F0F); - __ And(out, out, AT); - __ LoadConst32(TMP, 0x01010101); - __ MulR6(out, out, TMP); - __ Srl(out, out, 24); - } else if (type == DataType::Type::kInt64) { - __ Dsrl(TMP, in, 1); - __ LoadConst64(AT, 0x5555555555555555L); - __ And(TMP, TMP, AT); - __ Dsubu(TMP, in, TMP); - __ LoadConst64(AT, 0x3333333333333333L); - __ And(out, TMP, AT); - __ Dsrl(TMP, TMP, 2); - __ And(TMP, TMP, AT); - __ Daddu(TMP, out, TMP); - __ Dsrl(out, TMP, 4); - __ Daddu(out, out, TMP); - __ LoadConst64(AT, 0x0F0F0F0F0F0F0F0FL); - __ And(out, out, AT); - __ LoadConst64(TMP, 0x0101010101010101L); - __ Dmul(out, out, TMP); - __ Dsrl32(out, out, 24); + if (hasMsa) { + if (type == DataType::Type::kInt32) { + __ Mtc1(in, FTMP); + __ PcntW(static_cast(FTMP), static_cast(FTMP)); + __ Mfc1(out, FTMP); + } else { + __ Dmtc1(in, FTMP); + __ PcntD(static_cast(FTMP), static_cast(FTMP)); + __ Dmfc1(out, FTMP); + } + } else { + if (type == DataType::Type::kInt32) { + __ Srl(TMP, in, 1); + __ LoadConst32(AT, 0x55555555); + __ And(TMP, TMP, AT); + __ Subu(TMP, in, TMP); + __ LoadConst32(AT, 0x33333333); + __ And(out, TMP, AT); + __ Srl(TMP, TMP, 2); + __ And(TMP, TMP, AT); + __ Addu(TMP, out, TMP); + __ Srl(out, TMP, 4); + __ Addu(out, out, TMP); + __ LoadConst32(AT, 0x0F0F0F0F); + __ And(out, out, AT); + __ LoadConst32(TMP, 0x01010101); + __ MulR6(out, out, TMP); + __ Srl(out, out, 24); + } else { + __ Dsrl(TMP, in, 1); + __ LoadConst64(AT, 0x5555555555555555L); + __ And(TMP, TMP, AT); + __ Dsubu(TMP, in, TMP); + __ LoadConst64(AT, 0x3333333333333333L); + __ And(out, TMP, AT); + __ Dsrl(TMP, TMP, 2); + __ And(TMP, TMP, AT); + __ Daddu(TMP, out, TMP); + __ Dsrl(out, TMP, 4); + __ Daddu(out, out, TMP); + __ LoadConst64(AT, 0x0F0F0F0F0F0F0F0FL); + __ And(out, out, AT); + __ LoadConst64(TMP, 0x0101010101010101L); + __ Dmul(out, out, TMP); + __ Dsrl32(out, out, 24); + } } } @@ -458,7 +474,7 @@ void IntrinsicLocationsBuilderMIPS64::VisitIntegerBitCount(HInvoke* invoke) { } void IntrinsicCodeGeneratorMIPS64::VisitIntegerBitCount(HInvoke* invoke) { - GenBitCount(invoke->GetLocations(), DataType::Type::kInt32, GetAssembler()); + GenBitCount(invoke->GetLocations(), DataType::Type::kInt32, HasMsa(), GetAssembler()); } // int java.lang.Long.bitCount(long) @@ -467,7 +483,7 @@ void IntrinsicLocationsBuilderMIPS64::VisitLongBitCount(HInvoke* invoke) { } void IntrinsicCodeGeneratorMIPS64::VisitLongBitCount(HInvoke* invoke) { - GenBitCount(invoke->GetLocations(), DataType::Type::kInt64, GetAssembler()); + GenBitCount(invoke->GetLocations(), DataType::Type::kInt64, HasMsa(), GetAssembler()); } static void GenMinMaxFP(LocationSummary* locations, diff --git a/compiler/optimizing/intrinsics_mips64.h b/compiler/optimizing/intrinsics_mips64.h index 6f40d90ddb..748b0b02b2 100644 --- a/compiler/optimizing/intrinsics_mips64.h +++ b/compiler/optimizing/intrinsics_mips64.h @@ -68,6 +68,8 @@ class IntrinsicCodeGeneratorMIPS64 FINAL : public IntrinsicVisitor { #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS + bool HasMsa() const; + private: Mips64Assembler* GetAssembler(); diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc index 2218ef9af2..b2ad490a57 100644 --- a/compiler/utils/mips/assembler_mips.cc +++ b/compiler/utils/mips/assembler_mips.cc @@ -2793,6 +2793,26 @@ void MipsAssembler::Hadd_uD(VectorRegister wd, VectorRegister ws, VectorRegister DsFsmInstr(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x15)).FprOuts(wd).FprIns(ws, wt); } +void MipsAssembler::PcntB(VectorRegister wd, VectorRegister ws) { + CHECK(HasMsa()); + DsFsmInstr(EmitMsa2R(0xc1, 0x0, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws); +} + +void MipsAssembler::PcntH(VectorRegister wd, VectorRegister ws) { + CHECK(HasMsa()); + DsFsmInstr(EmitMsa2R(0xc1, 0x1, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws); +} + +void MipsAssembler::PcntW(VectorRegister wd, VectorRegister ws) { + CHECK(HasMsa()); + DsFsmInstr(EmitMsa2R(0xc1, 0x2, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws); +} + +void MipsAssembler::PcntD(VectorRegister wd, VectorRegister ws) { + CHECK(HasMsa()); + DsFsmInstr(EmitMsa2R(0xc1, 0x3, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws); +} + void MipsAssembler::ReplicateFPToVectorRegister(VectorRegister dst, FRegister src, bool is_double) { diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h index 7de8e2e366..c6ce62b4f4 100644 --- a/compiler/utils/mips/assembler_mips.h +++ b/compiler/utils/mips/assembler_mips.h @@ -756,6 +756,11 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler Date: Wed, 7 Mar 2018 10:44:55 +0000 Subject: [PATCH 027/749] Reduce number of tests run in art-gcstress-gcverify. In particular, remove the --interpreter tests, because it takes too long to run them all on the bot. Bug: 74196452 Bug: 74225325 Test: Start ./test/testrunner/run_build_test_target.py -j30 art-gcstress-gcverify Change-Id: I7d61b79a3b8c0057419e5382eebc745d2551648f --- test/testrunner/target_config.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index 9d0377510a..2c433af512 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -69,7 +69,13 @@ target_config = { } }, 'art-gcstress-gcverify': { - 'run-test': ['--gcstress', + # Don't include --interpreter, because it takes too long to run all + # the tests on the build bot (b/74225325) + 'run-test': ['--interp-ac', + '--jit', + '--optimizing', + '--speed-profile', + '--gcstress', '--gcverify'], 'env' : { 'ART_USE_READ_BARRIER' : 'false', -- GitLab From 74ed8d3b0b22bf7c2c5dbe0842f349c528d1cece Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Wed, 7 Mar 2018 11:19:54 +0000 Subject: [PATCH 028/749] ART: JitCodeCache remove only_for_tlb_shootdown ScopedCodeCacheWrite should only be necessary for modifications that affect the instruction cache. Test: test.py --host -j32 Change-Id: I1bc69a8ed80d1fb9bf15754da2248da1f9f4f755 --- runtime/jit/jit_code_cache.cc | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 51a63ddf8a..0941b0beb3 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -328,33 +328,20 @@ const void* JitCodeCache::GetJniStubCode(ArtMethod* method) { class ScopedCodeCacheWrite : ScopedTrace { public: - explicit ScopedCodeCacheWrite(MemMap* code_map, bool only_for_tlb_shootdown = false) + explicit ScopedCodeCacheWrite(MemMap* code_map) : ScopedTrace("ScopedCodeCacheWrite"), - code_map_(code_map), - only_for_tlb_shootdown_(only_for_tlb_shootdown) { + code_map_(code_map) { ScopedTrace trace("mprotect all"); - CheckedCall(mprotect, - "make code writable", - code_map_->Begin(), - only_for_tlb_shootdown_ ? kPageSize : code_map_->Size(), - kProtAll); + CheckedCall(mprotect, "make code writable", code_map_->Begin(), code_map_->Size(), kProtAll); } ~ScopedCodeCacheWrite() { ScopedTrace trace("mprotect code"); - CheckedCall(mprotect, - "make code protected", - code_map_->Begin(), - only_for_tlb_shootdown_ ? kPageSize : code_map_->Size(), - kProtCode); + CheckedCall(mprotect, "make code protected", code_map_->Begin(), code_map_->Size(), kProtCode); } private: MemMap* const code_map_; - // If we're using ScopedCacheWrite only for TLB shootdown, we limit the scope of mprotect to - // one page. - const bool only_for_tlb_shootdown_; - DISALLOW_COPY_AND_ASSIGN(ScopedCodeCacheWrite); }; @@ -812,8 +799,6 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, FillRootTable(roots_data, roots); { // Flush data cache, as compiled code references literals in it. - // We also need a TLB shootdown to act as memory barrier across cores. - ScopedCodeCacheWrite ccw(code_map_.get(), /* only_for_tlb_shootdown */ true); FlushDataCache(reinterpret_cast(roots_data), reinterpret_cast(roots_data + data_size)); } -- GitLab From 2da72ed76edca8818191fb59386cdefde5049d30 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 7 Mar 2018 12:58:11 +0000 Subject: [PATCH 029/749] ART: Do not load wrong libart(d)/libdexfile(d).so for tests. Test: run-test --gdb 570-checker-osr (with and without -O), set breakpoint in dlopen and observe that only the correct libraries are loaded. Change-Id: I4922b6bc5f6d99ed59a810fcedc3991c4509d2a2 --- test/etc/run-test-jar | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 6444eb9a89..a60c8580e3 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -868,7 +868,12 @@ if [ "$HOST" = "n" ]; then fi # System libraries needed by libarttestd.so - PUBLIC_LIBS=libart.so:libartd.so:libc++.so:libbacktrace.so:libdexfile.so:libdexfiled.so:libbase.so:libnativehelper.so + PUBLIC_LIBS=libc++.so:libbacktrace.so:libbase.so:libnativehelper.so + if [ "$TEST_IS_NDEBUG" = "y" ]; then + PUBLIC_LIBS=$PUBLIC_LIBS:libart.so:libdexfile.so + else + PUBLIC_LIBS=$PUBLIC_LIBS:libartd.so:libdexfiled.so + fi # Create a script with the command. The command can get longer than the longest # allowed adb command and there is no way to get the exit status from a adb shell -- GitLab From 247ff3719c42fce05d435bf9dbaa0bd869467245 Mon Sep 17 00:00:00 2001 From: Goran Jakovljevic Date: Wed, 7 Mar 2018 10:34:05 +0100 Subject: [PATCH 030/749] Fix sigchain_test on mips Pass correct size to syscall. Unlike on arm and x86, _KERNEL__NSIG=128 on mips. This fixes the test on mips32 and mips64. Test: mma test-art-target-gtest-sigchain_test in QEMU Change-Id: I3acd93f3595bcbd2a9a0dc3248955226a1051745 --- sigchainlib/sigchain_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sigchainlib/sigchain_test.cc b/sigchainlib/sigchain_test.cc index 9f338ad3a2..1d1e54f127 100644 --- a/sigchainlib/sigchain_test.cc +++ b/sigchainlib/sigchain_test.cc @@ -50,7 +50,7 @@ static int sigismember64(sigset64_t* set, int member) { static int RealSigprocmask(int how, const sigset64_t* new_sigset, sigset64_t* old_sigset) { // glibc's sigset_t is overly large, so sizeof(*new_sigset) doesn't work. - return syscall(__NR_rt_sigprocmask, how, new_sigset, old_sigset, 8); + return syscall(__NR_rt_sigprocmask, how, new_sigset, old_sigset, NSIG/8); } class SigchainTest : public ::testing::Test { -- GitLab From 1f8d51bc03cbc607ae32fadf3a90f385adeffb95 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Thu, 15 Feb 2018 10:42:37 -0800 Subject: [PATCH 031/749] Introduce MIN/MAX/ABS as HIR nodes. Rationale: Having explicit MIN/MAX/ABS operations (in contrast with intrinsics) simplifies recognition and optimization of these common operations (e.g. constant folding, hoisting, detection of saturation arithmetic). Furthermore, mapping conditionals, selectors, intrinsics, etc. (some still TBD) onto these operations generalizes the way they are optimized downstream substantially. Bug: b/65164101 Test: test-art-host,target Change-Id: I69240683339356e5a012802f179298f0b04c6726 --- compiler/optimizing/code_generator_arm64.cc | 114 +++++ compiler/optimizing/code_generator_arm64.h | 3 + .../optimizing/code_generator_arm_vixl.cc | 248 ++++++++++ compiler/optimizing/code_generator_arm_vixl.h | 5 + compiler/optimizing/code_generator_mips.cc | 391 +++++++++++++++ compiler/optimizing/code_generator_mips.h | 2 + compiler/optimizing/code_generator_mips64.cc | 182 +++++++ compiler/optimizing/code_generator_mips64.h | 3 + compiler/optimizing/code_generator_x86.cc | 214 +++++++++ compiler/optimizing/code_generator_x86.h | 2 + compiler/optimizing/code_generator_x86_64.cc | 177 +++++++ compiler/optimizing/code_generator_x86_64.h | 3 + compiler/optimizing/induction_var_range.cc | 33 +- compiler/optimizing/instruction_simplifier.cc | 40 ++ compiler/optimizing/intrinsics.h | 8 + compiler/optimizing/intrinsics_arm64.cc | 115 ----- compiler/optimizing/intrinsics_arm_vixl.cc | 258 ---------- compiler/optimizing/intrinsics_mips.cc | 452 ------------------ compiler/optimizing/intrinsics_mips64.cc | 215 --------- compiler/optimizing/intrinsics_x86.cc | 277 ----------- compiler/optimizing/intrinsics_x86_64.cc | 202 -------- compiler/optimizing/loop_optimization.cc | 176 ++----- compiler/optimizing/nodes.h | 72 +++ compiler/optimizing/pc_relative_fixups_x86.cc | 5 +- compiler/optimizing/scheduler.cc | 2 + test/445-checker-licm/src/Main.java | 10 +- .../562-checker-no-intermediate/src/Main.java | 2 +- .../src/Main.java | 10 +- .../src/Main.java | 6 +- .../src/Main.java | 4 +- .../src/Main.java | 4 +- .../651-checker-int-simd-minmax/src/Main.java | 4 +- .../src/Main.java | 4 +- .../src/Main.java | 10 +- test/661-checker-simd-reduc/src/Main.java | 4 +- 35 files changed, 1556 insertions(+), 1701 deletions(-) diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 1f9c5546e2..b0ddd8e8c6 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -78,6 +78,7 @@ using helpers::OutputFPRegister; using helpers::OutputRegister; using helpers::QRegisterFrom; using helpers::RegisterFrom; +using helpers::SRegisterFrom; using helpers::StackOperandFrom; using helpers::VIXLRegCodeFromART; using helpers::WRegisterFrom; @@ -5462,6 +5463,119 @@ void InstructionCodeGeneratorARM64::VisitRem(HRem* rem) { } } +// TODO: integrate with HandleBinaryOp? +static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) { + LocationSummary* locations = new (allocator) LocationSummary(minmax); + switch (minmax->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); + break; + default: + LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType(); + } +} + +void InstructionCodeGeneratorARM64::GenerateMinMax(LocationSummary* locations, + bool is_min, + DataType::Type type) { + Location op1 = locations->InAt(0); + Location op2 = locations->InAt(1); + Location out = locations->Out(); + + Register op1_reg; + Register op2_reg; + Register out_reg; + if (type == DataType::Type::kInt64) { + op1_reg = XRegisterFrom(op1); + op2_reg = XRegisterFrom(op2); + out_reg = XRegisterFrom(out); + } else { + DCHECK_EQ(type, DataType::Type::kInt32); + op1_reg = WRegisterFrom(op1); + op2_reg = WRegisterFrom(op2); + out_reg = WRegisterFrom(out); + } + + __ Cmp(op1_reg, op2_reg); + __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt); +} + +void InstructionCodeGeneratorARM64::GenerateMinMaxFP(LocationSummary* locations, + bool is_min, + DataType::Type type) { + Location op1 = locations->InAt(0); + Location op2 = locations->InAt(1); + Location out = locations->Out(); + + FPRegister op1_reg; + FPRegister op2_reg; + FPRegister out_reg; + if (type == DataType::Type::kFloat64) { + op1_reg = DRegisterFrom(op1); + op2_reg = DRegisterFrom(op2); + out_reg = DRegisterFrom(out); + } else { + DCHECK_EQ(type, DataType::Type::kFloat32); + op1_reg = SRegisterFrom(op1); + op2_reg = SRegisterFrom(op2); + out_reg = SRegisterFrom(out); + } + + if (is_min) { + __ Fmin(out_reg, op1_reg, op2_reg); + } else { + __ Fmax(out_reg, op1_reg, op2_reg); + } +} + +void LocationsBuilderARM64::VisitMin(HMin* min) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), min); +} + +// TODO: integrate with HandleBinaryOp? +void InstructionCodeGeneratorARM64::VisitMin(HMin* min) { + switch (min->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + GenerateMinMax(min->GetLocations(), /*is_min*/ true, min->GetResultType()); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + GenerateMinMaxFP(min->GetLocations(), /*is_min*/ true, min->GetResultType()); + break; + default: + LOG(FATAL) << "Unexpected type for HMin " << min->GetResultType(); + } +} + +void LocationsBuilderARM64::VisitMax(HMax* max) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), max); +} + +void InstructionCodeGeneratorARM64::VisitMax(HMax* max) { + switch (max->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + GenerateMinMax(max->GetLocations(), /*is_min*/ false, max->GetResultType()); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + GenerateMinMaxFP(max->GetLocations(), /*is_min*/ false, max->GetResultType()); + break; + default: + LOG(FATAL) << "Unexpected type for HMax " << max->GetResultType(); + } +} + void LocationsBuilderARM64::VisitAbs(HAbs* abs) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs); switch (abs->GetResultType()) { diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index e34f799d15..70f5500016 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -273,6 +273,9 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); void HandleCondition(HCondition* instruction); + void GenerateMinMax(LocationSummary* locations, bool is_min, DataType::Type type); + void GenerateMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type); + // Generate a heap reference load using one register `out`: // // out <- *(out + offset) diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 13518adf7d..4fef027e6d 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -4690,6 +4690,254 @@ void InstructionCodeGeneratorARMVIXL::VisitRem(HRem* rem) { } } +static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) { + LocationSummary* locations = new (allocator) LocationSummary(minmax); + switch (minmax->GetResultType()) { + case DataType::Type::kInt32: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + case DataType::Type::kFloat32: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + locations->AddTemp(Location::RequiresRegister()); + break; + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + default: + LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType(); + } +} + +void InstructionCodeGeneratorARMVIXL::GenerateMinMax(LocationSummary* locations, bool is_min) { + Location op1_loc = locations->InAt(0); + Location op2_loc = locations->InAt(1); + Location out_loc = locations->Out(); + + vixl32::Register op1 = RegisterFrom(op1_loc); + vixl32::Register op2 = RegisterFrom(op2_loc); + vixl32::Register out = RegisterFrom(out_loc); + + __ Cmp(op1, op2); + + { + ExactAssemblyScope aas(GetVIXLAssembler(), + 3 * kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); + + __ ite(is_min ? lt : gt); + __ mov(is_min ? lt : gt, out, op1); + __ mov(is_min ? ge : le, out, op2); + } +} + +void InstructionCodeGeneratorARMVIXL::GenerateMinMaxLong(LocationSummary* locations, bool is_min) { + Location op1_loc = locations->InAt(0); + Location op2_loc = locations->InAt(1); + Location out_loc = locations->Out(); + + // Optimization: don't generate any code if inputs are the same. + if (op1_loc.Equals(op2_loc)) { + DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in location builder. + return; + } + + vixl32::Register op1_lo = LowRegisterFrom(op1_loc); + vixl32::Register op1_hi = HighRegisterFrom(op1_loc); + vixl32::Register op2_lo = LowRegisterFrom(op2_loc); + vixl32::Register op2_hi = HighRegisterFrom(op2_loc); + vixl32::Register out_lo = LowRegisterFrom(out_loc); + vixl32::Register out_hi = HighRegisterFrom(out_loc); + UseScratchRegisterScope temps(GetVIXLAssembler()); + const vixl32::Register temp = temps.Acquire(); + + DCHECK(op1_lo.Is(out_lo)); + DCHECK(op1_hi.Is(out_hi)); + + // Compare op1 >= op2, or op1 < op2. + __ Cmp(out_lo, op2_lo); + __ Sbcs(temp, out_hi, op2_hi); + + // Now GE/LT condition code is correct for the long comparison. + { + vixl32::ConditionType cond = is_min ? ge : lt; + ExactAssemblyScope it_scope(GetVIXLAssembler(), + 3 * kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); + __ itt(cond); + __ mov(cond, out_lo, op2_lo); + __ mov(cond, out_hi, op2_hi); + } +} + +void InstructionCodeGeneratorARMVIXL::GenerateMinMaxFloat(HInstruction* min_max, bool is_min) { + LocationSummary* locations = min_max->GetLocations(); + Location op1_loc = locations->InAt(0); + Location op2_loc = locations->InAt(1); + Location out_loc = locations->Out(); + + // Optimization: don't generate any code if inputs are the same. + if (op1_loc.Equals(op2_loc)) { + DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in location builder. + return; + } + + vixl32::SRegister op1 = SRegisterFrom(op1_loc); + vixl32::SRegister op2 = SRegisterFrom(op2_loc); + vixl32::SRegister out = SRegisterFrom(out_loc); + + UseScratchRegisterScope temps(GetVIXLAssembler()); + const vixl32::Register temp1 = temps.Acquire(); + vixl32::Register temp2 = RegisterFrom(locations->GetTemp(0)); + vixl32::Label nan, done; + vixl32::Label* final_label = codegen_->GetFinalLabel(min_max, &done); + + DCHECK(op1.Is(out)); + + __ Vcmp(op1, op2); + __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR); + __ B(vs, &nan, /* far_target */ false); // if un-ordered, go to NaN handling. + + // op1 <> op2 + vixl32::ConditionType cond = is_min ? gt : lt; + { + ExactAssemblyScope it_scope(GetVIXLAssembler(), + 2 * kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); + __ it(cond); + __ vmov(cond, F32, out, op2); + } + // for <>(not equal), we've done min/max calculation. + __ B(ne, final_label, /* far_target */ false); + + // handle op1 == op2, max(+0.0,-0.0), min(+0.0,-0.0). + __ Vmov(temp1, op1); + __ Vmov(temp2, op2); + if (is_min) { + __ Orr(temp1, temp1, temp2); + } else { + __ And(temp1, temp1, temp2); + } + __ Vmov(out, temp1); + __ B(final_label); + + // handle NaN input. + __ Bind(&nan); + __ Movt(temp1, High16Bits(kNanFloat)); // 0x7FC0xxxx is a NaN. + __ Vmov(out, temp1); + + if (done.IsReferenced()) { + __ Bind(&done); + } +} + +void InstructionCodeGeneratorARMVIXL::GenerateMinMaxDouble(HInstruction* min_max, bool is_min) { + LocationSummary* locations = min_max->GetLocations(); + Location op1_loc = locations->InAt(0); + Location op2_loc = locations->InAt(1); + Location out_loc = locations->Out(); + + // Optimization: don't generate any code if inputs are the same. + if (op1_loc.Equals(op2_loc)) { + DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in. + return; + } + + vixl32::DRegister op1 = DRegisterFrom(op1_loc); + vixl32::DRegister op2 = DRegisterFrom(op2_loc); + vixl32::DRegister out = DRegisterFrom(out_loc); + vixl32::Label handle_nan_eq, done; + vixl32::Label* final_label = codegen_->GetFinalLabel(min_max, &done); + + DCHECK(op1.Is(out)); + + __ Vcmp(op1, op2); + __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR); + __ B(vs, &handle_nan_eq, /* far_target */ false); // if un-ordered, go to NaN handling. + + // op1 <> op2 + vixl32::ConditionType cond = is_min ? gt : lt; + { + ExactAssemblyScope it_scope(GetVIXLAssembler(), + 2 * kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); + __ it(cond); + __ vmov(cond, F64, out, op2); + } + // for <>(not equal), we've done min/max calculation. + __ B(ne, final_label, /* far_target */ false); + + // handle op1 == op2, max(+0.0,-0.0). + if (!is_min) { + __ Vand(F64, out, op1, op2); + __ B(final_label); + } + + // handle op1 == op2, min(+0.0,-0.0), NaN input. + __ Bind(&handle_nan_eq); + __ Vorr(F64, out, op1, op2); // assemble op1/-0.0/NaN. + + if (done.IsReferenced()) { + __ Bind(&done); + } +} + +void LocationsBuilderARMVIXL::VisitMin(HMin* min) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), min); +} + +void InstructionCodeGeneratorARMVIXL::VisitMin(HMin* min) { + switch (min->GetResultType()) { + case DataType::Type::kInt32: + GenerateMinMax(min->GetLocations(), /*is_min*/ true); + break; + case DataType::Type::kInt64: + GenerateMinMaxLong(min->GetLocations(), /*is_min*/ true); + break; + case DataType::Type::kFloat32: + GenerateMinMaxFloat(min, /*is_min*/ true); + break; + case DataType::Type::kFloat64: + GenerateMinMaxDouble(min, /*is_min*/ true); + break; + default: + LOG(FATAL) << "Unexpected type for HMin " << min->GetResultType(); + } +} + +void LocationsBuilderARMVIXL::VisitMax(HMax* max) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), max); +} + +void InstructionCodeGeneratorARMVIXL::VisitMax(HMax* max) { + switch (max->GetResultType()) { + case DataType::Type::kInt32: + GenerateMinMax(max->GetLocations(), /*is_min*/ false); + break; + case DataType::Type::kInt64: + GenerateMinMaxLong(max->GetLocations(), /*is_min*/ false); + break; + case DataType::Type::kFloat32: + GenerateMinMaxFloat(max, /*is_min*/ false); + break; + case DataType::Type::kFloat64: + GenerateMinMaxDouble(max, /*is_min*/ false); + break; + default: + LOG(FATAL) << "Unexpected type for HMax " << max->GetResultType(); + } +} + void LocationsBuilderARMVIXL::VisitAbs(HAbs* abs) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs); switch (abs->GetResultType()) { diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index bbc715c59d..726a2f9030 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -349,6 +349,11 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { bool value_can_be_null); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); + void GenerateMinMax(LocationSummary* locations, bool is_min); + void GenerateMinMaxLong(LocationSummary* locations, bool is_min); + void GenerateMinMaxFloat(HInstruction* min_max, bool is_min); + void GenerateMinMaxDouble(HInstruction* min_max, bool is_min); + // Generate a heap reference load using one register `out`: // // out <- *(out + offset) diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index eb5f72e953..ae42bbcc70 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -8779,6 +8779,397 @@ void InstructionCodeGeneratorMIPS::VisitRem(HRem* instruction) { } } +static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) { + LocationSummary* locations = new (allocator) LocationSummary(minmax); + switch (minmax->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kOutputOverlap); + break; + default: + LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType(); + } +} + +void InstructionCodeGeneratorMIPS::GenerateMinMax(LocationSummary* locations, + bool is_min, + bool isR6, + DataType::Type type) { + if (isR6) { + // Some architectures, such as ARM and MIPS (prior to r6), have a + // conditional move instruction which only changes the target + // (output) register if the condition is true (MIPS prior to r6 had + // MOVF, MOVT, MOVN, and MOVZ). The SELEQZ and SELNEZ instructions + // always change the target (output) register. If the condition is + // true the output register gets the contents of the "rs" register; + // otherwise, the output register is set to zero. One consequence + // of this is that to implement something like "rd = c==0 ? rs : rt" + // MIPS64r6 needs to use a pair of SELEQZ/SELNEZ instructions. + // After executing this pair of instructions one of the output + // registers from the pair will necessarily contain zero. Then the + // code ORs the output registers from the SELEQZ/SELNEZ instructions + // to get the final result. + // + // The initial test to see if the output register is same as the + // first input register is needed to make sure that value in the + // first input register isn't clobbered before we've finished + // computing the output value. The logic in the corresponding else + // clause performs the same task but makes sure the second input + // register isn't clobbered in the event that it's the same register + // as the output register; the else clause also handles the case + // where the output register is distinct from both the first, and the + // second input registers. + if (type == DataType::Type::kInt64) { + Register a_lo = locations->InAt(0).AsRegisterPairLow(); + Register a_hi = locations->InAt(0).AsRegisterPairHigh(); + Register b_lo = locations->InAt(1).AsRegisterPairLow(); + Register b_hi = locations->InAt(1).AsRegisterPairHigh(); + Register out_lo = locations->Out().AsRegisterPairLow(); + Register out_hi = locations->Out().AsRegisterPairHigh(); + + MipsLabel compare_done; + + if (a_lo == b_lo) { + if (out_lo != a_lo) { + __ Move(out_lo, a_lo); + __ Move(out_hi, a_hi); + } + } else { + __ Slt(TMP, b_hi, a_hi); + __ Bne(b_hi, a_hi, &compare_done); + + __ Sltu(TMP, b_lo, a_lo); + + __ Bind(&compare_done); + + if (is_min) { + __ Seleqz(AT, a_lo, TMP); + __ Selnez(out_lo, b_lo, TMP); // Safe even if out_lo == a_lo/b_lo + // because at this point we're + // done using a_lo/b_lo. + } else { + __ Selnez(AT, a_lo, TMP); + __ Seleqz(out_lo, b_lo, TMP); // ditto + } + __ Or(out_lo, out_lo, AT); + if (is_min) { + __ Seleqz(AT, a_hi, TMP); + __ Selnez(out_hi, b_hi, TMP); // ditto but for out_hi & a_hi/b_hi + } else { + __ Selnez(AT, a_hi, TMP); + __ Seleqz(out_hi, b_hi, TMP); // ditto but for out_hi & a_hi/b_hi + } + __ Or(out_hi, out_hi, AT); + } + } else { + DCHECK_EQ(type, DataType::Type::kInt32); + Register a = locations->InAt(0).AsRegister(); + Register b = locations->InAt(1).AsRegister(); + Register out = locations->Out().AsRegister(); + + if (a == b) { + if (out != a) { + __ Move(out, a); + } + } else { + __ Slt(AT, b, a); + if (is_min) { + __ Seleqz(TMP, a, AT); + __ Selnez(AT, b, AT); + } else { + __ Selnez(TMP, a, AT); + __ Seleqz(AT, b, AT); + } + __ Or(out, TMP, AT); + } + } + } else { // !isR6 + if (type == DataType::Type::kInt64) { + Register a_lo = locations->InAt(0).AsRegisterPairLow(); + Register a_hi = locations->InAt(0).AsRegisterPairHigh(); + Register b_lo = locations->InAt(1).AsRegisterPairLow(); + Register b_hi = locations->InAt(1).AsRegisterPairHigh(); + Register out_lo = locations->Out().AsRegisterPairLow(); + Register out_hi = locations->Out().AsRegisterPairHigh(); + + MipsLabel compare_done; + + if (a_lo == b_lo) { + if (out_lo != a_lo) { + __ Move(out_lo, a_lo); + __ Move(out_hi, a_hi); + } + } else { + __ Slt(TMP, a_hi, b_hi); + __ Bne(a_hi, b_hi, &compare_done); + + __ Sltu(TMP, a_lo, b_lo); + + __ Bind(&compare_done); + + if (is_min) { + if (out_lo != a_lo) { + __ Movn(out_hi, a_hi, TMP); + __ Movn(out_lo, a_lo, TMP); + } + if (out_lo != b_lo) { + __ Movz(out_hi, b_hi, TMP); + __ Movz(out_lo, b_lo, TMP); + } + } else { + if (out_lo != a_lo) { + __ Movz(out_hi, a_hi, TMP); + __ Movz(out_lo, a_lo, TMP); + } + if (out_lo != b_lo) { + __ Movn(out_hi, b_hi, TMP); + __ Movn(out_lo, b_lo, TMP); + } + } + } + } else { + DCHECK_EQ(type, DataType::Type::kInt32); + Register a = locations->InAt(0).AsRegister(); + Register b = locations->InAt(1).AsRegister(); + Register out = locations->Out().AsRegister(); + + if (a == b) { + if (out != a) { + __ Move(out, a); + } + } else { + __ Slt(AT, a, b); + if (is_min) { + if (out != a) { + __ Movn(out, a, AT); + } + if (out != b) { + __ Movz(out, b, AT); + } + } else { + if (out != a) { + __ Movz(out, a, AT); + } + if (out != b) { + __ Movn(out, b, AT); + } + } + } + } + } +} + +void InstructionCodeGeneratorMIPS::GenerateMinMaxFP(LocationSummary* locations, + bool is_min, + bool isR6, + DataType::Type type) { + FRegister out = locations->Out().AsFpuRegister(); + FRegister a = locations->InAt(0).AsFpuRegister(); + FRegister b = locations->InAt(1).AsFpuRegister(); + + if (isR6) { + MipsLabel noNaNs; + MipsLabel done; + FRegister ftmp = ((out != a) && (out != b)) ? out : FTMP; + + // When Java computes min/max it prefers a NaN to a number; the + // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of + // the inputs is a NaN and the other is a valid number, the MIPS + // instruction will return the number; Java wants the NaN value + // returned. This is why there is extra logic preceding the use of + // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a + // NaN, return the NaN, otherwise return the min/max. + if (type == DataType::Type::kFloat64) { + __ CmpUnD(FTMP, a, b); + __ Bc1eqz(FTMP, &noNaNs); + + // One of the inputs is a NaN + __ CmpEqD(ftmp, a, a); + // If a == a then b is the NaN, otherwise a is the NaN. + __ SelD(ftmp, a, b); + + if (ftmp != out) { + __ MovD(out, ftmp); + } + + __ B(&done); + + __ Bind(&noNaNs); + + if (is_min) { + __ MinD(out, a, b); + } else { + __ MaxD(out, a, b); + } + } else { + DCHECK_EQ(type, DataType::Type::kFloat32); + __ CmpUnS(FTMP, a, b); + __ Bc1eqz(FTMP, &noNaNs); + + // One of the inputs is a NaN + __ CmpEqS(ftmp, a, a); + // If a == a then b is the NaN, otherwise a is the NaN. + __ SelS(ftmp, a, b); + + if (ftmp != out) { + __ MovS(out, ftmp); + } + + __ B(&done); + + __ Bind(&noNaNs); + + if (is_min) { + __ MinS(out, a, b); + } else { + __ MaxS(out, a, b); + } + } + + __ Bind(&done); + + } else { // !isR6 + MipsLabel ordered; + MipsLabel compare; + MipsLabel select; + MipsLabel done; + + if (type == DataType::Type::kFloat64) { + __ CunD(a, b); + } else { + DCHECK_EQ(type, DataType::Type::kFloat32); + __ CunS(a, b); + } + __ Bc1f(&ordered); + + // a or b (or both) is a NaN. Return one, which is a NaN. + if (type == DataType::Type::kFloat64) { + __ CeqD(b, b); + } else { + __ CeqS(b, b); + } + __ B(&select); + + __ Bind(&ordered); + + // Neither is a NaN. + // a == b? (-0.0 compares equal with +0.0) + // If equal, handle zeroes, else compare further. + if (type == DataType::Type::kFloat64) { + __ CeqD(a, b); + } else { + __ CeqS(a, b); + } + __ Bc1f(&compare); + + // a == b either bit for bit or one is -0.0 and the other is +0.0. + if (type == DataType::Type::kFloat64) { + __ MoveFromFpuHigh(TMP, a); + __ MoveFromFpuHigh(AT, b); + } else { + __ Mfc1(TMP, a); + __ Mfc1(AT, b); + } + + if (is_min) { + // -0.0 prevails over +0.0. + __ Or(TMP, TMP, AT); + } else { + // +0.0 prevails over -0.0. + __ And(TMP, TMP, AT); + } + + if (type == DataType::Type::kFloat64) { + __ Mfc1(AT, a); + __ Mtc1(AT, out); + __ MoveToFpuHigh(TMP, out); + } else { + __ Mtc1(TMP, out); + } + __ B(&done); + + __ Bind(&compare); + + if (type == DataType::Type::kFloat64) { + if (is_min) { + // return (a <= b) ? a : b; + __ ColeD(a, b); + } else { + // return (a >= b) ? a : b; + __ ColeD(b, a); // b <= a + } + } else { + if (is_min) { + // return (a <= b) ? a : b; + __ ColeS(a, b); + } else { + // return (a >= b) ? a : b; + __ ColeS(b, a); // b <= a + } + } + + __ Bind(&select); + + if (type == DataType::Type::kFloat64) { + __ MovtD(out, a); + __ MovfD(out, b); + } else { + __ MovtS(out, a); + __ MovfS(out, b); + } + + __ Bind(&done); + } +} + +void LocationsBuilderMIPS::VisitMin(HMin* min) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), min); +} + +void InstructionCodeGeneratorMIPS::VisitMin(HMin* min) { + bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); + switch (min->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + GenerateMinMax(min->GetLocations(), /*is_min*/ true, isR6, min->GetResultType()); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + GenerateMinMaxFP(min->GetLocations(), /*is_min*/ true, isR6, min->GetResultType()); + break; + default: + LOG(FATAL) << "Unexpected type for HMin " << min->GetResultType(); + } +} + +void LocationsBuilderMIPS::VisitMax(HMax* max) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), max); +} + +void InstructionCodeGeneratorMIPS::VisitMax(HMax* max) { + bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); + switch (max->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + GenerateMinMax(max->GetLocations(), /*is_min*/ false, isR6, max->GetResultType()); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + GenerateMinMaxFP(max->GetLocations(), /*is_min*/ false, isR6, max->GetResultType()); + break; + default: + LOG(FATAL) << "Unexpected type for HMax " << max->GetResultType(); + } +} + void LocationsBuilderMIPS::VisitAbs(HAbs* abs) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs); switch (abs->GetResultType()) { diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index d09ab7cce0..ae5fe5be19 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -246,6 +246,8 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator { bool value_can_be_null); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info, uint32_t dex_pc); + void GenerateMinMax(LocationSummary* locations, bool is_min, bool isR6, DataType::Type type); + void GenerateMinMaxFP(LocationSummary* locations, bool is_min, bool isR6, DataType::Type type); void GenerateAbsFP(LocationSummary* locations, DataType::Type type, bool isR2OrNewer, bool isR6); // Generate a heap reference load using one register `out`: diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 9593eec455..8031cca7cb 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -6665,6 +6665,188 @@ void InstructionCodeGeneratorMIPS64::VisitRem(HRem* instruction) { } } +static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) { + LocationSummary* locations = new (allocator) LocationSummary(minmax); + switch (minmax->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); + break; + default: + LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType(); + } +} + +void InstructionCodeGeneratorMIPS64::GenerateMinMax(LocationSummary* locations, bool is_min) { + GpuRegister lhs = locations->InAt(0).AsRegister(); + GpuRegister rhs = locations->InAt(1).AsRegister(); + GpuRegister out = locations->Out().AsRegister(); + + if (lhs == rhs) { + if (out != lhs) { + __ Move(out, lhs); + } + } else { + // Some architectures, such as ARM and MIPS (prior to r6), have a + // conditional move instruction which only changes the target + // (output) register if the condition is true (MIPS prior to r6 had + // MOVF, MOVT, and MOVZ). The SELEQZ and SELNEZ instructions always + // change the target (output) register. If the condition is true the + // output register gets the contents of the "rs" register; otherwise, + // the output register is set to zero. One consequence of this is + // that to implement something like "rd = c==0 ? rs : rt" MIPS64r6 + // needs to use a pair of SELEQZ/SELNEZ instructions. After + // executing this pair of instructions one of the output registers + // from the pair will necessarily contain zero. Then the code ORs the + // output registers from the SELEQZ/SELNEZ instructions to get the + // final result. + // + // The initial test to see if the output register is same as the + // first input register is needed to make sure that value in the + // first input register isn't clobbered before we've finished + // computing the output value. The logic in the corresponding else + // clause performs the same task but makes sure the second input + // register isn't clobbered in the event that it's the same register + // as the output register; the else clause also handles the case + // where the output register is distinct from both the first, and the + // second input registers. + if (out == lhs) { + __ Slt(AT, rhs, lhs); + if (is_min) { + __ Seleqz(out, lhs, AT); + __ Selnez(AT, rhs, AT); + } else { + __ Selnez(out, lhs, AT); + __ Seleqz(AT, rhs, AT); + } + } else { + __ Slt(AT, lhs, rhs); + if (is_min) { + __ Seleqz(out, rhs, AT); + __ Selnez(AT, lhs, AT); + } else { + __ Selnez(out, rhs, AT); + __ Seleqz(AT, lhs, AT); + } + } + __ Or(out, out, AT); + } +} + +void InstructionCodeGeneratorMIPS64::GenerateMinMaxFP(LocationSummary* locations, + bool is_min, + DataType::Type type) { + FpuRegister a = locations->InAt(0).AsFpuRegister(); + FpuRegister b = locations->InAt(1).AsFpuRegister(); + FpuRegister out = locations->Out().AsFpuRegister(); + + Mips64Label noNaNs; + Mips64Label done; + FpuRegister ftmp = ((out != a) && (out != b)) ? out : FTMP; + + // When Java computes min/max it prefers a NaN to a number; the + // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of + // the inputs is a NaN and the other is a valid number, the MIPS + // instruction will return the number; Java wants the NaN value + // returned. This is why there is extra logic preceding the use of + // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a + // NaN, return the NaN, otherwise return the min/max. + if (type == DataType::Type::kFloat64) { + __ CmpUnD(FTMP, a, b); + __ Bc1eqz(FTMP, &noNaNs); + + // One of the inputs is a NaN + __ CmpEqD(ftmp, a, a); + // If a == a then b is the NaN, otherwise a is the NaN. + __ SelD(ftmp, a, b); + + if (ftmp != out) { + __ MovD(out, ftmp); + } + + __ Bc(&done); + + __ Bind(&noNaNs); + + if (is_min) { + __ MinD(out, a, b); + } else { + __ MaxD(out, a, b); + } + } else { + DCHECK_EQ(type, DataType::Type::kFloat32); + __ CmpUnS(FTMP, a, b); + __ Bc1eqz(FTMP, &noNaNs); + + // One of the inputs is a NaN + __ CmpEqS(ftmp, a, a); + // If a == a then b is the NaN, otherwise a is the NaN. + __ SelS(ftmp, a, b); + + if (ftmp != out) { + __ MovS(out, ftmp); + } + + __ Bc(&done); + + __ Bind(&noNaNs); + + if (is_min) { + __ MinS(out, a, b); + } else { + __ MaxS(out, a, b); + } + } + + __ Bind(&done); +} + +void LocationsBuilderMIPS64::VisitMin(HMin* min) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), min); +} + +void InstructionCodeGeneratorMIPS64::VisitMin(HMin* min) { + switch (min->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + GenerateMinMax(min->GetLocations(), /*is_min*/ true); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + GenerateMinMaxFP(min->GetLocations(), /*is_min*/ true, min->GetResultType()); + break; + default: + LOG(FATAL) << "Unexpected type for HMin " << min->GetResultType(); + } +} + +void LocationsBuilderMIPS64::VisitMax(HMax* max) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), max); +} + +void InstructionCodeGeneratorMIPS64::VisitMax(HMax* max) { + switch (max->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + GenerateMinMax(max->GetLocations(), /*is_min*/ false); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + GenerateMinMaxFP(max->GetLocations(), /*is_min*/ false, max->GetResultType()); + break; + default: + LOG(FATAL) << "Unexpected type for HMax " << max->GetResultType(); + } +} + void LocationsBuilderMIPS64::VisitAbs(HAbs* abs) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs); switch (abs->GetResultType()) { diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index ddeb3eb90c..5d925d5d5a 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -242,6 +242,9 @@ class InstructionCodeGeneratorMIPS64 : public InstructionCodeGenerator { bool value_can_be_null); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); + void GenerateMinMax(LocationSummary* locations, bool is_min); + void GenerateMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type); + // Generate a heap reference load using one register `out`: // // out <- *(out + offset) diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 51b96be0f8..536909aa1f 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -51,6 +51,9 @@ static constexpr int kC2ConditionMask = 0x400; static constexpr int kFakeReturnRegister = Register(8); +static constexpr int64_t kDoubleNaN = INT64_C(0x7FF8000000000000); +static constexpr int32_t kFloatNaN = INT32_C(0x7FC00000); + // NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy. #define __ down_cast(codegen->GetAssembler())-> // NOLINT #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kX86PointerSize, x).Int32Value() @@ -3802,6 +3805,217 @@ void InstructionCodeGeneratorX86::VisitRem(HRem* rem) { } } +static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) { + LocationSummary* locations = new (allocator) LocationSummary(minmax); + switch (minmax->GetResultType()) { + case DataType::Type::kInt32: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + // Register to use to perform a long subtract to set cc. + locations->AddTemp(Location::RequiresRegister()); + break; + case DataType::Type::kFloat32: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + locations->AddTemp(Location::RequiresRegister()); + break; + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + default: + LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType(); + } +} + +void InstructionCodeGeneratorX86::GenerateMinMax(LocationSummary* locations, + bool is_min, + DataType::Type type) { + Location op1_loc = locations->InAt(0); + Location op2_loc = locations->InAt(1); + + // Shortcut for same input locations. + if (op1_loc.Equals(op2_loc)) { + // Can return immediately, as op1_loc == out_loc. + // Note: if we ever support separate registers, e.g., output into memory, we need to check for + // a copy here. + DCHECK(locations->Out().Equals(op1_loc)); + return; + } + + if (type == DataType::Type::kInt64) { + // Need to perform a subtract to get the sign right. + // op1 is already in the same location as the output. + Location output = locations->Out(); + Register output_lo = output.AsRegisterPairLow(); + Register output_hi = output.AsRegisterPairHigh(); + + Register op2_lo = op2_loc.AsRegisterPairLow(); + Register op2_hi = op2_loc.AsRegisterPairHigh(); + + // The comparison is performed by subtracting the second operand from + // the first operand and then setting the status flags in the same + // manner as the SUB instruction." + __ cmpl(output_lo, op2_lo); + + // Now use a temp and the borrow to finish the subtraction of op2_hi. + Register temp = locations->GetTemp(0).AsRegister(); + __ movl(temp, output_hi); + __ sbbl(temp, op2_hi); + + // Now the condition code is correct. + Condition cond = is_min ? Condition::kGreaterEqual : Condition::kLess; + __ cmovl(cond, output_lo, op2_lo); + __ cmovl(cond, output_hi, op2_hi); + } else { + DCHECK_EQ(type, DataType::Type::kInt32); + Register out = locations->Out().AsRegister(); + Register op2 = op2_loc.AsRegister(); + + // (out := op1) + // out <=? op2 + // if out is min jmp done + // out := op2 + // done: + + __ cmpl(out, op2); + Condition cond = is_min ? Condition::kGreater : Condition::kLess; + __ cmovl(cond, out, op2); + } +} + +void InstructionCodeGeneratorX86::GenerateMinMaxFP(LocationSummary* locations, + bool is_min, + DataType::Type type) { + Location op1_loc = locations->InAt(0); + Location op2_loc = locations->InAt(1); + Location out_loc = locations->Out(); + XmmRegister out = out_loc.AsFpuRegister(); + + // Shortcut for same input locations. + if (op1_loc.Equals(op2_loc)) { + DCHECK(out_loc.Equals(op1_loc)); + return; + } + + // (out := op1) + // out <=? op2 + // if Nan jmp Nan_label + // if out is min jmp done + // if op2 is min jmp op2_label + // handle -0/+0 + // jmp done + // Nan_label: + // out := NaN + // op2_label: + // out := op2 + // done: + // + // This removes one jmp, but needs to copy one input (op1) to out. + // + // TODO: This is straight from Quick (except literal pool). Make NaN an out-of-line slowpath? + + XmmRegister op2 = op2_loc.AsFpuRegister(); + + NearLabel nan, done, op2_label; + if (type == DataType::Type::kFloat64) { + __ ucomisd(out, op2); + } else { + DCHECK_EQ(type, DataType::Type::kFloat32); + __ ucomiss(out, op2); + } + + __ j(Condition::kParityEven, &nan); + + __ j(is_min ? Condition::kAbove : Condition::kBelow, &op2_label); + __ j(is_min ? Condition::kBelow : Condition::kAbove, &done); + + // Handle 0.0/-0.0. + if (is_min) { + if (type == DataType::Type::kFloat64) { + __ orpd(out, op2); + } else { + __ orps(out, op2); + } + } else { + if (type == DataType::Type::kFloat64) { + __ andpd(out, op2); + } else { + __ andps(out, op2); + } + } + __ jmp(&done); + + // NaN handling. + __ Bind(&nan); + if (type == DataType::Type::kFloat64) { + // TODO: Use a constant from the constant table (requires extra input). + __ LoadLongConstant(out, kDoubleNaN); + } else { + Register constant = locations->GetTemp(0).AsRegister(); + __ movl(constant, Immediate(kFloatNaN)); + __ movd(out, constant); + } + __ jmp(&done); + + // out := op2; + __ Bind(&op2_label); + if (type == DataType::Type::kFloat64) { + __ movsd(out, op2); + } else { + __ movss(out, op2); + } + + // Done. + __ Bind(&done); +} + +void LocationsBuilderX86::VisitMin(HMin* min) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), min); +} + +void InstructionCodeGeneratorX86::VisitMin(HMin* min) { + switch (min->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + GenerateMinMax(min->GetLocations(), /*is_min*/ true, min->GetResultType()); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + GenerateMinMaxFP(min->GetLocations(), /*is_min*/ true, min->GetResultType()); + break; + default: + LOG(FATAL) << "Unexpected type for HMin " << min->GetResultType(); + } +} + +void LocationsBuilderX86::VisitMax(HMax* max) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), max); +} + +void InstructionCodeGeneratorX86::VisitMax(HMax* max) { + switch (max->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + GenerateMinMax(max->GetLocations(), /*is_min*/ false, max->GetResultType()); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + GenerateMinMaxFP(max->GetLocations(), /*is_min*/ false, max->GetResultType()); + break; + default: + LOG(FATAL) << "Unexpected type for HMax " << max->GetResultType(); + } +} + void LocationsBuilderX86::VisitAbs(HAbs* abs) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs); switch (abs->GetResultType()) { diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 51e5bca00b..82496d12e5 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -225,6 +225,8 @@ class InstructionCodeGeneratorX86 : public InstructionCodeGenerator { void GenerateShlLong(const Location& loc, int shift); void GenerateShrLong(const Location& loc, int shift); void GenerateUShrLong(const Location& loc, int shift); + void GenerateMinMax(LocationSummary* locations, bool is_min, DataType::Type type); + void GenerateMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type); void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info, diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 0bb56a2b4a..bb1fbc5290 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -3821,6 +3821,183 @@ void InstructionCodeGeneratorX86_64::VisitRem(HRem* rem) { } } +static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) { + LocationSummary* locations = new (allocator) LocationSummary(minmax); + switch (minmax->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + // The following is sub-optimal, but all we can do for now. It would be fine to also accept + // the second input to be the output (we can simply swap inputs). + locations->SetOut(Location::SameAsFirstInput()); + break; + default: + LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType(); + } +} + +void InstructionCodeGeneratorX86_64::GenerateMinMax(LocationSummary* locations, + bool is_min, + DataType::Type type) { + Location op1_loc = locations->InAt(0); + Location op2_loc = locations->InAt(1); + + // Shortcut for same input locations. + if (op1_loc.Equals(op2_loc)) { + // Can return immediately, as op1_loc == out_loc. + // Note: if we ever support separate registers, e.g., output into memory, we need to check for + // a copy here. + DCHECK(locations->Out().Equals(op1_loc)); + return; + } + + CpuRegister out = locations->Out().AsRegister(); + CpuRegister op2 = op2_loc.AsRegister(); + + // (out := op1) + // out <=? op2 + // if out is min jmp done + // out := op2 + // done: + + if (type == DataType::Type::kInt64) { + __ cmpq(out, op2); + __ cmov(is_min ? Condition::kGreater : Condition::kLess, out, op2, /*is64bit*/ true); + } else { + DCHECK_EQ(type, DataType::Type::kInt32); + __ cmpl(out, op2); + __ cmov(is_min ? Condition::kGreater : Condition::kLess, out, op2, /*is64bit*/ false); + } +} + +void InstructionCodeGeneratorX86_64::GenerateMinMaxFP(LocationSummary* locations, + bool is_min, + DataType::Type type) { + Location op1_loc = locations->InAt(0); + Location op2_loc = locations->InAt(1); + Location out_loc = locations->Out(); + XmmRegister out = out_loc.AsFpuRegister(); + + // Shortcut for same input locations. + if (op1_loc.Equals(op2_loc)) { + DCHECK(out_loc.Equals(op1_loc)); + return; + } + + // (out := op1) + // out <=? op2 + // if Nan jmp Nan_label + // if out is min jmp done + // if op2 is min jmp op2_label + // handle -0/+0 + // jmp done + // Nan_label: + // out := NaN + // op2_label: + // out := op2 + // done: + // + // This removes one jmp, but needs to copy one input (op1) to out. + // + // TODO: This is straight from Quick. Make NaN an out-of-line slowpath? + + XmmRegister op2 = op2_loc.AsFpuRegister(); + + NearLabel nan, done, op2_label; + if (type == DataType::Type::kFloat64) { + __ ucomisd(out, op2); + } else { + DCHECK_EQ(type, DataType::Type::kFloat32); + __ ucomiss(out, op2); + } + + __ j(Condition::kParityEven, &nan); + + __ j(is_min ? Condition::kAbove : Condition::kBelow, &op2_label); + __ j(is_min ? Condition::kBelow : Condition::kAbove, &done); + + // Handle 0.0/-0.0. + if (is_min) { + if (type == DataType::Type::kFloat64) { + __ orpd(out, op2); + } else { + __ orps(out, op2); + } + } else { + if (type == DataType::Type::kFloat64) { + __ andpd(out, op2); + } else { + __ andps(out, op2); + } + } + __ jmp(&done); + + // NaN handling. + __ Bind(&nan); + if (type == DataType::Type::kFloat64) { + __ movsd(out, codegen_->LiteralInt64Address(INT64_C(0x7FF8000000000000))); + } else { + __ movss(out, codegen_->LiteralInt32Address(INT32_C(0x7FC00000))); + } + __ jmp(&done); + + // out := op2; + __ Bind(&op2_label); + if (type == DataType::Type::kFloat64) { + __ movsd(out, op2); + } else { + __ movss(out, op2); + } + + // Done. + __ Bind(&done); +} + +void LocationsBuilderX86_64::VisitMin(HMin* min) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), min); +} + +void InstructionCodeGeneratorX86_64::VisitMin(HMin* min) { + switch (min->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + GenerateMinMax(min->GetLocations(), /*is_min*/ true, min->GetResultType()); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + GenerateMinMaxFP(min->GetLocations(), /*is_min*/ true, min->GetResultType()); + break; + default: + LOG(FATAL) << "Unexpected type for HMin " << min->GetResultType(); + } +} + +void LocationsBuilderX86_64::VisitMax(HMax* max) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), max); +} + +void InstructionCodeGeneratorX86_64::VisitMax(HMax* max) { + switch (max->GetResultType()) { + case DataType::Type::kInt32: + case DataType::Type::kInt64: + GenerateMinMax(max->GetLocations(), /*is_min*/ false, max->GetResultType()); + break; + case DataType::Type::kFloat32: + case DataType::Type::kFloat64: + GenerateMinMaxFP(max->GetLocations(), /*is_min*/ false, max->GetResultType()); + break; + default: + LOG(FATAL) << "Unexpected type for HMax " << max->GetResultType(); + } +} + void LocationsBuilderX86_64::VisitAbs(HAbs* abs) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs); switch (abs->GetResultType()) { diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 1079e94dfc..933afdab26 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -222,6 +222,9 @@ class InstructionCodeGeneratorX86_64 : public InstructionCodeGenerator { bool value_can_be_null); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); + void GenerateMinMax(LocationSummary* locations, bool is_min, DataType::Type type); + void GenerateMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type); + // Generate a heap reference load using one register `out`: // // out <- *(out + offset) diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index d699d01ecb..0a310ca940 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -78,16 +78,10 @@ static bool IsGEZero(HInstruction* instruction) { DCHECK(instruction != nullptr); if (instruction->IsArrayLength()) { return true; - } else if (instruction->IsInvokeStaticOrDirect()) { - switch (instruction->AsInvoke()->GetIntrinsic()) { - case Intrinsics::kMathMinIntInt: - case Intrinsics::kMathMinLongLong: - // Instruction MIN(>=0, >=0) is >= 0. - return IsGEZero(instruction->InputAt(0)) && - IsGEZero(instruction->InputAt(1)); - default: - break; - } + } else if (instruction->IsMin()) { + // Instruction MIN(>=0, >=0) is >= 0. + return IsGEZero(instruction->InputAt(0)) && + IsGEZero(instruction->InputAt(1)); } else if (instruction->IsAbs()) { // Instruction ABS(>=0) is >= 0. // NOTE: ABS(minint) = minint prevents assuming @@ -101,21 +95,14 @@ static bool IsGEZero(HInstruction* instruction) { /** Hunts "under the hood" for a suitable instruction at the hint. */ static bool IsMaxAtHint( HInstruction* instruction, HInstruction* hint, /*out*/HInstruction** suitable) { - if (instruction->IsInvokeStaticOrDirect()) { - switch (instruction->AsInvoke()->GetIntrinsic()) { - case Intrinsics::kMathMinIntInt: - case Intrinsics::kMathMinLongLong: - // For MIN(x, y), return most suitable x or y as maximum. - return IsMaxAtHint(instruction->InputAt(0), hint, suitable) || - IsMaxAtHint(instruction->InputAt(1), hint, suitable); - default: - break; - } + if (instruction->IsMin()) { + // For MIN(x, y), return most suitable x or y as maximum. + return IsMaxAtHint(instruction->InputAt(0), hint, suitable) || + IsMaxAtHint(instruction->InputAt(1), hint, suitable); } else { *suitable = instruction; return HuntForDeclaration(instruction) == hint; } - return false; } /** Post-analysis simplification of a minimum value that makes the bound more useful to clients. */ @@ -364,11 +351,11 @@ void InductionVarRange::Replace(HInstruction* instruction, } } -bool InductionVarRange::IsFinite(HLoopInformation* loop, /*out*/ int64_t* tc) const { +bool InductionVarRange::IsFinite(HLoopInformation* loop, /*out*/ int64_t* trip_count) const { HInductionVarAnalysis::InductionInfo *trip = induction_analysis_->LookupInfo(loop, GetLoopControl(loop)); if (trip != nullptr && !IsUnsafeTripCount(trip)) { - IsConstant(trip->op_a, kExact, tc); + IsConstant(trip->op_a, kExact, trip_count); return true; } return false; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index eac8d2f593..34837700a2 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -120,6 +120,8 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void SimplifyReturnThis(HInvoke* invoke); void SimplifyAllocationIntrinsic(HInvoke* invoke); void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind); + void SimplifyMin(HInvoke* invoke, DataType::Type type); + void SimplifyMax(HInvoke* invoke, DataType::Type type); void SimplifyAbs(HInvoke* invoke, DataType::Type type); CodeGenerator* codegen_; @@ -2407,6 +2409,20 @@ void InstructionSimplifierVisitor::SimplifyMemBarrier(HInvoke* invoke, invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, mem_barrier); } +void InstructionSimplifierVisitor::SimplifyMin(HInvoke* invoke, DataType::Type type) { + DCHECK(invoke->IsInvokeStaticOrDirect()); + HMin* min = new (GetGraph()->GetAllocator()) + HMin(type, invoke->InputAt(0), invoke->InputAt(1), invoke->GetDexPc()); + invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, min); +} + +void InstructionSimplifierVisitor::SimplifyMax(HInvoke* invoke, DataType::Type type) { + DCHECK(invoke->IsInvokeStaticOrDirect()); + HMax* max = new (GetGraph()->GetAllocator()) + HMax(type, invoke->InputAt(0), invoke->InputAt(1), invoke->GetDexPc()); + invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, max); +} + void InstructionSimplifierVisitor::SimplifyAbs(HInvoke* invoke, DataType::Type type) { DCHECK(invoke->IsInvokeStaticOrDirect()); HAbs* abs = new (GetGraph()->GetAllocator()) @@ -2497,6 +2513,30 @@ void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { case Intrinsics::kVarHandleStoreStoreFence: SimplifyMemBarrier(instruction, MemBarrierKind::kStoreStore); break; + case Intrinsics::kMathMinIntInt: + SimplifyMin(instruction, DataType::Type::kInt32); + break; + case Intrinsics::kMathMinLongLong: + SimplifyMin(instruction, DataType::Type::kInt64); + break; + case Intrinsics::kMathMinFloatFloat: + SimplifyMin(instruction, DataType::Type::kFloat32); + break; + case Intrinsics::kMathMinDoubleDouble: + SimplifyMin(instruction, DataType::Type::kFloat64); + break; + case Intrinsics::kMathMaxIntInt: + SimplifyMax(instruction, DataType::Type::kInt32); + break; + case Intrinsics::kMathMaxLongLong: + SimplifyMax(instruction, DataType::Type::kInt64); + break; + case Intrinsics::kMathMaxFloatFloat: + SimplifyMax(instruction, DataType::Type::kFloat32); + break; + case Intrinsics::kMathMaxDoubleDouble: + SimplifyMax(instruction, DataType::Type::kFloat64); + break; case Intrinsics::kMathAbsInt: SimplifyAbs(instruction, DataType::Type::kInt32); break; diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index 24aff226ca..1035cbc2c4 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -266,6 +266,14 @@ void IntrinsicCodeGenerator ## Arch::Visit ## Name(HInvoke* invoke) { \ << " should have been converted to HIR"; \ } #define UNREACHABLE_INTRINSICS(Arch) \ +UNREACHABLE_INTRINSIC(Arch, MathMinIntInt) \ +UNREACHABLE_INTRINSIC(Arch, MathMinLongLong) \ +UNREACHABLE_INTRINSIC(Arch, MathMinFloatFloat) \ +UNREACHABLE_INTRINSIC(Arch, MathMinDoubleDouble) \ +UNREACHABLE_INTRINSIC(Arch, MathMaxIntInt) \ +UNREACHABLE_INTRINSIC(Arch, MathMaxLongLong) \ +UNREACHABLE_INTRINSIC(Arch, MathMaxFloatFloat) \ +UNREACHABLE_INTRINSIC(Arch, MathMaxDoubleDouble) \ UNREACHABLE_INTRINSIC(Arch, MathAbsInt) \ UNREACHABLE_INTRINSIC(Arch, MathAbsLong) \ UNREACHABLE_INTRINSIC(Arch, MathAbsFloat) \ diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 0b04fff927..81c0b50932 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -344,14 +344,6 @@ void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) { GenReverseBytes(invoke->GetLocations(), DataType::Type::kInt16, GetVIXLAssembler()); } -static void CreateIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - static void GenNumberOfLeadingZeros(LocationSummary* locations, DataType::Type type, MacroAssembler* masm) { @@ -529,113 +521,6 @@ void IntrinsicCodeGeneratorARM64::VisitLongLowestOneBit(HInvoke* invoke) { GenLowestOneBit(invoke, DataType::Type::kInt64, GetVIXLAssembler()); } -static void GenMinMaxFP(LocationSummary* locations, - bool is_min, - bool is_double, - MacroAssembler* masm) { - Location op1 = locations->InAt(0); - Location op2 = locations->InAt(1); - Location out = locations->Out(); - - FPRegister op1_reg = is_double ? DRegisterFrom(op1) : SRegisterFrom(op1); - FPRegister op2_reg = is_double ? DRegisterFrom(op2) : SRegisterFrom(op2); - FPRegister out_reg = is_double ? DRegisterFrom(out) : SRegisterFrom(out); - if (is_min) { - __ Fmin(out_reg, op1_reg, op2_reg); - } else { - __ Fmax(out_reg, op1_reg, op2_reg); - } -} - -static void CreateFPFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetInAt(1, Location::RequiresFpuRegister()); - locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); -} - -void IntrinsicLocationsBuilderARM64::VisitMathMinDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathMinDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetVIXLAssembler()); -} - -void IntrinsicLocationsBuilderARM64::VisitMathMinFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathMinFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetVIXLAssembler()); -} - -void IntrinsicLocationsBuilderARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetVIXLAssembler()); -} - -void IntrinsicLocationsBuilderARM64::VisitMathMaxFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathMaxFloatFloat(HInvoke* invoke) { - GenMinMaxFP( - invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetVIXLAssembler()); -} - -static void GenMinMax(LocationSummary* locations, - bool is_min, - bool is_long, - MacroAssembler* masm) { - Location op1 = locations->InAt(0); - Location op2 = locations->InAt(1); - Location out = locations->Out(); - - Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1); - Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2); - Register out_reg = is_long ? XRegisterFrom(out) : WRegisterFrom(out); - - __ Cmp(op1_reg, op2_reg); - __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt); -} - -void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathMinIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetVIXLAssembler()); -} - -void IntrinsicLocationsBuilderARM64::VisitMathMinLongLong(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathMinLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetVIXLAssembler()); -} - -void IntrinsicLocationsBuilderARM64::VisitMathMaxIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathMaxIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetVIXLAssembler()); -} - -void IntrinsicLocationsBuilderARM64::VisitMathMaxLongLong(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARM64::VisitMathMaxLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetVIXLAssembler()); -} - static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { LocationSummary* locations = new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index e351fcc706..e61a0b0809 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -432,264 +432,6 @@ void IntrinsicCodeGeneratorARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* invo GenNumberOfTrailingZeros(invoke, DataType::Type::kInt64, codegen_); } -static void GenMinMaxFloat(HInvoke* invoke, bool is_min, CodeGeneratorARMVIXL* codegen) { - ArmVIXLAssembler* assembler = codegen->GetAssembler(); - Location op1_loc = invoke->GetLocations()->InAt(0); - Location op2_loc = invoke->GetLocations()->InAt(1); - Location out_loc = invoke->GetLocations()->Out(); - - // Optimization: don't generate any code if inputs are the same. - if (op1_loc.Equals(op2_loc)) { - DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in location builder. - return; - } - - vixl32::SRegister op1 = SRegisterFrom(op1_loc); - vixl32::SRegister op2 = SRegisterFrom(op2_loc); - vixl32::SRegister out = OutputSRegister(invoke); - UseScratchRegisterScope temps(assembler->GetVIXLAssembler()); - const vixl32::Register temp1 = temps.Acquire(); - vixl32::Register temp2 = RegisterFrom(invoke->GetLocations()->GetTemp(0)); - vixl32::Label nan, done; - vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &done); - - DCHECK(op1.Is(out)); - - __ Vcmp(op1, op2); - __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR); - __ B(vs, &nan, /* far_target */ false); // if un-ordered, go to NaN handling. - - // op1 <> op2 - vixl32::ConditionType cond = is_min ? gt : lt; - { - ExactAssemblyScope it_scope(assembler->GetVIXLAssembler(), - 2 * kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); - __ it(cond); - __ vmov(cond, F32, out, op2); - } - // for <>(not equal), we've done min/max calculation. - __ B(ne, final_label, /* far_target */ false); - - // handle op1 == op2, max(+0.0,-0.0), min(+0.0,-0.0). - __ Vmov(temp1, op1); - __ Vmov(temp2, op2); - if (is_min) { - __ Orr(temp1, temp1, temp2); - } else { - __ And(temp1, temp1, temp2); - } - __ Vmov(out, temp1); - __ B(final_label); - - // handle NaN input. - __ Bind(&nan); - __ Movt(temp1, High16Bits(kNanFloat)); // 0x7FC0xxxx is a NaN. - __ Vmov(out, temp1); - - if (done.IsReferenced()) { - __ Bind(&done); - } -} - -static void CreateFPFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetInAt(1, Location::RequiresFpuRegister()); - locations->SetOut(Location::SameAsFirstInput()); -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathMinFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); - invoke->GetLocations()->AddTemp(Location::RequiresRegister()); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathMinFloatFloat(HInvoke* invoke) { - GenMinMaxFloat(invoke, /* is_min */ true, codegen_); -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathMaxFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); - invoke->GetLocations()->AddTemp(Location::RequiresRegister()); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathMaxFloatFloat(HInvoke* invoke) { - GenMinMaxFloat(invoke, /* is_min */ false, codegen_); -} - -static void GenMinMaxDouble(HInvoke* invoke, bool is_min, CodeGeneratorARMVIXL* codegen) { - ArmVIXLAssembler* assembler = codegen->GetAssembler(); - Location op1_loc = invoke->GetLocations()->InAt(0); - Location op2_loc = invoke->GetLocations()->InAt(1); - Location out_loc = invoke->GetLocations()->Out(); - - // Optimization: don't generate any code if inputs are the same. - if (op1_loc.Equals(op2_loc)) { - DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in. - return; - } - - vixl32::DRegister op1 = DRegisterFrom(op1_loc); - vixl32::DRegister op2 = DRegisterFrom(op2_loc); - vixl32::DRegister out = OutputDRegister(invoke); - vixl32::Label handle_nan_eq, done; - vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &done); - - DCHECK(op1.Is(out)); - - __ Vcmp(op1, op2); - __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR); - __ B(vs, &handle_nan_eq, /* far_target */ false); // if un-ordered, go to NaN handling. - - // op1 <> op2 - vixl32::ConditionType cond = is_min ? gt : lt; - { - ExactAssemblyScope it_scope(assembler->GetVIXLAssembler(), - 2 * kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); - __ it(cond); - __ vmov(cond, F64, out, op2); - } - // for <>(not equal), we've done min/max calculation. - __ B(ne, final_label, /* far_target */ false); - - // handle op1 == op2, max(+0.0,-0.0). - if (!is_min) { - __ Vand(F64, out, op1, op2); - __ B(final_label); - } - - // handle op1 == op2, min(+0.0,-0.0), NaN input. - __ Bind(&handle_nan_eq); - __ Vorr(F64, out, op1, op2); // assemble op1/-0.0/NaN. - - if (done.IsReferenced()) { - __ Bind(&done); - } -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathMinDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathMinDoubleDouble(HInvoke* invoke) { - GenMinMaxDouble(invoke, /* is_min */ true , codegen_); -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathMaxDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathMaxDoubleDouble(HInvoke* invoke) { - GenMinMaxDouble(invoke, /* is_min */ false, codegen_); -} - -static void GenMinMaxLong(HInvoke* invoke, bool is_min, ArmVIXLAssembler* assembler) { - Location op1_loc = invoke->GetLocations()->InAt(0); - Location op2_loc = invoke->GetLocations()->InAt(1); - Location out_loc = invoke->GetLocations()->Out(); - - // Optimization: don't generate any code if inputs are the same. - if (op1_loc.Equals(op2_loc)) { - DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in location builder. - return; - } - - vixl32::Register op1_lo = LowRegisterFrom(op1_loc); - vixl32::Register op1_hi = HighRegisterFrom(op1_loc); - vixl32::Register op2_lo = LowRegisterFrom(op2_loc); - vixl32::Register op2_hi = HighRegisterFrom(op2_loc); - vixl32::Register out_lo = LowRegisterFrom(out_loc); - vixl32::Register out_hi = HighRegisterFrom(out_loc); - UseScratchRegisterScope temps(assembler->GetVIXLAssembler()); - const vixl32::Register temp = temps.Acquire(); - - DCHECK(op1_lo.Is(out_lo)); - DCHECK(op1_hi.Is(out_hi)); - - // Compare op1 >= op2, or op1 < op2. - __ Cmp(out_lo, op2_lo); - __ Sbcs(temp, out_hi, op2_hi); - - // Now GE/LT condition code is correct for the long comparison. - { - vixl32::ConditionType cond = is_min ? ge : lt; - ExactAssemblyScope it_scope(assembler->GetVIXLAssembler(), - 3 * kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); - __ itt(cond); - __ mov(cond, out_lo, op2_lo); - __ mov(cond, out_hi, op2_hi); - } -} - -static void CreateLongLongToLongLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathMinLongLong(HInvoke* invoke) { - CreateLongLongToLongLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathMinLongLong(HInvoke* invoke) { - GenMinMaxLong(invoke, /* is_min */ true, GetAssembler()); -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathMaxLongLong(HInvoke* invoke) { - CreateLongLongToLongLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathMaxLongLong(HInvoke* invoke) { - GenMinMaxLong(invoke, /* is_min */ false, GetAssembler()); -} - -static void GenMinMax(HInvoke* invoke, bool is_min, ArmVIXLAssembler* assembler) { - vixl32::Register op1 = InputRegisterAt(invoke, 0); - vixl32::Register op2 = InputRegisterAt(invoke, 1); - vixl32::Register out = OutputRegister(invoke); - - __ Cmp(op1, op2); - - { - ExactAssemblyScope aas(assembler->GetVIXLAssembler(), - 3 * kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); - - __ ite(is_min ? lt : gt); - __ mov(is_min ? lt : gt, out, op1); - __ mov(is_min ? ge : le, out, op2); - } -} - -static void CreateIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathMinIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathMinIntInt(HInvoke* invoke) { - GenMinMax(invoke, /* is_min */ true, GetAssembler()); -} - -void IntrinsicLocationsBuilderARMVIXL::VisitMathMaxIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorARMVIXL::VisitMathMaxIntInt(HInvoke* invoke) { - GenMinMax(invoke, /* is_min */ false, GetAssembler()); -} - void IntrinsicLocationsBuilderARMVIXL::VisitMathSqrt(HInvoke* invoke) { CreateFPToFPLocations(allocator_, invoke); } diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 6d6ff7576b..d108c43a4c 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -742,458 +742,6 @@ void IntrinsicCodeGeneratorMIPS::VisitLongBitCount(HInvoke* invoke) { GenBitCount(invoke->GetLocations(), DataType::Type::kInt64, IsR6(), GetAssembler()); } -static void GenMinMaxFP(LocationSummary* locations, - bool is_min, - DataType::Type type, - bool is_R6, - MipsAssembler* assembler) { - FRegister out = locations->Out().AsFpuRegister(); - FRegister a = locations->InAt(0).AsFpuRegister(); - FRegister b = locations->InAt(1).AsFpuRegister(); - - if (is_R6) { - MipsLabel noNaNs; - MipsLabel done; - FRegister ftmp = ((out != a) && (out != b)) ? out : FTMP; - - // When Java computes min/max it prefers a NaN to a number; the - // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of - // the inputs is a NaN and the other is a valid number, the MIPS - // instruction will return the number; Java wants the NaN value - // returned. This is why there is extra logic preceding the use of - // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a - // NaN, return the NaN, otherwise return the min/max. - if (type == DataType::Type::kFloat64) { - __ CmpUnD(FTMP, a, b); - __ Bc1eqz(FTMP, &noNaNs); - - // One of the inputs is a NaN - __ CmpEqD(ftmp, a, a); - // If a == a then b is the NaN, otherwise a is the NaN. - __ SelD(ftmp, a, b); - - if (ftmp != out) { - __ MovD(out, ftmp); - } - - __ B(&done); - - __ Bind(&noNaNs); - - if (is_min) { - __ MinD(out, a, b); - } else { - __ MaxD(out, a, b); - } - } else { - DCHECK_EQ(type, DataType::Type::kFloat32); - __ CmpUnS(FTMP, a, b); - __ Bc1eqz(FTMP, &noNaNs); - - // One of the inputs is a NaN - __ CmpEqS(ftmp, a, a); - // If a == a then b is the NaN, otherwise a is the NaN. - __ SelS(ftmp, a, b); - - if (ftmp != out) { - __ MovS(out, ftmp); - } - - __ B(&done); - - __ Bind(&noNaNs); - - if (is_min) { - __ MinS(out, a, b); - } else { - __ MaxS(out, a, b); - } - } - - __ Bind(&done); - } else { - MipsLabel ordered; - MipsLabel compare; - MipsLabel select; - MipsLabel done; - - if (type == DataType::Type::kFloat64) { - __ CunD(a, b); - } else { - DCHECK_EQ(type, DataType::Type::kFloat32); - __ CunS(a, b); - } - __ Bc1f(&ordered); - - // a or b (or both) is a NaN. Return one, which is a NaN. - if (type == DataType::Type::kFloat64) { - __ CeqD(b, b); - } else { - __ CeqS(b, b); - } - __ B(&select); - - __ Bind(&ordered); - - // Neither is a NaN. - // a == b? (-0.0 compares equal with +0.0) - // If equal, handle zeroes, else compare further. - if (type == DataType::Type::kFloat64) { - __ CeqD(a, b); - } else { - __ CeqS(a, b); - } - __ Bc1f(&compare); - - // a == b either bit for bit or one is -0.0 and the other is +0.0. - if (type == DataType::Type::kFloat64) { - __ MoveFromFpuHigh(TMP, a); - __ MoveFromFpuHigh(AT, b); - } else { - __ Mfc1(TMP, a); - __ Mfc1(AT, b); - } - - if (is_min) { - // -0.0 prevails over +0.0. - __ Or(TMP, TMP, AT); - } else { - // +0.0 prevails over -0.0. - __ And(TMP, TMP, AT); - } - - if (type == DataType::Type::kFloat64) { - __ Mfc1(AT, a); - __ Mtc1(AT, out); - __ MoveToFpuHigh(TMP, out); - } else { - __ Mtc1(TMP, out); - } - __ B(&done); - - __ Bind(&compare); - - if (type == DataType::Type::kFloat64) { - if (is_min) { - // return (a <= b) ? a : b; - __ ColeD(a, b); - } else { - // return (a >= b) ? a : b; - __ ColeD(b, a); // b <= a - } - } else { - if (is_min) { - // return (a <= b) ? a : b; - __ ColeS(a, b); - } else { - // return (a >= b) ? a : b; - __ ColeS(b, a); // b <= a - } - } - - __ Bind(&select); - - if (type == DataType::Type::kFloat64) { - __ MovtD(out, a); - __ MovfD(out, b); - } else { - __ MovtS(out, a); - __ MovfS(out, b); - } - - __ Bind(&done); - } -} - -static void CreateFPFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetInAt(1, Location::RequiresFpuRegister()); - locations->SetOut(Location::RequiresFpuRegister(), Location::kOutputOverlap); -} - -// double java.lang.Math.min(double, double) -void IntrinsicLocationsBuilderMIPS::VisitMathMinDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathMinDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), - /* is_min */ true, - DataType::Type::kFloat64, - IsR6(), - GetAssembler()); -} - -// float java.lang.Math.min(float, float) -void IntrinsicLocationsBuilderMIPS::VisitMathMinFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathMinFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), - /* is_min */ true, - DataType::Type::kFloat32, - IsR6(), - GetAssembler()); -} - -// double java.lang.Math.max(double, double) -void IntrinsicLocationsBuilderMIPS::VisitMathMaxDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathMaxDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), - /* is_min */ false, - DataType::Type::kFloat64, - IsR6(), - GetAssembler()); -} - -// float java.lang.Math.max(float, float) -void IntrinsicLocationsBuilderMIPS::VisitMathMaxFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathMaxFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), - /* is_min */ false, - DataType::Type::kFloat32, - IsR6(), - GetAssembler()); -} - -static void CreateIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - -static void GenMinMax(LocationSummary* locations, - bool is_min, - DataType::Type type, - bool is_R6, - MipsAssembler* assembler) { - if (is_R6) { - // Some architectures, such as ARM and MIPS (prior to r6), have a - // conditional move instruction which only changes the target - // (output) register if the condition is true (MIPS prior to r6 had - // MOVF, MOVT, MOVN, and MOVZ). The SELEQZ and SELNEZ instructions - // always change the target (output) register. If the condition is - // true the output register gets the contents of the "rs" register; - // otherwise, the output register is set to zero. One consequence - // of this is that to implement something like "rd = c==0 ? rs : rt" - // MIPS64r6 needs to use a pair of SELEQZ/SELNEZ instructions. - // After executing this pair of instructions one of the output - // registers from the pair will necessarily contain zero. Then the - // code ORs the output registers from the SELEQZ/SELNEZ instructions - // to get the final result. - // - // The initial test to see if the output register is same as the - // first input register is needed to make sure that value in the - // first input register isn't clobbered before we've finished - // computing the output value. The logic in the corresponding else - // clause performs the same task but makes sure the second input - // register isn't clobbered in the event that it's the same register - // as the output register; the else clause also handles the case - // where the output register is distinct from both the first, and the - // second input registers. - if (type == DataType::Type::kInt64) { - Register a_lo = locations->InAt(0).AsRegisterPairLow(); - Register a_hi = locations->InAt(0).AsRegisterPairHigh(); - Register b_lo = locations->InAt(1).AsRegisterPairLow(); - Register b_hi = locations->InAt(1).AsRegisterPairHigh(); - Register out_lo = locations->Out().AsRegisterPairLow(); - Register out_hi = locations->Out().AsRegisterPairHigh(); - - MipsLabel compare_done; - - if (a_lo == b_lo) { - if (out_lo != a_lo) { - __ Move(out_lo, a_lo); - __ Move(out_hi, a_hi); - } - } else { - __ Slt(TMP, b_hi, a_hi); - __ Bne(b_hi, a_hi, &compare_done); - - __ Sltu(TMP, b_lo, a_lo); - - __ Bind(&compare_done); - - if (is_min) { - __ Seleqz(AT, a_lo, TMP); - __ Selnez(out_lo, b_lo, TMP); // Safe even if out_lo == a_lo/b_lo - // because at this point we're - // done using a_lo/b_lo. - } else { - __ Selnez(AT, a_lo, TMP); - __ Seleqz(out_lo, b_lo, TMP); // ditto - } - __ Or(out_lo, out_lo, AT); - if (is_min) { - __ Seleqz(AT, a_hi, TMP); - __ Selnez(out_hi, b_hi, TMP); // ditto but for out_hi & a_hi/b_hi - } else { - __ Selnez(AT, a_hi, TMP); - __ Seleqz(out_hi, b_hi, TMP); // ditto but for out_hi & a_hi/b_hi - } - __ Or(out_hi, out_hi, AT); - } - } else { - DCHECK_EQ(type, DataType::Type::kInt32); - Register a = locations->InAt(0).AsRegister(); - Register b = locations->InAt(1).AsRegister(); - Register out = locations->Out().AsRegister(); - - if (a == b) { - if (out != a) { - __ Move(out, a); - } - } else { - __ Slt(AT, b, a); - if (is_min) { - __ Seleqz(TMP, a, AT); - __ Selnez(AT, b, AT); - } else { - __ Selnez(TMP, a, AT); - __ Seleqz(AT, b, AT); - } - __ Or(out, TMP, AT); - } - } - } else { - if (type == DataType::Type::kInt64) { - Register a_lo = locations->InAt(0).AsRegisterPairLow(); - Register a_hi = locations->InAt(0).AsRegisterPairHigh(); - Register b_lo = locations->InAt(1).AsRegisterPairLow(); - Register b_hi = locations->InAt(1).AsRegisterPairHigh(); - Register out_lo = locations->Out().AsRegisterPairLow(); - Register out_hi = locations->Out().AsRegisterPairHigh(); - - MipsLabel compare_done; - - if (a_lo == b_lo) { - if (out_lo != a_lo) { - __ Move(out_lo, a_lo); - __ Move(out_hi, a_hi); - } - } else { - __ Slt(TMP, a_hi, b_hi); - __ Bne(a_hi, b_hi, &compare_done); - - __ Sltu(TMP, a_lo, b_lo); - - __ Bind(&compare_done); - - if (is_min) { - if (out_lo != a_lo) { - __ Movn(out_hi, a_hi, TMP); - __ Movn(out_lo, a_lo, TMP); - } - if (out_lo != b_lo) { - __ Movz(out_hi, b_hi, TMP); - __ Movz(out_lo, b_lo, TMP); - } - } else { - if (out_lo != a_lo) { - __ Movz(out_hi, a_hi, TMP); - __ Movz(out_lo, a_lo, TMP); - } - if (out_lo != b_lo) { - __ Movn(out_hi, b_hi, TMP); - __ Movn(out_lo, b_lo, TMP); - } - } - } - } else { - DCHECK_EQ(type, DataType::Type::kInt32); - Register a = locations->InAt(0).AsRegister(); - Register b = locations->InAt(1).AsRegister(); - Register out = locations->Out().AsRegister(); - - if (a == b) { - if (out != a) { - __ Move(out, a); - } - } else { - __ Slt(AT, a, b); - if (is_min) { - if (out != a) { - __ Movn(out, a, AT); - } - if (out != b) { - __ Movz(out, b, AT); - } - } else { - if (out != a) { - __ Movz(out, a, AT); - } - if (out != b) { - __ Movn(out, b, AT); - } - } - } - } - } -} - -// int java.lang.Math.min(int, int) -void IntrinsicLocationsBuilderMIPS::VisitMathMinIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathMinIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), - /* is_min */ true, - DataType::Type::kInt32, - IsR6(), - GetAssembler()); -} - -// long java.lang.Math.min(long, long) -void IntrinsicLocationsBuilderMIPS::VisitMathMinLongLong(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathMinLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), - /* is_min */ true, - DataType::Type::kInt64, - IsR6(), - GetAssembler()); -} - -// int java.lang.Math.max(int, int) -void IntrinsicLocationsBuilderMIPS::VisitMathMaxIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathMaxIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), - /* is_min */ false, - DataType::Type::kInt32, - IsR6(), - GetAssembler()); -} - -// long java.lang.Math.max(long, long) -void IntrinsicLocationsBuilderMIPS::VisitMathMaxLongLong(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS::VisitMathMaxLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), - /* is_min */ false, - DataType::Type::kInt64, - IsR6(), - GetAssembler()); -} - // double java.lang.Math.sqrt(double) void IntrinsicLocationsBuilderMIPS::VisitMathSqrt(HInvoke* invoke) { CreateFPToFPLocations(allocator_, invoke); diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 5debd26c5a..9987d05fb3 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -470,221 +470,6 @@ void IntrinsicCodeGeneratorMIPS64::VisitLongBitCount(HInvoke* invoke) { GenBitCount(invoke->GetLocations(), DataType::Type::kInt64, GetAssembler()); } -static void GenMinMaxFP(LocationSummary* locations, - bool is_min, - DataType::Type type, - Mips64Assembler* assembler) { - FpuRegister a = locations->InAt(0).AsFpuRegister(); - FpuRegister b = locations->InAt(1).AsFpuRegister(); - FpuRegister out = locations->Out().AsFpuRegister(); - - Mips64Label noNaNs; - Mips64Label done; - FpuRegister ftmp = ((out != a) && (out != b)) ? out : FTMP; - - // When Java computes min/max it prefers a NaN to a number; the - // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of - // the inputs is a NaN and the other is a valid number, the MIPS - // instruction will return the number; Java wants the NaN value - // returned. This is why there is extra logic preceding the use of - // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a - // NaN, return the NaN, otherwise return the min/max. - if (type == DataType::Type::kFloat64) { - __ CmpUnD(FTMP, a, b); - __ Bc1eqz(FTMP, &noNaNs); - - // One of the inputs is a NaN - __ CmpEqD(ftmp, a, a); - // If a == a then b is the NaN, otherwise a is the NaN. - __ SelD(ftmp, a, b); - - if (ftmp != out) { - __ MovD(out, ftmp); - } - - __ Bc(&done); - - __ Bind(&noNaNs); - - if (is_min) { - __ MinD(out, a, b); - } else { - __ MaxD(out, a, b); - } - } else { - DCHECK_EQ(type, DataType::Type::kFloat32); - __ CmpUnS(FTMP, a, b); - __ Bc1eqz(FTMP, &noNaNs); - - // One of the inputs is a NaN - __ CmpEqS(ftmp, a, a); - // If a == a then b is the NaN, otherwise a is the NaN. - __ SelS(ftmp, a, b); - - if (ftmp != out) { - __ MovS(out, ftmp); - } - - __ Bc(&done); - - __ Bind(&noNaNs); - - if (is_min) { - __ MinS(out, a, b); - } else { - __ MaxS(out, a, b); - } - } - - __ Bind(&done); -} - -static void CreateFPFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetInAt(1, Location::RequiresFpuRegister()); - locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); -} - -// double java.lang.Math.min(double, double) -void IntrinsicLocationsBuilderMIPS64::VisitMathMinDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathMinDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, DataType::Type::kFloat64, GetAssembler()); -} - -// float java.lang.Math.min(float, float) -void IntrinsicLocationsBuilderMIPS64::VisitMathMinFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathMinFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, DataType::Type::kFloat32, GetAssembler()); -} - -// double java.lang.Math.max(double, double) -void IntrinsicLocationsBuilderMIPS64::VisitMathMaxDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathMaxDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, DataType::Type::kFloat64, GetAssembler()); -} - -// float java.lang.Math.max(float, float) -void IntrinsicLocationsBuilderMIPS64::VisitMathMaxFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathMaxFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, DataType::Type::kFloat32, GetAssembler()); -} - -static void GenMinMax(LocationSummary* locations, - bool is_min, - Mips64Assembler* assembler) { - GpuRegister lhs = locations->InAt(0).AsRegister(); - GpuRegister rhs = locations->InAt(1).AsRegister(); - GpuRegister out = locations->Out().AsRegister(); - - if (lhs == rhs) { - if (out != lhs) { - __ Move(out, lhs); - } - } else { - // Some architectures, such as ARM and MIPS (prior to r6), have a - // conditional move instruction which only changes the target - // (output) register if the condition is true (MIPS prior to r6 had - // MOVF, MOVT, and MOVZ). The SELEQZ and SELNEZ instructions always - // change the target (output) register. If the condition is true the - // output register gets the contents of the "rs" register; otherwise, - // the output register is set to zero. One consequence of this is - // that to implement something like "rd = c==0 ? rs : rt" MIPS64r6 - // needs to use a pair of SELEQZ/SELNEZ instructions. After - // executing this pair of instructions one of the output registers - // from the pair will necessarily contain zero. Then the code ORs the - // output registers from the SELEQZ/SELNEZ instructions to get the - // final result. - // - // The initial test to see if the output register is same as the - // first input register is needed to make sure that value in the - // first input register isn't clobbered before we've finished - // computing the output value. The logic in the corresponding else - // clause performs the same task but makes sure the second input - // register isn't clobbered in the event that it's the same register - // as the output register; the else clause also handles the case - // where the output register is distinct from both the first, and the - // second input registers. - if (out == lhs) { - __ Slt(AT, rhs, lhs); - if (is_min) { - __ Seleqz(out, lhs, AT); - __ Selnez(AT, rhs, AT); - } else { - __ Selnez(out, lhs, AT); - __ Seleqz(AT, rhs, AT); - } - } else { - __ Slt(AT, lhs, rhs); - if (is_min) { - __ Seleqz(out, rhs, AT); - __ Selnez(AT, lhs, AT); - } else { - __ Selnez(out, rhs, AT); - __ Seleqz(AT, lhs, AT); - } - } - __ Or(out, out, AT); - } -} - -static void CreateIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - -// int java.lang.Math.min(int, int) -void IntrinsicLocationsBuilderMIPS64::VisitMathMinIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathMinIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ true, GetAssembler()); -} - -// long java.lang.Math.min(long, long) -void IntrinsicLocationsBuilderMIPS64::VisitMathMinLongLong(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathMinLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ true, GetAssembler()); -} - -// int java.lang.Math.max(int, int) -void IntrinsicLocationsBuilderMIPS64::VisitMathMaxIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathMaxIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ false, GetAssembler()); -} - -// long java.lang.Math.max(long, long) -void IntrinsicLocationsBuilderMIPS64::VisitMathMaxLongLong(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorMIPS64::VisitMathMaxLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ false, GetAssembler()); -} - // double java.lang.Math.sqrt(double) void IntrinsicLocationsBuilderMIPS64::VisitMathSqrt(HInvoke* invoke) { CreateFPToFPLocations(allocator_, invoke); diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 0edc061e97..c4f322bf0c 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -40,11 +40,6 @@ namespace art { namespace x86 { -static constexpr int kDoubleNaNHigh = 0x7FF80000; -static constexpr int kDoubleNaNLow = 0x00000000; -static constexpr int64_t kDoubleNaN = INT64_C(0x7FF8000000000000); -static constexpr int32_t kFloatNaN = INT32_C(0x7FC00000); - IntrinsicLocationsBuilderX86::IntrinsicLocationsBuilderX86(CodeGeneratorX86* codegen) : allocator_(codegen->GetGraph()->GetAllocator()), codegen_(codegen) { @@ -333,278 +328,6 @@ void IntrinsicCodeGeneratorX86::VisitShortReverseBytes(HInvoke* invoke) { GenReverseBytes(invoke->GetLocations(), DataType::Type::kInt16, GetAssembler()); } -static void GenMinMaxFP(HInvoke* invoke, - bool is_min, - bool is_double, - X86Assembler* assembler, - CodeGeneratorX86* codegen) { - LocationSummary* locations = invoke->GetLocations(); - Location op1_loc = locations->InAt(0); - Location op2_loc = locations->InAt(1); - Location out_loc = locations->Out(); - XmmRegister out = out_loc.AsFpuRegister(); - - // Shortcut for same input locations. - if (op1_loc.Equals(op2_loc)) { - DCHECK(out_loc.Equals(op1_loc)); - return; - } - - // (out := op1) - // out <=? op2 - // if Nan jmp Nan_label - // if out is min jmp done - // if op2 is min jmp op2_label - // handle -0/+0 - // jmp done - // Nan_label: - // out := NaN - // op2_label: - // out := op2 - // done: - // - // This removes one jmp, but needs to copy one input (op1) to out. - // - // TODO: This is straight from Quick (except literal pool). Make NaN an out-of-line slowpath? - - XmmRegister op2 = op2_loc.AsFpuRegister(); - - NearLabel nan, done, op2_label; - if (is_double) { - __ ucomisd(out, op2); - } else { - __ ucomiss(out, op2); - } - - __ j(Condition::kParityEven, &nan); - - __ j(is_min ? Condition::kAbove : Condition::kBelow, &op2_label); - __ j(is_min ? Condition::kBelow : Condition::kAbove, &done); - - // Handle 0.0/-0.0. - if (is_min) { - if (is_double) { - __ orpd(out, op2); - } else { - __ orps(out, op2); - } - } else { - if (is_double) { - __ andpd(out, op2); - } else { - __ andps(out, op2); - } - } - __ jmp(&done); - - // NaN handling. - __ Bind(&nan); - // Do we have a constant area pointer? - if (locations->GetInputCount() == 3 && locations->InAt(2).IsValid()) { - HX86ComputeBaseMethodAddress* method_address = - invoke->InputAt(2)->AsX86ComputeBaseMethodAddress(); - DCHECK(locations->InAt(2).IsRegister()); - Register constant_area = locations->InAt(2).AsRegister(); - if (is_double) { - __ movsd(out, codegen->LiteralInt64Address(kDoubleNaN, method_address, constant_area)); - } else { - __ movss(out, codegen->LiteralInt32Address(kFloatNaN, method_address, constant_area)); - } - } else { - if (is_double) { - __ pushl(Immediate(kDoubleNaNHigh)); - __ pushl(Immediate(kDoubleNaNLow)); - __ movsd(out, Address(ESP, 0)); - __ addl(ESP, Immediate(8)); - } else { - __ pushl(Immediate(kFloatNaN)); - __ movss(out, Address(ESP, 0)); - __ addl(ESP, Immediate(4)); - } - } - __ jmp(&done); - - // out := op2; - __ Bind(&op2_label); - if (is_double) { - __ movsd(out, op2); - } else { - __ movss(out, op2); - } - - // Done. - __ Bind(&done); -} - -static void CreateFPFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetInAt(1, Location::RequiresFpuRegister()); - // The following is sub-optimal, but all we can do for now. It would be fine to also accept - // the second input to be the output (we can simply swap inputs). - locations->SetOut(Location::SameAsFirstInput()); - HInvokeStaticOrDirect* static_or_direct = invoke->AsInvokeStaticOrDirect(); - DCHECK(static_or_direct != nullptr); - if (static_or_direct->HasSpecialInput() && - invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) { - locations->SetInAt(2, Location::RequiresRegister()); - } -} - -void IntrinsicLocationsBuilderX86::VisitMathMinDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathMinDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke, - /* is_min */ true, - /* is_double */ true, - GetAssembler(), - codegen_); -} - -void IntrinsicLocationsBuilderX86::VisitMathMinFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathMinFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke, - /* is_min */ true, - /* is_double */ false, - GetAssembler(), - codegen_); -} - -void IntrinsicLocationsBuilderX86::VisitMathMaxDoubleDouble(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathMaxDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke, - /* is_min */ false, - /* is_double */ true, - GetAssembler(), - codegen_); -} - -void IntrinsicLocationsBuilderX86::VisitMathMaxFloatFloat(HInvoke* invoke) { - CreateFPFPToFPLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathMaxFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke, - /* is_min */ false, - /* is_double */ false, - GetAssembler(), - codegen_); -} - -static void GenMinMax(LocationSummary* locations, bool is_min, bool is_long, - X86Assembler* assembler) { - Location op1_loc = locations->InAt(0); - Location op2_loc = locations->InAt(1); - - // Shortcut for same input locations. - if (op1_loc.Equals(op2_loc)) { - // Can return immediately, as op1_loc == out_loc. - // Note: if we ever support separate registers, e.g., output into memory, we need to check for - // a copy here. - DCHECK(locations->Out().Equals(op1_loc)); - return; - } - - if (is_long) { - // Need to perform a subtract to get the sign right. - // op1 is already in the same location as the output. - Location output = locations->Out(); - Register output_lo = output.AsRegisterPairLow(); - Register output_hi = output.AsRegisterPairHigh(); - - Register op2_lo = op2_loc.AsRegisterPairLow(); - Register op2_hi = op2_loc.AsRegisterPairHigh(); - - // Spare register to compute the subtraction to set condition code. - Register temp = locations->GetTemp(0).AsRegister(); - - // Subtract off op2_low. - __ movl(temp, output_lo); - __ subl(temp, op2_lo); - - // Now use the same tempo and the borrow to finish the subtraction of op2_hi. - __ movl(temp, output_hi); - __ sbbl(temp, op2_hi); - - // Now the condition code is correct. - Condition cond = is_min ? Condition::kGreaterEqual : Condition::kLess; - __ cmovl(cond, output_lo, op2_lo); - __ cmovl(cond, output_hi, op2_hi); - } else { - Register out = locations->Out().AsRegister(); - Register op2 = op2_loc.AsRegister(); - - // (out := op1) - // out <=? op2 - // if out is min jmp done - // out := op2 - // done: - - __ cmpl(out, op2); - Condition cond = is_min ? Condition::kGreater : Condition::kLess; - __ cmovl(cond, out, op2); - } -} - -static void CreateIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); -} - -static void CreateLongLongToLongLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); - // Register to use to perform a long subtract to set cc. - locations->AddTemp(Location::RequiresRegister()); -} - -void IntrinsicLocationsBuilderX86::VisitMathMinIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathMinIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetAssembler()); -} - -void IntrinsicLocationsBuilderX86::VisitMathMinLongLong(HInvoke* invoke) { - CreateLongLongToLongLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathMinLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetAssembler()); -} - -void IntrinsicLocationsBuilderX86::VisitMathMaxIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathMaxIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetAssembler()); -} - -void IntrinsicLocationsBuilderX86::VisitMathMaxLongLong(HInvoke* invoke) { - CreateLongLongToLongLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86::VisitMathMaxLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetAssembler()); -} - static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { LocationSummary* locations = new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 9d378d6b59..437bc3dd3c 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -236,208 +236,6 @@ void IntrinsicCodeGeneratorX86_64::VisitShortReverseBytes(HInvoke* invoke) { GenReverseBytes(invoke->GetLocations(), DataType::Type::kInt16, GetAssembler()); } -static void GenMinMaxFP(LocationSummary* locations, - bool is_min, - bool is_double, - X86_64Assembler* assembler, - CodeGeneratorX86_64* codegen) { - Location op1_loc = locations->InAt(0); - Location op2_loc = locations->InAt(1); - Location out_loc = locations->Out(); - XmmRegister out = out_loc.AsFpuRegister(); - - // Shortcut for same input locations. - if (op1_loc.Equals(op2_loc)) { - DCHECK(out_loc.Equals(op1_loc)); - return; - } - - // (out := op1) - // out <=? op2 - // if Nan jmp Nan_label - // if out is min jmp done - // if op2 is min jmp op2_label - // handle -0/+0 - // jmp done - // Nan_label: - // out := NaN - // op2_label: - // out := op2 - // done: - // - // This removes one jmp, but needs to copy one input (op1) to out. - // - // TODO: This is straight from Quick. Make NaN an out-of-line slowpath? - - XmmRegister op2 = op2_loc.AsFpuRegister(); - - NearLabel nan, done, op2_label; - if (is_double) { - __ ucomisd(out, op2); - } else { - __ ucomiss(out, op2); - } - - __ j(Condition::kParityEven, &nan); - - __ j(is_min ? Condition::kAbove : Condition::kBelow, &op2_label); - __ j(is_min ? Condition::kBelow : Condition::kAbove, &done); - - // Handle 0.0/-0.0. - if (is_min) { - if (is_double) { - __ orpd(out, op2); - } else { - __ orps(out, op2); - } - } else { - if (is_double) { - __ andpd(out, op2); - } else { - __ andps(out, op2); - } - } - __ jmp(&done); - - // NaN handling. - __ Bind(&nan); - if (is_double) { - __ movsd(out, codegen->LiteralInt64Address(INT64_C(0x7FF8000000000000))); - } else { - __ movss(out, codegen->LiteralInt32Address(INT32_C(0x7FC00000))); - } - __ jmp(&done); - - // out := op2; - __ Bind(&op2_label); - if (is_double) { - __ movsd(out, op2); - } else { - __ movss(out, op2); - } - - // Done. - __ Bind(&done); -} - -static void CreateFPFPToFP(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetInAt(1, Location::RequiresFpuRegister()); - // The following is sub-optimal, but all we can do for now. It would be fine to also accept - // the second input to be the output (we can simply swap inputs). - locations->SetOut(Location::SameAsFirstInput()); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathMinDoubleDouble(HInvoke* invoke) { - CreateFPFPToFP(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathMinDoubleDouble(HInvoke* invoke) { - GenMinMaxFP( - invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetAssembler(), codegen_); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathMinFloatFloat(HInvoke* invoke) { - CreateFPFPToFP(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathMinFloatFloat(HInvoke* invoke) { - GenMinMaxFP( - invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetAssembler(), codegen_); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathMaxDoubleDouble(HInvoke* invoke) { - CreateFPFPToFP(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathMaxDoubleDouble(HInvoke* invoke) { - GenMinMaxFP( - invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetAssembler(), codegen_); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathMaxFloatFloat(HInvoke* invoke) { - CreateFPFPToFP(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathMaxFloatFloat(HInvoke* invoke) { - GenMinMaxFP( - invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetAssembler(), codegen_); -} - -static void GenMinMax(LocationSummary* locations, bool is_min, bool is_long, - X86_64Assembler* assembler) { - Location op1_loc = locations->InAt(0); - Location op2_loc = locations->InAt(1); - - // Shortcut for same input locations. - if (op1_loc.Equals(op2_loc)) { - // Can return immediately, as op1_loc == out_loc. - // Note: if we ever support separate registers, e.g., output into memory, we need to check for - // a copy here. - DCHECK(locations->Out().Equals(op1_loc)); - return; - } - - CpuRegister out = locations->Out().AsRegister(); - CpuRegister op2 = op2_loc.AsRegister(); - - // (out := op1) - // out <=? op2 - // if out is min jmp done - // out := op2 - // done: - - if (is_long) { - __ cmpq(out, op2); - } else { - __ cmpl(out, op2); - } - - __ cmov(is_min ? Condition::kGreater : Condition::kLess, out, op2, is_long); -} - -static void CreateIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) { - LocationSummary* locations = - new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathMinIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathMinIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetAssembler()); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathMinLongLong(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathMinLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetAssembler()); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathMaxIntInt(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathMaxIntInt(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetAssembler()); -} - -void IntrinsicLocationsBuilderX86_64::VisitMathMaxLongLong(HInvoke* invoke) { - CreateIntIntToIntLocations(allocator_, invoke); -} - -void IntrinsicCodeGeneratorX86_64::VisitMathMaxLongLong(HInvoke* invoke) { - GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetAssembler()); -} - static void CreateFPToFPLocations(ArenaAllocator* allocator, HInvoke* invoke) { LocationSummary* locations = new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 5a483e234b..d3b081e005 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -334,29 +334,14 @@ static bool IsAddConst(HInstruction* instruction, // Detect reductions of the following forms, // x = x_phi + .. // x = x_phi - .. -// x = max(x_phi, ..) // x = min(x_phi, ..) +// x = max(x_phi, ..) static bool HasReductionFormat(HInstruction* reduction, HInstruction* phi) { - if (reduction->IsAdd()) { + if (reduction->IsAdd() || reduction->IsMin() || reduction->IsMax()) { return (reduction->InputAt(0) == phi && reduction->InputAt(1) != phi) || (reduction->InputAt(0) != phi && reduction->InputAt(1) == phi); } else if (reduction->IsSub()) { return (reduction->InputAt(0) == phi && reduction->InputAt(1) != phi); - } else if (reduction->IsInvokeStaticOrDirect()) { - switch (reduction->AsInvokeStaticOrDirect()->GetIntrinsic()) { - case Intrinsics::kMathMinIntInt: - case Intrinsics::kMathMinLongLong: - case Intrinsics::kMathMinFloatFloat: - case Intrinsics::kMathMinDoubleDouble: - case Intrinsics::kMathMaxIntInt: - case Intrinsics::kMathMaxLongLong: - case Intrinsics::kMathMaxFloatFloat: - case Intrinsics::kMathMaxDoubleDouble: - return (reduction->InputAt(0) == phi && reduction->InputAt(1) != phi) || - (reduction->InputAt(0) != phi && reduction->InputAt(1) == phi); - default: - return false; - } } return false; } @@ -1322,50 +1307,34 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node, } return true; } - } else if (instruction->IsInvokeStaticOrDirect()) { - // Accept particular intrinsics. - HInvokeStaticOrDirect* invoke = instruction->AsInvokeStaticOrDirect(); - switch (invoke->GetIntrinsic()) { - case Intrinsics::kMathMinIntInt: - case Intrinsics::kMathMinLongLong: - case Intrinsics::kMathMinFloatFloat: - case Intrinsics::kMathMinDoubleDouble: - case Intrinsics::kMathMaxIntInt: - case Intrinsics::kMathMaxLongLong: - case Intrinsics::kMathMaxFloatFloat: - case Intrinsics::kMathMaxDoubleDouble: { - // Deal with vector restrictions. - HInstruction* opa = instruction->InputAt(0); - HInstruction* opb = instruction->InputAt(1); - HInstruction* r = opa; - HInstruction* s = opb; - bool is_unsigned = false; - if (HasVectorRestrictions(restrictions, kNoMinMax)) { - return false; - } else if (HasVectorRestrictions(restrictions, kNoHiBits) && - !IsNarrowerOperands(opa, opb, type, &r, &s, &is_unsigned)) { - return false; // reject, unless all operands are same-extension narrower - } - // Accept MIN/MAX(x, y) for vectorizable operands. - DCHECK(r != nullptr); - DCHECK(s != nullptr); - if (generate_code && vector_mode_ != kVector) { // de-idiom - r = opa; - s = opb; - } - if (VectorizeUse(node, r, generate_code, type, restrictions) && - VectorizeUse(node, s, generate_code, type, restrictions)) { - if (generate_code) { - GenerateVecOp( - instruction, vector_map_->Get(r), vector_map_->Get(s), type, is_unsigned); - } - return true; - } - return false; + } else if (instruction->IsMin() || instruction->IsMax()) { + // Deal with vector restrictions. + HInstruction* opa = instruction->InputAt(0); + HInstruction* opb = instruction->InputAt(1); + HInstruction* r = opa; + HInstruction* s = opb; + bool is_unsigned = false; + if (HasVectorRestrictions(restrictions, kNoMinMax)) { + return false; + } else if (HasVectorRestrictions(restrictions, kNoHiBits) && + !IsNarrowerOperands(opa, opb, type, &r, &s, &is_unsigned)) { + return false; // reject, unless all operands are same-extension narrower + } + // Accept MIN/MAX(x, y) for vectorizable operands. + DCHECK(r != nullptr); + DCHECK(s != nullptr); + if (generate_code && vector_mode_ != kVector) { // de-idiom + r = opa; + s = opb; + } + if (VectorizeUse(node, r, generate_code, type, restrictions) && + VectorizeUse(node, s, generate_code, type, restrictions)) { + if (generate_code) { + GenerateVecOp( + instruction, vector_map_->Get(r), vector_map_->Get(s), type, is_unsigned); } - default: - return false; - } // switch + return true; + } } return false; } @@ -1806,80 +1775,29 @@ void HLoopOptimization::GenerateVecOp(HInstruction* org, GENERATE_VEC( new (global_allocator_) HVecUShr(global_allocator_, opa, opb, type, vector_length_, dex_pc), new (global_allocator_) HUShr(org_type, opa, opb, dex_pc)); + case HInstruction::kMin: + GENERATE_VEC( + new (global_allocator_) HVecMin(global_allocator_, + opa, + opb, + HVecOperation::ToProperType(type, is_unsigned), + vector_length_, + dex_pc), + new (global_allocator_) HMin(org_type, opa, opb, dex_pc)); + case HInstruction::kMax: + GENERATE_VEC( + new (global_allocator_) HVecMax(global_allocator_, + opa, + opb, + HVecOperation::ToProperType(type, is_unsigned), + vector_length_, + dex_pc), + new (global_allocator_) HMax(org_type, opa, opb, dex_pc)); case HInstruction::kAbs: DCHECK(opb == nullptr); GENERATE_VEC( new (global_allocator_) HVecAbs(global_allocator_, opa, type, vector_length_, dex_pc), new (global_allocator_) HAbs(org_type, opa, dex_pc)); - case HInstruction::kInvokeStaticOrDirect: { - HInvokeStaticOrDirect* invoke = org->AsInvokeStaticOrDirect(); - if (vector_mode_ == kVector) { - switch (invoke->GetIntrinsic()) { - case Intrinsics::kMathMinIntInt: - case Intrinsics::kMathMinLongLong: - case Intrinsics::kMathMinFloatFloat: - case Intrinsics::kMathMinDoubleDouble: { - vector = new (global_allocator_) - HVecMin(global_allocator_, - opa, - opb, - HVecOperation::ToProperType(type, is_unsigned), - vector_length_, - dex_pc); - break; - } - case Intrinsics::kMathMaxIntInt: - case Intrinsics::kMathMaxLongLong: - case Intrinsics::kMathMaxFloatFloat: - case Intrinsics::kMathMaxDoubleDouble: { - vector = new (global_allocator_) - HVecMax(global_allocator_, - opa, - opb, - HVecOperation::ToProperType(type, is_unsigned), - vector_length_, - dex_pc); - break; - } - default: - LOG(FATAL) << "Unsupported SIMD intrinsic " << org->GetId(); - UNREACHABLE(); - } // switch invoke - } else { - // In scalar code, simply clone the method invoke, and replace its operands with the - // corresponding new scalar instructions in the loop. The instruction will get an - // environment while being inserted from the instruction map in original program order. - DCHECK(vector_mode_ == kSequential); - size_t num_args = invoke->GetNumberOfArguments(); - HInvokeStaticOrDirect* new_invoke = new (global_allocator_) HInvokeStaticOrDirect( - global_allocator_, - num_args, - invoke->GetType(), - invoke->GetDexPc(), - invoke->GetDexMethodIndex(), - invoke->GetResolvedMethod(), - invoke->GetDispatchInfo(), - invoke->GetInvokeType(), - invoke->GetTargetMethod(), - invoke->GetClinitCheckRequirement()); - HInputsRef inputs = invoke->GetInputs(); - size_t num_inputs = inputs.size(); - DCHECK_LE(num_args, num_inputs); - DCHECK_EQ(num_inputs, new_invoke->GetInputs().size()); // both invokes agree - for (size_t index = 0; index < num_inputs; ++index) { - HInstruction* new_input = index < num_args - ? vector_map_->Get(inputs[index]) - : inputs[index]; // beyond arguments: just pass through - new_invoke->SetArgumentAt(index, new_input); - } - new_invoke->SetIntrinsic(invoke->GetIntrinsic(), - kNeedsEnvironmentOrCache, - kNoSideEffects, - kNoThrow); - vector = new_invoke; - } - break; - } default: break; } // switch diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 62550be526..9da46206da 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1384,7 +1384,9 @@ class HLoopInformationOutwardIterator : public ValueObject { M(LoadException, Instruction) \ M(LoadString, Instruction) \ M(LongConstant, Constant) \ + M(Max, Instruction) \ M(MemoryBarrier, Instruction) \ + M(Min, BinaryOperation) \ M(MonitorOperation, Instruction) \ M(Mul, BinaryOperation) \ M(NativeDebugInfo, Instruction) \ @@ -5017,6 +5019,76 @@ class HRem FINAL : public HBinaryOperation { DEFAULT_COPY_CONSTRUCTOR(Rem); }; +class HMin FINAL : public HBinaryOperation { + public: + HMin(DataType::Type result_type, + HInstruction* left, + HInstruction* right, + uint32_t dex_pc) + : HBinaryOperation(kMin, result_type, left, right, SideEffects::None(), dex_pc) {} + + bool IsCommutative() const OVERRIDE { return true; } + + // Evaluation for integral values. + template static T ComputeIntegral(T x, T y) { + return (x <= y) ? x : y; + } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant( + ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc()); + } + // TODO: Evaluation for floating-point values. + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { return nullptr; } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { return nullptr; } + + DECLARE_INSTRUCTION(Min); + + protected: + DEFAULT_COPY_CONSTRUCTOR(Min); +}; + +class HMax FINAL : public HBinaryOperation { + public: + HMax(DataType::Type result_type, + HInstruction* left, + HInstruction* right, + uint32_t dex_pc) + : HBinaryOperation(kMax, result_type, left, right, SideEffects::None(), dex_pc) {} + + bool IsCommutative() const OVERRIDE { return true; } + + // Evaluation for integral values. + template static T ComputeIntegral(T x, T y) { + return (x >= y) ? x : y; + } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant( + ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc()); + } + // TODO: Evaluation for floating-point values. + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { return nullptr; } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { return nullptr; } + + DECLARE_INSTRUCTION(Max); + + protected: + DEFAULT_COPY_CONSTRUCTOR(Max); +}; + class HAbs FINAL : public HUnaryOperation { public: HAbs(DataType::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc) diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc index a3ca6311c2..2591783cb6 100644 --- a/compiler/optimizing/pc_relative_fixups_x86.cc +++ b/compiler/optimizing/pc_relative_fixups_x86.cc @@ -234,12 +234,13 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { switch (invoke->GetIntrinsic()) { case Intrinsics::kMathAbsDouble: case Intrinsics::kMathAbsFloat: - LOG(FATAL) << "Unreachable abs"; - UNREACHABLE(); case Intrinsics::kMathMaxDoubleDouble: case Intrinsics::kMathMaxFloatFloat: case Intrinsics::kMathMinDoubleDouble: case Intrinsics::kMathMinFloatFloat: + LOG(FATAL) << "Unreachable min/max/abs: intrinsics should have been lowered " + "to IR nodes by instruction simplifier"; + UNREACHABLE(); case Intrinsics::kMathRoundFloat: if (!base_added) { DCHECK(invoke_static_or_direct != nullptr); diff --git a/compiler/optimizing/scheduler.cc b/compiler/optimizing/scheduler.cc index cdbe48342d..bca538fb17 100644 --- a/compiler/optimizing/scheduler.cc +++ b/compiler/optimizing/scheduler.cc @@ -679,6 +679,8 @@ bool HScheduler::IsSchedulable(const HInstruction* instruction) const { instruction->IsCompare() || instruction->IsCondition() || instruction->IsDiv() || + instruction->IsMin() || + instruction->IsMax() || instruction->IsMul() || instruction->IsOr() || instruction->IsRem() || diff --git a/test/445-checker-licm/src/Main.java b/test/445-checker-licm/src/Main.java index bd5d9e20c7..517aacd9f2 100644 --- a/test/445-checker-licm/src/Main.java +++ b/test/445-checker-licm/src/Main.java @@ -153,7 +153,6 @@ public class Main { return result; } - /// CHECK-START: int Main.invariantBoundIntrinsic(int) instruction_simplifier (before) /// CHECK-DAG: InvokeStaticOrDirect loop:{{B\d+}} // @@ -176,14 +175,17 @@ public class Main { return result; } - /// CHECK-START: int Main.invariantBodyIntrinsic(int, int) licm (before) + /// CHECK-START: int Main.invariantBodyIntrinsic(int, int) instruction_simplifier (before) /// CHECK-DAG: InvokeStaticOrDirect loop:{{B\d+}} + /// CHECK-START: int Main.invariantBodyIntrinsic(int, int) licm (before) + /// CHECK-DAG: Max loop:{{B\d+}} + /// CHECK-START: int Main.invariantBodyIntrinsic(int, int) licm (after) - /// CHECK-NOT: InvokeStaticOrDirect loop:{{B\d+}} + /// CHECK-NOT: Max loop:{{B\d+}} /// CHECK-START: int Main.invariantBodyIntrinsic(int, int) licm (after) - /// CHECK-DAG: InvokeStaticOrDirect loop:none + /// CHECK-DAG: Max loop:none public static int invariantBodyIntrinsic(int x, int y) { int result = 0; diff --git a/test/562-checker-no-intermediate/src/Main.java b/test/562-checker-no-intermediate/src/Main.java index d61a9b1d89..2b19918142 100644 --- a/test/562-checker-no-intermediate/src/Main.java +++ b/test/562-checker-no-intermediate/src/Main.java @@ -18,7 +18,7 @@ public class Main { /** * Check that the intermediate address computation is not reordered or merged - * across the call to Math.abs(). + * across a method call. */ /// CHECK-START-ARM: void Main.main(java.lang.String[]) instruction_simplifier_arm (before) diff --git a/test/651-checker-byte-simd-minmax/src/Main.java b/test/651-checker-byte-simd-minmax/src/Main.java index 45949ae90a..4e667bbc6f 100644 --- a/test/651-checker-byte-simd-minmax/src/Main.java +++ b/test/651-checker-byte-simd-minmax/src/Main.java @@ -23,7 +23,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMinIntInt loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // @@ -54,7 +54,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMinIntInt loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // @@ -74,7 +74,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMaxIntInt loop:<> outer_loop:none + /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // @@ -105,7 +105,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMaxIntInt loop:<> outer_loop:none + /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // @@ -125,7 +125,7 @@ public class Main { /// CHECK-DAG: <> IntConstant 100 loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMinIntInt loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // diff --git a/test/651-checker-char-simd-minmax/src/Main.java b/test/651-checker-char-simd-minmax/src/Main.java index 9b056094a3..520e10b6c1 100644 --- a/test/651-checker-char-simd-minmax/src/Main.java +++ b/test/651-checker-char-simd-minmax/src/Main.java @@ -23,7 +23,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMinIntInt loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // @@ -43,7 +43,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMaxIntInt loop:<> outer_loop:none + /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // @@ -63,7 +63,7 @@ public class Main { /// CHECK-DAG: <> IntConstant 100 loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMinIntInt loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // diff --git a/test/651-checker-double-simd-minmax/src/Main.java b/test/651-checker-double-simd-minmax/src/Main.java index 6b12e7e63c..2eaf907167 100644 --- a/test/651-checker-double-simd-minmax/src/Main.java +++ b/test/651-checker-double-simd-minmax/src/Main.java @@ -23,7 +23,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMinDoubleDouble loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // // TODO x86: 0.0 vs -0.0? @@ -45,7 +45,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMaxDoubleDouble loop:<> outer_loop:none + /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // // TODO x86: 0.0 vs -0.0? diff --git a/test/651-checker-float-simd-minmax/src/Main.java b/test/651-checker-float-simd-minmax/src/Main.java index 278a9c9367..dc09dfc7cc 100644 --- a/test/651-checker-float-simd-minmax/src/Main.java +++ b/test/651-checker-float-simd-minmax/src/Main.java @@ -23,7 +23,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMinFloatFloat loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // // TODO x86: 0.0 vs -0.0? @@ -45,7 +45,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMaxFloatFloat loop:<> outer_loop:none + /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // // TODO x86: 0.0 vs -0.0? diff --git a/test/651-checker-int-simd-minmax/src/Main.java b/test/651-checker-int-simd-minmax/src/Main.java index cfa0ae7dca..82fad84d08 100644 --- a/test/651-checker-int-simd-minmax/src/Main.java +++ b/test/651-checker-int-simd-minmax/src/Main.java @@ -23,7 +23,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMinIntInt loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(int[], int[], int[]) loop_optimization (after) @@ -42,7 +42,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMaxIntInt loop:<> outer_loop:none + /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(int[], int[], int[]) loop_optimization (after) diff --git a/test/651-checker-long-simd-minmax/src/Main.java b/test/651-checker-long-simd-minmax/src/Main.java index 458cb8bf1b..f52686e54c 100644 --- a/test/651-checker-long-simd-minmax/src/Main.java +++ b/test/651-checker-long-simd-minmax/src/Main.java @@ -23,7 +23,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMinLongLong loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // // Not directly supported for longs. @@ -48,7 +48,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMaxLongLong loop:<> outer_loop:none + /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // // Not directly supported for longs. diff --git a/test/651-checker-short-simd-minmax/src/Main.java b/test/651-checker-short-simd-minmax/src/Main.java index 5f10adab79..4300ca2951 100644 --- a/test/651-checker-short-simd-minmax/src/Main.java +++ b/test/651-checker-short-simd-minmax/src/Main.java @@ -23,7 +23,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMinIntInt loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // @@ -54,7 +54,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMinIntInt loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // @@ -74,7 +74,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMaxIntInt loop:<> outer_loop:none + /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // @@ -105,7 +105,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMaxIntInt loop:<> outer_loop:none + /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // @@ -125,7 +125,7 @@ public class Main { /// CHECK-DAG: <> IntConstant 100 loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMinIntInt loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // diff --git a/test/661-checker-simd-reduc/src/Main.java b/test/661-checker-simd-reduc/src/Main.java index 3a0a0495c4..fcd50a6d15 100644 --- a/test/661-checker-simd-reduc/src/Main.java +++ b/test/661-checker-simd-reduc/src/Main.java @@ -378,7 +378,7 @@ public class Main { /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect [<>,<>] intrinsic:MathMinIntInt loop:<> outer_loop:none + /// CHECK-DAG: Min [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Return [<>] loop:none // @@ -438,7 +438,7 @@ public class Main { /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect [<>,<>] intrinsic:MathMaxIntInt loop:<> outer_loop:none + /// CHECK-DAG: Max [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Return [<>] loop:none // -- GitLab From 351df3e70521ebbe00ed6c7ac4ea25a0c26f4034 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Wed, 7 Mar 2018 11:54:57 -0800 Subject: [PATCH 032/749] Minor cleanup of MIN/MAX code. Rationale: Share the type dispatching code better. Bug: b/65164101 Test: test-art-host,target Change-Id: Ib06c915d570fd0a53f7734cdb316d2d16310db74 --- compiler/optimizing/code_generator_arm64.cc | 42 ++++++-------- compiler/optimizing/code_generator_arm64.h | 3 +- .../optimizing/code_generator_arm_vixl.cc | 58 ++++++++----------- compiler/optimizing/code_generator_arm_vixl.h | 7 ++- compiler/optimizing/code_generator_mips.cc | 45 ++++++-------- compiler/optimizing/code_generator_mips.h | 3 +- compiler/optimizing/code_generator_mips64.cc | 38 +++++------- compiler/optimizing/code_generator_mips64.h | 3 +- compiler/optimizing/code_generator_x86.cc | 42 ++++++-------- compiler/optimizing/code_generator_x86.h | 3 +- compiler/optimizing/code_generator_x86_64.cc | 42 ++++++-------- compiler/optimizing/code_generator_x86_64.h | 3 +- 12 files changed, 127 insertions(+), 162 deletions(-) diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index b0ddd8e8c6..04da898859 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -5484,9 +5484,9 @@ static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* m } } -void InstructionCodeGeneratorARM64::GenerateMinMax(LocationSummary* locations, - bool is_min, - DataType::Type type) { +void InstructionCodeGeneratorARM64::GenerateMinMaxInt(LocationSummary* locations, + bool is_min, + DataType::Type type) { Location op1 = locations->InAt(0); Location op2 = locations->InAt(1); Location out = locations->Out(); @@ -5537,43 +5537,37 @@ void InstructionCodeGeneratorARM64::GenerateMinMaxFP(LocationSummary* locations, } } -void LocationsBuilderARM64::VisitMin(HMin* min) { - CreateMinMaxLocations(GetGraph()->GetAllocator(), min); -} - // TODO: integrate with HandleBinaryOp? -void InstructionCodeGeneratorARM64::VisitMin(HMin* min) { - switch (min->GetResultType()) { +void InstructionCodeGeneratorARM64::GenerateMinMax(HBinaryOperation* minmax, bool is_min) { + DataType::Type type = minmax->GetResultType(); + switch (type) { case DataType::Type::kInt32: case DataType::Type::kInt64: - GenerateMinMax(min->GetLocations(), /*is_min*/ true, min->GetResultType()); + GenerateMinMaxInt(minmax->GetLocations(), is_min, type); break; case DataType::Type::kFloat32: case DataType::Type::kFloat64: - GenerateMinMaxFP(min->GetLocations(), /*is_min*/ true, min->GetResultType()); + GenerateMinMaxFP(minmax->GetLocations(), is_min, type); break; default: - LOG(FATAL) << "Unexpected type for HMin " << min->GetResultType(); + LOG(FATAL) << "Unexpected type for HMinMax " << type; } } +void LocationsBuilderARM64::VisitMin(HMin* min) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), min); +} + +void InstructionCodeGeneratorARM64::VisitMin(HMin* min) { + GenerateMinMax(min, /*is_min*/ true); +} + void LocationsBuilderARM64::VisitMax(HMax* max) { CreateMinMaxLocations(GetGraph()->GetAllocator(), max); } void InstructionCodeGeneratorARM64::VisitMax(HMax* max) { - switch (max->GetResultType()) { - case DataType::Type::kInt32: - case DataType::Type::kInt64: - GenerateMinMax(max->GetLocations(), /*is_min*/ false, max->GetResultType()); - break; - case DataType::Type::kFloat32: - case DataType::Type::kFloat64: - GenerateMinMaxFP(max->GetLocations(), /*is_min*/ false, max->GetResultType()); - break; - default: - LOG(FATAL) << "Unexpected type for HMax " << max->GetResultType(); - } + GenerateMinMax(max, /*is_min*/ false); } void LocationsBuilderARM64::VisitAbs(HAbs* abs) { diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 70f5500016..06cd540d0a 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -273,8 +273,9 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); void HandleCondition(HCondition* instruction); - void GenerateMinMax(LocationSummary* locations, bool is_min, DataType::Type type); + void GenerateMinMaxInt(LocationSummary* locations, bool is_min, DataType::Type type); void GenerateMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type); + void GenerateMinMax(HBinaryOperation* minmax, bool is_min); // Generate a heap reference load using one register `out`: // diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 4fef027e6d..8222fc0a28 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -4719,7 +4719,7 @@ static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* m } } -void InstructionCodeGeneratorARMVIXL::GenerateMinMax(LocationSummary* locations, bool is_min) { +void InstructionCodeGeneratorARMVIXL::GenerateMinMaxInt(LocationSummary* locations, bool is_min) { Location op1_loc = locations->InAt(0); Location op2_loc = locations->InAt(1); Location out_loc = locations->Out(); @@ -4780,8 +4780,8 @@ void InstructionCodeGeneratorARMVIXL::GenerateMinMaxLong(LocationSummary* locati } } -void InstructionCodeGeneratorARMVIXL::GenerateMinMaxFloat(HInstruction* min_max, bool is_min) { - LocationSummary* locations = min_max->GetLocations(); +void InstructionCodeGeneratorARMVIXL::GenerateMinMaxFloat(HInstruction* minmax, bool is_min) { + LocationSummary* locations = minmax->GetLocations(); Location op1_loc = locations->InAt(0); Location op2_loc = locations->InAt(1); Location out_loc = locations->Out(); @@ -4800,7 +4800,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateMinMaxFloat(HInstruction* min_max, const vixl32::Register temp1 = temps.Acquire(); vixl32::Register temp2 = RegisterFrom(locations->GetTemp(0)); vixl32::Label nan, done; - vixl32::Label* final_label = codegen_->GetFinalLabel(min_max, &done); + vixl32::Label* final_label = codegen_->GetFinalLabel(minmax, &done); DCHECK(op1.Is(out)); @@ -4841,8 +4841,8 @@ void InstructionCodeGeneratorARMVIXL::GenerateMinMaxFloat(HInstruction* min_max, } } -void InstructionCodeGeneratorARMVIXL::GenerateMinMaxDouble(HInstruction* min_max, bool is_min) { - LocationSummary* locations = min_max->GetLocations(); +void InstructionCodeGeneratorARMVIXL::GenerateMinMaxDouble(HInstruction* minmax, bool is_min) { + LocationSummary* locations = minmax->GetLocations(); Location op1_loc = locations->InAt(0); Location op2_loc = locations->InAt(1); Location out_loc = locations->Out(); @@ -4857,7 +4857,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateMinMaxDouble(HInstruction* min_max vixl32::DRegister op2 = DRegisterFrom(op2_loc); vixl32::DRegister out = DRegisterFrom(out_loc); vixl32::Label handle_nan_eq, done; - vixl32::Label* final_label = codegen_->GetFinalLabel(min_max, &done); + vixl32::Label* final_label = codegen_->GetFinalLabel(minmax, &done); DCHECK(op1.Is(out)); @@ -4892,50 +4892,40 @@ void InstructionCodeGeneratorARMVIXL::GenerateMinMaxDouble(HInstruction* min_max } } -void LocationsBuilderARMVIXL::VisitMin(HMin* min) { - CreateMinMaxLocations(GetGraph()->GetAllocator(), min); -} - -void InstructionCodeGeneratorARMVIXL::VisitMin(HMin* min) { - switch (min->GetResultType()) { +void InstructionCodeGeneratorARMVIXL::GenerateMinMax(HBinaryOperation* minmax, bool is_min) { + DataType::Type type = minmax->GetResultType(); + switch (type) { case DataType::Type::kInt32: - GenerateMinMax(min->GetLocations(), /*is_min*/ true); + GenerateMinMaxInt(minmax->GetLocations(), is_min); break; case DataType::Type::kInt64: - GenerateMinMaxLong(min->GetLocations(), /*is_min*/ true); + GenerateMinMaxLong(minmax->GetLocations(), is_min); break; case DataType::Type::kFloat32: - GenerateMinMaxFloat(min, /*is_min*/ true); + GenerateMinMaxFloat(minmax, is_min); break; case DataType::Type::kFloat64: - GenerateMinMaxDouble(min, /*is_min*/ true); + GenerateMinMaxDouble(minmax, is_min); break; default: - LOG(FATAL) << "Unexpected type for HMin " << min->GetResultType(); + LOG(FATAL) << "Unexpected type for HMinMax " << type; } } +void LocationsBuilderARMVIXL::VisitMin(HMin* min) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), min); +} + +void InstructionCodeGeneratorARMVIXL::VisitMin(HMin* min) { + GenerateMinMax(min, /*is_min*/ true); +} + void LocationsBuilderARMVIXL::VisitMax(HMax* max) { CreateMinMaxLocations(GetGraph()->GetAllocator(), max); } void InstructionCodeGeneratorARMVIXL::VisitMax(HMax* max) { - switch (max->GetResultType()) { - case DataType::Type::kInt32: - GenerateMinMax(max->GetLocations(), /*is_min*/ false); - break; - case DataType::Type::kInt64: - GenerateMinMaxLong(max->GetLocations(), /*is_min*/ false); - break; - case DataType::Type::kFloat32: - GenerateMinMaxFloat(max, /*is_min*/ false); - break; - case DataType::Type::kFloat64: - GenerateMinMaxDouble(max, /*is_min*/ false); - break; - default: - LOG(FATAL) << "Unexpected type for HMax " << max->GetResultType(); - } + GenerateMinMax(max, /*is_min*/ false); } void LocationsBuilderARMVIXL::VisitAbs(HAbs* abs) { diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 726a2f9030..8e6bc0627a 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -349,10 +349,11 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { bool value_can_be_null); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); - void GenerateMinMax(LocationSummary* locations, bool is_min); + void GenerateMinMaxInt(LocationSummary* locations, bool is_min); void GenerateMinMaxLong(LocationSummary* locations, bool is_min); - void GenerateMinMaxFloat(HInstruction* min_max, bool is_min); - void GenerateMinMaxDouble(HInstruction* min_max, bool is_min); + void GenerateMinMaxFloat(HInstruction* minmax, bool is_min); + void GenerateMinMaxDouble(HInstruction* minmax, bool is_min); + void GenerateMinMax(HBinaryOperation* minmax, bool is_min); // Generate a heap reference load using one register `out`: // diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index ae42bbcc70..825b2377c8 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -8799,10 +8799,10 @@ static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* m } } -void InstructionCodeGeneratorMIPS::GenerateMinMax(LocationSummary* locations, - bool is_min, - bool isR6, - DataType::Type type) { +void InstructionCodeGeneratorMIPS::GenerateMinMaxInt(LocationSummary* locations, + bool is_min, + bool isR6, + DataType::Type type) { if (isR6) { // Some architectures, such as ARM and MIPS (prior to r6), have a // conditional move instruction which only changes the target @@ -9130,44 +9130,37 @@ void InstructionCodeGeneratorMIPS::GenerateMinMaxFP(LocationSummary* locations, } } -void LocationsBuilderMIPS::VisitMin(HMin* min) { - CreateMinMaxLocations(GetGraph()->GetAllocator(), min); -} - -void InstructionCodeGeneratorMIPS::VisitMin(HMin* min) { +void InstructionCodeGeneratorMIPS::GenerateMinMax(HBinaryOperation* minmax, bool is_min) { bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); - switch (min->GetResultType()) { + DataType::Type type = minmax->GetResultType(); + switch (type) { case DataType::Type::kInt32: case DataType::Type::kInt64: - GenerateMinMax(min->GetLocations(), /*is_min*/ true, isR6, min->GetResultType()); + GenerateMinMaxInt(minmax->GetLocations(), is_min, isR6, type); break; case DataType::Type::kFloat32: case DataType::Type::kFloat64: - GenerateMinMaxFP(min->GetLocations(), /*is_min*/ true, isR6, min->GetResultType()); + GenerateMinMaxFP(minmax->GetLocations(), is_min, isR6, type); break; default: - LOG(FATAL) << "Unexpected type for HMin " << min->GetResultType(); + LOG(FATAL) << "Unexpected type for HMinMax " << type; } } +void LocationsBuilderMIPS::VisitMin(HMin* min) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), min); +} + +void InstructionCodeGeneratorMIPS::VisitMin(HMin* min) { + GenerateMinMax(min, /*is_min*/ true); +} + void LocationsBuilderMIPS::VisitMax(HMax* max) { CreateMinMaxLocations(GetGraph()->GetAllocator(), max); } void InstructionCodeGeneratorMIPS::VisitMax(HMax* max) { - bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); - switch (max->GetResultType()) { - case DataType::Type::kInt32: - case DataType::Type::kInt64: - GenerateMinMax(max->GetLocations(), /*is_min*/ false, isR6, max->GetResultType()); - break; - case DataType::Type::kFloat32: - case DataType::Type::kFloat64: - GenerateMinMaxFP(max->GetLocations(), /*is_min*/ false, isR6, max->GetResultType()); - break; - default: - LOG(FATAL) << "Unexpected type for HMax " << max->GetResultType(); - } + GenerateMinMax(max, /*is_min*/ false); } void LocationsBuilderMIPS::VisitAbs(HAbs* abs) { diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index ae5fe5be19..b02a216aaf 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -246,8 +246,9 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator { bool value_can_be_null); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info, uint32_t dex_pc); - void GenerateMinMax(LocationSummary* locations, bool is_min, bool isR6, DataType::Type type); + void GenerateMinMaxInt(LocationSummary* locations, bool is_min, bool isR6, DataType::Type type); void GenerateMinMaxFP(LocationSummary* locations, bool is_min, bool isR6, DataType::Type type); + void GenerateMinMax(HBinaryOperation*, bool is_min); void GenerateAbsFP(LocationSummary* locations, DataType::Type type, bool isR2OrNewer, bool isR6); // Generate a heap reference load using one register `out`: diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 8031cca7cb..9bb1bfb596 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -6685,7 +6685,7 @@ static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* m } } -void InstructionCodeGeneratorMIPS64::GenerateMinMax(LocationSummary* locations, bool is_min) { +void InstructionCodeGeneratorMIPS64::GenerateMinMaxInt(LocationSummary* locations, bool is_min) { GpuRegister lhs = locations->InAt(0).AsRegister(); GpuRegister rhs = locations->InAt(1).AsRegister(); GpuRegister out = locations->Out().AsRegister(); @@ -6809,42 +6809,36 @@ void InstructionCodeGeneratorMIPS64::GenerateMinMaxFP(LocationSummary* locations __ Bind(&done); } -void LocationsBuilderMIPS64::VisitMin(HMin* min) { - CreateMinMaxLocations(GetGraph()->GetAllocator(), min); -} - -void InstructionCodeGeneratorMIPS64::VisitMin(HMin* min) { - switch (min->GetResultType()) { +void InstructionCodeGeneratorMIPS64::GenerateMinMax(HBinaryOperation* minmax, bool is_min) { + DataType::Type type = minmax->GetResultType(); + switch (type) { case DataType::Type::kInt32: case DataType::Type::kInt64: - GenerateMinMax(min->GetLocations(), /*is_min*/ true); + GenerateMinMaxInt(minmax->GetLocations(), is_min); break; case DataType::Type::kFloat32: case DataType::Type::kFloat64: - GenerateMinMaxFP(min->GetLocations(), /*is_min*/ true, min->GetResultType()); + GenerateMinMaxFP(minmax->GetLocations(), is_min, type); break; default: - LOG(FATAL) << "Unexpected type for HMin " << min->GetResultType(); + LOG(FATAL) << "Unexpected type for HMinMax " << type; } } +void LocationsBuilderMIPS64::VisitMin(HMin* min) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), min); +} + +void InstructionCodeGeneratorMIPS64::VisitMin(HMin* min) { + GenerateMinMax(min, /*is_min*/ true); +} + void LocationsBuilderMIPS64::VisitMax(HMax* max) { CreateMinMaxLocations(GetGraph()->GetAllocator(), max); } void InstructionCodeGeneratorMIPS64::VisitMax(HMax* max) { - switch (max->GetResultType()) { - case DataType::Type::kInt32: - case DataType::Type::kInt64: - GenerateMinMax(max->GetLocations(), /*is_min*/ false); - break; - case DataType::Type::kFloat32: - case DataType::Type::kFloat64: - GenerateMinMaxFP(max->GetLocations(), /*is_min*/ false, max->GetResultType()); - break; - default: - LOG(FATAL) << "Unexpected type for HMax " << max->GetResultType(); - } + GenerateMinMax(max, /*is_min*/ false); } void LocationsBuilderMIPS64::VisitAbs(HAbs* abs) { diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 5d925d5d5a..ef4386bf4d 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -242,8 +242,9 @@ class InstructionCodeGeneratorMIPS64 : public InstructionCodeGenerator { bool value_can_be_null); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); - void GenerateMinMax(LocationSummary* locations, bool is_min); + void GenerateMinMaxInt(LocationSummary* locations, bool is_min); void GenerateMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type); + void GenerateMinMax(HBinaryOperation* minmax, bool is_min); // Generate a heap reference load using one register `out`: // diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 536909aa1f..c887d9ecc8 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -3836,9 +3836,9 @@ static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* m } } -void InstructionCodeGeneratorX86::GenerateMinMax(LocationSummary* locations, - bool is_min, - DataType::Type type) { +void InstructionCodeGeneratorX86::GenerateMinMaxInt(LocationSummary* locations, + bool is_min, + DataType::Type type) { Location op1_loc = locations->InAt(0); Location op2_loc = locations->InAt(1); @@ -3978,42 +3978,36 @@ void InstructionCodeGeneratorX86::GenerateMinMaxFP(LocationSummary* locations, __ Bind(&done); } -void LocationsBuilderX86::VisitMin(HMin* min) { - CreateMinMaxLocations(GetGraph()->GetAllocator(), min); -} - -void InstructionCodeGeneratorX86::VisitMin(HMin* min) { - switch (min->GetResultType()) { +void InstructionCodeGeneratorX86::GenerateMinMax(HBinaryOperation* minmax, bool is_min) { + DataType::Type type = minmax->GetResultType(); + switch (type) { case DataType::Type::kInt32: case DataType::Type::kInt64: - GenerateMinMax(min->GetLocations(), /*is_min*/ true, min->GetResultType()); + GenerateMinMaxInt(minmax->GetLocations(), is_min, type); break; case DataType::Type::kFloat32: case DataType::Type::kFloat64: - GenerateMinMaxFP(min->GetLocations(), /*is_min*/ true, min->GetResultType()); + GenerateMinMaxFP(minmax->GetLocations(), is_min, type); break; default: - LOG(FATAL) << "Unexpected type for HMin " << min->GetResultType(); + LOG(FATAL) << "Unexpected type for HMinMax " << type; } } +void LocationsBuilderX86::VisitMin(HMin* min) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), min); +} + +void InstructionCodeGeneratorX86::VisitMin(HMin* min) { + GenerateMinMax(min, /*is_min*/ true); +} + void LocationsBuilderX86::VisitMax(HMax* max) { CreateMinMaxLocations(GetGraph()->GetAllocator(), max); } void InstructionCodeGeneratorX86::VisitMax(HMax* max) { - switch (max->GetResultType()) { - case DataType::Type::kInt32: - case DataType::Type::kInt64: - GenerateMinMax(max->GetLocations(), /*is_min*/ false, max->GetResultType()); - break; - case DataType::Type::kFloat32: - case DataType::Type::kFloat64: - GenerateMinMaxFP(max->GetLocations(), /*is_min*/ false, max->GetResultType()); - break; - default: - LOG(FATAL) << "Unexpected type for HMax " << max->GetResultType(); - } + GenerateMinMax(max, /*is_min*/ false); } void LocationsBuilderX86::VisitAbs(HAbs* abs) { diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 82496d12e5..d5abf2a5fa 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -225,8 +225,9 @@ class InstructionCodeGeneratorX86 : public InstructionCodeGenerator { void GenerateShlLong(const Location& loc, int shift); void GenerateShrLong(const Location& loc, int shift); void GenerateUShrLong(const Location& loc, int shift); - void GenerateMinMax(LocationSummary* locations, bool is_min, DataType::Type type); + void GenerateMinMaxInt(LocationSummary* locations, bool is_min, DataType::Type type); void GenerateMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type); + void GenerateMinMax(HBinaryOperation* minmax, bool is_min); void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info, diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index bb1fbc5290..d42990d51f 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -3843,9 +3843,9 @@ static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* m } } -void InstructionCodeGeneratorX86_64::GenerateMinMax(LocationSummary* locations, - bool is_min, - DataType::Type type) { +void InstructionCodeGeneratorX86_64::GenerateMinMaxInt(LocationSummary* locations, + bool is_min, + DataType::Type type) { Location op1_loc = locations->InAt(0); Location op2_loc = locations->InAt(1); @@ -3960,42 +3960,36 @@ void InstructionCodeGeneratorX86_64::GenerateMinMaxFP(LocationSummary* locations __ Bind(&done); } -void LocationsBuilderX86_64::VisitMin(HMin* min) { - CreateMinMaxLocations(GetGraph()->GetAllocator(), min); -} - -void InstructionCodeGeneratorX86_64::VisitMin(HMin* min) { - switch (min->GetResultType()) { +void InstructionCodeGeneratorX86_64::GenerateMinMax(HBinaryOperation* minmax, bool is_min) { + DataType::Type type = minmax->GetResultType(); + switch (type) { case DataType::Type::kInt32: case DataType::Type::kInt64: - GenerateMinMax(min->GetLocations(), /*is_min*/ true, min->GetResultType()); + GenerateMinMaxInt(minmax->GetLocations(), is_min, type); break; case DataType::Type::kFloat32: case DataType::Type::kFloat64: - GenerateMinMaxFP(min->GetLocations(), /*is_min*/ true, min->GetResultType()); + GenerateMinMaxFP(minmax->GetLocations(), is_min, type); break; default: - LOG(FATAL) << "Unexpected type for HMin " << min->GetResultType(); + LOG(FATAL) << "Unexpected type for HMinMax " << type; } } +void LocationsBuilderX86_64::VisitMin(HMin* min) { + CreateMinMaxLocations(GetGraph()->GetAllocator(), min); +} + +void InstructionCodeGeneratorX86_64::VisitMin(HMin* min) { + GenerateMinMax(min, /*is_min*/ true); +} + void LocationsBuilderX86_64::VisitMax(HMax* max) { CreateMinMaxLocations(GetGraph()->GetAllocator(), max); } void InstructionCodeGeneratorX86_64::VisitMax(HMax* max) { - switch (max->GetResultType()) { - case DataType::Type::kInt32: - case DataType::Type::kInt64: - GenerateMinMax(max->GetLocations(), /*is_min*/ false, max->GetResultType()); - break; - case DataType::Type::kFloat32: - case DataType::Type::kFloat64: - GenerateMinMaxFP(max->GetLocations(), /*is_min*/ false, max->GetResultType()); - break; - default: - LOG(FATAL) << "Unexpected type for HMax " << max->GetResultType(); - } + GenerateMinMax(max, /*is_min*/ false); } void LocationsBuilderX86_64::VisitAbs(HAbs* abs) { diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 933afdab26..7fc36a9d66 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -222,8 +222,9 @@ class InstructionCodeGeneratorX86_64 : public InstructionCodeGenerator { bool value_can_be_null); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); - void GenerateMinMax(LocationSummary* locations, bool is_min, DataType::Type type); + void GenerateMinMaxInt(LocationSummary* locations, bool is_min, DataType::Type type); void GenerateMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type); + void GenerateMinMax(HBinaryOperation* minmax, bool is_min); // Generate a heap reference load using one register `out`: // -- GitLab From b1f23f99cbb73a00c4c333d479b40893ceaeff35 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Wed, 7 Mar 2018 14:10:49 -0800 Subject: [PATCH 033/749] Ignore vdex files for addr2line. Bug: 74349704 Test: Ran the 141 art test with a patch to cause a crash. Observed no addr2line Test: errors, and all non-vdex mapped lines properly have line information. Change-Id: I82a9f351f33353f3a17ed03bfaa8f28a92e75ee2 --- runtime/native_stack_dump.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc index 530c266975..c26c26e544 100644 --- a/runtime/native_stack_dump.cc +++ b/runtime/native_stack_dump.cc @@ -39,6 +39,7 @@ #include #include "android-base/stringprintf.h" +#include "android-base/strings.h" #include "arch/instruction_set.h" #include "base/aborting.h" @@ -217,8 +218,10 @@ static void Addr2line(const std::string& map_src, std::unique_ptr* pipe /* inout */) { DCHECK(pipe != nullptr); - if (map_src == "[vdso]") { - // Special-case this, our setup has problems with this. + if (map_src == "[vdso]" || android::base::EndsWith(map_src, ".vdex")) { + // addr2line will not work on the vdso. + // vdex files are special frames injected for the interpreter + // so they don't have any line number information available. return; } -- GitLab From 02eebcf01abc6df5ea861a5c688f5836f70abaf2 Mon Sep 17 00:00:00 2001 From: Artem Serov Date: Wed, 13 Dec 2017 19:48:31 +0000 Subject: [PATCH 034/749] ART: Implement loop peeling/unrolling routines. Implement loop peeling/unrolling routines using SuperblockCloner. Fixes bug b/74198030 and provides tests for it. Bug: b/74198030 Test: superblock_cloner_test.cc, loop_optimization_test.cc. Change-Id: Id0d9a91575c88f8e45186441b14e903b89b007dd --- compiler/optimizing/loop_optimization_test.cc | 5 +- compiler/optimizing/superblock_cloner.cc | 351 ++++++++++- compiler/optimizing/superblock_cloner.h | 108 +++- compiler/optimizing/superblock_cloner_test.cc | 557 +++++++++++++++++- 4 files changed, 975 insertions(+), 46 deletions(-) diff --git a/compiler/optimizing/loop_optimization_test.cc b/compiler/optimizing/loop_optimization_test.cc index db8368986c..c21bd65d97 100644 --- a/compiler/optimizing/loop_optimization_test.cc +++ b/compiler/optimizing/loop_optimization_test.cc @@ -227,11 +227,14 @@ TEST_F(LoopOptimizationTest, SimplifyLoopReoderPredecessors) { graph_->ClearDominanceInformation(); graph_->BuildDominatorTree(); + // BuildDominatorTree inserts a block beetween loop header and entry block. + EXPECT_EQ(header->GetPredecessors()[0]->GetSinglePredecessor(), entry_block_); + // Check that after optimizations in BuildDominatorTree()/SimplifyCFG() phi inputs // are still mapped correctly to the block predecessors. for (size_t i = 0, e = phi->InputCount(); i < e; i++) { HInstruction* input = phi->InputAt(i); - ASSERT_TRUE(input->GetBlock()->Dominates(header->GetPredecessors()[i])); + EXPECT_TRUE(input->GetBlock()->Dominates(header->GetPredecessors()[i])); } } diff --git a/compiler/optimizing/superblock_cloner.cc b/compiler/optimizing/superblock_cloner.cc index a7c23bef7e..04942f9a4a 100644 --- a/compiler/optimizing/superblock_cloner.cc +++ b/compiler/optimizing/superblock_cloner.cc @@ -28,6 +28,11 @@ using HInstructionMap = SuperblockCloner::HInstructionMap; using HBasicBlockSet = SuperblockCloner::HBasicBlockSet; using HEdgeSet = SuperblockCloner::HEdgeSet; +// When doing peeling we can choose whether to keep original loop (made of original basic blocks) +// and form a peeled iteration of the copy blocks (preserve the header) or transfer original loop +// blocks to the peeled iteration and create new loop from the copy blocks. Similar for unrolling. +static const bool kPeelUnrollPreserveHeader = true; + void HEdge::Dump(std::ostream& stream) const { stream << "(" << from_ << "->" << to_ << ")"; } @@ -70,20 +75,18 @@ static bool ArePhiInputsTheSame(const HPhi* phi) { return true; } -// Returns a common predecessor of loop1 and loop2 in the loop tree or nullptr if it is the whole -// graph. -static HLoopInformation* FindCommonLoop(HLoopInformation* loop1, HLoopInformation* loop2) { - if (loop1 != nullptr || loop2 != nullptr) { - return nullptr; +// Returns whether two Edge sets are equal (ArenaHashSet doesn't have "Equal" method). +static bool EdgeHashSetsEqual(const HEdgeSet* set1, const HEdgeSet* set2) { + if (set1->Size() != set2->Size()) { + return false; } - if (loop1->IsIn(*loop2)) { - return loop2; - } else if (loop2->IsIn(*loop1)) { - return loop1; + for (auto e : *set1) { + if (set2->Find(e) == set2->end()) { + return false; + } } - HBasicBlock* block = CommonDominator::ForPair(loop1->GetHeader(), loop2->GetHeader()); - return block->GetLoopInformation(); + return true; } // Calls HGraph::OrderLoopHeaderPredecessors for each loop in the graph. @@ -95,6 +98,21 @@ static void OrderLoopsHeadersPredecessors(HGraph* graph) { } } +// Performs DFS on the subgraph (specified by 'bb_set') starting from the specified block; while +// traversing the function removes basic blocks from the bb_set (instead of traditional DFS +// 'marking'). So what is left in the 'bb_set' after the traversal is not reachable from the start +// block. +static void TraverseSubgraphForConnectivity(HBasicBlock* block, HBasicBlockSet* bb_set) { + DCHECK(bb_set->IsBitSet(block->GetBlockId())); + bb_set->ClearBit(block->GetBlockId()); + + for (HBasicBlock* succ : block->GetSuccessors()) { + if (bb_set->IsBitSet(succ->GetBlockId())) { + TraverseSubgraphForConnectivity(succ, bb_set); + } + } +} + // // Helpers for CloneBasicBlock. // @@ -268,7 +286,6 @@ void SuperblockCloner::FindBackEdgesLocal(HBasicBlock* entry_block, ArenaBitVect } void SuperblockCloner::RecalculateBackEdgesInfo(ArenaBitVector* outer_loop_bb_set) { - // TODO: DCHECK that after the transformation the graph is connected. HBasicBlock* block_entry = nullptr; if (outer_loop_ == nullptr) { @@ -424,6 +441,11 @@ void SuperblockCloner::FindAndSetLocalAreaForAdjustments() { outer_loop_ = nullptr; break; } + if (outer_loop_ == nullptr) { + // We should not use the initial outer_loop_ value 'nullptr' when finding the most outer + // common loop. + outer_loop_ = loop_exit_loop_info; + } outer_loop_ = FindCommonLoop(outer_loop_, loop_exit_loop_info); } @@ -507,6 +529,34 @@ void SuperblockCloner::ResolveDataFlow() { // Debug and logging methods. // +// Debug function to dump graph' BasicBlocks info. +void DumpBB(HGraph* graph) { + for (HBasicBlock* bb : graph->GetBlocks()) { + if (bb == nullptr) { + continue; + } + std::cout << bb->GetBlockId(); + std::cout << " <- "; + for (HBasicBlock* pred : bb->GetPredecessors()) { + std::cout << pred->GetBlockId() << " "; + } + std::cout << " -> "; + for (HBasicBlock* succ : bb->GetSuccessors()) { + std::cout << succ->GetBlockId() << " "; + } + + if (bb->GetDominator()) { + std::cout << " dom " << bb->GetDominator()->GetBlockId(); + } + + if (bb->GetLoopInformation()) { + std::cout << "\tloop: " << bb->GetLoopInformation()->GetHeader()->GetBlockId(); + } + + std::cout << std::endl; + } +} + void SuperblockCloner::CheckInstructionInputsRemapping(HInstruction* orig_instr) { DCHECK(!orig_instr->IsPhi()); HInstruction* copy_instr = GetInstrCopy(orig_instr); @@ -542,6 +592,82 @@ void SuperblockCloner::CheckInstructionInputsRemapping(HInstruction* orig_instr) } } +bool SuperblockCloner::CheckRemappingInfoIsValid() { + for (HEdge edge : *remap_orig_internal_) { + if (!IsEdgeValid(edge, graph_) || + !IsInOrigBBSet(edge.GetFrom()) || + !IsInOrigBBSet(edge.GetTo())) { + return false; + } + } + + for (auto edge : *remap_copy_internal_) { + if (!IsEdgeValid(edge, graph_) || + !IsInOrigBBSet(edge.GetFrom()) || + !IsInOrigBBSet(edge.GetTo())) { + return false; + } + } + + for (auto edge : *remap_incoming_) { + if (!IsEdgeValid(edge, graph_) || + IsInOrigBBSet(edge.GetFrom()) || + !IsInOrigBBSet(edge.GetTo())) { + return false; + } + } + + return true; +} + +void SuperblockCloner::VerifyGraph() { + for (auto it : *hir_map_) { + HInstruction* orig_instr = it.first; + HInstruction* copy_instr = it.second; + if (!orig_instr->IsPhi() && !orig_instr->IsSuspendCheck()) { + DCHECK(it.first->GetBlock() != nullptr); + } + if (!copy_instr->IsPhi() && !copy_instr->IsSuspendCheck()) { + DCHECK(it.second->GetBlock() != nullptr); + } + } + + GraphChecker checker(graph_); + checker.Run(); + if (!checker.IsValid()) { + for (const std::string& error : checker.GetErrors()) { + std::cout << error << std::endl; + } + LOG(FATAL) << "GraphChecker failed: superblock cloner\n"; + } +} + +void DumpBBSet(const ArenaBitVector* set) { + for (uint32_t idx : set->Indexes()) { + std::cout << idx << "\n"; + } +} + +void SuperblockCloner::DumpInputSets() { + std::cout << graph_->PrettyMethod() << "\n"; + std::cout << "orig_bb_set:\n"; + for (uint32_t idx : orig_bb_set_.Indexes()) { + std::cout << idx << "\n"; + } + std::cout << "remap_orig_internal:\n"; + for (HEdge e : *remap_orig_internal_) { + std::cout << e << "\n"; + } + std::cout << "remap_copy_internal:\n"; + for (auto e : *remap_copy_internal_) { + std::cout << e << "\n"; + } + std::cout << "remap_incoming:\n"; + for (auto e : *remap_incoming_) { + std::cout << e << "\n"; + } +} + // // Public methods. // @@ -569,6 +695,7 @@ void SuperblockCloner::SetSuccessorRemappingInfo(const HEdgeSet* remap_orig_inte remap_orig_internal_ = remap_orig_internal; remap_copy_internal_ = remap_copy_internal; remap_incoming_ = remap_incoming; + DCHECK(CheckRemappingInfoIsValid()); } bool SuperblockCloner::IsSubgraphClonable() const { @@ -602,6 +729,63 @@ bool SuperblockCloner::IsSubgraphClonable() const { return true; } +bool SuperblockCloner::IsFastCase() const { + // Check that loop unrolling/loop peeling is being conducted. + // Check that all the basic blocks belong to the same loop. + bool flag = false; + HLoopInformation* common_loop_info = nullptr; + for (uint32_t idx : orig_bb_set_.Indexes()) { + HBasicBlock* block = GetBlockById(idx); + HLoopInformation* block_loop_info = block->GetLoopInformation(); + if (!flag) { + common_loop_info = block_loop_info; + } else { + if (block_loop_info != common_loop_info) { + return false; + } + } + } + + // Check that orig_bb_set_ corresponds to loop peeling/unrolling. + if (common_loop_info == nullptr || !orig_bb_set_.SameBitsSet(&common_loop_info->GetBlocks())) { + return false; + } + + bool peeling_or_unrolling = false; + HEdgeSet remap_orig_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + HEdgeSet remap_copy_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + HEdgeSet remap_incoming(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + + + // Check whether remapping info corresponds to loop unrolling. + CollectRemappingInfoForPeelUnroll(/* to_unroll*/ true, + common_loop_info, + &remap_orig_internal, + &remap_copy_internal, + &remap_incoming); + + peeling_or_unrolling |= EdgeHashSetsEqual(&remap_orig_internal, remap_orig_internal_) && + EdgeHashSetsEqual(&remap_copy_internal, remap_copy_internal_) && + EdgeHashSetsEqual(&remap_incoming, remap_incoming_); + + remap_orig_internal.Clear(); + remap_copy_internal.Clear(); + remap_incoming.Clear(); + + // Check whether remapping info corresponds to loop peeling. + CollectRemappingInfoForPeelUnroll(/* to_unroll*/ false, + common_loop_info, + &remap_orig_internal, + &remap_copy_internal, + &remap_incoming); + + peeling_or_unrolling |= EdgeHashSetsEqual(&remap_orig_internal, remap_orig_internal_) && + EdgeHashSetsEqual(&remap_copy_internal, remap_copy_internal_) && + EdgeHashSetsEqual(&remap_incoming, remap_incoming_); + + return peeling_or_unrolling; +} + void SuperblockCloner::Run() { DCHECK(bb_map_ != nullptr); DCHECK(hir_map_ != nullptr); @@ -609,6 +793,11 @@ void SuperblockCloner::Run() { remap_copy_internal_ != nullptr && remap_incoming_ != nullptr); DCHECK(IsSubgraphClonable()); + DCHECK(IsFastCase()); + + if (kSuperblockClonerLogging) { + DumpInputSets(); + } // Find an area in the graph for which control flow information should be adjusted. FindAndSetLocalAreaForAdjustments(); @@ -618,6 +807,19 @@ void SuperblockCloner::Run() { // Connect the blocks together/remap successors and fix phis which are directly affected my the // remapping. RemapEdgesSuccessors(); + + // Check that the subgraph is connected. + if (kIsDebugBuild) { + HBasicBlockSet work_set(arena_, orig_bb_set_.GetSizeOf(), true, kArenaAllocSuperblockCloner); + + // Add original and copy blocks of the subgraph to the work set. + for (auto iter : *bb_map_) { + work_set.SetBit(iter.first->GetBlockId()); // Original block. + work_set.SetBit(iter.second->GetBlockId()); // Copy block. + } + CHECK(IsSubgraphConnected(&work_set, graph_)); + } + // Recalculate dominance and backedge information which is required by the next stage. AdjustControlFlowInfo(); // Fix data flow of the graph. @@ -650,6 +852,10 @@ void SuperblockCloner::CleanUp() { } } } + + if (kSuperblockClonerVerify) { + VerifyGraph(); + } } HBasicBlock* SuperblockCloner::CloneBasicBlock(const HBasicBlock* orig_block) { @@ -701,4 +907,125 @@ void SuperblockCloner::CloneBasicBlocks() { } } +// +// Stand-alone methods. +// + +void CollectRemappingInfoForPeelUnroll(bool to_unroll, + HLoopInformation* loop_info, + HEdgeSet* remap_orig_internal, + HEdgeSet* remap_copy_internal, + HEdgeSet* remap_incoming) { + DCHECK(loop_info != nullptr); + HBasicBlock* loop_header = loop_info->GetHeader(); + // Set up remap_orig_internal edges set - set is empty. + // Set up remap_copy_internal edges set. + for (HBasicBlock* back_edge_block : loop_info->GetBackEdges()) { + HEdge e = HEdge(back_edge_block, loop_header); + if (to_unroll) { + remap_orig_internal->Insert(e); + remap_copy_internal->Insert(e); + } else { + if (kPeelUnrollPreserveHeader) { + remap_copy_internal->Insert(e); + } else { + remap_orig_internal->Insert(e); + } + } + } + + // Set up remap_incoming edges set. + if (to_unroll != kPeelUnrollPreserveHeader) { + remap_incoming->Insert(HEdge(loop_info->GetPreHeader(), loop_header)); + } +} + +bool IsSubgraphConnected(SuperblockCloner::HBasicBlockSet* work_set, HGraph* graph) { + ArenaVector entry_blocks( + graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + + // Find subgraph entry blocks. + for (uint32_t orig_block_id : work_set->Indexes()) { + HBasicBlock* block = graph->GetBlocks()[orig_block_id]; + for (HBasicBlock* pred : block->GetPredecessors()) { + if (!work_set->IsBitSet(pred->GetBlockId())) { + entry_blocks.push_back(block); + break; + } + } + } + + for (HBasicBlock* entry_block : entry_blocks) { + if (work_set->IsBitSet(entry_block->GetBlockId())) { + TraverseSubgraphForConnectivity(entry_block, work_set); + } + } + + // Return whether there are unvisited - unreachable - blocks. + return work_set->NumSetBits() == 0; +} + +HLoopInformation* FindCommonLoop(HLoopInformation* loop1, HLoopInformation* loop2) { + if (loop1 == nullptr || loop2 == nullptr) { + return nullptr; + } + + if (loop1->IsIn(*loop2)) { + return loop2; + } + + HLoopInformation* current = loop1; + while (current != nullptr && !loop2->IsIn(*current)) { + current = current->GetPreHeader()->GetLoopInformation(); + } + + return current; +} + +bool PeelUnrollHelper::IsLoopClonable(HLoopInformation* loop_info) { + PeelUnrollHelper helper(loop_info, nullptr, nullptr); + return helper.IsLoopClonable(); +} + +HBasicBlock* PeelUnrollHelper::DoPeelUnrollImpl(bool to_unroll) { + // For now do peeling only for natural loops. + DCHECK(!loop_info_->IsIrreducible()); + + HBasicBlock* loop_header = loop_info_->GetHeader(); + HGraph* graph = loop_header->GetGraph(); + ArenaAllocator allocator(graph->GetAllocator()->GetArenaPool()); + + HEdgeSet remap_orig_internal(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + HEdgeSet remap_copy_internal(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + HEdgeSet remap_incoming(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + + CollectRemappingInfoForPeelUnroll(to_unroll, + loop_info_, + &remap_orig_internal, + &remap_copy_internal, + &remap_incoming); + + cloner_.SetSuccessorRemappingInfo(&remap_orig_internal, &remap_copy_internal, &remap_incoming); + cloner_.Run(); + cloner_.CleanUp(); + + return kPeelUnrollPreserveHeader ? loop_header : cloner_.GetBlockCopy(loop_header); +} + +PeelUnrollSimpleHelper::PeelUnrollSimpleHelper(HLoopInformation* info) + : bb_map_(std::less(), + info->GetHeader()->GetGraph()->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)), + hir_map_(std::less(), + info->GetHeader()->GetGraph()->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)), + helper_(info, &bb_map_, &hir_map_) {} + } // namespace art + +namespace std { + +ostream& operator<<(ostream& os, const art::HEdge& e) { + e.Dump(os); + return os; +} + +} // namespace std diff --git a/compiler/optimizing/superblock_cloner.h b/compiler/optimizing/superblock_cloner.h index 23de692673..19c9dd471c 100644 --- a/compiler/optimizing/superblock_cloner.h +++ b/compiler/optimizing/superblock_cloner.h @@ -152,6 +152,15 @@ class SuperblockCloner : public ValueObject { // TODO: Start from small range of graph patterns then extend it. bool IsSubgraphClonable() const; + // Returns whether selected subgraph satisfies the criteria for fast data flow resolution + // when iterative DF algorithm is not required and dominators/instructions inputs can be + // trivially adjusted. + // + // TODO: formally describe the criteria. + // + // Loop peeling and unrolling satisfy the criteria. + bool IsFastCase() const; + // Runs the copy algorithm according to the description. void Run(); @@ -202,11 +211,17 @@ class SuperblockCloner : public ValueObject { return IsInOrigBBSet(block->GetBlockId()); } + // Returns the area (the most outer loop) in the graph for which control flow (back edges, loops, + // dominators) needs to be adjusted. + HLoopInformation* GetRegionToBeAdjusted() const { + return outer_loop_; + } + private: // Fills the 'exits' vector with the subgraph exits. void SearchForSubgraphExits(ArenaVector* exits); - // Finds and records information about the area in the graph for which control-flow (back edges, + // Finds and records information about the area in the graph for which control flow (back edges, // loops, dominators) needs to be adjusted. void FindAndSetLocalAreaForAdjustments(); @@ -217,7 +232,7 @@ class SuperblockCloner : public ValueObject { // phis' nor instructions' inputs values are resolved. void RemapEdgesSuccessors(); - // Adjusts control-flow (back edges, loops, dominators) for the local area defined by + // Adjusts control flow (back edges, loops, dominators) for the local area defined by // FindAndSetLocalAreaForAdjustments. void AdjustControlFlowInfo(); @@ -272,6 +287,9 @@ class SuperblockCloner : public ValueObject { // Debug and logging methods. // void CheckInstructionInputsRemapping(HInstruction* orig_instr); + bool CheckRemappingInfoIsValid(); + void VerifyGraph(); + void DumpInputSets(); HBasicBlock* GetBlockById(uint32_t block_id) const { DCHECK(block_id < graph_->GetBlocks().size()); @@ -295,15 +313,94 @@ class SuperblockCloner : public ValueObject { HBasicBlockMap* bb_map_; // Correspondence map for instructions: (original HInstruction, copy HInstruction). HInstructionMap* hir_map_; - // Area in the graph for which control-flow (back edges, loops, dominators) needs to be adjusted. + // Area in the graph for which control flow (back edges, loops, dominators) needs to be adjusted. HLoopInformation* outer_loop_; HBasicBlockSet outer_loop_bb_set_; ART_FRIEND_TEST(SuperblockClonerTest, AdjustControlFlowInfo); + ART_FRIEND_TEST(SuperblockClonerTest, IsGraphConnected); DISALLOW_COPY_AND_ASSIGN(SuperblockCloner); }; +// Helper class to perform loop peeling/unrolling. +// +// This helper should be used when correspondence map between original and copied +// basic blocks/instructions are demanded. +class PeelUnrollHelper : public ValueObject { + public: + explicit PeelUnrollHelper(HLoopInformation* info, + SuperblockCloner::HBasicBlockMap* bb_map, + SuperblockCloner::HInstructionMap* hir_map) : + loop_info_(info), + cloner_(info->GetHeader()->GetGraph(), &info->GetBlocks(), bb_map, hir_map) { + // For now do peeling/unrolling only for natural loops. + DCHECK(!info->IsIrreducible()); + } + + // Returns whether the loop can be peeled/unrolled (static function). + static bool IsLoopClonable(HLoopInformation* loop_info); + + // Returns whether the loop can be peeled/unrolled. + bool IsLoopClonable() const { return cloner_.IsSubgraphClonable(); } + + HBasicBlock* DoPeeling() { return DoPeelUnrollImpl(/* to_unroll */ false); } + HBasicBlock* DoUnrolling() { return DoPeelUnrollImpl(/* to_unroll */ true); } + HLoopInformation* GetRegionToBeAdjusted() const { return cloner_.GetRegionToBeAdjusted(); } + + protected: + // Applies loop peeling/unrolling for the loop specified by 'loop_info'. + // + // Depending on 'do_unroll' either unrolls loop by 2 or peels one iteration from it. + HBasicBlock* DoPeelUnrollImpl(bool to_unroll); + + private: + HLoopInformation* loop_info_; + SuperblockCloner cloner_; + + DISALLOW_COPY_AND_ASSIGN(PeelUnrollHelper); +}; + +// Helper class to perform loop peeling/unrolling. +// +// This helper should be used when there is no need to get correspondence information between +// original and copied basic blocks/instructions. +class PeelUnrollSimpleHelper : public ValueObject { + public: + explicit PeelUnrollSimpleHelper(HLoopInformation* info); + bool IsLoopClonable() const { return helper_.IsLoopClonable(); } + HBasicBlock* DoPeeling() { return helper_.DoPeeling(); } + HBasicBlock* DoUnrolling() { return helper_.DoUnrolling(); } + HLoopInformation* GetRegionToBeAdjusted() const { return helper_.GetRegionToBeAdjusted(); } + + private: + SuperblockCloner::HBasicBlockMap bb_map_; + SuperblockCloner::HInstructionMap hir_map_; + PeelUnrollHelper helper_; + + DISALLOW_COPY_AND_ASSIGN(PeelUnrollSimpleHelper); +}; + +// Collects edge remapping info for loop peeling/unrolling for the loop specified by loop info. +void CollectRemappingInfoForPeelUnroll(bool to_unroll, + HLoopInformation* loop_info, + SuperblockCloner::HEdgeSet* remap_orig_internal, + SuperblockCloner::HEdgeSet* remap_copy_internal, + SuperblockCloner::HEdgeSet* remap_incoming); + +// Returns whether blocks from 'work_set' are reachable from the rest of the graph. +// +// Returns whether such a set 'outer_entries' of basic blocks exists that: +// - each block from 'outer_entries' is not from 'work_set'. +// - each block from 'work_set' is reachable from at least one block from 'outer_entries'. +// +// After the function returns work_set contains only blocks from the original 'work_set' +// which are unreachable from the rest of the graph. +bool IsSubgraphConnected(SuperblockCloner::HBasicBlockSet* work_set, HGraph* graph); + +// Returns a common predecessor of loop1 and loop2 in the loop tree or nullptr if it is the whole +// graph. +HLoopInformation* FindCommonLoop(HLoopInformation* loop1, HLoopInformation* loop2); } // namespace art namespace std { @@ -312,11 +409,12 @@ template <> struct hash { size_t operator()(art::HEdge const& x) const noexcept { // Use Cantor pairing function as the hash function. - uint32_t a = x.GetFrom(); - uint32_t b = x.GetTo(); + size_t a = x.GetFrom(); + size_t b = x.GetTo(); return (a + b) * (a + b + 1) / 2 + b; } }; +ostream& operator<<(ostream& os, const art::HEdge& e); } // namespace std diff --git a/compiler/optimizing/superblock_cloner_test.cc b/compiler/optimizing/superblock_cloner_test.cc index f1b7bffdf5..df2e517aff 100644 --- a/compiler/optimizing/superblock_cloner_test.cc +++ b/compiler/optimizing/superblock_cloner_test.cc @@ -25,52 +25,65 @@ namespace art { using HBasicBlockMap = SuperblockCloner::HBasicBlockMap; using HInstructionMap = SuperblockCloner::HInstructionMap; +using HBasicBlockSet = SuperblockCloner::HBasicBlockSet; +using HEdgeSet = SuperblockCloner::HEdgeSet; // This class provides methods and helpers for testing various cloning and copying routines: // individual instruction cloning and cloning of the more coarse-grain structures. class SuperblockClonerTest : public OptimizingUnitTest { public: - SuperblockClonerTest() - : graph_(CreateGraph()), entry_block_(nullptr), exit_block_(nullptr), parameter_(nullptr) {} + SuperblockClonerTest() : graph_(CreateGraph()), + entry_block_(nullptr), + return_block_(nullptr), + exit_block_(nullptr), + parameter_(nullptr) {} - void CreateBasicLoopControlFlow(/* out */ HBasicBlock** header_p, - /* out */ HBasicBlock** body_p) { + void InitGraph() { entry_block_ = new (GetAllocator()) HBasicBlock(graph_); graph_->AddBlock(entry_block_); graph_->SetEntryBlock(entry_block_); + return_block_ = new (GetAllocator()) HBasicBlock(graph_); + graph_->AddBlock(return_block_); + + exit_block_ = new (GetAllocator()) HBasicBlock(graph_); + graph_->AddBlock(exit_block_); + graph_->SetExitBlock(exit_block_); + + entry_block_->AddSuccessor(return_block_); + return_block_->AddSuccessor(exit_block_); + + parameter_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(), + dex::TypeIndex(0), + 0, + DataType::Type::kInt32); + entry_block_->AddInstruction(parameter_); + return_block_->AddInstruction(new (GetAllocator()) HReturnVoid()); + exit_block_->AddInstruction(new (GetAllocator()) HExit()); + } + + void CreateBasicLoopControlFlow(HBasicBlock* position, + HBasicBlock* successor, + /* out */ HBasicBlock** header_p, + /* out */ HBasicBlock** body_p) { HBasicBlock* loop_preheader = new (GetAllocator()) HBasicBlock(graph_); HBasicBlock* loop_header = new (GetAllocator()) HBasicBlock(graph_); HBasicBlock* loop_body = new (GetAllocator()) HBasicBlock(graph_); - HBasicBlock* loop_exit = new (GetAllocator()) HBasicBlock(graph_); graph_->AddBlock(loop_preheader); graph_->AddBlock(loop_header); graph_->AddBlock(loop_body); - graph_->AddBlock(loop_exit); - exit_block_ = new (GetAllocator()) HBasicBlock(graph_); - graph_->AddBlock(exit_block_); - graph_->SetExitBlock(exit_block_); + position->ReplaceSuccessor(successor, loop_preheader); - entry_block_->AddSuccessor(loop_preheader); loop_preheader->AddSuccessor(loop_header); // Loop exit first to have a proper exit condition/target for HIf. - loop_header->AddSuccessor(loop_exit); + loop_header->AddSuccessor(successor); loop_header->AddSuccessor(loop_body); loop_body->AddSuccessor(loop_header); - loop_exit->AddSuccessor(exit_block_); *header_p = loop_header; *body_p = loop_body; - - parameter_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(), - dex::TypeIndex(0), - 0, - DataType::Type::kInt32); - entry_block_->AddInstruction(parameter_); - loop_exit->AddInstruction(new (GetAllocator()) HReturnVoid()); - exit_block_->AddInstruction(new (GetAllocator()) HExit()); } void CreateBasicLoopDataFlow(HBasicBlock* loop_header, HBasicBlock* loop_body) { @@ -84,11 +97,12 @@ class SuperblockClonerTest : public OptimizingUnitTest { // Header block. HPhi* phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32); HInstruction* suspend_check = new (GetAllocator()) HSuspendCheck(); + HInstruction* loop_check = new (GetAllocator()) HGreaterThanOrEqual(phi, const_128); loop_header->AddPhi(phi); loop_header->AddInstruction(suspend_check); - loop_header->AddInstruction(new (GetAllocator()) HGreaterThanOrEqual(phi, const_128)); - loop_header->AddInstruction(new (GetAllocator()) HIf(parameter_)); + loop_header->AddInstruction(loop_check); + loop_header->AddInstruction(new (GetAllocator()) HIf(loop_check)); // Loop body block. HInstruction* null_check = new (GetAllocator()) HNullCheck(parameter_, dex_pc); @@ -97,8 +111,8 @@ class SuperblockClonerTest : public OptimizingUnitTest { HInstruction* array_get = new (GetAllocator()) HArrayGet(null_check, bounds_check, DataType::Type::kInt32, dex_pc); HInstruction* add = new (GetAllocator()) HAdd(DataType::Type::kInt32, array_get, const_1); - HInstruction* array_set = - new (GetAllocator()) HArraySet(null_check, bounds_check, add, DataType::Type::kInt32, dex_pc); + HInstruction* array_set = new (GetAllocator()) HArraySet( + null_check, bounds_check, add, DataType::Type::kInt32, dex_pc); HInstruction* induction_inc = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi, const_1); loop_body->AddInstruction(null_check); @@ -153,6 +167,7 @@ class SuperblockClonerTest : public OptimizingUnitTest { HGraph* graph_; HBasicBlock* entry_block_; + HBasicBlock* return_block_; HBasicBlock* exit_block_; HInstruction* parameter_; @@ -162,10 +177,11 @@ TEST_F(SuperblockClonerTest, IndividualInstrCloner) { HBasicBlock* header = nullptr; HBasicBlock* loop_body = nullptr; - CreateBasicLoopControlFlow(&header, &loop_body); + InitGraph(); + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); CreateBasicLoopDataFlow(header, loop_body); graph_->BuildDominatorTree(); - ASSERT_TRUE(CheckGraph()); + EXPECT_TRUE(CheckGraph()); HSuspendCheck* old_suspend_check = header->GetLoopInformation()->GetSuspendCheck(); CloneAndReplaceInstructionVisitor visitor(graph_); @@ -193,7 +209,8 @@ TEST_F(SuperblockClonerTest, CloneBasicBlocks) { HBasicBlock* loop_body = nullptr; ArenaAllocator* arena = graph_->GetAllocator(); - CreateBasicLoopControlFlow(&header, &loop_body); + InitGraph(); + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); CreateBasicLoopDataFlow(header, loop_body); graph_->BuildDominatorTree(); ASSERT_TRUE(CheckGraph()); @@ -272,7 +289,8 @@ TEST_F(SuperblockClonerTest, AdjustControlFlowInfo) { HBasicBlock* loop_body = nullptr; ArenaAllocator* arena = graph_->GetAllocator(); - CreateBasicLoopControlFlow(&header, &loop_body); + InitGraph(); + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); CreateBasicLoopDataFlow(header, loop_body); graph_->BuildDominatorTree(); ASSERT_TRUE(CheckGraph()); @@ -303,4 +321,487 @@ TEST_F(SuperblockClonerTest, AdjustControlFlowInfo) { EXPECT_TRUE(loop_info->IsBackEdge(*loop_body)); } +// Tests IsSubgraphConnected function for negative case. +TEST_F(SuperblockClonerTest, IsGraphConnected) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + ArenaAllocator* arena = graph_->GetAllocator(); + + InitGraph(); + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* unreachable_block = new (GetAllocator()) HBasicBlock(graph_); + graph_->AddBlock(unreachable_block); + + HBasicBlockSet bb_set( + arena, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner); + bb_set.SetBit(header->GetBlockId()); + bb_set.SetBit(loop_body->GetBlockId()); + bb_set.SetBit(unreachable_block->GetBlockId()); + + EXPECT_FALSE(IsSubgraphConnected(&bb_set, graph_)); + EXPECT_EQ(bb_set.NumSetBits(), 1u); + EXPECT_TRUE(bb_set.IsBitSet(unreachable_block->GetBlockId())); +} + +// Tests SuperblockCloner for loop peeling case. +// +// Control Flow of the example (ignoring critical edges splitting). +// +// Before After +// +// |B| |B| +// | | +// v v +// |1| |1| +// | | +// v v +// |2|<-\ (6) |2A| +// / \ / / \ +// v v/ / v +// |4| |3| / |3A| (7) +// | / / +// v | v +// |E| \ |2|<-\ +// \ / \ / +// v v / +// |4| |3| +// | +// v +// |E| +TEST_F(SuperblockClonerTest, LoopPeeling) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + + InitGraph(); + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + graph_->BuildDominatorTree(); + EXPECT_TRUE(CheckGraph()); + + HBasicBlockMap bb_map( + std::less(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + HInstructionMap hir_map( + std::less(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + + HLoopInformation* loop_info = header->GetLoopInformation(); + PeelUnrollHelper helper(loop_info, &bb_map, &hir_map); + EXPECT_TRUE(helper.IsLoopClonable()); + HBasicBlock* new_header = helper.DoPeeling(); + HLoopInformation* new_loop_info = new_header->GetLoopInformation(); + + EXPECT_TRUE(CheckGraph()); + + // Check loop body successors. + EXPECT_EQ(loop_body->GetSingleSuccessor(), header); + EXPECT_EQ(bb_map.Get(loop_body)->GetSingleSuccessor(), header); + + // Check loop structure. + EXPECT_EQ(header, new_header); + EXPECT_EQ(new_loop_info->GetHeader(), header); + EXPECT_EQ(new_loop_info->GetBackEdges().size(), 1u); + EXPECT_EQ(new_loop_info->GetBackEdges()[0], loop_body); +} + +// Tests SuperblockCloner for loop unrolling case. +// +// Control Flow of the example (ignoring critical edges splitting). +// +// Before After +// +// |B| |B| +// | | +// v v +// |1| |1| +// | | +// v v +// |2|<-\ (6) |2A|<-\ +// / \ / / \ \ +// v v/ / v \ +// |4| |3| /(7)|3A| | +// | / / / +// v | v / +// |E| \ |2| / +// \ / \ / +// v v/ +// |4| |3| +// | +// v +// |E| +TEST_F(SuperblockClonerTest, LoopUnrolling) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + + InitGraph(); + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + graph_->BuildDominatorTree(); + EXPECT_TRUE(CheckGraph()); + + HBasicBlockMap bb_map( + std::less(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + HInstructionMap hir_map( + std::less(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + + HLoopInformation* loop_info = header->GetLoopInformation(); + PeelUnrollHelper helper(loop_info, &bb_map, &hir_map); + EXPECT_TRUE(helper.IsLoopClonable()); + HBasicBlock* new_header = helper.DoUnrolling(); + + EXPECT_TRUE(CheckGraph()); + + // Check loop body successors. + EXPECT_EQ(loop_body->GetSingleSuccessor(), bb_map.Get(header)); + EXPECT_EQ(bb_map.Get(loop_body)->GetSingleSuccessor(), header); + + // Check loop structure. + EXPECT_EQ(header, new_header); + EXPECT_EQ(loop_info, new_header->GetLoopInformation()); + EXPECT_EQ(loop_info->GetHeader(), new_header); + EXPECT_EQ(loop_info->GetBackEdges().size(), 1u); + EXPECT_EQ(loop_info->GetBackEdges()[0], bb_map.Get(loop_body)); +} + +// Checks that loop unrolling works fine for a loop with multiple back edges. Tests that after +// the transformation the loop has a single preheader. +TEST_F(SuperblockClonerTest, LoopPeelingMultipleBackEdges) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + + InitGraph(); + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + + // Transform a basic loop to have multiple back edges. + HBasicBlock* latch = header->GetSuccessors()[1]; + HBasicBlock* if_block = new (GetAllocator()) HBasicBlock(graph_); + HBasicBlock* temp1 = new (GetAllocator()) HBasicBlock(graph_); + graph_->AddBlock(if_block); + graph_->AddBlock(temp1); + header->ReplaceSuccessor(latch, if_block); + if_block->AddSuccessor(latch); + if_block->AddSuccessor(temp1); + temp1->AddSuccessor(header); + + if_block->AddInstruction(new (GetAllocator()) HIf(parameter_)); + + HInstructionIterator it(header->GetPhis()); + DCHECK(!it.Done()); + HPhi* loop_phi = it.Current()->AsPhi(); + HInstruction* temp_add = new (GetAllocator()) HAdd(DataType::Type::kInt32, + loop_phi, + graph_->GetIntConstant(2)); + temp1->AddInstruction(temp_add); + temp1->AddInstruction(new (GetAllocator()) HGoto()); + loop_phi->AddInput(temp_add); + + graph_->BuildDominatorTree(); + EXPECT_TRUE(CheckGraph()); + + HLoopInformation* loop_info = header->GetLoopInformation(); + PeelUnrollSimpleHelper helper(loop_info); + HBasicBlock* new_header = helper.DoPeeling(); + EXPECT_EQ(header, new_header); + + EXPECT_TRUE(CheckGraph()); + EXPECT_EQ(header->GetPredecessors().size(), 3u); +} + +static void CheckLoopStructureForLoopPeelingNested(HBasicBlock* loop1_header, + HBasicBlock* loop2_header, + HBasicBlock* loop3_header) { + EXPECT_EQ(loop1_header->GetLoopInformation()->GetHeader(), loop1_header); + EXPECT_EQ(loop2_header->GetLoopInformation()->GetHeader(), loop2_header); + EXPECT_EQ(loop3_header->GetLoopInformation()->GetHeader(), loop3_header); + EXPECT_EQ(loop1_header->GetLoopInformation()->GetPreHeader()->GetLoopInformation(), nullptr); + EXPECT_EQ(loop2_header->GetLoopInformation()->GetPreHeader()->GetLoopInformation(), nullptr); + EXPECT_EQ(loop3_header->GetLoopInformation()->GetPreHeader()->GetLoopInformation()->GetHeader(), + loop2_header); +} + +TEST_F(SuperblockClonerTest, LoopPeelingNested) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + + InitGraph(); + + // Create the following nested structure of loops + // Headers: 1 2 3 + // [ ], [ [ ] ] + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop1_header = header; + + CreateBasicLoopControlFlow(header, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop2_header = header; + + CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop3_header = header; + + graph_->BuildDominatorTree(); + EXPECT_TRUE(CheckGraph()); + + HLoopInformation* loop2_info_before = loop2_header->GetLoopInformation(); + HLoopInformation* loop3_info_before = loop3_header->GetLoopInformation(); + + // Check nested loops structure. + CheckLoopStructureForLoopPeelingNested(loop1_header, loop2_header, loop3_header); + PeelUnrollSimpleHelper helper(loop1_header->GetLoopInformation()); + helper.DoPeeling(); + // Check that nested loops structure has not changed after the transformation. + CheckLoopStructureForLoopPeelingNested(loop1_header, loop2_header, loop3_header); + + // Test that the loop info is preserved. + EXPECT_EQ(loop2_info_before, loop2_header->GetLoopInformation()); + EXPECT_EQ(loop3_info_before, loop3_header->GetLoopInformation()); + + EXPECT_EQ(loop3_info_before->GetPreHeader()->GetLoopInformation(), loop2_info_before); + EXPECT_EQ(loop2_info_before->GetPreHeader()->GetLoopInformation(), nullptr); + + EXPECT_EQ(helper.GetRegionToBeAdjusted(), nullptr); + + EXPECT_TRUE(CheckGraph()); +} + +// Checks that the loop population is correctly propagated after an inner loop is peeled. +TEST_F(SuperblockClonerTest, OuterLoopPopulationAfterInnerPeeled) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + + InitGraph(); + + // Create the following nested structure of loops + // Headers: 1 2 3 4 + // [ [ [ ] ] ], [ ] + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop1_header = header; + + CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop2_header = header; + + CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop3_header = header; + + CreateBasicLoopControlFlow(loop1_header, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop4_header = header; + + graph_->BuildDominatorTree(); + EXPECT_TRUE(CheckGraph()); + + PeelUnrollSimpleHelper helper(loop3_header->GetLoopInformation()); + helper.DoPeeling(); + HLoopInformation* loop1 = loop1_header->GetLoopInformation(); + HLoopInformation* loop2 = loop2_header->GetLoopInformation(); + HLoopInformation* loop3 = loop3_header->GetLoopInformation(); + HLoopInformation* loop4 = loop4_header->GetLoopInformation(); + + EXPECT_TRUE(loop1->Contains(*loop2_header)); + EXPECT_TRUE(loop1->Contains(*loop3_header)); + EXPECT_TRUE(loop1->Contains(*loop3_header->GetLoopInformation()->GetPreHeader())); + + // Check that loop4 info has not been touched after local run of AnalyzeLoops. + EXPECT_EQ(loop4, loop4_header->GetLoopInformation()); + + EXPECT_TRUE(loop1->IsIn(*loop1)); + EXPECT_TRUE(loop2->IsIn(*loop1)); + EXPECT_TRUE(loop3->IsIn(*loop1)); + EXPECT_TRUE(loop3->IsIn(*loop2)); + EXPECT_TRUE(!loop4->IsIn(*loop1)); + + EXPECT_EQ(loop4->GetPreHeader()->GetLoopInformation(), nullptr); + + EXPECT_EQ(helper.GetRegionToBeAdjusted(), loop2); + + EXPECT_TRUE(CheckGraph()); +} + +// Checks the case when inner loop have an exit not to its immediate outer_loop but some other loop +// in the hierarchy. Loop population information must be valid after loop peeling. +TEST_F(SuperblockClonerTest, NestedCaseExitToOutermost) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + + InitGraph(); + + // Create the following nested structure of loops then peel loop3. + // Headers: 1 2 3 + // [ [ [ ] ] ] + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop1_header = header; + HBasicBlock* loop_body1 = loop_body; + + CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + + CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop3_header = header; + HBasicBlock* loop_body3 = loop_body; + + // Change the loop3 - insert an exit which leads to loop1. + HBasicBlock* loop3_extra_if_block = new (GetAllocator()) HBasicBlock(graph_); + graph_->AddBlock(loop3_extra_if_block); + loop3_extra_if_block->AddInstruction(new (GetAllocator()) HIf(parameter_)); + + loop3_header->ReplaceSuccessor(loop_body3, loop3_extra_if_block); + loop3_extra_if_block->AddSuccessor(loop_body1); // Long exit. + loop3_extra_if_block->AddSuccessor(loop_body3); + + graph_->BuildDominatorTree(); + EXPECT_TRUE(CheckGraph()); + + HBasicBlock* loop3_long_exit = loop3_extra_if_block->GetSuccessors()[0]; + EXPECT_TRUE(loop1_header->GetLoopInformation()->Contains(*loop3_long_exit)); + + PeelUnrollSimpleHelper helper(loop3_header->GetLoopInformation()); + helper.DoPeeling(); + + HLoopInformation* loop1 = loop1_header->GetLoopInformation(); + // Check that after the transformation the local area for CF adjustments has been chosen + // correctly and loop population has been updated. + loop3_long_exit = loop3_extra_if_block->GetSuccessors()[0]; + EXPECT_TRUE(loop1->Contains(*loop3_long_exit)); + + EXPECT_EQ(helper.GetRegionToBeAdjusted(), loop1); + + EXPECT_TRUE(loop1->Contains(*loop3_header)); + EXPECT_TRUE(loop1->Contains(*loop3_header->GetLoopInformation()->GetPreHeader())); + + EXPECT_TRUE(CheckGraph()); +} + +TEST_F(SuperblockClonerTest, FastCaseCheck) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + ArenaAllocator* arena = graph_->GetAllocator(); + + InitGraph(); + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + graph_->BuildDominatorTree(); + + HLoopInformation* loop_info = header->GetLoopInformation(); + + ArenaBitVector orig_bb_set( + arena, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner); + orig_bb_set.Union(&loop_info->GetBlocks()); + + HEdgeSet remap_orig_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + HEdgeSet remap_copy_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + HEdgeSet remap_incoming(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); + + CollectRemappingInfoForPeelUnroll(true, + loop_info, + &remap_orig_internal, + &remap_copy_internal, + &remap_incoming); + + // Insert some extra nodes and edges. + HBasicBlock* preheader = loop_info->GetPreHeader(); + orig_bb_set.SetBit(preheader->GetBlockId()); + + // Adjust incoming edges. + remap_incoming.Clear(); + remap_incoming.Insert(HEdge(preheader->GetSinglePredecessor(), preheader)); + + HBasicBlockMap bb_map(std::less(), arena->Adapter(kArenaAllocSuperblockCloner)); + HInstructionMap hir_map(std::less(), arena->Adapter(kArenaAllocSuperblockCloner)); + + SuperblockCloner cloner(graph_, + &orig_bb_set, + &bb_map, + &hir_map); + cloner.SetSuccessorRemappingInfo(&remap_orig_internal, &remap_copy_internal, &remap_incoming); + + EXPECT_FALSE(cloner.IsFastCase()); +} + +// Helper for FindCommonLoop which also check that FindCommonLoop is symmetric. +static HLoopInformation* FindCommonLoopCheck(HLoopInformation* loop1, HLoopInformation* loop2) { + HLoopInformation* common_loop12 = FindCommonLoop(loop1, loop2); + HLoopInformation* common_loop21 = FindCommonLoop(loop2, loop1); + EXPECT_EQ(common_loop21, common_loop12); + return common_loop12; +} + +// Tests FindCommonLoop function on a loop nest. +TEST_F(SuperblockClonerTest, FindCommonLoop) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + + InitGraph(); + + // Create the following nested structure of loops + // Headers: 1 2 3 4 5 + // [ [ [ ] ], [ ] ], [ ] + CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop1_header = header; + + CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop2_header = header; + + CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop3_header = header; + + CreateBasicLoopControlFlow(loop2_header, loop2_header->GetSuccessors()[0], &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop4_header = header; + + CreateBasicLoopControlFlow(loop1_header, return_block_, &header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + HBasicBlock* loop5_header = header; + + graph_->BuildDominatorTree(); + EXPECT_TRUE(CheckGraph()); + + HLoopInformation* loop1 = loop1_header->GetLoopInformation(); + HLoopInformation* loop2 = loop2_header->GetLoopInformation(); + HLoopInformation* loop3 = loop3_header->GetLoopInformation(); + HLoopInformation* loop4 = loop4_header->GetLoopInformation(); + HLoopInformation* loop5 = loop5_header->GetLoopInformation(); + + EXPECT_TRUE(loop1->IsIn(*loop1)); + EXPECT_TRUE(loop2->IsIn(*loop1)); + EXPECT_TRUE(loop3->IsIn(*loop1)); + EXPECT_TRUE(loop3->IsIn(*loop2)); + EXPECT_TRUE(loop4->IsIn(*loop1)); + + EXPECT_FALSE(loop5->IsIn(*loop1)); + EXPECT_FALSE(loop4->IsIn(*loop2)); + EXPECT_FALSE(loop4->IsIn(*loop3)); + + EXPECT_EQ(loop1->GetPreHeader()->GetLoopInformation(), nullptr); + EXPECT_EQ(loop4->GetPreHeader()->GetLoopInformation(), loop1); + + EXPECT_EQ(FindCommonLoopCheck(nullptr, nullptr), nullptr); + EXPECT_EQ(FindCommonLoopCheck(loop2, nullptr), nullptr); + + EXPECT_EQ(FindCommonLoopCheck(loop1, loop1), loop1); + EXPECT_EQ(FindCommonLoopCheck(loop1, loop2), loop1); + EXPECT_EQ(FindCommonLoopCheck(loop1, loop3), loop1); + EXPECT_EQ(FindCommonLoopCheck(loop1, loop4), loop1); + EXPECT_EQ(FindCommonLoopCheck(loop1, loop5), nullptr); + + EXPECT_EQ(FindCommonLoopCheck(loop2, loop3), loop2); + EXPECT_EQ(FindCommonLoopCheck(loop2, loop4), loop1); + EXPECT_EQ(FindCommonLoopCheck(loop2, loop5), nullptr); + + EXPECT_EQ(FindCommonLoopCheck(loop3, loop4), loop1); + EXPECT_EQ(FindCommonLoopCheck(loop3, loop5), nullptr); + + EXPECT_EQ(FindCommonLoopCheck(loop4, loop5), nullptr); + + EXPECT_EQ(FindCommonLoopCheck(loop5, loop5), loop5); +} + } // namespace art -- GitLab From 4ca17350c0c0f7582d909d2776cc3d5f99b4b18a Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Wed, 7 Mar 2018 15:47:39 -0800 Subject: [PATCH 035/749] Saturation arithmetic instructions for X86 and X86_64. Rationale: Saturation arithmetic? It is coming! Bug: b/74026074 Test: assember_x86[_64]_test Change-Id: I6084161683c5f83ccf632a2ad0280913cec84931 --- compiler/utils/x86/assembler_x86.cc | 72 +++++++++++++++++ compiler/utils/x86/assembler_x86.h | 9 +++ compiler/utils/x86/assembler_x86_test.cc | 32 ++++++++ compiler/utils/x86_64/assembler_x86_64.cc | 80 +++++++++++++++++++ compiler/utils/x86_64/assembler_x86_64.h | 9 +++ .../utils/x86_64/assembler_x86_64_test.cc | 32 ++++++++ 6 files changed, 234 insertions(+) diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index ea160c8993..42c2541421 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -913,6 +913,78 @@ void X86Assembler::psubq(XmmRegister dst, XmmRegister src) { } +void X86Assembler::paddusb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xDC); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::paddsb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xEC); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::paddusw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xDD); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::paddsw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xED); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::psubusb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xD8); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::psubsb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xE8); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::psubusw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xD9); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::psubsw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xE9); + EmitXmmRegisterOperand(dst, src); +} + + void X86Assembler::cvtsi2ss(XmmRegister dst, Register src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF3); diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index a085677083..22eaedce61 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -449,6 +449,15 @@ class X86Assembler FINAL : public Assembler { void paddq(XmmRegister dst, XmmRegister src); void psubq(XmmRegister dst, XmmRegister src); + void paddusb(XmmRegister dst, XmmRegister src); + void paddsb(XmmRegister dst, XmmRegister src); + void paddusw(XmmRegister dst, XmmRegister src); + void paddsw(XmmRegister dst, XmmRegister src); + void psubusb(XmmRegister dst, XmmRegister src); + void psubsb(XmmRegister dst, XmmRegister src); + void psubusw(XmmRegister dst, XmmRegister src); + void psubsw(XmmRegister dst, XmmRegister src); + void cvtsi2ss(XmmRegister dst, Register src); void cvtsi2sd(XmmRegister dst, Register src); diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index 2fd1b27182..8f72db748b 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -600,6 +600,38 @@ TEST_F(AssemblerX86Test, PSubQ) { DriverStr(RepeatFF(&x86::X86Assembler::psubq, "psubq %{reg2}, %{reg1}"), "psubq"); } +TEST_F(AssemblerX86Test, PAddUSB) { + DriverStr(RepeatFF(&x86::X86Assembler::paddusb, "paddusb %{reg2}, %{reg1}"), "paddusb"); +} + +TEST_F(AssemblerX86Test, PAddSB) { + DriverStr(RepeatFF(&x86::X86Assembler::paddsb, "paddsb %{reg2}, %{reg1}"), "paddsb"); +} + +TEST_F(AssemblerX86Test, PAddUSW) { + DriverStr(RepeatFF(&x86::X86Assembler::paddusw, "paddusw %{reg2}, %{reg1}"), "paddusw"); +} + +TEST_F(AssemblerX86Test, PAddSW) { + DriverStr(RepeatFF(&x86::X86Assembler::psubsw, "psubsw %{reg2}, %{reg1}"), "psubsw"); +} + +TEST_F(AssemblerX86Test, PSubUSB) { + DriverStr(RepeatFF(&x86::X86Assembler::psubusb, "psubusb %{reg2}, %{reg1}"), "psubusb"); +} + +TEST_F(AssemblerX86Test, PSubSB) { + DriverStr(RepeatFF(&x86::X86Assembler::psubsb, "psubsb %{reg2}, %{reg1}"), "psubsb"); +} + +TEST_F(AssemblerX86Test, PSubUSW) { + DriverStr(RepeatFF(&x86::X86Assembler::psubusw, "psubusw %{reg2}, %{reg1}"), "psubusw"); +} + +TEST_F(AssemblerX86Test, PSubSW) { + DriverStr(RepeatFF(&x86::X86Assembler::psubsw, "psubsw %{reg2}, %{reg1}"), "psubsw"); +} + TEST_F(AssemblerX86Test, XorPD) { DriverStr(RepeatFF(&x86::X86Assembler::xorpd, "xorpd %{reg2}, %{reg1}"), "xorpd"); } diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index ff5a357c5e..c6e16e754c 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -1011,6 +1011,86 @@ void X86_64Assembler::psubq(XmmRegister dst, XmmRegister src) { } +void X86_64Assembler::paddusb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xDC); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::paddsb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xEC); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::paddusw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xDD); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::paddsw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xED); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::psubusb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xD8); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::psubsb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xE8); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::psubusw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xD9); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::psubsw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xE9); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + void X86_64Assembler::cvtsi2ss(XmmRegister dst, CpuRegister src) { cvtsi2ss(dst, src, false); } diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index 7a5fdb502f..ab761fb1fc 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -485,6 +485,15 @@ class X86_64Assembler FINAL : public Assembler { void paddq(XmmRegister dst, XmmRegister src); void psubq(XmmRegister dst, XmmRegister src); + void paddusb(XmmRegister dst, XmmRegister src); + void paddsb(XmmRegister dst, XmmRegister src); + void paddusw(XmmRegister dst, XmmRegister src); + void paddsw(XmmRegister dst, XmmRegister src); + void psubusb(XmmRegister dst, XmmRegister src); + void psubsb(XmmRegister dst, XmmRegister src); + void psubusw(XmmRegister dst, XmmRegister src); + void psubsw(XmmRegister dst, XmmRegister src); + void cvtsi2ss(XmmRegister dst, CpuRegister src); // Note: this is the r/m32 version. void cvtsi2ss(XmmRegister dst, CpuRegister src, bool is64bit); void cvtsi2ss(XmmRegister dst, const Address& src, bool is64bit); diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index 6b1e53c35a..104e215cf5 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -1282,6 +1282,38 @@ TEST_F(AssemblerX86_64Test, Psubq) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubq, "psubq %{reg2}, %{reg1}"), "psubq"); } +TEST_F(AssemblerX86_64Test, Paddusb) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddusb, "paddusb %{reg2}, %{reg1}"), "paddusb"); +} + +TEST_F(AssemblerX86_64Test, Paddsb) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddsb, "paddsb %{reg2}, %{reg1}"), "paddsb"); +} + +TEST_F(AssemblerX86_64Test, Paddusw) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddusw, "paddusw %{reg2}, %{reg1}"), "paddusw"); +} + +TEST_F(AssemblerX86_64Test, Paddsw) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddsw, "paddsw %{reg2}, %{reg1}"), "paddsw"); +} + +TEST_F(AssemblerX86_64Test, Psubusb) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubusb, "psubusb %{reg2}, %{reg1}"), "psubusb"); +} + +TEST_F(AssemblerX86_64Test, Psubsb) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubsb, "psubsb %{reg2}, %{reg1}"), "psubsb"); +} + +TEST_F(AssemblerX86_64Test, Psubusw) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubusw, "psubusw %{reg2}, %{reg1}"), "psubusw"); +} + +TEST_F(AssemblerX86_64Test, Psubsw) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubsw, "psubsw %{reg2}, %{reg1}"), "psubsw"); +} + TEST_F(AssemblerX86_64Test, Cvtsi2ss) { DriverStr(RepeatFr(&x86_64::X86_64Assembler::cvtsi2ss, "cvtsi2ss %{reg2}, %{reg1}"), "cvtsi2ss"); } -- GitLab From f0e3d9fc0354dfebb06d6930515c315ecc76fe1d Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 8 Mar 2018 10:20:36 +0000 Subject: [PATCH 036/749] Disable failing test. bug: 73804944 bug: 74375666 Change-Id: Iba4eeedf254f85db7fd763c9208120feea047f13 --- dex2oat/dex2oat_image_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc index 49b84bb0c6..f542c60d81 100644 --- a/dex2oat/dex2oat_image_test.cc +++ b/dex2oat/dex2oat_image_test.cc @@ -228,7 +228,7 @@ class Dex2oatImageTest : public CommonRuntimeTest { } }; -TEST_F(Dex2oatImageTest, TestModesAndFilters) { +TEST_F(Dex2oatImageTest, DISABLED_TestModesAndFilters) { if (kIsTargetBuild) { // This test is too slow for target builds. return; -- GitLab From b066d43b1d9184899aff32b1f243d092611ad9c6 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 3 Jan 2018 13:14:37 +0000 Subject: [PATCH 037/749] Load ArtMethod* from .data.bimg.rel.ro entries. Introduce a new .data.bimg.rel.ro section in oat files where we store offsets of boot image objects from the beginning of the boot image. At runtime we relocate these entries using the actual boot image address to turn offsets to pointers. Use the .data.bimg.rel.ro to prepare the boot image methods used by HInvokeStaticOrDirect for PIC AOT app compilation. Loading the ArtMethod* from .data.bimg.rel.ro instead of the .bss avoids the initial call to the resolution trampoline. Test: Additional test in 522-checker-sharpening Test: m test-art-host-gtest Test: testrunner.py --host --optimizing --pictest --npictest Test: Pixel 2 XL boots. Test: testrunner.py --target --optimizing --pictest --npictest Bug: 71526895 Change-Id: Ie5f5b1f622704877b36730377146e59092e46c0c --- .../linker/arm64/relative_patcher_arm64.cc | 4 +- compiler/linker/elf_builder.h | 27 ++++ compiler/linker/linker_patch.h | 28 ++++- compiler/optimizing/code_generator_arm64.cc | 33 ++++- compiler/optimizing/code_generator_arm64.h | 15 ++- .../optimizing/code_generator_arm_vixl.cc | 26 +++- compiler/optimizing/code_generator_arm_vixl.h | 3 +- compiler/optimizing/code_generator_mips.cc | 27 +++- compiler/optimizing/code_generator_mips.h | 4 +- compiler/optimizing/code_generator_mips64.cc | 27 +++- compiler/optimizing/code_generator_mips64.h | 4 +- compiler/optimizing/code_generator_x86.cc | 27 +++- compiler/optimizing/code_generator_x86.h | 4 +- compiler/optimizing/code_generator_x86_64.cc | 23 +++- compiler/optimizing/code_generator_x86_64.h | 3 +- compiler/optimizing/nodes.cc | 2 + compiler/optimizing/nodes.h | 5 + compiler/optimizing/sharpening.cc | 8 +- dex2oat/dex2oat.cc | 18 ++- dex2oat/linker/elf_writer.h | 3 + dex2oat/linker/elf_writer_quick.cc | 22 ++++ dex2oat/linker/image_test.h | 14 ++- dex2oat/linker/oat_writer.cc | 118 +++++++++++++++++- dex2oat/linker/oat_writer.h | 31 +++++ dex2oat/linker/oat_writer_test.cc | 15 ++- oatdump/oatdump.cc | 1 + runtime/class_linker.cc | 34 ++++- runtime/oat.h | 4 +- runtime/oat_file.cc | 75 ++++++++--- runtime/oat_file.h | 22 +++- test/552-checker-sharpening/src/Main.java | 28 +++++ 31 files changed, 587 insertions(+), 68 deletions(-) diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc index 52a07965b9..7230f11f73 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.cc +++ b/compiler/linker/arm64/relative_patcher_arm64.cc @@ -60,6 +60,7 @@ inline bool IsAdrpPatch(const LinkerPatch& patch) { case LinkerPatch::Type::kCallRelative: case LinkerPatch::Type::kBakerReadBarrierBranch: return false; + case LinkerPatch::Type::kDataBimgRelRo: case LinkerPatch::Type::kMethodRelative: case LinkerPatch::Type::kMethodBssEntry: case LinkerPatch::Type::kTypeRelative: @@ -271,7 +272,8 @@ void Arm64RelativePatcher::PatchPcRelativeReference(std::vector* code, shift = 0u; // No shift for ADD. } else { // LDR/STR 32-bit or 64-bit with imm12 == 0 (unset). - DCHECK(patch.GetType() == LinkerPatch::Type::kMethodBssEntry || + DCHECK(patch.GetType() == LinkerPatch::Type::kDataBimgRelRo || + patch.GetType() == LinkerPatch::Type::kMethodBssEntry || patch.GetType() == LinkerPatch::Type::kTypeClassTable || patch.GetType() == LinkerPatch::Type::kTypeBssEntry || patch.GetType() == LinkerPatch::Type::kStringInternTable || diff --git a/compiler/linker/elf_builder.h b/compiler/linker/elf_builder.h index a5f60992ca..3da7a43762 100644 --- a/compiler/linker/elf_builder.h +++ b/compiler/linker/elf_builder.h @@ -529,6 +529,8 @@ class ElfBuilder FINAL { stream_(output), rodata_(this, ".rodata", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), text_(this, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, nullptr, 0, kPageSize, 0), + data_bimg_rel_ro_( + this, ".data.bimg.rel.ro", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), bss_(this, ".bss", SHT_NOBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), dex_(this, ".dex", SHT_NOBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), dynstr_(this, ".dynstr", SHF_ALLOC, kPageSize), @@ -552,6 +554,7 @@ class ElfBuilder FINAL { loaded_size_(0u), virtual_address_(0) { text_.phdr_flags_ = PF_R | PF_X; + data_bimg_rel_ro_.phdr_flags_ = PF_R | PF_W; // Shall be made read-only at run time. bss_.phdr_flags_ = PF_R | PF_W; dex_.phdr_flags_ = PF_R; dynamic_.phdr_flags_ = PF_R | PF_W; @@ -566,6 +569,7 @@ class ElfBuilder FINAL { BuildIdSection* GetBuildId() { return &build_id_; } Section* GetRoData() { return &rodata_; } Section* GetText() { return &text_; } + Section* GetDataBimgRelRo() { return &data_bimg_rel_ro_; } Section* GetBss() { return &bss_; } Section* GetDex() { return &dex_; } StringSection* GetStrTab() { return &strtab_; } @@ -694,6 +698,7 @@ class ElfBuilder FINAL { void PrepareDynamicSection(const std::string& elf_file_path, Elf_Word rodata_size, Elf_Word text_size, + Elf_Word data_bimg_rel_ro_size, Elf_Word bss_size, Elf_Word bss_methods_offset, Elf_Word bss_roots_offset, @@ -707,6 +712,9 @@ class ElfBuilder FINAL { // Allocate all pre-dynamic sections. rodata_.AllocateVirtualMemory(rodata_size); text_.AllocateVirtualMemory(text_size); + if (data_bimg_rel_ro_size != 0) { + data_bimg_rel_ro_.AllocateVirtualMemory(data_bimg_rel_ro_size); + } if (bss_size != 0) { bss_.AllocateVirtualMemory(bss_size); } @@ -735,6 +743,24 @@ class ElfBuilder FINAL { Elf_Word oatlastword_address = rodata_.GetAddress() + rodata_size - 4; dynsym_.Add(oatlastword, &rodata_, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT); } + if (data_bimg_rel_ro_size != 0u) { + Elf_Word oatdatabimgrelro = dynstr_.Add("oatdatabimgrelro"); + dynsym_.Add(oatdatabimgrelro, + &data_bimg_rel_ro_, + data_bimg_rel_ro_.GetAddress(), + data_bimg_rel_ro_size, + STB_GLOBAL, + STT_OBJECT); + Elf_Word oatdatabimgrelrolastword = dynstr_.Add("oatdatabimgrelrolastword"); + Elf_Word oatdatabimgrelrolastword_address = + data_bimg_rel_ro_.GetAddress() + data_bimg_rel_ro_size - 4; + dynsym_.Add(oatdatabimgrelrolastword, + &data_bimg_rel_ro_, + oatdatabimgrelrolastword_address, + 4, + STB_GLOBAL, + STT_OBJECT); + } DCHECK_LE(bss_roots_offset, bss_size); if (bss_size != 0u) { Elf_Word oatbss = dynstr_.Add("oatbss"); @@ -1010,6 +1036,7 @@ class ElfBuilder FINAL { Section rodata_; Section text_; + Section data_bimg_rel_ro_; Section bss_; Section dex_; CachedStringSection dynstr_; diff --git a/compiler/linker/linker_patch.h b/compiler/linker/linker_patch.h index 6f4e7746a6..a3c737c4f9 100644 --- a/compiler/linker/linker_patch.h +++ b/compiler/linker/linker_patch.h @@ -41,6 +41,7 @@ class LinkerPatch { // choose to squeeze the Type into fewer than 8 bits, we'll have to declare // patch_type_ as an uintN_t and do explicit static_cast<>s. enum class Type : uint8_t { + kDataBimgRelRo, // NOTE: Actual patching is instruction_set-dependent. kMethodRelative, // NOTE: Actual patching is instruction_set-dependent. kMethodBssEntry, // NOTE: Actual patching is instruction_set-dependent. kCall, @@ -54,6 +55,15 @@ class LinkerPatch { kBakerReadBarrierBranch, // NOTE: Actual patching is instruction_set-dependent. }; + static LinkerPatch DataBimgRelRoPatch(size_t literal_offset, + uint32_t pc_insn_offset, + uint32_t boot_image_offset) { + LinkerPatch patch(literal_offset, Type::kDataBimgRelRo, /* target_dex_file */ nullptr); + patch.boot_image_offset_ = boot_image_offset; + patch.pc_insn_offset_ = pc_insn_offset; + return patch; + } + static LinkerPatch RelativeMethodPatch(size_t literal_offset, const DexFile* target_dex_file, uint32_t pc_insn_offset, @@ -172,6 +182,7 @@ class LinkerPatch { bool IsPcRelative() const { switch (GetType()) { + case Type::kDataBimgRelRo: case Type::kMethodRelative: case Type::kMethodBssEntry: case Type::kCallRelative: @@ -188,6 +199,11 @@ class LinkerPatch { } } + uint32_t BootImageOffset() const { + DCHECK(patch_type_ == Type::kDataBimgRelRo); + return boot_image_offset_; + } + MethodReference TargetMethod() const { DCHECK(patch_type_ == Type::kMethodRelative || patch_type_ == Type::kMethodBssEntry || @@ -225,7 +241,8 @@ class LinkerPatch { } uint32_t PcInsnOffset() const { - DCHECK(patch_type_ == Type::kMethodRelative || + DCHECK(patch_type_ == Type::kDataBimgRelRo || + patch_type_ == Type::kMethodRelative || patch_type_ == Type::kMethodBssEntry || patch_type_ == Type::kTypeRelative || patch_type_ == Type::kTypeClassTable || @@ -263,10 +280,11 @@ class LinkerPatch { uint32_t literal_offset_ : 24; // Method code size up to 16MiB. Type patch_type_ : 8; union { - uint32_t cmp1_; // Used for relational operators. - uint32_t method_idx_; // Method index for Call/Method patches. - uint32_t type_idx_; // Type index for Type patches. - uint32_t string_idx_; // String index for String patches. + uint32_t cmp1_; // Used for relational operators. + uint32_t boot_image_offset_; // Data to write to the .data.bimg.rel.ro entry. + uint32_t method_idx_; // Method index for Call/Method patches. + uint32_t type_idx_; // Type index for Type patches. + uint32_t string_idx_; // String index for String patches. uint32_t baker_custom_value1_; static_assert(sizeof(method_idx_) == sizeof(cmp1_), "needed by relational operators"); static_assert(sizeof(type_idx_) == sizeof(cmp1_), "needed by relational operators"); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 1f9c5546e2..6bf3d86808 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -4459,12 +4459,23 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall( // Load method address from literal pool. __ Ldr(XRegisterFrom(temp), DeduplicateUint64Literal(invoke->GetMethodAddress())); break; + case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: { + // Add ADRP with its PC-relative .data.bimg.rel.ro patch. + uint32_t boot_image_offset = invoke->GetDispatchInfo().method_load_data; + vixl::aarch64::Label* adrp_label = NewBootImageRelRoPatch(boot_image_offset); + EmitAdrpPlaceholder(adrp_label, XRegisterFrom(temp)); + // Add LDR with its PC-relative .data.bimg.rel.ro patch. + vixl::aarch64::Label* ldr_label = NewBootImageRelRoPatch(boot_image_offset, adrp_label); + // Note: Boot image is in the low 4GiB and the entry is 32-bit, so emit a 32-bit load. + EmitLdrOffsetPlaceholder(ldr_label, WRegisterFrom(temp), XRegisterFrom(temp)); + break; + } case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: { - // Add ADRP with its PC-relative DexCache access patch. + // Add ADRP with its PC-relative .bss entry patch. MethodReference target_method(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()); vixl::aarch64::Label* adrp_label = NewMethodBssEntryPatch(target_method); EmitAdrpPlaceholder(adrp_label, XRegisterFrom(temp)); - // Add LDR with its PC-relative DexCache access patch. + // Add LDR with its PC-relative .bss entry patch. vixl::aarch64::Label* ldr_label = NewMethodBssEntryPatch(target_method, adrp_label); EmitLdrOffsetPlaceholder(ldr_label, XRegisterFrom(temp), XRegisterFrom(temp)); @@ -4559,6 +4570,13 @@ void InstructionCodeGeneratorARM64::VisitInvokePolymorphic(HInvokePolymorphic* i codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__); } +vixl::aarch64::Label* CodeGeneratorARM64::NewBootImageRelRoPatch( + uint32_t boot_image_offset, + vixl::aarch64::Label* adrp_label) { + return NewPcRelativePatch( + /* dex_file */ nullptr, boot_image_offset, adrp_label, &boot_image_method_patches_); +} + vixl::aarch64::Label* CodeGeneratorARM64::NewBootImageMethodPatch( MethodReference target_method, vixl::aarch64::Label* adrp_label) { @@ -4681,6 +4699,14 @@ inline void CodeGeneratorARM64::EmitPcRelativeLinkerPatches( } } +linker::LinkerPatch DataBimgRelRoPatchAdapter(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t boot_image_offset) { + DCHECK(target_dex_file == nullptr); // Unused for DataBimgRelRoPatch(), should be null. + return linker::LinkerPatch::DataBimgRelRoPatch(literal_offset, pc_insn_offset, boot_image_offset); +} + void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector* linker_patches) { DCHECK(linker_patches->empty()); size_t size = @@ -4700,7 +4726,8 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector* lin EmitPcRelativeLinkerPatches( boot_image_string_patches_, linker_patches); } else { - DCHECK(boot_image_method_patches_.empty()); + EmitPcRelativeLinkerPatches( + boot_image_method_patches_, linker_patches); EmitPcRelativeLinkerPatches( boot_image_type_patches_, linker_patches); EmitPcRelativeLinkerPatches( diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index e34f799d15..d78ad87c51 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -561,7 +561,14 @@ class CodeGeneratorARM64 : public CodeGenerator { UNIMPLEMENTED(FATAL); } - // Add a new PC-relative method patch for an instruction and return the label + // Add a new boot image relocation patch for an instruction and return the label + // to be bound before the instruction. The instruction will be either the + // ADRP (pass `adrp_label = null`) or the LDR (pass `adrp_label` pointing + // to the associated ADRP patch label). + vixl::aarch64::Label* NewBootImageRelRoPatch(uint32_t boot_image_offset, + vixl::aarch64::Label* adrp_label = nullptr); + + // Add a new boot image method patch for an instruction and return the label // to be bound before the instruction. The instruction will be either the // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing // to the associated ADRP patch label). @@ -575,7 +582,7 @@ class CodeGeneratorARM64 : public CodeGenerator { vixl::aarch64::Label* NewMethodBssEntryPatch(MethodReference target_method, vixl::aarch64::Label* adrp_label = nullptr); - // Add a new PC-relative type patch for an instruction and return the label + // Add a new boot image type patch for an instruction and return the label // to be bound before the instruction. The instruction will be either the // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing // to the associated ADRP patch label). @@ -591,7 +598,7 @@ class CodeGeneratorARM64 : public CodeGenerator { dex::TypeIndex type_index, vixl::aarch64::Label* adrp_label = nullptr); - // Add a new PC-relative string patch for an instruction and return the label + // Add a new boot image string patch for an instruction and return the label // to be bound before the instruction. The instruction will be either the // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing // to the associated ADRP patch label). @@ -820,7 +827,7 @@ class CodeGeneratorARM64 : public CodeGenerator { Uint32ToLiteralMap uint32_literals_; // Deduplication map for 64-bit literals, used for non-patchable method address or method code. Uint64ToLiteralMap uint64_literals_; - // PC-relative method patch info for kBootImageLinkTimePcRelative. + // PC-relative method patch info for kBootImageLinkTimePcRelative/BootImageRelRo. ArenaDeque boot_image_method_patches_; // PC-relative method patch info for kBssEntry. ArenaDeque method_bss_entry_patches_; diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 13518adf7d..13d5764280 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -8956,6 +8956,14 @@ void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall( case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: __ Mov(RegisterFrom(temp), Operand::From(invoke->GetMethodAddress())); break; + case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: { + uint32_t boot_image_offset = invoke->GetDispatchInfo().method_load_data; + PcRelativePatchInfo* labels = NewBootImageRelRoPatch(boot_image_offset); + vixl32::Register temp_reg = RegisterFrom(temp); + EmitMovwMovtPlaceholder(labels, temp_reg); + GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, temp_reg, /* offset*/ 0); + break; + } case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: { PcRelativePatchInfo* labels = NewMethodBssEntryPatch( MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex())); @@ -9053,6 +9061,13 @@ void CodeGeneratorARMVIXL::GenerateVirtualCall( } } +CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageRelRoPatch( + uint32_t boot_image_offset) { + return NewPcRelativePatch(/* dex_file */ nullptr, + boot_image_offset, + &boot_image_method_patches_); +} + CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageMethodPatch( MethodReference target_method) { return NewPcRelativePatch( @@ -9143,6 +9158,14 @@ inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches( } } +linker::LinkerPatch DataBimgRelRoPatchAdapter(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t boot_image_offset) { + DCHECK(target_dex_file == nullptr); // Unused for DataBimgRelRoPatch(), should be null. + return linker::LinkerPatch::DataBimgRelRoPatch(literal_offset, pc_insn_offset, boot_image_offset); +} + void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector* linker_patches) { DCHECK(linker_patches->empty()); size_t size = @@ -9162,7 +9185,8 @@ void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector* l EmitPcRelativeLinkerPatches( boot_image_string_patches_, linker_patches); } else { - DCHECK(boot_image_method_patches_.empty()); + EmitPcRelativeLinkerPatches( + boot_image_method_patches_, linker_patches); EmitPcRelativeLinkerPatches( boot_image_type_patches_, linker_patches); EmitPcRelativeLinkerPatches( diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index bbc715c59d..8a2ab711f2 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -574,6 +574,7 @@ class CodeGeneratorARMVIXL : public CodeGenerator { vixl::aarch32::Label add_pc_label; }; + PcRelativePatchInfo* NewBootImageRelRoPatch(uint32_t boot_image_offset); PcRelativePatchInfo* NewBootImageMethodPatch(MethodReference target_method); PcRelativePatchInfo* NewMethodBssEntryPatch(MethodReference target_method); PcRelativePatchInfo* NewBootImageTypePatch(const DexFile& dex_file, dex::TypeIndex type_index); @@ -798,7 +799,7 @@ class CodeGeneratorARMVIXL : public CodeGenerator { // Deduplication map for 32-bit literals, used for non-patchable boot image addresses. Uint32ToLiteralMap uint32_literals_; - // PC-relative method patch info for kBootImageLinkTimePcRelative. + // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo. ArenaDeque boot_image_method_patches_; // PC-relative method patch info for kBssEntry. ArenaDeque method_bss_entry_patches_; diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index eb5f72e953..931c9b033e 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -1597,6 +1597,14 @@ inline void CodeGeneratorMIPS::EmitPcRelativeLinkerPatches( } } +linker::LinkerPatch DataBimgRelRoPatchAdapter(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t boot_image_offset) { + DCHECK(target_dex_file == nullptr); // Unused for DataBimgRelRoPatch(), should be null. + return linker::LinkerPatch::DataBimgRelRoPatch(literal_offset, pc_insn_offset, boot_image_offset); +} + void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector* linker_patches) { DCHECK(linker_patches->empty()); size_t size = @@ -1615,7 +1623,8 @@ void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector* link EmitPcRelativeLinkerPatches( boot_image_string_patches_, linker_patches); } else { - DCHECK(boot_image_method_patches_.empty()); + EmitPcRelativeLinkerPatches( + boot_image_method_patches_, linker_patches); EmitPcRelativeLinkerPatches( boot_image_type_patches_, linker_patches); EmitPcRelativeLinkerPatches( @@ -1630,6 +1639,13 @@ void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector* link DCHECK_EQ(size, linker_patches->size()); } +CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewBootImageRelRoPatch( + uint32_t boot_image_offset, + const PcRelativePatchInfo* info_high) { + return NewPcRelativePatch( + /* dex_file */ nullptr, boot_image_offset, info_high, &boot_image_method_patches_); +} + CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewBootImageMethodPatch( MethodReference target_method, const PcRelativePatchInfo* info_high) { @@ -7835,6 +7851,15 @@ void CodeGeneratorMIPS::GenerateStaticOrDirectCall( case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: __ LoadConst32(temp.AsRegister(), invoke->GetMethodAddress()); break; + case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: { + uint32_t boot_image_offset = invoke->GetDispatchInfo().method_load_data; + PcRelativePatchInfo* info_high = NewBootImageRelRoPatch(boot_image_offset); + PcRelativePatchInfo* info_low = NewBootImageRelRoPatch(boot_image_offset, info_high); + Register temp_reg = temp.AsRegister(); + EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base_reg); + __ Lw(temp_reg, TMP, /* placeholder */ 0x5678, &info_low->label); + break; + } case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: { PcRelativePatchInfo* info_high = NewMethodBssEntryPatch( MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex())); diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index d09ab7cce0..3cb3b52807 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -617,6 +617,8 @@ class CodeGeneratorMIPS : public CodeGenerator { DISALLOW_COPY_AND_ASSIGN(PcRelativePatchInfo); }; + PcRelativePatchInfo* NewBootImageRelRoPatch(uint32_t boot_image_offset, + const PcRelativePatchInfo* info_high = nullptr); PcRelativePatchInfo* NewBootImageMethodPatch(MethodReference target_method, const PcRelativePatchInfo* info_high = nullptr); PcRelativePatchInfo* NewMethodBssEntryPatch(MethodReference target_method, @@ -691,7 +693,7 @@ class CodeGeneratorMIPS : public CodeGenerator { // Deduplication map for 32-bit literals, used for non-patchable boot image addresses. Uint32ToLiteralMap uint32_literals_; - // PC-relative method patch info for kBootImageLinkTimePcRelative. + // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo. ArenaDeque boot_image_method_patches_; // PC-relative method patch info for kBssEntry. ArenaDeque method_bss_entry_patches_; diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 9593eec455..78db1a397c 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1509,6 +1509,14 @@ inline void CodeGeneratorMIPS64::EmitPcRelativeLinkerPatches( } } +linker::LinkerPatch DataBimgRelRoPatchAdapter(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t boot_image_offset) { + DCHECK(target_dex_file == nullptr); // Unused for DataBimgRelRoPatch(), should be null. + return linker::LinkerPatch::DataBimgRelRoPatch(literal_offset, pc_insn_offset, boot_image_offset); +} + void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector* linker_patches) { DCHECK(linker_patches->empty()); size_t size = @@ -1527,7 +1535,8 @@ void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector* li EmitPcRelativeLinkerPatches( boot_image_string_patches_, linker_patches); } else { - DCHECK(boot_image_method_patches_.empty()); + EmitPcRelativeLinkerPatches( + boot_image_method_patches_, linker_patches); EmitPcRelativeLinkerPatches( boot_image_type_patches_, linker_patches); EmitPcRelativeLinkerPatches( @@ -1542,6 +1551,13 @@ void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector* li DCHECK_EQ(size, linker_patches->size()); } +CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewBootImageRelRoPatch( + uint32_t boot_image_offset, + const PcRelativePatchInfo* info_high) { + return NewPcRelativePatch( + /* dex_file */ nullptr, boot_image_offset, info_high, &boot_image_method_patches_); +} + CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewBootImageMethodPatch( MethodReference target_method, const PcRelativePatchInfo* info_high) { @@ -5926,6 +5942,15 @@ void CodeGeneratorMIPS64::GenerateStaticOrDirectCall( kLoadDoubleword, DeduplicateUint64Literal(invoke->GetMethodAddress())); break; + case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: { + uint32_t boot_image_offset = invoke->GetDispatchInfo().method_load_data; + PcRelativePatchInfo* info_high = NewBootImageRelRoPatch(boot_image_offset); + PcRelativePatchInfo* info_low = NewBootImageRelRoPatch(boot_image_offset, info_high); + EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low); + // Note: Boot image is in the low 4GiB and the entry is 32-bit, so emit a 32-bit load. + __ Lwu(temp.AsRegister(), AT, /* placeholder */ 0x5678); + break; + } case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: { PcRelativePatchInfo* info_high = NewMethodBssEntryPatch( MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex())); diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index ddeb3eb90c..9ff3f63384 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -586,6 +586,8 @@ class CodeGeneratorMIPS64 : public CodeGenerator { DISALLOW_COPY_AND_ASSIGN(PcRelativePatchInfo); }; + PcRelativePatchInfo* NewBootImageRelRoPatch(uint32_t boot_image_offset, + const PcRelativePatchInfo* info_high = nullptr); PcRelativePatchInfo* NewBootImageMethodPatch(MethodReference target_method, const PcRelativePatchInfo* info_high = nullptr); PcRelativePatchInfo* NewMethodBssEntryPatch(MethodReference target_method, @@ -655,7 +657,7 @@ class CodeGeneratorMIPS64 : public CodeGenerator { // Deduplication map for 64-bit literals, used for non-patchable method address or method code // address. Uint64ToLiteralMap uint64_literals_; - // PC-relative method patch info for kBootImageLinkTimePcRelative. + // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo. ArenaDeque boot_image_method_patches_; // PC-relative method patch info for kBssEntry. ArenaDeque method_bss_entry_patches_; diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 51b96be0f8..b9ecfeebb6 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -4624,6 +4624,15 @@ void CodeGeneratorX86::GenerateStaticOrDirectCall( case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: __ movl(temp.AsRegister(), Immediate(invoke->GetMethodAddress())); break; + case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: { + Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke, + temp.AsRegister()); + __ movl(temp.AsRegister(), Address(base_reg, kDummy32BitOffset)); + RecordBootImageRelRoPatch( + invoke->InputAt(invoke->GetSpecialInputIndex())->AsX86ComputeBaseMethodAddress(), + invoke->GetDispatchInfo().method_load_data); + break; + } case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: { Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke, temp.AsRegister()); @@ -4685,6 +4694,13 @@ void CodeGeneratorX86::GenerateVirtualCall( RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); } +void CodeGeneratorX86::RecordBootImageRelRoPatch(HX86ComputeBaseMethodAddress* method_address, + uint32_t boot_image_offset) { + boot_image_method_patches_.emplace_back( + method_address, /* target_dex_file */ nullptr, boot_image_offset); + __ Bind(&boot_image_method_patches_.back().label); +} + void CodeGeneratorX86::RecordBootImageMethodPatch(HInvokeStaticOrDirect* invoke) { DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u); HX86ComputeBaseMethodAddress* method_address = @@ -4754,6 +4770,14 @@ inline void CodeGeneratorX86::EmitPcRelativeLinkerPatches( } } +linker::LinkerPatch DataBimgRelRoPatchAdapter(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t boot_image_offset) { + DCHECK(target_dex_file == nullptr); // Unused for DataBimgRelRoPatch(), should be null. + return linker::LinkerPatch::DataBimgRelRoPatch(literal_offset, pc_insn_offset, boot_image_offset); +} + void CodeGeneratorX86::EmitLinkerPatches(ArenaVector* linker_patches) { DCHECK(linker_patches->empty()); size_t size = @@ -4772,7 +4796,8 @@ void CodeGeneratorX86::EmitLinkerPatches(ArenaVector* linke EmitPcRelativeLinkerPatches( boot_image_string_patches_, linker_patches); } else { - DCHECK(boot_image_method_patches_.empty()); + EmitPcRelativeLinkerPatches( + boot_image_method_patches_, linker_patches); EmitPcRelativeLinkerPatches( boot_image_type_patches_, linker_patches); EmitPcRelativeLinkerPatches( diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 51e5bca00b..e21ccb5fe3 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -414,6 +414,8 @@ class CodeGeneratorX86 : public CodeGenerator { void GenerateVirtualCall( HInvokeVirtual* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE; + void RecordBootImageRelRoPatch(HX86ComputeBaseMethodAddress* method_address, + uint32_t boot_image_offset); void RecordBootImageMethodPatch(HInvokeStaticOrDirect* invoke); void RecordMethodBssEntryPatch(HInvokeStaticOrDirect* invoke); void RecordBootImageTypePatch(HLoadClass* load_class); @@ -631,7 +633,7 @@ class CodeGeneratorX86 : public CodeGenerator { X86Assembler assembler_; const X86InstructionSetFeatures& isa_features_; - // PC-relative method patch info for kBootImageLinkTimePcRelative. + // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo. ArenaDeque boot_image_method_patches_; // PC-relative method patch info for kBssEntry. ArenaDeque method_bss_entry_patches_; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 0bb56a2b4a..728e375ad8 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -998,6 +998,13 @@ void CodeGeneratorX86_64::GenerateStaticOrDirectCall( case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: Load64BitValue(temp.AsRegister(), invoke->GetMethodAddress()); break; + case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: { + // Note: Boot image is in the low 4GiB and the entry is 32-bit, so emit a 32-bit load. + __ movl(temp.AsRegister(), + Address::Absolute(kDummy32BitOffset, /* no_rip */ false)); + RecordBootImageRelRoPatch(invoke->GetDispatchInfo().method_load_data); + break; + } case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: { __ movq(temp.AsRegister(), Address::Absolute(kDummy32BitOffset, /* no_rip */ false)); @@ -1059,6 +1066,11 @@ void CodeGeneratorX86_64::GenerateVirtualCall( RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); } +void CodeGeneratorX86_64::RecordBootImageRelRoPatch(uint32_t boot_image_offset) { + boot_image_method_patches_.emplace_back(/* target_dex_file */ nullptr, boot_image_offset); + __ Bind(&boot_image_method_patches_.back().label); +} + void CodeGeneratorX86_64::RecordBootImageMethodPatch(HInvokeStaticOrDirect* invoke) { boot_image_method_patches_.emplace_back( invoke->GetTargetMethod().dex_file, invoke->GetTargetMethod().index); @@ -1110,6 +1122,14 @@ inline void CodeGeneratorX86_64::EmitPcRelativeLinkerPatches( } } +linker::LinkerPatch DataBimgRelRoPatchAdapter(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t boot_image_offset) { + DCHECK(target_dex_file == nullptr); // Unused for DataBimgRelRoPatch(), should be null. + return linker::LinkerPatch::DataBimgRelRoPatch(literal_offset, pc_insn_offset, boot_image_offset); +} + void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector* linker_patches) { DCHECK(linker_patches->empty()); size_t size = @@ -1128,7 +1148,8 @@ void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector* li EmitPcRelativeLinkerPatches( boot_image_string_patches_, linker_patches); } else { - DCHECK(boot_image_method_patches_.empty()); + EmitPcRelativeLinkerPatches( + boot_image_method_patches_, linker_patches); EmitPcRelativeLinkerPatches( boot_image_type_patches_, linker_patches); EmitPcRelativeLinkerPatches( diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 1079e94dfc..0637b274f6 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -410,6 +410,7 @@ class CodeGeneratorX86_64 : public CodeGenerator { void GenerateVirtualCall( HInvokeVirtual* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE; + void RecordBootImageRelRoPatch(uint32_t boot_image_offset); void RecordBootImageMethodPatch(HInvokeStaticOrDirect* invoke); void RecordMethodBssEntryPatch(HInvokeStaticOrDirect* invoke); void RecordBootImageTypePatch(HLoadClass* load_class); @@ -604,7 +605,7 @@ class CodeGeneratorX86_64 : public CodeGenerator { // Used for fixups to the constant area. int constant_area_start_; - // PC-relative method patch info for kBootImageLinkTimePcRelative. + // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo. ArenaDeque> boot_image_method_patches_; // PC-relative method patch info for kBssEntry. ArenaDeque> method_bss_entry_patches_; diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index f6ba19f22a..a8ddb7cfdc 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -2891,6 +2891,8 @@ std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::MethodLoadKind return os << "BootImageLinkTimePcRelative"; case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: return os << "DirectAddress"; + case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: + return os << "BootImageRelRo"; case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: return os << "BssEntry"; case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 99d80d77c5..7f2d43fbd5 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -4428,6 +4428,10 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { // Used for app->boot calls with non-relocatable image and for JIT-compiled calls. kDirectAddress, + // Load from an entry in the .data.bimg.rel.ro using a PC-relative load. + // Used for app->boot calls with relocatable image. + kBootImageRelRo, + // Load from an entry in the .bss section using a PC-relative load. // Used for classes outside boot image when .bss is accessible with a PC-relative load. kBssEntry, @@ -4560,6 +4564,7 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { bool HasMethodAddress() const { return GetMethodLoadKind() == MethodLoadKind::kDirectAddress; } bool HasPcRelativeMethodLoadKind() const { return GetMethodLoadKind() == MethodLoadKind::kBootImageLinkTimePcRelative || + GetMethodLoadKind() == MethodLoadKind::kBootImageRelRo || GetMethodLoadKind() == MethodLoadKind::kBssEntry; } bool HasCurrentMethodInput() const { diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index 1e49411c72..b65628e441 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -125,8 +125,14 @@ void HSharpening::SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, BootImageAOTCanEmbedMethod(callee, compiler_driver)) { method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kBootImageLinkTimePcRelative; code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; + } else if (IsInBootImage(callee)) { + // Use PC-relative access to the .data.bimg.rel.ro methods array. + method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo; + uint8_t* begin = Runtime::Current()->GetHeap()->GetBootImageSpaces().front()->Begin(); + method_load_data = reinterpret_cast(callee) - reinterpret_cast(begin); + code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; } else { - // Use PC-relative access to the .bss methods arrays. + // Use PC-relative access to the .bss methods array. method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kBssEntry; code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; } diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 926575e10c..931ff1643c 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -2067,11 +2067,9 @@ class Dex2Oat FINAL { std::unique_ptr& oat_writer = oat_writers_[i]; oat_writer->PrepareLayout(&patcher); - - size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset(); - size_t text_size = oat_writer->GetOatSize() - rodata_size; - elf_writer->PrepareDynamicSection(rodata_size, - text_size, + elf_writer->PrepareDynamicSection(oat_writer->GetOatHeader().GetExecutableOffset(), + oat_writer->GetCodeSize(), + oat_writer->GetDataBimgRelRoSize(), oat_writer->GetBssSize(), oat_writer->GetBssMethodsOffset(), oat_writer->GetBssRootsOffset(), @@ -2121,6 +2119,16 @@ class Dex2Oat FINAL { } elf_writer->EndText(text); + if (oat_writer->GetDataBimgRelRoSize() != 0u) { + linker::OutputStream* data_bimg_rel_ro = elf_writer->StartDataBimgRelRo(); + if (!oat_writer->WriteDataBimgRelRo(data_bimg_rel_ro)) { + LOG(ERROR) << "Failed to write .data.bimg.rel.ro section to the ELF file " + << oat_file->GetPath(); + return false; + } + elf_writer->EndDataBimgRelRo(data_bimg_rel_ro); + } + if (!oat_writer->WriteHeader(elf_writer->GetStream(), image_file_location_oat_checksum_, image_file_location_oat_data_begin_, diff --git a/dex2oat/linker/elf_writer.h b/dex2oat/linker/elf_writer.h index 7c4774038e..8b26bf83ef 100644 --- a/dex2oat/linker/elf_writer.h +++ b/dex2oat/linker/elf_writer.h @@ -63,6 +63,7 @@ class ElfWriter { // This method must be called before calling GetLoadedSize(). virtual void PrepareDynamicSection(size_t rodata_size, size_t text_size, + size_t data_bimg_rel_ro_size, size_t bss_size, size_t bss_methods_offset, size_t bss_roots_offset, @@ -72,6 +73,8 @@ class ElfWriter { virtual void EndRoData(OutputStream* rodata) = 0; virtual OutputStream* StartText() = 0; virtual void EndText(OutputStream* text) = 0; + virtual OutputStream* StartDataBimgRelRo() = 0; + virtual void EndDataBimgRelRo(OutputStream* data_bimg_rel_ro) = 0; virtual void WriteDynamicSection() = 0; virtual void WriteDebugInfo(const debug::DebugInfo& debug_info) = 0; virtual bool End() = 0; diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc index d2e863c2a8..ece57da072 100644 --- a/dex2oat/linker/elf_writer_quick.cc +++ b/dex2oat/linker/elf_writer_quick.cc @@ -105,6 +105,7 @@ class ElfWriterQuick FINAL : public ElfWriter { void Start() OVERRIDE; void PrepareDynamicSection(size_t rodata_size, size_t text_size, + size_t data_bimg_rel_ro_size, size_t bss_size, size_t bss_methods_offset, size_t bss_roots_offset, @@ -114,6 +115,8 @@ class ElfWriterQuick FINAL : public ElfWriter { void EndRoData(OutputStream* rodata) OVERRIDE; OutputStream* StartText() OVERRIDE; void EndText(OutputStream* text) OVERRIDE; + OutputStream* StartDataBimgRelRo() OVERRIDE; + void EndDataBimgRelRo(OutputStream* data_bimg_rel_ro) OVERRIDE; void WriteDynamicSection() OVERRIDE; void WriteDebugInfo(const debug::DebugInfo& debug_info) OVERRIDE; bool End() OVERRIDE; @@ -131,6 +134,7 @@ class ElfWriterQuick FINAL : public ElfWriter { File* const elf_file_; size_t rodata_size_; size_t text_size_; + size_t data_bimg_rel_ro_size_; size_t bss_size_; size_t dex_section_size_; std::unique_ptr output_stream_; @@ -171,6 +175,7 @@ ElfWriterQuick::ElfWriterQuick(InstructionSet instruction_set, elf_file_(elf_file), rodata_size_(0u), text_size_(0u), + data_bimg_rel_ro_size_(0u), bss_size_(0u), dex_section_size_(0u), output_stream_( @@ -192,6 +197,7 @@ void ElfWriterQuick::Start() { template void ElfWriterQuick::PrepareDynamicSection(size_t rodata_size, size_t text_size, + size_t data_bimg_rel_ro_size, size_t bss_size, size_t bss_methods_offset, size_t bss_roots_offset, @@ -200,6 +206,8 @@ void ElfWriterQuick::PrepareDynamicSection(size_t rodata_size, rodata_size_ = rodata_size; DCHECK_EQ(text_size_, 0u); text_size_ = text_size; + DCHECK_EQ(data_bimg_rel_ro_size_, 0u); + data_bimg_rel_ro_size_ = data_bimg_rel_ro_size; DCHECK_EQ(bss_size_, 0u); bss_size_ = bss_size; DCHECK_EQ(dex_section_size_, 0u); @@ -207,6 +215,7 @@ void ElfWriterQuick::PrepareDynamicSection(size_t rodata_size, builder_->PrepareDynamicSection(elf_file_->GetPath(), rodata_size_, text_size_, + data_bimg_rel_ro_size_, bss_size_, bss_methods_offset, bss_roots_offset, @@ -239,6 +248,19 @@ void ElfWriterQuick::EndText(OutputStream* text) { builder_->GetText()->End(); } +template +OutputStream* ElfWriterQuick::StartDataBimgRelRo() { + auto* data_bimg_rel_ro = builder_->GetDataBimgRelRo(); + data_bimg_rel_ro->Start(); + return data_bimg_rel_ro; +} + +template +void ElfWriterQuick::EndDataBimgRelRo(OutputStream* data_bimg_rel_ro) { + CHECK_EQ(builder_->GetDataBimgRelRo(), data_bimg_rel_ro); + builder_->GetDataBimgRelRo()->End(); +} + template void ElfWriterQuick::WriteDynamicSection() { if (builder_->GetIsa() == InstructionSet::kMips || diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index c6ce951506..917bef2661 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -313,10 +313,9 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, oat_writer->WriteChecksumsAndVdexHeader(vdex_out.get()); oat_writer->PrepareLayout(&patcher); - size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset(); - size_t text_size = oat_writer->GetOatSize() - rodata_size; - elf_writer->PrepareDynamicSection(rodata_size, - text_size, + elf_writer->PrepareDynamicSection(oat_writer->GetOatHeader().GetExecutableOffset(), + oat_writer->GetCodeSize(), + oat_writer->GetDataBimgRelRoSize(), oat_writer->GetBssSize(), oat_writer->GetBssMethodsOffset(), oat_writer->GetBssRootsOffset(), @@ -336,6 +335,13 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, ASSERT_TRUE(text_ok); elf_writer->EndText(text); + if (oat_writer->GetDataBimgRelRoSize() != 0u) { + OutputStream* data_bimg_rel_ro = elf_writer->StartDataBimgRelRo(); + bool data_bimg_rel_ro_ok = oat_writer->WriteDataBimgRelRo(data_bimg_rel_ro); + ASSERT_TRUE(data_bimg_rel_ro_ok); + elf_writer->EndDataBimgRelRo(data_bimg_rel_ro); + } + bool header_ok = oat_writer->WriteHeader(elf_writer->GetStream(), 0u, 0u, 0u); ASSERT_TRUE(header_ok); diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 2feb14a357..c9b477a9db 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -375,11 +375,15 @@ OatWriter::OatWriter(bool compiling_boot_image, vdex_dex_shared_data_offset_(0u), vdex_verifier_deps_offset_(0u), vdex_quickening_info_offset_(0u), + code_size_(0u), oat_size_(0u), + data_bimg_rel_ro_start_(0u), + data_bimg_rel_ro_size_(0u), bss_start_(0u), bss_size_(0u), bss_methods_offset_(0u), bss_roots_offset_(0u), + data_bimg_rel_ro_entries_(), bss_method_entry_references_(), bss_method_entries_(), bss_type_entries_(), @@ -409,6 +413,8 @@ OatWriter::OatWriter(bool compiling_boot_image, size_method_header_(0), size_code_(0), size_code_alignment_(0), + size_data_bimg_rel_ro_(0), + size_data_bimg_rel_ro_alignment_(0), size_relative_call_thunks_(0), size_misc_thunks_(0), size_vmap_table_(0), @@ -737,8 +743,13 @@ void OatWriter::PrepareLayout(MultiOatRelativePatcher* relative_patcher) { { TimingLogger::ScopedTiming split("InitOatCodeDexFiles", timings_); offset = InitOatCodeDexFiles(offset); + code_size_ = offset - GetOatHeader().GetExecutableOffset(); } - oat_size_ = offset; + { + TimingLogger::ScopedTiming split("InitDataBimgRelRoLayout", timings_); + offset = InitDataBimgRelRoLayout(offset); + } + oat_size_ = offset; // .bss does not count towards oat_size_. bss_start_ = (bss_size_ != 0u) ? RoundUp(oat_size_, kPageSize) : 0u; CHECK_EQ(dex_files_->size(), oat_dex_files_.size()); @@ -845,7 +856,10 @@ class OatWriter::InitBssLayoutMethodVisitor : public DexMethodVisitor { MethodReference(dex_file_, it.GetMemberIndex())); if (HasCompiledCode(compiled_method)) { for (const LinkerPatch& patch : compiled_method->GetPatches()) { - if (patch.GetType() == LinkerPatch::Type::kMethodBssEntry) { + if (patch.GetType() == LinkerPatch::Type::kDataBimgRelRo) { + writer_->data_bimg_rel_ro_entries_.Overwrite(patch.BootImageOffset(), + /* placeholder */ 0u); + } else if (patch.GetType() == LinkerPatch::Type::kMethodBssEntry) { MethodReference target_method = patch.TargetMethod(); AddBssReference(target_method, target_method.dex_file->NumMethodIds(), @@ -1776,6 +1790,16 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { for (const LinkerPatch& patch : compiled_method->GetPatches()) { uint32_t literal_offset = patch.LiteralOffset(); switch (patch.GetType()) { + case LinkerPatch::Type::kDataBimgRelRo: { + uint32_t target_offset = + writer_->data_bimg_rel_ro_start_ + + writer_->data_bimg_rel_ro_entries_.Get(patch.BootImageOffset()); + writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, + patch, + offset_ + literal_offset, + target_offset); + break; + } case LinkerPatch::Type::kMethodBssEntry: { uint32_t target_offset = writer_->bss_start_ + writer_->bss_method_entries_.Get(patch.TargetMethod()); @@ -2510,6 +2534,25 @@ size_t OatWriter::InitOatCodeDexFiles(size_t offset) { return offset; } +size_t OatWriter::InitDataBimgRelRoLayout(size_t offset) { + DCHECK_EQ(data_bimg_rel_ro_size_, 0u); + if (data_bimg_rel_ro_entries_.empty()) { + // Nothing to put to the .data.bimg.rel.ro section. + return offset; + } + + data_bimg_rel_ro_start_ = RoundUp(offset, kPageSize); + + for (auto& entry : data_bimg_rel_ro_entries_) { + size_t& entry_offset = entry.second; + entry_offset = data_bimg_rel_ro_size_; + data_bimg_rel_ro_size_ += sizeof(uint32_t); + } + + offset = data_bimg_rel_ro_start_ + data_bimg_rel_ro_size_; + return offset; +} + void OatWriter::InitBssLayout(InstructionSet instruction_set) { { InitBssLayoutMethodVisitor visitor(this); @@ -2905,6 +2948,49 @@ bool OatWriter::WriteCode(OutputStream* out) { return false; } + if (data_bimg_rel_ro_size_ != 0u) { + write_state_ = WriteState::kWriteDataBimgRelRo; + } else { + if (!CheckOatSize(out, file_offset, relative_offset)) { + return false; + } + write_state_ = WriteState::kWriteHeader; + } + return true; +} + +bool OatWriter::WriteDataBimgRelRo(OutputStream* out) { + CHECK(write_state_ == WriteState::kWriteDataBimgRelRo); + + // Wrap out to update checksum with each write. + ChecksumUpdatingOutputStream checksum_updating_out(out, oat_header_.get()); + out = &checksum_updating_out; + + const size_t file_offset = oat_data_offset_; + size_t relative_offset = data_bimg_rel_ro_start_; + + // Record the padding before the .data.bimg.rel.ro section. + // Do not write anything, this zero-filled part was skipped (Seek()) when starting the section. + size_t code_end = GetOatHeader().GetExecutableOffset() + code_size_; + DCHECK_EQ(RoundUp(code_end, kPageSize), relative_offset); + size_t padding_size = relative_offset - code_end; + DCHECK_EQ(size_data_bimg_rel_ro_alignment_, 0u); + size_data_bimg_rel_ro_alignment_ = padding_size; + + relative_offset = WriteDataBimgRelRo(out, file_offset, relative_offset); + if (relative_offset == 0) { + LOG(ERROR) << "Failed to write boot image relocations to " << out->GetLocation(); + return false; + } + + if (!CheckOatSize(out, file_offset, relative_offset)) { + return false; + } + write_state_ = WriteState::kWriteHeader; + return true; +} + +bool OatWriter::CheckOatSize(OutputStream* out, size_t file_offset, size_t relative_offset) { const off_t oat_end_file_offset = out->Seek(0, kSeekCurrent); if (oat_end_file_offset == static_cast(-1)) { LOG(ERROR) << "Failed to get oat end file offset in " << out->GetLocation(); @@ -2939,6 +3025,8 @@ bool OatWriter::WriteCode(OutputStream* out) { DO_STAT(size_method_header_); DO_STAT(size_code_); DO_STAT(size_code_alignment_); + DO_STAT(size_data_bimg_rel_ro_); + DO_STAT(size_data_bimg_rel_ro_alignment_); DO_STAT(size_relative_call_thunks_); DO_STAT(size_misc_thunks_); DO_STAT(size_vmap_table_); @@ -3316,6 +3404,32 @@ size_t OatWriter::WriteCodeDexFiles(OutputStream* out, return relative_offset; } +size_t OatWriter::WriteDataBimgRelRo(OutputStream* out, + size_t file_offset, + size_t relative_offset) { + if (data_bimg_rel_ro_entries_.empty()) { + return relative_offset; + } + + // Write the entire .data.bimg.rel.ro with a single WriteFully(). + std::vector data; + data.reserve(data_bimg_rel_ro_entries_.size()); + for (const auto& entry : data_bimg_rel_ro_entries_) { + uint32_t boot_image_offset = entry.first; + data.push_back(boot_image_offset); + } + DCHECK_EQ(data.size(), data_bimg_rel_ro_entries_.size()); + DCHECK_OFFSET(); + if (!out->WriteFully(data.data(), data.size() * sizeof(data[0]))) { + PLOG(ERROR) << "Failed to write .data.bimg.rel.ro in " << out->GetLocation(); + return 0u; + } + DCHECK_EQ(size_data_bimg_rel_ro_, 0u); + size_data_bimg_rel_ro_ = data.size() * sizeof(data[0]); + relative_offset += size_data_bimg_rel_ro_; + return relative_offset; +} + bool OatWriter::RecordOatDataOffset(OutputStream* out) { // Get the elf file offset of the oat file. const off_t raw_file_offset = out->Seek(0, kSeekCurrent); diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index d67e4dedfc..8e18f9bb64 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -137,6 +137,7 @@ class OatWriter { // - PrepareLayout(), // - WriteRodata(), // - WriteCode(), + // - WriteDataBimgRelRo() iff GetDataBimgRelRoSize() != 0, // - WriteHeader(). // Add dex file source(s) from a file, either a plain dex file or @@ -197,6 +198,10 @@ class OatWriter { bool WriteRodata(OutputStream* out); // Write the code to the .text section. bool WriteCode(OutputStream* out); + // Write the boot image relocation data to the .data.bimg.rel.ro section. + bool WriteDataBimgRelRo(OutputStream* out); + // Check the size of the written oat file. + bool CheckOatSize(OutputStream* out, size_t file_offset, size_t relative_offset); // Write the oat header. This finalizes the oat file. bool WriteHeader(OutputStream* out, uint32_t image_file_location_oat_checksum, @@ -218,10 +223,18 @@ class OatWriter { return *oat_header_; } + size_t GetCodeSize() const { + return code_size_; + } + size_t GetOatSize() const { return oat_size_; } + size_t GetDataBimgRelRoSize() const { + return data_bimg_rel_ro_size_; + } + size_t GetBssSize() const { return bss_size_; } @@ -323,6 +336,7 @@ class OatWriter { size_t InitOatDexFiles(size_t offset); size_t InitOatCode(size_t offset); size_t InitOatCodeDexFiles(size_t offset); + size_t InitDataBimgRelRoLayout(size_t offset); void InitBssLayout(InstructionSet instruction_set); size_t WriteClassOffsets(OutputStream* out, size_t file_offset, size_t relative_offset); @@ -332,6 +346,7 @@ class OatWriter { size_t WriteOatDexFiles(OutputStream* out, size_t file_offset, size_t relative_offset); size_t WriteCode(OutputStream* out, size_t file_offset, size_t relative_offset); size_t WriteCodeDexFiles(OutputStream* out, size_t file_offset, size_t relative_offset); + size_t WriteDataBimgRelRo(OutputStream* out, size_t file_offset, size_t relative_offset); bool RecordOatDataOffset(OutputStream* out); bool WriteTypeLookupTables(OutputStream* oat_rodata, @@ -360,6 +375,7 @@ class OatWriter { kPrepareLayout, kWriteRoData, kWriteText, + kWriteDataBimgRelRo, kWriteHeader, kDone }; @@ -401,9 +417,18 @@ class OatWriter { // Offset of section holding quickening info inside Vdex. size_t vdex_quickening_info_offset_; + // Size of the .text segment. + size_t code_size_; + // Size required for Oat data structures. size_t oat_size_; + // The start of the required .data.bimg.rel.ro section. + size_t data_bimg_rel_ro_start_; + + // The size of the required .data.bimg.rel.ro section holding the boot image relocations. + size_t data_bimg_rel_ro_size_; + // The start of the required .bss section. size_t bss_start_; @@ -416,6 +441,10 @@ class OatWriter { // The offset of the GC roots in .bss section. size_t bss_roots_offset_; + // Map for allocating .data.bimg.rel.ro entries. Indexed by the boot image offset of the + // relocation. The value is the assigned offset within the .data.bimg.rel.ro section. + SafeMap data_bimg_rel_ro_entries_; + // Map for recording references to ArtMethod entries in .bss. SafeMap bss_method_entry_references_; @@ -484,6 +513,8 @@ class OatWriter { uint32_t size_method_header_; uint32_t size_code_; uint32_t size_code_alignment_; + uint32_t size_data_bimg_rel_ro_; + uint32_t size_data_bimg_rel_ro_alignment_; uint32_t size_relative_call_thunks_; uint32_t size_misc_thunks_; uint32_t size_vmap_table_; diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 00b9abe69b..f713ed6faa 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -216,10 +216,9 @@ class OatTest : public CommonCompilerTest { instruction_set_features_.get()); oat_writer.Initialize(compiler_driver_.get(), nullptr, dex_files); oat_writer.PrepareLayout(&patcher); - size_t rodata_size = oat_writer.GetOatHeader().GetExecutableOffset(); - size_t text_size = oat_writer.GetOatSize() - rodata_size; - elf_writer->PrepareDynamicSection(rodata_size, - text_size, + elf_writer->PrepareDynamicSection(oat_writer.GetOatHeader().GetExecutableOffset(), + oat_writer.GetCodeSize(), + oat_writer.GetDataBimgRelRoSize(), oat_writer.GetBssSize(), oat_writer.GetBssMethodsOffset(), oat_writer.GetBssRootsOffset(), @@ -248,6 +247,14 @@ class OatTest : public CommonCompilerTest { } elf_writer->EndText(text); + if (oat_writer.GetDataBimgRelRoSize() != 0u) { + OutputStream* data_bimg_rel_ro = elf_writer->StartDataBimgRelRo(); + if (!oat_writer.WriteDataBimgRelRo(data_bimg_rel_ro)) { + return false; + } + elf_writer->EndDataBimgRelRo(data_bimg_rel_ro); + } + if (!oat_writer.WriteHeader(elf_writer->GetStream(), 42U, 4096U, 0)) { return false; } diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 85c7281102..80fa2156f1 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -172,6 +172,7 @@ class OatSymbolizer FINAL { builder_->PrepareDynamicSection(elf_file->GetPath(), rodata_size, text_size, + oat_file_->DataBimgRelRoSize(), oat_file_->BssSize(), oat_file_->BssMethodsOffset(), oat_file_->BssRootsOffset(), diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 0c205568fa..2e625a7048 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3360,9 +3360,10 @@ void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, CHECK_EQ(dex_cache_location, dex_file_suffix); const OatFile* oat_file = (dex_file.GetOatDexFile() != nullptr) ? dex_file.GetOatDexFile()->GetOatFile() : nullptr; - // Clean up pass to remove null dex caches. Also check if we need to initialize OatFile .bss. - // Null dex caches can occur due to class unloading and we are lazily removing null entries. - bool initialize_oat_file_bss = (oat_file != nullptr); + // Clean up pass to remove null dex caches; null dex caches can occur due to class unloading + // and we are lazily removing null entries. Also check if we need to initialize OatFile data + // (.data.bimg.rel.ro and .bss sections) needed for code execution. + bool initialize_oat_file_data = (oat_file != nullptr) && oat_file->IsExecutable(); JavaVMExt* const vm = self->GetJniEnv()->GetVm(); for (auto it = dex_caches_.begin(); it != dex_caches_.end(); ) { DexCacheData data = *it; @@ -3370,15 +3371,36 @@ void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, vm->DeleteWeakGlobalRef(self, data.weak_root); it = dex_caches_.erase(it); } else { - if (initialize_oat_file_bss && + if (initialize_oat_file_data && it->dex_file->GetOatDexFile() != nullptr && it->dex_file->GetOatDexFile()->GetOatFile() == oat_file) { - initialize_oat_file_bss = false; // Already initialized. + initialize_oat_file_data = false; // Already initialized. } ++it; } } - if (initialize_oat_file_bss) { + if (initialize_oat_file_data) { + // Initialize the .data.bimg.rel.ro section. + if (!oat_file->GetBootImageRelocations().empty()) { + uint8_t* reloc_begin = const_cast(oat_file->DataBimgRelRoBegin()); + CheckedCall(mprotect, + "un-protect boot image relocations", + reloc_begin, + oat_file->DataBimgRelRoSize(), + PROT_READ | PROT_WRITE); + uint32_t boot_image_begin = dchecked_integral_cast(reinterpret_cast( + Runtime::Current()->GetHeap()->GetBootImageSpaces().front()->Begin())); + for (const uint32_t& relocation : oat_file->GetBootImageRelocations()) { + const_cast(relocation) += boot_image_begin; + } + CheckedCall(mprotect, + "protect boot image relocations", + reloc_begin, + oat_file->DataBimgRelRoSize(), + PROT_READ); + } + + // Initialize the .bss section. // TODO: Pre-initialize from boot/app image? ArtMethod* resolution_method = Runtime::Current()->GetResolutionMethod(); for (ArtMethod*& entry : oat_file->GetBssMethods()) { diff --git a/runtime/oat.h b/runtime/oat.h index 292c9d6f41..0fa1d4b4c2 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: Math.pow() intrinsic. - static constexpr uint8_t kOatVersion[] = { '1', '3', '8', '\0' }; + // Last oat version changed reason: Retrieve ArtMethod* from .data.bimg.rel.ro . + static constexpr uint8_t kOatVersion[] = { '1', '3', '9', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 3576683fee..20297e0d70 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -343,6 +343,19 @@ bool OatFileBase::ComputeFields(uint8_t* requested_base, // Readjust to be non-inclusive upper bound. end_ += sizeof(uint32_t); + data_bimg_rel_ro_begin_ = FindDynamicSymbolAddress("oatdatabimgrelro", &symbol_error_msg); + if (data_bimg_rel_ro_begin_ != nullptr) { + data_bimg_rel_ro_end_ = + FindDynamicSymbolAddress("oatdatabimgrelrolastword", &symbol_error_msg); + if (data_bimg_rel_ro_end_ == nullptr) { + *error_msg = + StringPrintf("Failed to find oatdatabimgrelrolastword symbol in '%s'", file_path.c_str()); + return false; + } + // Readjust to be non-inclusive upper bound. + data_bimg_rel_ro_end_ += sizeof(uint32_t); + } + bss_begin_ = const_cast(FindDynamicSymbolAddress("oatbss", &symbol_error_msg)); if (bss_begin_ == nullptr) { // No .bss section. @@ -536,6 +549,17 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { } const uint8_t* oat = Begin() + oat_dex_files_offset; // Jump to the OatDexFile records. + if (!IsAligned(data_bimg_rel_ro_begin_) || + !IsAligned(data_bimg_rel_ro_end_) || + data_bimg_rel_ro_begin_ > data_bimg_rel_ro_end_) { + *error_msg = StringPrintf("In oat file '%s' found unaligned or unordered databimgrelro " + "symbol(s): begin = %p, end = %p", + GetLocation().c_str(), + data_bimg_rel_ro_begin_, + data_bimg_rel_ro_end_); + return false; + } + DCHECK_GE(static_cast(pointer_size), alignof(GcRoot)); if (!IsAligned(bss_begin_) || !IsAlignedParam(bss_methods_, static_cast(pointer_size)) || @@ -849,8 +873,29 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { } } + Runtime* runtime = Runtime::Current(); + + if (DataBimgRelRoBegin() != nullptr) { + // Make .data.bimg.rel.ro read only. ClassLinker shall make it writable for relocation. + uint8_t* reloc_begin = const_cast(DataBimgRelRoBegin()); + CheckedCall(mprotect, "protect relocations", reloc_begin, DataBimgRelRoSize(), PROT_READ); + if (UNLIKELY(runtime == nullptr)) { + // This must be oatdump without boot image. + } else if (!IsExecutable()) { + // Do not check whether we have a boot image if the oat file is not executable. + } else if (UNLIKELY(runtime->GetHeap()->GetBootImageSpaces().empty())) { + *error_msg = StringPrintf("Cannot load oat file '%s' with .data.bimg.rel.ro as executable " + "without boot image.", + GetLocation().c_str()); + return false; + } else { + // ClassLinker shall perform the relocation when we register a dex file from + // this oat file. We do not do the relocation here to avoid dirtying the pages + // if the code is never actually ready to be executed. + } + } + if (boot_image_tables != nullptr) { - Runtime* runtime = Runtime::Current(); if (UNLIKELY(runtime == nullptr)) { // This must be oatdump without boot image. Make sure the .bss is inaccessible. CheckedCall(mprotect, "protect bss", const_cast(BssBegin()), BssSize(), PROT_NONE); @@ -1513,6 +1558,8 @@ OatFile::OatFile(const std::string& location, bool is_executable) vdex_(nullptr), begin_(nullptr), end_(nullptr), + data_bimg_rel_ro_begin_(nullptr), + data_bimg_rel_ro_end_(nullptr), bss_begin_(nullptr), bss_end_(nullptr), bss_methods_(nullptr), @@ -1542,22 +1589,6 @@ const uint8_t* OatFile::End() const { return end_; } -const uint8_t* OatFile::BssBegin() const { - return bss_begin_; -} - -const uint8_t* OatFile::BssEnd() const { - return bss_end_; -} - -const uint8_t* OatFile::VdexBegin() const { - return vdex_begin_; -} - -const uint8_t* OatFile::VdexEnd() const { - return vdex_end_; -} - const uint8_t* OatFile::DexBegin() const { return vdex_->Begin(); } @@ -1566,6 +1597,16 @@ const uint8_t* OatFile::DexEnd() const { return vdex_->End(); } +ArrayRef OatFile::GetBootImageRelocations() const { + if (data_bimg_rel_ro_begin_ != nullptr) { + const uint32_t* relocations = reinterpret_cast(data_bimg_rel_ro_begin_); + const uint32_t* relocations_end = reinterpret_cast(data_bimg_rel_ro_end_); + return ArrayRef(relocations, relocations_end - relocations); + } else { + return ArrayRef(); + } +} + ArrayRef OatFile::GetBssMethods() const { if (bss_methods_ != nullptr) { ArtMethod** methods = reinterpret_cast(bss_methods_); diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 255a31bba9..f32874adc6 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -275,6 +275,10 @@ class OatFile { return p >= Begin() && p < End(); } + size_t DataBimgRelRoSize() const { + return DataBimgRelRoEnd() - DataBimgRelRoBegin(); + } + size_t BssSize() const { return BssEnd() - BssBegin(); } @@ -300,15 +304,19 @@ class OatFile { const uint8_t* Begin() const; const uint8_t* End() const; - const uint8_t* BssBegin() const; - const uint8_t* BssEnd() const; + const uint8_t* DataBimgRelRoBegin() const { return data_bimg_rel_ro_begin_; } + const uint8_t* DataBimgRelRoEnd() const { return data_bimg_rel_ro_end_; } + + const uint8_t* BssBegin() const { return bss_begin_; } + const uint8_t* BssEnd() const { return bss_end_; } - const uint8_t* VdexBegin() const; - const uint8_t* VdexEnd() const; + const uint8_t* VdexBegin() const { return vdex_begin_; } + const uint8_t* VdexEnd() const { return vdex_end_; } const uint8_t* DexBegin() const; const uint8_t* DexEnd() const; + ArrayRef GetBootImageRelocations() const; ArrayRef GetBssMethods() const; ArrayRef> GetBssGcRoots() const; @@ -355,6 +363,12 @@ class OatFile { // Pointer to end of oat region for bounds checking. const uint8_t* end_; + // Pointer to the .data.bimg.rel.ro section, if present, otherwise null. + const uint8_t* data_bimg_rel_ro_begin_; + + // Pointer to the end of the .data.bimg.rel.ro section, if present, otherwise null. + const uint8_t* data_bimg_rel_ro_end_; + // Pointer to the .bss section, if present, otherwise null. uint8_t* bss_begin_; diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java index 3173afdfcd..121e8f2014 100644 --- a/test/552-checker-sharpening/src/Main.java +++ b/test/552-checker-sharpening/src/Main.java @@ -195,6 +195,32 @@ public class Main { return Other.class; } + /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.String Main.$noinline$toHexString(int) builder (after) + /// CHECK: InvokeStaticOrDirect method_load_kind:RuntimeCall + + /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.String Main.$noinline$toHexString(int) sharpening (after) + // Note: load kind depends on PIC/non-PIC + /// CHECK: InvokeStaticOrDirect method_load_kind:{{BootImageRelRo|DirectAddress}} + public static String $noinline$toHexString(int value) { + return Integer.toString(value, 16); + } + + /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.String Main.$noinline$toHexStringIndirect(int) builder (after) + /// CHECK: InvokeStaticOrDirect method_load_kind:RuntimeCall + + /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.String Main.$noinline$toHexStringIndirect(int) sharpening (after) + /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry + + /// CHECK-START-X86: java.lang.String Main.$noinline$toHexStringIndirect(int) pc_relative_fixups_x86 (before) + /// CHECK-NOT: X86ComputeBaseMethodAddress + + /// CHECK-START-X86: java.lang.String Main.$noinline$toHexStringIndirect(int) pc_relative_fixups_x86 (after) + /// CHECK-DAG: X86ComputeBaseMethodAddress + /// CHECK-DAG: InvokeStaticOrDirect method_load_kind:BssEntry + public static String $noinline$toHexStringIndirect(int value) { + return $noinline$toHexString(value); + } + public static void main(String[] args) { assertIntEquals(1, testSimple(1)); assertIntEquals(1, testDiamond(false, 1)); @@ -208,6 +234,8 @@ public class Main { assertStringEquals("non-boot-image-string", $noinline$getNonBootImageString()); assertClassEquals(String.class, $noinline$getStringClass()); assertClassEquals(Other.class, $noinline$getOtherClass()); + assertStringEquals("12345678", $noinline$toHexString(0x12345678)); + assertStringEquals("76543210", $noinline$toHexStringIndirect(0x76543210)); } } -- GitLab From e47f60c482648172334aaca59e6c1ab7a3d42610 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 21 Feb 2018 13:43:28 +0000 Subject: [PATCH 038/749] Retrieve String/Class references from .data.bimg.rel.ro. For PIC AOT-compiled app, use the .data.bimg.rel.ro to load the boot image String/Class references instead of using the mmapped boot image ClassTable and InternTable. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing --pictest --npictest Test: Pixel 2 XL boots. Test: testrunner.py --target --optimizing --pictest --npictest Bug: 71526895 Change-Id: Id5703229777aecb589a933a41f92e44d3ec02a3d --- .../linker/arm64/relative_patcher_arm64.cc | 4 - compiler/linker/linker_patch.h | 30 ----- compiler/optimizing/code_generator.cc | 43 ++++++++ compiler/optimizing/code_generator.h | 4 + compiler/optimizing/code_generator_arm64.cc | 48 ++++---- compiler/optimizing/code_generator_arm64.h | 3 +- .../optimizing/code_generator_arm_vixl.cc | 26 ++--- compiler/optimizing/code_generator_arm_vixl.h | 3 +- compiler/optimizing/code_generator_mips.cc | 40 +++---- compiler/optimizing/code_generator_mips.h | 3 +- compiler/optimizing/code_generator_mips64.cc | 32 +++--- compiler/optimizing/code_generator_mips64.h | 3 +- compiler/optimizing/code_generator_x86.cc | 32 +++--- compiler/optimizing/code_generator_x86.h | 7 +- compiler/optimizing/code_generator_x86_64.cc | 26 ++--- compiler/optimizing/code_generator_x86_64.h | 7 +- compiler/optimizing/nodes.cc | 12 +- compiler/optimizing/nodes.h | 35 ++++-- .../optimizing/pc_relative_fixups_mips.cc | 4 +- compiler/optimizing/pc_relative_fixups_x86.cc | 10 +- compiler/optimizing/sharpening.cc | 6 +- dex2oat/linker/image_writer.cc | 18 +-- dex2oat/linker/image_writer.h | 2 +- dex2oat/linker/oat_writer.cc | 103 +----------------- dex2oat/linker/oat_writer.h | 10 -- oatdump/oatdump.cc | 2 +- runtime/class_linker.cc | 15 --- runtime/class_linker.h | 1 - runtime/class_table.h | 1 - runtime/image.cc | 2 +- runtime/image.h | 17 --- runtime/intern_table.h | 2 - runtime/oat.h | 4 +- runtime/oat_file.cc | 78 ++----------- test/552-checker-sharpening/src/Main.java | 4 +- 35 files changed, 201 insertions(+), 436 deletions(-) diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc index 7230f11f73..b268204b4a 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.cc +++ b/compiler/linker/arm64/relative_patcher_arm64.cc @@ -64,10 +64,8 @@ inline bool IsAdrpPatch(const LinkerPatch& patch) { case LinkerPatch::Type::kMethodRelative: case LinkerPatch::Type::kMethodBssEntry: case LinkerPatch::Type::kTypeRelative: - case LinkerPatch::Type::kTypeClassTable: case LinkerPatch::Type::kTypeBssEntry: case LinkerPatch::Type::kStringRelative: - case LinkerPatch::Type::kStringInternTable: case LinkerPatch::Type::kStringBssEntry: return patch.LiteralOffset() == patch.PcInsnOffset(); } @@ -274,9 +272,7 @@ void Arm64RelativePatcher::PatchPcRelativeReference(std::vector* code, // LDR/STR 32-bit or 64-bit with imm12 == 0 (unset). DCHECK(patch.GetType() == LinkerPatch::Type::kDataBimgRelRo || patch.GetType() == LinkerPatch::Type::kMethodBssEntry || - patch.GetType() == LinkerPatch::Type::kTypeClassTable || patch.GetType() == LinkerPatch::Type::kTypeBssEntry || - patch.GetType() == LinkerPatch::Type::kStringInternTable || patch.GetType() == LinkerPatch::Type::kStringBssEntry) << patch.GetType(); DCHECK_EQ(insn & 0xbfbffc00, 0xb9000000) << std::hex << insn; } diff --git a/compiler/linker/linker_patch.h b/compiler/linker/linker_patch.h index a3c737c4f9..36051d2726 100644 --- a/compiler/linker/linker_patch.h +++ b/compiler/linker/linker_patch.h @@ -47,10 +47,8 @@ class LinkerPatch { kCall, kCallRelative, // NOTE: Actual patching is instruction_set-dependent. kTypeRelative, // NOTE: Actual patching is instruction_set-dependent. - kTypeClassTable, // NOTE: Actual patching is instruction_set-dependent. kTypeBssEntry, // NOTE: Actual patching is instruction_set-dependent. kStringRelative, // NOTE: Actual patching is instruction_set-dependent. - kStringInternTable, // NOTE: Actual patching is instruction_set-dependent. kStringBssEntry, // NOTE: Actual patching is instruction_set-dependent. kBakerReadBarrierBranch, // NOTE: Actual patching is instruction_set-dependent. }; @@ -110,16 +108,6 @@ class LinkerPatch { return patch; } - static LinkerPatch TypeClassTablePatch(size_t literal_offset, - const DexFile* target_dex_file, - uint32_t pc_insn_offset, - uint32_t target_type_idx) { - LinkerPatch patch(literal_offset, Type::kTypeClassTable, target_dex_file); - patch.type_idx_ = target_type_idx; - patch.pc_insn_offset_ = pc_insn_offset; - return patch; - } - static LinkerPatch TypeBssEntryPatch(size_t literal_offset, const DexFile* target_dex_file, uint32_t pc_insn_offset, @@ -140,16 +128,6 @@ class LinkerPatch { return patch; } - static LinkerPatch StringInternTablePatch(size_t literal_offset, - const DexFile* target_dex_file, - uint32_t pc_insn_offset, - uint32_t target_string_idx) { - LinkerPatch patch(literal_offset, Type::kStringInternTable, target_dex_file); - patch.string_idx_ = target_string_idx; - patch.pc_insn_offset_ = pc_insn_offset; - return patch; - } - static LinkerPatch StringBssEntryPatch(size_t literal_offset, const DexFile* target_dex_file, uint32_t pc_insn_offset, @@ -187,10 +165,8 @@ class LinkerPatch { case Type::kMethodBssEntry: case Type::kCallRelative: case Type::kTypeRelative: - case Type::kTypeClassTable: case Type::kTypeBssEntry: case Type::kStringRelative: - case Type::kStringInternTable: case Type::kStringBssEntry: case Type::kBakerReadBarrierBranch: return true; @@ -214,28 +190,24 @@ class LinkerPatch { const DexFile* TargetTypeDexFile() const { DCHECK(patch_type_ == Type::kTypeRelative || - patch_type_ == Type::kTypeClassTable || patch_type_ == Type::kTypeBssEntry); return target_dex_file_; } dex::TypeIndex TargetTypeIndex() const { DCHECK(patch_type_ == Type::kTypeRelative || - patch_type_ == Type::kTypeClassTable || patch_type_ == Type::kTypeBssEntry); return dex::TypeIndex(type_idx_); } const DexFile* TargetStringDexFile() const { DCHECK(patch_type_ == Type::kStringRelative || - patch_type_ == Type::kStringInternTable || patch_type_ == Type::kStringBssEntry); return target_dex_file_; } dex::StringIndex TargetStringIndex() const { DCHECK(patch_type_ == Type::kStringRelative || - patch_type_ == Type::kStringInternTable || patch_type_ == Type::kStringBssEntry); return dex::StringIndex(string_idx_); } @@ -245,10 +217,8 @@ class LinkerPatch { patch_type_ == Type::kMethodRelative || patch_type_ == Type::kMethodBssEntry || patch_type_ == Type::kTypeRelative || - patch_type_ == Type::kTypeClassTable || patch_type_ == Type::kTypeBssEntry || patch_type_ == Type::kStringRelative || - patch_type_ == Type::kStringInternTable || patch_type_ == Type::kStringBssEntry); return pc_insn_offset_; } diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index ff59173c8b..0fcc9c6d05 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -51,6 +51,8 @@ #include "dex/verified_method.h" #include "driver/compiler_driver.h" #include "graph_visualizer.h" +#include "image.h" +#include "gc/space/image_space.h" #include "intern_table.h" #include "intrinsics.h" #include "mirror/array-inl.h" @@ -722,6 +724,47 @@ void CodeGenerator::GenerateLoadClassRuntimeCall(HLoadClass* cls) { } } +static uint32_t GetBootImageOffsetImpl(const void* object, ImageHeader::ImageSections section) { + Runtime* runtime = Runtime::Current(); + DCHECK(runtime->IsAotCompiler()); + const std::vector& boot_image_spaces = + runtime->GetHeap()->GetBootImageSpaces(); + // Check that the `object` is in the expected section of one of the boot image files. + DCHECK(std::any_of(boot_image_spaces.begin(), + boot_image_spaces.end(), + [object, section](gc::space::ImageSpace* space) { + uintptr_t begin = reinterpret_cast(space->Begin()); + uintptr_t offset = reinterpret_cast(object) - begin; + return space->GetImageHeader().GetImageSection(section).Contains(offset); + })); + uintptr_t begin = reinterpret_cast(boot_image_spaces.front()->Begin()); + uintptr_t offset = reinterpret_cast(object) - begin; + return dchecked_integral_cast(offset); +} + +// NO_THREAD_SAFETY_ANALYSIS: Avoid taking the mutator lock, boot image classes are non-moveable. +uint32_t CodeGenerator::GetBootImageOffset(HLoadClass* load_class) NO_THREAD_SAFETY_ANALYSIS { + DCHECK_EQ(load_class->GetLoadKind(), HLoadClass::LoadKind::kBootImageRelRo); + ObjPtr klass = load_class->GetClass().Get(); + DCHECK(klass != nullptr); + return GetBootImageOffsetImpl(klass.Ptr(), ImageHeader::kSectionObjects); +} + +// NO_THREAD_SAFETY_ANALYSIS: Avoid taking the mutator lock, boot image strings are non-moveable. +uint32_t CodeGenerator::GetBootImageOffset(HLoadString* load_string) NO_THREAD_SAFETY_ANALYSIS { + DCHECK_EQ(load_string->GetLoadKind(), HLoadString::LoadKind::kBootImageRelRo); + ObjPtr string = load_string->GetString().Get(); + DCHECK(string != nullptr); + return GetBootImageOffsetImpl(string.Ptr(), ImageHeader::kSectionObjects); +} + +uint32_t CodeGenerator::GetBootImageOffset(HInvokeStaticOrDirect* invoke) { + DCHECK_EQ(invoke->GetMethodLoadKind(), HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo); + ArtMethod* method = invoke->GetResolvedMethod(); + DCHECK(method != nullptr); + return GetBootImageOffsetImpl(method, ImageHeader::kSectionArtMethods); +} + void CodeGenerator::BlockIfInRegister(Location location, bool is_out) const { // The DCHECKS below check that a register is not specified twice in // the summary. The out location can overlap with an input, so we need diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 60de722285..7031483a77 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -556,6 +556,10 @@ class CodeGenerator : public DeletableArenaObject { Location runtime_return_location); void GenerateLoadClassRuntimeCall(HLoadClass* cls); + uint32_t GetBootImageOffset(HLoadClass* load_class); + uint32_t GetBootImageOffset(HLoadString* load_string); + uint32_t GetBootImageOffset(HInvokeStaticOrDirect* invoke); + static void CreateSystemArrayCopyLocationSummary(HInvoke* invoke); void SetDisassemblyInformation(DisassemblyInformation* info) { disasm_info_ = info; } diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 6bf3d86808..47ec643b14 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -4461,7 +4461,7 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall( break; case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: { // Add ADRP with its PC-relative .data.bimg.rel.ro patch. - uint32_t boot_image_offset = invoke->GetDispatchInfo().method_load_data; + uint32_t boot_image_offset = GetBootImageOffset(invoke); vixl::aarch64::Label* adrp_label = NewBootImageRelRoPatch(boot_image_offset); EmitAdrpPlaceholder(adrp_label, XRegisterFrom(temp)); // Add LDR with its PC-relative .data.bimg.rel.ro patch. @@ -4728,10 +4728,8 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector* lin } else { EmitPcRelativeLinkerPatches( boot_image_method_patches_, linker_patches); - EmitPcRelativeLinkerPatches( - boot_image_type_patches_, linker_patches); - EmitPcRelativeLinkerPatches( - boot_image_string_patches_, linker_patches); + DCHECK(boot_image_type_patches_.empty()); + DCHECK(boot_image_string_patches_.empty()); } EmitPcRelativeLinkerPatches( method_bss_entry_patches_, linker_patches); @@ -4806,7 +4804,7 @@ HLoadClass::LoadKind CodeGeneratorARM64::GetSupportedLoadClassKind( case HLoadClass::LoadKind::kReferrersClass: break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: - case HLoadClass::LoadKind::kBootImageClassTable: + case HLoadClass::LoadKind::kBootImageRelRo: case HLoadClass::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -4915,23 +4913,16 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(address)); break; } - case HLoadClass::LoadKind::kBootImageClassTable: { + case HLoadClass::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); - // Add ADRP with its PC-relative type patch. - const DexFile& dex_file = cls->GetDexFile(); - dex::TypeIndex type_index = cls->GetTypeIndex(); - vixl::aarch64::Label* adrp_label = codegen_->NewBootImageTypePatch(dex_file, type_index); + uint32_t boot_image_offset = codegen_->GetBootImageOffset(cls); + // Add ADRP with its PC-relative .data.bimg.rel.ro patch. + vixl::aarch64::Label* adrp_label = codegen_->NewBootImageRelRoPatch(boot_image_offset); codegen_->EmitAdrpPlaceholder(adrp_label, out.X()); - // Add LDR with its PC-relative type patch. + // Add LDR with its PC-relative .data.bimg.rel.ro patch. vixl::aarch64::Label* ldr_label = - codegen_->NewBootImageTypePatch(dex_file, type_index, adrp_label); + codegen_->NewBootImageRelRoPatch(boot_image_offset, adrp_label); codegen_->EmitLdrOffsetPlaceholder(ldr_label, out.W(), out.X()); - // Extract the reference from the slot data, i.e. clear the hash bits. - int32_t masked_hash = ClassTable::TableSlot::MaskHash( - ComputeModifiedUtf8Hash(dex_file.StringByTypeIdx(type_index))); - if (masked_hash != 0) { - __ Sub(out.W(), out.W(), Operand(masked_hash)); - } break; } case HLoadClass::LoadKind::kBssEntry: { @@ -4941,7 +4932,7 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA vixl::aarch64::Register temp = XRegisterFrom(out_loc); vixl::aarch64::Label* adrp_label = codegen_->NewBssEntryTypePatch(dex_file, type_index); codegen_->EmitAdrpPlaceholder(adrp_label, temp); - // Add LDR with its PC-relative Class patch. + // Add LDR with its PC-relative Class .bss entry patch. vixl::aarch64::Label* ldr_label = codegen_->NewBssEntryTypePatch(dex_file, type_index, adrp_label); // /* GcRoot */ out = *(base_address + offset) /* PC-relative */ @@ -5016,7 +5007,7 @@ HLoadString::LoadKind CodeGeneratorARM64::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { switch (desired_string_load_kind) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: - case HLoadString::LoadKind::kBootImageInternTable: + case HLoadString::LoadKind::kBootImageRelRo: case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -5082,16 +5073,15 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(address)); return; } - case HLoadString::LoadKind::kBootImageInternTable: { + case HLoadString::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); - // Add ADRP with its PC-relative String patch. - const DexFile& dex_file = load->GetDexFile(); - const dex::StringIndex string_index = load->GetStringIndex(); - vixl::aarch64::Label* adrp_label = codegen_->NewBootImageStringPatch(dex_file, string_index); + // Add ADRP with its PC-relative .data.bimg.rel.ro patch. + uint32_t boot_image_offset = codegen_->GetBootImageOffset(load); + vixl::aarch64::Label* adrp_label = codegen_->NewBootImageRelRoPatch(boot_image_offset); codegen_->EmitAdrpPlaceholder(adrp_label, out.X()); - // Add LDR with its PC-relative String patch. + // Add LDR with its PC-relative .data.bimg.rel.ro patch. vixl::aarch64::Label* ldr_label = - codegen_->NewBootImageStringPatch(dex_file, string_index, adrp_label); + codegen_->NewBootImageRelRoPatch(boot_image_offset, adrp_label); codegen_->EmitLdrOffsetPlaceholder(ldr_label, out.W(), out.X()); return; } @@ -5103,7 +5093,7 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD Register temp = XRegisterFrom(out_loc); vixl::aarch64::Label* adrp_label = codegen_->NewStringBssEntryPatch(dex_file, string_index); codegen_->EmitAdrpPlaceholder(adrp_label, temp); - // Add LDR with its .bss entry String patch. + // Add LDR with its PC-relative String .bss entry patch. vixl::aarch64::Label* ldr_label = codegen_->NewStringBssEntryPatch(dex_file, string_index, adrp_label); // /* GcRoot */ out = *(base_address + offset) /* PC-relative */ diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index d78ad87c51..584a642cca 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -828,6 +828,7 @@ class CodeGeneratorARM64 : public CodeGenerator { // Deduplication map for 64-bit literals, used for non-patchable method address or method code. Uint64ToLiteralMap uint64_literals_; // PC-relative method patch info for kBootImageLinkTimePcRelative/BootImageRelRo. + // Also used for type/string patches for kBootImageRelRo (same linker patch as for methods). ArenaDeque boot_image_method_patches_; // PC-relative method patch info for kBssEntry. ArenaDeque method_bss_entry_patches_; @@ -835,7 +836,7 @@ class CodeGeneratorARM64 : public CodeGenerator { ArenaDeque boot_image_type_patches_; // PC-relative type patch info for kBssEntry. ArenaDeque type_bss_entry_patches_; - // PC-relative String patch info; type depends on configuration (intern table or boot image PIC). + // PC-relative String patch info for kBootImageLinkTimePcRelative. ArenaDeque boot_image_string_patches_; // PC-relative String patch info for kBssEntry. ArenaDeque string_bss_entry_patches_; diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 13d5764280..353c8e06d0 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -7088,7 +7088,7 @@ HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind( case HLoadClass::LoadKind::kReferrersClass: break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: - case HLoadClass::LoadKind::kBootImageClassTable: + case HLoadClass::LoadKind::kBootImageRelRo: case HLoadClass::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -7198,18 +7198,12 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address)); break; } - case HLoadClass::LoadKind::kBootImageClassTable: { + case HLoadClass::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = - codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); + codegen_->NewBootImageRelRoPatch(codegen_->GetBootImageOffset(cls)); codegen_->EmitMovwMovtPlaceholder(labels, out); __ Ldr(out, MemOperand(out, /* offset */ 0)); - // Extract the reference from the slot data, i.e. clear the hash bits. - int32_t masked_hash = ClassTable::TableSlot::MaskHash( - ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex()))); - if (masked_hash != 0) { - __ Sub(out, out, Operand(masked_hash)); - } break; } case HLoadClass::LoadKind::kBssEntry: { @@ -7295,7 +7289,7 @@ HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { switch (desired_string_load_kind) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: - case HLoadString::LoadKind::kBootImageInternTable: + case HLoadString::LoadKind::kBootImageRelRo: case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -7359,10 +7353,10 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address)); return; } - case HLoadString::LoadKind::kBootImageInternTable: { + case HLoadString::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = - codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex()); + codegen_->NewBootImageRelRoPatch(codegen_->GetBootImageOffset(load)); codegen_->EmitMovwMovtPlaceholder(labels, out); __ Ldr(out, MemOperand(out, /* offset */ 0)); return; @@ -8957,7 +8951,7 @@ void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall( __ Mov(RegisterFrom(temp), Operand::From(invoke->GetMethodAddress())); break; case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: { - uint32_t boot_image_offset = invoke->GetDispatchInfo().method_load_data; + uint32_t boot_image_offset = GetBootImageOffset(invoke); PcRelativePatchInfo* labels = NewBootImageRelRoPatch(boot_image_offset); vixl32::Register temp_reg = RegisterFrom(temp); EmitMovwMovtPlaceholder(labels, temp_reg); @@ -9187,10 +9181,8 @@ void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector* l } else { EmitPcRelativeLinkerPatches( boot_image_method_patches_, linker_patches); - EmitPcRelativeLinkerPatches( - boot_image_type_patches_, linker_patches); - EmitPcRelativeLinkerPatches( - boot_image_string_patches_, linker_patches); + DCHECK(boot_image_type_patches_.empty()); + DCHECK(boot_image_string_patches_.empty()); } EmitPcRelativeLinkerPatches( method_bss_entry_patches_, linker_patches); diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 8a2ab711f2..3b22d4eaac 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -800,6 +800,7 @@ class CodeGeneratorARMVIXL : public CodeGenerator { // Deduplication map for 32-bit literals, used for non-patchable boot image addresses. Uint32ToLiteralMap uint32_literals_; // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo. + // Also used for type/string patches for kBootImageRelRo (same linker patch as for methods). ArenaDeque boot_image_method_patches_; // PC-relative method patch info for kBssEntry. ArenaDeque method_bss_entry_patches_; @@ -807,7 +808,7 @@ class CodeGeneratorARMVIXL : public CodeGenerator { ArenaDeque boot_image_type_patches_; // PC-relative type patch info for kBssEntry. ArenaDeque type_bss_entry_patches_; - // PC-relative String patch info; type depends on configuration (intern table or boot image PIC). + // PC-relative String patch info for kBootImageLinkTimePcRelative. ArenaDeque boot_image_string_patches_; // PC-relative String patch info for kBssEntry. ArenaDeque string_bss_entry_patches_; diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 931c9b033e..1b217920bb 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -1625,10 +1625,8 @@ void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector* link } else { EmitPcRelativeLinkerPatches( boot_image_method_patches_, linker_patches); - EmitPcRelativeLinkerPatches( - boot_image_type_patches_, linker_patches); - EmitPcRelativeLinkerPatches( - boot_image_string_patches_, linker_patches); + DCHECK(boot_image_type_patches_.empty()); + DCHECK(boot_image_string_patches_.empty()); } EmitPcRelativeLinkerPatches( method_bss_entry_patches_, linker_patches); @@ -7741,7 +7739,7 @@ HLoadString::LoadKind CodeGeneratorMIPS::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { switch (desired_string_load_kind) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: - case HLoadString::LoadKind::kBootImageInternTable: + case HLoadString::LoadKind::kBootImageRelRo: case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -7764,7 +7762,7 @@ HLoadClass::LoadKind CodeGeneratorMIPS::GetSupportedLoadClassKind( case HLoadClass::LoadKind::kReferrersClass: break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: - case HLoadClass::LoadKind::kBootImageClassTable: + case HLoadClass::LoadKind::kBootImageRelRo: case HLoadClass::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -7852,7 +7850,7 @@ void CodeGeneratorMIPS::GenerateStaticOrDirectCall( __ LoadConst32(temp.AsRegister(), invoke->GetMethodAddress()); break; case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: { - uint32_t boot_image_offset = invoke->GetDispatchInfo().method_load_data; + uint32_t boot_image_offset = GetBootImageOffset(invoke); PcRelativePatchInfo* info_high = NewBootImageRelRoPatch(boot_image_offset); PcRelativePatchInfo* info_low = NewBootImageRelRoPatch(boot_image_offset, info_high); Register temp_reg = temp.AsRegister(); @@ -7981,7 +7979,7 @@ void LocationsBuilderMIPS::VisitLoadClass(HLoadClass* cls) { // We need an extra register for PC-relative literals on R2. case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: case HLoadClass::LoadKind::kBootImageAddress: - case HLoadClass::LoadKind::kBootImageClassTable: + case HLoadClass::LoadKind::kBootImageRelRo: case HLoadClass::LoadKind::kBssEntry: if (isR6) { break; @@ -8033,7 +8031,7 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF // We need an extra register for PC-relative literals on R2. case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: case HLoadClass::LoadKind::kBootImageAddress: - case HLoadClass::LoadKind::kBootImageClassTable: + case HLoadClass::LoadKind::kBootImageRelRo: case HLoadClass::LoadKind::kBssEntry: base_or_current_method_reg = (isR6 || has_irreducible_loops) ? ZERO : locations->InAt(0).AsRegister(); @@ -8090,22 +8088,17 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF } break; } - case HLoadClass::LoadKind::kBootImageClassTable: { + case HLoadClass::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); + uint32_t boot_image_offset = codegen_->GetBootImageOffset(cls); CodeGeneratorMIPS::PcRelativePatchInfo* info_high = - codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); + codegen_->NewBootImageRelRoPatch(boot_image_offset); CodeGeneratorMIPS::PcRelativePatchInfo* info_low = - codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex(), info_high); + codegen_->NewBootImageRelRoPatch(boot_image_offset, info_high); codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, out, base_or_current_method_reg); __ Lw(out, out, /* placeholder */ 0x5678, &info_low->label); - // Extract the reference from the slot data, i.e. clear the hash bits. - int32_t masked_hash = ClassTable::TableSlot::MaskHash( - ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex()))); - if (masked_hash != 0) { - __ Addiu(out, out, -masked_hash); - } break; } case HLoadClass::LoadKind::kBssEntry: { @@ -8196,7 +8189,7 @@ void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) { // We need an extra register for PC-relative literals on R2. case HLoadString::LoadKind::kBootImageAddress: case HLoadString::LoadKind::kBootImageLinkTimePcRelative: - case HLoadString::LoadKind::kBootImageInternTable: + case HLoadString::LoadKind::kBootImageRelRo: case HLoadString::LoadKind::kBssEntry: if (isR6) { break; @@ -8248,7 +8241,7 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_ // We need an extra register for PC-relative literals on R2. case HLoadString::LoadKind::kBootImageAddress: case HLoadString::LoadKind::kBootImageLinkTimePcRelative: - case HLoadString::LoadKind::kBootImageInternTable: + case HLoadString::LoadKind::kBootImageRelRo: case HLoadString::LoadKind::kBssEntry: base_or_current_method_reg = (isR6 || has_irreducible_loops) ? ZERO : locations->InAt(0).AsRegister(); @@ -8284,12 +8277,13 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_ } return; } - case HLoadString::LoadKind::kBootImageInternTable: { + case HLoadString::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); + uint32_t boot_image_offset = codegen_->GetBootImageOffset(load); CodeGeneratorMIPS::PcRelativePatchInfo* info_high = - codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex()); + codegen_->NewBootImageRelRoPatch(boot_image_offset); CodeGeneratorMIPS::PcRelativePatchInfo* info_low = - codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex(), info_high); + codegen_->NewBootImageRelRoPatch(boot_image_offset, info_high); codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, out, base_or_current_method_reg); diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 3cb3b52807..2d971c5147 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -694,6 +694,7 @@ class CodeGeneratorMIPS : public CodeGenerator { // Deduplication map for 32-bit literals, used for non-patchable boot image addresses. Uint32ToLiteralMap uint32_literals_; // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo. + // Also used for type/string patches for kBootImageRelRo (same linker patch as for methods). ArenaDeque boot_image_method_patches_; // PC-relative method patch info for kBssEntry. ArenaDeque method_bss_entry_patches_; @@ -701,7 +702,7 @@ class CodeGeneratorMIPS : public CodeGenerator { ArenaDeque boot_image_type_patches_; // PC-relative type patch info for kBssEntry. ArenaDeque type_bss_entry_patches_; - // PC-relative String patch info; type depends on configuration (intern table or boot image PIC). + // PC-relative String patch info for kBootImageLinkTimePcRelative. ArenaDeque boot_image_string_patches_; // PC-relative String patch info for kBssEntry. ArenaDeque string_bss_entry_patches_; diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 78db1a397c..7713faf6eb 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1537,10 +1537,8 @@ void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector* li } else { EmitPcRelativeLinkerPatches( boot_image_method_patches_, linker_patches); - EmitPcRelativeLinkerPatches( - boot_image_type_patches_, linker_patches); - EmitPcRelativeLinkerPatches( - boot_image_string_patches_, linker_patches); + DCHECK(boot_image_type_patches_.empty()); + DCHECK(boot_image_string_patches_.empty()); } EmitPcRelativeLinkerPatches( method_bss_entry_patches_, linker_patches); @@ -5855,7 +5853,7 @@ HLoadString::LoadKind CodeGeneratorMIPS64::GetSupportedLoadStringKind( bool fallback_load = false; switch (desired_string_load_kind) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: - case HLoadString::LoadKind::kBootImageInternTable: + case HLoadString::LoadKind::kBootImageRelRo: case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -5882,7 +5880,7 @@ HLoadClass::LoadKind CodeGeneratorMIPS64::GetSupportedLoadClassKind( case HLoadClass::LoadKind::kReferrersClass: break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: - case HLoadClass::LoadKind::kBootImageClassTable: + case HLoadClass::LoadKind::kBootImageRelRo: case HLoadClass::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -5943,7 +5941,7 @@ void CodeGeneratorMIPS64::GenerateStaticOrDirectCall( DeduplicateUint64Literal(invoke->GetMethodAddress())); break; case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: { - uint32_t boot_image_offset = invoke->GetDispatchInfo().method_load_data; + uint32_t boot_image_offset = GetBootImageOffset(invoke); PcRelativePatchInfo* info_high = NewBootImageRelRoPatch(boot_image_offset); PcRelativePatchInfo* info_low = NewBootImageRelRoPatch(boot_image_offset, info_high); EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low); @@ -6138,20 +6136,15 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S codegen_->DeduplicateBootImageAddressLiteral(address)); break; } - case HLoadClass::LoadKind::kBootImageClassTable: { + case HLoadClass::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); + uint32_t boot_image_offset = codegen_->GetBootImageOffset(cls); CodeGeneratorMIPS64::PcRelativePatchInfo* info_high = - codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); + codegen_->NewBootImageRelRoPatch(boot_image_offset); CodeGeneratorMIPS64::PcRelativePatchInfo* info_low = - codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex(), info_high); + codegen_->NewBootImageRelRoPatch(boot_image_offset, info_high); codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low); __ Lwu(out, AT, /* placeholder */ 0x5678); - // Extract the reference from the slot data, i.e. clear the hash bits. - int32_t masked_hash = ClassTable::TableSlot::MaskHash( - ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex()))); - if (masked_hash != 0) { - __ Daddiu(out, out, -masked_hash); - } break; } case HLoadClass::LoadKind::kBssEntry: { @@ -6273,12 +6266,13 @@ void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) NO_THREA codegen_->DeduplicateBootImageAddressLiteral(address)); return; } - case HLoadString::LoadKind::kBootImageInternTable: { + case HLoadString::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); + uint32_t boot_image_offset = codegen_->GetBootImageOffset(load); CodeGeneratorMIPS64::PcRelativePatchInfo* info_high = - codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex()); + codegen_->NewBootImageRelRoPatch(boot_image_offset); CodeGeneratorMIPS64::PcRelativePatchInfo* info_low = - codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex(), info_high); + codegen_->NewBootImageRelRoPatch(boot_image_offset, info_high); codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low); __ Lwu(out, AT, /* placeholder */ 0x5678); return; diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 9ff3f63384..e12df0bfd2 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -658,6 +658,7 @@ class CodeGeneratorMIPS64 : public CodeGenerator { // address. Uint64ToLiteralMap uint64_literals_; // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo. + // Also used for type/string patches for kBootImageRelRo (same linker patch as for methods). ArenaDeque boot_image_method_patches_; // PC-relative method patch info for kBssEntry. ArenaDeque method_bss_entry_patches_; @@ -665,7 +666,7 @@ class CodeGeneratorMIPS64 : public CodeGenerator { ArenaDeque boot_image_type_patches_; // PC-relative type patch info for kBssEntry. ArenaDeque type_bss_entry_patches_; - // PC-relative String patch info; type depends on configuration (intern table or boot image PIC). + // PC-relative String patch info for kBootImageLinkTimePcRelative. ArenaDeque boot_image_string_patches_; // PC-relative type patch info for kBssEntry. ArenaDeque string_bss_entry_patches_; diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index b9ecfeebb6..21b8395d5d 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -4630,7 +4630,7 @@ void CodeGeneratorX86::GenerateStaticOrDirectCall( __ movl(temp.AsRegister(), Address(base_reg, kDummy32BitOffset)); RecordBootImageRelRoPatch( invoke->InputAt(invoke->GetSpecialInputIndex())->AsX86ComputeBaseMethodAddress(), - invoke->GetDispatchInfo().method_load_data); + GetBootImageOffset(invoke)); break; } case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: { @@ -4798,10 +4798,8 @@ void CodeGeneratorX86::EmitLinkerPatches(ArenaVector* linke } else { EmitPcRelativeLinkerPatches( boot_image_method_patches_, linker_patches); - EmitPcRelativeLinkerPatches( - boot_image_type_patches_, linker_patches); - EmitPcRelativeLinkerPatches( - boot_image_string_patches_, linker_patches); + DCHECK(boot_image_type_patches_.empty()); + DCHECK(boot_image_string_patches_.empty()); } EmitPcRelativeLinkerPatches( method_bss_entry_patches_, linker_patches); @@ -6170,7 +6168,7 @@ HLoadClass::LoadKind CodeGeneratorX86::GetSupportedLoadClassKind( case HLoadClass::LoadKind::kReferrersClass: break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: - case HLoadClass::LoadKind::kBootImageClassTable: + case HLoadClass::LoadKind::kBootImageRelRo: case HLoadClass::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -6208,7 +6206,7 @@ void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) { if (load_kind == HLoadClass::LoadKind::kReferrersClass || load_kind == HLoadClass::LoadKind::kBootImageLinkTimePcRelative || - load_kind == HLoadClass::LoadKind::kBootImageClassTable || + load_kind == HLoadClass::LoadKind::kBootImageRelRo || load_kind == HLoadClass::LoadKind::kBssEntry) { locations->SetInAt(0, Location::RequiresRegister()); } @@ -6284,17 +6282,12 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFE __ movl(out, Immediate(address)); break; } - case HLoadClass::LoadKind::kBootImageClassTable: { + case HLoadClass::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); Register method_address = locations->InAt(0).AsRegister(); __ movl(out, Address(method_address, CodeGeneratorX86::kDummy32BitOffset)); - codegen_->RecordBootImageTypePatch(cls); - // Extract the reference from the slot data, i.e. clear the hash bits. - int32_t masked_hash = ClassTable::TableSlot::MaskHash( - ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex()))); - if (masked_hash != 0) { - __ subl(out, Immediate(masked_hash)); - } + codegen_->RecordBootImageRelRoPatch(cls->InputAt(0)->AsX86ComputeBaseMethodAddress(), + codegen_->GetBootImageOffset(cls)); break; } case HLoadClass::LoadKind::kBssEntry: { @@ -6374,7 +6367,7 @@ HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { switch (desired_string_load_kind) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: - case HLoadString::LoadKind::kBootImageInternTable: + case HLoadString::LoadKind::kBootImageRelRo: case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -6393,7 +6386,7 @@ void LocationsBuilderX86::VisitLoadString(HLoadString* load) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(load, call_kind); HLoadString::LoadKind load_kind = load->GetLoadKind(); if (load_kind == HLoadString::LoadKind::kBootImageLinkTimePcRelative || - load_kind == HLoadString::LoadKind::kBootImageInternTable || + load_kind == HLoadString::LoadKind::kBootImageRelRo || load_kind == HLoadString::LoadKind::kBssEntry) { locations->SetInAt(0, Location::RequiresRegister()); } @@ -6447,11 +6440,12 @@ void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) NO_THREAD_S __ movl(out, Immediate(address)); return; } - case HLoadString::LoadKind::kBootImageInternTable: { + case HLoadString::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); Register method_address = locations->InAt(0).AsRegister(); __ movl(out, Address(method_address, CodeGeneratorX86::kDummy32BitOffset)); - codegen_->RecordBootImageStringPatch(load); + codegen_->RecordBootImageRelRoPatch(load->InputAt(0)->AsX86ComputeBaseMethodAddress(), + codegen_->GetBootImageOffset(load)); return; } case HLoadString::LoadKind::kBssEntry: { diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index e21ccb5fe3..342b8a9457 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -634,16 +634,17 @@ class CodeGeneratorX86 : public CodeGenerator { const X86InstructionSetFeatures& isa_features_; // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo. + // Also used for type/string patches for kBootImageRelRo (same linker patch as for methods). ArenaDeque boot_image_method_patches_; // PC-relative method patch info for kBssEntry. ArenaDeque method_bss_entry_patches_; // PC-relative type patch info for kBootImageLinkTimePcRelative. ArenaDeque boot_image_type_patches_; - // Type patch locations for kBssEntry. + // PC-relative type patch info for kBssEntry. ArenaDeque type_bss_entry_patches_; - // String patch locations; type depends on configuration (intern table or boot image PIC). + // PC-relative String patch info for kBootImageLinkTimePcRelative. ArenaDeque boot_image_string_patches_; - // String patch locations for kBssEntry. + // PC-relative String patch info for kBssEntry. ArenaDeque string_bss_entry_patches_; // Patches for string root accesses in JIT compiled code. diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 728e375ad8..fec4660447 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -1002,7 +1002,7 @@ void CodeGeneratorX86_64::GenerateStaticOrDirectCall( // Note: Boot image is in the low 4GiB and the entry is 32-bit, so emit a 32-bit load. __ movl(temp.AsRegister(), Address::Absolute(kDummy32BitOffset, /* no_rip */ false)); - RecordBootImageRelRoPatch(invoke->GetDispatchInfo().method_load_data); + RecordBootImageRelRoPatch(GetBootImageOffset(invoke)); break; } case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: { @@ -1150,10 +1150,8 @@ void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector* li } else { EmitPcRelativeLinkerPatches( boot_image_method_patches_, linker_patches); - EmitPcRelativeLinkerPatches( - boot_image_type_patches_, linker_patches); - EmitPcRelativeLinkerPatches( - boot_image_string_patches_, linker_patches); + DCHECK(boot_image_type_patches_.empty()); + DCHECK(boot_image_string_patches_.empty()); } EmitPcRelativeLinkerPatches( method_bss_entry_patches_, linker_patches); @@ -5556,7 +5554,7 @@ HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind( case HLoadClass::LoadKind::kReferrersClass: break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: - case HLoadClass::LoadKind::kBootImageClassTable: + case HLoadClass::LoadKind::kBootImageRelRo: case HLoadClass::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -5664,16 +5662,10 @@ void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S __ movl(out, Immediate(static_cast(address))); // Zero-extended. break; } - case HLoadClass::LoadKind::kBootImageClassTable: { + case HLoadClass::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); __ movl(out, Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false)); - codegen_->RecordBootImageTypePatch(cls); - // Extract the reference from the slot data, i.e. clear the hash bits. - int32_t masked_hash = ClassTable::TableSlot::MaskHash( - ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex()))); - if (masked_hash != 0) { - __ subl(out, Immediate(masked_hash)); - } + codegen_->RecordBootImageRelRoPatch(codegen_->GetBootImageOffset(cls)); break; } case HLoadClass::LoadKind::kBssEntry: { @@ -5738,7 +5730,7 @@ HLoadString::LoadKind CodeGeneratorX86_64::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { switch (desired_string_load_kind) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: - case HLoadString::LoadKind::kBootImageInternTable: + case HLoadString::LoadKind::kBootImageRelRo: case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; @@ -5804,10 +5796,10 @@ void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) NO_THREA __ movl(out, Immediate(static_cast(address))); // Zero-extended. return; } - case HLoadString::LoadKind::kBootImageInternTable: { + case HLoadString::LoadKind::kBootImageRelRo: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); __ movl(out, Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false)); - codegen_->RecordBootImageStringPatch(load); + codegen_->RecordBootImageRelRoPatch(codegen_->GetBootImageOffset(load)); return; } case HLoadString::LoadKind::kBssEntry: { diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 0637b274f6..fc95373c68 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -606,16 +606,17 @@ class CodeGeneratorX86_64 : public CodeGenerator { int constant_area_start_; // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo. + // Also used for type/string patches for kBootImageRelRo (same linker patch as for methods). ArenaDeque> boot_image_method_patches_; // PC-relative method patch info for kBssEntry. ArenaDeque> method_bss_entry_patches_; // PC-relative type patch info for kBootImageLinkTimePcRelative. ArenaDeque> boot_image_type_patches_; - // Type patch locations for kBssEntry. + // PC-relative type patch info for kBssEntry. ArenaDeque> type_bss_entry_patches_; - // String patch locations; type depends on configuration (intern table or boot image PIC). + // PC-relative String patch info for kBootImageLinkTimePcRelative. ArenaDeque> boot_image_string_patches_; - // String patch locations for kBssEntry. + // PC-relative String patch info for kBssEntry. ArenaDeque> string_bss_entry_patches_; // Patches for string literals in JIT compiled code. diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index a8ddb7cfdc..d3212cbbc0 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -2927,7 +2927,7 @@ bool HLoadClass::InstructionDataEquals(const HInstruction* other) const { } switch (GetLoadKind()) { case LoadKind::kBootImageAddress: - case LoadKind::kBootImageClassTable: + case LoadKind::kBootImageRelRo: case LoadKind::kJitTableAddress: { ScopedObjectAccess soa(Thread::Current()); return GetClass().Get() == other_load_class->GetClass().Get(); @@ -2946,8 +2946,8 @@ std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs) { return os << "BootImageLinkTimePcRelative"; case HLoadClass::LoadKind::kBootImageAddress: return os << "BootImageAddress"; - case HLoadClass::LoadKind::kBootImageClassTable: - return os << "BootImageClassTable"; + case HLoadClass::LoadKind::kBootImageRelRo: + return os << "BootImageRelRo"; case HLoadClass::LoadKind::kBssEntry: return os << "BssEntry"; case HLoadClass::LoadKind::kJitTableAddress: @@ -2970,7 +2970,7 @@ bool HLoadString::InstructionDataEquals(const HInstruction* other) const { } switch (GetLoadKind()) { case LoadKind::kBootImageAddress: - case LoadKind::kBootImageInternTable: + case LoadKind::kBootImageRelRo: case LoadKind::kJitTableAddress: { ScopedObjectAccess soa(Thread::Current()); return GetString().Get() == other_load_string->GetString().Get(); @@ -2986,8 +2986,8 @@ std::ostream& operator<<(std::ostream& os, HLoadString::LoadKind rhs) { return os << "BootImageLinkTimePcRelative"; case HLoadString::LoadKind::kBootImageAddress: return os << "BootImageAddress"; - case HLoadString::LoadKind::kBootImageInternTable: - return os << "BootImageInternTable"; + case HLoadString::LoadKind::kBootImageRelRo: + return os << "BootImageRelRo"; case HLoadString::LoadKind::kBssEntry: return os << "BssEntry"; case HLoadString::LoadKind::kJitTableAddress: diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 7f2d43fbd5..846b185b59 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -6071,12 +6071,12 @@ class HLoadClass FINAL : public HInstruction { kBootImageLinkTimePcRelative, // Use a known boot image Class* address, embedded in the code by the codegen. - // Used for boot image classes referenced by apps in AOT- and JIT-compiled code. + // Used for boot image classes referenced by apps in JIT- and AOT-compiled code (non-PIC). kBootImageAddress, - // Use a PC-relative load from a boot image ClassTable mmapped into the .bss - // of the oat file. - kBootImageClassTable, + // Load from an entry in the .data.bimg.rel.ro using a PC-relative load. + // Used for boot image classes referenced by apps in AOT-compiled code (PIC). + kBootImageRelRo, // Load from an entry in the .bss section using a PC-relative load. // Used for classes outside boot image when .bss is accessible with a PC-relative load. @@ -6124,6 +6124,12 @@ class HLoadClass FINAL : public HInstruction { return GetPackedField(); } + bool HasPcRelativeLoadKind() const { + return GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative || + GetLoadKind() == LoadKind::kBootImageRelRo || + GetLoadKind() == LoadKind::kBssEntry; + } + bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other) const; @@ -6228,7 +6234,6 @@ class HLoadClass FINAL : public HInstruction { static bool HasTypeReference(LoadKind load_kind) { return load_kind == LoadKind::kReferrersClass || load_kind == LoadKind::kBootImageLinkTimePcRelative || - load_kind == LoadKind::kBootImageClassTable || load_kind == LoadKind::kBssEntry || load_kind == LoadKind::kRuntimeCall; } @@ -6274,7 +6279,7 @@ inline void HLoadClass::AddSpecialInput(HInstruction* special_input) { // including literal pool loads, which are PC-relative too. DCHECK(GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative || GetLoadKind() == LoadKind::kBootImageAddress || - GetLoadKind() == LoadKind::kBootImageClassTable || + GetLoadKind() == LoadKind::kBootImageRelRo || GetLoadKind() == LoadKind::kBssEntry) << GetLoadKind(); DCHECK(special_input_.GetInstruction() == nullptr); special_input_ = HUserRecord(special_input); @@ -6290,12 +6295,12 @@ class HLoadString FINAL : public HInstruction { kBootImageLinkTimePcRelative, // Use a known boot image String* address, embedded in the code by the codegen. - // Used for boot image strings referenced by apps in AOT- and JIT-compiled code. + // Used for boot image strings referenced by apps in JIT- and AOT-compiled code (non-PIC). kBootImageAddress, - // Use a PC-relative load from a boot image InternTable mmapped into the .bss - // of the oat file. - kBootImageInternTable, + // Load from an entry in the .data.bimg.rel.ro using a PC-relative load. + // Used for boot image strings referenced by apps in AOT-compiled code (PIC). + kBootImageRelRo, // Load from an entry in the .bss section using a PC-relative load. // Used for strings outside boot image when .bss is accessible with a PC-relative load. @@ -6330,6 +6335,12 @@ class HLoadString FINAL : public HInstruction { return GetPackedField(); } + bool HasPcRelativeLoadKind() const { + return GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative || + GetLoadKind() == LoadKind::kBootImageRelRo || + GetLoadKind() == LoadKind::kBssEntry; + } + const DexFile& GetDexFile() const { return dex_file_; } @@ -6358,7 +6369,7 @@ class HLoadString FINAL : public HInstruction { LoadKind load_kind = GetLoadKind(); if (load_kind == LoadKind::kBootImageLinkTimePcRelative || load_kind == LoadKind::kBootImageAddress || - load_kind == LoadKind::kBootImageInternTable || + load_kind == LoadKind::kBootImageRelRo || load_kind == LoadKind::kJitTableAddress) { return false; } @@ -6436,7 +6447,7 @@ inline void HLoadString::AddSpecialInput(HInstruction* special_input) { // including literal pool loads, which are PC-relative too. DCHECK(GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative || GetLoadKind() == LoadKind::kBootImageAddress || - GetLoadKind() == LoadKind::kBootImageInternTable || + GetLoadKind() == LoadKind::kBootImageRelRo || GetLoadKind() == LoadKind::kBssEntry) << GetLoadKind(); // HLoadString::GetInputRecords() returns an empty array at this point, // so use the GetInputRecords() from the base class to set the input record. diff --git a/compiler/optimizing/pc_relative_fixups_mips.cc b/compiler/optimizing/pc_relative_fixups_mips.cc index 9d5358514e..0102254206 100644 --- a/compiler/optimizing/pc_relative_fixups_mips.cc +++ b/compiler/optimizing/pc_relative_fixups_mips.cc @@ -75,7 +75,7 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { switch (load_kind) { case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: case HLoadClass::LoadKind::kBootImageAddress: - case HLoadClass::LoadKind::kBootImageClassTable: + case HLoadClass::LoadKind::kBootImageRelRo: case HLoadClass::LoadKind::kBssEntry: // Add a base register for PC-relative literals on R2. InitializePCRelativeBasePointer(); @@ -91,7 +91,7 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { switch (load_kind) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: case HLoadString::LoadKind::kBootImageAddress: - case HLoadString::LoadKind::kBootImageInternTable: + case HLoadString::LoadKind::kBootImageRelRo: case HLoadString::LoadKind::kBssEntry: // Add a base register for PC-relative literals on R2. InitializePCRelativeBasePointer(); diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc index a3ca6311c2..0114603a15 100644 --- a/compiler/optimizing/pc_relative_fixups_x86.cc +++ b/compiler/optimizing/pc_relative_fixups_x86.cc @@ -81,20 +81,14 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { } void VisitLoadClass(HLoadClass* load_class) OVERRIDE { - HLoadClass::LoadKind load_kind = load_class->GetLoadKind(); - if (load_kind == HLoadClass::LoadKind::kBootImageLinkTimePcRelative || - load_kind == HLoadClass::LoadKind::kBootImageClassTable || - load_kind == HLoadClass::LoadKind::kBssEntry) { + if (load_class->HasPcRelativeLoadKind()) { HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(load_class); load_class->AddSpecialInput(method_address); } } void VisitLoadString(HLoadString* load_string) OVERRIDE { - HLoadString::LoadKind load_kind = load_string->GetLoadKind(); - if (load_kind == HLoadString::LoadKind::kBootImageLinkTimePcRelative || - load_kind == HLoadString::LoadKind::kBootImageInternTable || - load_kind == HLoadString::LoadKind::kBssEntry) { + if (load_string->HasPcRelativeLoadKind()) { HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(load_string); load_string->AddSpecialInput(method_address); } diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index b65628e441..7dffb2a378 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -128,8 +128,6 @@ void HSharpening::SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, } else if (IsInBootImage(callee)) { // Use PC-relative access to the .data.bimg.rel.ro methods array. method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo; - uint8_t* begin = Runtime::Current()->GetHeap()->GetBootImageSpaces().front()->Begin(); - method_load_data = reinterpret_cast(callee) - reinterpret_cast(begin); code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; } else { // Use PC-relative access to the .bss methods array. @@ -213,7 +211,7 @@ HLoadClass::LoadKind HSharpening::ComputeLoadClassKind( } else if (is_in_boot_image) { // AOT app compilation, boot image class. if (codegen->GetCompilerOptions().GetCompilePic()) { - desired_load_kind = HLoadClass::LoadKind::kBootImageClassTable; + desired_load_kind = HLoadClass::LoadKind::kBootImageRelRo; } else { desired_load_kind = HLoadClass::LoadKind::kBootImageAddress; } @@ -294,7 +292,7 @@ void HSharpening::ProcessLoadString( string = class_linker->LookupString(string_index, dex_cache.Get()); if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { if (codegen->GetCompilerOptions().GetCompilePic()) { - desired_load_kind = HLoadString::LoadKind::kBootImageInternTable; + desired_load_kind = HLoadString::LoadKind::kBootImageRelRo; } else { desired_load_kind = HLoadString::LoadKind::kBootImageAddress; } diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index a2ba816f6c..6530ead2d1 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -692,7 +692,7 @@ bool ImageWriter::AllocMemory() { for (ImageInfo& image_info : image_infos_) { ImageSection unused_sections[ImageHeader::kSectionCount]; const size_t length = RoundUp( - image_info.CreateImageSections(unused_sections, compile_app_image_), kPageSize); + image_info.CreateImageSections(unused_sections), kPageSize); std::string error_msg; image_info.image_.reset(MemMap::MapAnonymous("image writer image", @@ -1842,7 +1842,7 @@ void ImageWriter::CalculateNewObjectOffsets() { image_info.image_offset_ = image_offset; ImageSection unused_sections[ImageHeader::kSectionCount]; image_info.image_size_ = - RoundUp(image_info.CreateImageSections(unused_sections, compile_app_image_), kPageSize); + RoundUp(image_info.CreateImageSections(unused_sections), kPageSize); // There should be no gaps until the next image. image_offset += image_info.image_size_; } @@ -1873,8 +1873,7 @@ void ImageWriter::CalculateNewObjectOffsets() { } } -size_t ImageWriter::ImageInfo::CreateImageSections(ImageSection* out_sections, - bool app_image) const { +size_t ImageWriter::ImageInfo::CreateImageSections(ImageSection* out_sections) const { DCHECK(out_sections != nullptr); // Do not round up any sections here that are represented by the bins since it will break @@ -1912,13 +1911,8 @@ size_t ImageWriter::ImageInfo::CreateImageSections(ImageSection* out_sections, ImageSection* dex_cache_arrays_section = &out_sections[ImageHeader::kSectionDexCacheArrays]; *dex_cache_arrays_section = ImageSection(GetBinSlotOffset(Bin::kDexCacheArray), GetBinSlotSize(Bin::kDexCacheArray)); - // For boot image, round up to the page boundary to separate the interned strings and - // class table from the modifiable data. We shall mprotect() these pages read-only when - // we load the boot image. This is more than sufficient for the string table alignment, - // namely sizeof(uint64_t). See HashSet::WriteToMemory. - static_assert(IsAligned(kPageSize), "String table alignment check."); - size_t cur_pos = - RoundUp(dex_cache_arrays_section->End(), app_image ? sizeof(uint64_t) : kPageSize); + // Round up to the alignment the string table expects. See HashSet::WriteToMemory. + size_t cur_pos = RoundUp(dex_cache_arrays_section->End(), sizeof(uint64_t)); // Calculate the size of the interned strings. ImageSection* interned_strings_section = &out_sections[ImageHeader::kSectionInternedStrings]; *interned_strings_section = ImageSection(cur_pos, intern_table_bytes_); @@ -1941,7 +1935,7 @@ void ImageWriter::CreateHeader(size_t oat_index) { // Create the image sections. ImageSection sections[ImageHeader::kSectionCount]; - const size_t image_end = image_info.CreateImageSections(sections, compile_app_image_); + const size_t image_end = image_info.CreateImageSections(sections); // Finally bitmap section. const size_t bitmap_bytes = image_info.image_bitmap_->Size(); diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h index 856edfb3bc..0441fa4892 100644 --- a/dex2oat/linker/image_writer.h +++ b/dex2oat/linker/image_writer.h @@ -267,7 +267,7 @@ class ImageWriter FINAL { // Create the image sections into the out sections variable, returns the size of the image // excluding the bitmap. - size_t CreateImageSections(ImageSection* out_sections, bool app_image) const; + size_t CreateImageSections(ImageSection* out_sections) const; size_t GetStubOffset(StubType stub_type) const { DCHECK_LT(static_cast(stub_type), kNumberOfStubTypes); diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index c9b477a9db..f9ea7e7692 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -388,7 +388,6 @@ OatWriter::OatWriter(bool compiling_boot_image, bss_method_entries_(), bss_type_entries_(), bss_string_entries_(), - map_boot_image_tables_to_bss_(false), oat_data_offset_(0u), oat_header_(nullptr), size_vdex_header_(0), @@ -877,9 +876,6 @@ class OatWriter::InitBssLayoutMethodVisitor : public DexMethodVisitor { target_string.dex_file->NumStringIds(), &writer_->bss_string_entry_references_); writer_->bss_string_entries_.Overwrite(target_string, /* placeholder */ 0u); - } else if (patch.GetType() == LinkerPatch::Type::kStringInternTable || - patch.GetType() == LinkerPatch::Type::kTypeClassTable) { - writer_->map_boot_image_tables_to_bss_ = true; } } } else { @@ -1826,14 +1822,6 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { target_offset); break; } - case LinkerPatch::Type::kStringInternTable: { - uint32_t target_offset = GetInternTableEntryOffset(patch); - writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, - patch, - offset_ + literal_offset, - target_offset); - break; - } case LinkerPatch::Type::kStringBssEntry: { StringReference ref(patch.TargetStringDexFile(), patch.TargetStringIndex()); uint32_t target_offset = @@ -1852,14 +1840,6 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { target_offset); break; } - case LinkerPatch::Type::kTypeClassTable: { - uint32_t target_offset = GetClassTableEntryOffset(patch); - writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, - patch, - offset_ + literal_offset, - target_offset); - break; - } case LinkerPatch::Type::kTypeBssEntry: { TypeReference ref(patch.TargetTypeDexFile(), patch.TargetTypeIndex()); uint32_t target_offset = writer_->bss_start_ + writer_->bss_type_entries_.Get(ref); @@ -2061,42 +2041,6 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { data[2] = (address >> 16) & 0xffu; data[3] = (address >> 24) & 0xffu; } - - // Calculate the offset of the InternTable slot (GcRoot) when mmapped to the .bss. - uint32_t GetInternTableEntryOffset(const LinkerPatch& patch) - REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(!writer_->HasBootImage()); - const uint8_t* string_root = writer_->LookupBootImageInternTableSlot( - *patch.TargetStringDexFile(), patch.TargetStringIndex()); - DCHECK(string_root != nullptr); - return GetBootImageTableEntryOffset(string_root); - } - - // Calculate the offset of the ClassTable::TableSlot when mmapped to the .bss. - uint32_t GetClassTableEntryOffset(const LinkerPatch& patch) - REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(!writer_->HasBootImage()); - const uint8_t* table_slot = - writer_->LookupBootImageClassTableSlot(*patch.TargetTypeDexFile(), patch.TargetTypeIndex()); - DCHECK(table_slot != nullptr); - return GetBootImageTableEntryOffset(table_slot); - } - - uint32_t GetBootImageTableEntryOffset(const uint8_t* raw_root) { - uint32_t base_offset = writer_->bss_start_; - for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) { - const uint8_t* const_tables_begin = - space->Begin() + space->GetImageHeader().GetBootImageConstantTablesOffset(); - size_t offset = static_cast(raw_root - const_tables_begin); - if (offset < space->GetImageHeader().GetBootImageConstantTablesSize()) { - DCHECK_LE(base_offset + offset, writer_->bss_start_ + writer_->bss_methods_offset_); - return base_offset + offset; - } - base_offset += space->GetImageHeader().GetBootImageConstantTablesSize(); - } - LOG(FATAL) << "Didn't find boot image string in boot image intern tables!"; - UNREACHABLE(); - } }; class OatWriter::WriteMapMethodVisitor : public OatDexMethodVisitor { @@ -2562,25 +2506,16 @@ void OatWriter::InitBssLayout(InstructionSet instruction_set) { DCHECK_EQ(bss_size_, 0u); if (HasBootImage()) { - DCHECK(!map_boot_image_tables_to_bss_); DCHECK(bss_string_entries_.empty()); } - if (!map_boot_image_tables_to_bss_ && - bss_method_entries_.empty() && + if (bss_method_entries_.empty() && bss_type_entries_.empty() && bss_string_entries_.empty()) { // Nothing to put to the .bss section. return; } - // Allocate space for boot image tables in the .bss section. PointerSize pointer_size = GetInstructionSetPointerSize(instruction_set); - if (map_boot_image_tables_to_bss_) { - for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) { - bss_size_ += space->GetImageHeader().GetBootImageConstantTablesSize(); - } - } - bss_methods_offset_ = bss_size_; // Prepare offsets for .bss ArtMethod entries. @@ -4470,42 +4405,6 @@ bool OatWriter::OatClass::Write(OatWriter* oat_writer, OutputStream* out) const return true; } -const uint8_t* OatWriter::LookupBootImageInternTableSlot(const DexFile& dex_file, - dex::StringIndex string_idx) - NO_THREAD_SAFETY_ANALYSIS { // Single-threaded OatWriter can avoid locking. - uint32_t utf16_length; - const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length); - DCHECK_EQ(utf16_length, CountModifiedUtf8Chars(utf8_data)); - InternTable::Utf8String string(utf16_length, - utf8_data, - ComputeUtf16HashFromModifiedUtf8(utf8_data, utf16_length)); - const InternTable* intern_table = Runtime::Current()->GetClassLinker()->intern_table_; - for (const InternTable::Table::UnorderedSet& table : intern_table->strong_interns_.tables_) { - auto it = table.Find(string); - if (it != table.end()) { - return reinterpret_cast(std::addressof(*it)); - } - } - LOG(FATAL) << "Did not find boot image string " << utf8_data; - UNREACHABLE(); -} - -const uint8_t* OatWriter::LookupBootImageClassTableSlot(const DexFile& dex_file, - dex::TypeIndex type_idx) - NO_THREAD_SAFETY_ANALYSIS { // Single-threaded OatWriter can avoid locking. - const char* descriptor = dex_file.StringByTypeIdx(type_idx); - ClassTable::DescriptorHashPair pair(descriptor, ComputeModifiedUtf8Hash(descriptor)); - ClassTable* table = Runtime::Current()->GetClassLinker()->boot_class_table_.get(); - for (const ClassTable::ClassSet& class_set : table->classes_) { - auto it = class_set.Find(pair); - if (it != class_set.end()) { - return reinterpret_cast(std::addressof(*it)); - } - } - LOG(FATAL) << "Did not find boot image class " << descriptor; - UNREACHABLE(); -} - debug::DebugInfo OatWriter::GetDebugInfo() const { debug::DebugInfo debug_info{}; debug_info.compiled_methods = ArrayRef(method_info_); diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index 8e18f9bb64..65249720d7 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -364,12 +364,6 @@ class OatWriter { return dex_files_ != nullptr && extract_dex_files_into_vdex_; } - // Find the address of the GcRoot in the InternTable for a boot image string. - const uint8_t* LookupBootImageInternTableSlot(const DexFile& dex_file, - dex::StringIndex string_idx); - // Find the address of the ClassTable::TableSlot for a boot image class. - const uint8_t* LookupBootImageClassTableSlot(const DexFile& dex_file, dex::TypeIndex type_idx); - enum class WriteState { kAddingDexFileSources, kPrepareLayout, @@ -469,10 +463,6 @@ class OatWriter { // is the target offset for patching, starting at `bss_start_ + bss_roots_offset_`. SafeMap bss_string_entries_; - // Whether boot image tables should be mapped to the .bss. This is needed for compiled - // code that reads from these tables with PC-relative instructions. - bool map_boot_image_tables_to_bss_; - // Offset of the oat data from the start of the mmapped region of the elf file. size_t oat_data_offset_; diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 80fa2156f1..ac734496d3 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -2180,7 +2180,7 @@ class ImageDumper { // Intern table is 8-byte aligned. uint32_t end_caches = dex_cache_arrays_section.Offset() + dex_cache_arrays_section.Size(); - CHECK_ALIGNED(intern_section.Offset(), sizeof(uint64_t)); + CHECK_EQ(RoundUp(end_caches, 8U), intern_section.Offset()); stats_.alignment_bytes += intern_section.Offset() - end_caches; // Add space between intern table and class table. diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 2e625a7048..7625ec07a9 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1806,21 +1806,6 @@ bool ClassLinker::AddImageSpace( header.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_); } - if (!app_image) { - // Make the string intern table and class table immutable for boot image. - // PIC app oat files may mmap a read-only copy into their own .bss section, - // so enforce that the data in the boot image tables remains unchanged. - // - // We cannot do that for app image even after the fixup as some interned - // String references may actually end up pointing to moveable Strings. - uint8_t* const_section_begin = space->Begin() + header.GetBootImageConstantTablesOffset(); - CheckedCall(mprotect, - "protect constant tables", - const_section_begin, - header.GetBootImageConstantTablesSize(), - PROT_READ); - } - ClassTable* class_table = nullptr; { WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 712e3aeffa..8d6b3d245c 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -1316,7 +1316,6 @@ class ClassLinker { friend class ImageDumper; // for DexLock friend struct linker::CompilationHelper; // For Compile in ImageTest. friend class linker::ImageWriter; // for GetClassRoots - friend class linker::OatWriter; // for boot image string/class table slot address lookup. friend class JniCompilerTest; // for GetRuntimeQuickGenericJniStub friend class JniInternalTest; // for GetRuntimeQuickGenericJniStub friend class VMClassLoader; // for LookupClass and FindClassInBaseDexClassLoader. diff --git a/runtime/class_table.h b/runtime/class_table.h index 48129b1241..52e9f82396 100644 --- a/runtime/class_table.h +++ b/runtime/class_table.h @@ -295,7 +295,6 @@ class ClassTable { std::vector oat_files_ GUARDED_BY(lock_); friend class linker::ImageWriter; // for InsertWithoutLocks. - friend class linker::OatWriter; // for boot class TableSlot address lookup. }; } // namespace art diff --git a/runtime/image.cc b/runtime/image.cc index 99406229a5..eeb1594980 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -26,7 +26,7 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '5', '\0' }; // Bitstring type check off. +const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '6', '\0' }; // No image tables in .bss. ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/image.h b/runtime/image.h index 159a308fb3..fe544cc44b 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -19,7 +19,6 @@ #include -#include "base/bit_utils.h" #include "base/enums.h" #include "globals.h" #include "mirror/object.h" @@ -327,22 +326,6 @@ class PACKED(4) ImageHeader { return boot_image_size_ != 0u; } - uint32_t GetBootImageConstantTablesOffset() const { - // Interned strings table and class table for boot image are mmapped read only. - DCHECK(!IsAppImage()); - const ImageSection& interned_strings = GetInternedStringsSection(); - DCHECK_ALIGNED(interned_strings.Offset(), kPageSize); - return interned_strings.Offset(); - } - - uint32_t GetBootImageConstantTablesSize() const { - uint32_t start_offset = GetBootImageConstantTablesOffset(); - const ImageSection& class_table = GetClassTableSection(); - DCHECK_LE(start_offset, class_table.Offset()); - size_t tables_size = class_table.Offset() + class_table.Size() - start_offset; - return RoundUp(tables_size, kPageSize); - } - // Visit mirror::Objects in the section starting at base. // TODO: Delete base parameter if it is always equal to GetImageBegin. void VisitObjects(ObjectVisitor* visitor, diff --git a/runtime/intern_table.h b/runtime/intern_table.h index 05f2794b38..b794c7a2c2 100644 --- a/runtime/intern_table.h +++ b/runtime/intern_table.h @@ -227,7 +227,6 @@ class InternTable { // modifying the zygote intern table. The back of table is modified when strings are interned. std::vector tables_; - friend class linker::OatWriter; // for boot image string table slot address lookup. ART_FRIEND_TEST(InternTableTest, CrossHash); }; @@ -287,7 +286,6 @@ class InternTable { // Weak root state, used for concurrent system weak processing and more. gc::WeakRootState weak_root_state_ GUARDED_BY(Locks::intern_table_lock_); - friend class linker::OatWriter; // for boot image string table slot address lookup. friend class Transaction; ART_FRIEND_TEST(InternTableTest, CrossHash); DISALLOW_COPY_AND_ASSIGN(InternTable); diff --git a/runtime/oat.h b/runtime/oat.h index 0fa1d4b4c2..01d391401d 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: Retrieve ArtMethod* from .data.bimg.rel.ro . - static constexpr uint8_t kOatVersion[] = { '1', '3', '9', '\0' }; + // Last oat version changed reason: Retrieve Class* and String* from .data.bimg.rel.ro . + static constexpr uint8_t kOatVersion[] = { '1', '4', '0', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 20297e0d70..348e0088a1 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -412,37 +412,6 @@ inline static bool ReadOatDexFileData(const OatFile& oat_file, return true; } -static inline bool MapConstantTables(const gc::space::ImageSpace* space, - uint8_t* address) { - // If MREMAP_DUP is ever merged to Linux kernel, use it to avoid the unnecessary open()/close(). - // Note: The current approach relies on the filename still referencing the same inode. - - File file(space->GetImageFilename(), O_RDONLY, /* checkUsage */ false); - if (!file.IsOpened()) { - LOG(ERROR) << "Failed to open boot image file " << space->GetImageFilename(); - return false; - } - - uint32_t offset = space->GetImageHeader().GetBootImageConstantTablesOffset(); - uint32_t size = space->GetImageHeader().GetBootImageConstantTablesSize(); - std::string error_msg; - std::unique_ptr mem_map(MemMap::MapFileAtAddress(address, - size, - PROT_READ, - MAP_PRIVATE, - file.Fd(), - offset, - /* low_4gb */ false, - /* reuse */ true, - file.GetPath().c_str(), - &error_msg)); - if (mem_map == nullptr) { - LOG(ERROR) << "Failed to mmap boot image tables from file " << space->GetImageFilename(); - return false; - } - return true; -} - static bool ReadIndexBssMapping(OatFile* oat_file, /*inout*/const uint8_t** oat, size_t dex_file_index, @@ -588,12 +557,15 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { return false; } - uint8_t* after_tables = - (bss_methods_ != nullptr) ? bss_methods_ : bss_roots_; // May be null. - uint8_t* boot_image_tables = (bss_begin_ == after_tables) ? nullptr : bss_begin_; - uint8_t* boot_image_tables_end = - (bss_begin_ == after_tables) ? nullptr : (after_tables != nullptr) ? after_tables : bss_end_; - DCHECK_EQ(boot_image_tables != nullptr, boot_image_tables_end != nullptr); + if (bss_methods_ != nullptr && bss_methods_ != bss_begin_) { + *error_msg = StringPrintf("In oat file '%s' found unexpected .bss gap before 'oatbssmethods': " + "begin = %p, methods = %p", + GetLocation().c_str(), + bss_begin_, + bss_methods_); + return false; + } + uint32_t dex_file_count = GetOatHeader().GetDexFileCount(); oat_dex_files_storage_.reserve(dex_file_count); for (size_t i = 0; i < dex_file_count; i++) { @@ -895,38 +867,6 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { } } - if (boot_image_tables != nullptr) { - if (UNLIKELY(runtime == nullptr)) { - // This must be oatdump without boot image. Make sure the .bss is inaccessible. - CheckedCall(mprotect, "protect bss", const_cast(BssBegin()), BssSize(), PROT_NONE); - } else if (!IsExecutable()) { - // Do not try to mmap boot image tables into .bss if the oat file is not executable. - } else { - // Map boot image tables into the .bss. The reserved size must match size of the tables. - size_t reserved_size = static_cast(boot_image_tables_end - boot_image_tables); - size_t tables_size = 0u; - for (gc::space::ImageSpace* space : runtime->GetHeap()->GetBootImageSpaces()) { - tables_size += space->GetImageHeader().GetBootImageConstantTablesSize(); - DCHECK_ALIGNED(tables_size, kPageSize); - } - if (tables_size != reserved_size) { - *error_msg = StringPrintf("In oat file '%s' found unexpected boot image table sizes, " - " %zu bytes, should be %zu.", - GetLocation().c_str(), - reserved_size, - tables_size); - return false; - } - for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) { - uint32_t current_tables_size = space->GetImageHeader().GetBootImageConstantTablesSize(); - if (current_tables_size != 0u && !MapConstantTables(space, boot_image_tables)) { - return false; - } - boot_image_tables += current_tables_size; - } - DCHECK(boot_image_tables == boot_image_tables_end); - } - } return true; } diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java index 121e8f2014..17707e1278 100644 --- a/test/552-checker-sharpening/src/Main.java +++ b/test/552-checker-sharpening/src/Main.java @@ -141,7 +141,7 @@ public class Main { /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.String Main.$noinline$getBootImageString() builder (after) // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadString load_kind:{{BootImageAddress|BootImageInternTable}} + /// CHECK: LoadString load_kind:{{BootImageAddress|BootImageRelRo}} public static String $noinline$getBootImageString() { // Prevent inlining to avoid the string comparison being optimized away. @@ -169,7 +169,7 @@ public class Main { /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.Class Main.$noinline$getStringClass() builder (after) // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadClass load_kind:{{BootImageAddress|BootImageClassTable}} class_name:java.lang.String + /// CHECK: LoadClass load_kind:{{BootImageAddress|BootImageRelRo}} class_name:java.lang.String public static Class $noinline$getStringClass() { // Prevent inlining to avoid the string comparison being optimized away. -- GitLab From 7e614110f7fe2cf5206236c3ac318941bcd0c519 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 6 Mar 2018 14:34:06 +0000 Subject: [PATCH 039/749] ARM64: Simplify save/restore regs in invoke stub. Save/restore fewer registers and use common macros to do so. Rewrite the return sequence to avoid many chained branches. And a few other minor simplifications. Test: Pixel 2 XL boots. Test: testrunner.py --target --64 --optimizing Change-Id: I32ee7bad685b8bd73d07e5a4c48a6ac0b22ff762 --- runtime/arch/arm64/quick_entrypoints_arm64.S | 120 +++++-------------- 1 file changed, 30 insertions(+), 90 deletions(-) diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 09fc2c2c8a..375b0506e2 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -613,56 +613,18 @@ INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvo .macro INVOKE_STUB_CREATE_FRAME +SAVE_SIZE=6*8 // x4, x5, x19, x20, FP, LR saved. + SAVE_TWO_REGS_INCREASE_FRAME x4, x5, SAVE_SIZE + SAVE_TWO_REGS x19, x20, 16 + SAVE_TWO_REGS xFP, xLR, 32 -SAVE_SIZE=15*8 // x4, x5, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, SP, LR, FP saved. -SAVE_SIZE_AND_METHOD=SAVE_SIZE+8 - - - mov x9, sp // Save stack pointer. - .cfi_register sp,x9 - - add x10, x2, # SAVE_SIZE_AND_METHOD // calculate size of frame. - sub x10, sp, x10 // Calculate SP position - saves + ArtMethod* + args - and x10, x10, # ~0xf // Enforce 16 byte stack alignment. - mov sp, x10 // Set new SP. - - sub x10, x9, #SAVE_SIZE // Calculate new FP (later). Done here as we must move SP - .cfi_def_cfa_register x10 // before this. - .cfi_adjust_cfa_offset SAVE_SIZE - - str x28, [x10, #112] - .cfi_rel_offset x28, 112 - - stp x26, x27, [x10, #96] - .cfi_rel_offset x26, 96 - .cfi_rel_offset x27, 104 - - stp x24, x25, [x10, #80] - .cfi_rel_offset x24, 80 - .cfi_rel_offset x25, 88 - - stp x22, x23, [x10, #64] - .cfi_rel_offset x22, 64 - .cfi_rel_offset x23, 72 - - stp x20, x21, [x10, #48] - .cfi_rel_offset x20, 48 - .cfi_rel_offset x21, 56 - - stp x9, x19, [x10, #32] // Save old stack pointer and x19. - .cfi_rel_offset sp, 32 - .cfi_rel_offset x19, 40 - - stp x4, x5, [x10, #16] // Save result and shorty addresses. - .cfi_rel_offset x4, 16 - .cfi_rel_offset x5, 24 + mov xFP, sp // Use xFP for frame pointer, as it's callee-saved. + .cfi_def_cfa_register xFP - stp xFP, xLR, [x10] // Store LR & FP. - .cfi_rel_offset x29, 0 - .cfi_rel_offset x30, 8 + add x10, x2, #(__SIZEOF_POINTER__ + 0xf) // Reserve space for ArtMethod*, arguments and + and x10, x10, # ~0xf // round up for 16-byte stack alignment. + sub sp, sp, x10 // Adjust SP for ArtMethod*, args and alignment padding. - mov xFP, x10 // Use xFP now, as it's callee-saved. - .cfi_def_cfa_register x29 mov xSELF, x3 // Move thread pointer into SELF register. // Copy arguments into stack frame. @@ -677,12 +639,10 @@ SAVE_SIZE_AND_METHOD=SAVE_SIZE+8 // Copy parameters into the stack. Use numeric label as this is a macro and Clang's assembler // does not have unique-id variables. 1: - cmp w2, #0 - beq 2f + cbz w2, 2f sub w2, w2, #4 // Need 65536 bytes of range. ldr w10, [x1, x2] str w10, [x9, x2] - b 1b 2: @@ -699,29 +659,14 @@ SAVE_SIZE_AND_METHOD=SAVE_SIZE+8 // Branch to method. blr x9 - // Restore return value address and shorty address. - ldp x4, x5, [xFP, #16] - .cfi_restore x4 - .cfi_restore x5 - - ldr x28, [xFP, #112] - .cfi_restore x28 - - ldp x26, x27, [xFP, #96] - .cfi_restore x26 - .cfi_restore x27 - - ldp x24, x25, [xFP, #80] - .cfi_restore x24 - .cfi_restore x25 - - ldp x22, x23, [xFP, #64] - .cfi_restore x22 - .cfi_restore x23 + // Pop the ArtMethod* (null), arguments and alignment padding from the stack. + mov sp, xFP + .cfi_def_cfa_register sp - ldp x20, x21, [xFP, #48] - .cfi_restore x20 - .cfi_restore x21 + // Restore saved registers including value address and shorty address. + RESTORE_TWO_REGS x19, x20, 16 + RESTORE_TWO_REGS xFP, xLR, 32 + RESTORE_TWO_REGS_DECREASE_FRAME x4, x5, SAVE_SIZE // Store result (w0/x0/s0/d0) appropriately, depending on resultType. ldrb w10, [x5] @@ -731,33 +676,28 @@ SAVE_SIZE_AND_METHOD=SAVE_SIZE+8 // Don't set anything for a void type. cmp w10, #'V' - beq 3f + beq 1f // Is it a double? cmp w10, #'D' - bne 1f - str d0, [x4] - b 3f + beq 2f -1: // Is it a float? + // Is it a float? cmp w10, #'F' - bne 2f - str s0, [x4] - b 3f + beq 3f -2: // Just store x0. Doesn't matter if it is 64 or 32 bits. + // Just store x0. Doesn't matter if it is 64 or 32 bits. str x0, [x4] -3: // Finish up. - ldp x2, x19, [xFP, #32] // Restore stack pointer and x19. - .cfi_restore x19 - mov sp, x2 - .cfi_restore sp +1: // Finish up. + ret - ldp xFP, xLR, [xFP] // Restore old frame pointer and link register. - .cfi_restore x29 - .cfi_restore x30 +2: // Store double. + str d0, [x4] + ret +3: // Store float. + str s0, [x4] ret .endm @@ -1056,7 +996,7 @@ END art_quick_invoke_static_stub /* extern"C" void art_quick_osr_stub(void** stack, x0 * size_t stack_size_in_bytes, x1 - * const uin8_t* native_pc, x2 + * const uint8_t* native_pc, x2 * JValue *result, x3 * char *shorty, x4 * Thread *self) x5 -- GitLab From 34088e16c93f482c251e55351022762760500b63 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 8 Mar 2018 10:56:09 +0000 Subject: [PATCH 040/749] Don't do a read barrier in JIT GC code. The ArtMethod the JIT GC iterates over might be in the process of being unloaded. bug: 74369794 Test: test.py --jit Test: test-art-host-run-test-debug-no-prebuild-jit-no-relocate-ntrace-cms-checkjni-picimage-npictest-ndebuggable-no-jvmti-cdex-fast-674-hiddenapi64 Change-Id: Ibf8acb0df26f90c479420ef9d0c1b1bc1fcd3b0d --- runtime/art_method.h | 13 +++++++++---- runtime/jit/jit_code_cache.cc | 6 +++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/runtime/art_method.h b/runtime/art_method.h index 5d9b729847..579e554901 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -176,8 +176,9 @@ class ArtMethod FINAL { return (GetAccessFlags() & kAccFinal) != 0; } + template bool IsIntrinsic() { - return (GetAccessFlags() & kAccIntrinsic) != 0; + return (GetAccessFlags() & kAccIntrinsic) != 0; } ALWAYS_INLINE void SetIntrinsic(uint32_t intrinsic) REQUIRES_SHARED(Locks::mutator_lock_); @@ -315,12 +316,13 @@ class ArtMethod FINAL { return (GetAccessFlags() & kAccPreviouslyWarm) != 0; } + template void SetPreviouslyWarm() { - if (IsIntrinsic()) { + if (IsIntrinsic()) { // kAccPreviouslyWarm overlaps with kAccIntrinsicBits. return; } - AddAccessFlags(kAccPreviouslyWarm); + AddAccessFlags(kAccPreviouslyWarm); } // Should this method be run in the interpreter and count locks (e.g., failed structured- @@ -839,8 +841,11 @@ class ArtMethod FINAL { } // This setter guarantees atomicity. + template void AddAccessFlags(uint32_t flag) { - DCHECK(!IsIntrinsic() || !OverlapsIntrinsicBits(flag) || IsValidIntrinsicUpdate(flag)); + DCHECK(!IsIntrinsic() || + !OverlapsIntrinsicBits(flag) || + IsValidIntrinsicUpdate(flag)); uint32_t old_access_flags; uint32_t new_access_flags; do { diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 0941b0beb3..b2d58da80e 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -671,7 +671,11 @@ void JitCodeCache::CopyInlineCacheInto(const InlineCache& ic, static void ClearMethodCounter(ArtMethod* method, bool was_warm) { if (was_warm) { - method->SetPreviouslyWarm(); + // Don't do any read barrier, as the declaring class of `method` may + // be in the process of being GC'ed (reading the declaring class is done + // when DCHECKing the declaring class is resolved, which we know it is + // at this point). + method->SetPreviouslyWarm(); } // We reset the counter to 1 so that the profile knows that the method was executed at least once. // This is required for layout purposes. -- GitLab From 79b144527bee5c384791fb826a45d5f411fd8b6d Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Wed, 28 Feb 2018 14:44:33 -0800 Subject: [PATCH 041/749] Update cfi directives for art_quick_osr_stub. Bug: 73954823 Test: testrunner.py -t 570 --jit Test: Check the backtrace works in gdb at every instruction. Change-Id: I7ad5463eca89851a0ce6fd4354e888ca5a0f9918 --- runtime/arch/arm/quick_entrypoints_arm.S | 26 ++++++++++++++++++------ runtime/arch/x86/quick_entrypoints_x86.S | 22 ++++++++++++++------ 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 737d2a86a1..aa77187175 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -601,7 +601,10 @@ END art_quick_invoke_stub_internal */ ENTRY art_quick_osr_stub SPILL_ALL_CALLEE_SAVE_GPRS @ Spill regs (9) + SAVE_SIZE=9*4 mov r11, sp @ Save the stack pointer + .cfi_def_cfa r11, SAVE_SIZE @ CFA = r11 + SAVE_SIZE + .cfi_remember_state mov r10, r1 @ Save size of stack ldr r9, [r11, #40] @ Move managed thread pointer into r9 REFRESH_MARKING_REGISTER @@ -614,14 +617,18 @@ ENTRY art_quick_osr_stub str r3, [sp, #8] @ Save JValue* result mov ip, #0 str ip, [sp] @ Store null for ArtMethod* at bottom of frame - sub sp, sp, r1 @ Reserve space for callee stack - mov r2, r1 - mov r1, r0 - mov r0, sp - bl memcpy @ memcpy (dest r0, src r1, bytes r2) + // r11 isn't properly spilled in the osr method, so we need use DWARF expression. + // NB: the CFI must be before the call since this is the address gdb will lookup. + // NB: gdb expects that cfa_expression returns the CFA value (not address to it). + .cfi_escape /* CFA = [sp + 4] + SAVE_SIZE */ \ + 0x0f, 6, /* DW_CFA_def_cfa_expression(len) */ \ + 0x92, 13, 4, /* DW_OP_bregx(reg,offset) */ \ + 0x06, /* DW_OP_deref */ \ + 0x23, SAVE_SIZE /* DW_OP_plus_uconst(val) */ bl .Losr_entry @ Call the method ldr r10, [sp, #8] @ Restore JValue* result ldr sp, [sp, #4] @ Restore saved stack pointer + .cfi_def_cfa sp, SAVE_SIZE @ CFA = sp + SAVE_SIZE ldr r4, [sp, #36] @ load shorty ldrb r4, [r4, #0] @ load return type cmp r4, #68 @ Test if result type char == 'D'. @@ -635,8 +642,15 @@ ENTRY art_quick_osr_stub .Losr_exit: pop {r4, r5, r6, r7, r8, r9, r10, r11, pc} .Losr_entry: + .cfi_restore_state + .cfi_def_cfa r11, SAVE_SIZE @ CFA = r11 + SAVE_SIZE + sub sp, sp, r10 @ Reserve space for callee stack sub r10, r10, #4 - str lr, [sp, r10] @ Store link register per the compiler ABI + str lr, [sp, r10] @ Store link register per the compiler ABI + mov r2, r10 + mov r1, r0 + mov r0, sp + bl memcpy @ memcpy (dest r0, src r1, bytes r2) bx r6 END art_quick_osr_stub diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 5a28120b30..abd784a91c 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -2369,20 +2369,28 @@ DEFINE_FUNCTION art_quick_osr_stub PUSH ebx PUSH esi PUSH edi + SAVE_SIZE=20 // 4 registers and the return address mov 4+16(%esp), %esi // ESI = argument array mov 8+16(%esp), %ecx // ECX = size of args mov 12+16(%esp), %ebx // EBX = pc to call mov %esp, %ebp // Save stack pointer + .cfi_def_cfa ebp, SAVE_SIZE // CFA = ebp + SAVE_SIZE + .cfi_remember_state andl LITERAL(0xFFFFFFF0), %esp // Align stack - PUSH ebp // Save old stack pointer + pushl %ebp // Save old stack pointer subl LITERAL(12), %esp // Align stack movl LITERAL(0), (%esp) // Store null for ArtMethod* slot + // ebp isn't properly spilled in the osr method, so we need use DWARF expression. + // NB: the CFI must be before the call since this is the address gdb will lookup. + // NB: gdb expects that cfa_expression returns the CFA value (not address to it). + .cfi_escape /* cfa = [sp + 12] + SAVE_SIZE */ \ + 0x0f, 6, /* DW_CFA_def_cfa_expression(len) */ \ + 0x92, 4, 12, /* DW_OP_bregx(reg,offset) */ \ + 0x06, /* DW_OP_deref */ \ + 0x23, SAVE_SIZE /* DW_OP_plus_uconst(val) */ call .Losr_entry - - // Restore stack pointer. - addl LITERAL(12), %esp - POP ebp - mov %ebp, %esp + mov 12(%esp), %esp // Restore stack pointer. + .cfi_def_cfa esp, SAVE_SIZE // CFA = esp + SAVE_SIZE // Restore callee saves. POP edi @@ -2405,6 +2413,8 @@ DEFINE_FUNCTION art_quick_osr_stub movss %xmm0, (%ecx) // Store the floating point result ret .Losr_entry: + .cfi_restore_state + .cfi_def_cfa ebp, SAVE_SIZE // CFA = ebp + SAVE_SIZE subl LITERAL(4), %ecx // Given stack size contains pushed frame pointer, substract it. subl %ecx, %esp mov %esp, %edi // EDI = beginning of stack -- GitLab From a5785a2fda0f01c0a0e3e8632901772c08c41488 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 8 Mar 2018 14:25:47 +0000 Subject: [PATCH 042/749] Fix OS::GetFileSizeBytes(). Also improve CHECKs in dex2oat_image_test and Revert "Disable failing test." This reverts commit f0e3d9fc0354dfebb06d6930515c315ecc76fe1d. Test: m test-art-host-gtest-dex2oat_image_test Bug: 74375666 Change-Id: Iebeceb5a1cdc9d80a76039f52e826711085a7626 --- dex2oat/dex2oat_image_test.cc | 17 ++++++++++------- libartbase/base/os_linux.cc | 6 ++++-- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc index f542c60d81..d8952826b3 100644 --- a/dex2oat/dex2oat_image_test.cc +++ b/dex2oat/dex2oat_image_test.cc @@ -129,12 +129,15 @@ class Dex2oatImageTest : public CommonRuntimeTest { std::string art_file = scratch.GetFilename() + ".art"; std::string oat_file = scratch.GetFilename() + ".oat"; std::string vdex_file = scratch.GetFilename() + ".vdex"; - ret.art_size = OS::GetFileSizeBytes(art_file.c_str()); - ret.oat_size = OS::GetFileSizeBytes(oat_file.c_str()); - ret.vdex_size = OS::GetFileSizeBytes(vdex_file.c_str()); - CHECK_GT(ret.art_size, 0u) << art_file; - CHECK_GT(ret.oat_size, 0u) << oat_file; - CHECK_GT(ret.vdex_size, 0u) << vdex_file; + int64_t art_size = OS::GetFileSizeBytes(art_file.c_str()); + int64_t oat_size = OS::GetFileSizeBytes(oat_file.c_str()); + int64_t vdex_size = OS::GetFileSizeBytes(vdex_file.c_str()); + CHECK_GT(art_size, 0u) << art_file; + CHECK_GT(oat_size, 0u) << oat_file; + CHECK_GT(vdex_size, 0u) << vdex_file; + ret.art_size = art_size; + ret.oat_size = oat_size; + ret.vdex_size = vdex_size; scratch.Close(); // Clear image files since we compile the image multiple times and don't want to leave any // artifacts behind. @@ -228,7 +231,7 @@ class Dex2oatImageTest : public CommonRuntimeTest { } }; -TEST_F(Dex2oatImageTest, DISABLED_TestModesAndFilters) { +TEST_F(Dex2oatImageTest, TestModesAndFilters) { if (kIsTargetBuild) { // This test is too slow for target builds. return; diff --git a/libartbase/base/os_linux.cc b/libartbase/base/os_linux.cc index 6b5a604423..cb228bd853 100644 --- a/libartbase/base/os_linux.cc +++ b/libartbase/base/os_linux.cc @@ -89,9 +89,11 @@ bool OS::DirectoryExists(const char* name) { int64_t OS::GetFileSizeBytes(const char* name) { struct stat st; if (stat(name, &st) == 0) { - return -1; // TODO: Deal with symlinks? + return st.st_size; // TODO: Deal with symlinks? According to the documentation, + // the st_size for a symlink is "the length of the pathname + // it contains, without a terminating null byte." } else { - return st.st_size; + return -1; } } -- GitLab From a5dca524abcbf55c3cfa778e3b72aa1efd1e9813 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Tue, 27 Feb 2018 12:42:11 +0000 Subject: [PATCH 043/749] ART: Update invoke-custom implementation Adds type checking for bootstrap arguments and support for variable arity bootstrap arguments. Adds tests for malformed bootstrap arguments, variable arity bootstrap arguments, and invocation arguments to passed to the CallSite's MethodHandle. Removes unnecessary wrapping when j.l.Error's are raised during BSM invocation. Removes BSM argument type checking from verifier. This is now performed during invocation. Bug: 73927525 Test: art/test/run-test --host 952-invoke-custom Change-Id: Id43226edad64ad9812e4ba1a069dfb70b8196dad --- runtime/common_throws.cc | 4 +- runtime/common_throws.h | 4 +- runtime/interpreter/interpreter_common.cc | 605 +++++++++++++----- runtime/method_handles.h | 7 + runtime/mirror/method_type.cc | 51 +- runtime/mirror/method_type.h | 8 + runtime/mirror/throwable.cc | 4 + runtime/mirror/throwable.h | 1 + runtime/verifier/method_verifier.cc | 96 +-- test/952-invoke-custom/expected.txt | 64 ++ test/952-invoke-custom/src/Main.java | 11 +- .../src/TestBadBootstrapArguments.java | 583 +++++++++++++++++ .../src/TestDynamicBootstrapArguments.java | 92 +++ .../src/TestInvocationKinds.java | 1 + .../src/TestVariableArityLinkerMethod.java | 569 ++++++++++++++++ test/knownfailures.json | 1 - 16 files changed, 1843 insertions(+), 258 deletions(-) create mode 100644 test/952-invoke-custom/src/TestBadBootstrapArguments.java create mode 100644 test/952-invoke-custom/src/TestDynamicBootstrapArguments.java create mode 100644 test/952-invoke-custom/src/TestVariableArityLinkerMethod.java diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index 8f65c66441..7484dd9207 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -884,8 +884,8 @@ void ThrowVerifyError(ObjPtr referrer, const char* fmt, ...) { // WrongMethodTypeException -void ThrowWrongMethodTypeException(mirror::MethodType* expected_type, - mirror::MethodType* actual_type) { +void ThrowWrongMethodTypeException(ObjPtr expected_type, + ObjPtr actual_type) { ThrowException("Ljava/lang/invoke/WrongMethodTypeException;", nullptr, StringPrintf("Expected %s but was %s", diff --git a/runtime/common_throws.h b/runtime/common_throws.h index e9baa4fef0..29a056e9ea 100644 --- a/runtime/common_throws.h +++ b/runtime/common_throws.h @@ -270,8 +270,8 @@ void ThrowVerifyError(ObjPtr referrer, const char* fmt, ...) // WrongMethodTypeException -void ThrowWrongMethodTypeException(mirror::MethodType* callee_type, - mirror::MethodType* callsite_type) +void ThrowWrongMethodTypeException(ObjPtr callee_type, + ObjPtr callsite_type) REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; } // namespace art diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 380a981d09..8a85ee41f8 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -900,171 +900,489 @@ bool DoInvokePolymorphic(Thread* self, } } +static JValue ConvertScalarBootstrapArgument(jvalue value) { + // value either contains a primitive scalar value if it corresponds + // to a primitive type, or it contains an integer value if it + // corresponds to an object instance reference id (e.g. a string id). + return JValue::FromPrimitive(value.j); +} + +static ObjPtr GetClassForBootstrapArgument(EncodedArrayValueIterator::ValueType type) + REQUIRES_SHARED(Locks::mutator_lock_) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + switch (type) { + case EncodedArrayValueIterator::ValueType::kBoolean: + case EncodedArrayValueIterator::ValueType::kByte: + case EncodedArrayValueIterator::ValueType::kChar: + case EncodedArrayValueIterator::ValueType::kShort: + // These types are disallowed by JVMS. Treat as integers. This + // will result in CCE's being raised if the BSM has one of these + // types. + case EncodedArrayValueIterator::ValueType::kInt: + return class_linker->FindPrimitiveClass('I'); + case EncodedArrayValueIterator::ValueType::kLong: + return class_linker->FindPrimitiveClass('J'); + case EncodedArrayValueIterator::ValueType::kFloat: + return class_linker->FindPrimitiveClass('F'); + case EncodedArrayValueIterator::ValueType::kDouble: + return class_linker->FindPrimitiveClass('D'); + case EncodedArrayValueIterator::ValueType::kMethodType: + return mirror::MethodType::StaticClass(); + case EncodedArrayValueIterator::ValueType::kMethodHandle: + return mirror::MethodHandle::StaticClass(); + case EncodedArrayValueIterator::ValueType::kString: + return mirror::String::GetJavaLangString(); + case EncodedArrayValueIterator::ValueType::kType: + return mirror::Class::GetJavaLangClass(); + case EncodedArrayValueIterator::ValueType::kField: + case EncodedArrayValueIterator::ValueType::kMethod: + case EncodedArrayValueIterator::ValueType::kEnum: + case EncodedArrayValueIterator::ValueType::kArray: + case EncodedArrayValueIterator::ValueType::kAnnotation: + case EncodedArrayValueIterator::ValueType::kNull: + return nullptr; + } +} + +static bool GetArgumentForBootstrapMethod(Thread* self, + ArtMethod* referrer, + EncodedArrayValueIterator::ValueType type, + const JValue* encoded_value, + JValue* decoded_value) + REQUIRES_SHARED(Locks::mutator_lock_) { + // The encoded_value contains either a scalar value (IJDF) or a + // scalar DEX file index to a reference type to be materialized. + switch (type) { + case EncodedArrayValueIterator::ValueType::kInt: + case EncodedArrayValueIterator::ValueType::kFloat: + decoded_value->SetI(encoded_value->GetI()); + return true; + case EncodedArrayValueIterator::ValueType::kLong: + case EncodedArrayValueIterator::ValueType::kDouble: + decoded_value->SetJ(encoded_value->GetJ()); + return true; + case EncodedArrayValueIterator::ValueType::kMethodType: { + StackHandleScope<2> hs(self); + Handle class_loader(hs.NewHandle(referrer->GetClassLoader())); + Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); + uint32_t index = static_cast(encoded_value->GetI()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + ObjPtr o = cl->ResolveMethodType(self, index, dex_cache, class_loader); + if (UNLIKELY(o.IsNull())) { + DCHECK(self->IsExceptionPending()); + return false; + } + decoded_value->SetL(o); + return true; + } + case EncodedArrayValueIterator::ValueType::kMethodHandle: { + uint32_t index = static_cast(encoded_value->GetI()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + ObjPtr o = cl->ResolveMethodHandle(self, index, referrer); + if (UNLIKELY(o.IsNull())) { + DCHECK(self->IsExceptionPending()); + return false; + } + decoded_value->SetL(o); + return true; + } + case EncodedArrayValueIterator::ValueType::kString: { + StackHandleScope<1> hs(self); + Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); + dex::StringIndex index(static_cast(encoded_value->GetI())); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + ObjPtr o = cl->ResolveString(index, dex_cache); + if (UNLIKELY(o.IsNull())) { + DCHECK(self->IsExceptionPending()); + return false; + } + decoded_value->SetL(o); + return true; + } + case EncodedArrayValueIterator::ValueType::kType: { + StackHandleScope<2> hs(self); + Handle class_loader(hs.NewHandle(referrer->GetClassLoader())); + Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); + dex::TypeIndex index(static_cast(encoded_value->GetI())); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + ObjPtr o = cl->ResolveType(index, dex_cache, class_loader); + if (UNLIKELY(o.IsNull())) { + DCHECK(self->IsExceptionPending()); + return false; + } + decoded_value->SetL(o); + return true; + } + case EncodedArrayValueIterator::ValueType::kBoolean: + case EncodedArrayValueIterator::ValueType::kByte: + case EncodedArrayValueIterator::ValueType::kChar: + case EncodedArrayValueIterator::ValueType::kShort: + case EncodedArrayValueIterator::ValueType::kField: + case EncodedArrayValueIterator::ValueType::kMethod: + case EncodedArrayValueIterator::ValueType::kEnum: + case EncodedArrayValueIterator::ValueType::kArray: + case EncodedArrayValueIterator::ValueType::kAnnotation: + case EncodedArrayValueIterator::ValueType::kNull: + // Unreachable - unsupported types that have been checked when + // determining the effect call site type based on the bootstrap + // argument types. + UNREACHABLE(); + } +} + +static bool PackArgumentForBootstrapMethod(Thread* self, + ArtMethod* referrer, + CallSiteArrayValueIterator* it, + ShadowFrameSetter* setter) + REQUIRES_SHARED(Locks::mutator_lock_) { + auto type = it->GetValueType(); + const JValue encoded_value = ConvertScalarBootstrapArgument(it->GetJavaValue()); + JValue decoded_value; + if (!GetArgumentForBootstrapMethod(self, referrer, type, &encoded_value, &decoded_value)) { + return false; + } + switch (it->GetValueType()) { + case EncodedArrayValueIterator::ValueType::kInt: + case EncodedArrayValueIterator::ValueType::kFloat: + setter->Set(static_cast(decoded_value.GetI())); + return true; + case EncodedArrayValueIterator::ValueType::kLong: + case EncodedArrayValueIterator::ValueType::kDouble: + setter->SetLong(decoded_value.GetJ()); + return true; + case EncodedArrayValueIterator::ValueType::kMethodType: + case EncodedArrayValueIterator::ValueType::kMethodHandle: + case EncodedArrayValueIterator::ValueType::kString: + case EncodedArrayValueIterator::ValueType::kType: + setter->SetReference(decoded_value.GetL()); + return true; + case EncodedArrayValueIterator::ValueType::kBoolean: + case EncodedArrayValueIterator::ValueType::kByte: + case EncodedArrayValueIterator::ValueType::kChar: + case EncodedArrayValueIterator::ValueType::kShort: + case EncodedArrayValueIterator::ValueType::kField: + case EncodedArrayValueIterator::ValueType::kMethod: + case EncodedArrayValueIterator::ValueType::kEnum: + case EncodedArrayValueIterator::ValueType::kArray: + case EncodedArrayValueIterator::ValueType::kAnnotation: + case EncodedArrayValueIterator::ValueType::kNull: + // Unreachable - unsupported types that have been checked when + // determining the effect call site type based on the bootstrap + // argument types. + UNREACHABLE(); + } +} + +static bool PackCollectorArrayForBootstrapMethod(Thread* self, + ArtMethod* referrer, + ObjPtr array_type, + int32_t array_length, + CallSiteArrayValueIterator* it, + ShadowFrameSetter* setter) + REQUIRES_SHARED(Locks::mutator_lock_) { + StackHandleScope<1> hs(self); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + JValue decoded_value; + +#define COLLECT_PRIMITIVE_ARRAY(Descriptor, Type) \ + Handle array = \ + hs.NewHandle(mirror::Type ## Array::Alloc(self, array_length)); \ + if (array.IsNull()) { \ + return false; \ + } \ + for (int32_t i = 0; it->HasNext(); it->Next(), ++i) { \ + auto type = it->GetValueType(); \ + DCHECK_EQ(type, EncodedArrayValueIterator::ValueType::k ## Type); \ + const JValue encoded_value = \ + ConvertScalarBootstrapArgument(it->GetJavaValue()); \ + GetArgumentForBootstrapMethod(self, \ + referrer, \ + type, \ + &encoded_value, \ + &decoded_value); \ + array->Set(i, decoded_value.Get ## Descriptor()); \ + } \ + setter->SetReference(array.Get()); \ + return true; + +#define COLLECT_REFERENCE_ARRAY(T, Type) \ + Handle> array = \ + hs.NewHandle(mirror::ObjectArray::Alloc(self, \ + array_type, \ + array_length)); \ + if (array.IsNull()) { \ + return false; \ + } \ + for (int32_t i = 0; it->HasNext(); it->Next(), ++i) { \ + auto type = it->GetValueType(); \ + DCHECK_EQ(type, EncodedArrayValueIterator::ValueType::k ## Type); \ + const JValue encoded_value = \ + ConvertScalarBootstrapArgument(it->GetJavaValue()); \ + if (!GetArgumentForBootstrapMethod(self, \ + referrer, \ + type, \ + &encoded_value, \ + &decoded_value)) { \ + return false; \ + } \ + ObjPtr o = decoded_value.GetL(); \ + if (Runtime::Current()->IsActiveTransaction()) { \ + array->Set(i, ObjPtr::DownCast(o)); \ + } else { \ + array->Set(i, ObjPtr::DownCast(o)); \ + } \ + } \ + setter->SetReference(array.Get()); \ + return true; + + if (array_type->GetComponentType() == class_linker->FindPrimitiveClass('I')) { + COLLECT_PRIMITIVE_ARRAY(I, Int); + } else if (array_type->GetComponentType() == class_linker->FindPrimitiveClass('J')) { + COLLECT_PRIMITIVE_ARRAY(J, Long); + } else if (array_type->GetComponentType() == class_linker->FindPrimitiveClass('F')) { + COLLECT_PRIMITIVE_ARRAY(F, Float); + } else if (array_type->GetComponentType() == class_linker->FindPrimitiveClass('D')) { + COLLECT_PRIMITIVE_ARRAY(D, Double); + } else if (array_type->GetComponentType() == mirror::MethodType::StaticClass()) { + COLLECT_REFERENCE_ARRAY(mirror::MethodType, MethodType); + } else if (array_type->GetComponentType() == mirror::MethodHandle::StaticClass()) { + COLLECT_REFERENCE_ARRAY(mirror::MethodHandle, MethodHandle); + } else if (array_type->GetComponentType() == mirror::String::GetJavaLangString()) { + COLLECT_REFERENCE_ARRAY(mirror::String, String); + } else if (array_type->GetComponentType() == mirror::Class::GetJavaLangClass()) { + COLLECT_REFERENCE_ARRAY(mirror::Class, Type); + } else { + UNREACHABLE(); + } + #undef COLLECT_PRIMITIVE_ARRAY + #undef COLLECT_REFERENCE_ARRAY +} + +static ObjPtr BuildCallSiteForBootstrapMethod(Thread* self, + const DexFile* dex_file, + uint32_t call_site_idx) + REQUIRES_SHARED(Locks::mutator_lock_) { + const DexFile::CallSiteIdItem& csi = dex_file->GetCallSiteId(call_site_idx); + CallSiteArrayValueIterator it(*dex_file, csi); + DCHECK_GE(it.Size(), 1u); + + StackHandleScope<2> hs(self); + // Create array for parameter types. + ObjPtr class_type = mirror::Class::GetJavaLangClass(); + mirror::Class* class_array_type = + Runtime::Current()->GetClassLinker()->FindArrayClass(self, &class_type); + Handle> ptypes = hs.NewHandle( + mirror::ObjectArray::Alloc(self, + class_array_type, + static_cast(it.Size()))); + if (ptypes.IsNull()) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + // Populate the first argument with an instance of j.l.i.MethodHandles.Lookup + // that the runtime will construct. + ptypes->Set(0, mirror::MethodHandlesLookup::StaticClass()); + it.Next(); + + // The remaining parameter types are derived from the types of + // arguments present in the DEX file. + int index = 1; + while (it.HasNext()) { + ObjPtr ptype = GetClassForBootstrapArgument(it.GetValueType()); + if (ptype.IsNull()) { + ThrowClassCastException("Unsupported bootstrap argument type"); + return nullptr; + } + ptypes->Set(index, ptype); + index++; + it.Next(); + } + DCHECK_EQ(static_cast(index), it.Size()); + + // By definition, the return type is always a j.l.i.CallSite. + Handle rtype = hs.NewHandle(mirror::CallSite::StaticClass()); + return mirror::MethodType::Create(self, rtype, ptypes); +} + static ObjPtr InvokeBootstrapMethod(Thread* self, ShadowFrame& shadow_frame, uint32_t call_site_idx) REQUIRES_SHARED(Locks::mutator_lock_) { + StackHandleScope<7> hs(self); + // There are three mandatory arguments expected from the call site + // value array in the DEX file: the bootstrap method handle, the + // method name to pass to the bootstrap method, and the method type + // to pass to the bootstrap method. + static constexpr size_t kMandatoryArgumentsCount = 3; ArtMethod* referrer = shadow_frame.GetMethod(); const DexFile* dex_file = referrer->GetDexFile(); const DexFile::CallSiteIdItem& csi = dex_file->GetCallSiteId(call_site_idx); + CallSiteArrayValueIterator it(*dex_file, csi); + if (it.Size() < kMandatoryArgumentsCount) { + ThrowBootstrapMethodError("Truncated bootstrap arguments (%zu < %zu)", + it.Size(), kMandatoryArgumentsCount); + return nullptr; + } - StackHandleScope<10> hs(self); - Handle class_loader(hs.NewHandle(referrer->GetClassLoader())); - Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); + if (it.GetValueType() != EncodedArrayValueIterator::ValueType::kMethodHandle) { + ThrowBootstrapMethodError("First bootstrap argument is not a method handle"); + return nullptr; + } + + uint32_t bsm_index = static_cast(it.GetJavaValue().i); + it.Next(); - CallSiteArrayValueIterator it(*dex_file, csi); - uint32_t method_handle_idx = static_cast(it.GetJavaValue().i); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - Handle - bootstrap(hs.NewHandle(class_linker->ResolveMethodHandle(self, method_handle_idx, referrer))); - if (bootstrap.IsNull()) { + Handle bsm = + hs.NewHandle(class_linker->ResolveMethodHandle(self, bsm_index, referrer)); + if (bsm.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; } - Handle bootstrap_method_type = hs.NewHandle(bootstrap->GetMethodType()); - it.Next(); - DCHECK_EQ(static_cast(bootstrap->GetMethodType()->GetPTypes()->GetLength()), it.Size()); - const size_t num_bootstrap_vregs = bootstrap->GetMethodType()->NumberOfVRegs(); + if (bsm->GetHandleKind() != mirror::MethodHandle::Kind::kInvokeStatic) { + // JLS suggests also accepting constructors. This is currently + // hard as constructor invocations happen via transformers in ART + // today. The constructor would need to be a class derived from java.lang.invoke.CallSite. + ThrowBootstrapMethodError("Unsupported bootstrap method invocation kind"); + return nullptr; + } - // Set-up a shadow frame for invoking the bootstrap method handle. - ShadowFrameAllocaUniquePtr bootstrap_frame = - CREATE_SHADOW_FRAME(num_bootstrap_vregs, nullptr, referrer, shadow_frame.GetDexPC()); - ScopedStackedShadowFramePusher pusher( - self, bootstrap_frame.get(), StackedShadowFrameType::kShadowFrameUnderConstruction); - size_t vreg = 0; + // Construct the local call site type information based on the 3 + // mandatory arguments provided by the runtime and the static arguments + // in the DEX file. We will use these arguments to build a shadow frame. + MutableHandle call_site_type = + hs.NewHandle(BuildCallSiteForBootstrapMethod(self, dex_file, call_site_idx)); + if (call_site_type.IsNull()) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } - // The first parameter is a MethodHandles lookup instance. - { - Handle lookup_class = - hs.NewHandle(shadow_frame.GetMethod()->GetDeclaringClass()); - ObjPtr lookup = - mirror::MethodHandlesLookup::Create(self, lookup_class); - if (lookup.IsNull()) { + // Check if this BSM is targeting a variable arity method. If so, + // we'll need to collect the trailing arguments into an array. + Handle collector_arguments; + int32_t collector_arguments_length; + if (bsm->GetTargetMethod()->IsVarargs()) { + int number_of_bsm_parameters = bsm->GetMethodType()->GetNumberOfPTypes(); + if (number_of_bsm_parameters == 0) { + ThrowBootstrapMethodError("Variable arity BSM does not have any arguments"); + return nullptr; + } + Handle collector_array_class = + hs.NewHandle(bsm->GetMethodType()->GetPTypes()->Get(number_of_bsm_parameters - 1)); + if (!collector_array_class->IsArrayClass()) { + ThrowBootstrapMethodError("Variable arity BSM does not have array as final argument"); + return nullptr; + } + // The call site may include no arguments to be collected. In this + // case the number of arguments must be at least the number of BSM + // parameters less the collector array. + if (call_site_type->GetNumberOfPTypes() < number_of_bsm_parameters - 1) { + ThrowWrongMethodTypeException(bsm->GetMethodType(), call_site_type.Get()); + return nullptr; + } + // Check all the arguments to be collected match the collector array component type. + for (int i = number_of_bsm_parameters - 1; i < call_site_type->GetNumberOfPTypes(); ++i) { + if (call_site_type->GetPTypes()->Get(i) != collector_array_class->GetComponentType()) { + ThrowClassCastException(collector_array_class->GetComponentType(), + call_site_type->GetPTypes()->Get(i)); + return nullptr; + } + } + // Update the call site method type so it now includes the collector array. + int32_t collector_arguments_start = number_of_bsm_parameters - 1; + collector_arguments_length = call_site_type->GetNumberOfPTypes() - number_of_bsm_parameters + 1; + call_site_type.Assign( + mirror::MethodType::CollectTrailingArguments(self, + call_site_type.Get(), + collector_array_class.Get(), + collector_arguments_start)); + if (call_site_type.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; } - bootstrap_frame->SetVRegReference(vreg++, lookup.Ptr()); + } else { + collector_arguments_length = 0; } - // The second parameter is the name to lookup. - { - dex::StringIndex name_idx(static_cast(it.GetJavaValue().i)); - ObjPtr name = class_linker->ResolveString(name_idx, dex_cache); - if (name.IsNull()) { - DCHECK(self->IsExceptionPending()); + if (call_site_type->GetNumberOfPTypes() != bsm->GetMethodType()->GetNumberOfPTypes()) { + ThrowWrongMethodTypeException(bsm->GetMethodType(), call_site_type.Get()); + return nullptr; + } + + // BSM invocation has a different set of exceptions that + // j.l.i.MethodHandle.invoke(). Scan arguments looking for CCE + // "opportunities". Unfortunately we cannot just leave this to the + // method handle invocation as this might generate a WMTE. + for (int32_t i = 0; i < call_site_type->GetNumberOfPTypes(); ++i) { + ObjPtr from = call_site_type->GetPTypes()->Get(i); + ObjPtr to = bsm->GetMethodType()->GetPTypes()->Get(i); + if (!IsParameterTypeConvertible(from, to)) { + ThrowClassCastException(from, to); return nullptr; } - bootstrap_frame->SetVRegReference(vreg++, name.Ptr()); } - it.Next(); + if (!IsReturnTypeConvertible(call_site_type->GetRType(), bsm->GetMethodType()->GetRType())) { + ThrowClassCastException(bsm->GetMethodType()->GetRType(), call_site_type->GetRType()); + return nullptr; + } + + // Set-up a shadow frame for invoking the bootstrap method handle. + ShadowFrameAllocaUniquePtr bootstrap_frame = + CREATE_SHADOW_FRAME(call_site_type->NumberOfVRegs(), + nullptr, + referrer, + shadow_frame.GetDexPC()); + ScopedStackedShadowFramePusher pusher( + self, bootstrap_frame.get(), StackedShadowFrameType::kShadowFrameUnderConstruction); + ShadowFrameSetter setter(bootstrap_frame.get(), 0u); - // The third parameter is the method type associated with the name. - uint32_t method_type_idx = static_cast(it.GetJavaValue().i); - Handle method_type(hs.NewHandle( - class_linker->ResolveMethodType(self, method_type_idx, dex_cache, class_loader))); - if (method_type.IsNull()) { + // The first parameter is a MethodHandles lookup instance. + Handle lookup_class = + hs.NewHandle(shadow_frame.GetMethod()->GetDeclaringClass()); + ObjPtr lookup = + mirror::MethodHandlesLookup::Create(self, lookup_class); + if (lookup.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; } - bootstrap_frame->SetVRegReference(vreg++, method_type.Get()); - it.Next(); - - // Append remaining arguments (if any). - while (it.HasNext()) { - const jvalue& jvalue = it.GetJavaValue(); - switch (it.GetValueType()) { - case EncodedArrayValueIterator::ValueType::kBoolean: - case EncodedArrayValueIterator::ValueType::kByte: - case EncodedArrayValueIterator::ValueType::kChar: - case EncodedArrayValueIterator::ValueType::kShort: - case EncodedArrayValueIterator::ValueType::kInt: - bootstrap_frame->SetVReg(vreg, jvalue.i); - vreg += 1; - break; - case EncodedArrayValueIterator::ValueType::kLong: - bootstrap_frame->SetVRegLong(vreg, jvalue.j); - vreg += 2; - break; - case EncodedArrayValueIterator::ValueType::kFloat: - bootstrap_frame->SetVRegFloat(vreg, jvalue.f); - vreg += 1; - break; - case EncodedArrayValueIterator::ValueType::kDouble: - bootstrap_frame->SetVRegDouble(vreg, jvalue.d); - vreg += 2; - break; - case EncodedArrayValueIterator::ValueType::kMethodType: { - uint32_t idx = static_cast(jvalue.i); - ObjPtr ref = - class_linker->ResolveMethodType(self, idx, dex_cache, class_loader); - if (ref.IsNull()) { - DCHECK(self->IsExceptionPending()); - return nullptr; - } - bootstrap_frame->SetVRegReference(vreg, ref.Ptr()); - vreg += 1; - break; - } - case EncodedArrayValueIterator::ValueType::kMethodHandle: { - uint32_t idx = static_cast(jvalue.i); - ObjPtr ref = - class_linker->ResolveMethodHandle(self, idx, referrer); - if (ref.IsNull()) { - DCHECK(self->IsExceptionPending()); - return nullptr; - } - bootstrap_frame->SetVRegReference(vreg, ref.Ptr()); - vreg += 1; - break; - } - case EncodedArrayValueIterator::ValueType::kString: { - dex::StringIndex idx(static_cast(jvalue.i)); - ObjPtr ref = class_linker->ResolveString(idx, dex_cache); - if (ref.IsNull()) { - DCHECK(self->IsExceptionPending()); - return nullptr; - } - bootstrap_frame->SetVRegReference(vreg, ref.Ptr()); - vreg += 1; - break; - } - case EncodedArrayValueIterator::ValueType::kType: { - dex::TypeIndex idx(static_cast(jvalue.i)); - ObjPtr ref = class_linker->ResolveType(idx, dex_cache, class_loader); - if (ref.IsNull()) { - DCHECK(self->IsExceptionPending()); - return nullptr; - } - bootstrap_frame->SetVRegReference(vreg, ref.Ptr()); - vreg += 1; - break; + setter.SetReference(lookup); + + // Pack the remaining arguments into the frame. + int number_of_arguments = call_site_type->GetNumberOfPTypes(); + int argument_index; + for (argument_index = 1; argument_index < number_of_arguments; ++argument_index) { + if (argument_index == number_of_arguments - 1 && + call_site_type->GetPTypes()->Get(argument_index)->IsArrayClass()) { + ObjPtr array_type = call_site_type->GetPTypes()->Get(argument_index); + if (!PackCollectorArrayForBootstrapMethod(self, + referrer, + array_type, + collector_arguments_length, + &it, + &setter)) { + DCHECK(self->IsExceptionPending()); + return nullptr; } - case EncodedArrayValueIterator::ValueType::kNull: - bootstrap_frame->SetVRegReference(vreg, nullptr); - vreg += 1; - break; - case EncodedArrayValueIterator::ValueType::kField: - case EncodedArrayValueIterator::ValueType::kMethod: - case EncodedArrayValueIterator::ValueType::kEnum: - case EncodedArrayValueIterator::ValueType::kArray: - case EncodedArrayValueIterator::ValueType::kAnnotation: - // Unreachable based on current EncodedArrayValueIterator::Next(). - UNREACHABLE(); + } else if (!PackArgumentForBootstrapMethod(self, referrer, &it, &setter)) { + DCHECK(self->IsExceptionPending()); + return nullptr; } - it.Next(); } + DCHECK(!it.HasNext()); + DCHECK(setter.Done()); // Invoke the bootstrap method handle. JValue result; - RangeInstructionOperands operands(0, vreg); - bool invoke_success = MethodHandleInvokeExact(self, - *bootstrap_frame, - bootstrap, - bootstrap_method_type, - &operands, - &result); + RangeInstructionOperands operands(0, bootstrap_frame->NumberOfVRegs()); + bool invoke_success = MethodHandleInvoke(self, + *bootstrap_frame, + bsm, + call_site_type, + &operands, + &result); if (!invoke_success) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -1077,31 +1395,20 @@ static ObjPtr InvokeBootstrapMethod(Thread* self, return nullptr; } - // Check the result type is a subclass of CallSite. + // Check the result type is a subclass of j.l.i.CallSite. if (UNLIKELY(!object->InstanceOf(mirror::CallSite::StaticClass()))) { ThrowClassCastException(object->GetClass(), mirror::CallSite::StaticClass()); return nullptr; } + // Check the call site target is not null as we're going to invoke it. Handle call_site = hs.NewHandle(ObjPtr::DownCast(ObjPtr(result.GetL()))); - // Check the call site target is not null as we're going to invoke it. Handle target = hs.NewHandle(call_site->GetTarget()); if (UNLIKELY(target.IsNull())) { - ThrowClassCastException("Bootstrap method did not return a callsite"); - return nullptr; - } - - // Check the target method type matches the method type requested modulo the receiver - // needs to be compatible rather than exact. - Handle target_method_type = hs.NewHandle(target->GetMethodType()); - if (UNLIKELY(!target_method_type->IsExactMatch(method_type.Get()) && - !IsParameterTypeConvertible(target_method_type->GetPTypes()->GetWithoutChecks(0), - method_type->GetPTypes()->GetWithoutChecks(0)))) { - ThrowWrongMethodTypeException(target_method_type.Get(), method_type.Get()); + ThrowClassCastException("Bootstrap method returned a CallSite with a null target"); return nullptr; } - return call_site.Get(); } @@ -1129,8 +1436,11 @@ bool DoInvokeCustom(Thread* self, call_site.Assign(InvokeBootstrapMethod(self, shadow_frame, call_site_idx)); if (UNLIKELY(call_site.IsNull())) { CHECK(self->IsExceptionPending()); - ThrowWrappedBootstrapMethodError("Exception from call site #%u bootstrap method", - call_site_idx); + if (!self->GetException()->IsError()) { + // Use a BootstrapMethodError if the exception is not an instance of java.lang.Error. + ThrowWrappedBootstrapMethodError("Exception from call site #%u bootstrap method", + call_site_idx); + } result->SetJ(0); return false; } @@ -1139,9 +1449,6 @@ bool DoInvokeCustom(Thread* self, call_site.Assign(winning_call_site); } - // CallSite.java checks the re-assignment of the call site target - // when mutating call site targets. We only check the target is - // non-null and has the right type during bootstrap method execution. Handle target = hs.NewHandle(call_site->GetTarget()); Handle target_method_type = hs.NewHandle(target->GetMethodType()); DCHECK_EQ(static_cast(inst->VRegA()), target_method_type->NumberOfVRegs()); diff --git a/runtime/method_handles.h b/runtime/method_handles.h index 7e60a5c170..fce3d06639 100644 --- a/runtime/method_handles.h +++ b/runtime/method_handles.h @@ -174,19 +174,26 @@ class ShadowFrameSetter { : shadow_frame_(shadow_frame), arg_index_(first_dst_reg) {} ALWAYS_INLINE void Set(uint32_t value) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_LT(arg_index_, shadow_frame_->NumberOfVRegs()); shadow_frame_->SetVReg(arg_index_++, value); } ALWAYS_INLINE void SetReference(ObjPtr value) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_LT(arg_index_, shadow_frame_->NumberOfVRegs()); shadow_frame_->SetVRegReference(arg_index_++, value.Ptr()); } ALWAYS_INLINE void SetLong(int64_t value) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_LT(arg_index_, shadow_frame_->NumberOfVRegs()); shadow_frame_->SetVRegLong(arg_index_, value); arg_index_ += 2; } + ALWAYS_INLINE bool Done() const { + return arg_index_ == shadow_frame_->NumberOfVRegs(); + } + private: ShadowFrame* shadow_frame_; size_t arg_index_; diff --git a/runtime/mirror/method_type.cc b/runtime/mirror/method_type.cc index 6ac5012ca3..45f7a87951 100644 --- a/runtime/mirror/method_type.cc +++ b/runtime/mirror/method_type.cc @@ -23,6 +23,18 @@ namespace art { namespace mirror { +namespace { + +ObjPtr> AllocatePTypesArray(Thread* self, int count) + REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr class_type = Class::GetJavaLangClass(); + ObjPtr class_array_type = + Runtime::Current()->GetClassLinker()->FindArrayClass(self, &class_type); + return ObjectArray::Alloc(self, class_array_type, count); +} + +} // namespace + GcRoot MethodType::static_class_; MethodType* MethodType::Create(Thread* const self, @@ -47,18 +59,41 @@ MethodType* MethodType::Create(Thread* const self, MethodType* MethodType::CloneWithoutLeadingParameter(Thread* const self, ObjPtr method_type) { StackHandleScope<3> hs(self); - Handle rtype = hs.NewHandle(method_type->GetRType()); Handle> src_ptypes = hs.NewHandle(method_type->GetPTypes()); - ObjPtr class_type = Class::GetJavaLangClass(); - ObjPtr class_array_type = - Runtime::Current()->GetClassLinker()->FindArrayClass(self, &class_type); - const int32_t dst_ptypes_count = src_ptypes->GetLength() - 1; - Handle> dst_ptypes = hs.NewHandle( - ObjectArray::Alloc(self, class_array_type, dst_ptypes_count)); + Handle dst_rtype = hs.NewHandle(method_type->GetRType()); + const int32_t dst_ptypes_count = method_type->GetNumberOfPTypes() - 1; + Handle> dst_ptypes = hs.NewHandle(AllocatePTypesArray(self, dst_ptypes_count)); + if (dst_ptypes.IsNull()) { + return nullptr; + } for (int32_t i = 0; i < dst_ptypes_count; ++i) { dst_ptypes->Set(i, src_ptypes->Get(i + 1)); } - return Create(self, rtype, dst_ptypes); + return Create(self, dst_rtype, dst_ptypes); +} + +MethodType* MethodType::CollectTrailingArguments(Thread* self, + ObjPtr method_type, + ObjPtr collector_array_class, + int32_t start_index) { + int32_t ptypes_length = method_type->GetNumberOfPTypes(); + if (start_index > ptypes_length) { + return method_type.Ptr(); + } + + StackHandleScope<4> hs(self); + Handle collector_class = hs.NewHandle(collector_array_class); + Handle dst_rtype = hs.NewHandle(method_type->GetRType()); + Handle> src_ptypes = hs.NewHandle(method_type->GetPTypes()); + Handle> dst_ptypes = hs.NewHandle(AllocatePTypesArray(self, start_index + 1)); + if (dst_ptypes.IsNull()) { + return nullptr; + } + for (int32_t i = 0; i < start_index; ++i) { + dst_ptypes->Set(i, src_ptypes->Get(i)); + } + dst_ptypes->Set(start_index, collector_class.Get()); + return Create(self, dst_rtype, dst_ptypes); } size_t MethodType::NumberOfVRegs() REQUIRES_SHARED(Locks::mutator_lock_) { diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h index 3627214d90..771162a2de 100644 --- a/runtime/mirror/method_type.h +++ b/runtime/mirror/method_type.h @@ -40,6 +40,14 @@ class MANAGED MethodType : public Object { ObjPtr method_type) REQUIRES_SHARED(Locks::mutator_lock_); + // Collects trailing parameter types into an array. Assumes caller + // has checked trailing arguments are all of the same type. + static MethodType* CollectTrailingArguments(Thread* const self, + ObjPtr method_type, + ObjPtr collector_array_class, + int32_t start_index) + REQUIRES_SHARED(Locks::mutator_lock_); + static Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { return static_class_.Read(); } diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc index b6173d422b..70069733d1 100644 --- a/runtime/mirror/throwable.cc +++ b/runtime/mirror/throwable.cc @@ -75,6 +75,10 @@ bool Throwable::IsCheckedException() { return !InstanceOf(WellKnownClasses::ToClass(WellKnownClasses::java_lang_RuntimeException)); } +bool Throwable::IsError() { + return InstanceOf(WellKnownClasses::ToClass(WellKnownClasses::java_lang_Error)); +} + int32_t Throwable::GetStackDepth() { ObjPtr stack_state = GetStackState(); if (stack_state == nullptr || !stack_state->IsObjectArray()) { diff --git a/runtime/mirror/throwable.h b/runtime/mirror/throwable.h index fb45228f49..b901ca2d00 100644 --- a/runtime/mirror/throwable.h +++ b/runtime/mirror/throwable.h @@ -44,6 +44,7 @@ class MANAGED Throwable : public Object { void SetCause(ObjPtr cause) REQUIRES_SHARED(Locks::mutator_lock_); void SetStackState(ObjPtr state) REQUIRES_SHARED(Locks::mutator_lock_); bool IsCheckedException() REQUIRES_SHARED(Locks::mutator_lock_); + bool IsError() REQUIRES_SHARED(Locks::mutator_lock_); static Class* GetJavaLangThrowable() REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(!java_lang_Throwable_.IsNull()); diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 74c2244cfe..21a4ecca47 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -4206,14 +4206,19 @@ bool MethodVerifier::CheckCallSite(uint32_t call_site_idx) { if (it.Size() < 3) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx << " has too few arguments: " - << it.Size() << "< 3"; + << it.Size() << " < 3"; return false; } // Get and check the first argument: the method handle (index range // checked by the dex file verifier). uint32_t method_handle_idx = static_cast(it.GetJavaValue().i); - it.Next(); + if (method_handle_idx > dex_file_->NumMethodHandles()) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site id #" << call_site_idx + << " method handle index invalid " << method_handle_idx + << " >= " << dex_file_->NumMethodHandles(); + return false; + } const DexFile::MethodHandleItem& mh = dex_file_->GetMethodHandle(method_handle_idx); if (mh.method_handle_type_ != static_cast(DexFile::MethodHandleType::kInvokeStatic)) { @@ -4222,93 +4227,6 @@ bool MethodVerifier::CheckCallSite(uint32_t call_site_idx) { << mh.method_handle_type_; return false; } - - // Skip the second argument, the name to resolve, as checked by the - // dex file verifier. - it.Next(); - - // Skip the third argument, the method type expected, as checked by - // the dex file verifier. - it.Next(); - - // Check the bootstrap method handle and remaining arguments. - const DexFile::MethodId& method_id = dex_file_->GetMethodId(mh.field_or_method_idx_); - uint32_t length; - const char* shorty = dex_file_->GetMethodShorty(method_id, &length); - - if (it.Size() < length - 1) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx - << " too few arguments for bootstrap method: " - << it.Size() << " < " << (length - 1); - return false; - } - - // Check the return type and first 3 arguments are references - // (CallSite, Lookup, String, MethodType). If they are not of the - // expected types (or subtypes), it will trigger a - // WrongMethodTypeException during execution. - if (shorty[0] != 'L') { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx - << " bootstrap return type is not a reference"; - return false; - } - - for (uint32_t i = 1; i < 4; ++i) { - if (shorty[i] != 'L') { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx - << " bootstrap method argument " << (i - 1) - << " is not a reference"; - return false; - } - } - - // Check the optional arguments. - for (uint32_t i = 4; i < length; ++i, it.Next()) { - bool match = false; - switch (it.GetValueType()) { - case EncodedArrayValueIterator::ValueType::kBoolean: - case EncodedArrayValueIterator::ValueType::kByte: - case EncodedArrayValueIterator::ValueType::kShort: - case EncodedArrayValueIterator::ValueType::kChar: - case EncodedArrayValueIterator::ValueType::kInt: - // These all fit within one register and encoders do not seem - // too exacting on the encoding type they use (ie using - // integer for all of these). - match = (strchr("ZBCSI", shorty[i]) != nullptr); - break; - case EncodedArrayValueIterator::ValueType::kLong: - match = ('J' == shorty[i]); - break; - case EncodedArrayValueIterator::ValueType::kFloat: - match = ('F' == shorty[i]); - break; - case EncodedArrayValueIterator::ValueType::kDouble: - match = ('D' == shorty[i]); - break; - case EncodedArrayValueIterator::ValueType::kMethodType: - case EncodedArrayValueIterator::ValueType::kMethodHandle: - case EncodedArrayValueIterator::ValueType::kString: - case EncodedArrayValueIterator::ValueType::kType: - case EncodedArrayValueIterator::ValueType::kNull: - match = ('L' == shorty[i]); - break; - case EncodedArrayValueIterator::ValueType::kField: - case EncodedArrayValueIterator::ValueType::kMethod: - case EncodedArrayValueIterator::ValueType::kEnum: - case EncodedArrayValueIterator::ValueType::kArray: - case EncodedArrayValueIterator::ValueType::kAnnotation: - // Unreachable based on current EncodedArrayValueIterator::Next(). - UNREACHABLE(); - } - - if (!match) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx - << " bootstrap method argument " << (i - 1) - << " expected " << shorty[i] - << " got value type: " << it.GetValueType(); - return false; - } - } return true; } diff --git a/test/952-invoke-custom/expected.txt b/test/952-invoke-custom/expected.txt index 767cc7e734..7e8ffa6f41 100644 --- a/test/952-invoke-custom/expected.txt +++ b/test/952-invoke-custom/expected.txt @@ -18,3 +18,67 @@ testStaticFieldAccessors testInstanceFieldAccessors testInvokeVirtual => max(77, -3) = 77 testConstructor => class TestInvocationKinds$Widget +TestDynamicArguments +bsm +0, One, 3.141592653589793 +bsm +1, Two, 2.718281828459045 +bsm +2, Three, 0.0 +0, One, 3.141592653589793 +1, Two, 2.718281828459045 +2, Three, 0.0 +TestBadBootstrapArguments +bsm(class TestBadBootstrapArguments, happy, ()void, -1, very) +happy +invokeWrongParameterTypes => class java.lang.NoSuchMethodError +invokeMissingParameterTypes => class java.lang.NoSuchMethodError +invokeExtraArguments => class java.lang.BootstrapMethodError => class java.lang.invoke.WrongMethodTypeException +invokeWrongArguments => class java.lang.BootstrapMethodError => class java.lang.ClassCastException +invokeWrongArguments => class java.lang.BootstrapMethodError => class java.lang.ClassCastException +invokeWrongArgumentsAgain => class java.lang.BootstrapMethodError => class java.lang.ClassCastException +invokeNarrowArguments => class java.lang.BootstrapMethodError => class java.lang.ClassCastException +bsmDJ(..., 1.7976931348623157E308, 2147483647) +wideningArguments +bsmDoubleLong(..., 1.7976931348623157E308, 9223372036854775807) +boxingArguments +invokeWideningBoxingArguments => class java.lang.BootstrapMethodError => class java.lang.ClassCastException +bsm returning void value. +invokeVoidReturnType() => class java.lang.BootstrapMethodError => class java.lang.ClassCastException +bsm returning Object value. +invokeObjectReturnType() => class java.lang.BootstrapMethodError => class java.lang.ClassCastException +bsm returning Integer value. +invokeIntegerReturnType() => class java.lang.BootstrapMethodError => class java.lang.ClassCastException +Hello! +bsmWithStringArray(TestVariableArityLinkerMethod, methodA, ()void, [Aachen, Aalborg, Aalto]); +methodA +bsmWithStringArray(TestVariableArityLinkerMethod, methodB, ()void, [barium]); +methodB +bsmWithStringArray(TestVariableArityLinkerMethod, methodC, ()void, []); +methodC +methodA +methodB +methodC +bsmWithIntAndStringArray(TestVariableArityLinkerMethod, methodD, ()void, 101, [zoo, zoogene, zoogenic]); +methodD +bsmWithIntAndStringArray(TestVariableArityLinkerMethod, methodE, ()void, 102, [zonic]); +methodE +bsmWithIntAndStringArray(TestVariableArityLinkerMethod, methodF, ()void, 103, []); +methodF +methodD +methodE +methodF +bsmWithLongAndIntArray(TestVariableArityLinkerMethod, methodG, ()void, 81985529216486895, [1, -1, 2, -2]); +methodG +bsmWithFloatAndLongArray(TestVariableArityLinkerMethod, methodH, ()void, -2.7182817, [999999999999, -8888888888888]); +methodH +bsmWithClassAndFloatArray(TestVariableArityLinkerMethod, methodI, ()void, class java.lang.Throwable, [3.4028235E38, 1.4E-45, 3.1415927, -3.1415927]); +methodI +bsmWithDoubleArray(TestVariableArityLinkerMethod, methodJ, ()void, [1.7976931348623157E308, 4.9E-324, 2.718281828459045, -3.141592653589793]); +methodJ +bsmWithClassArray(TestVariableArityLinkerMethod, methodK, ()void, [class java.lang.Integer, class java.lang.invoke.MethodHandles, class java.util.Arrays]); +methodK +methodO => class java.lang.BootstrapMethodError => class java.lang.ClassCastException +methodP => class java.lang.BootstrapMethodError => class java.lang.ClassCastException +methodQ => class java.lang.BootstrapMethodError => class java.lang.invoke.WrongMethodTypeException +methodR => class java.lang.BootstrapMethodError => class java.lang.invoke.WrongMethodTypeException diff --git a/test/952-invoke-custom/src/Main.java b/test/952-invoke-custom/src/Main.java index 0b1c1fffe5..d2250a9647 100644 --- a/test/952-invoke-custom/src/Main.java +++ b/test/952-invoke-custom/src/Main.java @@ -74,18 +74,15 @@ public class Main extends TestBase { TestLinkerMethodMinimalArguments.FAILURE_TYPE_NONE, 10, 13); } - private static void TestInvokeCustomWithConcurrentThreads() throws Throwable { - // This is a concurrency test that attempts to run invoke-custom on the same - // call site. - TestInvokeCustomWithConcurrentThreads.test(); - } - public static void main(String[] args) throws Throwable { TestUninitializedCallSite(); TestLinkerMethodMinimalArguments(); TestLinkerMethodMultipleArgumentTypes(); TestLinkerUnrelatedBSM.test(); - TestInvokeCustomWithConcurrentThreads(); + TestInvokeCustomWithConcurrentThreads.test(); TestInvocationKinds.test(); + TestDynamicBootstrapArguments.test(); + TestBadBootstrapArguments.test(); + TestVariableArityLinkerMethod.test(); } } diff --git a/test/952-invoke-custom/src/TestBadBootstrapArguments.java b/test/952-invoke-custom/src/TestBadBootstrapArguments.java new file mode 100644 index 0000000000..25d8b59eaa --- /dev/null +++ b/test/952-invoke-custom/src/TestBadBootstrapArguments.java @@ -0,0 +1,583 @@ +/* + * 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.BootstrapMethod; +import annotations.CalledByIndy; +import annotations.Constant; +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.invoke.WrongMethodTypeException; + +public class TestBadBootstrapArguments extends TestBase { + private static CallSite bsm( + MethodHandles.Lookup lookup, + String methodName, + MethodType methodType, + int extraInt, + String extraString) + throws Throwable { + System.out.print("bsm("); + System.out.print(lookup.lookupClass()); + System.out.print(", "); + System.out.print(methodName); + System.out.print(", "); + System.out.print(methodType); + System.out.print(", "); + System.out.print(extraInt); + System.out.print(", "); + System.out.print(extraString); + System.out.println(")"); + MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); + return new ConstantCallSite(mh); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestBadBootstrapArguments.class, + name = "bsm", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + int.class, + String.class + } + ), + fieldOrMethodName = "happy", + constantArgumentsForBootstrapMethod = { + @Constant(intValue = -1), + @Constant(stringValue = "very") + } + ) + private static void invokeHappy() { + assertNotReached(); + } + + private static void happy() { + System.out.println("happy"); + } + + // BootstrapMethod.parameterTypes != parameterTypesOf(constantArgumentsForBootstrapMethod) + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestBadBootstrapArguments.class, + name = "bsm", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + int.class, + double.class + } + ), + fieldOrMethodName = "wrongParameterTypes", + constantArgumentsForBootstrapMethod = { + @Constant(intValue = -1), + @Constant(stringValue = "very") + } + ) + private static void invokeWrongParameterTypes() throws NoSuchMethodError { + assertNotReached(); + } + + private static void wrongParameterTypes() { + System.out.println("wrongParameterTypes"); + } + + // BootstrapMethod.parameterTypes != parameterTypesOf(constantArgumentsForBootstrapMethod) + // (missing constantArgumentTypes)) + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestBadBootstrapArguments.class, + name = "bsm", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + int.class, + double.class + } + ), + fieldOrMethodName = "missingParameterTypes", + constantArgumentsForBootstrapMethod = {} + ) + private static void invokeMissingParameterTypes() throws NoSuchMethodError { + assertNotReached(); + } + + private static void missingParameterTypes() { + System.out.println("missingParameterTypes"); + } + + // BootstrapMethod.parameterTypes != parameterTypesOf(constantArgumentsForBootstrapMethod): + // extra constant present + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestBadBootstrapArguments.class, + name = "bsm", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + int.class, + String.class + } + ), + fieldOrMethodName = "extraArguments", + constantArgumentsForBootstrapMethod = { + @Constant(intValue = 1), + @Constant(stringValue = "2"), + @Constant(intValue = 3) + } + ) + private static void invokeExtraArguments() { + assertNotReached(); + } + + private static void extraArguments() { + System.out.println("extraArguments"); + } + + // constantArgumentTypes do not correspond to expected parameter types + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestBadBootstrapArguments.class, + name = "bsm", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + int.class, + String.class + } + ), + fieldOrMethodName = "wrongArguments", + constantArgumentsForBootstrapMethod = { + @Constant(stringValue = "1"), + @Constant(doubleValue = Math.PI) + } + ) + private static void invokeWrongArguments() { + assertNotReached(); + } + + private static void wrongArguments() { + System.out.println("wrongArguments"); + } + + // constantArgumentTypes do not correspond to expected parameter types + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestBadBootstrapArguments.class, + name = "bsm", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + int.class, + String.class + } + ), + fieldOrMethodName = "wrongArgumentsAgain", + constantArgumentsForBootstrapMethod = { + @Constant(doubleValue = Math.PI), + @Constant(stringValue = "pie") + } + ) + private static void invokeWrongArgumentsAgain() { + assertNotReached(); + } + + private static void wrongArgumentsAgain() { + System.out.println("wrongArgumentsAgain"); + } + + // Primitive argument types not supported {Z, B, C, S}. + private static CallSite bsmZBCS( + MethodHandles.Lookup lookup, + String methodName, + MethodType methodType, + boolean extraArg0, + byte extraArg1, + char extraArg2, + short extraArg3) + throws Throwable { + assertNotReached(); + return null; + } + + // Arguments are narrower than supported. + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestBadBootstrapArguments.class, + name = "bsmZBCS", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + boolean.class, + byte.class, + char.class, + short.class + } + ), + fieldOrMethodName = "narrowArguments", + constantArgumentsForBootstrapMethod = { + @Constant(booleanValue = true), + @Constant(byteValue = Byte.MAX_VALUE), + @Constant(charValue = 'A'), + @Constant(shortValue = Short.MIN_VALUE) + } + ) + private static void invokeNarrowArguments() { + assertNotReached(); + } + + private static void narrowArguments() { + assertNotReached(); + } + + private static CallSite bsmDJ( + MethodHandles.Lookup lookup, + String methodName, + MethodType methodType, + double extraArg0, + long extraArg1) + throws Throwable { + System.out.print("bsmDJ(..., "); + System.out.print(extraArg0); + System.out.print(", "); + System.out.print(extraArg1); + System.out.println(")"); + MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); + return new ConstantCallSite(mh); + } + + // Arguments need widening to parameter types. + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestBadBootstrapArguments.class, + name = "bsmDJ", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + double.class, + long.class + } + ), + fieldOrMethodName = "wideningArguments", + constantArgumentsForBootstrapMethod = { + @Constant(doubleValue = Double.MAX_VALUE), + @Constant(intValue = Integer.MAX_VALUE) + } + ) + private static void invokeWideningArguments() { + assertNotReached(); + } + + private static void wideningArguments() { + System.out.println("wideningArguments"); + } + + private static CallSite bsmDoubleLong( + MethodHandles.Lookup lookup, + String methodName, + MethodType methodType, + Double extraArg0, + Long extraArg1) + throws Throwable { + System.out.print("bsmDoubleLong(..., "); + System.out.print(extraArg0); + System.out.print(", "); + System.out.print(extraArg1); + System.out.println(")"); + MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); + return new ConstantCallSite(mh); + } + + // Arguments need boxing to parameter types + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestBadBootstrapArguments.class, + name = "bsmDoubleLong", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + Double.class, + Long.class + } + ), + fieldOrMethodName = "boxingArguments", + constantArgumentsForBootstrapMethod = { + @Constant(doubleValue = Double.MAX_VALUE), + @Constant(longValue = Long.MAX_VALUE) + } + ) + private static void invokeBoxingArguments() { + assertNotReached(); + } + + private static void boxingArguments() { + System.out.println("boxingArguments"); + } + + // Arguments need widening and boxing to parameter types + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestBadBootstrapArguments.class, + name = "bsmDoubleLong", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + Double.class, + Long.class + } + ), + fieldOrMethodName = "wideningBoxingArguments", + constantArgumentsForBootstrapMethod = { + @Constant(floatValue = Float.MAX_VALUE), + @Constant(longValue = Integer.MAX_VALUE) + } + ) + private static void invokeWideningBoxingArguments() { + assertNotReached(); + } + + private static void wideningBoxingArguments() { + System.out.println("wideningBoxingArguments"); + } + + static void bsmReturningVoid(MethodHandles.Lookup lookup, String name, MethodType type) { + System.out.println("bsm returning void value."); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestBadBootstrapArguments.class, + name = "bsmReturningVoid", + parameterTypes = {MethodHandles.Lookup.class, String.class, MethodType.class}, + returnType = void.class + ), + fieldOrMethodName = "voidReturnType" + ) + private static void invokeVoidReturnType() { + assertNotReached(); + } + + private static void voidReturnType() { + assertNotReached(); + } + + static Object bsmReturningObject(MethodHandles.Lookup lookup, String name, MethodType type) { + System.out.println("bsm returning Object value."); + return new Object(); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestBadBootstrapArguments.class, + name = "bsmReturningObject", + parameterTypes = {MethodHandles.Lookup.class, String.class, MethodType.class}, + returnType = Object.class + ), + fieldOrMethodName = "ObjectReturnType" + ) + private static void invokeObjectReturnType() { + assertNotReached(); + } + + private static void objectReturnType() { + assertNotReached(); + } + + static Integer bsmReturningInteger(MethodHandles.Lookup lookup, String name, MethodType type) { + System.out.println("bsm returning Integer value."); + return Integer.valueOf(3); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestBadBootstrapArguments.class, + name = "bsmReturningInteger", + parameterTypes = {MethodHandles.Lookup.class, String.class, MethodType.class}, + returnType = Integer.class + ), + fieldOrMethodName = "integerReturnType" + ) + private static void invokeIntegerReturnType() { + assertNotReached(); + } + + private static void integerReturnType() { + assertNotReached(); + } + + static class TestersConstantCallSite extends ConstantCallSite { + public TestersConstantCallSite(MethodHandle mh) { + super(mh); + } + } + + static TestersConstantCallSite bsmReturningTestersConstantCallsite( + MethodHandles.Lookup lookup, String name, MethodType type) throws Throwable { + return new TestersConstantCallSite(lookup.findStatic(lookup.lookupClass(), name, type)); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestBadBootstrapArguments.class, + name = "bsmReturningTestersConstantCallsite", + parameterTypes = {MethodHandles.Lookup.class, String.class, MethodType.class}, + returnType = TestersConstantCallSite.class + ), + fieldOrMethodName = "sayHello" + ) + private static void invokeViaCustomCallSiteClass() { + assertNotReached(); + } + + private static void sayHello() { + System.out.println("Hello!"); + } + + static void test() { + System.out.println("TestBadBootstrapArguments"); + invokeHappy(); + try { + invokeWrongParameterTypes(); + assertNotReached(); + } catch (NoSuchMethodError expected) { + System.out.print("invokeWrongParameterTypes => "); + System.out.println(expected.getClass()); + } + try { + invokeMissingParameterTypes(); + assertNotReached(); + } catch (NoSuchMethodError expected) { + System.out.print("invokeMissingParameterTypes => "); + System.out.println(expected.getClass()); + } + try { + invokeExtraArguments(); + assertNotReached(); + } catch (BootstrapMethodError expected) { + assertEquals(WrongMethodTypeException.class, expected.getCause().getClass()); + System.out.print("invokeExtraArguments => "); + System.out.print(expected.getClass()); + System.out.print(" => "); + System.out.println(expected.getCause().getClass()); + } + try { + invokeWrongArguments(); + assertNotReached(); + } catch (BootstrapMethodError expected) { + assertEquals(ClassCastException.class, expected.getCause().getClass()); + System.out.print("invokeWrongArguments => "); + System.out.print(expected.getClass()); + System.out.print(" => "); + System.out.println(expected.getCause().getClass()); + } + try { + invokeWrongArguments(); + assertNotReached(); + } catch (BootstrapMethodError expected) { + assertEquals(ClassCastException.class, expected.getCause().getClass()); + System.out.print("invokeWrongArguments => "); + System.out.print(expected.getClass()); + System.out.print(" => "); + System.out.println(expected.getCause().getClass()); + } + try { + invokeWrongArgumentsAgain(); + assertNotReached(); + } catch (BootstrapMethodError expected) { + assertEquals(ClassCastException.class, expected.getCause().getClass()); + System.out.print("invokeWrongArgumentsAgain => "); + System.out.print(expected.getClass()); + System.out.print(" => "); + System.out.println(expected.getCause().getClass()); + } + try { + invokeNarrowArguments(); + assertNotReached(); + } catch (BootstrapMethodError expected) { + assertEquals(ClassCastException.class, expected.getCause().getClass()); + System.out.print("invokeNarrowArguments => "); + System.out.print(expected.getClass()); + System.out.print(" => "); + System.out.println(expected.getCause().getClass()); + } + invokeWideningArguments(); + invokeBoxingArguments(); + try { + invokeWideningBoxingArguments(); + assertNotReached(); + } catch (BootstrapMethodError expected) { + System.out.print("invokeWideningBoxingArguments => "); + System.out.print(expected.getClass()); + System.out.print(" => "); + System.out.println(expected.getCause().getClass()); + } + try { + invokeVoidReturnType(); + assertNotReached(); + } catch (BootstrapMethodError expected) { + System.out.print("invokeVoidReturnType() => "); + System.out.print(expected.getClass()); + System.out.print(" => "); + System.out.println(expected.getCause().getClass()); + } + try { + invokeObjectReturnType(); + assertNotReached(); + } catch (BootstrapMethodError expected) { + System.out.print("invokeObjectReturnType() => "); + System.out.print(expected.getClass()); + System.out.print(" => "); + System.out.println(expected.getCause().getClass()); + } + try { + invokeIntegerReturnType(); + assertNotReached(); + } catch (BootstrapMethodError expected) { + System.out.print("invokeIntegerReturnType() => "); + System.out.print(expected.getClass()); + System.out.print(" => "); + System.out.println(expected.getCause().getClass()); + } + invokeViaCustomCallSiteClass(); + } +} diff --git a/test/952-invoke-custom/src/TestDynamicBootstrapArguments.java b/test/952-invoke-custom/src/TestDynamicBootstrapArguments.java new file mode 100644 index 0000000000..782feca6da --- /dev/null +++ b/test/952-invoke-custom/src/TestDynamicBootstrapArguments.java @@ -0,0 +1,92 @@ +/* + * 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.BootstrapMethod; +import annotations.CalledByIndy; +import annotations.Constant; +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +class TestDynamicBootstrapArguments extends TestBase { + private static int bsmCalls = 0; + + static CallSite bsm( + MethodHandles.Lookup lookup, + String name, + MethodType methodType, + String otherNameComponent, + long nameSuffix) + throws Throwable { + bsmCalls = bsmCalls + 1; + Class definingClass = TestDynamicBootstrapArguments.class; + String methodName = name + otherNameComponent + nameSuffix; + MethodHandle mh = lookup.findStatic(definingClass, methodName, methodType); + System.out.println("bsm"); + return new ConstantCallSite(mh); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestDynamicBootstrapArguments.class, + name = "bsm", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + String.class, + long.class + } + ), + fieldOrMethodName = "target", + returnType = int.class, + parameterTypes = {int.class, String.class, double.class}, + constantArgumentsForBootstrapMethod = { + @Constant(stringValue = "A"), + @Constant(longValue = 100000000l) + } + ) + private static int testDynamic(int i, String s, Double d) { + assertNotReached(); + return 0; + } + + private static int targetA100000000(int i, String s, Double d) { + System.out.print(i); + System.out.print(", "); + System.out.print(s); + System.out.print(", "); + System.out.println(d); + return i; + } + + static void testCallSites() { + assertEquals(0, testDynamic(0, "One", Math.PI)); + assertEquals(1, testDynamic(1, "Two", Math.E)); + assertEquals(2, testDynamic(2, "Three", 0.0)); + } + + static void test() { + System.out.println("TestDynamicArguments"); + testCallSites(); + assertEquals(3, bsmCalls); + testCallSites(); + assertEquals(3, bsmCalls); + } +} diff --git a/test/952-invoke-custom/src/TestInvocationKinds.java b/test/952-invoke-custom/src/TestInvocationKinds.java index 7b88c18c66..f743bef158 100644 --- a/test/952-invoke-custom/src/TestInvocationKinds.java +++ b/test/952-invoke-custom/src/TestInvocationKinds.java @@ -173,6 +173,7 @@ class TestInvocationKinds extends TestBase { static class Widget { int value; + public Widget(int value) {} } diff --git a/test/952-invoke-custom/src/TestVariableArityLinkerMethod.java b/test/952-invoke-custom/src/TestVariableArityLinkerMethod.java new file mode 100644 index 0000000000..597273c8ec --- /dev/null +++ b/test/952-invoke-custom/src/TestVariableArityLinkerMethod.java @@ -0,0 +1,569 @@ +/* + * 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.BootstrapMethod; +import annotations.CalledByIndy; +import annotations.Constant; +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.Arrays; + +public class TestVariableArityLinkerMethod extends TestBase { + private static void printBsmArgs(String method, Object... args) { + System.out.print(method); + System.out.print("("); + for (int i = 0; i < args.length; ++i) { + if (i != 0) { + System.out.print(", "); + } + if (args[i] != null && args[i].getClass().isArray()) { + Object array = args[i]; + if (array.getClass() == int[].class) { + System.out.print(Arrays.toString((int[]) array)); + } else if (array.getClass() == long[].class) { + System.out.print(Arrays.toString((long[]) array)); + } else if (array.getClass() == float[].class) { + System.out.print(Arrays.toString((float[]) array)); + } else if (array.getClass() == double[].class) { + System.out.print(Arrays.toString((double[]) array)); + } else { + System.out.print(Arrays.toString((Object[]) array)); + } + } else { + System.out.print(args[i]); + } + } + System.out.println(");"); + } + + private static CallSite bsmWithStringArray( + MethodHandles.Lookup lookup, + String methodName, + MethodType methodType, + String... arityArgs) + throws Throwable { + printBsmArgs("bsmWithStringArray", lookup, methodName, methodType, arityArgs); + MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); + return new ConstantCallSite(mh); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestVariableArityLinkerMethod.class, + name = "bsmWithStringArray", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + String[].class + } + ), + fieldOrMethodName = "methodA", + constantArgumentsForBootstrapMethod = { + @Constant(stringValue = "Aachen"), + @Constant(stringValue = "Aalborg"), + @Constant(stringValue = "Aalto") + } + ) + private static void methodA() { + System.out.println("methodA"); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestVariableArityLinkerMethod.class, + name = "bsmWithStringArray", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + String[].class + } + ), + fieldOrMethodName = "methodB", + constantArgumentsForBootstrapMethod = {@Constant(stringValue = "barium")} + ) + private static void methodB() { + System.out.println("methodB"); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestVariableArityLinkerMethod.class, + name = "bsmWithStringArray", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + String[].class + } + ), + fieldOrMethodName = "methodC" + ) + private static void methodC() { + System.out.println("methodC"); + } + + private static CallSite bsmWithIntAndStringArray( + MethodHandles.Lookup lookup, + String methodName, + MethodType methodType, + int extraInt, + String... extraArityArgs) + throws Throwable { + printBsmArgs( + "bsmWithIntAndStringArray", + lookup, + methodName, + methodType, + extraInt, + extraArityArgs); + MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); + return new ConstantCallSite(mh); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestVariableArityLinkerMethod.class, + name = "bsmWithIntAndStringArray", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + int.class, + String[].class + } + ), + fieldOrMethodName = "methodD", + constantArgumentsForBootstrapMethod = { + @Constant(intValue = 101), + @Constant(stringValue = "zoo"), + @Constant(stringValue = "zoogene"), + @Constant(stringValue = "zoogenic") + } + ) + private static void methodD() { + System.out.println("methodD"); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestVariableArityLinkerMethod.class, + name = "bsmWithIntAndStringArray", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + int.class, + String[].class + } + ), + fieldOrMethodName = "methodE", + constantArgumentsForBootstrapMethod = { + @Constant(intValue = 102), + @Constant(stringValue = "zonic") + } + ) + private static void methodE() { + System.out.println("methodE"); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestVariableArityLinkerMethod.class, + name = "bsmWithIntAndStringArray", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + int.class, + String[].class + } + ), + fieldOrMethodName = "methodF", + constantArgumentsForBootstrapMethod = {@Constant(intValue = 103)} + ) + private static void methodF() { + System.out.println("methodF"); + } + + private static CallSite bsmWithLongAndIntArray( + MethodHandles.Lookup lookup, + String methodName, + MethodType methodType, + long extraArg, + int... arityArgs) + throws Throwable { + printBsmArgs("bsmWithLongAndIntArray", lookup, methodName, methodType, extraArg, arityArgs); + MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); + return new ConstantCallSite(mh); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestVariableArityLinkerMethod.class, + name = "bsmWithLongAndIntArray", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + long.class, + int[].class + } + ), + fieldOrMethodName = "methodG", + constantArgumentsForBootstrapMethod = { + @Constant(longValue = 0x123456789abcdefl), + @Constant(intValue = +1), + @Constant(intValue = -1), + @Constant(intValue = +2), + @Constant(intValue = -2) + } + ) + private static void methodG() { + System.out.println("methodG"); + } + + private static CallSite bsmWithFloatAndLongArray( + MethodHandles.Lookup lookup, + String methodName, + MethodType methodType, + float extraArg, + long... arityArgs) + throws Throwable { + printBsmArgs( + "bsmWithFloatAndLongArray", lookup, methodName, methodType, extraArg, arityArgs); + MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); + return new ConstantCallSite(mh); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestVariableArityLinkerMethod.class, + name = "bsmWithFloatAndLongArray", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + float.class, + long[].class + } + ), + fieldOrMethodName = "methodH", + constantArgumentsForBootstrapMethod = { + @Constant(floatValue = (float) -Math.E), + @Constant(longValue = 999999999999l), + @Constant(longValue = -8888888888888l) + } + ) + private static void methodH() { + System.out.println("methodH"); + } + + private static CallSite bsmWithClassAndFloatArray( + MethodHandles.Lookup lookup, + String methodName, + MethodType methodType, + Class extraArg, + float... arityArgs) + throws Throwable { + printBsmArgs( + "bsmWithClassAndFloatArray", lookup, methodName, methodType, extraArg, arityArgs); + MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); + return new ConstantCallSite(mh); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestVariableArityLinkerMethod.class, + name = "bsmWithClassAndFloatArray", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + Class.class, + float[].class + } + ), + fieldOrMethodName = "methodI", + constantArgumentsForBootstrapMethod = { + @Constant(classValue = Throwable.class), + @Constant(floatValue = Float.MAX_VALUE), + @Constant(floatValue = Float.MIN_VALUE), + @Constant(floatValue = (float) Math.PI), + @Constant(floatValue = (float) -Math.PI) + } + ) + private static void methodI() { + System.out.println("methodI"); + } + + private static CallSite bsmWithDoubleArray( + MethodHandles.Lookup lookup, + String methodName, + MethodType methodType, + double... arityArgs) + throws Throwable { + printBsmArgs("bsmWithDoubleArray", lookup, methodName, methodType, arityArgs); + MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); + return new ConstantCallSite(mh); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestVariableArityLinkerMethod.class, + name = "bsmWithDoubleArray", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + double[].class + } + ), + fieldOrMethodName = "methodJ", + constantArgumentsForBootstrapMethod = { + @Constant(doubleValue = Double.MAX_VALUE), + @Constant(doubleValue = Double.MIN_VALUE), + @Constant(doubleValue = Math.E), + @Constant(doubleValue = -Math.PI) + } + ) + private static void methodJ() { + System.out.println("methodJ"); + } + + private static CallSite bsmWithClassArray( + MethodHandles.Lookup lookup, + String methodName, + MethodType methodType, + Class... arityArgs) + throws Throwable { + printBsmArgs("bsmWithClassArray", lookup, methodName, methodType, arityArgs); + MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); + return new ConstantCallSite(mh); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestVariableArityLinkerMethod.class, + name = "bsmWithClassArray", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + Class[].class + } + ), + fieldOrMethodName = "methodK", + constantArgumentsForBootstrapMethod = { + @Constant(classValue = Integer.class), + @Constant(classValue = MethodHandles.class), + @Constant(classValue = Arrays.class) + } + ) + private static void methodK() { + System.out.println("methodK"); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestVariableArityLinkerMethod.class, + name = "bsmWithIntAndStringArray", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + int.class, + String[].class + } + ), + fieldOrMethodName = "methodO", + constantArgumentsForBootstrapMethod = {@Constant(intValue = 103), @Constant(intValue = 104)} + ) + private static void methodO() { + // Arguments are not compatible + assertNotReached(); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestVariableArityLinkerMethod.class, + name = "bsmWithIntAndStringArray", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + int.class, + String[].class + } + ), + fieldOrMethodName = "methodP", + constantArgumentsForBootstrapMethod = { + @Constant(intValue = 103), + @Constant(stringValue = "A"), + @Constant(stringValue = "B"), + @Constant(intValue = 42) + } + ) + private static void methodP() { + // Arguments are not compatible - specifically, the third + // component of potential collector array is an integer + // argument (42). + assertNotReached(); + } + + private static CallSite bsmWithWiderArray( + MethodHandles.Lookup lookup, String methodName, MethodType methodType, long[] extraArgs) + throws Throwable { + printBsmArgs("bsmWithWiderArray", lookup, methodName, methodType, extraArgs); + MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); + return new ConstantCallSite(mh); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestVariableArityLinkerMethod.class, + name = "bsmWithWiderArray", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + long[].class + } + ), + fieldOrMethodName = "methodQ", + constantArgumentsForBootstrapMethod = {@Constant(intValue = 103), @Constant(intValue = 42)} + ) + private static void methodQ() { + assertNotReached(); + } + + private static CallSite bsmWithBoxedArray( + MethodHandles.Lookup lookup, + String methodName, + MethodType methodType, + Integer[] extraArgs) + throws Throwable { + printBsmArgs("bsmWithBoxedArray", lookup, methodName, methodType, extraArgs); + MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); + return new ConstantCallSite(mh); + } + + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod( + enclosingType = TestVariableArityLinkerMethod.class, + name = "bsmWithBoxedArray", + parameterTypes = { + MethodHandles.Lookup.class, + String.class, + MethodType.class, + Integer[].class + } + ), + fieldOrMethodName = "methodR", + constantArgumentsForBootstrapMethod = { + @Constant(intValue = 1030), + @Constant(intValue = 420) + } + ) + private static void methodR() { + assertNotReached(); + } + + static void test() { + // Happy cases + for (int i = 0; i < 2; ++i) { + methodA(); + methodB(); + methodC(); + } + for (int i = 0; i < 2; ++i) { + methodD(); + methodE(); + methodF(); + } + methodG(); + methodH(); + methodI(); + methodJ(); + methodK(); + + // Broken cases + try { + // bsm has incompatible static methods. Collector + // component type is String, the corresponding static + // arguments are int values. + methodO(); + assertNotReached(); + } catch (BootstrapMethodError expected) { + System.out.print("methodO => "); + System.out.print(expected.getClass()); + System.out.print(" => "); + System.out.println(expected.getCause().getClass()); + } + try { + // bsm has a trailing String array for the collector array. + // There is an int value amongst the String values. + methodP(); + assertNotReached(); + } catch (BootstrapMethodError expected) { + System.out.print("methodP => "); + System.out.print(expected.getClass()); + System.out.print(" => "); + System.out.println(expected.getCause().getClass()); + } + try { + // bsm has as trailing long[] element for the collector array. + // The corresponding static bsm arguments are of type int. + methodQ(); + assertNotReached(); + } catch (BootstrapMethodError expected) { + System.out.print("methodQ => "); + System.out.print(expected.getClass()); + System.out.print(" => "); + System.out.println(expected.getCause().getClass()); + } + try { + // bsm has as trailing Integer[] element for the collector array. + // The corresponding static bsm arguments are of type int. + methodR(); + assertNotReached(); + } catch (BootstrapMethodError expected) { + System.out.print("methodR => "); + System.out.print(expected.getClass()); + System.out.print(" => "); + System.out.println(expected.getCause().getClass()); + } + } +} diff --git a/test/knownfailures.json b/test/knownfailures.json index 5fb78191ef..b2f579d3b9 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -934,7 +934,6 @@ "946-obsolete-throw", "948-change-annotations", "950-redefine-intrinsic", - "952-invoke-custom", "954-invoke-polymorphic-verifier", "955-methodhandles-smali", "956-methodhandles", -- GitLab From 281fee0dd44a4349fec38fd6f6348ca381c80218 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 5 Mar 2018 12:59:30 -0800 Subject: [PATCH 044/749] ART: Remove static libbase Except for static binaries, do not include a static copy of libbase. Bug: 34867873 Test: mmma art Change-Id: I6ed37b45c01c9dac33c4fc3bb71ee416138c2cb8 --- dexdump/Android.bp | 1 - dexlayout/Android.bp | 1 + libdexfile/Android.bp | 1 - runtime/Android.bp | 1 - 4 files changed, 1 insertion(+), 3 deletions(-) diff --git a/dexdump/Android.bp b/dexdump/Android.bp index eca08448bc..c63d6c319e 100644 --- a/dexdump/Android.bp +++ b/dexdump/Android.bp @@ -46,7 +46,6 @@ art_cc_binary { device_supported: false, static_libs: [ "libdexfile", - "libbase", ] + art_static_dependencies, target: { darwin: { diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp index 24be25bd26..facda11c60 100644 --- a/dexlayout/Android.bp +++ b/dexlayout/Android.bp @@ -113,6 +113,7 @@ art_cc_binary { shared_libs: [ "libart", "libart-dexlayout", + "libbase", ], target: { android: { diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp index ae4ded58e2..988ee03b55 100644 --- a/libdexfile/Android.bp +++ b/libdexfile/Android.bp @@ -40,7 +40,6 @@ cc_defaults { static_libs: [ "libziparchive", "libz", - "libbase", ], shared_libs: [ "libutils", diff --git a/runtime/Android.bp b/runtime/Android.bp index daab2326c0..590a399738 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -351,7 +351,6 @@ cc_defaults { // ZipArchive support, the order matters here to get all symbols. "libziparchive", "libz", - "libbase", ], }, android_arm: { -- GitLab From d432acd28fa5b9f641effff3a4b93218ce851e9b Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Thu, 8 Mar 2018 11:48:27 -0800 Subject: [PATCH 045/749] Remove Python minor version. Test: bisection_test, run_jfuzz_test Change-Id: Idee4e0d4e676de61256f2c5f78b584b96b65461d --- tools/bisection_search/bisection_search.py | 2 +- tools/bisection_search/bisection_test.py | 2 +- tools/common/common.py | 2 +- tools/jfuzz/run_dex_fuzz_test.py | 2 +- tools/jfuzz/run_jfuzz_test.py | 2 +- tools/jfuzz/run_jfuzz_test_nightly.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/bisection_search/bisection_search.py b/tools/bisection_search/bisection_search.py index 27bd599aaa..a1ac72df9f 100755 --- a/tools/bisection_search/bisection_search.py +++ b/tools/bisection_search/bisection_search.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3.4 +#!/usr/bin/env python3 # # Copyright (C) 2016 The Android Open Source Project # diff --git a/tools/bisection_search/bisection_test.py b/tools/bisection_search/bisection_test.py index 9aa08fb5b1..b6a73c0267 100755 --- a/tools/bisection_search/bisection_test.py +++ b/tools/bisection_search/bisection_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3.4 +#!/usr/bin/env python3 # # Copyright (C) 2016 The Android Open Source Project # diff --git a/tools/common/common.py b/tools/common/common.py index b822dcadb7..735bbaa4a4 100755 --- a/tools/common/common.py +++ b/tools/common/common.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3.4 +#!/usr/bin/env python3 # # Copyright (C) 2016 The Android Open Source Project # diff --git a/tools/jfuzz/run_dex_fuzz_test.py b/tools/jfuzz/run_dex_fuzz_test.py index 203e03d678..47fe072b6d 100755 --- a/tools/jfuzz/run_dex_fuzz_test.py +++ b/tools/jfuzz/run_dex_fuzz_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3.4 +#!/usr/bin/env python3 # # Copyright (C) 2016 The Android Open Source Project # diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py index 4a54a3a4f2..3ff9f450a1 100755 --- a/tools/jfuzz/run_jfuzz_test.py +++ b/tools/jfuzz/run_jfuzz_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3.4 +#!/usr/bin/env python3 # # Copyright (C) 2016 The Android Open Source Project # diff --git a/tools/jfuzz/run_jfuzz_test_nightly.py b/tools/jfuzz/run_jfuzz_test_nightly.py index e6c216d1f7..fecf116d8e 100755 --- a/tools/jfuzz/run_jfuzz_test_nightly.py +++ b/tools/jfuzz/run_jfuzz_test_nightly.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3.4 +#!/usr/bin/env python3 # # Copyright (C) 2016 The Android Open Source Project # -- GitLab From 88c68094eec056610208891c237dc074f138b276 Mon Sep 17 00:00:00 2001 From: Igor Murashkin Date: Wed, 7 Mar 2018 17:02:51 -0800 Subject: [PATCH 046/749] test: Cache repeated soong invocations of get_build_var This speeds up testing considerably since every call into soong was taking 10-40s and this had to be done for every run-test to query the javac libcore bootclasspath. Now it should only call into soong dumpvars once total. Also speeds up 674-hiddenapi testrunner time by 2x. Bug: 74196452 Test: time art/test/testrunner/testrunner.py --host --jit --64 -t 674-hiddenapi Test: art/test/testrunner/testrunner.py --host Test: # manual check soong isnt taking all the CPU while running above. Test: art/test/run-test --host 004-JniTest # or any test really. Test: art/test/testrunner/run_build_test_target.py art-test Change-Id: I32f9247db76cfd61993b8f6ea1f0fffa1322a2c5 --- test/testrunner/env.py | 59 ++----- test/testrunner/run_build_test_target.py | 16 +- tools/bootjars.sh | 2 +- tools/build/var_cache.py | 148 +++++++++++++++++ tools/build/var_cache.sh | 195 +++++++++++++++++++++++ tools/build/var_list | 38 +++++ 6 files changed, 401 insertions(+), 57 deletions(-) create mode 100644 tools/build/var_cache.py create mode 100755 tools/build/var_cache.sh create mode 100644 tools/build/var_list diff --git a/test/testrunner/env.py b/test/testrunner/env.py index 55569629ea..70efce51ee 100644 --- a/test/testrunner/env.py +++ b/test/testrunner/env.py @@ -17,6 +17,16 @@ import re import tempfile import subprocess +# begin import $ANDROID_BUILD_TOP/art/tools/build/var_cache.py +_THIS_DIR = os.path.dirname(os.path.realpath(__file__)) +_TOP = os.path.join(_THIS_DIR, "../../..") +_VAR_CACHE_DIR = os.path.join(_TOP, "art/tools/build/") + +import sys +sys.path.append(_VAR_CACHE_DIR) +import var_cache +# end import var_cache.py + _env = dict(os.environ) def _getEnvBoolean(var, default): @@ -28,55 +38,8 @@ def _getEnvBoolean(var, default): return False return default -_DUMP_MANY_VARS_LIST = ['HOST_2ND_ARCH_PREFIX', - 'TARGET_2ND_ARCH', - 'TARGET_ARCH', - 'HOST_PREFER_32_BIT', - 'HOST_OUT_EXECUTABLES', - 'ANDROID_JAVA_TOOLCHAIN', - 'ANDROID_COMPILE_WITH_JACK', - 'USE_D8_BY_DEFAULT'] -_DUMP_MANY_VARS = None # To be set to a dictionary with above list being the keys, - # and the build variable being the value. -def _dump_many_vars(var_name): - """ - Reach into the Android build system to dump many build vars simultaneously. - Since the make system is so slow, we want to avoid calling into build frequently. - """ - global _DUMP_MANY_VARS - global _DUMP_MANY_VARS_LIST - - # Look up var from cache. - if _DUMP_MANY_VARS: - return _DUMP_MANY_VARS[var_name] - - all_vars=" ".join(_DUMP_MANY_VARS_LIST) - - # The command is taken from build/envsetup.sh to fetch build variables. - command = ("build/soong/soong_ui.bash --dumpvars-mode --vars=\"%s\"") % (all_vars) - - config = subprocess.Popen(command, - stdout=subprocess.PIPE, - universal_newlines=True, - shell=True, - cwd=ANDROID_BUILD_TOP).communicate()[0] # read until EOF, select stdin - # Prints out something like: - # TARGET_ARCH='arm64' - # HOST_ARCH='x86_64' - _DUMP_MANY_VARS = {} - for line in config.split("\n"): - # Split out "$key='$value'" via regex. - match = re.search("([^=]+)='([^']*)", line) - if not match: - continue - key = match.group(1) - value = match.group(2) - _DUMP_MANY_VARS[key] = value - - return _DUMP_MANY_VARS[var_name] - def _get_build_var(var_name): - return _dump_many_vars(var_name) + return var_cache.get_build_var(var_name) def _get_build_var_boolean(var, default): val = _get_build_var(var) diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py index fcc5505a95..e0ccc3e14c 100755 --- a/test/testrunner/run_build_test_target.py +++ b/test/testrunner/run_build_test_target.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright 2017, The Android Open Source Project # @@ -45,9 +45,9 @@ options = parser.parse_args() ########## if options.list: - print "List of all known build_target: " - for k in sorted(target_config.iterkeys()): - print " * " + k + print("List of all known build_target: ") + for k in sorted(target_config.keys()): + print(" * " + k) # TODO: would be nice if this was the same order as the target config file. sys.exit(1) @@ -59,10 +59,10 @@ target = target_config[options.build_target] n_threads = options.n_threads custom_env = target.get('env', {}) custom_env['SOONG_ALLOW_MISSING_DEPENDENCIES'] = 'true' -print custom_env +print(custom_env) os.environ.update(custom_env) -if target.has_key('make'): +if 'make' in target: build_command = 'make' build_command += ' DX=' build_command += ' -j' + str(n_threads) @@ -75,7 +75,7 @@ if target.has_key('make'): if subprocess.call(build_command.split()): sys.exit(1) -if target.has_key('golem'): +if 'golem' in target: machine_type = target.get('golem') # use art-opt-cc by default since it mimics the default preopt config. default_golem_config = 'art-opt-cc' @@ -93,7 +93,7 @@ if target.has_key('golem'): if subprocess.call(cmd): sys.exit(1) -if target.has_key('run-test'): +if 'run-test' in target: run_test_command = [os.path.join(env.ANDROID_BUILD_TOP, 'art/test/testrunner/testrunner.py')] test_flags = target.get('run-test', []) diff --git a/tools/bootjars.sh b/tools/bootjars.sh index f710de99bb..dca209d580 100755 --- a/tools/bootjars.sh +++ b/tools/bootjars.sh @@ -21,7 +21,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" TOP="$DIR/../.." -source "${TOP}/build/envsetup.sh" >&/dev/null # import get_build_var +source "${TOP}/art/tools/build/var_cache.sh" >&/dev/null # import get_build_var selected_env_var= core_jars_only=n diff --git a/tools/build/var_cache.py b/tools/build/var_cache.py new file mode 100644 index 0000000000..9e616faafc --- /dev/null +++ b/tools/build/var_cache.py @@ -0,0 +1,148 @@ +# 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 +# +# 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. + +# +# !!! Keep up-to-date with var_cache.sh +# + +# +# Provide a soong-build variable query mechanism that is cached +# in the current process and any other subchild process that knows +# how to parse the exported variable: +# +# export ART_TOOLS_BUILD_VAR_CACHE="..." +# +# Of the format: +# +# =''\n +# =''\n +# ... +# ='' +# +# Note: This is intentionally the same output format as +# build/soong/soong_ui.bash --dumpvars-mode --vars "key1 key2 ... keyN" +# +# For example, this would be a valid var-cache: +# +# export ART_TOOLS_BUILD_VAR_CACHE="TARGET_CORE_JARS='core-oj core-libart' +# HOST_CORE_JARS='core-oj-hostdex core-libart-hostdex'" +# +# Calling into soong repeatedly is very slow; whenever it needs to be done +# more than once, the var_cache.py or var_cache.sh script should be used instead. +# + +import os +import subprocess +import sys + +def get_build_var(name): + """ + Query soong build for a variable value and return it as a string. + + Var lookup is cached, subsequent var lookups in any child process + (that includes a var-cache is free). The var must be in 'var_list' + to participate in the cache. + + Example: + host_out = var_cache.get_build_var('HOST_OUT') + + Note that build vars can often have spaces in them, + so the caller must take care to ensure space-correctness. + + Raises KeyError if the variable name is not in 'var_list'. + """ + _populate() + _build_dict() + + value = _var_cache_dict.get(name) + if value is None: + _debug(_var_cache_dict) + raise KeyError("The variable '%s' is not in 'var_list', can't lookup" %(name)) + + return value + +_var_cache_dict = None +_THIS_DIR = os.path.dirname(os.path.realpath(__file__)) +_TOP = os.path.join(_THIS_DIR, "../../..") +_VAR_LIST_PATH = os.path.join(_THIS_DIR, "var_list") +_SOONG_UI_SCRIPT = os.path.join(_TOP, "build/soong/soong_ui.bash") +_DEBUG = False + +def _populate(): + if os.environ.get('ART_TOOLS_BUILD_VAR_CACHE'): + return + + _debug("Varcache missing (PY)... repopulate") + + interesting_vars=[] + with open(_VAR_LIST_PATH) as f: + for line in f.readlines(): + line = line.strip() + if not line or line.startswith('#'): + continue + + _debug(line) + + interesting_vars.append(line) + + _debug("Interesting vars: ", interesting_vars) + + # Invoke soong exactly once for optimal performance. + var_values = subprocess.check_output([ + _SOONG_UI_SCRIPT, '--dumpvars-mode', '-vars', " ".join(interesting_vars)], + cwd=_TOP) + + # Export the ART_TOOLS_BUILD_VAR_CACHE in the same format as soong_ui.bash --dumpvars-mode. + os.environb[b'ART_TOOLS_BUILD_VAR_CACHE'] = var_values + + _debug("Soong output: ", var_values) + +def _build_dict(): + global _var_cache_dict + + if _var_cache_dict: + return + + _debug("_var_cache_build_dict()") + + _var_cache_dict = {} + + # Parse $ART_TOOLS_BUILD_VAR_CACHE, e.g. + # TARGET_CORE_JARS='core-oj core-libart conscrypt okhttp bouncycastle apache-xml' + # HOST_CORE_JARS='core-oj-hostdex core-libart-hostdex ...' + + for line in os.environ['ART_TOOLS_BUILD_VAR_CACHE'].splitlines(): + _debug(line) + var_name, var_value = line.split("=") + var_value = var_value.strip("'") + + _debug("Var name =", var_name) + _debug("Var value =", var_value) + + _var_cache_dict[var_name] = var_value + + _debug("Num entries in dict: ", len(_var_cache_dict)) + +def _debug(*args): + if _DEBUG: + print(*args, file=sys.stderr) + +# Below definitions are for interactive use only, e.g. +# python -c 'import var_cache; var_cache._dump_cache()' + +def _dump_cache(): + _populate() + print(os.environ['ART_TOOLS_BUILD_VAR_CACHE']) + +#get_build_var("xyz") diff --git a/tools/build/var_cache.sh b/tools/build/var_cache.sh new file mode 100755 index 0000000000..26e9770f95 --- /dev/null +++ b/tools/build/var_cache.sh @@ -0,0 +1,195 @@ +#!/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. + +# +# !!! Keep up-to-date with var_cache.py +# + +# +# Provide a soong-build variable query mechanism that is cached +# in the current process and any other subchild process that knows +# how to parse the exported variable: +# +# export ART_TOOLS_BUILD_VAR_CACHE="..." +# +# Of the format: +# +# =''\n +# =''\n +# ... +# ='' +# +# Note: This is intentionally the same output format as +# build/soong/soong_ui.bash --dumpvars-mode --vars "key1 key2 ... keyN" +# +# For example, this would be a valid var-cache: +# +# export ART_TOOLS_BUILD_VAR_CACHE="TARGET_CORE_JARS='core-oj core-libart' +# HOST_CORE_JARS='core-oj-hostdex core-libart-hostdex'" +# +# Calling into soong repeatedly is very slow; whenever it needs to be done +# more than once, the var_cache.py or var_cache.sh script should be used instead. +# + +# ------------------------------------------------------- + +# Echoes the result of get_build_var . +# Var lookup is cached, subsequent var lookups in any child process +# (that includes a var-cache is free). The var must be in 'var_list' +# to participate in the cache. +# +# Example: +# local host_out="$(get_build_var HOST_OUT)" +# +# Note that build vars can often have spaces in them, +# so the caller must take care to ensure space-correctness. +get_build_var() { + local var_name="$1" + + _var_cache_populate + _var_cache_build_dict + + if [[ ${_VAR_CACHE_DICT[$var_name]+exists} ]]; then + echo "${_VAR_CACHE_DICT[$var_name]}" + return 0 + else + echo "[ERROR] get_build_var: The variable '$var_name' is not in 'var_list', can't lookup." >&2 + return 1 + fi +} + +# The above functions are "public" and are intentionally not exported. +# User scripts must have "source var_cache.sh" to take advantage of caching. + +# ------------------------------------------------------- +# Below functions are "private"; +# do not call them outside of this file. + +_var_cache_populate() { + if [[ -n $ART_TOOLS_BUILD_VAR_CACHE ]]; then + _var_cache_debug "ART_TOOLS_BUILD_VAR_CACHE preset to (quotes added)..." + _var_cache_debug \""$ART_TOOLS_BUILD_VAR_CACHE"\" + return 0 + fi + + _var_cache_debug "Varcache missing... repopulate" + + local this_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + local top="$this_dir/../../.." + + local interesting_vars=() + while read -r line; do + if [[ -z $line ]] || [[ $line == '#'* ]]; then + continue; + fi + interesting_vars+=($line) + done < "$this_dir"/var_list + + _var_cache_debug "Interesting vars: " ${interesting_vars[@]} + + local flat_vars="${interesting_vars[*]}" + + local var_values + _var_cache_show_command "$top"/build/soong/soong_ui.bash --dumpvars-mode -vars=\"${interesting_vars[*]}\" + + # Invoke soong exactly once for optimal performance. + # soong_ui.bash must be invoked from $ANDROID_BUILD_TOP or it gets confused and breaks. + var_values="$(cd "$top" && "$top"/build/soong/soong_ui.bash --dumpvars-mode -vars="$flat_vars")" + + # Export the ART_TOOLS_BUILD_VAR_CACHE in the same format as soong_ui.bash --dumpvars-mode. + export ART_TOOLS_BUILD_VAR_CACHE="$var_values" + + _var_cache_debug ART_TOOLS_BUILD_VAR_CACHE=\"$var_values\" +} + +_var_cache_build_dict() { + if [[ ${#_VAR_CACHE_DICT[@]} -ne 0 ]]; then + # Associative arrays cannot be exported, have + # a separate step to reconstruct the associative + # array from a flat variable. + return 0 + fi + + # Parse $ART_TOOLS_BUILD_VAR_CACHE, e.g. + # TARGET_CORE_JARS='core-oj core-libart conscrypt okhttp bouncycastle apache-xml' + # HOST_CORE_JARS='core-oj-hostdex core-libart-hostdex ...' + + local var_name + local var_value + local strip_quotes + + _var_cache_debug "_var_cache_build_dict()" + + declare -g -A _VAR_CACHE_DICT # global associative array. + while IFS='=' read -r var_name var_value; do + if [[ -z $var_name ]]; then + # skip empty lines, e.g. blank newline at the end + continue + fi + _var_cache_debug "Var_name was $var_name" + _var_cache_debug "Var_value was $var_value" + strip_quotes=${var_value//\'/} + _VAR_CACHE_DICT["$var_name"]="$strip_quotes" + done < <(echo "$ART_TOOLS_BUILD_VAR_CACHE") + + _var_cache_debug ${#_VAR_CACHE_DICT[@]} -eq 0 +} + +_var_cache_debug() { + if ((_var_cache_debug_enabled)); then + echo "[DBG]: " "$@" >&2 + fi +} + +_var_cache_show_command() { + if (( _var_cache_show_commands || _var_cache_debug_enabled)); then + echo "$@" >&2 + fi +} + +while true; do + case $1 in + --help) + echo "Usage: $0 [--debug] [--show-commands] [--dump-cache] [--var ] [--var ...]" + echo "" + echo "Exposes a function 'get_build_var' which returns the result of" + echo "a soong build variable." + echo "" + echo "Primarily intended to be used as 'source var_cache.sh'," + echo "but also allows interactive command line usage for simplifying development." + exit 0 + ;; + --var) + echo -ne "$2=" + get_build_var "$2" + shift + ;; + --debug) + _var_cache_debug_enabled=1 + ;; + --show-commands) + _var_cache_show_commands=1 + ;; + --dump-cache) + _var_cache_populate + echo "ART_TOOLS_BUILD_VAR_CACHE=\"$ART_TOOLS_BUILD_VAR_CACHE\"" + ;; + *) + break + ;; + esac + shift +done diff --git a/tools/build/var_list b/tools/build/var_list new file mode 100644 index 0000000000..3727741dac --- /dev/null +++ b/tools/build/var_list @@ -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. + +# +# This file contains a list of all the build vars that need to be eagerly cached +# by the var_cache.sh and var_cache.py scripts. +# Lines starting with '#' or blank lines are ignored. +# + +# javac-helper.sh +TARGET_CORE_JARS +PRODUCT_BOOT_JARS +TARGET_OUT_COMMON_INTERMEDIATES +HOST_CORE_JARS +HOST_OUT_COMMON_INTERMEDIATES + +# testrunner/env.py +HOST_2ND_ARCH_PREFIX +TARGET_2ND_ARCH +TARGET_ARCH +HOST_PREFER_32_BIT +HOST_OUT_EXECUTABLES +ANDROID_JAVA_TOOLCHAIN +ANDROID_COMPILE_WITH_JACK +USE_D8_BY_DEFAULT + -- GitLab From 230fa94bb87e6414376aa828bd47068c684ed57f Mon Sep 17 00:00:00 2001 From: Alex Light Date: Thu, 8 Mar 2018 14:07:02 -0800 Subject: [PATCH 047/749] Make LOG(INFO) a VLOG(jdwp) This log is often printed during cts tests where it is not useful. Test: build Bug: 62821960 Change-Id: I16c349152215fd41266725870e6ff39819fb03d4 --- runtime/runtime.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 2074f1e5f5..adc3b57c3c 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1275,7 +1275,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { jdwp_provider_ = runtime_options.GetOrDefault(Opt::JdwpProvider); switch (jdwp_provider_) { case JdwpProvider::kNone: { - LOG(INFO) << "Disabling all JDWP support."; + VLOG(jdwp) << "Disabling all JDWP support."; if (!jdwp_options_.empty()) { bool has_transport = jdwp_options_.find("transport") != std::string::npos; const char* transport_internal = !has_transport ? "transport=dt_android_adb," : ""; -- GitLab From e9278663e0d6e6a0d2b0fd99f6d9f28b58773669 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Thu, 8 Mar 2018 16:55:58 -0800 Subject: [PATCH 048/749] Revert "Correctly handle instrumenting threads multiple times." This CL is incorrect in multiple ways. First it is way too strict with the checks about where additional methods can be. More importantly we cannot bail-out early without breaking the instrumentation stack. This is linked with the next CL that fixes this correctly. This reverts commit 055407c8c0d7658d53fef595dec8ec8095797992. Test: build. Bug: 67384421 Bug: 74386428 Change-Id: I745a8e88a5789f68794faaa705c82ed81b8fb29f --- runtime/instrumentation.cc | 46 +++++++++++++------------------------- test/knownfailures.json | 6 +++++ 2 files changed, 21 insertions(+), 31 deletions(-) diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 7ddd17329c..b0795982eb 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -209,9 +209,7 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) : StackVisitor(thread_in, context, kInstrumentationStackWalk), instrumentation_stack_(thread_in->GetInstrumentationStack()), instrumentation_exit_pc_(instrumentation_exit_pc), - reached_existing_instrumentation_frames_(false), - should_be_at_top_(false), - instrumentation_stack_depth_(0), + reached_existing_instrumentation_frames_(false), instrumentation_stack_depth_(0), last_return_pc_(0) { } @@ -235,20 +233,6 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) return true; // Continue. } uintptr_t return_pc = GetReturnPc(); - if (UNLIKELY(should_be_at_top_)) { - std::string thread_name; - GetThread()->GetThreadName(thread_name); - uint32_t dex_pc = dex::kDexNoIndex; - if (last_return_pc_ != 0 && - GetCurrentOatQuickMethodHeader() != nullptr) { - dex_pc = GetCurrentOatQuickMethodHeader()->ToDexPc(m, last_return_pc_); - } - LOG(FATAL) << "While walking " << thread_name << " Reached unexpected frame above what " - << "should have been top. Method is " << GetMethod()->PrettyMethod() - << " return_pc: " << std::hex << return_pc - << " dex pc: " << dex_pc; - UNREACHABLE(); - } if (kVerboseInstrumentation) { LOG(INFO) << " Installing exit stub in " << DescribeLocation(); } @@ -283,21 +267,22 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) if (kVerboseInstrumentation) { LOG(INFO) << "Ignoring already instrumented " << frame.Dump(); } - } else if (UNLIKELY(reached_existing_instrumentation_frames_)) { - // If tracing was enabled we might have had all methods have the instrumentation frame - // except the runtime transition method at the very top of the stack. This isn't really a - // problem since the transition method just goes back into the runtime and never leaves it - // so it can be ignored. - should_be_at_top_ = true; - DCHECK(m->IsRuntimeMethod()) << "Expected method to be runtime method at start of thread " - << "but was " << m->PrettyMethod(); - if (kVerboseInstrumentation) { - LOG(INFO) << "reached expected top frame " << m->PrettyMethod(); - } - // Don't bother continuing on the upcalls on non-debug builds. - return kIsDebugBuild ? true : false; } else { CHECK_NE(return_pc, 0U); + if (UNLIKELY(reached_existing_instrumentation_frames_)) { + std::string thread_name; + GetThread()->GetThreadName(thread_name); + uint32_t dex_pc = dex::kDexNoIndex; + if (last_return_pc_ != 0 && + GetCurrentOatQuickMethodHeader() != nullptr) { + dex_pc = GetCurrentOatQuickMethodHeader()->ToDexPc(m, last_return_pc_); + } + LOG(FATAL) << "While walking " << thread_name << " found existing instrumentation frames." + << " method is " << GetMethod()->PrettyMethod() + << " return_pc is " << std::hex << return_pc + << " dex pc: " << dex_pc; + UNREACHABLE(); + } InstrumentationStackFrame instrumentation_frame( m->IsRuntimeMethod() ? nullptr : GetThisObject(), m, @@ -335,7 +320,6 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) std::vector dex_pcs_; const uintptr_t instrumentation_exit_pc_; bool reached_existing_instrumentation_frames_; - bool should_be_at_top_; size_t instrumentation_stack_depth_; uintptr_t last_return_pc_; }; diff --git a/test/knownfailures.json b/test/knownfailures.json index 5fb78191ef..70bb42c3ce 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -1,4 +1,10 @@ [ + { + "tests": "1934-jvmti-signal-thread", + "description": ["Disables 1934-jvmti-signal-thread in tracing configurations"], + "variant": "trace | stream", + "bug": "http://b/67384421" + }, { "tests": "153-reference-stress", "description": ["Disable 153-reference-stress temporarily until a fix", -- GitLab From 74c91c9e90ca502a88240b3d14aed4dd818eedc2 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Thu, 8 Mar 2018 14:01:44 -0800 Subject: [PATCH 049/749] Fix instrumentation stack tracking We were incorrectly tracking the instrumentation stack, bailing out of installation early. This could lead to missing instrumentation events, failing CHECKs or (potentially) corrupt thread stacks. Test: ./test.py --host --trace --ntrace --stream -j50 Test: Use gapid. Bug: 67384421 Bug: 74386428 Change-Id: I91686ac4f44550d89b9d7a5f716df88f505d91b1 --- runtime/instrumentation.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index b0795982eb..84a148f21c 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -256,7 +256,7 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) } // We've reached a frame which has already been installed with instrumentation exit stub. - // We should have already installed instrumentation on previous frames. + // We should have already installed instrumentation or be interpreter on previous frames. reached_existing_instrumentation_frames_ = true; const InstrumentationStackFrame& frame = @@ -269,7 +269,9 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) } } else { CHECK_NE(return_pc, 0U); - if (UNLIKELY(reached_existing_instrumentation_frames_)) { + if (UNLIKELY(reached_existing_instrumentation_frames_ && !m->IsRuntimeMethod())) { + // We already saw an existing instrumentation frame so this should be a runtime-method + // inserted by the interpreter or runtime. std::string thread_name; GetThread()->GetThreadName(thread_name); uint32_t dex_pc = dex::kDexNoIndex; @@ -277,7 +279,8 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) GetCurrentOatQuickMethodHeader() != nullptr) { dex_pc = GetCurrentOatQuickMethodHeader()->ToDexPc(m, last_return_pc_); } - LOG(FATAL) << "While walking " << thread_name << " found existing instrumentation frames." + LOG(FATAL) << "While walking " << thread_name << " found unexpected non-runtime method" + << " without instrumentation exit return or interpreter frame." << " method is " << GetMethod()->PrettyMethod() << " return_pc is " << std::hex << return_pc << " dex pc: " << dex_pc; -- GitLab From 5ebc4cc1489a1992fbee969e05478a474c2ec111 Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Thu, 8 Mar 2018 09:47:46 +0000 Subject: [PATCH 050/749] Quick script to get API access violations. Test: $ adb logcat | perl find_api_violations.pl $ perl find_api_violations.pl --help DESCRIPTION This script parses API violations from "adb logcat". Output is in CSV format with columns "package", "symbol", "list". The package name is mapped from a PID, parsed from the same log. To ensure you get all packages names, you should process the logcat from device boot time. SYNOPSIS adb logcat | perl find_api_violations.pl > violations.csv cat bugreport.txt | perl find_api_violations.pl --bugreport > violations.csv OPTIONS --[no]lightgrey (Don't) show light grey list accesses (default true) --[no]darkgrey (Don't) show dark grey list accesses (default true) --[no]black (Don't) show black list accesses (default true) --bugreport|-b Process a bugreport, rather than raw logcat --help Bug: 64382372 Change-Id: Id4baf10412b016a4bfd80bd3bd25cf9ca437601c --- tools/hiddenapi/find_api_violations.pl | 124 +++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100755 tools/hiddenapi/find_api_violations.pl diff --git a/tools/hiddenapi/find_api_violations.pl b/tools/hiddenapi/find_api_violations.pl new file mode 100755 index 0000000000..a022999ea5 --- /dev/null +++ b/tools/hiddenapi/find_api_violations.pl @@ -0,0 +1,124 @@ +#!/usr/bin/perl +# 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. + +use strict; +use Getopt::Long; +use Pod::Usage; + +=pod + +=head1 DESCRIPTION + +This script parses API violations from C. Output is in CSV format +with columns C, C, C. + +The package name is mapped from a PID, parsed from the same log. To ensure you +get all packages names, you should process the logcat from device boot time. + +=head1 SYNOPSIS + + adb logcat | perl find_api_violations.pl > violations.csv + cat bugreport.txt | perl find_api_violations.pl --bugreport > violations.csv + +=head1 OPTIONS + +=over + +=item --[no]lightgrey + +(Don't) show light grey list accesses (default true) + +=item --[no]darkgrey + +(Don't) show dark grey list accesses (default true) + +=item --[no]black + +(Don't) show black list accesses (default true) + +=item --bugreport|-b + +Process a bugreport, rather than raw logcat + +=item --help + +=back + +=cut + +my $lightgrey = 1; +my $darkgrey = 1; +my $black = 1; +my $bugreport = 0; +my $help = 0; + +GetOptions("lightgrey!" => \$lightgrey, + "darkgrey!" => \$darkgrey, + "black!" => \$black, + "bugreport|b" => \$bugreport, + "help" => \$help) + or pod2usage(q(-verbose) => 1); + +pod2usage(q(-verbose) => 2) if ($help); + +my $in; + +if ($bugreport) { + my $found_main = 0; + while (my $line = <>) { + chomp $line; + if ($line =~ m/^------ SYSTEM LOG /) { + $found_main = 1; + last; + } + } + if (!$found_main) { + die "Couldn't find main log in bugreport\n"; + } +} + +my $procmap = {}; +print "package,symbol,list\n"; +while (my $line = <>) { + chomp $line; + last if $bugreport and $line =~ m/^------ \d+\.\d+s was the duration of 'SYSTEM LOG' ------/; + next if $line =~ m/^--------- beginning of/; + unless ($line =~ m/^\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3}\s+(?:\w+\s+)?(\d+)\s+(\d+)\s+([VWIDE])\s+(.*?): (.*)$/) { + die "Cannot match line: $line\n"; + next; + } + my ($pid, $tid, $class, $tag, $msg) = ($1, $2, $3, $4, $5); + if ($tag eq "ActivityManager" && $msg =~ m/^Start proc (\d+):(.*?) for /) { + my ($new_pid, $proc_name) = ($1, $2); + my $package; + if ($proc_name =~ m/^(.*?)(:.*?)?\/(.*)$/) { + $package = $1; + } else { + $package = $proc_name; + } + $procmap->{$new_pid} = $package; + } + if ($tag eq "zygote" || $tag eq "zygote64") { + if ($msg =~ m/Accessing hidden (\w+) (L.*?) \((.*list), (.*?)\)/) { + my ($member_type, $symbol, $list, $access_type) = ($1, $2, $3, $4); + my $package = $procmap->{$pid} || "unknown($pid)"; + print "$package,$symbol,$list\n" + if (($list =~ m/light/ && $lightgrey) + || ($list =~ m/dark/ && $darkgrey) + || ($list =~ m/black/ && $black)); + } + } +} + -- GitLab From 62c2d71b583820685761519bbfe3ce1e79358e25 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 9 Mar 2018 12:54:05 +0000 Subject: [PATCH 051/749] Fix ReplaceFileExtension() to stop looking for '.' at '/'. Test: new test in file_utils_test. Test: m test-art-host-gtest Change-Id: Iaf9d16c8595f4f88a5920549c9cee51ecd4f9d13 --- runtime/base/file_utils.cc | 4 ++-- runtime/base/file_utils_test.cc | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc index f9d0d12d88..1cb3b9c380 100644 --- a/runtime/base/file_utils.cc +++ b/runtime/base/file_utils.cc @@ -306,8 +306,8 @@ std::string GetSystemImageFilename(const char* location, const InstructionSet is } std::string ReplaceFileExtension(const std::string& filename, const std::string& new_extension) { - const size_t last_ext = filename.find_last_of('.'); - if (last_ext == std::string::npos) { + const size_t last_ext = filename.find_last_of("./"); + if (last_ext == std::string::npos || filename[last_ext] != '.') { return filename + "." + new_extension; } else { return filename.substr(0, last_ext + 1) + new_extension; diff --git a/runtime/base/file_utils_test.cc b/runtime/base/file_utils_test.cc index cf6e34d1ea..e74dfe5e64 100644 --- a/runtime/base/file_utils_test.cc +++ b/runtime/base/file_utils_test.cc @@ -94,4 +94,11 @@ TEST_F(FileUtilsTest, GetAndroidRootSafe) { ASSERT_EQ(0, setenv("ANDROID_ROOT", android_root_env.c_str(), 1 /* overwrite */)); } +TEST_F(FileUtilsTest, ReplaceFileExtension) { + EXPECT_EQ("/directory/file.vdex", ReplaceFileExtension("/directory/file.oat", "vdex")); + EXPECT_EQ("/.directory/file.vdex", ReplaceFileExtension("/.directory/file.oat", "vdex")); + EXPECT_EQ("/directory/file.vdex", ReplaceFileExtension("/directory/file", "vdex")); + EXPECT_EQ("/.directory/file.vdex", ReplaceFileExtension("/.directory/file", "vdex")); +} + } // namespace art -- GitLab From f0499c9007d6d74963a10d44e9b1494f7c49febb Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 9 Mar 2018 12:42:09 +0000 Subject: [PATCH 052/749] ART: Fix oat_writer_test. Use oat file names with .oat extension, so that ReplaceFileExtension() used for opening the associated vdex file does not replace everything starting from the .temp folder. Test: m test-art-host-gtest-oat_writer_test Change-Id: I2fa9b3d2cca5054568777d7c05ce03f6eb1131b2 --- dex2oat/linker/oat_writer_test.cc | 60 ++++++++++++++++++------------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index f713ed6faa..06d264eee7 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -414,7 +414,7 @@ TEST_F(OatTest, WriteRead) { compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings2); } - ScratchFile tmp_oat, tmp_vdex(tmp_oat, ".vdex"); + ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex"); SafeMap key_value_store; key_value_store.Put(OatHeader::kImageLocationKey, "lue.art"); bool success = WriteElf(tmp_vdex.GetFile(), @@ -551,10 +551,14 @@ TEST_F(OatTest, EmptyTextSection) { compiler_driver_->SetDexFilesForOatFile(dex_files); compiler_driver_->CompileAll(class_loader, dex_files, &timings); - ScratchFile tmp_oat, tmp_vdex(tmp_oat, ".vdex"); + ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex"); SafeMap key_value_store; key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); - bool success = WriteElf(tmp_vdex.GetFile(), tmp_oat.GetFile(), dex_files, key_value_store, false); + bool success = WriteElf(tmp_vdex.GetFile(), + tmp_oat.GetFile(), + dex_files, + key_value_store, + /* verify */ false); ASSERT_TRUE(success); std::unique_ptr oat_file(OatFile::Open(tmp_oat.GetFilename(), @@ -613,13 +617,13 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) { ASSERT_TRUE(success); input_filenames.push_back(dex_file2.GetFilename().c_str()); - ScratchFile oat_file, vdex_file(oat_file, ".vdex"); + ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex"); SafeMap key_value_store; key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); std::unique_ptr profile_compilation_info(use_profile ? new ProfileCompilationInfo() : nullptr); - success = WriteElf(vdex_file.GetFile(), - oat_file.GetFile(), + success = WriteElf(tmp_vdex.GetFile(), + tmp_oat.GetFile(), input_filenames, key_value_store, verify, @@ -634,19 +638,19 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) { ASSERT_TRUE(success); std::string error_msg; - std::unique_ptr opened_oat_file(OatFile::Open(oat_file.GetFilename(), - oat_file.GetFilename(), + std::unique_ptr opened_oat_file(OatFile::Open(tmp_oat.GetFilename(), + tmp_oat.GetFilename(), nullptr, nullptr, false, low_4gb, nullptr, &error_msg)); + ASSERT_TRUE(opened_oat_file != nullptr) << error_msg; if (low_4gb) { uintptr_t begin = reinterpret_cast(opened_oat_file->Begin()); EXPECT_EQ(begin, static_cast(begin)); } - ASSERT_TRUE(opened_oat_file != nullptr); ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size()); std::unique_ptr opened_dex_file1 = opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg); @@ -677,7 +681,7 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) { ASSERT_EQ(vdex_header.GetQuickeningInfoSize(), 0u); } - int64_t actual_vdex_size = vdex_file.GetFile()->GetLength(); + int64_t actual_vdex_size = tmp_vdex.GetFile()->GetLength(); ASSERT_GE(actual_vdex_size, 0); ASSERT_EQ((uint64_t) actual_vdex_size, vdex_header.GetComputedFileSize()); } @@ -749,9 +753,13 @@ void OatTest::TestZipFileInput(bool verify) { // Test using the AddDexFileSource() interface with the zip file. std::vector input_filenames = { zip_file.GetFilename().c_str() }; - ScratchFile oat_file, vdex_file(oat_file, ".vdex"); - success = WriteElf(vdex_file.GetFile(), oat_file.GetFile(), input_filenames, - key_value_store, verify, /*profile_compilation_info*/nullptr); + ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex"); + success = WriteElf(tmp_vdex.GetFile(), + tmp_oat.GetFile(), + input_filenames, + key_value_store, + verify, + /* profile_compilation_info */ nullptr); if (verify) { ASSERT_FALSE(success); @@ -759,15 +767,15 @@ void OatTest::TestZipFileInput(bool verify) { ASSERT_TRUE(success); std::string error_msg; - std::unique_ptr opened_oat_file(OatFile::Open(oat_file.GetFilename(), - oat_file.GetFilename(), + std::unique_ptr opened_oat_file(OatFile::Open(tmp_oat.GetFilename(), + tmp_oat.GetFilename(), nullptr, nullptr, false, /*low_4gb*/false, nullptr, &error_msg)); - ASSERT_TRUE(opened_oat_file != nullptr); + ASSERT_TRUE(opened_oat_file != nullptr) << error_msg; ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size()); std::unique_ptr opened_dex_file1 = opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg); @@ -795,9 +803,9 @@ void OatTest::TestZipFileInput(bool verify) { File zip_fd(dup(zip_file.GetFd()), /* check_usage */ false); ASSERT_NE(-1, zip_fd.Fd()); - ScratchFile oat_file, vdex_file(oat_file, ".vdex"); - success = WriteElf(vdex_file.GetFile(), - oat_file.GetFile(), + ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex"); + success = WriteElf(tmp_vdex.GetFile(), + tmp_oat.GetFile(), std::move(zip_fd), zip_file.GetFilename().c_str(), key_value_store, @@ -808,15 +816,15 @@ void OatTest::TestZipFileInput(bool verify) { ASSERT_TRUE(success); std::string error_msg; - std::unique_ptr opened_oat_file(OatFile::Open(oat_file.GetFilename(), - oat_file.GetFilename(), + std::unique_ptr opened_oat_file(OatFile::Open(tmp_oat.GetFilename(), + tmp_oat.GetFilename(), nullptr, nullptr, false, /*low_4gb*/false, nullptr, &error_msg)); - ASSERT_TRUE(opened_oat_file != nullptr); + ASSERT_TRUE(opened_oat_file != nullptr) << error_msg; ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size()); std::unique_ptr opened_dex_file1 = opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg); @@ -861,8 +869,12 @@ void OatTest::TestZipFileInputWithEmptyDex() { std::vector input_filenames = { zip_file.GetFilename().c_str() }; ScratchFile oat_file, vdex_file(oat_file, ".vdex"); std::unique_ptr profile_compilation_info(new ProfileCompilationInfo()); - success = WriteElf(vdex_file.GetFile(), oat_file.GetFile(), input_filenames, - key_value_store, /*verify*/false, profile_compilation_info.get()); + success = WriteElf(vdex_file.GetFile(), + oat_file.GetFile(), + input_filenames, + key_value_store, + /* verify */ false, + profile_compilation_info.get()); ASSERT_FALSE(success); } -- GitLab From 787b8d1768ece4e284a03ce1577bfdca260621d9 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Fri, 9 Mar 2018 13:22:25 +0000 Subject: [PATCH 053/749] Revert "Reduce number of tests run in art-gcstress-gcverify." This reverts commit 4f64fbb782987e48f67bae24fec12719482de97f. Reason for revert: The initial issue (timeout on build bots) should have been addressed by https://android-review.googlesource.com/635812. Bug: 74196452 Bug: 74225325 Change-Id: Ibbbd3f8747f57d0e00bc4bc960eb5b4dc1fa22e0 Test: Start ./test/testrunner/run_build_test_target.py -j30 art-gcstress-gcverif --- test/testrunner/target_config.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index 2c433af512..9d0377510a 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -69,13 +69,7 @@ target_config = { } }, 'art-gcstress-gcverify': { - # Don't include --interpreter, because it takes too long to run all - # the tests on the build bot (b/74225325) - 'run-test': ['--interp-ac', - '--jit', - '--optimizing', - '--speed-profile', - '--gcstress', + 'run-test': ['--gcstress', '--gcverify'], 'env' : { 'ART_USE_READ_BARRIER' : 'false', -- GitLab From 1079c823aa726de7dcb290eb7be6fbfd90c2e4c3 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Fri, 9 Mar 2018 09:04:20 -0800 Subject: [PATCH 054/749] Remove test 1934 skip The issue causing this failure was fixed in an earlier CL but we forgot to remove the skip. Test: ./test.py --trace --stream --host -j50 Bug: 67384421 Bug: 74386428 Follow up to commit: 74c91c9e90ca502a88240b3d14aed4dd818eedc2 Change-Id: Ic14a9d55a4253c176ef9479b893e825da27a0cae --- test/knownfailures.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/knownfailures.json b/test/knownfailures.json index 70bb42c3ce..5fb78191ef 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -1,10 +1,4 @@ [ - { - "tests": "1934-jvmti-signal-thread", - "description": ["Disables 1934-jvmti-signal-thread in tracing configurations"], - "variant": "trace | stream", - "bug": "http://b/67384421" - }, { "tests": "153-reference-stress", "description": ["Disable 153-reference-stress temporarily until a fix", -- GitLab From 1f49d979a0a239ea33750e8d07073b4df8b5da6f Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Fri, 9 Mar 2018 23:23:36 +0000 Subject: [PATCH 055/749] Fix mac build: Update cfi directives for art_quick_osr_stub. Mac build doesn't like cfi directives. Bug: 73954823 Test: testrunner.py -t 570 --jit Change-Id: Idbe44646c20d17079528a82bdb0c915691ac2748 --- runtime/arch/x86/asm_support_x86.S | 2 ++ runtime/arch/x86/quick_entrypoints_x86.S | 14 +++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/runtime/arch/x86/asm_support_x86.S b/runtime/arch/x86/asm_support_x86.S index 14b01c59be..c9514f5f27 100644 --- a/runtime/arch/x86/asm_support_x86.S +++ b/runtime/arch/x86/asm_support_x86.S @@ -79,6 +79,7 @@ #define CFI_REL_OFFSET(reg,size) .cfi_rel_offset reg,size #define CFI_RESTORE_STATE .cfi_restore_state #define CFI_REMEMBER_STATE .cfi_remember_state + #define CFI_ESCAPE(...) .cfi_escape __VA_ARGS__ #else // Mac OS' doesn't like cfi_* directives. #define CFI_STARTPROC @@ -90,6 +91,7 @@ #define CFI_REL_OFFSET(reg,size) #define CFI_RESTORE_STATE #define CFI_REMEMBER_STATE + #define CFI_ESCAPE(...) #endif // Symbols. On a Mac, we need a leading underscore. diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index abd784a91c..9251161ecc 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -2374,8 +2374,8 @@ DEFINE_FUNCTION art_quick_osr_stub mov 8+16(%esp), %ecx // ECX = size of args mov 12+16(%esp), %ebx // EBX = pc to call mov %esp, %ebp // Save stack pointer - .cfi_def_cfa ebp, SAVE_SIZE // CFA = ebp + SAVE_SIZE - .cfi_remember_state + CFI_DEF_CFA(ebp, SAVE_SIZE) // CFA = ebp + SAVE_SIZE + CFI_REMEMBER_STATE andl LITERAL(0xFFFFFFF0), %esp // Align stack pushl %ebp // Save old stack pointer subl LITERAL(12), %esp // Align stack @@ -2383,14 +2383,14 @@ DEFINE_FUNCTION art_quick_osr_stub // ebp isn't properly spilled in the osr method, so we need use DWARF expression. // NB: the CFI must be before the call since this is the address gdb will lookup. // NB: gdb expects that cfa_expression returns the CFA value (not address to it). - .cfi_escape /* cfa = [sp + 12] + SAVE_SIZE */ \ + CFI_ESCAPE( /* cfa = [sp + 12] + SAVE_SIZE */ \ 0x0f, 6, /* DW_CFA_def_cfa_expression(len) */ \ 0x92, 4, 12, /* DW_OP_bregx(reg,offset) */ \ 0x06, /* DW_OP_deref */ \ - 0x23, SAVE_SIZE /* DW_OP_plus_uconst(val) */ + 0x23, SAVE_SIZE) /* DW_OP_plus_uconst(val) */ call .Losr_entry mov 12(%esp), %esp // Restore stack pointer. - .cfi_def_cfa esp, SAVE_SIZE // CFA = esp + SAVE_SIZE + CFI_DEF_CFA(esp, SAVE_SIZE) // CFA = esp + SAVE_SIZE // Restore callee saves. POP edi @@ -2413,8 +2413,8 @@ DEFINE_FUNCTION art_quick_osr_stub movss %xmm0, (%ecx) // Store the floating point result ret .Losr_entry: - .cfi_restore_state - .cfi_def_cfa ebp, SAVE_SIZE // CFA = ebp + SAVE_SIZE + CFI_RESTORE_STATE + CFI_DEF_CFA(ebp, SAVE_SIZE) // CFA = ebp + SAVE_SIZE subl LITERAL(4), %ecx // Given stack size contains pushed frame pointer, substract it. subl %ecx, %esp mov %esp, %edi // EDI = beginning of stack -- GitLab From c7d25083a51e73bef66f15198f84767de84beb0f Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Fri, 9 Mar 2018 12:06:45 -0800 Subject: [PATCH 056/749] ART: Make patchoat more resistant Disable relocation, disallow dex-file fallback. These are unwelcome when patching. Refactor to have one set of options. Bug: 73887870 Test: m test-art-host Test: Flash-wipe device, boot Change-Id: Ic7698cb83d1a8b4bb318af472f388dc88981c49c --- patchoat/patchoat.cc | 56 ++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 3df640902a..b9a9a69ab5 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -65,6 +65,8 @@ namespace art { using android::base::StringPrintf; +namespace { + static const OatHeader* GetOatHeader(const ElfFile* elf_file) { uint64_t off = 0; if (!elf_file->GetSectionOffsetAndSize(".rodata", &off, nullptr)) { @@ -127,6 +129,38 @@ static bool SymlinkFile(const std::string& input_filename, const std::string& ou return true; } +// Holder class for runtime options and related objects. +class PatchoatRuntimeOptionsHolder { + public: + PatchoatRuntimeOptionsHolder(const std::string& image_location, InstructionSet isa) { + options_.push_back(std::make_pair("compilercallbacks", &callbacks_)); + img_ = "-Ximage:" + image_location; + options_.push_back(std::make_pair(img_.c_str(), nullptr)); + isa_name_ = GetInstructionSetString(isa); + options_.push_back(std::make_pair("imageinstructionset", + reinterpret_cast(isa_name_.c_str()))); + options_.push_back(std::make_pair("-Xno-sig-chain", nullptr)); + // We do not want the runtime to attempt to patch the image. + options_.push_back(std::make_pair("-Xnorelocate", nullptr)); + // Don't try to compile. + options_.push_back(std::make_pair("-Xnoimage-dex2oat", nullptr)); + // Do not accept broken image. + options_.push_back(std::make_pair("-Xno-dex-file-fallback", nullptr)); + } + + const RuntimeOptions& GetRuntimeOptions() { + return options_; + } + + private: + RuntimeOptions options_; + NoopCompilerCallbacks callbacks_; + std::string isa_name_; + std::string img_; +}; + +} // namespace + bool PatchOat::GeneratePatch( const MemMap& original, const MemMap& relocated, @@ -440,17 +474,10 @@ bool PatchOat::Patch(const std::string& image_location, TimingLogger::ScopedTiming t("Runtime Setup", timings); CHECK_NE(isa, InstructionSet::kNone); - const char* isa_name = GetInstructionSetString(isa); // Set up the runtime - RuntimeOptions options; - NoopCompilerCallbacks callbacks; - options.push_back(std::make_pair("compilercallbacks", &callbacks)); - std::string img = "-Ximage:" + image_location; - options.push_back(std::make_pair(img.c_str(), nullptr)); - options.push_back(std::make_pair("imageinstructionset", reinterpret_cast(isa_name))); - options.push_back(std::make_pair("-Xno-sig-chain", nullptr)); - if (!Runtime::Create(options, false)) { + PatchoatRuntimeOptionsHolder options_holder(image_location, isa); + if (!Runtime::Create(options_holder.GetRuntimeOptions(), false)) { LOG(ERROR) << "Unable to initialize runtime"; return false; } @@ -608,17 +635,10 @@ bool PatchOat::Verify(const std::string& image_location, TimingLogger::ScopedTiming t("Runtime Setup", timings); CHECK_NE(isa, InstructionSet::kNone); - const char* isa_name = GetInstructionSetString(isa); // Set up the runtime - RuntimeOptions options; - NoopCompilerCallbacks callbacks; - options.push_back(std::make_pair("compilercallbacks", &callbacks)); - std::string img = "-Ximage:" + image_location; - options.push_back(std::make_pair(img.c_str(), nullptr)); - options.push_back(std::make_pair("imageinstructionset", reinterpret_cast(isa_name))); - options.push_back(std::make_pair("-Xno-sig-chain", nullptr)); - if (!Runtime::Create(options, false)) { + PatchoatRuntimeOptionsHolder options_holder(image_location, isa); + if (!Runtime::Create(options_holder.GetRuntimeOptions(), false)) { LOG(ERROR) << "Unable to initialize runtime"; return false; } -- GitLab From 0530bccdc6e79d6015d16cb868444a19004f3bf9 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 12 Mar 2018 13:30:42 +0000 Subject: [PATCH 057/749] Fix oatdump_app_test for address sanitizer. Pass -Xmx64M to dex2oat to reduce the memory allocation as the test was failing to allocate the default 512MiB heap area for certain address sanitation configs. Test: SANITIZE_HOST=address ASAN_OPTIONS=detect_leaks=0 \ ART_HEAP_POISONING=true ART_USE_READ_BARRIER=false \ m test-art-host-gtest-oatdump_app_test Bug: 74378710 Change-Id: I74503df6e98a113a86a938150d69399c9eead306 --- oatdump/oatdump_app_test.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/oatdump/oatdump_app_test.cc b/oatdump/oatdump_app_test.cc index f12522277b..34b07d2ddf 100644 --- a/oatdump/oatdump_app_test.cc +++ b/oatdump/oatdump_app_test.cc @@ -20,25 +20,29 @@ namespace art { TEST_F(OatDumpTest, TestAppWithBootImage) { std::string error_msg; - ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {}, &error_msg)) << error_msg; + ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {"--runtime-arg", "-Xmx64M"}, &error_msg)) << error_msg; ASSERT_TRUE(Exec(kDynamic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg; } TEST_F(OatDumpTest, TestAppWithBootImageStatic) { TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); std::string error_msg; - ASSERT_TRUE(GenerateAppOdexFile(kStatic, {}, &error_msg)) << error_msg; + ASSERT_TRUE(GenerateAppOdexFile(kStatic, {"--runtime-arg", "-Xmx64M"}, &error_msg)) << error_msg; ASSERT_TRUE(Exec(kStatic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg; } TEST_F(OatDumpTest, TestPicAppWithBootImage) { std::string error_msg; - ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {"--compile-pic"}, &error_msg)) << error_msg; + ASSERT_TRUE( + GenerateAppOdexFile(kDynamic, {"--runtime-arg", "-Xmx64M", "--compile-pic"}, &error_msg)) + << error_msg; ASSERT_TRUE(Exec(kDynamic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg; } TEST_F(OatDumpTest, TestPicAppWithBootImageStatic) { TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); std::string error_msg; - ASSERT_TRUE(GenerateAppOdexFile(kStatic, {"--compile-pic"}, &error_msg)) << error_msg; + ASSERT_TRUE( + GenerateAppOdexFile(kStatic, {"--runtime-arg", "-Xmx64M", "--compile-pic"}, &error_msg)) + << error_msg; ASSERT_TRUE(Exec(kStatic, kModeOatWithBootImage, {}, kListAndCode, &error_msg)) << error_msg; } -- GitLab From 2303a8554312870fa2b782a1db25ec7d871a79fd Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Mon, 12 Mar 2018 14:16:11 +0000 Subject: [PATCH 058/749] Remove macro definition TEST_DISABLED_FOR_COMPACT_DEX. This macro is no longer used anywhere in ART. Test: mmma art Bug: 63756964 Change-Id: I0806791ccb5969e3df8fb666b027ba3b840ccc96 --- runtime/common_runtime_test.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 7fc70e294f..83a1f9a58a 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -332,12 +332,6 @@ class CheckJniAbortCatcher { return; \ } -#define TEST_DISABLED_FOR_COMPACT_DEX() \ - if (kDefaultCompactDexLevel != CompactDexLevel::kCompactDexLevelNone) { \ - printf("WARNING: TEST DISABLED FOR COMPACT DEX\n"); \ - return; \ - } - #define TEST_DISABLED_FOR_HEAP_POISONING() \ if (kPoisonHeapReferences) { \ printf("WARNING: TEST DISABLED FOR HEAP POISONING\n"); \ -- GitLab From 5047d9f73d522b8558c9a2836312d83d3fa75c18 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Fri, 9 Mar 2018 15:44:31 -0800 Subject: [PATCH 059/749] Make test 1934 not flaky. Test 1934 was slightly flaky since if the target thread is stopped during a class-load you would get a ExceptionInInitializerError (or a DCHECK failure) instead of the expected error. This change removes the DCHECK if we are using AsyncExceptions and force the class to be initialized before running the test. Test: ./test.py --host -j50 Bug: 74446036 Change-Id: I164611f08c7a6ba4ea127cda6cf1df3dca25f08d --- runtime/class_linker.cc | 10 +++++++--- .../src/art/Test1934.java | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index bf0d3adf0f..72c110a970 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -268,9 +268,13 @@ static void WrapExceptionInInitializer(Handle klass) // cannot in general be guaranteed, but in all likelihood leads to breakage down the line. if (klass->GetClassLoader() == nullptr && !Runtime::Current()->IsAotCompiler()) { std::string tmp; - LOG(kIsDebugBuild ? FATAL : WARNING) << klass->GetDescriptor(&tmp) - << " failed initialization: " - << self->GetException()->Dump(); + // We want to LOG(FATAL) on debug builds since this really shouldn't be happening but we need to + // make sure to only do it if we don't have AsyncExceptions being thrown around since those + // could have caused the error. + bool known_impossible = kIsDebugBuild && !Runtime::Current()->AreAsyncExceptionsThrown(); + LOG(known_impossible ? FATAL : WARNING) << klass->GetDescriptor(&tmp) + << " failed initialization: " + << self->GetException()->Dump(); } env->ExceptionClear(); diff --git a/test/1934-jvmti-signal-thread/src/art/Test1934.java b/test/1934-jvmti-signal-thread/src/art/Test1934.java index 2a3f8db5f8..308f17b961 100644 --- a/test/1934-jvmti-signal-thread/src/art/Test1934.java +++ b/test/1934-jvmti-signal-thread/src/art/Test1934.java @@ -25,6 +25,8 @@ public class Test1934 { public static final boolean PRINT_STACK_TRACE = false; public static void run() throws Exception { + ensureClassesLoaded(); + System.out.println("Interrupt before start"); testInterruptBeforeStart(); @@ -53,6 +55,22 @@ public class Test1934 { testStopInNative(); } + private static void ensureInitialized(Class c) { + try { + Class.forName(c.getName()); + } catch (Exception e) { + throw new Error("Failed to initialize " + c, e); + } + } + + private static void ensureClassesLoaded() { + // Depending on timing this class might (or might not) be loaded during testing of Stop. If it + // is and the StopThread occurs inside of it we will get a ExceptionInInitializerError which is + // not what we are looking for. In order to avoid this ever happening simply initialize the + // class that can cause it early. + ensureInitialized(java.util.concurrent.locks.LockSupport.class); + } + public static void testStopBeforeStart() throws Exception { final Throwable[] out_err = new Throwable[] { null, }; final Object tst = new Object(); -- GitLab From 19a81b80bef76952b298f60576b02c65f8437fc4 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 12 Mar 2018 16:22:19 +0000 Subject: [PATCH 060/749] Revert "Disable 639-checker-code-sinking in PIC mode." The test is now passing, probably since https://android-review.googlesource.com/478026 . Test: testrunner.py --host --pictest -t 639-checker-code-sinking Test: testrunner.py --target --pictest -t 639-checker-code-sinking Bug: 65366606 This reverts commit e4e692ad9f7fe828dfad451692fdb636cfd7df4c. Change-Id: Iaafff8b858347df5915608bad31c6001b68001c8 --- test/knownfailures.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/knownfailures.json b/test/knownfailures.json index b2f579d3b9..f8ce32afcb 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -285,11 +285,6 @@ "tests": "596-app-images", "variant": "npictest" }, - { - "tests": "639-checker-code-sinking", - "variant": "pictest", - "bug": "http://b/65366606" - }, { "tests": "055-enum-performance", "variant": "optimizing | regalloc_gc", -- GitLab From c2a2bdab0e34a130c54a0fdcd77b6bf6fa5bf6df Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 12 Mar 2018 11:21:52 -0700 Subject: [PATCH 061/749] Add more details to verifier dumping for gAborting Print the method we are verifying. Bug: 74521989 Test: test-art-host Change-Id: I5a92d5ff56cb7319bc19c47acc3d66e363da0c66 --- runtime/verifier/method_verifier.cc | 38 +++++++++++++++++------------ runtime/verifier/method_verifier.h | 2 ++ 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 21a4ecca47..2a2afc4a27 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -105,25 +105,27 @@ void PcToRegisterLineTable::Init(RegisterTrackingMode mode, InstructionFlags* fl PcToRegisterLineTable::~PcToRegisterLineTable() {} // Note: returns true on failure. -ALWAYS_INLINE static inline bool FailOrAbort(MethodVerifier* verifier, bool condition, - const char* error_msg, uint32_t work_insn_idx) { +inline bool MethodVerifier::FailOrAbort(bool condition, + const char* error_msg, + uint32_t work_insn_idx) { if (kIsDebugBuild) { // In a debug build, abort if the error condition is wrong. Only warn if // we are already aborting (as this verification is likely run to print // lock information). if (LIKELY(gAborting == 0)) { - DCHECK(condition) << error_msg << work_insn_idx; + DCHECK(condition) << error_msg << work_insn_idx << " " + << dex_file_->PrettyMethod(dex_method_idx_); } else { if (!condition) { LOG(ERROR) << error_msg << work_insn_idx; - verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << error_msg << work_insn_idx; + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << error_msg << work_insn_idx; return true; } } } else { // In a non-debug build, just fail the class. if (!condition) { - verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << error_msg << work_insn_idx; + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << error_msg << work_insn_idx; return true; } } @@ -2683,7 +2685,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { while (0 != instance_of_idx && !GetInstructionFlags(instance_of_idx).IsOpcode()) { instance_of_idx--; } - if (FailOrAbort(this, GetInstructionFlags(instance_of_idx).IsOpcode(), + if (FailOrAbort(GetInstructionFlags(instance_of_idx).IsOpcode(), "Unable to get previous instruction of if-eqz/if-nez for work index ", work_insn_idx_)) { break; @@ -2750,7 +2752,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { while (0 != move_idx && !GetInstructionFlags(move_idx).IsOpcode()) { move_idx--; } - if (FailOrAbort(this, GetInstructionFlags(move_idx).IsOpcode(), + if (FailOrAbort(GetInstructionFlags(move_idx).IsOpcode(), "Unable to get previous instruction of if-eqz/if-nez for work index ", work_insn_idx_)) { break; @@ -3863,8 +3865,7 @@ const RegType& MethodVerifier::GetCaughtExceptionType() { // odd case, but nothing to do } else { common_super = &common_super->Merge(exception, ®_types_, this); - if (FailOrAbort(this, - reg_types_.JavaLangThrowable(false).IsAssignableFrom( + if (FailOrAbort(reg_types_.JavaLangThrowable(false).IsAssignableFrom( *common_super, this), "java.lang.Throwable is not assignable-from common_super at ", work_insn_idx_)) { @@ -4430,8 +4431,9 @@ ArtMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst, Regist if (klass->IsInterface()) { // Derive Object.class from Class.class.getSuperclass(). mirror::Class* object_klass = klass->GetClass()->GetSuperClass(); - if (FailOrAbort(this, object_klass->IsObjectClass(), - "Failed to find Object class in quickened invoke receiver", work_insn_idx_)) { + if (FailOrAbort(object_klass->IsObjectClass(), + "Failed to find Object class in quickened invoke receiver", + work_insn_idx_)) { return nullptr; } dispatch_class = object_klass; @@ -4439,7 +4441,8 @@ ArtMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst, Regist dispatch_class = klass; } if (!dispatch_class->HasVTable()) { - FailOrAbort(this, allow_failure, "Receiver class has no vtable for quickened invoke at ", + FailOrAbort(allow_failure, + "Receiver class has no vtable for quickened invoke at ", work_insn_idx_); return nullptr; } @@ -4447,14 +4450,15 @@ ArtMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst, Regist auto* cl = Runtime::Current()->GetClassLinker(); auto pointer_size = cl->GetImagePointerSize(); if (static_cast(vtable_index) >= dispatch_class->GetVTableLength()) { - FailOrAbort(this, allow_failure, + 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(this, allow_failure, "Unexpected exception pending for quickened invoke at ", + FailOrAbort(allow_failure, + "Unexpected exception pending for quickened invoke at ", work_insn_idx_); return nullptr; } @@ -4470,11 +4474,13 @@ ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst, Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer method from " << inst->Name(); return nullptr; } - if (FailOrAbort(this, !res_method->IsDirect(), "Quick-invoked method is direct at ", + if (FailOrAbort(!res_method->IsDirect(), + "Quick-invoked method is direct at ", work_insn_idx_)) { return nullptr; } - if (FailOrAbort(this, !res_method->IsStatic(), "Quick-invoked method is static at ", + if (FailOrAbort(!res_method->IsStatic(), + "Quick-invoked method is static at ", work_insn_idx_)) { return nullptr; } diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 26c598f224..9a94297942 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -721,6 +721,8 @@ class MethodVerifier { const RegType& FromClass(const char* descriptor, mirror::Class* klass, bool precise) REQUIRES_SHARED(Locks::mutator_lock_); + ALWAYS_INLINE bool FailOrAbort(bool condition, const char* error_msg, uint32_t work_insn_idx); + // The thread we're verifying on. Thread* const self_; -- GitLab From 1f130f16f8f8cedd61808a40c04ad1150898d268 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Mon, 12 Mar 2018 18:39:19 +0000 Subject: [PATCH 062/749] Add missing Dex file dependency for ART gtest oat_writer_test. Test: m test-art-host-gtest-oat_writer_test Change-Id: Iad604d0f282219759814c14dea8fd9253a68ff7f --- build/Android.gtest.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index e171289410..8681642c0e 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -170,6 +170,7 @@ ART_GTEST_dexoptanalyzer_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_ ART_GTEST_image_space_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex MainUncompressed MultiDexUncompressed ART_GTEST_oat_test_DEX_DEPS := Main +ART_GTEST_oat_writer_test_DEX_DEPS := Main ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY ART_GTEST_patchoat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ART_GTEST_proxy_test_DEX_DEPS := Interfaces -- GitLab From 74d25c9040dfd1e0985987eb38817e526878a3db Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 12 Mar 2018 19:26:44 +0000 Subject: [PATCH 063/749] Revert "Revert "Add an option to disable native stack dumping on SIGQUIT."" Bug: 27185632 Bug: 74121887 Got flakes on device for 004-ThreadStress and 141-static-field-sigquit This reverts commit 8b089742252e827d863218413e8855e1bae75af5. Change-Id: Ie76c84a1889e85b885d5f4123d60e8a760cf186d --- runtime/parsed_options.cc | 5 +++++ runtime/runtime.cc | 2 ++ runtime/runtime.h | 7 +++++++ runtime/runtime_common.cc | 3 ++- runtime/runtime_options.def | 1 + runtime/thread.cc | 12 ++++++++---- runtime/thread.h | 2 ++ runtime/thread_list.cc | 21 +++++++++++++-------- runtime/thread_list.h | 2 +- test/etc/run-test-jar | 4 ++++ 10 files changed, 45 insertions(+), 14 deletions(-) diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 1d48817e94..c61ecc880b 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -161,6 +161,10 @@ std::unique_ptr ParsedOptions::MakeParser(bool ignore_unrecognize .Define({"-XX:EnableHSpaceCompactForOOM", "-XX:DisableHSpaceCompactForOOM"}) .WithValues({true, false}) .IntoKey(M::EnableHSpaceCompactForOOM) + .Define("-XX:DumpNativeStackOnSigQuit:_") + .WithType() + .WithValueMap({{"false", false}, {"true", true}}) + .IntoKey(M::DumpNativeStackOnSigQuit) .Define("-XX:MadviseRandomAccess:_") .WithType() .WithValueMap({{"false", false}, {"true", true}}) @@ -731,6 +735,7 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -XX:BackgroundGC=none\n"); UsageMessage(stream, " -XX:LargeObjectSpace={disabled,map,freelist}\n"); UsageMessage(stream, " -XX:LargeObjectThreshold=N\n"); + UsageMessage(stream, " -XX:DumpNativeStackOnSigQuit=booleanvalue\n"); UsageMessage(stream, " -XX:MadviseRandomAccess:booleanvalue\n"); UsageMessage(stream, " -XX:SlowDebug={false,true}\n"); UsageMessage(stream, " -Xmethod-trace\n"); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index d0a1acc072..4442fc6a54 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -269,6 +269,7 @@ Runtime::Runtime() pending_hidden_api_warning_(false), dedupe_hidden_api_warnings_(true), always_set_hidden_api_warning_flag_(false), + dump_native_stack_on_sig_quit_(true), pruned_dalvik_cache_(false), // Initially assume we perceive jank in case the process state is never updated. process_state_(kProcessStateJankPerceptible), @@ -1150,6 +1151,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { is_explicit_gc_disabled_ = runtime_options.Exists(Opt::DisableExplicitGC); dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::Dex2Oat); image_dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::ImageDex2Oat); + dump_native_stack_on_sig_quit_ = runtime_options.GetOrDefault(Opt::DumpNativeStackOnSigQuit); vfprintf_ = runtime_options.GetOrDefault(Opt::HookVfprintf); exit_ = runtime_options.GetOrDefault(Opt::HookExit); diff --git a/runtime/runtime.h b/runtime/runtime.h index b961e7f39a..c7f650ea3f 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -651,6 +651,10 @@ class Runtime { safe_mode_ = mode; } + bool GetDumpNativeStackOnSigQuit() const { + return dump_native_stack_on_sig_quit_; + } + bool GetPrunedDalvikCache() const { return pruned_dalvik_cache_; } @@ -1001,6 +1005,9 @@ class Runtime { // when there is a warning. This is only used for testing. bool always_set_hidden_api_warning_flag_; + // Whether threads should dump their native stack on SIGQUIT. + bool dump_native_stack_on_sig_quit_; + // Whether the dalvik cache was pruned when initializing the runtime. bool pruned_dalvik_cache_; diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc index 41bfb58d93..59af9187f9 100644 --- a/runtime/runtime_common.cc +++ b/runtime/runtime_common.cc @@ -41,6 +41,7 @@ namespace art { using android::base::StringPrintf; static constexpr bool kUseSigRTTimeout = true; +static constexpr bool kDumpNativeStackOnTimeout = true; const char* GetSignalName(int signal_number) { switch (signal_number) { @@ -440,7 +441,7 @@ void HandleUnexpectedSignalCommon(int signal_number, // Special timeout signal. Try to dump all threads. // Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts // are of value here. - runtime->GetThreadList()->Dump(std::cerr); + runtime->GetThreadList()->Dump(std::cerr, kDumpNativeStackOnTimeout); std::cerr << std::endl; } diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index dcb1335023..4121ad69ed 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -70,6 +70,7 @@ RUNTIME_OPTIONS_KEY (Unit, LowMemoryMode) RUNTIME_OPTIONS_KEY (bool, UseTLAB, (kUseTlab || kUseReadBarrier)) RUNTIME_OPTIONS_KEY (bool, EnableHSpaceCompactForOOM, true) RUNTIME_OPTIONS_KEY (bool, UseJitCompilation, false) +RUNTIME_OPTIONS_KEY (bool, DumpNativeStackOnSigQuit, true) RUNTIME_OPTIONS_KEY (bool, MadviseRandomAccess, false) RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold) RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold) diff --git a/runtime/thread.cc b/runtime/thread.cc index 87ffcb11d2..4cdf015478 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1161,9 +1161,10 @@ void Thread::ShortDump(std::ostream& os) const { << "]"; } -void Thread::Dump(std::ostream& os, BacktraceMap* backtrace_map, bool force_dump_stack) const { +void Thread::Dump(std::ostream& os, bool dump_native_stack, BacktraceMap* backtrace_map, + bool force_dump_stack) const { DumpState(os); - DumpStack(os, backtrace_map, force_dump_stack); + DumpStack(os, dump_native_stack, backtrace_map, force_dump_stack); } mirror::String* Thread::GetThreadName() const { @@ -1963,7 +1964,10 @@ void Thread::DumpJavaStack(std::ostream& os, bool check_suspended, bool dump_loc } } -void Thread::DumpStack(std::ostream& os, BacktraceMap* backtrace_map, bool force_dump_stack) const { +void Thread::DumpStack(std::ostream& os, + bool dump_native_stack, + BacktraceMap* backtrace_map, + bool force_dump_stack) const { // TODO: we call this code when dying but may not have suspended the thread ourself. The // IsSuspended check is therefore racy with the use for dumping (normally we inhibit // the race with the thread_suspend_count_lock_). @@ -1976,7 +1980,7 @@ void Thread::DumpStack(std::ostream& os, BacktraceMap* backtrace_map, bool force } if (safe_to_dump || force_dump_stack) { // If we're currently in native code, dump that stack before dumping the managed stack. - if (dump_for_abort || force_dump_stack || ShouldShowNativeStack(this)) { + if (dump_native_stack && (dump_for_abort || force_dump_stack || ShouldShowNativeStack(this))) { DumpKernelStack(os, GetTid(), " kernel: ", false); ArtMethod* method = GetCurrentMethod(nullptr, diff --git a/runtime/thread.h b/runtime/thread.h index 87515d6206..295685e799 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -207,6 +207,7 @@ class Thread { // Dumps the detailed thread state and the thread stack (used for SIGQUIT). void Dump(std::ostream& os, + bool dump_native_stack = true, BacktraceMap* backtrace_map = nullptr, bool force_dump_stack = false) const REQUIRES(!Locks::thread_suspend_count_lock_) @@ -1302,6 +1303,7 @@ class Thread { void DumpState(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_); void DumpStack(std::ostream& os, + bool dump_native_stack = true, BacktraceMap* backtrace_map = nullptr, bool force_dump_stack = false) const REQUIRES(!Locks::thread_suspend_count_lock_) diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 2e41b9f455..8095ef57c7 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -152,8 +152,9 @@ void ThreadList::DumpForSigQuit(std::ostream& os) { suspend_all_historam_.PrintConfidenceIntervals(os, 0.99, data); // Dump time to suspend. } } - Dump(os); - DumpUnattachedThreads(os, kDumpUnattachedThreadNativeStackForSigQuit); + bool dump_native_stack = Runtime::Current()->GetDumpNativeStackOnSigQuit(); + Dump(os, dump_native_stack); + DumpUnattachedThreads(os, dump_native_stack && kDumpUnattachedThreadNativeStackForSigQuit); } static void DumpUnattachedThread(std::ostream& os, pid_t tid, bool dump_native_stack) @@ -200,10 +201,11 @@ static constexpr uint32_t kDumpWaitTimeout = kIsTargetBuild ? 100000 : 20000; // A closure used by Thread::Dump. class DumpCheckpoint FINAL : public Closure { public: - explicit DumpCheckpoint(std::ostream* os) + DumpCheckpoint(std::ostream* os, bool dump_native_stack) : os_(os), barrier_(0), - backtrace_map_(BacktraceMap::Create(getpid())) { + backtrace_map_(dump_native_stack ? BacktraceMap::Create(getpid()) : nullptr), + dump_native_stack_(dump_native_stack) { if (backtrace_map_ != nullptr) { backtrace_map_->SetSuffixesToIgnore(std::vector { "oat", "odex" }); } @@ -217,7 +219,7 @@ class DumpCheckpoint FINAL : public Closure { std::ostringstream local_os; { ScopedObjectAccess soa(self); - thread->Dump(local_os, backtrace_map_.get()); + thread->Dump(local_os, dump_native_stack_, backtrace_map_.get()); } { // Use the logging lock to ensure serialization when writing to the common ostream. @@ -245,16 +247,18 @@ class DumpCheckpoint FINAL : public Closure { Barrier barrier_; // A backtrace map, so that all threads use a shared info and don't reacquire/parse separately. std::unique_ptr backtrace_map_; + // Whether we should dump the native stack. + const bool dump_native_stack_; }; -void ThreadList::Dump(std::ostream& os) { +void ThreadList::Dump(std::ostream& os, bool dump_native_stack) { Thread* self = Thread::Current(); { MutexLock mu(self, *Locks::thread_list_lock_); os << "DALVIK THREADS (" << list_.size() << "):\n"; } if (self != nullptr) { - DumpCheckpoint checkpoint(&os); + DumpCheckpoint checkpoint(&os, dump_native_stack); size_t threads_running_checkpoint; { // Use SOA to prevent deadlocks if multiple threads are calling Dump() at the same time. @@ -265,7 +269,7 @@ void ThreadList::Dump(std::ostream& os) { checkpoint.WaitForThreadsToRunThroughCheckpoint(threads_running_checkpoint); } } else { - DumpUnattachedThreads(os, /* dump_native_stack */ true); + DumpUnattachedThreads(os, dump_native_stack); } } @@ -487,6 +491,7 @@ void ThreadList::RunEmptyCheckpoint() { // Found a runnable thread that hasn't responded to the empty checkpoint request. // Assume it's stuck and safe to dump its stack. thread->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT), + /*dump_native_stack*/ true, /*backtrace_map*/ nullptr, /*force_dump_stack*/ true); } diff --git a/runtime/thread_list.h b/runtime/thread_list.h index 09b10d2ad3..895c1a41ce 100644 --- a/runtime/thread_list.h +++ b/runtime/thread_list.h @@ -57,7 +57,7 @@ class ThreadList { void DumpForSigQuit(std::ostream& os) REQUIRES(!Locks::thread_list_lock_, !Locks::mutator_lock_); // For thread suspend timeout dumps. - void Dump(std::ostream& os) + void Dump(std::ostream& os, bool dump_native_stack = true) REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_); pid_t GetLockOwner(); // For SignalCatcher. diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 5c51aed7a2..b8427f491b 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -775,6 +775,9 @@ if [ "$HOST" = "n" ]; then TMP_DIR_OPTION="-Djava.io.tmpdir=/data/local/tmp" fi +# We set DumpNativeStackOnSigQuit to false to avoid stressing libunwind. +# b/27185632 +# b/24664297 dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \ $GDB_ARGS \ $FLAGS \ @@ -789,6 +792,7 @@ dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \ $DEBUGGER_OPTS \ $DALVIKVM_BOOT_OPT \ $TMP_DIR_OPTION \ + -XX:DumpNativeStackOnSigQuit:false \ -cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX $MAIN $ARGS" # Remove whitespace. -- GitLab From 91460a5875a1f0f1ff60c8c3c0705ee61cda15dc Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Mon, 12 Mar 2018 16:06:56 -0700 Subject: [PATCH 064/749] Disassemble saturation arithmetic x86/x86_64. Rationale: Saturation arithmetic? It is coming! Bug: b/74026074 Test: visual inspection Change-Id: I056a2f785b01f9d56749a9fca611846f871e253c --- disassembler/disassembler_x86.cc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc index bbc8e370ea..dbdde647b2 100644 --- a/disassembler/disassembler_x86.cc +++ b/disassembler/disassembler_x86.cc @@ -1194,11 +1194,19 @@ DISASSEMBLER_ENTRY(cmp, opcode1 = opcode_tmp.c_str(); } break; + case 0xD8: + case 0xD9: case 0xDA: + case 0xDC: + case 0xDD: case 0xDE: case 0xE0: case 0xE3: + case 0xE8: + case 0xE9: case 0xEA: + case 0xEC: + case 0xED: case 0xEE: if (prefix[2] == 0x66) { src_reg_file = dst_reg_file = SSE; @@ -1207,11 +1215,19 @@ DISASSEMBLER_ENTRY(cmp, src_reg_file = dst_reg_file = MMX; } switch (*instr) { + case 0xD8: opcode1 = "psubusb"; break; + case 0xD9: opcode1 = "psubusw"; break; case 0xDA: opcode1 = "pminub"; break; + case 0xDC: opcode1 = "paddusb"; break; + case 0xDD: opcode1 = "paddusw"; break; case 0xDE: opcode1 = "pmaxub"; break; case 0xE0: opcode1 = "pavgb"; break; case 0xE3: opcode1 = "pavgw"; break; + case 0xE8: opcode1 = "psubsb"; break; + case 0xE9: opcode1 = "psubsw"; break; case 0xEA: opcode1 = "pminsw"; break; + case 0xEC: opcode1 = "paddsb"; break; + case 0xED: opcode1 = "paddsw"; break; case 0xEE: opcode1 = "pmaxsw"; break; } prefix[2] = 0; -- GitLab From 8574a3f3bd9bd4e77c8ac0018fb454b289e093f4 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 12 Mar 2018 16:26:14 -0700 Subject: [PATCH 065/749] Revert "Disable 130-hprof test on asan." This reverts commit 24d888e75a9a8cd6ee8b2b3f0a5d5e05c298ea5c. Reason for revert: For diagnostics. Bug: 73060923 Test: none Change-Id: Ia33f761c8b62cfe9e430a0228f88e40aa1728114 --- test/knownfailures.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/knownfailures.json b/test/knownfailures.json index f8ce32afcb..d1855dcbc7 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -664,12 +664,6 @@ "bug": "b/73275005", "description": ["Time out"] }, - { - "tests": ["130-hprof"], - "env_vars": {"SANITIZE_HOST": "address"}, - "bug": "b/73060923", - "description": ["ASAN issue"] - }, { "tests": ["1941-dispose-stress", "522-checker-regression-monitor-exit"], "variant": "jvm", -- GitLab From cca6fc0fbd2c4a4757956bbf20a27cd72281afcf Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Mon, 12 Mar 2018 16:37:21 -0700 Subject: [PATCH 066/749] sigchain: use libasync_safe logging. __android_log_write doesn't have async safe guarantees. Use libasync_safe, which has the added benefit of setting the abort message for fatal errors. Bug: http://b/67632085 Test: treehugger Change-Id: I4d710753fddbce43fca44485443c446ed745ec30 --- sigchainlib/Android.bp | 4 ++-- sigchainlib/log.h | 45 +++++++++++++++++++++++++++++++++++ sigchainlib/sigchain.cc | 23 +----------------- sigchainlib/sigchain_dummy.cc | 21 +--------------- 4 files changed, 49 insertions(+), 44 deletions(-) create mode 100644 sigchainlib/log.h diff --git a/sigchainlib/Android.bp b/sigchainlib/Android.bp index 1f490cd3b9..a151d7a6bc 100644 --- a/sigchainlib/Android.bp +++ b/sigchainlib/Android.bp @@ -35,7 +35,7 @@ cc_library { }, android: { - shared_libs: ["liblog"], + whole_static_libs: ["libasync_safe"], }, }, // Sigchainlib is whole-statically linked into binaries. For Android.mk-based binaries, @@ -56,7 +56,7 @@ cc_library_static { srcs: ["sigchain_dummy.cc"], target: { android: { - shared_libs: ["liblog"], + whole_static_libs: ["libasync_safe"], }, }, } diff --git a/sigchainlib/log.h b/sigchainlib/log.h new file mode 100644 index 0000000000..d689930c1e --- /dev/null +++ b/sigchainlib/log.h @@ -0,0 +1,45 @@ +/* + * 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. + */ + +#ifndef ART_SIGCHAINLIB_LOG_H_ +#define ART_SIGCHAINLIB_LOG_H_ + +#if defined(__ANDROID__) + +#include + +#define log(...) async_safe_format_log(ANDROID_LOG_ERROR, "libsigchain", __VA_ARGS__) +#define fatal async_safe_fatal + +#else + +#include +#include + +static void log(const char* format, ...) { + va_list ap; + va_start(ap, format); + vprintf(format, ap); + va_end(ap); + + printf("\n"); +} + +#define fatal(...) log(__VA_ARGS__); abort() + +#endif + +#endif // ART_SIGCHAINLIB_LOG_H_ diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc index 3127c5cfbd..2e5f46ca69 100644 --- a/sigchainlib/sigchain.cc +++ b/sigchainlib/sigchain.cc @@ -14,13 +14,6 @@ * limitations under the License. */ -#ifdef ART_TARGET_ANDROID -#include -#else -#include -#include -#endif - #include #include #include @@ -35,6 +28,7 @@ #include #include +#include "log.h" #include "sigchain.h" #if defined(__APPLE__) @@ -65,21 +59,6 @@ // doesn't have SA_RESTART, and raise the signal to avoid restarting syscalls that are // expected to be interrupted? -static void log(const char* format, ...) { - char buf[256]; - va_list ap; - va_start(ap, format); - vsnprintf(buf, sizeof(buf), format, ap); -#ifdef ART_TARGET_ANDROID - __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf); -#else - std::cout << buf << "\n"; -#endif - va_end(ap); -} - -#define fatal(...) log(__VA_ARGS__); abort() - #if defined(__BIONIC__) && !defined(__LP64__) && !defined(__mips__) static int sigismember(const sigset64_t* sigset, int signum) { return sigismember64(sigset, signum); diff --git a/sigchainlib/sigchain_dummy.cc b/sigchainlib/sigchain_dummy.cc index edce965e33..c274530987 100644 --- a/sigchainlib/sigchain_dummy.cc +++ b/sigchainlib/sigchain_dummy.cc @@ -17,13 +17,7 @@ #include #include -#ifdef ART_TARGET_ANDROID -#include -#else -#include -#include -#endif - +#include "log.h" #include "sigchain.h" #define ATTRIBUTE_UNUSED __attribute__((__unused__)) @@ -33,19 +27,6 @@ #pragma GCC diagnostic ignored "-Wunknown-pragmas" #pragma GCC diagnostic ignored "-Wmissing-noreturn" -static void log(const char* format, ...) { - char buf[256]; - va_list ap; - va_start(ap, format); - vsnprintf(buf, sizeof(buf), format, ap); -#ifdef ART_TARGET_ANDROID - __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf); -#else - std::cout << buf << "\n"; -#endif - va_end(ap); -} - namespace art { extern "C" void EnsureFrontOfChain(int signal ATTRIBUTE_UNUSED) { -- GitLab From be4c2bd892bd167a50b4dfa7133e70a809197698 Mon Sep 17 00:00:00 2001 From: Alexey Grebenkin Date: Thu, 1 Feb 2018 19:09:59 +0300 Subject: [PATCH 067/749] Fix dangling SingleImplementations left after class unloading Test: make test-art-host, manual using sample code bug: 73143991 Change-Id: I4d56b39c69d4ed60266a8b90b9e9d18fba7b8227 --- runtime/art_method-inl.h | 5 +- runtime/art_method.cc | 7 +- runtime/art_method.h | 17 ++- runtime/cha.cc | 101 +++++++++++++++ runtime/cha.h | 11 ++ runtime/class_linker.cc | 34 ++++- runtime/class_linker.h | 2 +- runtime/class_table-inl.h | 8 +- runtime/class_table.h | 4 +- runtime/mirror/class-inl.h | 19 ++- runtime/mirror/class.h | 6 + runtime/mirror/iftable.h | 5 +- test/616-cha-unloading/cha_unload.cc | 57 +++++++++ test/616-cha-unloading/expected.txt | 4 + test/616-cha-unloading/info.txt | 1 + test/616-cha-unloading/run | 18 +++ .../src-ex/AbstractCHATester.java | 19 +++ .../src-ex/ConcreteCHATester.java | 19 +++ .../src/AbstractCHATester.java | 19 +++ test/616-cha-unloading/src/Main.java | 121 ++++++++++++++++++ test/Android.bp | 1 + 21 files changed, 453 insertions(+), 25 deletions(-) create mode 100644 test/616-cha-unloading/cha_unload.cc create mode 100644 test/616-cha-unloading/expected.txt create mode 100644 test/616-cha-unloading/info.txt create mode 100644 test/616-cha-unloading/run create mode 100644 test/616-cha-unloading/src-ex/AbstractCHATester.java create mode 100644 test/616-cha-unloading/src-ex/ConcreteCHATester.java create mode 100644 test/616-cha-unloading/src/AbstractCHATester.java create mode 100644 test/616-cha-unloading/src/Main.java 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 ArtMethod::ResolveReturnType() { return ResolveClassFromTypeIndex(GetReturnTypeIndex()); } +template inline bool ArtMethod::HasSingleImplementation() { - if (IsFinal() || GetDeclaringClass()->IsFinal()) { + if (IsFinal() || GetDeclaringClass()->IsFinal()) { // We don't set kAccSingleImplementation for these cases since intrinsic // can use the flag also. return true; } - return (GetAccessFlags() & kAccSingleImplementation) != 0; + return (GetAccessFlags() & kAccSingleImplementation) != 0; } inline bool ArtMethod::IsHiddenIntrinsic(uint32_t ordinal) { diff --git a/runtime/art_method.cc b/runtime/art_method.cc index bbc60072b6..f3c495957f 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -88,13 +88,18 @@ ArtMethod* ArtMethod::GetNonObsoleteMethod() { } } +template ArtMethod* ArtMethod::GetSingleImplementation(PointerSize pointer_size) { - if (!IsAbstract()) { + if (!IsAbstract()) { // A non-abstract's single implementation is itself. return this; } return reinterpret_cast(GetDataPtrSize(pointer_size)); } +template ArtMethod* ArtMethod::GetSingleImplementation( + PointerSize pointer_size); +template ArtMethod* ArtMethod::GetSingleImplementation( + PointerSize pointer_size); ArtMethod* ArtMethod::FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject jlr_method) { diff --git a/runtime/art_method.h b/runtime/art_method.h index 579e554901..bd9b64df36 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -172,8 +172,9 @@ class ArtMethod FINAL { return (GetAccessFlags() & synchonized) != 0; } + template bool IsFinal() { - return (GetAccessFlags() & kAccFinal) != 0; + return (GetAccessFlags() & kAccFinal) != 0; } template @@ -242,10 +243,11 @@ class ArtMethod FINAL { } // This is set by the class linker. + template bool IsDefault() { static_assert((kAccDefault & (kAccIntrinsic | kAccIntrinsicBits)) == 0, "kAccDefault conflicts with intrinsic modifier"); - return (GetAccessFlags() & kAccDefault) != 0; + return (GetAccessFlags() & kAccDefault) != 0; } template @@ -280,8 +282,9 @@ class ArtMethod FINAL { return (GetAccessFlags() & mask) == mask; } + template bool IsAbstract() { - return (GetAccessFlags() & kAccAbstract) != 0; + return (GetAccessFlags() & kAccAbstract) != 0; } bool IsSynthetic() { @@ -496,6 +499,7 @@ class ArtMethod FINAL { return DataOffset(kRuntimePointerSize); } + template 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 ArtMethod* GetSingleImplementation(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); + template ALWAYS_INLINE void SetSingleImplementation(ArtMethod* method, PointerSize pointer_size) { - DCHECK(!IsNative()); - DCHECK(IsAbstract()); // Non-abstract method's single implementation is just itself. + DCHECK(!IsNative()); + // Non-abstract method's single implementation is just itself. + DCHECK(IsAbstract()); SetDataPtrSize(method, pointer_size); } 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 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 super = klass->GetSuperClass(); + + // 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(); + ObjPtr loader = + klass->GetClassLoader(); + for (int vtbl_index = 0; vtbl_index < vtbl_size; ++vtbl_index) { + ArtMethod* method = + klass->GetVTableEntry(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 super_it = super; + super_it != nullptr && + super_it->GetVTableLength() > vtbl_index; + super_it = super_it->GetSuperClass()) { + // Skip superclasses that are also going to be unloaded. + ObjPtr super_loader = super_it-> + GetClassLoader(); + if (super_loader == loader) { + continue; + } + + ArtMethod* super_method = super_it-> + GetVTableEntry(vtbl_index, pointer_size); + if (super_method->IsAbstract() && + super_method->HasSingleImplementation() && + super_method->GetSingleImplementation(pointer_size) == method) { + // Do like there was no single implementation defined previously + // for this method of the superclass. + super_method->SetSingleImplementation(nullptr, pointer_size); + } else { + // No related SingleImplementations could possibly be found any further. + DCHECK(!super_method->HasSingleImplementation()); + break; + } + } + } + + // Check all possible interface methods too. + ObjPtr iftable = klass->GetIfTable(); + const size_t ifcount = klass->GetIfTableCount(); + for (size_t i = 0; i < ifcount; ++i) { + ObjPtr interface = + iftable->GetInterface(i); + for (size_t j = 0, + count = iftable->GetMethodArrayCount(i); + j < count; + ++j) { + ArtMethod* method = interface->GetVirtualMethod(j, pointer_size); + if (method->HasSingleImplementation() && + alloc->ContainsUnsafe( + method->GetSingleImplementation(pointer_size)) && + !method->IsDefault()) { + // Do like there was no single implementation defined previously for this method. + method->SetSingleImplementation(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& 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 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 klass) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index bf0d3adf0f..3c1ffe0d50 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1162,6 +1162,25 @@ static bool FlattenPathClassLoader(ObjPtr 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 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_) @@ -2146,12 +2165,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); @@ -2166,6 +2187,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(visitor); + } + delete data.allocator; delete data.class_table; } @@ -8891,7 +8918,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 +template 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())) { return false; } } @@ -73,12 +73,12 @@ bool ClassTable::Visit(Visitor& visitor) { return true; } -template +template 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())) { 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 + template bool Visit(Visitor& visitor) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); - template + template bool Visit(const Visitor& visitor) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index ee7d217e8d..f63f105c3a 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 inline int32_t Class::GetVTableLength() { - if (ShouldHaveEmbeddedVTable()) { + if (ShouldHaveEmbeddedVTable()) { return GetEmbeddedVTableLength(); } - return GetVTable() != nullptr ? GetVTable()->GetLength() : 0; + return GetVTable() != nullptr ? + GetVTable()->GetLength() : 0; } + template inline ArtMethod* Class::GetVTableEntry(uint32_t i, PointerSize pointer_size) { - if (ShouldHaveEmbeddedVTable()) { + if (ShouldHaveEmbeddedVTable()) { return GetEmbeddedVTableEntry(i, pointer_size); } - auto* vtable = GetVTable(); + auto* vtable = GetVTable(); DCHECK(vtable != nullptr); - return vtable->GetElementPtrSize(i, pointer_size); + return vtable->template GetElementPtrSize(i, pointer_size); } inline int32_t Class::GetEmbeddedVTableLength() { @@ -627,8 +632,10 @@ inline IfTable* Class::GetIfTable() { return ret.Ptr(); } +template inline int32_t Class::GetIfTableCount() { - return GetIfTable()->Count(); + return GetIfTable()->Count(); } inline void Class::SetIfTable(ObjPtr new_iftable) { 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 int32_t GetVTableLength() REQUIRES_SHARED(Locks::mutator_lock_); + template 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 ALWAYS_INLINE int32_t GetIfTableCount() REQUIRES_SHARED(Locks::mutator_lock_); template { public: + template ALWAYS_INLINE Class* GetInterface(int32_t i) REQUIRES_SHARED(Locks::mutator_lock_) { - Class* interface = GetWithoutChecks((i * kMax) + kInterface)->AsClass(); + Class* interface = + GetWithoutChecks((i * kMax) + kInterface)->AsClass(); DCHECK(interface != nullptr); return interface; } diff --git a/test/616-cha-unloading/cha_unload.cc b/test/616-cha-unloading/cha_unload.cc new file mode 100644 index 0000000000..46ceac640b --- /dev/null +++ b/test/616-cha-unloading/cha_unload.cc @@ -0,0 +1,57 @@ +/* + * 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 + +#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(reinterpret_cast(method)); +} + +extern "C" JNIEXPORT jboolean JNICALL Java_Main_tryReuseArenaOfMethod(JNIEnv*, + jclass, + jlong art_method, + jint tries_count) { + // 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 alloc(Runtime::Current()->CreateLinearAlloc()); + for (int i = static_cast(tries_count); i > 0; --i) { + // Ask for a byte - it's sufficient to get an arena and not have issues with size. + alloc->Alloc(Thread::Current(), 1); + } + bool retval = alloc->Contains(reinterpret_cast(static_cast(art_method))); + + return retval; +} + +} // 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..a71b724876 --- /dev/null +++ b/test/616-cha-unloading/expected.txt @@ -0,0 +1,4 @@ +JNI_OnLoad called +null +true +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-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/616-cha-unloading/src/AbstractCHATester.java b/test/616-cha-unloading/src/AbstractCHATester.java new file mode 100644 index 0000000000..e11094584a --- /dev/null +++ b/test/616-cha-unloading/src/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/Main.java b/test/616-cha-unloading/src/Main.java new file mode 100644 index 0000000000..b633a0c22e --- /dev/null +++ b/test/616-cha-unloading/src/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 sConstructor; + + private static class CHAUnloaderRetType { + private CHAUnloaderRetType(WeakReference cl, + AbstractCHATester obj, + long methodPtr) { + this.cl = cl; + this.obj = obj; + this.methodPtr = methodPtr; + } + public WeakReference cl; + public AbstractCHATester obj; + public long methodPtr; + } + + public static void main(String[] args) throws Exception { + System.loadLibrary(args[0]); + + Class pathClassLoader = (Class) 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 loader = result.cl; + long methodPtr = result.methodPtr; + // Check that the classloader is indeed unloaded. + System.out.println(loader.get()); + + // Reuse the linear alloc so old pointers so it becomes invalid. + boolean ret = tryReuseArenaOfMethod(methodPtr, 10); + // Check that we indeed reused it. + System.out.println(ret); + + // 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, ""); + 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 boolean tryReuseArenaOfMethod(long artMethod, int tries_count); +} 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", -- GitLab From 6717e5fae190386273accaa638e42000fa46ffcb Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 12 Mar 2018 11:54:48 -0700 Subject: [PATCH 068/749] Write a wrapper to record logcat while another process is running It is often useful to have the device logcat from while our tests are running. Until now this had to be done manually. This adds a wrapper script that will run another script with logcat running in the background. The logcat is written to the '-o' or --output file. --logcat-invoke '' lets one control how the logcat is collected. The output of the command is piped to the logcat file. The command is killed with SIGTERM when the testing ends. Users should be very careful that everything is properly quoted. Test: ./tools/wrap-logcat.py -o /tmp/test-logcat \ --logcat-invoke 'adb logcat -v threadtime' \ './test/run-test --dev 001-HelloWorld' Bug: 74429665 Change-Id: I70527b5b9e1547aa1f97a217ec56fd1ffc0c235c --- tools/wrap-logcat.py | 59 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100755 tools/wrap-logcat.py diff --git a/tools/wrap-logcat.py b/tools/wrap-logcat.py new file mode 100755 index 0000000000..91bbb25c49 --- /dev/null +++ b/tools/wrap-logcat.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# +# 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 +# +# 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 argparse +import subprocess +import sys +import shlex + +def main(): + parser = argparse.ArgumentParser( + description='Runs a command while collecting logcat in the background') + parser.add_argument('--output', '-o', + type=lambda f: open(f,'w'), + required=True, + action='store', + help='File to store the logcat to. Will be created if not already existing') + parser.add_argument('--logcat-invoke', + action='store', + default='adb logcat', + help="""Command to run to retrieve logcat data. Defaults to 'adb logcat'. + It will be run in the background and killed by SIGTERM when the 'command' + finishes.""") + parser.add_argument('command', + action='store', + help='The command to run with logcat in the background.') + args = parser.parse_args() + # Send all output from logcat to the file. + with subprocess.Popen(shlex.split(args.logcat_invoke), + stdout=args.output, + stderr=subprocess.STDOUT, + shell=False, + universal_newlines=True) as logcat_proc: + # Let the run-test-proc inherit our stdout FDs + with subprocess.Popen(shlex.split(args.command), + stdout=None, + stderr=None, + shell=False) as run_test_proc: + # Don't actually do anything. Just let the run-test proc finish. + pass + # Send SIGTERM to the logcat process. + logcat_proc.kill() + return 0 + +if __name__ == '__main__': + sys.exit(main()) + -- GitLab From 4bfca1cc7bf6c213b546ee20a6f2e131fca4831c Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 12 Mar 2018 14:26:18 -0700 Subject: [PATCH 069/749] Give symbolize-buildbot-crashes.sh the ability to read from a file. symbolize-buildbot-crashes.sh would always read from adb logcat. Sometimes it is useful to read from a file though. This change lets the crash logs be given as the first argument. Test: manual Bug: 74429665 Change-Id: Idd09e8eb5117082c98860e98b9e8afa05fe83e77 --- tools/symbolize-buildbot-crashes.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/symbolize-buildbot-crashes.sh b/tools/symbolize-buildbot-crashes.sh index 8dc4e27885..0200346fa0 100755 --- a/tools/symbolize-buildbot-crashes.sh +++ b/tools/symbolize-buildbot-crashes.sh @@ -17,7 +17,11 @@ # We push art and its dependencies to '/data/local/tmp', but the 'stack' # script expect things to be in '/'. So we just remove the # '/data/local/tmp' prefix. -adb logcat -d | sed 's,/data/local/tmp,,g' | development/scripts/stack +if [[ -n "$1" ]]; then + cat $1 +else + adb logcat -d +fi | sed 's,/data/local/tmp,,g' | development/scripts/stack # Always return 0 to avoid having the buildbot complain about wrong stacks. exit 0 -- GitLab From c032251c472fb491c0c0d7396bfed302606a08a6 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Tue, 13 Mar 2018 10:33:52 -0700 Subject: [PATCH 070/749] Group testrunner options in --help testrunner.py --help was not very helpful. This groups related options together to hopefully make it somewhat more understandable what each option does. Test: manual Change-Id: I0b779964ecb1cf9b6ffc0aeb81650d0d9b1cb3d3 --- test/testrunner/testrunner.py | 61 +++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 3974ccb4f9..a2215f9e9b 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -927,33 +927,40 @@ def parse_option(): parser = argparse.ArgumentParser(description="Runs all or a subset of the ART test suite.") parser.add_argument('-t', '--test', action='append', dest='tests', help='name(s) of the test(s)') - parser.add_argument('-j', type=int, dest='n_thread') - parser.add_argument('--timeout', default=timeout, type=int, dest='timeout') - for variant in TOTAL_VARIANTS_SET: - flag = '--' + variant - parser.add_argument(flag, action='store_true', dest=variant) - parser.add_argument('--verbose', '-v', action='store_true', dest='verbose') - parser.add_argument('--dry-run', action='store_true', dest='dry_run') - parser.add_argument("--skip", action="append", dest="skips", default=[], - help="Skip the given test in all circumstances.") - parser.add_argument("--no-skips", dest="ignore_skips", action="store_true", default=False, - help="Don't skip any run-test configurations listed in knownfailures.json.") - parser.add_argument('--no-build-dependencies', - action='store_false', dest='build', - help="Don't build dependencies under any circumstances. This is the " + - "behavior if ART_TEST_RUN_TEST_ALWAYS_BUILD is not set to 'true'.") - parser.add_argument('-b', '--build-dependencies', - action='store_true', dest='build', - help="Build dependencies under all circumstances. By default we will " + - "not build dependencies unless ART_TEST_RUN_TEST_BUILD=true.") - parser.add_argument('--build-target', dest='build_target', help='master-art-host targets') - parser.set_defaults(build = env.ART_TEST_RUN_TEST_BUILD) - parser.add_argument('--gdb', action='store_true', dest='gdb') - parser.add_argument('--gdb-arg', dest='gdb_arg') - parser.add_argument('--dex2oat-jobs', type=int, dest='dex2oat_jobs', - help='Number of dex2oat jobs') - parser.add_argument('-a', '--all', action='store_true', dest='run_all', - help="Run all the possible configurations for the input test set") + global_group = parser.add_argument_group('Global options', + 'Options that affect all tests being run') + global_group.add_argument('-j', type=int, dest='n_thread') + global_group.add_argument('--timeout', default=timeout, type=int, dest='timeout') + global_group.add_argument('--verbose', '-v', action='store_true', dest='verbose') + global_group.add_argument('--dry-run', action='store_true', dest='dry_run') + global_group.add_argument("--skip", action="append", dest="skips", default=[], + help="Skip the given test in all circumstances.") + global_group.add_argument("--no-skips", dest="ignore_skips", action="store_true", default=False, + help="""Don't skip any run-test configurations listed in + knownfailures.json.""") + global_group.add_argument('--no-build-dependencies', + action='store_false', dest='build', + help="""Don't build dependencies under any circumstances. This is the + behavior if ART_TEST_RUN_TEST_ALWAYS_BUILD is not set to 'true'.""") + global_group.add_argument('-b', '--build-dependencies', + action='store_true', dest='build', + help="""Build dependencies under all circumstances. By default we will + not build dependencies unless ART_TEST_RUN_TEST_BUILD=true.""") + global_group.add_argument('--build-target', dest='build_target', help='master-art-host targets') + global_group.set_defaults(build = env.ART_TEST_RUN_TEST_BUILD) + global_group.add_argument('--gdb', action='store_true', dest='gdb') + global_group.add_argument('--gdb-arg', dest='gdb_arg') + global_group.add_argument('--dex2oat-jobs', type=int, dest='dex2oat_jobs', + help='Number of dex2oat jobs') + global_group.add_argument('-a', '--all', action='store_true', dest='run_all', + help="Run all the possible configurations for the input test set") + for variant_type, variant_set in VARIANT_TYPE_DICT.items(): + var_group = parser.add_argument_group( + '{}-type Options'.format(variant_type), + "Options that control the '{}' variants.".format(variant_type)) + for variant in variant_set: + flag = '--' + variant + var_group.add_argument(flag, action='store_true', dest=variant) options = vars(parser.parse_args()) if options['build_target']: -- GitLab From 99d8f895c03ce30f77c43d4db125424d45ce5286 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Tue, 13 Mar 2018 15:38:11 -0700 Subject: [PATCH 071/749] Make wrap-logcat a bit easier to call. Allow the command to be sent in as a non-quoted string. Test: ./tools/wrap-logcat.py -o /tmp/abc.txt ./test/run-test 001-Main Change-Id: Ia35c2090c7f0ad8b12ff913a3e1d608e01a56845 --- tools/wrap-logcat.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/wrap-logcat.py b/tools/wrap-logcat.py index 91bbb25c49..067890e870 100755 --- a/tools/wrap-logcat.py +++ b/tools/wrap-logcat.py @@ -35,8 +35,13 @@ def main(): finishes.""") parser.add_argument('command', action='store', + nargs=argparse.REMAINDER, help='The command to run with logcat in the background.') args = parser.parse_args() + if len(args.command) == 0: + print("Must have some command to run.", file=sys.stderr) + parser.print_help(file=sys.stderr) + return 1 # Send all output from logcat to the file. with subprocess.Popen(shlex.split(args.logcat_invoke), stdout=args.output, @@ -44,7 +49,7 @@ def main(): shell=False, universal_newlines=True) as logcat_proc: # Let the run-test-proc inherit our stdout FDs - with subprocess.Popen(shlex.split(args.command), + with subprocess.Popen(shlex.split(args.command[0]) if len(args.command) == 1 else args.command, stdout=None, stderr=None, shell=False) as run_test_proc: -- GitLab From 3a29355967f29999ba81b5772ec66d3ddee9ce3e Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 2 Mar 2018 10:52:16 +0000 Subject: [PATCH 072/749] Separate vdex versioning into two sections: verifier deps and dex code. The layout is still the same, but this CL allows updating the cdex and quickening encoding versions without having to update the verifier deps version. bug: 63920015 Test: test.py, dex2oat_test, oat_writer_test, oatdump_test Change-Id: Ifd282b4c1856f7597d8d26577bf58b9ad87da084 --- dex2oat/dex2oat.cc | 4 +- dex2oat/dex2oat_test.cc | 2 +- dex2oat/linker/oat_writer.cc | 57 ++++++---- dex2oat/linker/oat_writer_test.cc | 5 +- oatdump/oatdump.cc | 8 +- runtime/oat_file_assistant.cc | 2 +- runtime/vdex_file.cc | 59 +++++++---- runtime/vdex_file.h | 168 +++++++++++++++++++++--------- 8 files changed, 207 insertions(+), 98 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 73afbad184..9b6771d3b5 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1398,8 +1398,8 @@ class Dex2Oat FINAL { std::unique_ptr vdex_out = std::make_unique( std::make_unique(vdex_files_.back().get())); - if (!vdex_out->WriteFully(&VdexFile::Header::kVdexInvalidMagic, - arraysize(VdexFile::Header::kVdexInvalidMagic))) { + if (!vdex_out->WriteFully(&VdexFile::VerifierDepsHeader::kVdexInvalidMagic, + arraysize(VdexFile::VerifierDepsHeader::kVdexInvalidMagic))) { PLOG(ERROR) << "Failed to invalidate vdex header. File: " << vdex_out->GetLocation(); return false; } diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 96dd319946..094dfee3a6 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -1834,7 +1834,7 @@ TEST_F(Dex2oatTest, DontExtract) { /*unquicken*/ false, &error_msg)); ASSERT_TRUE(vdex != nullptr); - EXPECT_EQ(vdex->GetHeader().GetDexSize(), 0u) << output_; + EXPECT_FALSE(vdex->HasDexSection()) << output_; } std::unique_ptr odex_file(OatFile::Open(odex_location.c_str(), odex_location.c_str(), diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index c72beea6ce..089aa80ad4 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -555,8 +555,9 @@ bool OatWriter::AddVdexDexFilesSource(const VdexFile& vdex_file, const char* location, CreateTypeLookupTable create_type_lookup_table) { DCHECK(write_state_ == WriteState::kAddingDexFileSources); + DCHECK(vdex_file.HasDexSection()); const uint8_t* current_dex_data = nullptr; - for (size_t i = 0; i < vdex_file.GetHeader().GetNumberOfDexFiles(); ++i) { + for (size_t i = 0; i < vdex_file.GetVerifierDepsHeader().GetNumberOfDexFiles(); ++i) { current_dex_data = vdex_file.GetNextDexFileData(current_dex_data); if (current_dex_data == nullptr) { LOG(ERROR) << "Unexpected number of dex files in vdex " << location; @@ -659,7 +660,8 @@ bool OatWriter::WriteAndOpenDexFiles( // Initialize VDEX and OAT headers. // Reserve space for Vdex header and checksums. - vdex_size_ = sizeof(VdexFile::Header) + oat_dex_files_.size() * sizeof(VdexFile::VdexChecksum); + vdex_size_ = sizeof(VdexFile::VerifierDepsHeader) + + oat_dex_files_.size() * sizeof(VdexFile::VdexChecksum); oat_size_ = InitOatHeader(instruction_set, instruction_set_features, dchecked_integral_cast(oat_dex_files_.size()), @@ -3333,8 +3335,6 @@ bool OatWriter::WriteDexFiles(OutputStream* out, CopyOption copy_dex_files) { TimingLogger::ScopedTiming split("Write Dex files", timings_); - vdex_dex_files_offset_ = vdex_size_; - // If extraction is enabled, only do it if not all the dex files are aligned and uncompressed. if (copy_dex_files == CopyOption::kOnlyIfCompressed) { extract_dex_files_into_vdex_ = false; @@ -3357,6 +3357,9 @@ bool OatWriter::WriteDexFiles(OutputStream* out, } if (extract_dex_files_into_vdex_) { + // Add the dex section header. + vdex_size_ += sizeof(VdexFile::DexSectionHeader); + vdex_dex_files_offset_ = vdex_size_; // Write dex files. for (OatDexFile& oat_dex_file : oat_dex_files_) { if (!WriteDexFile(out, file, &oat_dex_file, update_input_vdex)) { @@ -4076,8 +4079,9 @@ bool OatWriter::WriteDexLayoutSections( bool OatWriter::WriteChecksumsAndVdexHeader(OutputStream* vdex_out) { // Write checksums - off_t actual_offset = vdex_out->Seek(sizeof(VdexFile::Header), kSeekSet); - if (actual_offset != sizeof(VdexFile::Header)) { + off_t checksums_offset = sizeof(VdexFile::VerifierDepsHeader); + off_t actual_offset = vdex_out->Seek(checksums_offset, kSeekSet); + if (actual_offset != checksums_offset) { PLOG(ERROR) << "Failed to seek to the checksum location of vdex file. Actual: " << actual_offset << " File: " << vdex_out->GetLocation(); return false; @@ -4094,6 +4098,27 @@ bool OatWriter::WriteChecksumsAndVdexHeader(OutputStream* vdex_out) { size_vdex_checksums_ += sizeof(VdexFile::VdexChecksum); } + // Maybe write dex section header. + DCHECK_NE(vdex_verifier_deps_offset_, 0u); + DCHECK_NE(vdex_quickening_info_offset_, 0u); + + bool has_dex_section = extract_dex_files_into_vdex_; + if (has_dex_section) { + DCHECK_NE(vdex_dex_files_offset_, 0u); + size_t dex_section_size = vdex_dex_shared_data_offset_ - vdex_dex_files_offset_; + size_t dex_shared_data_size = vdex_verifier_deps_offset_ - vdex_dex_shared_data_offset_; + size_t quickening_info_section_size = vdex_size_ - vdex_quickening_info_offset_; + + VdexFile::DexSectionHeader dex_section_header(dex_section_size, + dex_shared_data_size, + quickening_info_section_size); + if (!vdex_out->WriteFully(&dex_section_header, sizeof(VdexFile::DexSectionHeader))) { + PLOG(ERROR) << "Failed to write vdex header. File: " << vdex_out->GetLocation(); + return false; + } + size_vdex_header_ += sizeof(VdexFile::DexSectionHeader); + } + // Write header. actual_offset = vdex_out->Seek(0, kSeekSet); if (actual_offset != 0) { @@ -4102,25 +4127,15 @@ bool OatWriter::WriteChecksumsAndVdexHeader(OutputStream* vdex_out) { return false; } - DCHECK_NE(vdex_dex_files_offset_, 0u); - DCHECK_NE(vdex_verifier_deps_offset_, 0u); - DCHECK_NE(vdex_quickening_info_offset_, 0u); - - size_t dex_section_size = vdex_dex_shared_data_offset_ - vdex_dex_files_offset_; - size_t dex_shared_data_size = vdex_verifier_deps_offset_ - vdex_dex_shared_data_offset_; size_t verifier_deps_section_size = vdex_quickening_info_offset_ - vdex_verifier_deps_offset_; - size_t quickening_info_section_size = vdex_size_ - vdex_quickening_info_offset_; - - VdexFile::Header vdex_header(oat_dex_files_.size(), - dex_section_size, - dex_shared_data_size, - verifier_deps_section_size, - quickening_info_section_size); - if (!vdex_out->WriteFully(&vdex_header, sizeof(VdexFile::Header))) { + + VdexFile::VerifierDepsHeader deps_header( + oat_dex_files_.size(), verifier_deps_section_size, has_dex_section); + if (!vdex_out->WriteFully(&deps_header, sizeof(VdexFile::VerifierDepsHeader))) { PLOG(ERROR) << "Failed to write vdex header. File: " << vdex_out->GetLocation(); return false; } - size_vdex_header_ = sizeof(VdexFile::Header); + size_vdex_header_ += sizeof(VdexFile::VerifierDepsHeader); if (!vdex_out->Flush()) { PLOG(ERROR) << "Failed to flush stream after writing to vdex file." diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 00b9abe69b..0148a5792c 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -663,7 +663,8 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) { dex_file2_data->GetHeader().file_size_)); ASSERT_EQ(dex_file2_data->GetLocation(), opened_dex_file2->GetLocation()); - const VdexFile::Header &vdex_header = opened_oat_file->GetVdexFile()->GetHeader(); + const VdexFile::DexSectionHeader &vdex_header = + opened_oat_file->GetVdexFile()->GetDexSectionHeader(); if (!compiler_driver_->GetCompilerOptions().IsQuickeningCompilationEnabled()) { // If quickening is enabled we will always write the table since there is no special logic that // checks for all methods not being quickened (not worth the complexity). @@ -672,7 +673,7 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) { int64_t actual_vdex_size = vdex_file.GetFile()->GetLength(); ASSERT_GE(actual_vdex_size, 0); - ASSERT_EQ((uint64_t) actual_vdex_size, vdex_header.GetComputedFileSize()); + ASSERT_EQ((uint64_t) actual_vdex_size, opened_oat_file->GetVdexFile()->GetComputedFileSize()); } TEST_F(OatTest, DexFileInputCheckOutput) { diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 8069408b5a..9a49fb4752 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -567,7 +567,7 @@ class OatDumper { if (!options_.dump_header_only_) { VariableIndentationOutputStream vios(&os); - VdexFile::Header vdex_header = oat_file_.GetVdexFile()->GetHeader(); + VdexFile::VerifierDepsHeader vdex_header = oat_file_.GetVdexFile()->GetVerifierDepsHeader(); if (vdex_header.IsValid()) { std::string error_msg; std::vector dex_files; @@ -584,8 +584,10 @@ class OatDumper { } else { os << "UNRECOGNIZED vdex file, magic " << vdex_header.GetMagic() - << ", version " - << vdex_header.GetVersion() + << ", verifier deps version " + << vdex_header.GetVerifierDepsVersion() + << ", dex section version " + << vdex_header.GetDexSectionVersion() << "\n"; } for (size_t i = 0; i < oat_dex_files_.size(); i++) { diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 5888c37582..38ca4c9623 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -408,7 +408,7 @@ bool OatFileAssistant::DexChecksumUpToDate(const VdexFile& file, std::string* er return true; } - uint32_t number_of_dex_files = file.GetHeader().GetNumberOfDexFiles(); + uint32_t number_of_dex_files = file.GetVerifierDepsHeader().GetNumberOfDexFiles(); if (required_dex_checksums->size() != number_of_dex_files) { *error_msg = StringPrintf("expected %zu dex files but found %u", required_dex_checksums->size(), diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index ba640556df..ec4dc417d3 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -36,32 +36,52 @@ namespace art { -constexpr uint8_t VdexFile::Header::kVdexInvalidMagic[4]; -constexpr uint8_t VdexFile::Header::kVdexMagic[4]; -constexpr uint8_t VdexFile::Header::kVdexVersion[4]; +constexpr uint8_t VdexFile::VerifierDepsHeader::kVdexInvalidMagic[4]; +constexpr uint8_t VdexFile::VerifierDepsHeader::kVdexMagic[4]; +constexpr uint8_t VdexFile::VerifierDepsHeader::kVerifierDepsVersion[4]; +constexpr uint8_t VdexFile::VerifierDepsHeader::kDexSectionVersion[4]; +constexpr uint8_t VdexFile::VerifierDepsHeader::kDexSectionVersionEmpty[4]; -bool VdexFile::Header::IsMagicValid() const { +bool VdexFile::VerifierDepsHeader::IsMagicValid() const { return (memcmp(magic_, kVdexMagic, sizeof(kVdexMagic)) == 0); } -bool VdexFile::Header::IsVersionValid() const { - return (memcmp(version_, kVdexVersion, sizeof(kVdexVersion)) == 0); +bool VdexFile::VerifierDepsHeader::IsVerifierDepsVersionValid() const { + return (memcmp(verifier_deps_version_, kVerifierDepsVersion, sizeof(kVerifierDepsVersion)) == 0); } -VdexFile::Header::Header(uint32_t number_of_dex_files, - uint32_t dex_size, - uint32_t dex_shared_data_size, - uint32_t verifier_deps_size, - uint32_t quickening_info_size) +bool VdexFile::VerifierDepsHeader::IsDexSectionVersionValid() const { + return (memcmp(dex_section_version_, kDexSectionVersion, sizeof(kDexSectionVersion)) == 0) || + (memcmp(dex_section_version_, kDexSectionVersionEmpty, sizeof(kDexSectionVersionEmpty)) == 0); +} + +bool VdexFile::VerifierDepsHeader::HasDexSection() const { + return (memcmp(dex_section_version_, kDexSectionVersion, sizeof(kDexSectionVersion)) == 0); +} + +VdexFile::VerifierDepsHeader::VerifierDepsHeader(uint32_t number_of_dex_files, + uint32_t verifier_deps_size, + bool has_dex_section) : number_of_dex_files_(number_of_dex_files), - dex_size_(dex_size), - dex_shared_data_size_(dex_shared_data_size), - verifier_deps_size_(verifier_deps_size), - quickening_info_size_(quickening_info_size) { + verifier_deps_size_(verifier_deps_size) { memcpy(magic_, kVdexMagic, sizeof(kVdexMagic)); - memcpy(version_, kVdexVersion, sizeof(kVdexVersion)); + memcpy(verifier_deps_version_, kVerifierDepsVersion, sizeof(kVerifierDepsVersion)); + if (has_dex_section) { + memcpy(dex_section_version_, kDexSectionVersion, sizeof(kDexSectionVersion)); + } else { + memcpy(dex_section_version_, kDexSectionVersionEmpty, sizeof(kDexSectionVersionEmpty)); + } DCHECK(IsMagicValid()); - DCHECK(IsVersionValid()); + DCHECK(IsVerifierDepsVersionValid()); + DCHECK(IsDexSectionVersionValid()); +} + +VdexFile::DexSectionHeader::DexSectionHeader(uint32_t dex_size, + uint32_t dex_shared_data_size, + uint32_t quickening_info_size) + : dex_size_(dex_size), + dex_shared_data_size_(dex_shared_data_size), + quickening_info_size_(quickening_info_size) { } std::unique_ptr VdexFile::OpenAtAddress(uint8_t* mmap_addr, @@ -145,7 +165,7 @@ std::unique_ptr VdexFile::OpenAtAddress(uint8_t* mmap_addr, return nullptr; } - if (unquicken) { + if (unquicken && vdex->HasDexSection()) { std::vector> unique_ptr_dex_files; if (!vdex->OpenAllDexFiles(&unique_ptr_dex_files, error_msg)) { return nullptr; @@ -153,7 +173,8 @@ std::unique_ptr VdexFile::OpenAtAddress(uint8_t* mmap_addr, vdex->Unquicken(MakeNonOwningPointerVector(unique_ptr_dex_files), /* decompile_return_instruction */ false); // Update the quickening info size to pretend there isn't any. - reinterpret_cast(vdex->mmap_->Begin())->quickening_info_size_ = 0; + size_t offset = vdex->GetDexSectionHeaderOffset(); + reinterpret_cast(vdex->mmap_->Begin() + offset)->quickening_info_size_ = 0; } *error_msg = "Success"; diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index 72f03f266a..77e1f2ccfe 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -35,51 +35,53 @@ class DexFile; // memory and provides tools for accessing its individual sections. // // File format: -// VdexFile::Header fixed-length header +// VdexFile::VerifierDepsHeader fixed-length header +// Dex file checksums +// +// Optionally: +// VdexFile::DexSectionHeader fixed-length header +// +// quicken_table_off[0] offset into QuickeningInfo section for offset table for DEX[0]. +// DEX[0] array of the input DEX files, the bytecode may have been quickened. +// quicken_table_off[1] +// DEX[1] +// ... +// DEX[D] // -// quicken_table_off[0] offset into QuickeningInfo section for offset table for DEX[0]. -// DEX[0] array of the input DEX files, the bytecode may have been quickened. -// quicken_table_off[1] -// DEX[1] -// ... -// DEX[D] // VerifierDeps // uint8[D][] verification dependencies -// QuickeningInfo -// uint8[D][] quickening data -// uint32[D][] quickening data offset tables +// +// Optionally: +// QuickeningInfo +// uint8[D][] quickening data +// uint32[D][] quickening data offset tables class VdexFile { public: - struct Header { + struct VerifierDepsHeader { public: - Header(uint32_t number_of_dex_files_, - uint32_t dex_size, - uint32_t dex_shared_data_size, - uint32_t verifier_deps_size, - uint32_t quickening_info_size); + VerifierDepsHeader(uint32_t number_of_dex_files_, + uint32_t verifier_deps_size, + bool has_dex_section); const char* GetMagic() const { return reinterpret_cast(magic_); } - const char* GetVersion() const { return reinterpret_cast(version_); } + const char* GetVerifierDepsVersion() const { + return reinterpret_cast(verifier_deps_version_); + } + const char* GetDexSectionVersion() const { + return reinterpret_cast(dex_section_version_); + } bool IsMagicValid() const; - bool IsVersionValid() const; - bool IsValid() const { return IsMagicValid() && IsVersionValid(); } + bool IsVerifierDepsVersionValid() const; + bool IsDexSectionVersionValid() const; + bool IsValid() const { + return IsMagicValid() && IsVerifierDepsVersionValid() && IsDexSectionVersionValid(); + } + bool HasDexSection() const; - uint32_t GetDexSize() const { return dex_size_; } - uint32_t GetDexSharedDataSize() const { return dex_shared_data_size_; } uint32_t GetVerifierDepsSize() const { return verifier_deps_size_; } - uint32_t GetQuickeningInfoSize() const { return quickening_info_size_; } uint32_t GetNumberOfDexFiles() const { return number_of_dex_files_; } - size_t GetComputedFileSize() const { - return sizeof(Header) + - GetSizeOfChecksumsSection() + - GetDexSize() + - GetDexSharedDataSize() + - GetVerifierDepsSize() + - GetQuickeningInfoSize(); - } - size_t GetSizeOfChecksumsSection() const { return sizeof(VdexChecksum) * GetNumberOfDexFiles(); } @@ -88,20 +90,62 @@ class VdexFile { private: static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' }; - // Last update: Change quickening info table format. - static constexpr uint8_t kVdexVersion[] = { '0', '1', '8', '\0' }; + + // The format version of the verifier deps header and the verifier deps. + // Last update: Add DexSectionHeader + static constexpr uint8_t kVerifierDepsVersion[] = { '0', '1', '9', '\0' }; + + // 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' }; + + // If the .vdex file has no dex section (hence no dex code nor quickening data), + // we encode this magic version. + static constexpr uint8_t kDexSectionVersionEmpty[] = { '0', '0', '0', '\0' }; uint8_t magic_[4]; - uint8_t version_[4]; + uint8_t verifier_deps_version_[4]; + uint8_t dex_section_version_[4]; uint32_t number_of_dex_files_; + uint32_t verifier_deps_size_; + }; + + struct DexSectionHeader { + public: + DexSectionHeader(uint32_t dex_size, + uint32_t dex_shared_data_size, + uint32_t quickening_info_size); + + uint32_t GetDexSize() const { return dex_size_; } + uint32_t GetDexSharedDataSize() const { return dex_shared_data_size_; } + uint32_t GetQuickeningInfoSize() const { return quickening_info_size_; } + + size_t GetDexSectionSize() const { + return sizeof(DexSectionHeader) + + GetDexSize() + + GetDexSharedDataSize(); + } + + private: uint32_t dex_size_; uint32_t dex_shared_data_size_; - uint32_t verifier_deps_size_; uint32_t quickening_info_size_; - friend class VdexFile; + friend class VdexFile; // For updatig quickening_info_size_. }; + size_t GetComputedFileSize() const { + size_t size = sizeof(VerifierDepsHeader); + const VerifierDepsHeader& header = GetVerifierDepsHeader(); + size += header.GetVerifierDepsSize(); + size += header.GetSizeOfChecksumsSection(); + if (header.HasDexSection()) { + size += GetDexSectionHeader().GetDexSectionSize(); + size += GetDexSectionHeader().GetQuickeningInfoSize(); + } + return size; + } + // Note: The file is called "primary" to match the naming with profiles. static const constexpr char* kVdexNameInDmFile = "primary.vdex"; @@ -174,24 +218,48 @@ class VdexFile { const uint8_t* End() const { return mmap_->End(); } size_t Size() const { return mmap_->Size(); } - const Header& GetHeader() const { - return *reinterpret_cast(Begin()); + const VerifierDepsHeader& GetVerifierDepsHeader() const { + return *reinterpret_cast(Begin()); + } + + uint32_t GetDexSectionHeaderOffset() const { + return sizeof(VerifierDepsHeader) + GetVerifierDepsHeader().GetSizeOfChecksumsSection(); + } + + const DexSectionHeader& GetDexSectionHeader() const { + DCHECK(GetVerifierDepsHeader().HasDexSection()); + return *reinterpret_cast(Begin() + GetDexSectionHeaderOffset()); + } + + const uint8_t* GetVerifierDepsStart() const { + const uint8_t* result = Begin() + GetDexSectionHeaderOffset(); + if (GetVerifierDepsHeader().HasDexSection()) { + // When there is a dex section, the verifier deps are after it, but before the quickening. + return result + GetDexSectionHeader().GetDexSectionSize(); + } else { + // When there is no dex section, the verifier deps are just after the header. + return result; + } } ArrayRef GetVerifierDepsData() const { return ArrayRef( - DexBegin() + GetHeader().GetDexSize() + GetHeader().GetDexSharedDataSize(), - GetHeader().GetVerifierDepsSize()); + GetVerifierDepsStart(), + GetVerifierDepsHeader().GetVerifierDepsSize()); } ArrayRef GetQuickeningInfo() const { - return ArrayRef( - GetVerifierDepsData().data() + GetHeader().GetVerifierDepsSize(), - GetHeader().GetQuickeningInfoSize()); + if (GetVerifierDepsHeader().HasDexSection()) { + return ArrayRef( + GetVerifierDepsData().data() + GetVerifierDepsHeader().GetVerifierDepsSize(), + GetDexSectionHeader().GetQuickeningInfoSize()); + } else { + return ArrayRef(); + } } bool IsValid() const { - return mmap_->Size() >= sizeof(Header) && GetHeader().IsValid(); + return mmap_->Size() >= sizeof(VerifierDepsHeader) && GetVerifierDepsHeader().IsValid(); } // This method is for iterating over the dex files in the vdex. If `cursor` is null, @@ -202,8 +270,8 @@ class VdexFile { // Get the location checksum of the dex file number `dex_file_index`. uint32_t GetLocationChecksum(uint32_t dex_file_index) const { - DCHECK_LT(dex_file_index, GetHeader().GetNumberOfDexFiles()); - return reinterpret_cast(Begin() + sizeof(Header))[dex_file_index]; + DCHECK_LT(dex_file_index, GetVerifierDepsHeader().GetNumberOfDexFiles()); + return reinterpret_cast(Begin() + sizeof(VerifierDepsHeader))[dex_file_index]; } // Open all the dex files contained in this vdex file. @@ -228,7 +296,7 @@ class VdexFile { uint32_t dex_method_idx) const; bool HasDexSection() const { - return GetHeader().GetDexSize() != 0; + return GetVerifierDepsHeader().HasDexSection(); } private: @@ -250,11 +318,13 @@ class VdexFile { bool ContainsDexFile(const DexFile& dex_file) const; const uint8_t* DexBegin() const { - return Begin() + sizeof(Header) + GetHeader().GetSizeOfChecksumsSection(); + DCHECK(HasDexSection()); + return Begin() + GetDexSectionHeaderOffset() + sizeof(DexSectionHeader); } const uint8_t* DexEnd() const { - return DexBegin() + GetHeader().GetDexSize(); + DCHECK(HasDexSection()); + return DexBegin() + GetDexSectionHeader().GetDexSize(); } std::unique_ptr mmap_; -- GitLab From fac21782ed87ce800813cfa0f9b4ee4bb4aac462 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 13 Mar 2018 17:01:09 +0000 Subject: [PATCH 073/749] Fix delivering async exception while in compiled code. Change artInstrumentationMethodExitFromCode() to check for async exceptions. Also fix kDebugExceptionDelivery = true causing stale ObjPtr<> use. Test: run-test --jit -runtime-option -Xjitthreshold:0 \ 1934-jvmti-signal-thread Test: Repeat with kDebugExceptionDelivery = true. Bug: 74583459 Change-Id: Ia2e777cc3ccef4eba549aa290f8bc12608eafe44 --- runtime/entrypoints/quick/quick_trampoline_entrypoints.cc | 2 +- runtime/quick_exception_handler.cc | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index ac67bb30be..e9a0808843 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -1169,7 +1169,7 @@ extern "C" TwoWordReturn artInstrumentationMethodExitFromCode(Thread* self, instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); TwoWordReturn return_or_deoptimize_pc = instrumentation->PopInstrumentationStackFrame( self, return_pc, gpr_result, fpr_result); - if (self->IsExceptionPending()) { + if (self->IsExceptionPending() || self->ObserveAsyncException()) { return GetTwoWordFailureValue(); } return return_or_deoptimize_pc; diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 006405f095..077aa33925 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -143,14 +143,14 @@ class CatchBlockStackVisitor FINAL : public StackVisitor { void QuickExceptionHandler::FindCatch(ObjPtr exception) { DCHECK(!is_deoptimization_); + StackHandleScope<1> hs(self_); + Handle exception_ref(hs.NewHandle(exception)); if (kDebugExceptionDelivery) { - mirror::String* msg = exception->GetDetailMessage(); + ObjPtr msg = exception_ref->GetDetailMessage(); std::string str_msg(msg != nullptr ? msg->ToModifiedUtf8() : ""); - self_->DumpStack(LOG_STREAM(INFO) << "Delivering exception: " << exception->PrettyTypeOf() + self_->DumpStack(LOG_STREAM(INFO) << "Delivering exception: " << exception_ref->PrettyTypeOf() << ": " << str_msg << "\n"); } - StackHandleScope<1> hs(self_); - Handle exception_ref(hs.NewHandle(exception)); // Walk the stack to find catch handler. CatchBlockStackVisitor visitor(self_, context_, &exception_ref, this); -- GitLab From bdac3381d88d43afb7d55dc3ba3c8738927034a6 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Mon, 12 Mar 2018 15:08:50 +0000 Subject: [PATCH 074/749] Move gcstress test configurations to use the CC collector. These test configurations used to use the SS (semi-space) collector: - art-gcstress-gcverify - art-interpreter-gcstress - art-optimizing-gcstress - art-jit-gcstress Have them use the CC (concurrent copying) collector instead, as it is our main collector now. Test: art/test/testrunner/run_build_test_target.py art-gcstress-gcverify Test: art/test/testrunner/run_build_test_target.py art-interpreter-gcstress Test: art/test/testrunner/run_build_test_target.py art-optimizing-gcstress Test: art/test/testrunner/run_build_test_target.py art-jit-gcstress Bug: 62611253 Change-Id: I04fe75c00e10a3a6926460d8728411fac220c7e6 --- test/testrunner/target_config.py | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index 9d0377510a..40dc02e4b0 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -70,36 +70,22 @@ target_config = { }, 'art-gcstress-gcverify': { 'run-test': ['--gcstress', - '--gcverify'], - 'env' : { - 'ART_USE_READ_BARRIER' : 'false', - 'ART_DEFAULT_GC_TYPE' : 'SS' - } + '--gcverify'] }, 'art-interpreter-gcstress' : { 'run-test' : ['--interpreter', - '--gcstress'], - 'env' : { - 'ART_USE_READ_BARRIER' : 'false', - 'ART_DEFAULT_GC_TYPE' : 'SS' - } + '--gcstress'] }, 'art-optimizing-gcstress' : { 'run-test' : ['--gcstress', - '--optimizing'], - 'env' : { - 'ART_USE_READ_BARRIER' : 'false', - 'ART_DEFAULT_GC_TYPE' : 'SS' - } + '--optimizing'] }, 'art-jit-gcstress' : { 'run-test' : ['--jit', - '--gcstress'], - 'env' : { - 'ART_USE_READ_BARRIER' : 'false', - 'ART_DEFAULT_GC_TYPE' : 'SS' - } + '--gcstress'] }, + # TODO: Rename or repurpose this configuration as + # 'art-read-barrier-heap-poisoning' (b/62611253). 'art-read-barrier' : { 'run-test': ['--interpreter', '--optimizing'], @@ -108,6 +94,9 @@ target_config = { 'ART_HEAP_POISONING' : 'true' } }, + # TODO: Remove or disable this configuration, as it is now covered + # by 'art-interpreter-gcstress' and 'art-optimizing-gcstress' -- + # except for heap poisoning, but that's fine (b/62611253). 'art-read-barrier-gcstress' : { 'run-test' : ['--interpreter', '--optimizing', -- GitLab From 695de8dba3053c426f2c7a214b2f3acd06723648 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Tue, 13 Mar 2018 13:37:47 +0000 Subject: [PATCH 075/749] Remove superfluous environment variables in test configurations. The read barrier configuration is enabled by default, so we can remove all environment definitions setting `ART_USE_READ_BARRIER` to `true`. Test: art/test/testrunner/run_build_test_target.py art-test Test: art/test/testrunner/run_build_test_target.py art-ndebug Test: art/test/testrunner/run_build_test_target.py art-interpreter Test: art/test/testrunner/run_build_test_target.py art-interpreter-access-checks Test: art/test/testrunner/run_build_test_target.py art-jit Test: art/test/testrunner/run_build_test_target.py art-pictest Test: art/test/testrunner/run_build_test_target.py art-gcstress-gcverify Test: art/test/testrunner/run_build_test_target.py art-read-barrier Test: art/test/testrunner/run_build_test_target.py art-read-barrier-gcstress Test: art/test/testrunner/run_build_test_target.py art-read-barrier-table-lookup Test: art/test/testrunner/run_build_test_target.py art-tracing Test: art/test/testrunner/run_build_test_target.py art-interpreter-tracing Test: art/test/testrunner/run_build_test_target.py art-forcecopy Test: art/test/testrunner/run_build_test_target.py art-no-prebuild Test: art/test/testrunner/run_build_test_target.py art-no-image Test: art/test/testrunner/run_build_test_target.py art-interpreter-no-image Test: art/test/testrunner/run_build_test_target.py art-relocate-no-patchoat Test: art/test/testrunner/run_build_test_target.py art-no-dex2oat Test: art/test/testrunner/run_build_test_target.py art-preopt Test: art/test/testrunner/run_build_test_target.py art-gtest Test: art/test/testrunner/run_build_test_target.py art-gtest-read-barrier Test: art/test/testrunner/run_build_test_target.py art-gtest-read-barrier-table-lookup Bug: 62611253 Change-Id: I9926943631927b2056aa51ebaeb3ace263d50c1c --- test/testrunner/target_config.py | 85 ++++++-------------------------- 1 file changed, 16 insertions(+), 69 deletions(-) diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index 40dc02e4b0..b323ddc9cb 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -24,10 +24,7 @@ target_config = { 'art-test' : { 'make' : 'test-art-host-gtest', - 'run-test' : [], - 'env' : { - 'ART_USE_READ_BARRIER' : 'true' - } + 'run-test' : [] }, 'art-test-javac' : { @@ -38,35 +35,20 @@ target_config = { # (calls testrunner which builds and then runs the test targets) 'art-ndebug' : { - 'run-test' : ['--ndebug'], - 'env' : { - 'ART_USE_READ_BARRIER' : 'true' - } + 'run-test' : ['--ndebug'] }, 'art-interpreter' : { - 'run-test' : ['--interpreter'], - 'env' : { - 'ART_USE_READ_BARRIER' : 'true' - } + 'run-test' : ['--interpreter'] }, 'art-interpreter-access-checks' : { - 'run-test' : ['--interp-ac'], - 'env' : { - 'ART_USE_READ_BARRIER' : 'true' - } + 'run-test' : ['--interp-ac'] }, 'art-jit' : { - 'run-test' : ['--jit'], - 'env' : { - 'ART_USE_READ_BARRIER' : 'true' - } + 'run-test' : ['--jit'] }, 'art-pictest' : { 'run-test' : ['--pictest', - '--optimizing'], - 'env' : { - 'ART_USE_READ_BARRIER' : 'true' - } + '--optimizing'] }, 'art-gcstress-gcverify': { 'run-test': ['--gcstress', @@ -90,7 +72,6 @@ target_config = { 'run-test': ['--interpreter', '--optimizing'], 'env' : { - 'ART_USE_READ_BARRIER' : 'true', 'ART_HEAP_POISONING' : 'true' } }, @@ -102,7 +83,6 @@ target_config = { '--optimizing', '--gcstress'], 'env' : { - 'ART_USE_READ_BARRIER' : 'true', 'ART_HEAP_POISONING' : 'true' } }, @@ -110,7 +90,6 @@ target_config = { 'run-test' : ['--interpreter', '--optimizing'], 'env' : { - 'ART_USE_READ_BARRIER' : 'true', 'ART_READ_BARRIER_TYPE' : 'TABLELOOKUP', 'ART_HEAP_POISONING' : 'true' } @@ -162,54 +141,30 @@ target_config = { } }, 'art-tracing' : { - 'run-test' : ['--trace'], - 'env' : { - 'ART_USE_READ_BARRIER' : 'true' - } + 'run-test' : ['--trace'] }, 'art-interpreter-tracing' : { 'run-test' : ['--interpreter', - '--trace'], - 'env' : { - 'ART_USE_READ_BARRIER' : 'true', - } + '--trace'] }, 'art-forcecopy' : { - 'run-test' : ['--forcecopy'], - 'env' : { - 'ART_USE_READ_BARRIER' : 'true', - } + 'run-test' : ['--forcecopy'] }, 'art-no-prebuild' : { - 'run-test' : ['--no-prebuild'], - 'env' : { - 'ART_USE_READ_BARRIER' : 'true', - } + 'run-test' : ['--no-prebuild'] }, 'art-no-image' : { - 'run-test' : ['--no-image'], - 'env' : { - 'ART_USE_READ_BARRIER' : 'true', - } + 'run-test' : ['--no-image'] }, 'art-interpreter-no-image' : { 'run-test' : ['--interpreter', - '--no-image'], - 'env' : { - 'ART_USE_READ_BARRIER' : 'true', - } + '--no-image'] }, 'art-relocate-no-patchoat' : { - 'run-test' : ['--relocate-npatchoat'], - 'env' : { - 'ART_USE_READ_BARRIER' : 'true', - } + 'run-test' : ['--relocate-npatchoat'] }, 'art-no-dex2oat' : { - 'run-test' : ['--no-dex2oat'], - 'env' : { - 'ART_USE_READ_BARRIER' : 'true', - } + 'run-test' : ['--no-dex2oat'] }, 'art-heap-poisoning' : { 'run-test' : ['--interpreter', @@ -229,32 +184,24 @@ target_config = { 'run-test' : ['--pictest', '--prebuild', '--relocate', - '--jit'], - 'env' : { - 'ART_USE_READ_BARRIER' : 'true' - } + '--jit'] }, # ART gtest configurations # (calls make 'target' which builds and then runs the gtests). 'art-gtest' : { - 'make' : 'test-art-host-gtest', - 'env' : { - 'ART_USE_READ_BARRIER' : 'true' - } + 'make' : 'test-art-host-gtest' }, 'art-gtest-read-barrier': { 'make' : 'test-art-host-gtest', 'env' : { - 'ART_USE_READ_BARRIER' : 'true', 'ART_HEAP_POISONING' : 'true' } }, 'art-gtest-read-barrier-table-lookup': { 'make' : 'test-art-host-gtest', 'env': { - 'ART_USE_READ_BARRIER' : 'true', 'ART_READ_BARRIER_TYPE' : 'TABLELOOKUP', 'ART_HEAP_POISONING' : 'true' } -- GitLab From a28c9980102dff32d29f9d8df172d793faa0da8a Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Mon, 30 Oct 2017 13:14:28 +0000 Subject: [PATCH 076/749] Makes some ART gtests more robust to a non-standard environment. Two gtests (dexdump_test and dexlist_test) used to try to write a file in the root directory and expected this operation to fail. However, in a non-standard environment (e.g. a testing context where such a gtest is allowed to write in `/`), these tests would not fail as expected. Replace these paths with more explicit ones not depending on writing permissions. Test: Run ART gtest dexdump_test and dexlist_test in a chroot on device. Bug: 74730596 Bug: 34729697 Change-Id: I23537757639721133d694d5366cf05d9009f05ad --- dexdump/dexdump_test.cc | 2 +- dexlist/dexlist_test.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dexdump/dexdump_test.cc b/dexdump/dexdump_test.cc index 63e0e3f20d..3a2d38dec6 100644 --- a/dexdump/dexdump_test.cc +++ b/dexdump/dexdump_test.cc @@ -58,7 +58,7 @@ TEST_F(DexDumpTest, NoInputFileGiven) { TEST_F(DexDumpTest, CantOpenOutput) { std::string error_msg; - ASSERT_FALSE(Exec({"-o", "/joho", dex_file_}, &error_msg)) << error_msg; + ASSERT_FALSE(Exec({"-o", "/non/existent/path", dex_file_}, &error_msg)) << error_msg; } TEST_F(DexDumpTest, BadFlagCombination) { diff --git a/dexlist/dexlist_test.cc b/dexlist/dexlist_test.cc index 0b9adbddb8..68e67134cc 100644 --- a/dexlist/dexlist_test.cc +++ b/dexlist/dexlist_test.cc @@ -60,7 +60,7 @@ TEST_F(DexListTest, NoInputFileGiven) { TEST_F(DexListTest, CantOpenOutput) { std::string error_msg; - ASSERT_FALSE(Exec({"-o", "/joho", dex_file_}, &error_msg)) << error_msg; + ASSERT_FALSE(Exec({"-o", "/non/existent/path", dex_file_}, &error_msg)) << error_msg; } TEST_F(DexListTest, IllFormedMethod) { -- GitLab From 812fb4d31895727e2bfbff4f3beefbea8e42948f Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 14 Mar 2018 13:07:21 +0000 Subject: [PATCH 077/749] Dump .data.bimg.rel.ro entries in oatdump. Also dump .bss summary. Test: m test-art-host-gtest-oatdump_app_test Test: m test-art-host-gtest-oatdump_image_test Test: Manually inspect output with/without --boot-image. Bug: 71526895 Bug: 65737953 Change-Id: I53914bed59a95ae342f2bbcb4ece592f3a631842 --- oatdump/oatdump.cc | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index dbd90cc292..574343f145 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -514,6 +514,18 @@ class OatDumper { os << StringPrintf("0x%08x\n\n", resolved_addr2instr_); } + // Dump .data.bimg.rel.ro entries. + DumpDataBimgRelRoEntries(os); + + // Dump .bss summary, individual entries are dumped per dex file. + os << ".bss: "; + if (oat_file_.GetBssMethods().empty() && oat_file_.GetBssGcRoots().empty()) { + os << "empty.\n\n"; + } else { + os << oat_file_.GetBssMethods().size() << " methods, "; + os << oat_file_.GetBssGcRoots().size() << " GC roots.\n\n"; + } + // Dumping the dex file overview is compact enough to do even if header only. DexFileData cumulative; for (size_t i = 0; i < oat_dex_files_.size(); i++) { @@ -1906,6 +1918,66 @@ class OatDumper { } } + void DumpDataBimgRelRoEntries(std::ostream& os) { + os << ".data.bimg.rel.ro: "; + if (oat_file_.GetBootImageRelocations().empty()) { + os << "empty.\n\n"; + return; + } + + os << oat_file_.GetBootImageRelocations().size() << " entries.\n"; + Runtime* runtime = Runtime::Current(); + if (runtime != nullptr && !runtime->GetHeap()->GetBootImageSpaces().empty()) { + const std::vector& boot_image_spaces = + runtime->GetHeap()->GetBootImageSpaces(); + ScopedObjectAccess soa(Thread::Current()); + for (const uint32_t& object_offset : oat_file_.GetBootImageRelocations()) { + uint32_t entry_index = &object_offset - oat_file_.GetBootImageRelocations().data(); + uint32_t entry_offset = entry_index * sizeof(oat_file_.GetBootImageRelocations()[0]); + os << StringPrintf(" 0x%x: 0x%08x", entry_offset, object_offset); + uint8_t* object = boot_image_spaces[0]->Begin() + object_offset; + bool found = false; + for (gc::space::ImageSpace* space : boot_image_spaces) { + uint64_t local_offset = object - space->Begin(); + if (local_offset < space->GetImageHeader().GetImageSize()) { + if (space->GetImageHeader().GetObjectsSection().Contains(local_offset)) { + ObjPtr o = reinterpret_cast(object); + if (o->IsString()) { + os << " String: " << o->AsString()->ToModifiedUtf8(); + } else if (o->IsClass()) { + os << " Class: " << o->AsClass()->PrettyDescriptor(); + } else { + os << StringPrintf(" 0x%08x %s", + object_offset, + o->GetClass()->PrettyDescriptor().c_str()); + } + } else if (space->GetImageHeader().GetMethodsSection().Contains(local_offset)) { + ArtMethod* m = reinterpret_cast(object); + os << " ArtMethod: " << m->PrettyMethod(); + } else { + os << StringPrintf(" 0x%08x ", + object_offset, + space->GetImageFilename().c_str()); + } + found = true; + break; + } + } + if (!found) { + os << StringPrintf(" 0x%08x ", object_offset); + } + os << "\n"; + } + } else { + for (const uint32_t& object_offset : oat_file_.GetBootImageRelocations()) { + uint32_t entry_index = &object_offset - oat_file_.GetBootImageRelocations().data(); + uint32_t entry_offset = entry_index * sizeof(oat_file_.GetBootImageRelocations()[0]); + os << StringPrintf(" 0x%x: 0x%08x\n", entry_offset, object_offset); + } + } + os << "\n"; + } + template void DumpBssEntries(std::ostream& os, const char* slot_type, -- GitLab From fa4ea8243f1ed949bb76cf088a4b34275c622696 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 2 Mar 2018 13:48:54 -0800 Subject: [PATCH 078/749] Remove mandatory address for non moving space / zygote This used to not be safe when the immune region didn't correctly handle gaps between the regions. The immune spaces handles this correctly but may cause the GC to be slightly slower when it occurs. Test: test-art-host Bug: 74062530 Change-Id: Ia24db1c0a8c7b3667f3f893d0d9e9ead33d6a248 --- runtime/gc/heap.cc | 16 ++++++++-------- runtime/gc/heap.h | 1 + runtime/gc/heap_test.cc | 21 ++++++++++++++++++++- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index e8f720bf3a..a725ec40b6 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -149,15 +149,15 @@ static constexpr size_t kPostForkMaxHeapDurationMS = 2000; #if defined(__LP64__) || !defined(ADDRESS_SANITIZER) // 300 MB (0x12c00000) - (default non-moving space capacity). -static uint8_t* const kPreferredAllocSpaceBegin = - reinterpret_cast(300 * MB - Heap::kDefaultNonMovingSpaceCapacity); +uint8_t* const Heap::kPreferredAllocSpaceBegin = + reinterpret_cast(300 * MB - kDefaultNonMovingSpaceCapacity); #else #ifdef __ANDROID__ // For 32-bit Android, use 0x20000000 because asan reserves 0x04000000 - 0x20000000. -static uint8_t* const kPreferredAllocSpaceBegin = reinterpret_cast(0x20000000); +uint8_t* const Heap::kPreferredAllocSpaceBegin = reinterpret_cast(0x20000000); #else // For 32-bit host, use 0x40000000 because asan uses most of the space below this. -static uint8_t* const kPreferredAllocSpaceBegin = reinterpret_cast(0x40000000); +uint8_t* const Heap::kPreferredAllocSpaceBegin = reinterpret_cast(0x40000000); #endif #endif @@ -386,10 +386,10 @@ Heap::Heap(size_t initial_size, const char* space_name = is_zygote ? kZygoteSpaceName : kNonMovingSpaceName; // Reserve the non moving mem map before the other two since it needs to be at a specific // address. - non_moving_space_mem_map.reset( - MemMap::MapAnonymous(space_name, requested_alloc_space_begin, - non_moving_space_capacity, PROT_READ | PROT_WRITE, true, false, - &error_str)); + non_moving_space_mem_map.reset(MapAnonymousPreferredAddress(space_name, + requested_alloc_space_begin, + non_moving_space_capacity, + &error_str)); CHECK(non_moving_space_mem_map != nullptr) << error_str; // Try to reserve virtual memory at a lower address if we have a separate non moving space. request_begin = kPreferredAllocSpaceBegin + non_moving_space_capacity; diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index d9eff7bfef..021fe58cf0 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -144,6 +144,7 @@ class Heap { static constexpr size_t kDefaultLargeObjectThreshold = kMinLargeObjectThreshold; // Whether or not parallel GC is enabled. If not, then we never create the thread pool. static constexpr bool kDefaultEnableParallelGC = false; + static uint8_t* const kPreferredAllocSpaceBegin; // Whether or not we use the free list large object space. Only use it if USE_ART_LOW_4G_ALLOCATOR // since this means that we have to use the slow msync loop in MemMap::MapAnonymous. diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc index 2def52450b..c6b2120f5b 100644 --- a/runtime/gc/heap_test.cc +++ b/runtime/gc/heap_test.cc @@ -27,7 +27,26 @@ namespace art { namespace gc { -class HeapTest : public CommonRuntimeTest {}; +class HeapTest : public CommonRuntimeTest { + public: + void SetUp() OVERRIDE { + MemMap::Init(); + std::string error_msg; + // Reserve the preferred address to force the heap to use another one for testing. + reserved_.reset(MemMap::MapAnonymous("ReserveMap", + gc::Heap::kPreferredAllocSpaceBegin, + 16 * KB, + PROT_READ, + /*low_4gb*/ true, + /*reuse*/ false, + &error_msg)); + ASSERT_TRUE(reserved_ != nullptr) << error_msg; + CommonRuntimeTest::SetUp(); + } + + private: + std::unique_ptr reserved_; +}; TEST_F(HeapTest, ClearGrowthLimit) { Heap* heap = Runtime::Current()->GetHeap(); -- GitLab From 142b913fcda48aa2cd470b2e0102af66abbd0e97 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Wed, 14 Mar 2018 15:12:59 -0700 Subject: [PATCH 079/749] Recognize MIN-MAX. Rationale: Because more contextual information is better. Bug: b/74026074 Test: test-art-host,target Change-Id: If884d64d800823f32d7ca6217c28fef25b86af9e --- compiler/optimizing/instruction_simplifier.cc | 31 +++- test/679-checker-minmax/expected.txt | 1 + test/679-checker-minmax/info.txt | 1 + test/679-checker-minmax/src/Main.java | 159 ++++++++++++++++++ 4 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 test/679-checker-minmax/expected.txt create mode 100644 test/679-checker-minmax/info.txt create mode 100644 test/679-checker-minmax/src/Main.java 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/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: <> GreaterThanOrEqual [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> Min + /// CHECK-DAG: Return [<>] + // + /// 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: <> GreaterThan [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.min2(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> Min + /// CHECK-DAG: Return [<>] + // + /// 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: <> LessThanOrEqual [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.min3(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> Min + /// CHECK-DAG: Return [<>] + // + /// 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: <> LessThan [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.min4(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> Min + /// CHECK-DAG: Return [<>] + // + /// 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: <> GreaterThanOrEqual [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> Max + /// CHECK-DAG: Return [<>] + // + /// 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: <> GreaterThan [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.max2(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> Max + /// CHECK-DAG: Return [<>] + // + /// 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: <> LessThanOrEqual [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.max3(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> Max + /// CHECK-DAG: Return [<>] + // + /// 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: <> LessThan [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.max4(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> Max + /// CHECK-DAG: Return [<>] + // + /// 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); + } + } +} -- GitLab From 8ce3bfaf1da2139a70b67e6b53c0110489801d40 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Mon, 12 Mar 2018 18:01:18 +0000 Subject: [PATCH 080/749] Refactor enforcement of hidden API policy when linking Hidden API enforcement when linking was sub-optimal for two reasons: a) it was based on Class::CanAccessMember and would throw IllegalAccessError instead of NoSuchMethodError/NoSuchFieldError, and b) no warnings were printed in the code path. This patch moves the checks into ClassLinker's resolution routines and uses a code path which prints warning. Bug: 64382372 Test: art/test.py -r -t 674-hiddenapi Change-Id: I8a0fbdca58ce5803c1588b644a3e627a0a9a6504 --- runtime/class_linker.cc | 26 +++++++++++--- runtime/hidden_api.h | 43 +++++++++++------------ runtime/interpreter/unstarted_runtime.cc | 4 ++- runtime/mirror/class-inl.h | 4 --- test/674-hiddenapi/api-blacklist.txt | 2 ++ test/674-hiddenapi/api-dark-greylist.txt | 2 ++ test/674-hiddenapi/api-light-greylist.txt | 2 ++ test/674-hiddenapi/src-ex/ChildClass.java | 4 +-- test/674-hiddenapi/src-ex/Linking.java | 26 +++++++++----- test/674-hiddenapi/src/ParentClass.java | 27 ++++++++++++++ 10 files changed, 97 insertions(+), 43 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 72c110a970..9990a373c9 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -7907,6 +7907,10 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr 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 +8049,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 +8126,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 +8161,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 { diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index e0519a07da..7ca2378a07 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,8 @@ enum Action { enum AccessMethod { kReflection, - kJNI + kJNI, + kLinking, }; inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { @@ -44,6 +48,9 @@ inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { case kJNI: os << "JNI"; break; + case kLinking: + os << "linking"; + break; } return os; } @@ -88,7 +95,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 inline bool ShouldBlockAccessToMember(T* member, Thread* self, @@ -145,27 +152,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 +inline bool ShouldBlockAccessToMember(T* member, + ObjPtr 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/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 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/mirror/class-inl.h b/runtime/mirror/class-inl.h index ee7d217e8d..60062c8a41 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -1144,10 +1144,6 @@ inline bool Class::CanAccessMember(ObjPtr 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/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;->()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;->()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;->()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..582e907ca3 100644 --- a/test/674-hiddenapi/src-ex/ChildClass.java +++ b/test/674-hiddenapi/src-ex/ChildClass.java @@ -120,7 +120,7 @@ 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); } @@ -389,7 +389,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) { diff --git a/test/674-hiddenapi/src-ex/Linking.java b/test/674-hiddenapi/src-ex/Linking.java index c6735d85fe..a89b92b2b9 100644 --- a/test/674-hiddenapi/src-ex/Linking.java +++ b/test/674-hiddenapi/src-ex/Linking.java @@ -27,7 +27,7 @@ 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; @@ -66,25 +66,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 +122,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/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(); } + } -- GitLab From fc66129b478d49f493b8262f81f8813a5f41459e Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Wed, 14 Mar 2018 13:57:27 +0000 Subject: [PATCH 081/749] Warn on overriding of hidden methods We could prevent apps from overriding hidden methods in the same manner they cannot override a package-private method - by creating a separate vtable entry for the child method. For now, start by printing a warning when a hidden method is being overridden but do not change the semantics. Bug: 64382372 Test: art/test.py -r -t 674-hiddenapi Change-Id: I9d5bfa6b833a4c0f5aaffa5f82dbe9b1e1f03f1f --- runtime/class_linker.cc | 8 +++++ runtime/hidden_api.h | 4 +++ test/674-hiddenapi/src-ex/ChildClass.java | 34 +++++++++++++++++++ test/674-hiddenapi/src-ex/Linking.java | 11 ++++++ test/674-hiddenapi/src-ex/OverrideClass.java | 35 ++++++++++++++++++++ 5 files changed, 92 insertions(+) create mode 100644 test/674-hiddenapi/src-ex/OverrideClass.java diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 9990a373c9..f720558e51 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -5836,6 +5836,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()) { diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index 7ca2378a07..f2ea2fdaaa 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -38,6 +38,7 @@ enum AccessMethod { kReflection, kJNI, kLinking, + kOverride, }; inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { @@ -51,6 +52,9 @@ inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { case kLinking: os << "linking"; break; + case kOverride: + os << "override"; + break; } return os; } diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java index 582e907ca3..8cd237ab6f 100644 --- a/test/674-hiddenapi/src-ex/ChildClass.java +++ b/test/674-hiddenapi/src-ex/ChildClass.java @@ -123,6 +123,9 @@ public class ChildClass { // 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. @@ -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 a89b92b2b9..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 { @@ -34,6 +35,16 @@ public class Linking { } } } + + 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 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; } + +} -- GitLab From d767f2df01c6c1d533b81b8a8257eb3f8e0ce6e7 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Mon, 26 Feb 2018 16:18:40 +0000 Subject: [PATCH 082/749] More JIT debug data synchronisation. I want to be able to reason about consistency of the data even when it is being modified (e.g. debug-malloc backtrace running on one thread while the JIT is running on a different thread). Test: testrunner.py --host -t 137 Change-Id: I051bf8dcf2801d9671cf83f0e0a94e1f19b98c0f --- runtime/jit/debugger_interface.cc | 140 +++++++++++++++++++++--------- 1 file changed, 97 insertions(+), 43 deletions(-) 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 #include +#include // // 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 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 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) == alignof(void*), "Weird alignment"); + static_assert(sizeof(std::atomic) == 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& 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; } -- GitLab From 946bb09a5adc7d591498b4504aa5d9354457953e Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Fri, 9 Mar 2018 17:23:01 +0000 Subject: [PATCH 083/749] Support unwinding though the switch interpreter. Wrap the switch interpreter in small assembly method which defines DEX PC in CFI and thus it allows libunwind to backtrace through it. Bug: 22414682 Test: testrunner.py --host -t 137 Test: testrunner.py --target -t 137 Change-Id: I31dad9f0fb446151baaa99234b64f25c8ca2fa87 --- runtime/arch/arm/quick_entrypoints_arm.S | 16 ++++++ runtime/arch/arm64/quick_entrypoints_arm64.S | 14 +++++ runtime/arch/x86/quick_entrypoints_x86.S | 24 +++++++++ .../arch/x86_64/quick_entrypoints_x86_64.S | 16 ++++++ .../interpreter/{mterp => }/cfi_asm_support.h | 6 +-- .../interpreter/interpreter_switch_impl.cc | 54 ++++++++++--------- runtime/interpreter/interpreter_switch_impl.h | 48 ++++++++++++++--- runtime/interpreter/mterp/arm/header.S | 2 +- runtime/interpreter/mterp/arm64/header.S | 2 +- runtime/interpreter/mterp/gen_mterp.py | 2 +- runtime/interpreter/mterp/mips/header.S | 2 +- runtime/interpreter/mterp/mips64/header.S | 2 +- runtime/interpreter/mterp/out/mterp_arm.S | 2 +- runtime/interpreter/mterp/out/mterp_arm64.S | 2 +- runtime/interpreter/mterp/out/mterp_mips.S | 2 +- runtime/interpreter/mterp/out/mterp_mips64.S | 2 +- runtime/interpreter/mterp/out/mterp_x86.S | 2 +- runtime/interpreter/mterp/out/mterp_x86_64.S | 2 +- runtime/interpreter/mterp/x86/header.S | 2 +- runtime/interpreter/mterp/x86_64/header.S | 2 +- test/137-cfi/cfi.cc | 7 +-- test/knownfailures.json | 7 --- 22 files changed, 158 insertions(+), 60 deletions(-) rename runtime/interpreter/{mterp => }/cfi_asm_support.h (92%) 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/interpreter/mterp/cfi_asm_support.h b/runtime/interpreter/cfi_asm_support.h similarity index 92% rename from runtime/interpreter/mterp/cfi_asm_support.h rename to runtime/interpreter/cfi_asm_support.h index 0df4eb4f81..a9f01afe3c 100644 --- a/runtime/interpreter/mterp/cfi_asm_support.h +++ b/runtime/interpreter/cfi_asm_support.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_INTERPRETER_MTERP_CFI_ASM_SUPPORT_H_ -#define ART_RUNTIME_INTERPRETER_MTERP_CFI_ASM_SUPPORT_H_ +#ifndef ART_RUNTIME_INTERPRETER_CFI_ASM_SUPPORT_H_ +#define ART_RUNTIME_INTERPRETER_CFI_ASM_SUPPORT_H_ /* * Define the DEX PC (memory address of the currently interpreted bytecode) @@ -44,4 +44,4 @@ 0x13 /* DW_OP_drop */, \ 0x92 /* DW_OP_bregx */, dexReg, (dexOffset & 0x7F) /* 1-byte SLEB128 */ -#endif // ART_RUNTIME_INTERPRETER_MTERP_CFI_ASM_SUPPORT_H_ +#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(shadow_frame.GetDexPC()) - static_cast(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 -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(Thread* self, const CodeItemDataAccessor& accessor, - ShadowFrame& shadow_frame, JValue result_register, - bool interpret_one_instruction); +void ExecuteSwitchImplCpp(SwitchImplContext* ctx); template HOT_ATTR -JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, - ShadowFrame& shadow_frame, JValue result_register, - bool interpret_one_instruction); +void ExecuteSwitchImplCpp(SwitchImplContext* ctx); template -JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, - ShadowFrame& shadow_frame, JValue result_register, - bool interpret_one_instruction); +void ExecuteSwitchImplCpp(SwitchImplContext* ctx); template -JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, - ShadowFrame& shadow_frame, JValue result_register, - bool interpret_one_instruction); +void ExecuteSwitchImplCpp(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 +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 -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(&ExecuteSwitchImplCpp); + 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/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/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 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 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/knownfailures.json b/test/knownfailures.json index b2f579d3b9..3bc71b7a58 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -74,13 +74,6 @@ "work (and isn't meant to) without --prebuild", "--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", -- GitLab From 6f7d8b7c79d6d560ca70c22ec572b7a5cc3cb225 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Thu, 15 Mar 2018 15:45:00 +0000 Subject: [PATCH 084/749] Stop netd before running tests on Buildbot devices. When netd is running, some libcore and JDWP tests fail with this exception: android.system.ErrnoException: connect failed: EBADMSG (Not a data message) Turn it off to make these tests pass. Test: art/tools/run-libcore-tests.sh --mode=device Test: art/tools/run-jdwp-tests.sh --mode=device Bug: 74725685 Change-Id: If54723822ccdaf18fd684a1b61f1d1d26acfee3d --- tools/setup-buildbot-device.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) 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 -- GitLab From a83d20d93fce69890fbaeab607d41d2d3404bb97 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Thu, 15 Mar 2018 16:01:32 +0000 Subject: [PATCH 085/749] Adjust known test failures after recent changes in ART test configurations. Changes: - Have test 641-checker-arraycopy always run. - Have test 537-checker-arraycopy always run, except the table-lookup read barrier configuration. Test: Run ART run-tests with ART_READ_BARRIER_TYPE=TABLELOOKUP Test: Run ART run-tests without ART_READ_BARRIER_TYPE=TABLELOOKUP Bug: 62611253 Change-Id: Ib4b1e96c85b42cb6acb854229acd1df83d04cf5f --- test/knownfailures.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/knownfailures.json b/test/knownfailures.json index d1855dcbc7..44805ad31d 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -344,10 +344,8 @@ "variant": "optimizing | regalloc_gc" }, { - "tests": ["537-checker-arraycopy", - "641-checker-arraycopy"], - "env_vars": {"ART_USE_READ_BARRIER": "true"}, - "variant": "interpreter | optimizing | regalloc_gc | jit" + "tests": ["537-checker-arraycopy"], + "env_vars": {"ART_READ_BARRIER_TYPE": "TABLELOOKUP"} }, { "tests": ["476-clinit-inline-static-invoke", -- GitLab From c17b7d80652765750fa7f2baf236061014b23f93 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 14 Mar 2018 14:00:04 -0700 Subject: [PATCH 086/749] Add owned section for CompactDex The owned section is the part of the shared data section owned by a given dex file. This enables efficiently attributing an offset to a dex file. Bug: 74443371 Bug: 63756964 Test: test-art-host Change-Id: I2de9a281e18b02a20c3dcf5f484eacb591220cdc --- dex2oat/dex2oat_test.cc | 37 ++++++++++++++++++++++++++----- dexlayout/compact_dex_writer.cc | 5 ++++- dexlayout/compact_dex_writer.h | 4 ++++ libdexfile/dex/compact_dex_file.h | 14 ++++++++++++ libdexfile/dex/dex_file-inl.h | 12 ++++++++++ libdexfile/dex/dex_file.h | 21 ++++++++++-------- runtime/vdex_file.h | 3 ++- 7 files changed, 80 insertions(+), 16 deletions(-) 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 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 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> compact_dex_files; for (const OatDexFile* oat_dex : oat_dex_files) { std::unique_ptr 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(dex_file.release()->AsCompactDexFile())); + } + for (const std::unique_ptr& 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& 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 +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 + 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/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. -- GitLab From a7343b2093650c05835020ee1069e45dc5f25abd Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 15 Mar 2018 16:49:13 +0000 Subject: [PATCH 087/749] Move test so that it doesn't run with --jvm. bug: 73143991 Test: 616-cha-unloading Change-Id: I37b4c9023bad279fa3a4398e4fd9fb0bef70c934 --- test/616-cha-unloading/{src => src-art}/AbstractCHATester.java | 0 test/616-cha-unloading/{src => src-art}/Main.java | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename test/616-cha-unloading/{src => src-art}/AbstractCHATester.java (100%) rename test/616-cha-unloading/{src => src-art}/Main.java (100%) diff --git a/test/616-cha-unloading/src/AbstractCHATester.java b/test/616-cha-unloading/src-art/AbstractCHATester.java similarity index 100% rename from test/616-cha-unloading/src/AbstractCHATester.java rename to test/616-cha-unloading/src-art/AbstractCHATester.java diff --git a/test/616-cha-unloading/src/Main.java b/test/616-cha-unloading/src-art/Main.java similarity index 100% rename from test/616-cha-unloading/src/Main.java rename to test/616-cha-unloading/src-art/Main.java -- GitLab From 29aa08219ff72409e9f10ae2a5da4e6e604baad1 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Thu, 8 Mar 2018 11:28:00 -0800 Subject: [PATCH 088/749] Vectorization of saturation arithmetic. Rationale: Because faster is better. Bug: b/74026074 Test: test-art-host,target Change-Id: Ifa970a62cef1c0b8bb1c593f629d8c724f1ffe0e --- .../optimizing/code_generator_vector_arm64.cc | 138 +++++-- .../code_generator_vector_arm_vixl.cc | 126 ++++-- .../optimizing/code_generator_vector_mips.cc | 90 ++-- .../code_generator_vector_mips64.cc | 90 ++-- .../optimizing/code_generator_vector_x86.cc | 140 +++++-- .../code_generator_vector_x86_64.cc | 140 +++++-- compiler/optimizing/loop_optimization.cc | 152 ++++++- compiler/optimizing/loop_optimization.h | 6 + compiler/optimizing/nodes.h | 2 + compiler/optimizing/nodes_vector.h | 50 +++ test/678-checker-simd-saturation/expected.txt | 1 + test/678-checker-simd-saturation/info.txt | 1 + .../678-checker-simd-saturation/src/Main.java | 389 ++++++++++++++++++ 13 files changed, 1102 insertions(+), 223 deletions(-) create mode 100644 test/678-checker-simd-saturation/expected.txt create mode 100644 test/678-checker-simd-saturation/info.txt create mode 100644 test/678-checker-simd-saturation/src/Main.java 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(), 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(), 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 dst = locations->Out().AsFpuRegister(); + 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 dst = locations->Out().AsFpuRegister(); + 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(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(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(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()); 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 dst = locations->Out().AsFpuRegister(); + 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 dst = locations->Out().AsFpuRegister(); + 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(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(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(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()); 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/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index d3b081e005..abd644ae9b 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -36,6 +36,10 @@ static constexpr bool kEnableVectorization = true; // No loop unrolling factor (just one copy of the loop-body). static constexpr uint32_t kNoUnrollingFactor = 1; +// Values that indicate unbounded end. +static constexpr int64_t kNoLo = std::numeric_limits::min(); +static constexpr int64_t kNoHi = std::numeric_limits::max(); + // // Static helpers. // @@ -331,6 +335,74 @@ 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 bool IsClipped(HInstruction* instruction, + /*out*/ int64_t* lo, + /*out*/ int64_t* hi, + /*out*/ HInstruction** clippee) { + // Recurse into MIN-MAX expressions and 'tighten' the range [lo, hi]. + if (instruction->IsMin() || instruction->IsMax()) { + // Find MIN-MAX(const, ..) or MIN-MAX(.., const). + for (int i = 0; i < 2; i++) { + int64_t c = 0; + if (IsInt64AndGet(instruction->InputAt(i), &c)) { + if (instruction->IsMin()) { + *hi = std::min(*hi, c); + } else { + *lo = std::max(*lo, c); + } + return IsClipped(instruction->InputAt(1 - i), lo, hi, clippee); + } + } + // Recursion fails at any MIN/MAX that does not have one constant + // argument, e.g. MIN(x, y) or MAX(2 * x, f()). + return false; + } + // Recursion ends in any other expression. At this point we record the leaf + // expression as the clippee and report success on the range [lo, hi]. + DCHECK(*clippee == nullptr); + *clippee = instruction; + return true; +} + +// 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::max()) + : (lo == std::numeric_limits::min() && + hi == std::numeric_limits::max()); + } else if (DataType::Size(type) == 2) { + return is_unsigned + ? (lo <= 0 && hi == std::numeric_limits::max()) + : (lo == std::numeric_limits::min() && + hi == std::numeric_limits::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::max()) + : (lo == std::numeric_limits::min() && + hi == std::numeric_limits::max()); + } else if (DataType::Size(type) == 2) { + return is_unsigned + ? (lo == 0 && hi >= std::numeric_limits::min()) + : (lo == std::numeric_limits::min() && + hi == std::numeric_limits::max()); + } + return false; +} + // Detect reductions of the following forms, // x = x_phi + .. // x = x_phi - .. @@ -1109,7 +1181,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 +1379,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 +1514,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 +1543,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 +1886,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; + } + // Search for clipping of a clippee. + int64_t lo = kNoLo; + int64_t hi = kNoHi; + HInstruction* clippee = nullptr; + if (!IsClipped(instruction, &lo, &hi, &clippee)) { + return false; + } + CHECK(clippee != nullptr); + // Clipped addition or subtraction? + 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(new (global_allocator_) HVecSaturationAdd( + global_allocator_, op1, op2, vtype, vector_length_, kNoDexPc)) + : reinterpret_cast(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..523bca8d25 100644 --- a/compiler/optimizing/nodes_vector.h +++ b/compiler/optimizing/nodes_vector.h @@ -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/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: <> IntConstant 255 loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satAddUByte(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] 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: <> IntConstant -128 loop:none + /// CHECK-DAG: <> IntConstant 127 loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satAddSByte(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] 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: <> IntConstant 65535 loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satAddUShort(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] 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: <> IntConstant -32768 loop:none + /// CHECK-DAG: <> IntConstant 32767 loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satAddSShort(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] 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: <> IntConstant 0 loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satSubUByte(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] 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: <> IntConstant -128 loop:none + /// CHECK-DAG: <> IntConstant 127 loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satSubSByte(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] 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: <> IntConstant 0 loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satSubUShort(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] 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: <> IntConstant -32768 loop:none + /// CHECK-DAG: <> IntConstant 32767 loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satSubSShort(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] 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: <> IntConstant -32768 loop:none + /// CHECK-DAG: <> IntConstant 32767 loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satAlt1(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] 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); + } + } +} -- GitLab From a3cd12412475049c51ff16369d8044cae2438d54 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 15 Mar 2018 22:29:24 +0000 Subject: [PATCH 089/749] Fix a few issues with 616-cha-unloading. - Disable test on RI - Disable test on trace - Don't try N times for getting the allocation we want, do it until we get it. We shouldn't get in an infinite loop. If we do the harness will kill the test. Test: 616-cha-unloading bug: 73143991 Change-Id: I1eaf6db1a79010f1e5dc33401e5ccf351ff10be2 --- test/616-cha-unloading/cha_unload.cc | 14 +++++--------- test/616-cha-unloading/expected.txt | 2 -- test/616-cha-unloading/src-art/Main.java | 12 ++++++------ test/knownfailures.json | 12 +++++++++++- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/test/616-cha-unloading/cha_unload.cc b/test/616-cha-unloading/cha_unload.cc index 46ceac640b..4be3456e3d 100644 --- a/test/616-cha-unloading/cha_unload.cc +++ b/test/616-cha-unloading/cha_unload.cc @@ -37,20 +37,16 @@ extern "C" JNIEXPORT jlong JNICALL Java_Main_getArtMethod(JNIEnv* env, return static_cast(reinterpret_cast(method)); } -extern "C" JNIEXPORT jboolean JNICALL Java_Main_tryReuseArenaOfMethod(JNIEnv*, - jclass, - jlong art_method, - jint tries_count) { +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 alloc(Runtime::Current()->CreateLinearAlloc()); - for (int i = static_cast(tries_count); i > 0; --i) { + do { // Ask for a byte - it's sufficient to get an arena and not have issues with size. alloc->Alloc(Thread::Current(), 1); - } - bool retval = alloc->Contains(reinterpret_cast(static_cast(art_method))); - - return retval; + } while (!alloc->Contains(reinterpret_cast(static_cast(art_method)))); } } // namespace diff --git a/test/616-cha-unloading/expected.txt b/test/616-cha-unloading/expected.txt index a71b724876..77a1486479 100644 --- a/test/616-cha-unloading/expected.txt +++ b/test/616-cha-unloading/expected.txt @@ -1,4 +1,2 @@ JNI_OnLoad called -null -true Done diff --git a/test/616-cha-unloading/src-art/Main.java b/test/616-cha-unloading/src-art/Main.java index b633a0c22e..effa315e25 100644 --- a/test/616-cha-unloading/src-art/Main.java +++ b/test/616-cha-unloading/src-art/Main.java @@ -53,12 +53,12 @@ public class Main { WeakReference loader = result.cl; long methodPtr = result.methodPtr; // Check that the classloader is indeed unloaded. - System.out.println(loader.get()); + if (loader.get() != null) { + throw new Error("Expected class loader to be unloaded"); + } - // Reuse the linear alloc so old pointers so it becomes invalid. - boolean ret = tryReuseArenaOfMethod(methodPtr, 10); - // Check that we indeed reused it. - System.out.println(ret); + // Reuse the linear alloc used by the unloaded class loader. + reuseArenaOfMethod(methodPtr); // Try to JIT-compile under dangerous conditions. ensureJitCompiled(Main.class, "targetMethodForJit"); @@ -117,5 +117,5 @@ public class Main { private static native void ensureJitCompiled(Class itf, String method_name); private static native long getArtMethod(Object javaMethod); - private static native boolean tryReuseArenaOfMethod(long artMethod, int tries_count); + private static native void reuseArenaOfMethod(long artMethod); } diff --git a/test/knownfailures.json b/test/knownfailures.json index 7292280ac8..9f0f95411c 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -949,7 +949,17 @@ ], "variant": "jvm", "bug": "b/73888836", - "description": ["Failing on JVM. Needs further investigating."] + "description": ["Failing on RI. Needs further investigating."] + }, + { + "tests": ["616-cha-unloading"], + "variant": "jvm", + "description": ["Doesn't run on RI."] + }, + { + "tests": ["616-cha-unloading"], + "variant": "trace", + "description": ["Trace prevents class unloading."] }, { "tests": "677-fsi", -- GitLab From 1a38102a85e2a9a4545c54edc21cc2efbe510f9f Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Thu, 15 Mar 2018 15:51:37 -0700 Subject: [PATCH 090/749] Minor cleanup of saturation arithmetic code. Rationale: Changes requested by Artem and Vladimir. Bug: b/74026074 Test: test-art-host,target Change-Id: If99dd2345369d4260e3b514bb212f81d433420a3 --- compiler/optimizing/loop_optimization.cc | 61 ++++++++++-------------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index abd644ae9b..e1fb7ac17e 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -36,10 +36,6 @@ static constexpr bool kEnableVectorization = true; // No loop unrolling factor (just one copy of the loop-body). static constexpr uint32_t kNoUnrollingFactor = 1; -// Values that indicate unbounded end. -static constexpr int64_t kNoLo = std::numeric_limits::min(); -static constexpr int64_t kNoHi = std::numeric_limits::max(); - // // Static helpers. // @@ -338,33 +334,28 @@ static bool IsAddConst(HInstruction* instruction, // 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 bool IsClipped(HInstruction* instruction, - /*out*/ int64_t* lo, - /*out*/ int64_t* hi, - /*out*/ HInstruction** clippee) { - // Recurse into MIN-MAX expressions and 'tighten' the range [lo, hi]. - if (instruction->IsMin() || instruction->IsMax()) { - // Find MIN-MAX(const, ..) or MIN-MAX(.., const). - for (int i = 0; i < 2; i++) { - int64_t c = 0; - if (IsInt64AndGet(instruction->InputAt(i), &c)) { - if (instruction->IsMin()) { - *hi = std::min(*hi, c); - } else { - *lo = std::max(*lo, c); - } - return IsClipped(instruction->InputAt(1 - i), lo, hi, clippee); - } +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)); } - // Recursion fails at any MIN/MAX that does not have one constant - // argument, e.g. MIN(x, y) or MAX(2 * x, f()). - return false; + instruction = min_max->GetLeastConstantLeft(); } - // Recursion ends in any other expression. At this point we record the leaf - // expression as the clippee and report success on the range [lo, hi]. - DCHECK(*clippee == nullptr); - *clippee = instruction; - return true; + // 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. @@ -1896,15 +1887,15 @@ bool HLoopOptimization::VectorizeSaturationIdiom(LoopNode* node, if (HasVectorRestrictions(restrictions, kNoSaturation)) { return false; } - // Search for clipping of a clippee. - int64_t lo = kNoLo; - int64_t hi = kNoHi; - HInstruction* clippee = nullptr; - if (!IsClipped(instruction, &lo, &hi, &clippee)) { + // 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; } - CHECK(clippee != nullptr); // Clipped addition or subtraction? + int64_t lo = std::numeric_limits::min(); + int64_t hi = std::numeric_limits::max(); + HInstruction* clippee = FindClippee(instruction, &lo, &hi); bool is_add = true; if (clippee->IsAdd()) { is_add = true; -- GitLab From 6ff3b371e53ffdb6d0aa9f85ae9468696f5fa2b0 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 15 Mar 2018 08:53:16 -0700 Subject: [PATCH 091/749] ART: Clean up Trace Remove unneeded field. Refactor Start to have a cleaner interface. Add exception for invalid file descriptor. Test: mmma art Test: art/test/testrunner/testrunner.py -b --host --trace Change-Id: I7d0cd9629796c690a993dbbba5ae96d940c44dba --- runtime/native/dalvik_system_VMDebug.cc | 45 ++++++--- runtime/native/dalvik_system_ZygoteHooks.cc | 1 - runtime/runtime.cc | 1 - runtime/trace.cc | 100 +++++++++++++++----- runtime/trace.h | 43 ++++++++- 5 files changed, 144 insertions(+), 46 deletions(-) diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index fc9426650e..3692a308d8 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -89,17 +89,27 @@ static void VMDebug_resetAllocCount(JNIEnv*, jclass, jint kinds) { static void VMDebug_startMethodTracingDdmsImpl(JNIEnv*, jclass, jint bufferSize, jint flags, jboolean samplingEnabled, jint intervalUs) { - Trace::Start("[DDMS]", -1, bufferSize, flags, Trace::TraceOutputMode::kDDMS, - samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, - intervalUs); -} - -static void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, jstring javaTraceFilename, - jint javaFd, jint bufferSize, jint flags, - jboolean samplingEnabled, jint intervalUs, + Trace::StartDDMS(bufferSize, + flags, + samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, + intervalUs); +} + +static void VMDebug_startMethodTracingFd(JNIEnv* env, + jclass, + jstring javaTraceFilename ATTRIBUTE_UNUSED, + jint javaFd, + jint bufferSize, + jint flags, + jboolean samplingEnabled, + jint intervalUs, jboolean streamingOutput) { int originalFd = javaFd; if (originalFd < 0) { + ScopedObjectAccess soa(env); + soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", + "Trace fd is invalid: %d", + originalFd); return; } @@ -107,18 +117,20 @@ static void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, jstring javaTraceF if (fd < 0) { ScopedObjectAccess soa(env); soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", - "dup(%d) failed: %s", originalFd, strerror(errno)); + "dup(%d) failed: %s", + originalFd, + strerror(errno)); return; } - ScopedUtfChars traceFilename(env, javaTraceFilename); - if (traceFilename.c_str() == nullptr) { - return; - } + // Ignore the traceFilename. Trace::TraceOutputMode outputMode = streamingOutput ? Trace::TraceOutputMode::kStreaming : Trace::TraceOutputMode::kFile; - Trace::Start(traceFilename.c_str(), fd, bufferSize, flags, outputMode, + Trace::Start(fd, + bufferSize, + flags, + outputMode, samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, intervalUs); } @@ -130,7 +142,10 @@ static void VMDebug_startMethodTracingFilename(JNIEnv* env, jclass, jstring java if (traceFilename.c_str() == nullptr) { return; } - Trace::Start(traceFilename.c_str(), -1, bufferSize, flags, Trace::TraceOutputMode::kFile, + Trace::Start(traceFilename.c_str(), + bufferSize, + flags, + Trace::TraceOutputMode::kFile, samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, intervalUs); } diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index 89135698e3..5ecf965849 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -338,7 +338,6 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, std::string trace_file = StringPrintf("/data/misc/trace/%s.trace.bin", proc_name.c_str()); Trace::Start(trace_file.c_str(), - -1, buffer_size, 0, // TODO: Expose flags. output_mode, diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 7d9d3426fc..a5827a813c 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -839,7 +839,6 @@ bool Runtime::Start() { if (trace_config_.get() != nullptr && trace_config_->trace_file != "") { ScopedThreadStateChange tsc(self, kWaitingForMethodTracingStart); Trace::Start(trace_config_->trace_file.c_str(), - -1, static_cast(trace_config_->trace_file_size), 0, trace_config_->trace_output_mode, diff --git a/runtime/trace.cc b/runtime/trace.cc index 0f321b6591..91d2b3779e 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -319,8 +319,74 @@ void* Trace::RunSamplingThread(void* arg) { return nullptr; } -void Trace::Start(const char* trace_filename, int trace_fd, size_t buffer_size, int flags, - TraceOutputMode output_mode, TraceMode trace_mode, int interval_us) { +void Trace::Start(const char* trace_filename, + size_t buffer_size, + int flags, + TraceOutputMode output_mode, + TraceMode trace_mode, + int interval_us) { + std::unique_ptr file(OS::CreateEmptyFileWriteOnly(trace_filename)); + if (file == nullptr) { + std::string msg = android::base::StringPrintf("Unable to open trace file '%s'", trace_filename); + PLOG(ERROR) << msg; + ScopedObjectAccess soa(Thread::Current()); + Thread::Current()->ThrowNewException("Ljava/lang/RuntimeException;", msg.c_str()); + return; + } + Start(std::move(file), buffer_size, flags, output_mode, trace_mode, interval_us); +} + +void Trace::Start(int trace_fd, + size_t buffer_size, + int flags, + TraceOutputMode output_mode, + TraceMode trace_mode, + int interval_us) { + if (trace_fd < 0) { + std::string msg = android::base::StringPrintf("Unable to start tracing with invalid fd %d", + trace_fd); + LOG(ERROR) << msg; + ScopedObjectAccess soa(Thread::Current()); + Thread::Current()->ThrowNewException("Ljava/lang/RuntimeException;", msg.c_str()); + return; + } + std::unique_ptr file(new File(trace_fd, "tracefile")); + Start(std::move(file), buffer_size, flags, output_mode, trace_mode, interval_us); +} + +void Trace::StartDDMS(size_t buffer_size, + int flags, + TraceMode trace_mode, + int interval_us) { + Start(std::unique_ptr(), + buffer_size, + flags, + TraceOutputMode::kDDMS, + trace_mode, + interval_us); +} + +void Trace::Start(std::unique_ptr&& trace_file_in, + size_t buffer_size, + int flags, + TraceOutputMode output_mode, + TraceMode trace_mode, + int interval_us) { + // We own trace_file now and are responsible for closing it. To account for error situations, use + // a specialized unique_ptr to ensure we close it on the way out (if it hasn't been passed to a + // Trace instance). + auto deleter = [](File* file) { + if (file != nullptr) { + file->MarkUnchecked(); // Don't deal with flushing requirements. + int result ATTRIBUTE_UNUSED = file->Close(); + delete file; + } + }; + std::unique_ptr trace_file(trace_file_in.release(), deleter); + if (trace_file != nullptr) { + trace_file->DisableAutoClose(); + } + Thread* self = Thread::Current(); { MutexLock mu(self, *Locks::trace_lock_); @@ -338,23 +404,6 @@ void Trace::Start(const char* trace_filename, int trace_fd, size_t buffer_size, return; } - // Open trace file if not going directly to ddms. - std::unique_ptr trace_file; - if (output_mode != TraceOutputMode::kDDMS) { - if (trace_fd < 0) { - trace_file.reset(OS::CreateEmptyFileWriteOnly(trace_filename)); - } else { - trace_file.reset(new File(trace_fd, "tracefile")); - trace_file->DisableAutoClose(); - } - if (trace_file.get() == nullptr) { - PLOG(ERROR) << "Unable to open trace file '" << trace_filename << "'"; - ScopedObjectAccess soa(self); - ThrowRuntimeException("Unable to open trace file '%s'", trace_filename); - return; - } - } - Runtime* runtime = Runtime::Current(); // Enable count of allocs if specified in the flags. @@ -372,8 +421,7 @@ void Trace::Start(const char* trace_filename, int trace_fd, size_t buffer_size, LOG(ERROR) << "Trace already in progress, ignoring this request"; } else { enable_stats = (flags && kTraceCountAllocs) != 0; - the_trace_ = new Trace(trace_file.release(), trace_filename, buffer_size, flags, output_mode, - trace_mode); + the_trace_ = new Trace(trace_file.release(), buffer_size, flags, output_mode, trace_mode); if (trace_mode == TraceMode::kSampling) { CHECK_PTHREAD_CALL(pthread_create, (&sampling_pthread_, nullptr, &RunSamplingThread, reinterpret_cast(interval_us)), @@ -595,8 +643,11 @@ TracingMode Trace::GetMethodTracingMode() { static constexpr size_t kMinBufSize = 18U; // Trace header is up to 18B. -Trace::Trace(File* trace_file, const char* trace_name, size_t buffer_size, int flags, - TraceOutputMode output_mode, TraceMode trace_mode) +Trace::Trace(File* trace_file, + size_t buffer_size, + int flags, + TraceOutputMode output_mode, + TraceMode trace_mode) : trace_file_(trace_file), buf_(new uint8_t[std::max(kMinBufSize, buffer_size)]()), flags_(flags), trace_output_mode_(output_mode), trace_mode_(trace_mode), @@ -605,6 +656,8 @@ Trace::Trace(File* trace_file, const char* trace_name, size_t buffer_size, int f start_time_(MicroTime()), clock_overhead_ns_(GetClockOverheadNanoSeconds()), cur_offset_(0), overflow_(false), interval_us_(0), streaming_lock_(nullptr), unique_methods_lock_(new Mutex("unique methods lock", kTracingUniqueMethodsLock)) { + CHECK(trace_file != nullptr || output_mode == TraceOutputMode::kDDMS); + uint16_t trace_version = GetTraceVersion(clock_source_); if (output_mode == TraceOutputMode::kStreaming) { trace_version |= 0xF0U; @@ -625,7 +678,6 @@ Trace::Trace(File* trace_file, const char* trace_name, size_t buffer_size, int f cur_offset_.StoreRelaxed(kTraceHeaderLength); if (output_mode == TraceOutputMode::kStreaming) { - streaming_file_name_ = trace_name; streaming_lock_ = new Mutex("tracing lock", LockLevel::kTracingStreamingLock); seen_threads_.reset(new ThreadIDBitSet()); } diff --git a/runtime/trace.h b/runtime/trace.h index 86b8d00d51..7171f759c9 100644 --- a/runtime/trace.h +++ b/runtime/trace.h @@ -33,6 +33,10 @@ #include "globals.h" #include "instrumentation.h" +namespace unix_file { +class FdFile; +} // namespace unix_file + namespace art { class ArtField; @@ -115,10 +119,37 @@ class Trace FINAL : public instrumentation::InstrumentationListener { static void SetDefaultClockSource(TraceClockSource clock_source); - static void Start(const char* trace_filename, int trace_fd, size_t buffer_size, int flags, - TraceOutputMode output_mode, TraceMode trace_mode, int interval_us) + static void Start(const char* trace_filename, + size_t buffer_size, + int flags, + TraceOutputMode output_mode, + TraceMode trace_mode, + int interval_us) + REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_, + !Locks::trace_lock_); + static void Start(int trace_fd, + size_t buffer_size, + int flags, + TraceOutputMode output_mode, + TraceMode trace_mode, + int interval_us) + REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_, + !Locks::trace_lock_); + static void Start(std::unique_ptr&& file, + size_t buffer_size, + int flags, + TraceOutputMode output_mode, + TraceMode trace_mode, + int interval_us) + REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_, + !Locks::trace_lock_); + static void StartDDMS(size_t buffer_size, + int flags, + TraceMode trace_mode, + int interval_us) REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_, !Locks::trace_lock_); + static void Pause() REQUIRES(!Locks::trace_lock_, !Locks::thread_list_lock_); static void Resume() REQUIRES(!Locks::trace_lock_); @@ -212,8 +243,11 @@ class Trace FINAL : public instrumentation::InstrumentationListener { static bool IsTracingEnabled() REQUIRES(!Locks::trace_lock_); private: - Trace(File* trace_file, const char* trace_name, size_t buffer_size, int flags, - TraceOutputMode output_mode, TraceMode trace_mode); + Trace(File* trace_file, + size_t buffer_size, + int flags, + TraceOutputMode output_mode, + TraceMode trace_mode); // The sampling interval in microseconds is passed as an argument. static void* RunSamplingThread(void* arg) REQUIRES(!Locks::trace_lock_); @@ -318,7 +352,6 @@ class Trace FINAL : public instrumentation::InstrumentationListener { int interval_us_; // Streaming mode data. - std::string streaming_file_name_; Mutex* streaming_lock_; std::map seen_methods_; std::unique_ptr seen_threads_; -- GitLab From 64ebd81e5cc3f517eaa3a33b6352fa8ae6b36c77 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Fri, 16 Mar 2018 09:53:52 +0000 Subject: [PATCH 092/749] Fix Mac build: Don't use the CFI directives on Mac Bug: 22414682 Test: testrunner.py --host -t 137 Change-Id: Ia54fcf012149e1a78a7f2e7c6adb47ba26810cdf --- runtime/interpreter/cfi_asm_support.h | 57 +++++++++++++++------------ 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/runtime/interpreter/cfi_asm_support.h b/runtime/interpreter/cfi_asm_support.h index a9f01afe3c..c1e5fb5615 100644 --- a/runtime/interpreter/cfi_asm_support.h +++ b/runtime/interpreter/cfi_asm_support.h @@ -17,31 +17,36 @@ #ifndef ART_RUNTIME_INTERPRETER_CFI_ASM_SUPPORT_H_ #define ART_RUNTIME_INTERPRETER_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 */ +#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_ -- GitLab From 5b4b9a0cbd932a7a1b739d4f852c9fa4617fef36 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 16 Mar 2018 09:42:09 +0000 Subject: [PATCH 093/749] Add extra logging for bug 74410240. Test: Manually break in the resolution trampoline and force printing the message. Bug: 74410240 Change-Id: Id3480399fae30840d73ef105707fe1977f0f2a19 --- runtime/entrypoints/entrypoint_utils-inl.h | 5 +- .../quick/quick_trampoline_entrypoints.cc | 92 +++++++++++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) 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 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(-1)) { + CHECK(method == jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt)); + return ""; + } 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( + reinterpret_cast(sp) + callee_frame_size); + const size_t callee_return_pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, type); + uintptr_t caller_pc = *reinterpret_cast( + (reinterpret_cast(sp) + callee_return_pc_offset)); + ArtMethod* outer_method = *caller_sp; + + if (UNLIKELY(caller_pc == reinterpret_cast(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(-1)) { + tag = "special "; + CHECK_EQ(d + 1u, depth); + caller = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt); + CHECK_EQ(caller->GetDexMethodIndex(), method_index); + } else { + ObjPtr dex_cache = caller->GetDexCache(); + ObjPtr 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(); } -- GitLab From 91a834ef4ceea87c08a555fd27df356cd5927db1 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 16 Mar 2018 12:56:36 +0000 Subject: [PATCH 094/749] Disable failing test on table lookup. Test: 641-checker-arraycopy bug: 62611253 Change-Id: I816b8ae73317c547ca69a173209cb1802a066820 --- test/knownfailures.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/knownfailures.json b/test/knownfailures.json index 9f0f95411c..8bb3f9ed73 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -337,7 +337,8 @@ "variant": "optimizing | regalloc_gc" }, { - "tests": ["537-checker-arraycopy"], + "tests": ["537-checker-arraycopy", + "641-checker-arraycopy"], "env_vars": {"ART_READ_BARRIER_TYPE": "TABLELOOKUP"} }, { -- GitLab From 8430e7721fb86341b1cd4aaa29424a87f5e44e9b Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Thu, 15 Mar 2018 15:13:43 +0000 Subject: [PATCH 095/749] ART: Sources for 979-const-method-handle Removes binary dependencies from 979-const-method-handle test and uses ASM instead to build test cases. Test: art/test/run-test --host 979 Bug: 66890674 Change-Id: I239e389b4623b5cd33d80f5ba8d624678bdb614a --- test/979-const-method-handle/build | 43 +++- .../classes/Main.class | Bin 943 -> 0 bytes .../classes/constmethodhandle/ConstTest.class | Bin 1411 -> 0 bytes test/979-const-method-handle/expected.txt | 7 +- test/979-const-method-handle/info.txt | 6 - test/979-const-method-handle/src/Main.java | 85 +++++++ .../annotations/ConstantMethodHandle.java | 58 +++++ .../annotations/ConstantMethodType.java | 38 +++ .../transformer/ConstantTransformer.java | 229 ++++++++++++++++++ 9 files changed, 453 insertions(+), 13 deletions(-) mode change 100644 => 100755 test/979-const-method-handle/build delete mode 100644 test/979-const-method-handle/classes/Main.class delete mode 100644 test/979-const-method-handle/classes/constmethodhandle/ConstTest.class create mode 100644 test/979-const-method-handle/src/Main.java create mode 100644 test/979-const-method-handle/util-src/annotations/ConstantMethodHandle.java create mode 100644 test/979-const-method-handle/util-src/annotations/ConstantMethodType.java create mode 100644 test/979-const-method-handle/util-src/transformer/ConstantTransformer.java diff --git a/test/979-const-method-handle/build b/test/979-const-method-handle/build old mode 100644 new mode 100755 index 495557e3df..ce931a96d1 --- 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 8d6b7d88bba021967ab9983528184d5e7eeffe24..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 943 zcmX^0Z`VEs1_l!bGcE>h1~D!MaRv!?21yPE0Zs-f25EK%87>A{201PUc?Jb821N!X zb_Qj31{FpIHk-`6%o00B24)S-Fh&Lz=lqmZMh1SL%)C^;(%hufqL9R-oK!{zmfXb5 zJVpjFjcA{&#Ii*FoW#6z{osE!&p;*#9dl8pS6jKsW@oK$^hkVr^saS1zv zHV=aigDxY3Fcv%68T5D<^cf7;84P(Cj2MjB8BBN>Oc?|j8CXD;F)|2%tb;hyKPf9U zxrC8H7$FprQIuZ}@tGV#G&8R(KRZ?57wjGnuzQLb8Q602^Rr6}7#Y}npbQ?*yu8#R z=bXgi;?!bB22Bl3q(H%DhzdlfH6sHb!nUyd%oIijmXOE*S4IXwBon{}TQf39Bh;c8 z8d6!1%E-V42{s7FQ3FFih5$}Sf{d_cWKhLn9W)@IeqdzaO3Tbk2`))2$xLQsNW^O~ zG=U&F1;ZoOIGqKt*P4+*7O%q?8Mq;ax>h76moO+YfN}r>0|NsS10yK4F)%XlF)%R5 zGB7eQGB7Z(YHep=+{nPdz{tSQz`(!;78GOPWDsBwWMBd-wPj#sU}9ikklew*B*ZMl zvWbCJh;0W0`!)s+A8oB|44eq(fmuT{jE8}Zft`zig@J>SK{zF| zxF9F7(id!n2iOWm1|$H>5(Us}S*APO-!GhaWTC^N4lxTGjGG1r=hL4ZM!lYyT>h><}YVQg?o zQD$DcQ)y;SN@@{1g9sOcD9Fvc$WBqWDwDCLv|2Yi8T*{DuWsqg9?K>BZDkLKbD|pXV7G1V1r07GN>YZ7mK5v za}tY-t$7%<8FaW9v>0?588}MvAz=V=39>^VqSibN`V0mjj~T*Z7&Wfg8H^bj*b6|B zmy^fHAd2h&s1cwPX3Aj3#bCl<&d4B$lEOkN3sM;w6p(d6`~pp1AbD`=;)X;rh_ArG zV9m(DT9R5^V#vs#sDbQi6wQ$2Yt6{Ok)B!t_BtbjFp6@ppfwMJJ%a-%132=tGB`nv zHezIuM^TGnG{_y+j11Bm&cU8uZZ1eE3Kr^70t{}93@o{cnR$#1Vj9s%N|3_EmcfpT zfr-J3hk=WM8^rSAVQ^t^1+n~i7%UmAK&${p1{UZ1lvG9rexJ;|RKL>Pq|~AiQ2t_M z;Bu`2oqLKqmpM)ERn zf&2z_kr@Ls*cvG$YivNKV=)yJ93VePGRQG-GT1WMLCxR?o4~-pEabcaP%vUh py2B(r7(AhplF;zr-NE3!gTZ$PgZ~bOK#(^;Ars2Lz`(@73IMC?P5A%- diff --git a/test/979-const-method-handle/expected.txt b/test/979-const-method-handle/expected.txt index 573b80da99..335e5cb38c 100644 --- a/test/979-const-method-handle/expected.txt +++ b/test/979-const-method-handle/expected.txt @@ -1,2 +1,5 @@ -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 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..ddac1c8780 --- /dev/null +++ b/test/979-const-method-handle/src/Main.java @@ -0,0 +1,85 @@ +/* + * 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; + } + + 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()); + } +} 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. + * + *

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. + * + *

Suppose a method is annotated as: + * @ConstantMethodHandle( + * kind = ConstantMethodHandle.STATIC_GET, + * owner = "java/lang/Math", + * fieldOrMethodName = "E", + * descriptor = "D" + * ) + * private static MethodHandle getMathE() { + * unreachable(); + * return null; + * } + * 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. + * + *

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 constantMethodHandles; + private final Map constantMethodTypes; + + ConstantBuilder( + int api, + ClassVisitor cv, + Map constantMethodHandles, + Map 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 constantMethodHandles = new HashMap<>(); + final Map 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])); + } +} -- GitLab From fd7b2c240ff78a8b81c12ebc2a0f546304d8b5a5 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Thu, 15 Mar 2018 15:38:38 +0000 Subject: [PATCH 096/749] ART: Fix to ClassLinker::ResolveMethodHandleForField() Adds check for constant method handles to not mutate final fields. Test: art/test/run-test --host 979 Bug: 66890674 Change-Id: I21d55468c703093e9bc7807667257c07271883d3 --- runtime/class_linker.cc | 9 +++++++++ test/979-const-method-handle/expected.txt | 1 + test/979-const-method-handle/src/Main.java | 17 +++++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 72c110a970..fd92c14951 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -8236,29 +8236,34 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForField( DexFile::MethodHandleType handle_type = static_cast(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 +8285,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; diff --git a/test/979-const-method-handle/expected.txt b/test/979-const-method-handle/expected.txt index 335e5cb38c..bc943e368e 100644 --- a/test/979-const-method-handle/expected.txt +++ b/test/979-const-method-handle/expected.txt @@ -3,3 +3,4 @@ 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/src/Main.java b/test/979-const-method-handle/src/Main.java index ddac1c8780..663814f232 100644 --- a/test/979-const-method-handle/src/Main.java +++ b/test/979-const-method-handle/src/Main.java @@ -73,6 +73,17 @@ class Main { 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"); @@ -81,5 +92,11 @@ class Main { 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"); + } } } -- GitLab From fd8bb93874b9288baf288349321b86ebe3953275 Mon Sep 17 00:00:00 2001 From: Yi Kong Date: Mon, 12 Mar 2018 16:52:57 -0700 Subject: [PATCH 097/749] Enable ThinLTO for libart This helps reducing binary size as well as improve performance of libart. On Marlin No LTO ThinLTO Improvement libart.so 5.82MB 5.66MB 2.75% libart.so (64) 7.71MB 7.44MB 3.44% atest (AppStartup) 270.13 265.13 1.85% dex2oat 536.04 533.03 0.56% Bug: 62839002 Test: Benchmark on Marlin Change-Id: Ic386ebceecf89239ad4f28db28b9bfd641efc45c --- runtime/Android.bp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/runtime/Android.bp b/runtime/Android.bp index 590a399738..51fbb2ef30 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -476,6 +476,13 @@ art_cc_library { export_shared_lib_headers: [ "libdexfile", ], + target: { + android: { + lto: { + thin: true, + }, + }, + }, } art_cc_library { -- GitLab From 142f654df0f3af3ed0f1096ecd6c7720769fd0f6 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Fri, 16 Mar 2018 13:16:15 -0700 Subject: [PATCH 098/749] Have run-jdwp-tests try to dump stacks on device We can use 'su' and debuggerd to get backtraces of failing tests. Test: Manual Bug: 70838465 Change-Id: I266b1c9fc4a1af8a7f30d7b1b531e0a85997dcd0 --- tools/run-jdwp-tests.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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 -- GitLab From 5a0eb0cbeeabda48bfef05df9f59a6fd607e1a1e Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Fri, 16 Mar 2018 15:00:19 -0700 Subject: [PATCH 099/749] Minor DCHECK bug fix. Rationale: Should use the utility to test for SIMD result, not a hard is vector test. Test: 623 is regression test for DCHECK fail Change-Id: I1d7949fa25139f8a3734986d5de7989ed32ff2bd --- compiler/optimizing/nodes_vector.h | 2 +- test/623-checker-loop-regressions/src/Main.java | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h index 523bca8d25..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 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"); } -- GitLab From bd3e1cecf449ba9525e78493f3ced838e0f95d5e Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 15 Mar 2018 17:45:03 -0700 Subject: [PATCH 100/749] ART: Change GetOptimizationStatus Do not attempt to load the oat file executable, as this may not be permissible. Instead load it non-executable, and do a status check to predict how a non-encumbered runtime might react to the file. Bug: 74403474 Test: m test-art-host Test: Check logcat for denials Change-Id: I8528005564246712d2c540c5ae89b14f37ca9480 --- runtime/oat_file_assistant.cc | 44 ++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 6 deletions(-) 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 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 -- GitLab From b041a406daf5213ac1d5c9bcdc197d34cba85bf3 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 13 Nov 2017 15:16:22 +0000 Subject: [PATCH 101/749] Use vdex's quickening info when decoding a quickened instruction. bug: 74521989 Test: test.py, 678-quickening Change-Id: I3a85cc6014afcf11889941dcd15b90d4296b8408 --- oatdump/oatdump.cc | 5 +- runtime/art_method.cc | 19 +++ runtime/art_method.h | 1 + runtime/common_throws.cc | 50 +++---- runtime/verifier/method_verifier-inl.h | 2 +- runtime/verifier/method_verifier.cc | 175 ++++--------------------- runtime/verifier/method_verifier.h | 31 +---- test/678-quickening/expected.txt | 1 + test/678-quickening/info.txt | 1 + test/678-quickening/run | 18 +++ test/678-quickening/src-art/Main.java | 79 +++++++++++ test/knownfailures.json | 3 +- 12 files changed, 177 insertions(+), 208 deletions(-) create mode 100644 test/678-quickening/expected.txt create mode 100644 test/678-quickening/info.txt create mode 100644 test/678-quickening/run create mode 100644 test/678-quickening/src-art/Main.java 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 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/art_method.cc b/runtime/art_method.cc index f3c495957f..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" @@ -578,6 +579,24 @@ ArrayRef ArtMethod::GetQuickenedInfo() { GetDexMethodIndex()); } +uint16_t ArtMethod::GetIndexFromQuickening(uint32_t dex_pc) { + ArrayRef 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 bd9b64df36..64d293200f 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -683,6 +683,7 @@ class ArtMethod FINAL { } ArrayRef 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/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/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 dex_cache(hs.NewHandle(m->GetDexCache())); - Handle 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 dex_cache(hs.NewHandle(m->GetDexCache())); - Handle 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(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(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 @@ -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 return_type_class = can_load_classes_ - ? mirror_method_->ResolveReturnType() - : mirror_method_->LookupResolvedReturnType(); + ? method_being_verified_->ResolveReturnType() + : method_being_verified_->LookupResolvedReturnType(); if (return_type_class != nullptr) { - return_type_ = &FromClass(mirror_method_->GetReturnTypeDescriptor(), + return_type_ = &FromClass(method_being_verified_->GetReturnTypeDescriptor(), return_type_class.Ptr(), return_type_class->CannotBeAssignedFromOtherTypes()); } else { @@ -5347,8 +5224,8 @@ const RegType& MethodVerifier::GetDeclaringClass() { const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_); const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_)); - if (mirror_method_ != nullptr) { - mirror::Class* klass = mirror_method_->GetDeclaringClass(); + if (method_being_verified_ != nullptr) { + mirror::Class* klass = method_being_verified_->GetDeclaringClass(); declaring_class_ = &FromClass(descriptor, klass, klass->CannotBeAssignedFromOtherTypes()); } else { declaring_class_ = ®_types_.FromDescriptor(GetClassLoader(), descriptor, false); diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 9a94297942..4c9518b0ec 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -162,20 +162,11 @@ class MethodVerifier { }; // Fills 'monitor_enter_dex_pcs' with the dex pcs of the monitor-enter instructions corresponding // to the locks held at 'dex_pc' in method 'm'. + // Note: this is the only situation where the verifier will visit quickened instructions. static void FindLocksAtDexPc(ArtMethod* m, uint32_t dex_pc, std::vector* 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>& 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/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/knownfailures.json b/test/knownfailures.json index 8bb3f9ed73..a7e76d131e 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -953,7 +953,8 @@ "description": ["Failing on RI. Needs further investigating."] }, { - "tests": ["616-cha-unloading"], + "tests": ["616-cha-unloading", + "678-quickening"], "variant": "jvm", "description": ["Doesn't run on RI."] }, -- GitLab From cebbb65c19a511e76c5024b9e6713bf74be3f7c3 Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Mon, 19 Mar 2018 15:15:06 +0000 Subject: [PATCH 102/749] Fix find_api_violations script. - The log tag seems to have changed, no longer zygote[64] - Add option to output just method signatures, for adding to the light greylist. Test: $ find_api_violations.pl -s -b --nolightgrey < bugreport.txt Change-Id: I31fd21c0d77e23188c010a412db41b4bcdc53ee8 --- tools/hiddenapi/find_api_violations.pl | 27 +++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/tools/hiddenapi/find_api_violations.pl b/tools/hiddenapi/find_api_violations.pl index a022999ea5..caa4f543a1 100755 --- a/tools/hiddenapi/find_api_violations.pl +++ b/tools/hiddenapi/find_api_violations.pl @@ -52,6 +52,10 @@ get all packages names, you should process the logcat from device boot time. Process a bugreport, rather than raw logcat +=item --short|-s + +Output API signatures only, don't include CSV header/package names/list name. + =item --help =back @@ -62,12 +66,14 @@ my $lightgrey = 1; my $darkgrey = 1; my $black = 1; my $bugreport = 0; +my $short = 0; my $help = 0; GetOptions("lightgrey!" => \$lightgrey, "darkgrey!" => \$darkgrey, "black!" => \$black, "bugreport|b" => \$bugreport, + "short|s" => \$short, "help" => \$help) or pod2usage(q(-verbose) => 1); @@ -90,7 +96,7 @@ if ($bugreport) { } my $procmap = {}; -print "package,symbol,list\n"; +print "package,symbol,list\n" unless $short; while (my $line = <>) { chomp $line; last if $bugreport and $line =~ m/^------ \d+\.\d+s was the duration of 'SYSTEM LOG' ------/; @@ -110,14 +116,17 @@ while (my $line = <>) { } $procmap->{$new_pid} = $package; } - if ($tag eq "zygote" || $tag eq "zygote64") { - if ($msg =~ m/Accessing hidden (\w+) (L.*?) \((.*list), (.*?)\)/) { - my ($member_type, $symbol, $list, $access_type) = ($1, $2, $3, $4); - my $package = $procmap->{$pid} || "unknown($pid)"; - print "$package,$symbol,$list\n" - if (($list =~ m/light/ && $lightgrey) - || ($list =~ m/dark/ && $darkgrey) - || ($list =~ m/black/ && $black)); + if ($msg =~ m/Accessing hidden (\w+) (L.*?) \((.*list), (.*?)\)/) { + my ($member_type, $symbol, $list, $access_type) = ($1, $2, $3, $4); + my $package = $procmap->{$pid} || "unknown($pid)"; + if (($list =~ m/light/ && $lightgrey) + || ($list =~ m/dark/ && $darkgrey) + || ($list =~ m/black/ && $black)) { + if ($short) { + print "$symbol\n"; + } else { + print "$package,$symbol,$list\n" + } } } } -- GitLab From be0c7cfdbb2b73d6beb1284fdedb8e07766630e9 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 19 Mar 2018 13:40:56 +0000 Subject: [PATCH 103/749] Fix running some run-tests with -Xjitthreshold:0 Do not JIT non-compilable methods. Honor the $noinline$ directive for JIT and fix core image recognition. Make sure the 597-deopt-invoke-stub that actually relies on its own -Xjitthreshold: overrides the threshold we pass, work around already running compiled code in 570-checker-osr (and drop obsolete "doThrow" pattern replaced by $noinline$) and add a few necessary $noinline$ directives. Test: for t in \ 461-get-reference-vreg \ 536-checker-needs-access-check \ 570-checker-osr \ 597-deopt-invoke-stub \ 655-jit-clinit \ ; do \ art/test/run-test --host --jit $t \ --runtime-option -Xjitthreshold:0; \ done Test: testrunner.py --host --jit Bug: 62611253 Change-Id: Ia0a05c93e3fc8d913fe8556d3d7f23e7e61076c2 --- compiler/driver/compiler_driver.h | 15 +++++-- compiler/optimizing/inliner.cc | 12 +++--- runtime/jit/jit.cc | 14 +++--- .../get_reference_vreg_jni.cc | 8 ++-- test/461-get-reference-vreg/src/Main.java | 16 +++---- test/570-checker-osr/src/Main.java | 43 +++++++++++++------ test/597-deopt-invoke-stub/run | 4 +- test/655-jit-clinit/src/Main.java | 8 ++-- 8 files changed, 75 insertions(+), 45 deletions(-) diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 8db892bf18..46d8373c52 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -371,9 +371,18 @@ class CompilerDriver { // Is `boot_image_filename` the name of a core image (small boot // image used for ART testing only)? static bool IsCoreImageFilename(const std::string& boot_image_filename) { - // TODO: This is under-approximating... - return android::base::EndsWith(boot_image_filename, "core.art") - || android::base::EndsWith(boot_image_filename, "core-optimizing.art"); + // Look for "core.art" or "core-*.art". + if (android::base::EndsWith(boot_image_filename, "core.art")) { + return true; + } + if (!android::base::EndsWith(boot_image_filename, ".art")) { + return false; + } + size_t dash_pos = boot_image_filename.find_last_of("-/"); + if (dash_pos == std::string::npos || boot_image_filename[dash_pos] != '-') { + return false; + } + return (dash_pos >= 4u) && (boot_image_filename.compare(dash_pos - 4u, 4u, "core") == 0); } optimizer::DexToDexCompiler& GetDexToDexCompiler() { diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 4fc7262265..8b10a78212 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -147,10 +147,11 @@ void HInliner::Run() { // that this method is actually inlined; // - if a method's name contains the substring "$noinline$", do not // inline that method. - // We limit this to AOT compilation, as the JIT may or may not inline + // We limit the latter to AOT compilation, as the JIT may or may not inline // depending on the state of classes at runtime. - const bool honor_inlining_directives = - IsCompilingWithCoreImage() && Runtime::Current()->IsAotCompiler(); + const bool honor_noinline_directives = IsCompilingWithCoreImage(); + const bool honor_inline_directives = + honor_noinline_directives && Runtime::Current()->IsAotCompiler(); // Keep a copy of all blocks when starting the visit. ArenaVector blocks = graph_->GetReversePostOrder(); @@ -164,18 +165,19 @@ void HInliner::Run() { HInvoke* call = instruction->AsInvoke(); // As long as the call is not intrinsified, it is worth trying to inline. if (call != nullptr && call->GetIntrinsic() == Intrinsics::kNone) { - if (honor_inlining_directives) { + if (honor_noinline_directives) { // Debugging case: directives in method names control or assert on inlining. std::string callee_name = outer_compilation_unit_.GetDexFile()->PrettyMethod( call->GetDexMethodIndex(), /* with_signature */ false); // Tests prevent inlining by having $noinline$ in their method names. if (callee_name.find("$noinline$") == std::string::npos) { - if (!TryInline(call)) { + if (!TryInline(call) && honor_inline_directives) { bool should_have_inlined = (callee_name.find("$inline$") != std::string::npos); CHECK(!should_have_inlined) << "Could not inline " << callee_name; } } } else { + DCHECK(!honor_inline_directives); // Normal case: try to inline. TryInline(call); } diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index a757960e3e..23cf071d56 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -717,12 +717,14 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ void Jit::MethodEntered(Thread* thread, ArtMethod* method) { Runtime* runtime = Runtime::Current(); if (UNLIKELY(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse())) { - // The compiler requires a ProfilingInfo object. - ProfilingInfo::Create(thread, - method->GetInterfaceMethodIfProxy(kRuntimePointerSize), - /* retry_allocation */ true); - JitCompileTask compile_task(method, JitCompileTask::kCompile); - compile_task.Run(thread); + ArtMethod* np_method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize); + DCHECK(!np_method->IsNative()); + if (np_method->IsCompilable()) { + // The compiler requires a ProfilingInfo object. + ProfilingInfo::Create(thread, np_method, /* retry_allocation */ true); + JitCompileTask compile_task(method, JitCompileTask::kCompile); + compile_task.Run(thread); + } return; } diff --git a/test/461-get-reference-vreg/get_reference_vreg_jni.cc b/test/461-get-reference-vreg/get_reference_vreg_jni.cc index b2cad67829..7eb3fe5ae6 100644 --- a/test/461-get-reference-vreg/get_reference_vreg_jni.cc +++ b/test/461-get-reference-vreg/get_reference_vreg_jni.cc @@ -37,21 +37,21 @@ class TestVisitor : public StackVisitor { ArtMethod* m = GetMethod(); std::string m_name(m->GetName()); - if (m_name.compare("testThisWithInstanceCall") == 0) { + if (m_name.compare("$noinline$testThisWithInstanceCall") == 0) { found_method_index_ = 1; uint32_t value = 0; CHECK(GetVReg(m, 1, kReferenceVReg, &value)); CHECK_EQ(reinterpret_cast(value), this_value_); CHECK_EQ(GetThisObject(), this_value_); - } else if (m_name.compare("testThisWithStaticCall") == 0) { + } else if (m_name.compare("$noinline$testThisWithStaticCall") == 0) { found_method_index_ = 2; uint32_t value = 0; CHECK(GetVReg(m, 1, kReferenceVReg, &value)); - } else if (m_name.compare("testParameter") == 0) { + } else if (m_name.compare("$noinline$testParameter") == 0) { found_method_index_ = 3; uint32_t value = 0; CHECK(GetVReg(m, 1, kReferenceVReg, &value)); - } else if (m_name.compare("testObjectInScope") == 0) { + } else if (m_name.compare("$noinline$testObjectInScope") == 0) { found_method_index_ = 4; uint32_t value = 0; CHECK(GetVReg(m, 0, kReferenceVReg, &value)); diff --git a/test/461-get-reference-vreg/src/Main.java b/test/461-get-reference-vreg/src/Main.java index f7d43568d5..a14b0e96ed 100644 --- a/test/461-get-reference-vreg/src/Main.java +++ b/test/461-get-reference-vreg/src/Main.java @@ -18,19 +18,19 @@ public class Main { public Main() { } - int testThisWithInstanceCall() { + int $noinline$testThisWithInstanceCall() { return doNativeCallRef(); } - int testThisWithStaticCall() { + int $noinline$testThisWithStaticCall() { return doStaticNativeCallRef(); } - static int testParameter(Object a) { + static int $noinline$testParameter(Object a) { return doStaticNativeCallRef(); } - static int testObjectInScope() { + static int $noinline$testObjectInScope() { Object a = array[0]; return doStaticNativeCallRef(); } @@ -41,19 +41,19 @@ public class Main { public static void main(String[] args) { System.loadLibrary(args[0]); Main rm = new Main(); - if (rm.testThisWithInstanceCall() != 1) { + if (rm.$noinline$testThisWithInstanceCall() != 1) { throw new Error("Expected 1"); } - if (rm.testThisWithStaticCall() != 2) { + if (rm.$noinline$testThisWithStaticCall() != 2) { throw new Error("Expected 2"); } - if (testParameter(new Object()) != 3) { + if ($noinline$testParameter(new Object()) != 3) { throw new Error("Expected 3"); } - if (testObjectInScope() != 4) { + if ($noinline$testObjectInScope() != 4) { throw new Error("Expected 4"); } } diff --git a/test/570-checker-osr/src/Main.java b/test/570-checker-osr/src/Main.java index 4de563469d..78f3c32d2f 100644 --- a/test/570-checker-osr/src/Main.java +++ b/test/570-checker-osr/src/Main.java @@ -61,51 +61,71 @@ public class Main { } public static int $noinline$returnInt() { - if (doThrow) throw new Error(""); + // If we are running in non-JIT mode, or were unlucky enough to get this method + // already JITted, skip the wait for OSR code. + boolean interpreting = isInInterpreter("$noinline$returnInt"); int i = 0; for (; i < 100000; ++i) { } - while (!isInOsrCode("$noinline$returnInt")) {} + if (interpreting) { + while (!isInOsrCode("$noinline$returnInt")) {} + } System.out.println(i); return 53; } public static float $noinline$returnFloat() { - if (doThrow) throw new Error(""); + // If we are running in non-JIT mode, or were unlucky enough to get this method + // already JITted, skip the wait for OSR code. + boolean interpreting = isInInterpreter("$noinline$returnFloat"); int i = 0; for (; i < 200000; ++i) { } - while (!isInOsrCode("$noinline$returnFloat")) {} + if (interpreting) { + while (!isInOsrCode("$noinline$returnFloat")) {} + } System.out.println(i); return 42.2f; } public static double $noinline$returnDouble() { - if (doThrow) throw new Error(""); + // If we are running in non-JIT mode, or were unlucky enough to get this method + // already JITted, skip the wait for OSR code. + boolean interpreting = isInInterpreter("$noinline$returnDouble"); int i = 0; for (; i < 300000; ++i) { } - while (!isInOsrCode("$noinline$returnDouble")) {} + if (interpreting) { + while (!isInOsrCode("$noinline$returnDouble")) {} + } System.out.println(i); return Double.longBitsToDouble(0xF000000000001111L); } public static long $noinline$returnLong() { - if (doThrow) throw new Error(""); + // If we are running in non-JIT mode, or were unlucky enough to get this method + // already JITted, skip the wait for OSR code. + boolean interpreting = isInInterpreter("$noinline$returnLong"); int i = 0; for (; i < 400000; ++i) { } - while (!isInOsrCode("$noinline$returnLong")) {} + if (interpreting) { + while (!isInOsrCode("$noinline$returnLong")) {} + } System.out.println(i); return 0xFFFF000000001111L; } public static void $noinline$deopt() { - if (doThrow) throw new Error(""); + // If we are running in non-JIT mode, or were unlucky enough to get this method + // already JITted, skip the wait for OSR code. + boolean interpreting = isInInterpreter("$noinline$deopt"); int i = 0; for (; i < 100000; ++i) { } - while (!isInOsrCode("$noinline$deopt")) {} + if (interpreting) { + while (!isInOsrCode("$noinline$deopt")) {} + } DeoptimizationController.startDeoptimization(); } @@ -242,7 +262,6 @@ public class Main { public static void $opt$noinline$testOsrInlineLoop(String[] args) { // Regression test for inlining a method with a loop to a method without a loop in OSR mode. - if (doThrow) throw new Error(); assertIntEquals(12, $opt$inline$testRemoveSuspendCheck(12, 5)); // Since we cannot have a loop directly in this method, we need to force the OSR // compilation from native code. @@ -280,8 +299,6 @@ public class Main { public static native boolean isInInterpreter(String methodName); public static native void ensureHasProfilingInfo(String methodName); public static native void ensureHasOsrCode(String methodName); - - public static boolean doThrow = false; } class SubMain extends Main { diff --git a/test/597-deopt-invoke-stub/run b/test/597-deopt-invoke-stub/run index 53b7c4cc71..990c30e70c 100644 --- a/test/597-deopt-invoke-stub/run +++ b/test/597-deopt-invoke-stub/run @@ -16,6 +16,6 @@ # In order to test deoptimizing at quick-to-interpreter bridge, # we want to run in debuggable mode with jit compilation. -# We also bump up the jit threshold to 10 to make sure that the method +# We also bump up the jit threshold to 10000 to make sure that the method # that should be interpreted is not compiled. -exec ${RUN} --jit --runtime-option -Xjitthreshold:10000 -Xcompiler-option --debuggable "${@}" +exec ${RUN} "${@}" --jit --runtime-option -Xjitthreshold:10000 -Xcompiler-option --debuggable diff --git a/test/655-jit-clinit/src/Main.java b/test/655-jit-clinit/src/Main.java index 2fb8f2a86e..7fe5084b3f 100644 --- a/test/655-jit-clinit/src/Main.java +++ b/test/655-jit-clinit/src/Main.java @@ -20,7 +20,7 @@ public class Main { if (!hasJit()) { return; } - Foo.hotMethod(); + Foo.$noinline$hotMethod(); } public native static boolean hasJitCompiledEntrypoint(Class cls, String methodName); @@ -28,7 +28,7 @@ public class Main { } class Foo { - static void hotMethod() { + static void $noinline$hotMethod() { for (int i = 0; i < array.length; ++i) { array[i] = array; } @@ -36,8 +36,8 @@ class Foo { static { array = new Object[10000]; - while (!Main.hasJitCompiledEntrypoint(Foo.class, "hotMethod")) { - Foo.hotMethod(); + while (!Main.hasJitCompiledEntrypoint(Foo.class, "$noinline$hotMethod")) { + Foo.$noinline$hotMethod(); try { // Sleep to give a chance for the JIT to compile `hotMethod`. Thread.sleep(100); -- GitLab From 312f3b2fd0094c028a7d243b116947a35a745806 Mon Sep 17 00:00:00 2001 From: David Sehr Date: Mon, 19 Mar 2018 08:39:26 -0700 Subject: [PATCH 104/749] Move some remaining dex utilities There were several utilities related to building/walking/testing dex files that were not in libdexfile. This change consolidates these. Bug: 22322814 Test: make -j 50 test-art-host Change-Id: Id76e9179d03b8ec7d67f7e0f267121f54f0ec2e0 --- compiler/Android.bp | 2 -- compiler/dex/dex_to_dex_compiler.cc | 2 +- compiler/dex/dex_to_dex_compiler.h | 2 +- compiler/dex/inline_method_analyser.h | 2 +- compiler/dex/verification_results.h | 4 ++-- compiler/dex/verified_method.h | 2 +- compiler/driver/compiler_driver.h | 4 ++-- compiler/linker/arm/relative_patcher_arm_base.h | 2 +- compiler/linker/linker_patch.h | 2 +- compiler/linker/relative_patcher.h | 2 +- compiler/linker/relative_patcher_test.h | 4 ++-- compiler/optimizing/block_builder.cc | 2 +- compiler/optimizing/code_generator.cc | 2 +- compiler/optimizing/code_generator.h | 4 ++-- compiler/optimizing/code_generator_arm64.h | 4 ++-- compiler/optimizing/code_generator_arm_vixl.h | 4 ++-- compiler/optimizing/code_generator_mips.h | 4 ++-- compiler/optimizing/code_generator_mips64.h | 2 +- compiler/optimizing/instruction_builder.cc | 2 +- compiler/optimizing/nodes.h | 2 +- compiler/optimizing/ssa_builder.cc | 2 +- compiler/utils/atomic_dex_ref_map-inl.h | 6 +++--- compiler/utils/atomic_dex_ref_map_test.cc | 2 +- dex2oat/dex2oat_image_test.cc | 2 +- dex2oat/dex2oat_test.cc | 2 +- dex2oat/linker/multi_oat_relative_patcher.h | 2 +- dex2oat/linker/oat_writer.h | 8 ++++---- dex2oat/linker/oat_writer_test.cc | 2 +- libdexfile/Android.bp | 3 +++ {runtime => libdexfile/dex}/bytecode_utils.h | 8 ++++---- {runtime => libdexfile/dex}/class_reference.h | 6 +++--- {runtime => libdexfile/dex}/method_reference.h | 6 +++--- {runtime => libdexfile/dex}/string_reference.h | 6 +++--- .../utils => libdexfile/dex}/string_reference_test.cc | 2 +- .../utils => libdexfile/dex}/test_dex_file_builder.h | 10 ++++------ .../dex}/test_dex_file_builder_test.cc | 1 - {runtime => libdexfile/dex}/type_reference.h | 8 ++++---- oatdump/oatdump.cc | 2 +- profman/boot_image_profile.cc | 4 ++-- profman/profman.cc | 4 ++-- runtime/aot_class_linker.cc | 2 +- runtime/compiler_callbacks.h | 2 +- runtime/dex_to_dex_decompiler.cc | 2 +- .../entrypoints/quick/quick_trampoline_entrypoints.cc | 2 +- runtime/jit/jit_code_cache.h | 2 +- runtime/jit/profile_compilation_info.h | 4 ++-- runtime/jit/profile_compilation_info_test.cc | 4 ++-- runtime/jit/profile_saver.h | 2 +- runtime/runtime_callbacks_test.cc | 2 +- runtime/verifier/method_verifier.h | 2 +- test/595-profile-saving/profile-saving.cc | 2 +- test/664-aget-verifier/aget-verifier.cc | 2 +- 52 files changed, 83 insertions(+), 85 deletions(-) rename {runtime => libdexfile/dex}/bytecode_utils.h (96%) rename {runtime => libdexfile/dex}/class_reference.h (88%) rename {runtime => libdexfile/dex}/method_reference.h (96%) rename {runtime => libdexfile/dex}/string_reference.h (94%) rename {compiler/utils => libdexfile/dex}/string_reference_test.cc (99%) rename {compiler/utils => libdexfile/dex}/test_dex_file_builder.h (98%) rename {compiler/utils => libdexfile/dex}/test_dex_file_builder_test.cc (99%) rename {runtime => libdexfile/dex}/type_reference.h (90%) diff --git a/compiler/Android.bp b/compiler/Android.bp index c4d538fc88..e42261c556 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -359,9 +359,7 @@ art_cc_test { "utils/atomic_dex_ref_map_test.cc", "utils/dedupe_set_test.cc", "utils/intrusive_forward_list_test.cc", - "utils/string_reference_test.cc", "utils/swap_space_test.cc", - "utils/test_dex_file_builder_test.cc", "verifier_deps_test.cc", "jni/jni_cfi_test.cc", diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index 0caf1b14a6..be8641fd86 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -24,8 +24,8 @@ #include "base/logging.h" // For VLOG #include "base/macros.h" #include "base/mutex.h" -#include "bytecode_utils.h" #include "compiled_method.h" +#include "dex/bytecode_utils.h" #include "dex/dex_file-inl.h" #include "dex/dex_instruction-inl.h" #include "dex_to_dex_decompiler.h" diff --git a/compiler/dex/dex_to_dex_compiler.h b/compiler/dex/dex_to_dex_compiler.h index 7df09f140c..7536c3126a 100644 --- a/compiler/dex/dex_to_dex_compiler.h +++ b/compiler/dex/dex_to_dex_compiler.h @@ -24,8 +24,8 @@ #include "base/bit_vector.h" #include "dex/dex_file.h" #include "dex/invoke_type.h" +#include "dex/method_reference.h" #include "handle.h" -#include "method_reference.h" #include "quicken_info.h" namespace art { diff --git a/compiler/dex/inline_method_analyser.h b/compiler/dex/inline_method_analyser.h index 837cc85456..e1d652a642 100644 --- a/compiler/dex/inline_method_analyser.h +++ b/compiler/dex/inline_method_analyser.h @@ -21,7 +21,7 @@ #include "base/mutex.h" #include "dex/dex_file.h" #include "dex/dex_instruction.h" -#include "method_reference.h" +#include "dex/method_reference.h" /* * NOTE: This code is part of the quick compiler. It lives in the runtime diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h index 9e4192a0fa..56f00309c0 100644 --- a/compiler/dex/verification_results.h +++ b/compiler/dex/verification_results.h @@ -24,8 +24,8 @@ #include "base/macros.h" #include "base/mutex.h" #include "base/safe_map.h" -#include "class_reference.h" -#include "method_reference.h" +#include "dex/class_reference.h" +#include "dex/method_reference.h" #include "utils/atomic_dex_ref_map.h" namespace art { diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h index ecbeed3ccf..f04392d44e 100644 --- a/compiler/dex/verified_method.h +++ b/compiler/dex/verified_method.h @@ -22,7 +22,7 @@ #include "base/mutex.h" #include "base/safe_map.h" #include "dex/dex_file.h" -#include "method_reference.h" +#include "dex/method_reference.h" namespace art { diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 8db892bf18..fffa3b345f 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -33,14 +33,14 @@ #include "base/quasi_atomic.h" #include "base/safe_map.h" #include "base/timing_logger.h" -#include "class_reference.h" #include "class_status.h" #include "compiler.h" +#include "dex/class_reference.h" #include "dex/dex_file.h" #include "dex/dex_file_types.h" #include "dex/dex_to_dex_compiler.h" +#include "dex/method_reference.h" #include "driver/compiled_method_storage.h" -#include "method_reference.h" #include "thread_pool.h" #include "utils/atomic_dex_ref_map.h" #include "utils/dex_cache_arrays_layout.h" diff --git a/compiler/linker/arm/relative_patcher_arm_base.h b/compiler/linker/arm/relative_patcher_arm_base.h index b0064d1554..ee09bf96b3 100644 --- a/compiler/linker/arm/relative_patcher_arm_base.h +++ b/compiler/linker/arm/relative_patcher_arm_base.h @@ -21,8 +21,8 @@ #include #include "base/safe_map.h" +#include "dex/method_reference.h" #include "linker/relative_patcher.h" -#include "method_reference.h" namespace art { namespace linker { diff --git a/compiler/linker/linker_patch.h b/compiler/linker/linker_patch.h index 36051d2726..710d8a690a 100644 --- a/compiler/linker/linker_patch.h +++ b/compiler/linker/linker_patch.h @@ -23,7 +23,7 @@ #include #include "base/bit_utils.h" -#include "method_reference.h" +#include "dex/method_reference.h" namespace art { diff --git a/compiler/linker/relative_patcher.h b/compiler/linker/relative_patcher.h index 548e12896a..b58e3dffbd 100644 --- a/compiler/linker/relative_patcher.h +++ b/compiler/linker/relative_patcher.h @@ -23,7 +23,7 @@ #include "arch/instruction_set_features.h" #include "base/array_ref.h" #include "base/macros.h" -#include "method_reference.h" +#include "dex/method_reference.h" namespace art { diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h index 9e9d14af9e..d21f2795b9 100644 --- a/compiler/linker/relative_patcher_test.h +++ b/compiler/linker/relative_patcher_test.h @@ -23,15 +23,15 @@ #include "base/macros.h" #include "compiled_method-inl.h" #include "dex/verification_results.h" +#include "dex/method_reference.h" +#include "dex/string_reference.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "globals.h" #include "gtest/gtest.h" #include "linker/relative_patcher.h" -#include "method_reference.h" #include "oat.h" #include "oat_quick_method_header.h" -#include "string_reference.h" #include "vector_output_stream.h" namespace art { diff --git a/compiler/optimizing/block_builder.cc b/compiler/optimizing/block_builder.cc index 2b568bcffd..95f2e98ac6 100644 --- a/compiler/optimizing/block_builder.cc +++ b/compiler/optimizing/block_builder.cc @@ -17,7 +17,7 @@ #include "block_builder.h" #include "base/logging.h" // FOR VLOG. -#include "bytecode_utils.h" +#include "dex/bytecode_utils.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_file_exception_helpers.h" #include "quicken_info.h" diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 0fcc9c6d05..c2ae7646b5 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -44,9 +44,9 @@ #include "base/bit_utils_iterator.h" #include "base/casts.h" #include "base/leb128.h" -#include "bytecode_utils.h" #include "class_linker.h" #include "compiled_method.h" +#include "dex/bytecode_utils.h" #include "dex/code_item_accessors-inl.h" #include "dex/verified_method.h" #include "driver/compiler_driver.h" diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 7031483a77..a4873202b2 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -24,6 +24,8 @@ #include "base/bit_field.h" #include "base/bit_utils.h" #include "base/enums.h" +#include "dex/string_reference.h" +#include "dex/type_reference.h" #include "globals.h" #include "graph_visualizer.h" #include "locations.h" @@ -33,8 +35,6 @@ #include "read_barrier_option.h" #include "stack.h" #include "stack_map.h" -#include "string_reference.h" -#include "type_reference.h" #include "utils/label.h" namespace art { diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index b59ccd9034..a8a9802f9a 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -21,11 +21,11 @@ #include "code_generator.h" #include "common_arm64.h" #include "dex/dex_file_types.h" +#include "dex/string_reference.h" +#include "dex/type_reference.h" #include "driver/compiler_options.h" #include "nodes.h" #include "parallel_move_resolver.h" -#include "string_reference.h" -#include "type_reference.h" #include "utils/arm64/assembler_arm64.h" // TODO(VIXL): Make VIXL compile with -Wshadow. diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 2d8f6a6c78..6a07e36022 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -20,11 +20,11 @@ #include "base/enums.h" #include "code_generator.h" #include "common_arm.h" +#include "dex/string_reference.h" +#include "dex/type_reference.h" #include "driver/compiler_options.h" #include "nodes.h" #include "parallel_move_resolver.h" -#include "string_reference.h" -#include "type_reference.h" #include "utils/arm/assembler_arm_vixl.h" // TODO(VIXL): make vixl clean wrt -Wshadow. diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index d90689695b..1f1743ff9e 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -19,11 +19,11 @@ #include "code_generator.h" #include "dex/dex_file_types.h" +#include "dex/string_reference.h" +#include "dex/type_reference.h" #include "driver/compiler_options.h" #include "nodes.h" #include "parallel_move_resolver.h" -#include "string_reference.h" -#include "type_reference.h" #include "utils/mips/assembler_mips.h" namespace art { diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index d1da1cec15..74c947e5d5 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -18,10 +18,10 @@ #define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_MIPS64_H_ #include "code_generator.h" +#include "dex/type_reference.h" #include "driver/compiler_options.h" #include "nodes.h" #include "parallel_move_resolver.h" -#include "type_reference.h" #include "utils/mips64/assembler_mips64.h" namespace art { diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index a38e2717cf..c7aef3779d 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -20,9 +20,9 @@ #include "base/arena_bit_vector.h" #include "base/bit_vector-inl.h" #include "block_builder.h" -#include "bytecode_utils.h" #include "class_linker.h" #include "data_type-inl.h" +#include "dex/bytecode_utils.h" #include "dex/dex_instruction-inl.h" #include "driver/compiler_driver-inl.h" #include "driver/dex_compilation_unit.h" diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index cbf748d4fd..a8fcea2097 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -34,12 +34,12 @@ #include "dex/dex_file.h" #include "dex/dex_file_types.h" #include "dex/invoke_type.h" +#include "dex/method_reference.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "handle.h" #include "handle_scope.h" #include "intrinsics_enum.h" #include "locations.h" -#include "method_reference.h" #include "mirror/class.h" #include "offsets.h" #include "utils/intrusive_forward_list.h" diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index cb384768b7..dd54468217 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -16,8 +16,8 @@ #include "ssa_builder.h" -#include "bytecode_utils.h" #include "data_type-inl.h" +#include "dex/bytecode_utils.h" #include "mirror/class-inl.h" #include "nodes.h" #include "reference_type_propagation.h" diff --git a/compiler/utils/atomic_dex_ref_map-inl.h b/compiler/utils/atomic_dex_ref_map-inl.h index 7023b9a0e8..7977e8201f 100644 --- a/compiler/utils/atomic_dex_ref_map-inl.h +++ b/compiler/utils/atomic_dex_ref_map-inl.h @@ -21,10 +21,10 @@ #include -#include "class_reference.h" +#include "dex/class_reference.h" #include "dex/dex_file-inl.h" -#include "method_reference.h" -#include "type_reference.h" +#include "dex/method_reference.h" +#include "dex/type_reference.h" namespace art { diff --git a/compiler/utils/atomic_dex_ref_map_test.cc b/compiler/utils/atomic_dex_ref_map_test.cc index d58d60b4f3..4e1ef1248d 100644 --- a/compiler/utils/atomic_dex_ref_map_test.cc +++ b/compiler/utils/atomic_dex_ref_map_test.cc @@ -20,7 +20,7 @@ #include "common_runtime_test.h" #include "dex/dex_file-inl.h" -#include "method_reference.h" +#include "dex/method_reference.h" #include "scoped_thread_state_change-inl.h" namespace art { diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc index d8952826b3..6f1224916f 100644 --- a/dex2oat/dex2oat_image_test.cc +++ b/dex2oat/dex2oat_image_test.cc @@ -33,8 +33,8 @@ #include "dex/art_dex_file_loader.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" +#include "dex/method_reference.h" #include "jit/profile_compilation_info.h" -#include "method_reference.h" #include "runtime.h" namespace art { diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 09ff14e4ba..5590c8b3ab 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -30,9 +30,9 @@ #include "base/macros.h" #include "base/mutex-inl.h" #include "base/utils.h" -#include "bytecode_utils.h" #include "dex/art_dex_file_loader.h" #include "dex/base64_test_util.h" +#include "dex/bytecode_utils.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" diff --git a/dex2oat/linker/multi_oat_relative_patcher.h b/dex2oat/linker/multi_oat_relative_patcher.h index 2413c6e881..bd33b95318 100644 --- a/dex2oat/linker/multi_oat_relative_patcher.h +++ b/dex2oat/linker/multi_oat_relative_patcher.h @@ -20,7 +20,7 @@ #include "arch/instruction_set.h" #include "base/safe_map.h" #include "debug/method_debug_info.h" -#include "method_reference.h" +#include "dex/method_reference.h" #include "linker/relative_patcher.h" namespace art { diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index 120ea568d4..7b7bd13c18 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -27,15 +27,15 @@ #include "base/os.h" #include "base/safe_map.h" #include "compiler.h" -#include "dex/compact_dex_level.h" #include "debug/debug_info.h" +#include "dex/compact_dex_level.h" +#include "dex/method_reference.h" +#include "dex/string_reference.h" +#include "dex/type_reference.h" #include "linker/relative_patcher.h" // For RelativePatcherTargetProvider. #include "mem_map.h" -#include "method_reference.h" #include "mirror/class.h" #include "oat.h" -#include "string_reference.h" -#include "type_reference.h" namespace art { diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 6cd901372b..6e95393e80 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -28,6 +28,7 @@ #include "debug/method_debug_info.h" #include "dex/dex_file_loader.h" #include "dex/quick_compiler_callbacks.h" +#include "dex/test_dex_file_builder.h" #include "dex/verification_results.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" @@ -45,7 +46,6 @@ #include "oat_file-inl.h" #include "oat_writer.h" #include "scoped_thread_state_change-inl.h" -#include "utils/test_dex_file_builder.h" #include "vdex_file.h" namespace art { diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp index 988ee03b55..3fd61ee251 100644 --- a/libdexfile/Android.bp +++ b/libdexfile/Android.bp @@ -84,6 +84,7 @@ gensrcs { "dex/dex_instruction.h", "dex/dex_instruction_utils.h", "dex/invoke_type.h", + "dex/method_reference.h", ], output_extension: "operator_out.cc", } @@ -116,10 +117,12 @@ art_cc_test { "dex/compact_dex_file_test.cc", "dex/compact_offset_table_test.cc", "dex/descriptors_names_test.cc", + "dex/test_dex_file_builder_test.cc", "dex/dex_file_loader_test.cc", "dex/dex_file_verifier_test.cc", "dex/dex_instruction_test.cc", "dex/primitive_test.cc", + "dex/string_reference_test.cc", "dex/utf_test.cc", ], shared_libs: [ diff --git a/runtime/bytecode_utils.h b/libdexfile/dex/bytecode_utils.h similarity index 96% rename from runtime/bytecode_utils.h rename to libdexfile/dex/bytecode_utils.h index a7e0abf4e3..3f1d524286 100644 --- a/runtime/bytecode_utils.h +++ b/libdexfile/dex/bytecode_utils.h @@ -14,10 +14,10 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BYTECODE_UTILS_H_ -#define ART_RUNTIME_BYTECODE_UTILS_H_ +#ifndef ART_LIBDEXFILE_DEX_BYTECODE_UTILS_H_ +#define ART_LIBDEXFILE_DEX_BYTECODE_UTILS_H_ -#include "base/arena_object.h" +#include "base/value_object.h" #include "dex/dex_file-inl.h" #include "dex/dex_file.h" #include "dex/dex_instruction-inl.h" @@ -144,4 +144,4 @@ inline bool IsThrowingDexInstruction(const Instruction& instruction) { } // namespace art -#endif // ART_RUNTIME_BYTECODE_UTILS_H_ +#endif // ART_LIBDEXFILE_DEX_BYTECODE_UTILS_H_ diff --git a/runtime/class_reference.h b/libdexfile/dex/class_reference.h similarity index 88% rename from runtime/class_reference.h rename to libdexfile/dex/class_reference.h index e8e668e169..c056213de4 100644 --- a/runtime/class_reference.h +++ b/libdexfile/dex/class_reference.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_CLASS_REFERENCE_H_ -#define ART_RUNTIME_CLASS_REFERENCE_H_ +#ifndef ART_LIBDEXFILE_DEX_CLASS_REFERENCE_H_ +#define ART_LIBDEXFILE_DEX_CLASS_REFERENCE_H_ #include #include @@ -39,4 +39,4 @@ class ClassReference : public DexFileReference { } // namespace art -#endif // ART_RUNTIME_CLASS_REFERENCE_H_ +#endif // ART_LIBDEXFILE_DEX_CLASS_REFERENCE_H_ diff --git a/runtime/method_reference.h b/libdexfile/dex/method_reference.h similarity index 96% rename from runtime/method_reference.h rename to libdexfile/dex/method_reference.h index 50b6d6eb68..266582b059 100644 --- a/runtime/method_reference.h +++ b/libdexfile/dex/method_reference.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_METHOD_REFERENCE_H_ -#define ART_RUNTIME_METHOD_REFERENCE_H_ +#ifndef ART_LIBDEXFILE_DEX_METHOD_REFERENCE_H_ +#define ART_LIBDEXFILE_DEX_METHOD_REFERENCE_H_ #include #include @@ -88,4 +88,4 @@ struct MethodReferenceValueComparator { } // namespace art -#endif // ART_RUNTIME_METHOD_REFERENCE_H_ +#endif // ART_LIBDEXFILE_DEX_METHOD_REFERENCE_H_ diff --git a/runtime/string_reference.h b/libdexfile/dex/string_reference.h similarity index 94% rename from runtime/string_reference.h rename to libdexfile/dex/string_reference.h index 1ee5d6d53a..92095f4d2f 100644 --- a/runtime/string_reference.h +++ b/libdexfile/dex/string_reference.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_STRING_REFERENCE_H_ -#define ART_RUNTIME_STRING_REFERENCE_H_ +#ifndef ART_LIBDEXFILE_DEX_STRING_REFERENCE_H_ +#define ART_LIBDEXFILE_DEX_STRING_REFERENCE_H_ #include @@ -68,4 +68,4 @@ struct StringReferenceValueComparator { } // namespace art -#endif // ART_RUNTIME_STRING_REFERENCE_H_ +#endif // ART_LIBDEXFILE_DEX_STRING_REFERENCE_H_ diff --git a/compiler/utils/string_reference_test.cc b/libdexfile/dex/string_reference_test.cc similarity index 99% rename from compiler/utils/string_reference_test.cc rename to libdexfile/dex/string_reference_test.cc index 4b07e65771..b9cbf48978 100644 --- a/compiler/utils/string_reference_test.cc +++ b/libdexfile/dex/string_reference_test.cc @@ -19,8 +19,8 @@ #include #include "dex/dex_file_types.h" +#include "dex/test_dex_file_builder.h" #include "gtest/gtest.h" -#include "utils/test_dex_file_builder.h" namespace art { diff --git a/compiler/utils/test_dex_file_builder.h b/libdexfile/dex/test_dex_file_builder.h similarity index 98% rename from compiler/utils/test_dex_file_builder.h rename to libdexfile/dex/test_dex_file_builder.h index 58f1ec7b08..2d8a0bbfe4 100644 --- a/compiler/utils/test_dex_file_builder.h +++ b/libdexfile/dex/test_dex_file_builder.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_UTILS_TEST_DEX_FILE_BUILDER_H_ -#define ART_COMPILER_UTILS_TEST_DEX_FILE_BUILDER_H_ +#ifndef ART_LIBDEXFILE_DEX_TEST_DEX_FILE_BUILDER_H_ +#define ART_LIBDEXFILE_DEX_TEST_DEX_FILE_BUILDER_H_ #include @@ -26,8 +26,6 @@ #include -#include "base/bit_utils.h" -#include "dex/art_dex_file_loader.h" #include "dex/dex_file_loader.h" #include "dex/standard_dex_file.h" @@ -234,7 +232,7 @@ class TestDexFileBuilder { static constexpr bool kVerify = false; static constexpr bool kVerifyChecksum = false; std::string error_msg; - const ArtDexFileLoader dex_file_loader; + const DexFileLoader dex_file_loader; std::unique_ptr dex_file(dex_file_loader.Open( &dex_file_data_[0], dex_file_data_.size(), @@ -399,4 +397,4 @@ class TestDexFileBuilder { } // namespace art -#endif // ART_COMPILER_UTILS_TEST_DEX_FILE_BUILDER_H_ +#endif // ART_LIBDEXFILE_DEX_TEST_DEX_FILE_BUILDER_H_ diff --git a/compiler/utils/test_dex_file_builder_test.cc b/libdexfile/dex/test_dex_file_builder_test.cc similarity index 99% rename from compiler/utils/test_dex_file_builder_test.cc rename to libdexfile/dex/test_dex_file_builder_test.cc index 788afd8e1a..11e073a8a1 100644 --- a/compiler/utils/test_dex_file_builder_test.cc +++ b/libdexfile/dex/test_dex_file_builder_test.cc @@ -16,7 +16,6 @@ #include "test_dex_file_builder.h" -#include "base/utils.h" #include "dex/dex_file-inl.h" #include "gtest/gtest.h" diff --git a/runtime/type_reference.h b/libdexfile/dex/type_reference.h similarity index 90% rename from runtime/type_reference.h rename to libdexfile/dex/type_reference.h index 2b0b99f75e..9e7b8805af 100644 --- a/runtime/type_reference.h +++ b/libdexfile/dex/type_reference.h @@ -14,15 +14,15 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_TYPE_REFERENCE_H_ -#define ART_RUNTIME_TYPE_REFERENCE_H_ +#ifndef ART_LIBDEXFILE_DEX_TYPE_REFERENCE_H_ +#define ART_LIBDEXFILE_DEX_TYPE_REFERENCE_H_ #include #include #include "dex/dex_file_types.h" -#include "string_reference.h" +#include "dex/string_reference.h" namespace art { @@ -52,4 +52,4 @@ struct TypeReferenceValueComparator { } // namespace art -#endif // ART_RUNTIME_TYPE_REFERENCE_H_ +#endif // ART_LIBDEXFILE_DEX_TYPE_REFERENCE_H_ diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 10da2eaa83..433ed9aaee 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -48,6 +48,7 @@ #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_instruction-inl.h" +#include "dex/string_reference.h" #include "disassembler.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/space/image_space.h" @@ -73,7 +74,6 @@ #include "scoped_thread_state_change-inl.h" #include "stack.h" #include "stack_map.h" -#include "string_reference.h" #include "thread_list.h" #include "type_lookup_table.h" #include "vdex_file.h" diff --git a/profman/boot_image_profile.cc b/profman/boot_image_profile.cc index 3d003a7f06..60238e2082 100644 --- a/profman/boot_image_profile.cc +++ b/profman/boot_image_profile.cc @@ -19,9 +19,9 @@ #include "boot_image_profile.h" #include "dex/dex_file-inl.h" +#include "dex/method_reference.h" +#include "dex/type_reference.h" #include "jit/profile_compilation_info.h" -#include "method_reference.h" -#include "type_reference.h" namespace art { diff --git a/profman/profman.cc b/profman/profman.cc index d1cc56389a..90e342d16a 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -41,16 +41,16 @@ #include "base/unix_file/fd_file.h" #include "base/utils.h" #include "boot_image_profile.h" -#include "bytecode_utils.h" #include "dex/art_dex_file_loader.h" +#include "dex/bytecode_utils.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "dex/dex_file_types.h" +#include "dex/type_reference.h" #include "jit/profile_compilation_info.h" #include "profile_assistant.h" #include "runtime.h" -#include "type_reference.h" #include "zip_archive.h" namespace art { diff --git a/runtime/aot_class_linker.cc b/runtime/aot_class_linker.cc index 93e02eff69..c810b417fd 100644 --- a/runtime/aot_class_linker.cc +++ b/runtime/aot_class_linker.cc @@ -16,9 +16,9 @@ #include "aot_class_linker.h" -#include "class_reference.h" #include "class_status.h" #include "compiler_callbacks.h" +#include "dex/class_reference.h" #include "handle_scope-inl.h" #include "mirror/class-inl.h" #include "runtime.h" diff --git a/runtime/compiler_callbacks.h b/runtime/compiler_callbacks.h index 8395966404..6855dcdb2b 100644 --- a/runtime/compiler_callbacks.h +++ b/runtime/compiler_callbacks.h @@ -18,7 +18,7 @@ #define ART_RUNTIME_COMPILER_CALLBACKS_H_ #include "base/mutex.h" -#include "class_reference.h" +#include "dex/class_reference.h" #include "class_status.h" namespace art { diff --git a/runtime/dex_to_dex_decompiler.cc b/runtime/dex_to_dex_decompiler.cc index 7887191713..a5248e67f1 100644 --- a/runtime/dex_to_dex_decompiler.cc +++ b/runtime/dex_to_dex_decompiler.cc @@ -20,7 +20,7 @@ #include "base/macros.h" #include "base/mutex.h" -#include "bytecode_utils.h" +#include "dex/bytecode_utils.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_instruction-inl.h" diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index b33587204b..7a0850d4b8 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -23,6 +23,7 @@ #include "dex/dex_file-inl.h" #include "dex/dex_file_types.h" #include "dex/dex_instruction-inl.h" +#include "dex/method_reference.h" #include "entrypoints/entrypoint_utils-inl.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/card_table-inl.h" @@ -34,7 +35,6 @@ #include "jit/jit.h" #include "linear_alloc.h" #include "method_handles.h" -#include "method_reference.h" #include "mirror/class-inl.h" #include "mirror/dex_cache-inl.h" #include "mirror/method.h" diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 16335c6911..dfa7ac0970 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -25,8 +25,8 @@ #include "base/macros.h" #include "base/mutex.h" #include "base/safe_map.h" +#include "dex/method_reference.h" #include "gc_root.h" -#include "method_reference.h" namespace art { diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h index a0f6bf8dd6..6c56db9f49 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -28,9 +28,9 @@ #include "dex/dex_cache_resolved_classes.h" #include "dex/dex_file.h" #include "dex/dex_file_types.h" -#include "method_reference.h" +#include "dex/method_reference.h" +#include "dex/type_reference.h" #include "mem_map.h" -#include "type_reference.h" namespace art { diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc index e6917956ae..4e3774e389 100644 --- a/runtime/jit/profile_compilation_info_test.cc +++ b/runtime/jit/profile_compilation_info_test.cc @@ -23,14 +23,14 @@ #include "common_runtime_test.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" +#include "dex/method_reference.h" +#include "dex/type_reference.h" #include "handle_scope-inl.h" #include "jit/profile_compilation_info.h" #include "linear_alloc.h" -#include "method_reference.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "scoped_thread_state_change-inl.h" -#include "type_reference.h" #include "ziparchive/zip_writer.h" namespace art { diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h index e5cd11bae8..afbb3c122d 100644 --- a/runtime/jit/profile_saver.h +++ b/runtime/jit/profile_saver.h @@ -19,8 +19,8 @@ #include "base/mutex.h" #include "base/safe_map.h" +#include "dex/method_reference.h" #include "jit_code_cache.h" -#include "method_reference.h" #include "profile_compilation_info.h" #include "profile_saver_options.h" diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc index 0b69851a55..5603526177 100644 --- a/runtime/runtime_callbacks_test.cc +++ b/runtime/runtime_callbacks_test.cc @@ -30,8 +30,8 @@ #include "art_method-inl.h" #include "base/mutex.h" #include "class_linker.h" -#include "class_reference.h" #include "common_runtime_test.h" +#include "dex/class_reference.h" #include "handle.h" #include "handle_scope-inl.h" #include "mem_map.h" diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 4c9518b0ec..9237a8b44b 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -28,9 +28,9 @@ #include "dex/code_item_accessors.h" #include "dex/dex_file.h" #include "dex/dex_file_types.h" +#include "dex/method_reference.h" #include "handle.h" #include "instruction_flags.h" -#include "method_reference.h" #include "reg_type_cache.h" #include "register_line.h" #include "verifier_enums.h" diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc index b2af91e49f..bb9ab84fb5 100644 --- a/test/595-profile-saving/profile-saving.cc +++ b/test/595-profile-saving/profile-saving.cc @@ -17,10 +17,10 @@ #include "dex/dex_file.h" #include "art_method-inl.h" +#include "dex/method_reference.h" #include "jit/profile_compilation_info.h" #include "jit/profile_saver.h" #include "jni.h" -#include "method_reference.h" #include "mirror/class-inl.h" #include "mirror/executable.h" #include "nativehelper/ScopedUtfChars.h" diff --git a/test/664-aget-verifier/aget-verifier.cc b/test/664-aget-verifier/aget-verifier.cc index 4a263fae7a..2b48fff910 100644 --- a/test/664-aget-verifier/aget-verifier.cc +++ b/test/664-aget-verifier/aget-verifier.cc @@ -17,8 +17,8 @@ #include "dex/dex_file.h" #include "art_method-inl.h" +#include "dex/method_reference.h" #include "jni.h" -#include "method_reference.h" #include "mirror/class-inl.h" #include "mirror/executable.h" #include "scoped_thread_state_change-inl.h" -- GitLab From 5a3927662861e626615d9ae78e65c0645d71474b Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Fri, 16 Mar 2018 10:27:44 -0700 Subject: [PATCH 105/749] Recognize signed saturation in single clipping. Rationale: More saturation is better! Bug: b/74026074 Test: test-art-host,target Change-Id: Ib99e8965f26d96d956bcd3dbc7eb17b6c0050a24 --- compiler/optimizing/loop_optimization.cc | 108 ++++++-- .../678-checker-simd-saturation/src/Main.java | 257 ++++++++++++++++++ 2 files changed, 337 insertions(+), 28 deletions(-) diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index e1fb7ac17e..758aca2d0c 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -358,40 +358,92 @@ static HInstruction* FindClippee(HInstruction* instruction, 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. +// Set value range for type (or fail). +static bool CanSetRange(DataType::Type type, + /*out*/ int64_t* uhi, + /*out*/ int64_t* slo, + /*out*/ int64_t* shi) { if (DataType::Size(type) == 1) { - return is_unsigned - ? (lo <= 0 && hi == std::numeric_limits::max()) - : (lo == std::numeric_limits::min() && - hi == std::numeric_limits::max()); + *uhi = std::numeric_limits::max(); + *slo = std::numeric_limits::min(); + *shi = std::numeric_limits::max(); + return true; } else if (DataType::Size(type) == 2) { - return is_unsigned - ? (lo <= 0 && hi == std::numeric_limits::max()) - : (lo == std::numeric_limits::min() && - hi == std::numeric_limits::max()); + *uhi = std::numeric_limits::max(); + *slo = std::numeric_limits::min(); + *shi = std::numeric_limits::max(); + return true; } return false; } +// Accept various saturated addition forms. +static bool IsSaturatedAdd(HInstruction* clippee, + DataType::Type type, + int64_t lo, + int64_t hi, + bool is_unsigned) { + int64_t ulo = 0, uhi = 0, slo = 0, shi = 0; + if (!CanSetRange(type, &uhi, &slo, &shi)) { + return false; + } + // Tighten the range for signed single clipping on constant. + if (!is_unsigned) { + int64_t c = 0; + HInstruction* notused = nullptr; + if (IsAddConst(clippee, ¬used, &c)) { + // For c in proper range and narrower operand r: + // MIN(r + c, 127) c > 0 + // or MAX(r + c, -128) c < 0 (and possibly redundant bound). + if (0 < c && c <= shi && hi == shi) { + if (lo <= (slo + c)) { + return true; + } + } else if (slo <= c && c < 0 && lo == slo) { + if (hi >= (shi + c)) { + return true; + } + } + } + } + // Detect for narrower operands r and s: + // MIN(r + s, 255) => SAT_ADD_unsigned + // MAX(MIN(r + s, 127), -128) => SAT_ADD_signed. + return is_unsigned ? (lo <= ulo && hi == uhi) : (lo == slo && hi == shi); +} + // 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::max()) - : (lo == std::numeric_limits::min() && - hi == std::numeric_limits::max()); - } else if (DataType::Size(type) == 2) { - return is_unsigned - ? (lo == 0 && hi >= std::numeric_limits::min()) - : (lo == std::numeric_limits::min() && - hi == std::numeric_limits::max()); +static bool IsSaturatedSub(HInstruction* clippee, + DataType::Type type, + int64_t lo, + int64_t hi, + bool is_unsigned) { + int64_t ulo = 0, uhi = 0, slo = 0, shi = 0; + if (!CanSetRange(type, &uhi, &slo, &shi)) { + return false; } - return false; + // Tighten the range for signed single clipping on constant. + if (!is_unsigned) { + int64_t c = 0; + if (IsInt64AndGet(clippee->InputAt(0), /*out*/ &c)) { + // For c in proper range and narrower operand r: + // MIN(c - r, 127) c > 0 + // or MAX(c - r, -128) c < 0 (and possibly redundant bound). + if (0 < c && c <= shi && hi == shi) { + if (lo <= (c - shi)) { + return true; + } + } else if (slo <= c && c < 0 && lo == slo) { + if (hi >= (c - slo)) { + return true; + } + } + } + } + // Detect for narrower operands r and s: + // MAX(r - s, 0) => SAT_SUB_unsigned + // MIN(MAX(r - s, -128), 127) => SAT_ADD_signed. + return is_unsigned ? (lo == ulo && hi >= uhi) : (lo == slo && hi == shi); } // Detect reductions of the following forms, @@ -1909,8 +1961,8 @@ bool HLoopOptimization::VectorizeSaturationIdiom(LoopNode* node, 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))) { + (is_add ? IsSaturatedAdd(clippee, type, lo, hi, is_unsigned) + : IsSaturatedSub(clippee, type, lo, hi, is_unsigned))) { DCHECK(r != nullptr); DCHECK(s != nullptr); } else { diff --git a/test/678-checker-simd-saturation/src/Main.java b/test/678-checker-simd-saturation/src/Main.java index 33a6f5ec80..d123cc2e25 100644 --- a/test/678-checker-simd-saturation/src/Main.java +++ b/test/678-checker-simd-saturation/src/Main.java @@ -199,6 +199,110 @@ public class Main { } } + // + // Single clipping signed 8-bit saturation. + // + + /// CHECK-START-{ARM,ARM64}: void Main.satAddPConstSByte(byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void satAddPConstSByte(byte[] a, byte[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + b[i] = (byte) Math.min(a[i] + 15, 127); + } + } + + /// CHECK-START-{ARM,ARM64}: void Main.satAddNConstSByte(byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void satAddNConstSByte(byte[] a, byte[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + b[i] = (byte) Math.max(a[i] - 15, -128); + } + } + + /// CHECK-START-{ARM,ARM64}: void Main.satSubPConstSByte(byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void satSubPConstSByte(byte[] a, byte[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + b[i] = (byte) Math.min(15 - a[i], 127); + } + } + + /// CHECK-START-{ARM,ARM64}: void Main.satSubNConstSByte(byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void satSubNConstSByte(byte[] a, byte[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + b[i] = (byte) Math.max(-15 - a[i], -128); + } + } + + // + // Single clipping signed 16-bit saturation. + // + + /// CHECK-START-{ARM,ARM64}: void Main.satAddPConstSShort(short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void satAddPConstSShort(short[] a, short[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + b[i] = (short) Math.min(a[i] + 15, 32767); + } + } + + /// CHECK-START-{ARM,ARM64}: void Main.satAddNConstSShort(short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void satAddNConstSShort(short[] a, short[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + b[i] = (short) Math.max(a[i] - 15, -32768); + } + } + + /// CHECK-START-{ARM,ARM64}: void Main.satSubPConstSShort(short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void satSubPConstSShort(short[] a, short[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + b[i] = (short) Math.min(15 - a[i], 32767); + } + } + + /// CHECK-START-{ARM,ARM64}: void Main.satSubNConstSShort(short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void satSubNConstSShort(short[] a, short[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + b[i] = (short) Math.max(-15 - a[i], -32768); + } + } + // // Alternatives. // @@ -257,6 +361,87 @@ public class Main { } } + /// CHECK-START-{ARM,ARM64}: void Main.usatAlt1(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void usatAlt1(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 t = (0xffff & a[i]) + (0xffff & b[i]); + c[i] = (short) (t <= 65535 ? t : 65535); + } + } + + /// CHECK-START-{ARM,ARM64}: void Main.usatAlt2(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void usatAlt2(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 t = (a[i] & 0xffff) + (b[i] & 0xffff); + c[i] = (short) (t < 65535 ? t : 65535); + } + } + + /// CHECK-START-{ARM,ARM64}: void Main.usatAlt3(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void usatAlt3(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 x = (a[i] & 0xffff); + int y = (b[i] & 0xffff); + int t = y + x ; + if (t >= 65535) t = 65535; + c[i] = (short) t; + } + } + + /// CHECK-START-{ARM,ARM64}: void Main.usatAlt4(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void usatAlt4(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 x = (a[i] & 0xffff); + int y = (b[i] & 0xffff); + int t = y + x ; + if (t > 65535) t = 65535; + c[i] = (short) t; + } + } + + /// CHECK-START-{ARM,ARM64}: void Main.satRedundantClip(short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void satRedundantClip(short[] a, short[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + // Max clipping redundant. + b[i] = (short) Math.max(Math.min(a[i] + 15, 32767), -32768 + 15); + } + } + + /// CHECK-START: void Main.satNonRedundantClip(short[], short[]) loop_optimization (after) + /// CHECK-NOT: VecSaturationAdd + public static void satNonRedundantClip(short[] a, short[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + // Max clipping not redundant (one off). + b[i] = (short) Math.max(Math.min(a[i] + 15, 32767), -32768 + 16); + } + } + // // Test drivers. // @@ -297,6 +482,27 @@ public class Main { byte e = (byte) Math.max(Math.min(b1[i] - b2[i], 127), -128); expectEquals(e, out[i]); } + // Single clipping. + satAddPConstSByte(b1, out); + for (int i = 0; i < m; i++) { + byte e = (byte) Math.min(b1[i] + 15, 127); + expectEquals(e, out[i]); + } + satAddNConstSByte(b1, out); + for (int i = 0; i < m; i++) { + byte e = (byte) Math.max(b1[i] - 15, -128); + expectEquals(e, out[i]); + } + satSubPConstSByte(b1, out); + for (int i = 0; i < m; i++) { + byte e = (byte) Math.min(15 - b1[i], 127); + expectEquals(e, out[i]); + } + satSubNConstSByte(b1, out); + for (int i = 0; i < m; i++) { + byte e = (byte) Math.max(-15 - b1[i], -128); + expectEquals(e, out[i]); + } } private static void test16Bit() { @@ -357,6 +563,27 @@ public class Main { short e = (short) Math.max(Math.min(s1[i] - s2[i], 32767), -32768); expectEquals(e, out[i]); } + // Single clipping. + satAddPConstSShort(s1, out); + for (int i = 0; i < m; i++) { + short e = (short) Math.min(s1[i] + 15, 32767); + expectEquals(e, out[i]); + } + satAddNConstSShort(s1, out); + for (int i = 0; i < m; i++) { + short e = (short) Math.max(s1[i] - 15, -32768); + expectEquals(e, out[i]); + } + satSubPConstSShort(s1, out); + for (int i = 0; i < m; i++) { + short e = (short) Math.min(15 - s1[i], 32767); + expectEquals(e, out[i]); + } + satSubNConstSShort(s1, out); + for (int i = 0; i < m; i++) { + short e = (short) Math.max(-15 - s1[i], -32768); + expectEquals(e, out[i]); + } // Alternatives. satAlt1(s1, s2, out); for (int i = 0; i < m; i++) { @@ -373,6 +600,36 @@ public class Main { short e = (short) Math.max(Math.min(s1[i] + s2[i], 32767), -32768); expectEquals(e, out[i]); } + usatAlt1(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]); + } + usatAlt2(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]); + } + usatAlt3(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]); + } + usatAlt4(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]); + } + satRedundantClip(s1, out); + for (int i = 0; i < m; i++) { + short e = (short) Math.min(s1[i] + 15, 32767); + expectEquals(e, out[i]); + } + satNonRedundantClip(s1, out); + for (int i = 0; i < m; i++) { + short e = (short) Math.max(Math.min(s1[i] + 15, 32767), -32752); + expectEquals(e, out[i]); + } } public static void main(String[] args) { -- GitLab From 3d9bfc18d3f4f1b6263730ff708fd1efa0cb8ebc Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Mon, 19 Feb 2018 15:30:51 +0000 Subject: [PATCH 106/749] More flexible API enforcement policy support. This CL adds the ability to configure which banned API lists to enforce, defined by new enum hiddenapi::ApiEnforcementPolicy. Currently, the policy can be set at zygote fork time, but not at dex optimization time where blacklist enforcement is still assumed. As such, making the policy more strict will not work as expected yet. This will be improved in a follow up CL. Test: art tests pass Test: Device boots BUG: 73337509 Change-Id: I32c84368330dd163d8ee859465d2e7ac2d49d88d --- runtime/hidden_api.h | 69 ++++++++++++++++----- runtime/native/dalvik_system_ZygoteHooks.cc | 49 ++++++++------- runtime/native/java_lang_Class.cc | 4 +- runtime/runtime.cc | 13 ++-- runtime/runtime.h | 14 +++-- runtime/well_known_classes.cc | 9 +-- test/674-hiddenapi/hiddenapi.cc | 3 +- 7 files changed, 107 insertions(+), 54 deletions(-) diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index f2ea2fdaaa..321d55d9b7 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -27,6 +27,23 @@ namespace art { namespace hiddenapi { +// Hidden API enforcement policy +// This must be kept in sync with ApplicationInfo.ApiEnforcementPolicy in +// frameworks/base/core/java/android/content/pm/ApplicationInfo.java +enum class EnforcementPolicy { + kNoChecks = 0, + kAllLists = 1, // ban anything but whitelist + kDarkGreyAndBlackList = 2, // ban dark grey & blacklist + kBlacklistOnly = 3, // ban blacklist violations only + kMax = kBlacklistOnly, +}; + +inline EnforcementPolicy EnforcementPolicyFromInt(int api_policy_int) { + DCHECK_GE(api_policy_int, 0); + DCHECK_LE(api_policy_int, static_cast(EnforcementPolicy::kMax)); + return static_cast(api_policy_int); +} + enum Action { kAllow, kAllowButWarn, @@ -59,16 +76,38 @@ inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { return os; } +static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags::ApiList apiList) { + return static_cast(policy) == static_cast(apiList); +} + inline Action GetMemberAction(uint32_t access_flags) { - switch (HiddenApiAccessFlags::DecodeFromRuntime(access_flags)) { - case HiddenApiAccessFlags::kWhitelist: - return kAllow; - case HiddenApiAccessFlags::kLightGreylist: - return kAllowButWarn; - case HiddenApiAccessFlags::kDarkGreylist: - return kAllowButWarnAndToast; - case HiddenApiAccessFlags::kBlacklist: - return kDeny; + EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); + if (policy == EnforcementPolicy::kNoChecks) { + // Exit early. Nothing to enforce. + return kAllow; + } + + HiddenApiAccessFlags::ApiList api_list = HiddenApiAccessFlags::DecodeFromRuntime(access_flags); + if (api_list == HiddenApiAccessFlags::kWhitelist) { + return kAllow; + } + // The logic below relies on equality of values in the enums EnforcementPolicy and + // HiddenApiAccessFlags::ApiList, and their ordering. Assert that this is as expected. + static_assert( + EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) && + EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) && + EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist), + "Mismatch between EnforcementPolicy and ApiList enums"); + static_assert( + EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList && + EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly, + "EnforcementPolicy values ordering not correct"); + if (static_cast(policy) > static_cast(api_list)) { + return api_list == HiddenApiAccessFlags::kDarkGreylist + ? kAllowButWarnAndToast + : kAllowButWarn; + } else { + return kDeny; } } @@ -107,12 +146,6 @@ inline bool ShouldBlockAccessToMember(T* member, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); - Runtime* runtime = Runtime::Current(); - - if (!runtime->AreHiddenApiChecksEnabled()) { - // Exit early. Nothing to enforce. - return false; - } Action action = GetMemberAction(member->GetAccessFlags()); if (action == kAllow) { @@ -133,14 +166,16 @@ inline bool ShouldBlockAccessToMember(T* member, // We do this regardless of whether we block the access or not. WarnAboutMemberAccess(member, access_method); - // Block access if on blacklist. if (action == kDeny) { + // Block access return true; } // Allow access to this member but print a warning. DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast); + Runtime* runtime = Runtime::Current(); + // Depending on a runtime flag, we might move the member into whitelist and // skip the warning the next time the member is accessed. if (runtime->ShouldDedupeHiddenApiWarnings()) { @@ -150,7 +185,7 @@ inline bool ShouldBlockAccessToMember(T* member, // If this action requires a UI warning, set the appropriate flag. if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) { - Runtime::Current()->SetPendingHiddenApiWarning(true); + runtime->SetPendingHiddenApiWarning(true); } return false; diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index 89135698e3..cbc2aeb41f 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -162,19 +162,24 @@ static void CollectNonDebuggableClasses() REQUIRES(!Locks::mutator_lock_) { // Must match values in com.android.internal.os.Zygote. enum { - DEBUG_ENABLE_JDWP = 1, - DEBUG_ENABLE_CHECKJNI = 1 << 1, - DEBUG_ENABLE_ASSERT = 1 << 2, - DEBUG_ENABLE_SAFEMODE = 1 << 3, - DEBUG_ENABLE_JNI_LOGGING = 1 << 4, - DEBUG_GENERATE_DEBUG_INFO = 1 << 5, - DEBUG_ALWAYS_JIT = 1 << 6, - DEBUG_NATIVE_DEBUGGABLE = 1 << 7, - DEBUG_JAVA_DEBUGGABLE = 1 << 8, - DISABLE_VERIFIER = 1 << 9, - ONLY_USE_SYSTEM_OAT_FILES = 1 << 10, - ENABLE_HIDDEN_API_CHECKS = 1 << 11, - DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12, + DEBUG_ENABLE_JDWP = 1, + DEBUG_ENABLE_CHECKJNI = 1 << 1, + DEBUG_ENABLE_ASSERT = 1 << 2, + DEBUG_ENABLE_SAFEMODE = 1 << 3, + DEBUG_ENABLE_JNI_LOGGING = 1 << 4, + DEBUG_GENERATE_DEBUG_INFO = 1 << 5, + DEBUG_ALWAYS_JIT = 1 << 6, + DEBUG_NATIVE_DEBUGGABLE = 1 << 7, + DEBUG_JAVA_DEBUGGABLE = 1 << 8, + DISABLE_VERIFIER = 1 << 9, + ONLY_USE_SYSTEM_OAT_FILES = 1 << 10, + DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 11, + HIDDEN_API_ENFORCEMENT_POLICY_MASK = (1 << 12) + | (1 << 13), + + // bits to shift (flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) by to get a value + // corresponding to hiddenapi::EnforcementPolicy + API_ENFORCEMENT_POLICY_SHIFT = CTZ(HIDDEN_API_ENFORCEMENT_POLICY_MASK), }; static uint32_t EnableDebugFeatures(uint32_t runtime_flags) { @@ -285,7 +290,8 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, // Our system thread ID, etc, has changed so reset Thread state. thread->InitAfterFork(); runtime_flags = EnableDebugFeatures(runtime_flags); - bool do_hidden_api_checks = false; + hiddenapi::EnforcementPolicy api_enforcement_policy = hiddenapi::EnforcementPolicy::kNoChecks; + bool dedupe_hidden_api_warnings = true; if ((runtime_flags & DISABLE_VERIFIER) != 0) { Runtime::Current()->DisableVerifier(); @@ -297,10 +303,9 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, runtime_flags &= ~ONLY_USE_SYSTEM_OAT_FILES; } - if ((runtime_flags & ENABLE_HIDDEN_API_CHECKS) != 0) { - do_hidden_api_checks = true; - runtime_flags &= ~ENABLE_HIDDEN_API_CHECKS; - } + api_enforcement_policy = hiddenapi::EnforcementPolicyFromInt( + (runtime_flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT); + runtime_flags &= ~HIDDEN_API_ENFORCEMENT_POLICY_MASK; if (runtime_flags != 0) { LOG(ERROR) << StringPrintf("Unknown bits set in runtime_flags: %#x", runtime_flags); @@ -351,11 +356,13 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, } } + bool do_hidden_api_checks = api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks; DCHECK(!(is_system_server && do_hidden_api_checks)) - << "SystemServer should be forked with ENABLE_HIDDEN_API_CHECKS"; + << "SystemServer should be forked with EnforcementPolicy::kDisable"; DCHECK(!(is_zygote && do_hidden_api_checks)) - << "Child zygote processes should be forked with ENABLE_HIDDEN_API_CHECKS"; - Runtime::Current()->SetHiddenApiChecksEnabled(do_hidden_api_checks); + << "Child zygote processes should be forked with EnforcementPolicy::kDisable"; + Runtime::Current()->SetHiddenApiEnforcementPolicy(api_enforcement_policy); + Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe_hidden_api_warnings); // Clear the hidden API warning flag, in case it was set. Runtime::Current()->SetPendingHiddenApiWarning(false); diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 25d50376de..078102b033 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -89,8 +89,8 @@ static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator // access hidden APIs. This can be *very* expensive. Never call this in a loop. ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - return Runtime::Current()->AreHiddenApiChecksEnabled() && - !IsCallerInBootClassPath(self); + hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); + return policy != hiddenapi::EnforcementPolicy::kNoChecks && IsCallerInBootClassPath(self); } // Returns true if the first non-ClassClass caller up the stack should not be diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 7d9d3426fc..53982ae833 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -267,7 +267,7 @@ Runtime::Runtime() oat_file_manager_(nullptr), is_low_memory_mode_(false), safe_mode_(false), - do_hidden_api_checks_(false), + hidden_api_policy_(hiddenapi::EnforcementPolicy::kNoChecks), pending_hidden_api_warning_(false), dedupe_hidden_api_warnings_(true), always_set_hidden_api_warning_flag_(false), @@ -1196,9 +1196,14 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // by default and we only enable them if: // (a) runtime was started with a flag that enables the checks, or // (b) Zygote forked a new process that is not exempt (see ZygoteHooks). - do_hidden_api_checks_ = runtime_options.Exists(Opt::HiddenApiChecks); - DCHECK(!is_zygote_ || !do_hidden_api_checks_) - << "Zygote should not be started with hidden API checks"; + bool do_hidden_api_checks = runtime_options.Exists(Opt::HiddenApiChecks); + DCHECK(!is_zygote_ || !do_hidden_api_checks); + // TODO pass the actual enforcement policy in, rather than just a single bit. + // As is, we're encoding some logic here about which specific policy to use, which would be better + // controlled by the framework. + hidden_api_policy_ = do_hidden_api_checks + ? hiddenapi::EnforcementPolicy::kBlacklistOnly + : hiddenapi::EnforcementPolicy::kNoChecks; no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain); force_native_bridge_ = runtime_options.Exists(Opt::ForceNativeBridge); diff --git a/runtime/runtime.h b/runtime/runtime.h index c7f650ea3f..dba31b2939 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -49,6 +49,10 @@ class AbstractSystemWeakHolder; class Heap; } // namespace gc +namespace hiddenapi { +enum class EnforcementPolicy; +} // namespace hiddenapi + namespace jit { class Jit; class JitOptions; @@ -520,12 +524,12 @@ class Runtime { bool IsVerificationEnabled() const; bool IsVerificationSoftFail() const; - void SetHiddenApiChecksEnabled(bool value) { - do_hidden_api_checks_ = value; + void SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy policy) { + hidden_api_policy_ = policy; } - bool AreHiddenApiChecksEnabled() const { - return do_hidden_api_checks_; + hiddenapi::EnforcementPolicy GetHiddenApiEnforcementPolicy() const { + return hidden_api_policy_; } void SetPendingHiddenApiWarning(bool value) { @@ -990,7 +994,7 @@ class Runtime { bool safe_mode_; // Whether access checks on hidden API should be performed. - bool do_hidden_api_checks_; + hiddenapi::EnforcementPolicy hidden_api_policy_; // Whether the application has used an API which is not restricted but we // should issue a warning about it. diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 67ea64be74..bf36ccf0fa 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -24,6 +24,7 @@ #include #include "entrypoints/quick/quick_entrypoints_enum.h" +#include "hidden_api.h" #include "jni_internal.h" #include "mirror/class.h" #include "mirror/throwable.h" @@ -287,17 +288,17 @@ class ScopedHiddenApiExemption { public: explicit ScopedHiddenApiExemption(Runtime* runtime) : runtime_(runtime), - initially_enabled_(runtime_->AreHiddenApiChecksEnabled()) { - runtime_->SetHiddenApiChecksEnabled(false); + initial_policy_(runtime_->GetHiddenApiEnforcementPolicy()) { + runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks); } ~ScopedHiddenApiExemption() { - runtime_->SetHiddenApiChecksEnabled(initially_enabled_); + runtime_->SetHiddenApiEnforcementPolicy(initial_policy_); } private: Runtime* runtime_; - const bool initially_enabled_; + const hiddenapi::EnforcementPolicy initial_policy_; DISALLOW_COPY_AND_ASSIGN(ScopedHiddenApiExemption); }; diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc index effa37ade4..04c3fbf03a 100644 --- a/test/674-hiddenapi/hiddenapi.cc +++ b/test/674-hiddenapi/hiddenapi.cc @@ -16,6 +16,7 @@ #include "class_linker.h" #include "dex/art_dex_file_loader.h" +#include "hidden_api.h" #include "jni.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" @@ -27,7 +28,7 @@ namespace Test674HiddenApi { extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) { Runtime* runtime = Runtime::Current(); - runtime->SetHiddenApiChecksEnabled(true); + runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly); runtime->SetDedupeHiddenApiWarnings(false); runtime->AlwaysSetHiddenApiWarningFlag(); } -- GitLab From 51cba191cdd46cecad9e318bffae6aeb0d2e9123 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 19 Mar 2018 11:11:18 +0000 Subject: [PATCH 107/749] Add veridex project. Starters for detecting app compat issues. Change-Id: I523c82bdae7f30506188c57bece34850bf922072 --- tools/veridex/Android.bp | 24 +++++++ tools/veridex/veridex.cc | 152 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 tools/veridex/Android.bp create mode 100644 tools/veridex/veridex.cc diff --git a/tools/veridex/Android.bp b/tools/veridex/Android.bp new file mode 100644 index 0000000000..cac441aaf0 --- /dev/null +++ b/tools/veridex/Android.bp @@ -0,0 +1,24 @@ +// 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. + +art_cc_binary { + name: "veridex", + host_supported: true, + srcs: ["veridex.cc"], + cflags: ["-Wall", "-Werror"], + shared_libs: ["libdexfile", "libbase"], + header_libs: [ + "art_libartbase_headers", + ], +} diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc new file mode 100644 index 0000000000..9d0dd36019 --- /dev/null +++ b/tools/veridex/veridex.cc @@ -0,0 +1,152 @@ +/* + * 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. + */ + +#include + +#include "dex/dex_file.h" +#include "dex/dex_file_loader.h" + +#include + +namespace art { + +struct VeridexOptions { + const char* dex_file = nullptr; + const char* core_stubs = nullptr; + const char* blacklist = nullptr; + const char* light_greylist = nullptr; + const char* dark_greylist = nullptr; +}; + +static const char* Substr(const char* str, int index) { + return str + index; +} + +static bool StartsWith(const char* str, const char* val) { + return strlen(str) >= strlen(val) && memcmp(str, val, strlen(val)) == 0; +} + +static void ParseArgs(VeridexOptions* options, int argc, char** argv) { + // Skip over the command name. + argv++; + argc--; + + static const char* kDexFileOption = "--dex-file="; + static const char* kStubsOption = "--core-stubs="; + static const char* kBlacklistOption = "--blacklist="; + static const char* kDarkGreylistOption = "--dark-greylist="; + static const char* kLightGreylistOption = "--light-greylist="; + + for (int i = 0; i < argc; ++i) { + if (StartsWith(argv[i], kDexFileOption)) { + options->dex_file = Substr(argv[i], strlen(kDexFileOption)); + } else if (StartsWith(argv[i], kStubsOption)) { + options->core_stubs = Substr(argv[i], strlen(kStubsOption)); + } else if (StartsWith(argv[i], kBlacklistOption)) { + options->blacklist = Substr(argv[i], strlen(kBlacklistOption)); + } else if (StartsWith(argv[i], kDarkGreylistOption)) { + options->dark_greylist = Substr(argv[i], strlen(kDarkGreylistOption)); + } else if (StartsWith(argv[i], kLightGreylistOption)) { + options->light_greylist = Substr(argv[i], strlen(kLightGreylistOption)); + } + } +} + +static std::vector Split(const std::string& str, char sep) { + std::vector tokens; + std::string tmp; + std::istringstream iss(str); + while (std::getline(iss, tmp, sep)) { + tokens.push_back(tmp); + } + return tokens; +} + +class Veridex { + public: + static int Run(int argc, char** argv) { + VeridexOptions options; + ParseArgs(&options, argc, argv); + + std::vector boot_content; + std::vector app_content; + std::vector> boot_dex_files; + std::vector> app_dex_files; + std::string error_msg; + + // Read the boot classpath. + std::vector boot_classpath = Split(options.core_stubs, ':'); + boot_content.resize(boot_classpath.size()); + uint32_t i = 0; + for (const std::string& str : boot_classpath) { + if (!Load(str, boot_content[i++], &boot_dex_files, &error_msg)) { + LOG(ERROR) << error_msg; + return 1; + } + } + + // Read the apps dex files. + std::vector app_files = Split(options.dex_file, ':'); + app_content.resize(app_files.size()); + i = 0; + for (const std::string& str : app_files) { + if (!Load(str, app_content[i++], &app_dex_files, &error_msg)) { + LOG(ERROR) << error_msg; + return 1; + } + } + return 0; + } + + private: + static bool Load(const std::string& filename, + std::string& content, + std::vector>* dex_files, + std::string* error_msg) { + if (filename.empty()) { + *error_msg = "Missing file name"; + return false; + } + + // TODO: once added, use an api to android::base to read a std::vector. + if (!android::base::ReadFileToString(filename.c_str(), &content)) { + *error_msg = "ReadFileToString failed for " + filename; + return false; + } + + const DexFileLoader dex_file_loader; + static constexpr bool kVerifyChecksum = true; + static constexpr bool kRunDexFileVerifier = true; + if (!dex_file_loader.OpenAll(reinterpret_cast(content.data()), + content.size(), + filename.c_str(), + kRunDexFileVerifier, + kVerifyChecksum, + error_msg, + dex_files)) { + return false; + } + + return true; + } +}; + +} // namespace art + +int main(int argc, char** argv) { + return art::Veridex::Run(argc, argv); +} + -- GitLab From 4ab9f66bfc7fbfc338198a744b48b64284565659 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 19 Mar 2018 13:47:56 -0700 Subject: [PATCH 108/749] Filter out ddms error message in 1940 better Since the ddms error message in test 1940 is printed from within the runtime it lacks the synchronization with System.out that other prints have. This could cause the output to get mixed slightly with other messages from the test. In order to fix this we filter it out using python code that is able to understand the interleaving. Test: ./test.py --host -j50 Change-Id: Ic42816beeea2f04046b5bb88e17a1f6bde19a58e --- test/1940-ddms-ext/check | 2 +- test/1940-ddms-ext/expected_error.txt | 4 +++ test/1940-ddms-ext/remove_error.py | 38 +++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 test/1940-ddms-ext/expected_error.txt create mode 100755 test/1940-ddms-ext/remove_error.py diff --git a/test/1940-ddms-ext/check b/test/1940-ddms-ext/check index d2c03841fc..91966b41a0 100755 --- a/test/1940-ddms-ext/check +++ b/test/1940-ddms-ext/check @@ -16,6 +16,6 @@ # Need to pull out the describeException ouput since that won't be there on # device. -sed -e '/\t.*$/d' "$2" | sed -e '/java.lang.ArrayIndexOutOfBoundsException:.*$/d' > "$2.tmp" +./remove_error.py "$2" "./expected_error.txt" > "$2.tmp" ./default-check "$1" "$2.tmp" diff --git a/test/1940-ddms-ext/expected_error.txt b/test/1940-ddms-ext/expected_error.txt new file mode 100644 index 0000000000..73883b46e2 --- /dev/null +++ b/test/1940-ddms-ext/expected_error.txt @@ -0,0 +1,4 @@ +java.lang.ArrayIndexOutOfBoundsException: byte[] offset=12 length=55 src.length=1 + at art.Test1940.processChunk(Native Method) + at art.Test1940.run(Test1940.java:156) + at Main.main(Main.java:19) diff --git a/test/1940-ddms-ext/remove_error.py b/test/1940-ddms-ext/remove_error.py new file mode 100755 index 0000000000..638c479a31 --- /dev/null +++ b/test/1940-ddms-ext/remove_error.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# +# 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 argparse + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('input_data', type=open) + parser.add_argument('expected_error', type=str) + args = parser.parse_args() + + for line in map(str.rstrip, args.input_data.readlines()): + print_full = True + with open(args.expected_error) as err_file: + for err_line in map(str.rstrip, err_file): + if line.startswith(err_line): + print_full = False + if line != err_line: + print(line[len(err_line):]) + break + if print_full and line != '': + print(line) + +if __name__ == '__main__': + main() -- GitLab From d53aa8864e1a73fe9def1f93e76b2a4425d6b313 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Tue, 20 Mar 2018 20:20:57 +0000 Subject: [PATCH 109/749] Revert "More flexible API enforcement policy support." This reverts commit 3d9bfc18d3f4f1b6263730ff708fd1efa0cb8ebc. Reason for revert: Breaks run-test 674-hiddenapi Change-Id: Ie28adc7acc38dfe1d760c799cc7ec1fc90904205 --- runtime/hidden_api.h | 69 +++++---------------- runtime/native/dalvik_system_ZygoteHooks.cc | 49 +++++++-------- runtime/native/java_lang_Class.cc | 4 +- runtime/runtime.cc | 13 ++-- runtime/runtime.h | 14 ++--- runtime/well_known_classes.cc | 9 ++- test/674-hiddenapi/hiddenapi.cc | 3 +- 7 files changed, 54 insertions(+), 107 deletions(-) diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index 321d55d9b7..f2ea2fdaaa 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -27,23 +27,6 @@ namespace art { namespace hiddenapi { -// Hidden API enforcement policy -// This must be kept in sync with ApplicationInfo.ApiEnforcementPolicy in -// frameworks/base/core/java/android/content/pm/ApplicationInfo.java -enum class EnforcementPolicy { - kNoChecks = 0, - kAllLists = 1, // ban anything but whitelist - kDarkGreyAndBlackList = 2, // ban dark grey & blacklist - kBlacklistOnly = 3, // ban blacklist violations only - kMax = kBlacklistOnly, -}; - -inline EnforcementPolicy EnforcementPolicyFromInt(int api_policy_int) { - DCHECK_GE(api_policy_int, 0); - DCHECK_LE(api_policy_int, static_cast(EnforcementPolicy::kMax)); - return static_cast(api_policy_int); -} - enum Action { kAllow, kAllowButWarn, @@ -76,38 +59,16 @@ inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { return os; } -static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags::ApiList apiList) { - return static_cast(policy) == static_cast(apiList); -} - inline Action GetMemberAction(uint32_t access_flags) { - EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); - if (policy == EnforcementPolicy::kNoChecks) { - // Exit early. Nothing to enforce. - return kAllow; - } - - HiddenApiAccessFlags::ApiList api_list = HiddenApiAccessFlags::DecodeFromRuntime(access_flags); - if (api_list == HiddenApiAccessFlags::kWhitelist) { - return kAllow; - } - // The logic below relies on equality of values in the enums EnforcementPolicy and - // HiddenApiAccessFlags::ApiList, and their ordering. Assert that this is as expected. - static_assert( - EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) && - EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) && - EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist), - "Mismatch between EnforcementPolicy and ApiList enums"); - static_assert( - EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList && - EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly, - "EnforcementPolicy values ordering not correct"); - if (static_cast(policy) > static_cast(api_list)) { - return api_list == HiddenApiAccessFlags::kDarkGreylist - ? kAllowButWarnAndToast - : kAllowButWarn; - } else { - return kDeny; + switch (HiddenApiAccessFlags::DecodeFromRuntime(access_flags)) { + case HiddenApiAccessFlags::kWhitelist: + return kAllow; + case HiddenApiAccessFlags::kLightGreylist: + return kAllowButWarn; + case HiddenApiAccessFlags::kDarkGreylist: + return kAllowButWarnAndToast; + case HiddenApiAccessFlags::kBlacklist: + return kDeny; } } @@ -146,6 +107,12 @@ inline bool ShouldBlockAccessToMember(T* member, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); + Runtime* runtime = Runtime::Current(); + + if (!runtime->AreHiddenApiChecksEnabled()) { + // Exit early. Nothing to enforce. + return false; + } Action action = GetMemberAction(member->GetAccessFlags()); if (action == kAllow) { @@ -166,16 +133,14 @@ inline bool ShouldBlockAccessToMember(T* member, // We do this regardless of whether we block the access or not. WarnAboutMemberAccess(member, access_method); + // Block access if on blacklist. if (action == kDeny) { - // Block access return true; } // Allow access to this member but print a warning. DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast); - Runtime* runtime = Runtime::Current(); - // Depending on a runtime flag, we might move the member into whitelist and // skip the warning the next time the member is accessed. if (runtime->ShouldDedupeHiddenApiWarnings()) { @@ -185,7 +150,7 @@ inline bool ShouldBlockAccessToMember(T* member, // If this action requires a UI warning, set the appropriate flag. if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) { - runtime->SetPendingHiddenApiWarning(true); + Runtime::Current()->SetPendingHiddenApiWarning(true); } return false; diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index cbc2aeb41f..89135698e3 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -162,24 +162,19 @@ static void CollectNonDebuggableClasses() REQUIRES(!Locks::mutator_lock_) { // Must match values in com.android.internal.os.Zygote. enum { - DEBUG_ENABLE_JDWP = 1, - DEBUG_ENABLE_CHECKJNI = 1 << 1, - DEBUG_ENABLE_ASSERT = 1 << 2, - DEBUG_ENABLE_SAFEMODE = 1 << 3, - DEBUG_ENABLE_JNI_LOGGING = 1 << 4, - DEBUG_GENERATE_DEBUG_INFO = 1 << 5, - DEBUG_ALWAYS_JIT = 1 << 6, - DEBUG_NATIVE_DEBUGGABLE = 1 << 7, - DEBUG_JAVA_DEBUGGABLE = 1 << 8, - DISABLE_VERIFIER = 1 << 9, - ONLY_USE_SYSTEM_OAT_FILES = 1 << 10, - DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 11, - HIDDEN_API_ENFORCEMENT_POLICY_MASK = (1 << 12) - | (1 << 13), - - // bits to shift (flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) by to get a value - // corresponding to hiddenapi::EnforcementPolicy - API_ENFORCEMENT_POLICY_SHIFT = CTZ(HIDDEN_API_ENFORCEMENT_POLICY_MASK), + DEBUG_ENABLE_JDWP = 1, + DEBUG_ENABLE_CHECKJNI = 1 << 1, + DEBUG_ENABLE_ASSERT = 1 << 2, + DEBUG_ENABLE_SAFEMODE = 1 << 3, + DEBUG_ENABLE_JNI_LOGGING = 1 << 4, + DEBUG_GENERATE_DEBUG_INFO = 1 << 5, + DEBUG_ALWAYS_JIT = 1 << 6, + DEBUG_NATIVE_DEBUGGABLE = 1 << 7, + DEBUG_JAVA_DEBUGGABLE = 1 << 8, + DISABLE_VERIFIER = 1 << 9, + ONLY_USE_SYSTEM_OAT_FILES = 1 << 10, + ENABLE_HIDDEN_API_CHECKS = 1 << 11, + DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12, }; static uint32_t EnableDebugFeatures(uint32_t runtime_flags) { @@ -290,8 +285,7 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, // Our system thread ID, etc, has changed so reset Thread state. thread->InitAfterFork(); runtime_flags = EnableDebugFeatures(runtime_flags); - hiddenapi::EnforcementPolicy api_enforcement_policy = hiddenapi::EnforcementPolicy::kNoChecks; - bool dedupe_hidden_api_warnings = true; + bool do_hidden_api_checks = false; if ((runtime_flags & DISABLE_VERIFIER) != 0) { Runtime::Current()->DisableVerifier(); @@ -303,9 +297,10 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, runtime_flags &= ~ONLY_USE_SYSTEM_OAT_FILES; } - api_enforcement_policy = hiddenapi::EnforcementPolicyFromInt( - (runtime_flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT); - runtime_flags &= ~HIDDEN_API_ENFORCEMENT_POLICY_MASK; + if ((runtime_flags & ENABLE_HIDDEN_API_CHECKS) != 0) { + do_hidden_api_checks = true; + runtime_flags &= ~ENABLE_HIDDEN_API_CHECKS; + } if (runtime_flags != 0) { LOG(ERROR) << StringPrintf("Unknown bits set in runtime_flags: %#x", runtime_flags); @@ -356,13 +351,11 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, } } - bool do_hidden_api_checks = api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks; DCHECK(!(is_system_server && do_hidden_api_checks)) - << "SystemServer should be forked with EnforcementPolicy::kDisable"; + << "SystemServer should be forked with ENABLE_HIDDEN_API_CHECKS"; DCHECK(!(is_zygote && do_hidden_api_checks)) - << "Child zygote processes should be forked with EnforcementPolicy::kDisable"; - Runtime::Current()->SetHiddenApiEnforcementPolicy(api_enforcement_policy); - Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe_hidden_api_warnings); + << "Child zygote processes should be forked with ENABLE_HIDDEN_API_CHECKS"; + Runtime::Current()->SetHiddenApiChecksEnabled(do_hidden_api_checks); // Clear the hidden API warning flag, in case it was set. Runtime::Current()->SetPendingHiddenApiWarning(false); diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 078102b033..25d50376de 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -89,8 +89,8 @@ static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator // access hidden APIs. This can be *very* expensive. Never call this in a loop. ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); - return policy != hiddenapi::EnforcementPolicy::kNoChecks && IsCallerInBootClassPath(self); + return Runtime::Current()->AreHiddenApiChecksEnabled() && + !IsCallerInBootClassPath(self); } // Returns true if the first non-ClassClass caller up the stack should not be diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 53982ae833..7d9d3426fc 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -267,7 +267,7 @@ Runtime::Runtime() oat_file_manager_(nullptr), is_low_memory_mode_(false), safe_mode_(false), - hidden_api_policy_(hiddenapi::EnforcementPolicy::kNoChecks), + do_hidden_api_checks_(false), pending_hidden_api_warning_(false), dedupe_hidden_api_warnings_(true), always_set_hidden_api_warning_flag_(false), @@ -1196,14 +1196,9 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // by default and we only enable them if: // (a) runtime was started with a flag that enables the checks, or // (b) Zygote forked a new process that is not exempt (see ZygoteHooks). - bool do_hidden_api_checks = runtime_options.Exists(Opt::HiddenApiChecks); - DCHECK(!is_zygote_ || !do_hidden_api_checks); - // TODO pass the actual enforcement policy in, rather than just a single bit. - // As is, we're encoding some logic here about which specific policy to use, which would be better - // controlled by the framework. - hidden_api_policy_ = do_hidden_api_checks - ? hiddenapi::EnforcementPolicy::kBlacklistOnly - : hiddenapi::EnforcementPolicy::kNoChecks; + do_hidden_api_checks_ = runtime_options.Exists(Opt::HiddenApiChecks); + DCHECK(!is_zygote_ || !do_hidden_api_checks_) + << "Zygote should not be started with hidden API checks"; no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain); force_native_bridge_ = runtime_options.Exists(Opt::ForceNativeBridge); diff --git a/runtime/runtime.h b/runtime/runtime.h index dba31b2939..c7f650ea3f 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -49,10 +49,6 @@ class AbstractSystemWeakHolder; class Heap; } // namespace gc -namespace hiddenapi { -enum class EnforcementPolicy; -} // namespace hiddenapi - namespace jit { class Jit; class JitOptions; @@ -524,12 +520,12 @@ class Runtime { bool IsVerificationEnabled() const; bool IsVerificationSoftFail() const; - void SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy policy) { - hidden_api_policy_ = policy; + void SetHiddenApiChecksEnabled(bool value) { + do_hidden_api_checks_ = value; } - hiddenapi::EnforcementPolicy GetHiddenApiEnforcementPolicy() const { - return hidden_api_policy_; + bool AreHiddenApiChecksEnabled() const { + return do_hidden_api_checks_; } void SetPendingHiddenApiWarning(bool value) { @@ -994,7 +990,7 @@ class Runtime { bool safe_mode_; // Whether access checks on hidden API should be performed. - hiddenapi::EnforcementPolicy hidden_api_policy_; + bool do_hidden_api_checks_; // Whether the application has used an API which is not restricted but we // should issue a warning about it. diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index bf36ccf0fa..67ea64be74 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -24,7 +24,6 @@ #include #include "entrypoints/quick/quick_entrypoints_enum.h" -#include "hidden_api.h" #include "jni_internal.h" #include "mirror/class.h" #include "mirror/throwable.h" @@ -288,17 +287,17 @@ class ScopedHiddenApiExemption { public: explicit ScopedHiddenApiExemption(Runtime* runtime) : runtime_(runtime), - initial_policy_(runtime_->GetHiddenApiEnforcementPolicy()) { - runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks); + initially_enabled_(runtime_->AreHiddenApiChecksEnabled()) { + runtime_->SetHiddenApiChecksEnabled(false); } ~ScopedHiddenApiExemption() { - runtime_->SetHiddenApiEnforcementPolicy(initial_policy_); + runtime_->SetHiddenApiChecksEnabled(initially_enabled_); } private: Runtime* runtime_; - const hiddenapi::EnforcementPolicy initial_policy_; + const bool initially_enabled_; DISALLOW_COPY_AND_ASSIGN(ScopedHiddenApiExemption); }; diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc index 04c3fbf03a..effa37ade4 100644 --- a/test/674-hiddenapi/hiddenapi.cc +++ b/test/674-hiddenapi/hiddenapi.cc @@ -16,7 +16,6 @@ #include "class_linker.h" #include "dex/art_dex_file_loader.h" -#include "hidden_api.h" #include "jni.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" @@ -28,7 +27,7 @@ namespace Test674HiddenApi { extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) { Runtime* runtime = Runtime::Current(); - runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly); + runtime->SetHiddenApiChecksEnabled(true); runtime->SetDedupeHiddenApiWarnings(false); runtime->AlwaysSetHiddenApiWarningFlag(); } -- GitLab From d5cbc56de42b1de0dfe64bbbf3d25b9806811862 Mon Sep 17 00:00:00 2001 From: Alan Leung Date: Thu, 15 Mar 2018 14:02:46 -0700 Subject: [PATCH 110/749] Replace DX DexMerger with D8 in platform BUG: 73981693 Test: art/test.py --verbose -j200 --host -b Change-Id: Idf7b71807019aca91094f69dba8c6b62e1a6bf16 --- test/121-modifiers/classes/A$B.class | Bin 223 -> 0 bytes test/121-modifiers/classes/A$C.class | Bin 130 -> 0 bytes test/121-modifiers/classes/A.class | Bin 248 -> 0 bytes test/121-modifiers/classes/Inf.class | Bin 128 -> 0 bytes test/121-modifiers/classes/Main.class | Bin 3772 -> 0 bytes test/121-modifiers/classes/NonInf.class | Bin 1026 -> 0 bytes test/121-modifiers/info.txt | 6 +- .../161-final-abstract-class/smali/Main.smali | 214 ++++++++++++++++++ test/161-final-abstract-class/src/Main.java | 48 ---- .../expected.txt | 158 ++++++------- .../src/art/Test1929.java | 93 ++++---- test/Android.run-test.mk | 3 +- test/etc/default-build | 22 +- test/run-test | 12 +- test/testrunner/env.py | 5 +- 15 files changed, 380 insertions(+), 181 deletions(-) delete mode 100644 test/121-modifiers/classes/A$B.class delete mode 100644 test/121-modifiers/classes/A$C.class delete mode 100644 test/121-modifiers/classes/A.class delete mode 100644 test/121-modifiers/classes/Inf.class delete mode 100644 test/121-modifiers/classes/Main.class delete mode 100644 test/121-modifiers/classes/NonInf.class create mode 100644 test/161-final-abstract-class/smali/Main.smali delete mode 100644 test/161-final-abstract-class/src/Main.java diff --git a/test/121-modifiers/classes/A$B.class b/test/121-modifiers/classes/A$B.class deleted file mode 100644 index bd7ebfe11dbd3399503f49a6e329521a883e94ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 223 zcmX^0Z`VEs1_omWK`sVn1}=669(D$PMg}&U%)HDJJ4Oa(4b3n{1{UZ1lvG9rexJ;| zRKL>Pq|~C2#H5^5Mh33n{L-T2RJTkJhtoGPGfyuou`H2?frWvUok4(+f!R^TiIIWP ziIIWFGcPZ-$T=smxHz?#kwE~YPCq9xFJ0e1DJwO(gpq;KkwJlhiGi5`1Q;2ZK!z|d zGO#f)Fvv16GB7eQFtBQEXJFjOz`(%Bz|O$Hzy=l+W?*9AU|?WiV&G)pV_;z5VgPC8 JX5eMu0ssV%Bntol diff --git a/test/121-modifiers/classes/A$C.class b/test/121-modifiers/classes/A$C.class deleted file mode 100644 index 3ae872e3560da3021de3ec4995230a59e660624d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 130 zcmX^0Z`VEs1_omWZgvJXb_PyH2Cm@z(xT*4x6GVWMg~sb#LPUsti-ZJb_Om+24+VU zXGR7_XGR7d&%C_UBIlgM;^Nd|Mg{?p68)USymWp4q^#8B5=I6_M>a+VMg}GZ2w-Ah ZW?*1oVqjt5U|?Y2VgSjpGO)Ap004>U7i0hc diff --git a/test/121-modifiers/classes/A.class b/test/121-modifiers/classes/A.class deleted file mode 100644 index d89d029796acd0acd2e2071c2a4904230c6377d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 248 zcmX^0Z`VEs1_omW5iSO127Y!10d@vKb_O9v21aK_1|HA6ywoD+oW$bd)M9o9VMYc< zCq@P~o6Nk-5<5l)W)00SMg|t={FGEi27aH+yi~u^+@#c^ki?{%R7M7_;QZ2}BwFz{J4H#>2qQz{J48zy$zch9!ak diff --git a/test/121-modifiers/classes/Inf.class b/test/121-modifiers/classes/Inf.class deleted file mode 100644 index e8dd68029df6b8b7f647cb2b79a104c648f12fa8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 128 zcmX^0Z`VEs1_omWE=C4s&%88t21Z5(fvm)`ME#t^ymWp4q^#8B5_Se=Mg|Vgyfi(K z3?l=hCo=;B0|O%iuXBE0aYV_XbQ3`rae$y^MM3@IE8sT>Sx91Q7P3>gfWTnt$Z*&tR9J3}r9 zLmn3c7ehWX14HKlkYE8PLm@*EJ3}!Sg8;~c5)fU=#Zbmj4q{bsF;p^CaWGVKFw}4` z)N(M?aWK?_^fhoXG%_?XGcc@I0I>=<7@9d4S~wV5IT&0(j%edxXa}k5;9}?mt9!%% zVij;Oba60rb1?LP6!mg2^l>osgIqWPL`>vhn8d*_nS)^p2g6hjhG`rO(>WMsa4^i| zV3@_hFq?y64hO?r4u*Lg4D&e{7O*ocWM^2!$ROtF7vdV^=IHDi@8as_>F4Sa@8lU0 z%*ep#$;iO#oS#=*l9*Q#mY7qT%FMt}X3xkV2A!c80}_46Ml+sma-l z45Av?)$<0IBqnG3CKiCrXJ=T#&ajl7VHrEaa(0Fl>WwSi-}wieWW7!*U*mH4JNc7}hbYXJn89Y1aq)H>fnPBr`V^ z?o~zxG1rQM)Z~)X6ote*h0MH?)S|S+5v*vznnkwF|`4kX;1 zN;7j(Qi~WF#GG>ytE%)s9?4J1Ov_9yD%Q}nQs80O%CL={VLK1Q4u+jP47(V1voq}B zVb}|Dya-B?0VfwL1x5zN(7cq?w9LHJ6osVBl46Cl{2~;Gfz${hoUD+XlUQ7=$H*W6 zPfu{8WUv_p(ExJVK8F4549j>J4lwLyWDrWja6BV}06aOu%@hyKONE738YpVPt^^qu z%Mi!NAPsjJTv8z~zeJ%pwM37H;UL2yaJmr6#bTKtB89_klR&a97o0-i*2ROYlYv`@ zkcHdF$iNCtd^`+?8RB>tjxZc$WMC^OP0GnkW@KP5D9S8LEJ*&M3;y%dARGVPs%0ODsw(N-t()V9QG^$t+7{WZ+0lDlREXOa|FdTvC*oT#{D6 z$bcNaP(MM9W&vdcMg{>y8uU-fN(E^bMhGFLYkq{NGboKPGDsn3{G!yfoYdqJeQ*$2 zqoi#VDHIzash*KR8kA-or0BeooIFMb!SvJ;m(=8( z#G=#`aP$^4GRSCXVv94dv^66GdwOaKBwmD&G(%#on2|viyKaa)NPA{pN@|6F8Y2T| zaVaQSXXd4YY8p>$USv>YaAa^|U|?WjkYsRXaA9C%aAja%U}A7%kYsR2<9i_SJsBh! zypZ_b46F={ph|&(k-?9FfkBpmk%5tcfq_+PI|JiJ1_lO327d+y1~#yuBm*Zy07D=H z6IiJ&RH>*C(+&n^Ar=t93L@BcFfb#uiZQSKpiGhDNgFxhV20TVfl7uogrg$d2U<6(D>#GRD0|&ZhFB!uDQsg92FoD(4@q7T%6poZ+~*$7vwTd@gg zKwPbf;c8HTgACO~b{CSXwLz|y{f{h*>}nkhSMOlZg*j9c?od6rLsLOXlL?%vmoaEE zFffENurh=(@G*olh%!Vl$TLJSC^1AcXfwnxcrnB>WH7`rG&3YHv@s+y^fDwdOlL@8 zn8lFFu$Uo@;SfVQ!vls4hTjaij6w`~jFJrbjIs;`j2aAuj2;X{jA;zTj7~s6-4%0wRu*<6;@&pgH~%0}BH)0|P_7mXQ871_K-Ec?>aH%+lK!41INk zjKB$im1T{NkTHbGx<*IH1j1xrqa$PrX0i!ztkDrN1GCu$85-8;2$_R9Yyu8zbc8IR zY=Je%F=vUI>cPbqsI2i~5Mp3pn8d)&FqwgyVG4r~!&C-ohG`743^N$i7-lkPFwAB! zWSGNX%rK9^hG9N~J;Oo<4~9hyJ`773LK&7aL@+F8NMu;SkixJU9CCsT?0*?#8QB@4 zxVX4^xPLKlGjK7)!~D-^0`>ngEg>tY|C`D4zctjG3Tt$PY`|=2SRnkbBV-GeU}RXM zBV-5Vuz)!BP!0zJN-#O#2&NRhMf$?47(X@81^vOGwfsVVA#*#&2W$*gy9fF1j7-AM24dbDGbLMav4rA6f&G* rsA4$HP{(kVp`GCzLpQ@kaPWyRa3O+^n}?f+=NAJH10zEMIKmPEbX%}5 diff --git a/test/121-modifiers/classes/NonInf.class b/test/121-modifiers/classes/NonInf.class deleted file mode 100644 index 0f1e826fb74781d9d93abdd3af58822c1db633b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1026 zcmX^0Z`VEs1_omW14afmzx+JUyfk(OMn(pKti-ZJ{hY+SbbbG%tkmQZb_Qlf25yK7 zJ&+_L19w4bQch;FTV`rb3L^ufCnEz-K~ZK|Vo54gh_9e1za%xeBsB#pz+GIDSOV9< zS5lOiSDcxeR|1vhEz8eIEXmA)>E}wz%uCFHO0wBx=4F=HF)}b~Xom4H@GSz=LQQF<{b5eWXGcYl*FfcH%uU7F)*xO017t-hV==cP-9?NZvYB21_lQ1M~t92U|?j> zWnf@PU|?ckWME*>(Av%*sKvaML0F4rD}$&O>sAJFw#^KZTI^dHq_sG{U9vfIh;t>mjG7;)z9x*bg5TQ=t86$%#gAxP7BSi)^ zBGjur;$%=qQOCf*z<}vatyhc;8Vs6n|3E^KiEllcfAk(TGH5Yq!__e|Fkv;%=#e0U V4vM{?Si!W{jDe9sA7lapD*#-QuZaKv diff --git a/test/121-modifiers/info.txt b/test/121-modifiers/info.txt index 335df53f3d..7dba1133d1 100644 --- a/test/121-modifiers/info.txt +++ b/test/121-modifiers/info.txt @@ -10,9 +10,9 @@ Finally, compile with jack/jill or dx, and run baksmali. javac Inf.java NonInf.java Main.java javac -cp asm.jar:asm-tree.jar:. Asm.java java -cp asm.jar:asm-tree.jar:. Asm -mv Inf.out classes/Inf.class -mv NonInf.out classes/NonInf.class -mv Main.class A.class A\$B.class A\$C.class classes/ +mv Inf.out classes_tmp/Inf.class +mv NonInf.out classes_tmp/NonInf.class +mv Main.class A.class A\$B.class A\$C.class classes_tmp/ dx --debug --dex --output=classes.dex classes baksmali disassemble classes.dex mv out/*.smali smali/ diff --git a/test/161-final-abstract-class/smali/Main.smali b/test/161-final-abstract-class/smali/Main.smali new file mode 100644 index 0000000000..588854cf52 --- /dev/null +++ b/test/161-final-abstract-class/smali/Main.smali @@ -0,0 +1,214 @@ +# Created with baksmali. + +# Java file for reference. + +# import java.lang.reflect.InvocationTargetException; +# import java.lang.reflect.Method; +# +# public class Main { +# public static void main(String[] args) { +# try { +# // Make sure that the abstract final class is marked as erroneous. +# Class.forName("AbstractFinal"); +# System.out.println("UNREACHABLE!"); +# } catch (VerifyError expected) { +# } catch (Throwable t) { +# t.printStackTrace(System.out); +# } +# try { +# // Verification of TestClass.test() used to crash when processing +# // the final abstract (erroneous) class. +# Class tc = Class.forName("TestClass"); +# Method test = tc.getDeclaredMethod("test"); +# test.invoke(null); +# System.out.println("UNREACHABLE!"); +# } catch (InvocationTargetException ite) { +# if (ite.getCause() instanceof InstantiationError) { +# System.out.println( +# ite.getCause().getClass().getName() + ": " + ite.getCause().getMessage()); +# } else { +# ite.printStackTrace(System.out); +# } +# } catch (Throwable t) { +# t.printStackTrace(System.out); +# } +# } +# } + +.class public LMain; +.super Ljava/lang/Object; +.source "Main.java" + + +# direct methods +.method public constructor ()V + .registers 1 + + .line 20 + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + +.method public static main([Ljava/lang/String;)V + .registers 4 + + .line 24 + :try_start_0 + const-string p0, "AbstractFinal" + + invoke-static {p0}, Ljava/lang/Class;->forName(Ljava/lang/String;)Ljava/lang/Class; + + .line 25 + sget-object p0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + const-string v0, "UNREACHABLE!" + + invoke-virtual {p0, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + :try_end_c + .catch Ljava/lang/VerifyError; {:try_start_0 .. :try_end_c} :catch_14 + .catch Ljava/lang/Throwable; {:try_start_0 .. :try_end_c} :catch_d + + goto :goto_15 + + .line 27 + :catch_d + move-exception p0 + + .line 28 + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + invoke-virtual {p0, v0}, Ljava/lang/Throwable;->printStackTrace(Ljava/io/PrintStream;)V + + goto :goto_16 + + .line 26 + :catch_14 + move-exception p0 + + .line 29 + :goto_15 + nop + + .line 33 + :goto_16 + :try_start_16 + const-string p0, "TestClass" + + invoke-static {p0}, Ljava/lang/Class;->forName(Ljava/lang/String;)Ljava/lang/Class; + + move-result-object p0 + + .line 34 + const-string v0, "test" + + const/4 v1, 0x0 + + new-array v2, v1, [Ljava/lang/Class; + + invoke-virtual {p0, v0, v2}, Ljava/lang/Class;->getDeclaredMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method; + + move-result-object p0 + + .line 35 + const/4 v0, 0x0 + + new-array v1, v1, [Ljava/lang/Object; + + invoke-virtual {p0, v0, v1}, Ljava/lang/reflect/Method;->invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; + + .line 36 + sget-object p0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + const-string v0, "UNREACHABLE!" + + invoke-virtual {p0, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + :try_end_32 + .catch Ljava/lang/reflect/InvocationTargetException; {:try_start_16 .. :try_end_32} :catch_3a + .catch Ljava/lang/Throwable; {:try_start_16 .. :try_end_32} :catch_33 + + goto :goto_76 + + .line 44 + :catch_33 + move-exception p0 + + .line 45 + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + invoke-virtual {p0, v0}, Ljava/lang/Throwable;->printStackTrace(Ljava/io/PrintStream;)V + + goto :goto_77 + + .line 37 + :catch_3a + move-exception p0 + + .line 38 + invoke-virtual {p0}, Ljava/lang/reflect/InvocationTargetException;->getCause()Ljava/lang/Throwable; + + move-result-object v0 + + instance-of v0, v0, Ljava/lang/InstantiationError; + + if-eqz v0, :cond_71 + + .line 39 + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + new-instance v1, Ljava/lang/StringBuilder; + + invoke-direct {v1}, Ljava/lang/StringBuilder;->()V + + .line 40 + invoke-virtual {p0}, Ljava/lang/reflect/InvocationTargetException;->getCause()Ljava/lang/Throwable; + + move-result-object v2 + + invoke-virtual {v2}, Ljava/lang/Object;->getClass()Ljava/lang/Class; + + move-result-object v2 + + invoke-virtual {v2}, Ljava/lang/Class;->getName()Ljava/lang/String; + + move-result-object v2 + + invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + const-string v2, ": " + + invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + invoke-virtual {p0}, Ljava/lang/reflect/InvocationTargetException;->getCause()Ljava/lang/Throwable; + + move-result-object p0 + + invoke-virtual {p0}, Ljava/lang/Throwable;->getMessage()Ljava/lang/String; + + move-result-object p0 + + invoke-virtual {v1, p0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + + move-result-object p0 + + .line 39 + invoke-virtual {v0, p0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + + goto :goto_76 + + .line 42 + :cond_71 + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + invoke-virtual {p0, v0}, Ljava/lang/reflect/InvocationTargetException;->printStackTrace(Ljava/io/PrintStream;)V + + .line 46 + :goto_76 + nop + + .line 47 + :goto_77 + return-void +.end method diff --git a/test/161-final-abstract-class/src/Main.java b/test/161-final-abstract-class/src/Main.java deleted file mode 100644 index 2452490226..0000000000 --- a/test/161-final-abstract-class/src/Main.java +++ /dev/null @@ -1,48 +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. - */ - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -public class Main { - public static void main(String[] args) { - try { - // Make sure that the abstract final class is marked as erroneous. - Class.forName("AbstractFinal"); - System.out.println("UNREACHABLE!"); - } catch (VerifyError expected) { - } catch (Throwable t) { - t.printStackTrace(System.out); - } - try { - // Verification of TestClass.test() used to crash when processing - // the final abstract (erroneous) class. - Class tc = Class.forName("TestClass"); - Method test = tc.getDeclaredMethod("test"); - test.invoke(null); - System.out.println("UNREACHABLE!"); - } catch (InvocationTargetException ite) { - if (ite.getCause() instanceof InstantiationError) { - System.out.println( - ite.getCause().getClass().getName() + ": " + ite.getCause().getMessage()); - } else { - ite.printStackTrace(System.out); - } - } catch (Throwable t) { - t.printStackTrace(System.out); - } - } -} diff --git a/test/1929-exception-catch-exception/expected.txt b/test/1929-exception-catch-exception/expected.txt index bc5608ac4e..a82b732eda 100644 --- a/test/1929-exception-catch-exception/expected.txt +++ b/test/1929-exception-catch-exception/expected.txt @@ -1,11 +1,11 @@ Test "art.Test1929$DoThrowClass": Running breakpoint with handler "art.Test1929$DoNothingHandler" -main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: doThrow +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 298 caught class art.Test1929$TestException: doThrow Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.run() throws java.lang.Exception @ line = 283 + public static void art.Test1929.run() throws java.lang.Exception @ line = 298 Test "art.Test1929$DoThrowClass": Caught error art.Test1929$TestException:"doThrow" with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowClass": Finished running with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchBaseTestException": Running breakpoint with handler "art.Test1929$DoNothingHandler" @@ -17,71 +17,71 @@ main: public static void art.Test1929.throwCatchBaseTestException() @ line = 140 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 public static void art.Test1929.throwCatchBaseTestException() @ line = 140 public void art.Test1929$DoThrowCatchBaseTestException.run() @ line = 149 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Doing nothing! Caught art.Test1929$TestException: "throwCatchBaseTestException" Test "art.Test1929$DoThrowCatchBaseTestException": No error caught with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchBaseTestException": Finished running with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Running breakpoint with handler "art.Test1929$DoNothingHandler" -main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice +main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 157 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 - public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 197 - public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 201 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 157 + public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 203 + public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 210 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Caught art.Test1929$TestException: "throwCatchBaseTestExceptionTwice" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": No error caught with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Finished running with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchTestException": Running breakpoint with handler "art.Test1929$DoNothingHandler" -main: public static void art.Test1929.throwCatchTestException() @ line = 207 caught class art.Test1929$TestException: throwCatchTestException +main: public static void art.Test1929.throwCatchTestException() @ line = 216 caught class art.Test1929$TestException: throwCatchTestException Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.throwCatchTestException() @ line = 207 - public void art.Test1929$DoThrowCatchTestException.run() @ line = 216 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929.throwCatchTestException() @ line = 216 + public void art.Test1929$DoThrowCatchTestException.run() @ line = 225 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Doing nothing! Caught art.Test1929$TestException: "throwCatchTestException" Test "art.Test1929$DoThrowCatchTestException": No error caught with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchTestException": Finished running with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchTestExceptionTwice": Running breakpoint with handler "art.Test1929$DoNothingHandler" -main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 caught class art.Test1929$TestException: throwCatchTestExceptionTwice +main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 175 caught class art.Test1929$TestException: throwCatchTestExceptionTwice Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 - public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 222 - public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 226 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 175 + public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 234 + public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 241 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Caught art.Test1929$TestException: "throwCatchTestExceptionTwice" Test "art.Test1929$DoThrowCatchTestExceptionTwice": No error caught with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchTestExceptionTwice": Finished running with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Running breakpoint with handler "art.Test1929$DoNothingHandler" -main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 298 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.run() throws java.lang.Exception @ line = 283 + public static void art.Test1929.run() throws java.lang.Exception @ line = 298 Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1929$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1929$DoNothingHandler" Test "art.Test1929$DoThrowClass": Running breakpoint with handler "art.Test1929$ThrowCatchBase" -main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: doThrow +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 298 caught class art.Test1929$TestException: doThrow Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.run() throws java.lang.Exception @ line = 283 + public static void art.Test1929.run() throws java.lang.Exception @ line = 298 Test "art.Test1929$DoThrowClass": Caught error art.Test1929$TestException:"doThrow" with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowClass": Finished running with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchBaseTestException": Running breakpoint with handler "art.Test1929$ThrowCatchBase" @@ -93,73 +93,73 @@ main: public static void art.Test1929.throwCatchBaseTestException() @ line = 140 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 public static void art.Test1929.throwCatchBaseTestException() @ line = 140 public void art.Test1929$DoThrowCatchBaseTestException.run() @ line = 149 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Throwing BaseTestException and catching it! Caught art.Test1929$BaseTestException: "ThrowBaseHandler during throw from public static void art.Test1929.throwCatchBaseTestException() @ line = 140" Caught art.Test1929$TestException: "throwCatchBaseTestException" Test "art.Test1929$DoThrowCatchBaseTestException": No error caught with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchBaseTestException": Finished running with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowCatchBase" -main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice +main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 157 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 - public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 197 - public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 201 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 157 + public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 203 + public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 210 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Caught art.Test1929$TestException: "throwCatchBaseTestExceptionTwice" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": No error caught with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Finished running with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchTestException": Running breakpoint with handler "art.Test1929$ThrowCatchBase" -main: public static void art.Test1929.throwCatchTestException() @ line = 207 caught class art.Test1929$TestException: throwCatchTestException +main: public static void art.Test1929.throwCatchTestException() @ line = 216 caught class art.Test1929$TestException: throwCatchTestException Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.throwCatchTestException() @ line = 207 - public void art.Test1929$DoThrowCatchTestException.run() @ line = 216 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929.throwCatchTestException() @ line = 216 + public void art.Test1929$DoThrowCatchTestException.run() @ line = 225 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Throwing BaseTestException and catching it! -Caught art.Test1929$BaseTestException: "ThrowBaseHandler during throw from public static void art.Test1929.throwCatchTestException() @ line = 207" +Caught art.Test1929$BaseTestException: "ThrowBaseHandler during throw from public static void art.Test1929.throwCatchTestException() @ line = 216" Caught art.Test1929$TestException: "throwCatchTestException" Test "art.Test1929$DoThrowCatchTestException": No error caught with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchTestException": Finished running with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowCatchBase" -main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 caught class art.Test1929$TestException: throwCatchTestExceptionTwice +main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 175 caught class art.Test1929$TestException: throwCatchTestExceptionTwice Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 - public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 222 - public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 226 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 175 + public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 234 + public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 241 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Caught art.Test1929$TestException: "throwCatchTestExceptionTwice" Test "art.Test1929$DoThrowCatchTestExceptionTwice": No error caught with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchTestExceptionTwice": Finished running with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Running breakpoint with handler "art.Test1929$ThrowCatchBase" -main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 298 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.run() throws java.lang.Exception @ line = 283 + public static void art.Test1929.run() throws java.lang.Exception @ line = 298 Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1929$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1929$ThrowCatchBase" Test "art.Test1929$DoThrowClass": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler" -main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: doThrow +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 298 caught class art.Test1929$TestException: doThrow Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.run() throws java.lang.Exception @ line = 283 + public static void art.Test1929.run() throws java.lang.Exception @ line = 298 Test "art.Test1929$DoThrowClass": Caught error art.Test1929$TestException:"doThrow" with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowClass": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchBaseTestException": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler" @@ -171,69 +171,69 @@ main: public static void art.Test1929.throwCatchBaseTestException() @ line = 140 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 public static void art.Test1929.throwCatchBaseTestException() @ line = 140 public void art.Test1929$DoThrowCatchBaseTestException.run() @ line = 149 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Throwing BaseTestException! Test "art.Test1929$DoThrowCatchBaseTestException": Caught error art.Test1929$BaseTestException:"ThrowBaseHandler during throw from public static void art.Test1929.throwCatchBaseTestException() @ line = 140" with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchBaseTestException": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler" -main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice +main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 157 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 - public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 197 - public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 201 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 157 + public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 203 + public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 210 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Caught art.Test1929$TestException: "throwCatchBaseTestExceptionTwice" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": No error caught with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchTestException": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler" -main: public static void art.Test1929.throwCatchTestException() @ line = 207 caught class art.Test1929$TestException: throwCatchTestException +main: public static void art.Test1929.throwCatchTestException() @ line = 216 caught class art.Test1929$TestException: throwCatchTestException Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.throwCatchTestException() @ line = 207 - public void art.Test1929$DoThrowCatchTestException.run() @ line = 216 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929.throwCatchTestException() @ line = 216 + public void art.Test1929$DoThrowCatchTestException.run() @ line = 225 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Throwing BaseTestException! -Test "art.Test1929$DoThrowCatchTestException": Caught error art.Test1929$BaseTestException:"ThrowBaseHandler during throw from public static void art.Test1929.throwCatchTestException() @ line = 207" with handler "art.Test1929$ThrowBaseTestExceptionHandler" +Test "art.Test1929$DoThrowCatchTestException": Caught error art.Test1929$BaseTestException:"ThrowBaseHandler during throw from public static void art.Test1929.throwCatchTestException() @ line = 216" with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchTestException": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler" -main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 caught class art.Test1929$TestException: throwCatchTestExceptionTwice +main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 175 caught class art.Test1929$TestException: throwCatchTestExceptionTwice Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 - public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 222 - public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 226 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 175 + public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 234 + public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 241 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Caught art.Test1929$TestException: "throwCatchTestExceptionTwice" Test "art.Test1929$DoThrowCatchTestExceptionTwice": No error caught with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchTestExceptionTwice": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler" -main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 298 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.run() throws java.lang.Exception @ line = 283 + public static void art.Test1929.run() throws java.lang.Exception @ line = 298 Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1929$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler" Test "art.Test1929$DoThrowClass": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" -main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: doThrow +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 298 caught class art.Test1929$TestException: doThrow Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.run() throws java.lang.Exception @ line = 283 + public static void art.Test1929.run() throws java.lang.Exception @ line = 298 Test "art.Test1929$DoThrowClass": Caught error art.Test1929$TestException:"doThrow" with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowClass": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchBaseTestException": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" @@ -245,58 +245,58 @@ main: public static void art.Test1929.throwCatchBaseTestException() @ line = 140 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 public static void art.Test1929.throwCatchBaseTestException() @ line = 140 public void art.Test1929$DoThrowCatchBaseTestException.run() @ line = 149 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Throwing TestExceptionNoRethrow! Test "art.Test1929$DoThrowCatchBaseTestException": Caught error art.Test1929$TestExceptionNoRethrow:"ThrowTestExceptionNoRethrowHandler during throw from public static void art.Test1929.throwCatchBaseTestException() @ line = 140" with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchBaseTestException": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" -main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice +main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 157 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 - public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 197 - public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 201 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 157 + public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 203 + public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 210 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Caught art.Test1929$TestException: "throwCatchBaseTestExceptionTwice" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": No error caught with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchTestException": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" -main: public static void art.Test1929.throwCatchTestException() @ line = 207 caught class art.Test1929$TestException: throwCatchTestException +main: public static void art.Test1929.throwCatchTestException() @ line = 216 caught class art.Test1929$TestException: throwCatchTestException Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.throwCatchTestException() @ line = 207 - public void art.Test1929$DoThrowCatchTestException.run() @ line = 216 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929.throwCatchTestException() @ line = 216 + public void art.Test1929$DoThrowCatchTestException.run() @ line = 225 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Throwing TestExceptionNoRethrow! -Test "art.Test1929$DoThrowCatchTestException": Caught error art.Test1929$TestExceptionNoRethrow:"ThrowTestExceptionNoRethrowHandler during throw from public static void art.Test1929.throwCatchTestException() @ line = 207" with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" +Test "art.Test1929$DoThrowCatchTestException": Caught error art.Test1929$TestExceptionNoRethrow:"ThrowTestExceptionNoRethrowHandler during throw from public static void art.Test1929.throwCatchTestException() @ line = 216" with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchTestException": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" -main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 caught class art.Test1929$TestException: throwCatchTestExceptionTwice +main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 175 caught class art.Test1929$TestException: throwCatchTestExceptionTwice Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 - public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 222 - public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 226 - public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 175 + public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 234 + public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 241 + public static void art.Test1929.run() throws java.lang.Exception @ line = 295 Caught art.Test1929$TestException: "throwCatchTestExceptionTwice" Test "art.Test1929$DoThrowCatchTestExceptionTwice": No error caught with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchTestExceptionTwice": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" -main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 298 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow Current Stack: private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 61 private static void art.Test1929.PrintStack() @ line = 52 public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 - public static void art.Test1929.run() throws java.lang.Exception @ line = 283 + public static void art.Test1929.run() throws java.lang.Exception @ line = 298 Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1929$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" diff --git a/test/1929-exception-catch-exception/src/art/Test1929.java b/test/1929-exception-catch-exception/src/art/Test1929.java index 07d2087a0f..e2deb3f85f 100644 --- a/test/1929-exception-catch-exception/src/art/Test1929.java +++ b/test/1929-exception-catch-exception/src/art/Test1929.java @@ -152,49 +152,58 @@ public class Test1929 { // dx/d8/jack all do an optimization around catch blocks that (while legal) breaks assumptions // this test relies on so we have the actual implementation be corrected smali. This does work // for RI however. - public static final class Impl { - private Impl() {} - public static void throwCatchBaseTestExceptionTwiceImpl() { - try { - try { - throw new TestException("throwCatchBaseTestExceptionTwice"); - } catch (BaseTestException t) { - System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); - if (PRINT_FULL_EXCEPTION) { - t.printStackTrace(System.out); - } - } - } catch (BaseTestException t) { - System.out.println("2nd Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); - if (PRINT_FULL_EXCEPTION) { - t.printStackTrace(System.out); - } - } - } - public static void throwCatchTestExceptionTwiceImpl() { - try { - try { - throw new TestException("throwCatchTestExceptionTwice"); - } catch (TestException t) { - System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); - if (PRINT_FULL_EXCEPTION) { - t.printStackTrace(System.out); - } - } - } catch (TestException t) { - System.out.println("2nd Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); - if (PRINT_FULL_EXCEPTION) { - t.printStackTrace(System.out); - } - } - } - } + // For reference: + + // public static final class Impl { + // private Impl() {} + // public static void throwCatchBaseTestExceptionTwiceImpl() { + // try { + // try { + // throw new TestException("throwCatchBaseTestExceptionTwice"); + // } catch (BaseTestException t) { + // System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + // if (PRINT_FULL_EXCEPTION) { + // t.printStackTrace(System.out); + // } + // } + // } catch (BaseTestException t) { + // System.out.println("2nd Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + // if (PRINT_FULL_EXCEPTION) { + // t.printStackTrace(System.out); + // } + // } + // } + + // public static void throwCatchTestExceptionTwiceImpl() { + // try { + // try { + // throw new TestException("throwCatchTestExceptionTwice"); + // } catch (TestException t) { + // System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + // if (PRINT_FULL_EXCEPTION) { + // t.printStackTrace(System.out); + // } + // } + // } catch (TestException t) { + // System.out.println("2nd Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + // if (PRINT_FULL_EXCEPTION) { + // t.printStackTrace(System.out); + // } + // } + // } + // } public static void throwCatchBaseTestExceptionTwice() { // The implementation of this has to change depending upon the runtime slightly due to compiler // optimizations present in DX/D8/Jack. - Impl.throwCatchBaseTestExceptionTwiceImpl(); + try { + Class Impl = Class.forName("art.Test1929$Impl"); + Method m = Impl.getMethod("throwCatchBaseTestExceptionTwiceImpl"); + m.invoke(null); + } catch (Exception e) { + e.printStackTrace(System.out); + } } public static class DoThrowCatchBaseTestExceptionTwice implements Runnable { @@ -219,7 +228,13 @@ public class Test1929 { public static void throwCatchTestExceptionTwice() { // The implementation of this has to change depending upon the runtime slightly due to compiler // optimizations present in DX/D8/Jack. - Impl.throwCatchTestExceptionTwiceImpl(); + try { + Class Impl = Class.forName("art.Test1929$Impl"); + Method m = Impl.getMethod("throwCatchTestExceptionTwiceImpl"); + m.invoke(null); + } catch (Exception e) { + e.printStackTrace(System.out); + } } public static class DoThrowCatchTestExceptionTwice implements Runnable { diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index cf781d7f2b..6633958140 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -24,7 +24,6 @@ TEST_ART_RUN_TEST_DEPENDENCIES := \ $(HOST_OUT_EXECUTABLES)/hiddenapi \ $(HOST_OUT_EXECUTABLES)/jasmin \ $(HOST_OUT_EXECUTABLES)/smali \ - $(HOST_OUT_EXECUTABLES)/dexmerger \ $(HOST_OUT_JAVA_LIBRARIES)/desugar.jar # Add d8 dependency, if enabled. @@ -103,7 +102,7 @@ endif # Host executables. host_prereq_rules := $(ART_TEST_HOST_RUN_TEST_DEPENDENCIES) -# Required for dx, jasmin, smali, dexmerger. +# Required for dx, jasmin, smali. host_prereq_rules += $(TEST_ART_RUN_TEST_DEPENDENCIES) # Sync test files to the target, depends upon all things that must be pushed diff --git a/test/etc/default-build b/test/etc/default-build index 3e6577cfda..9de7294a59 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -341,8 +341,26 @@ function make_dexmerge() { shift done - # Should have at least 1 dex_files_to_merge here, otherwise dxmerger will print the help. - ${DXMERGER} "$dst_file" "${dex_files_to_merge[@]}" + # Skip merge if we are not merging anything. IE: input = output. + if [[ "${#dex_files_to_merge[@]}" -eq "1" ]]; then + local single_input=${dex_files_to_merge[0]} + if [[ "$dst_file" != "$single_input" ]]; then + mv "$single_input" "$dst_file"; + return + fi + fi + + # We assume the dexer did all the API level checks and just merge away. + mkdir d8_merge_out + ${DXMERGER} --min-api 1000 --output ./d8_merge_out "${dex_files_to_merge[@]}" + + if [[ -e "./d8_merge_out/classes2.dex" ]]; then + echo "Cannot merge all dex files into a single dex" + exit 1 + fi + + mv ./d8_merge_out/classes.dex "$dst_file"; + rmdir d8_merge_out } function make_hiddenapi() { diff --git a/test/run-test b/test/run-test index 260a65a056..5b43b52b41 100755 --- a/test/run-test +++ b/test/run-test @@ -50,11 +50,18 @@ export USE_JACK="false" export USE_DESUGAR="true" export SMALI_ARGS="" +# If d8 was not set by the environment variable, assume it is in the path. +if [ -z "$D8" ]; then + export D8="d8" +fi + # If dx was not set by the environment variable, assume it is in the path. if [ -z "$DX" ]; then export DX="dx" fi +export DXMERGER="$D8" + # If jasmin was not set by the environment variable, assume it is in the path. if [ -z "$JASMIN" ]; then export JASMIN="jasmin" @@ -65,11 +72,6 @@ if [ -z "$SMALI" ]; then export SMALI="smali" fi -# If dexmerger was not set by the environment variable, assume it is in the path. -if [ -z "$DXMERGER" ]; then - export DXMERGER="dexmerger" -fi - # If jack was not set by the environment variable, assume it is in the path. if [ -z "$JACK" ]; then export JACK="jack" diff --git a/test/testrunner/env.py b/test/testrunner/env.py index 70efce51ee..539499173c 100644 --- a/test/testrunner/env.py +++ b/test/testrunner/env.py @@ -136,9 +136,8 @@ HOST_OUT_EXECUTABLES = os.path.join(ANDROID_BUILD_TOP, _get_build_var("HOST_OUT_EXECUTABLES")) # Set up default values for $JACK, $DX, $SMALI, etc to the $HOST_OUT_EXECUTABLES/$name path. -for tool in ['jack', 'dx', 'smali', 'jasmin', 'dxmerger']: - binary = tool if tool != 'dxmerger' else 'dexmerger' - os.environ.setdefault(tool.upper(), HOST_OUT_EXECUTABLES + '/' + binary) +for tool in ['jack', 'dx', 'smali', 'jasmin', 'd8']: + os.environ.setdefault(tool.upper(), HOST_OUT_EXECUTABLES + '/' + tool) ANDROID_JAVA_TOOLCHAIN = os.path.join(ANDROID_BUILD_TOP, _get_build_var('ANDROID_JAVA_TOOLCHAIN')) -- GitLab From d1f70975c3067d63e7d5fed6f9a4bcceaca6fec3 Mon Sep 17 00:00:00 2001 From: Nan Zhang Date: Tue, 23 Jan 2018 16:46:51 -0800 Subject: [PATCH 111/749] Add Soong target for converting ahat droiddocs. Test: m ahat-docs the output is srcjar instead of a timestamp file. Bug: b/70351683 Change-Id: I278076a57df6d81032e64f390b61e3d5db885098 --- tools/ahat/Android.bp | 25 +++++++++++++++++++++++++ tools/ahat/Android.mk | 17 ++--------------- 2 files changed, 27 insertions(+), 15 deletions(-) create mode 100644 tools/ahat/Android.bp diff --git a/tools/ahat/Android.bp b/tools/ahat/Android.bp new file mode 100644 index 0000000000..dc9f098ff3 --- /dev/null +++ b/tools/ahat/Android.bp @@ -0,0 +1,25 @@ +// Copyright 2018 Google Inc. All rights reserved. +// +// 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. + +droiddoc_host { + name: "ahat-docs", + srcs: [ + "src/main/**/*.java", + ], + custom_template: "droiddoc-templates-sdk", + args: "-stubpackages com.android.ahat:com.android.ahat.*", + api_tag_name: "AHAT", + api_filename: "ahat_api.txt", + removed_api_filename: "ahat_removed_api.txt", +} diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk index bf79751659..ad33233159 100644 --- a/tools/ahat/Android.mk +++ b/tools/ahat/Android.mk @@ -37,23 +37,10 @@ LOCAL_COMPATIBILITY_SUITE := general-tests include $(BUILD_HOST_JAVA_LIBRARY) AHAT_JAR := $(LOCAL_BUILT_MODULE) -AHAT_API := $(intermediates.COMMON)/ahat_api.txt -AHAT_REMOVED_API := $(intermediates.COMMON)/ahat_removed_api.txt # --- api check for ahat.jar ---------- -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-java-files-under, src/main) -LOCAL_IS_HOST_MODULE := true -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := JAVA_LIBRARIES -LOCAL_MODULE := ahat -LOCAL_DROIDDOC_OPTIONS := \ - -stubpackages com.android.ahat:com.android.ahat.* \ - -api $(AHAT_API) \ - -removedApi $(AHAT_REMOVED_API) -LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR := external/doclava/res/assets/templates-sdk -include $(BUILD_DROIDDOC) -$(AHAT_API): $(full_target) +AHAT_API := $(INTERNAL_PLATFORM_AHAT_API_FILE) +AHAT_REMOVED_API := $(INTERNAL_PLATFORM_AHAT_REMOVED_API_FILE) $(eval $(call check-api, \ ahat-check-api, \ -- GitLab From 5c7e618a14d638109648094ba37bb3843279ad11 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Wed, 21 Mar 2018 14:45:15 +0000 Subject: [PATCH 112/749] Revert "Warn on overriding of hidden methods" This reverts commit fc66129b478d49f493b8262f81f8813a5f41459e. Reason for revert: This was also reverted in internal Bug: 64382372 Merged-In: I69bdc0fb79831b5ce546205fd40a3300d039de71 Change-Id: Ice53e73eae7313ae6c72369a1b2e984fb978f2ab --- runtime/class_linker.cc | 8 ----- runtime/hidden_api.h | 4 --- test/674-hiddenapi/src-ex/ChildClass.java | 34 ------------------- test/674-hiddenapi/src-ex/Linking.java | 11 ------ test/674-hiddenapi/src-ex/OverrideClass.java | 35 -------------------- 5 files changed, 92 deletions(-) delete mode 100644 test/674-hiddenapi/src-ex/OverrideClass.java diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index f720558e51..9990a373c9 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -5836,14 +5836,6 @@ 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()) { diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index f2ea2fdaaa..7ca2378a07 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -38,7 +38,6 @@ enum AccessMethod { kReflection, kJNI, kLinking, - kOverride, }; inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { @@ -52,9 +51,6 @@ inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { case kLinking: os << "linking"; break; - case kOverride: - os << "override"; - break; } return os; } diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java index 8cd237ab6f..582e907ca3 100644 --- a/test/674-hiddenapi/src-ex/ChildClass.java +++ b/test/674-hiddenapi/src-ex/ChildClass.java @@ -123,9 +123,6 @@ public class ChildClass { // 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. @@ -406,37 +403,6 @@ 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 b416250953..a89b92b2b9 100644 --- a/test/674-hiddenapi/src-ex/Linking.java +++ b/test/674-hiddenapi/src-ex/Linking.java @@ -14,7 +14,6 @@ * limitations under the License. */ -import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; public class Linking { @@ -35,16 +34,6 @@ public class Linking { } } } - - 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 diff --git a/test/674-hiddenapi/src-ex/OverrideClass.java b/test/674-hiddenapi/src-ex/OverrideClass.java deleted file mode 100644 index 1f1f4d6aac..0000000000 --- a/test/674-hiddenapi/src-ex/OverrideClass.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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; } - -} -- GitLab From dd96ed35d00eb9ea6ad35a7a04d98078cfce7f0e Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 21 Mar 2018 11:00:14 +0000 Subject: [PATCH 113/749] Add resolver to veridex. Resolver runs over dex file and cache classes/methods/fields. Change-Id: I0574891cd72b5aa72f97784c6391912fa8ed8c1c --- tools/veridex/Android.bp | 5 ++- tools/veridex/resolver.cc | 58 +++++++++++++++++++++++++++++++ tools/veridex/resolver.h | 46 +++++++++++++++++++++++++ tools/veridex/veridex.cc | 27 +++++++++++++++ tools/veridex/veridex.h | 72 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 tools/veridex/resolver.cc create mode 100644 tools/veridex/resolver.h create mode 100644 tools/veridex/veridex.h diff --git a/tools/veridex/Android.bp b/tools/veridex/Android.bp index cac441aaf0..31ff682828 100644 --- a/tools/veridex/Android.bp +++ b/tools/veridex/Android.bp @@ -15,7 +15,10 @@ art_cc_binary { name: "veridex", host_supported: true, - srcs: ["veridex.cc"], + srcs: [ + "resolver.cc", + "veridex.cc", + ], cflags: ["-Wall", "-Werror"], shared_libs: ["libdexfile", "libbase"], header_libs: [ diff --git a/tools/veridex/resolver.cc b/tools/veridex/resolver.cc new file mode 100644 index 0000000000..c0705e5ea8 --- /dev/null +++ b/tools/veridex/resolver.cc @@ -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. + */ + +#include "resolver.h" + +#include "dex/dex_file-inl.h" +#include "dex/primitive.h" +#include "veridex.h" + +namespace art { + +void VeridexResolver::Run() { + size_t class_def_count = dex_file_.NumClassDefs(); + for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) { + const DexFile::ClassDef& class_def = dex_file_.GetClassDef(class_def_index); + std::string name(dex_file_.StringByTypeIdx(class_def.class_idx_)); + auto existing = type_map_.find(name); + if (existing != type_map_.end()) { + // Class already exists, cache it and move on. + type_infos_[class_def.class_idx_.index_] = *existing->second; + continue; + } + type_infos_[class_def.class_idx_.index_] = VeriClass(Primitive::Type::kPrimNot, 0, &class_def); + type_map_[name] = &(type_infos_[class_def.class_idx_.index_]); + + const uint8_t* class_data = dex_file_.GetClassData(class_def); + if (class_data == nullptr) { + // Empty class. + continue; + } + + ClassDataItemIterator it(dex_file_, class_data); + for (; it.HasNextStaticField(); it.Next()) { + field_infos_[it.GetMemberIndex()] = it.DataPointer(); + } + for (; it.HasNextInstanceField(); it.Next()) { + field_infos_[it.GetMemberIndex()] = it.DataPointer(); + } + for (; it.HasNextMethod(); it.Next()) { + method_infos_[it.GetMemberIndex()] = it.DataPointer(); + } + } +} + +} // namespace art diff --git a/tools/veridex/resolver.h b/tools/veridex/resolver.h new file mode 100644 index 0000000000..4e0c5b3732 --- /dev/null +++ b/tools/veridex/resolver.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#ifndef ART_TOOLS_VERIDEX_RESOLVER_H_ +#define ART_TOOLS_VERIDEX_RESOLVER_H_ + +#include "dex/dex_file.h" +#include "veridex.h" + +namespace art { + +class VeridexResolver { + public: + VeridexResolver(const DexFile& dex_file, TypeMap& type_map) + : dex_file_(dex_file), + type_map_(type_map), + type_infos_(dex_file.NumTypeIds(), VeriClass()), + method_infos_(dex_file.NumMethodIds(), nullptr), + field_infos_(dex_file.NumFieldIds(), nullptr) {} + + void Run(); + + private: + const DexFile& dex_file_; + TypeMap& type_map_; + std::vector type_infos_; + std::vector method_infos_; + std::vector field_infos_; +}; + +} // namespace art + +#endif // ART_TOOLS_VERIDEX_RESOLVER_H_ diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc index 9d0dd36019..0370a0329c 100644 --- a/tools/veridex/veridex.cc +++ b/tools/veridex/veridex.cc @@ -14,10 +14,13 @@ * limitations under the License. */ +#include "veridex.h" + #include #include "dex/dex_file.h" #include "dex/dex_file_loader.h" +#include "resolver.h" #include @@ -108,6 +111,18 @@ class Veridex { return 1; } } + + // Resolve classes/methods/fields defined in each dex file. + + // Cache of types we've seen. This is used in case of duplicate classes. + TypeMap type_map; + + std::vector boot_resolvers; + Resolve(boot_dex_files, type_map, &boot_resolvers); + + std::vector app_resolvers; + Resolve(app_dex_files, type_map, &app_resolvers); + return 0; } @@ -142,6 +157,18 @@ class Veridex { return true; } + + static void Resolve(const std::vector>& dex_files, + TypeMap& type_map, + std::vector* resolvers) { + for (const std::unique_ptr& dex_file : dex_files) { + resolvers->push_back(VeridexResolver(*dex_file.get(), type_map)); + } + + for (VeridexResolver& resolver : *resolvers) { + resolver.Run(); + } + } }; } // namespace art diff --git a/tools/veridex/veridex.h b/tools/veridex/veridex.h new file mode 100644 index 0000000000..bbff254f0a --- /dev/null +++ b/tools/veridex/veridex.h @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#ifndef ART_TOOLS_VERIDEX_VERIDEX_H_ +#define ART_TOOLS_VERIDEX_VERIDEX_H_ + +#include + +#include "dex/dex_file.h" +#include "dex/primitive.h" + +namespace art { + +/** + * Abstraction for classes defined, or implicitly defined (for arrays and primitives) + * in dex files. + */ +class VeriClass { + public: + VeriClass(const VeriClass& other) = default; + VeriClass() = default; + VeriClass(Primitive::Type k, uint8_t dims, const DexFile::ClassDef* cl) + : kind_(k), dimensions_(dims), class_def_(cl) {} + + bool IsUninitialized() const { + return kind_ == Primitive::Type::kPrimNot && dimensions_ == 0 && class_def_ == nullptr; + } + + bool IsPrimitive() const { + return kind_ != Primitive::Type::kPrimNot && dimensions_ == 0; + } + + bool IsArray() const { + return dimensions_ != 0; + } + + private: + Primitive::Type kind_; + uint8_t dimensions_; + const DexFile::ClassDef* class_def_; +}; + +/** + * Abstraction for fields defined in dex files. Currently, that's a pointer into their + * `encoded_field` description. + */ +using VeriField = const uint8_t*; + +/** + * Abstraction for methods defined in dex files. Currently, that's a pointer into their + * `encoded_method` description. + */ +using VeriMethod = const uint8_t*; + +using TypeMap = std::map; + +} // namespace art + +#endif // ART_TOOLS_VERIDEX_VERIDEX_H_ -- GitLab From d45863a976c2fd10cf179d8ff42926a7a37c70f0 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 21 Mar 2018 18:16:36 -0700 Subject: [PATCH 114/749] Run dex verifier for OOB + compact-dex-level combination Previously, there was logic to not run the dex verifier if compact-dex-level was not "none". The idea was that the dex writing would have run the verifier. This caused issues for OOB APKs since these don't write out the DEX. This CL moves the dex verifier avoidance logic to the dex loader for the compact dex input case. Bug: 75970654 Test: test-art-host Change-Id: Ic7af6857edb8f7d8e449fee6a544f184aad79b3a --- dex2oat/dex2oat.cc | 8 +++----- dex2oat/dex2oat_test.cc | 33 +++++++++++++++++++++++++++++++ libdexfile/dex/dex_file_loader.cc | 2 ++ 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 9b370178f7..6950b93e51 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1609,11 +1609,9 @@ class Dex2Oat FINAL { // Unzip or copy dex files straight to the oat file. std::vector> opened_dex_files_map; std::vector> opened_dex_files; - // No need to verify the dex file for: - // 1) Dexlayout since it does the verification. It also may not pass the verification since - // we don't update the dex checksum. - // 2) when we have a vdex file, which means it was already verified. - const bool verify = !DoDexLayoutOptimizations() && (input_vdex_file_ == nullptr); + // No need to verify the dex file when we have a vdex file, which means it was already + // verified. + const bool verify = (input_vdex_file_ == nullptr); if (!oat_writers_[i]->WriteAndOpenDexFiles( vdex_files_[i].get(), rodata_.back(), diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 5590c8b3ab..5e9782aadf 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -2013,4 +2013,37 @@ TEST_F(Dex2oatTest, QuickenedInput) { ASSERT_EQ(vdex_unquickened->FlushCloseOrErase(), 0) << "Could not flush and close"; } +// Test that compact dex generation with invalid dex files doesn't crash dex2oat. b/75970654 +TEST_F(Dex2oatTest, CompactDexInvalidSource) { + ScratchFile invalid_dex; + { + FILE* file = fdopen(invalid_dex.GetFd(), "w+b"); + ZipWriter writer(file); + writer.StartEntry("classes.dex", ZipWriter::kAlign32); + DexFile::Header header = {}; + StandardDexFile::WriteMagic(header.magic_); + StandardDexFile::WriteCurrentVersion(header.magic_); + header.file_size_ = 4 * KB; + header.data_size_ = 4 * KB; + header.data_off_ = 10 * MB; + header.map_off_ = 10 * MB; + header.class_defs_off_ = 10 * MB; + header.class_defs_size_ = 10000; + ASSERT_GE(writer.WriteBytes(&header, sizeof(header)), 0); + writer.FinishEntry(); + writer.Finish(); + ASSERT_EQ(invalid_dex.GetFile()->Flush(), 0); + } + const std::string dex_location = invalid_dex.GetFilename(); + const std::string odex_location = GetOdexDir() + "/output.odex"; + std::string error_msg; + int status = GenerateOdexForTestWithStatus( + {dex_location}, + odex_location, + CompilerFilter::kQuicken, + &error_msg, + { "--compact-dex-level=fast" }); + ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) != 0) << status << " " << output_; +} + } // namespace art diff --git a/libdexfile/dex/dex_file_loader.cc b/libdexfile/dex/dex_file_loader.cc index 758a2f0599..1e0f5ac6ae 100644 --- a/libdexfile/dex/dex_file_loader.cc +++ b/libdexfile/dex/dex_file_loader.cc @@ -348,6 +348,8 @@ std::unique_ptr DexFileLoader::OpenCommon(const uint8_t* base, location_checksum, oat_dex_file, std::move(container))); + // Disable verification for CompactDex input. + verify = false; } else { *error_msg = "Invalid or truncated dex file"; } -- GitLab From 41b1f0ef0cf1ba3035dec395cc18b9ffe80ebe5d Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 21 Mar 2018 11:17:12 +0000 Subject: [PATCH 115/749] Pass --runtime-option from testrunner to run-test. Note that the python argument parser does not like option arguments starting with a '-' if passed like --runtime-option -Xjitthreshold:0 so such options need to be passed with '=' as in --runtime-option=-Xjitthreshold:0 Test: testrunner.py --help (manual inspection) Test: testrunner.py --host --jit -t 667-jit-jni-stub (OK) Test: testrunner.py --host --jit -t 667-jit-jni-stub \ --runtime-option=-Xjitthreshold:0 (2 FAILURES) Bug: 62611253 Change-Id: I6ebe18fe27b24989320ad963516146d12da4664f --- test/testrunner/testrunner.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index a2215f9e9b..734a600c5e 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -114,6 +114,7 @@ ignore_skips = False build = False gdb = False gdb_arg = '' +runtime_option = '' stop_testrunner = False dex2oat_jobs = -1 # -1 corresponds to default threads for dex2oat run_all_configs = False @@ -346,6 +347,10 @@ def run_tests(tests): if gdb_arg: options_all += ' --gdb-arg ' + gdb_arg + if runtime_option: + for opt in runtime_option: + options_all += ' --runtime-option ' + opt + if dex2oat_jobs != -1: options_all += ' --dex2oat-jobs ' + str(dex2oat_jobs) @@ -921,6 +926,7 @@ def parse_option(): global build global gdb global gdb_arg + global runtime_option global timeout global dex2oat_jobs global run_all_configs @@ -933,9 +939,9 @@ def parse_option(): global_group.add_argument('--timeout', default=timeout, type=int, dest='timeout') global_group.add_argument('--verbose', '-v', action='store_true', dest='verbose') global_group.add_argument('--dry-run', action='store_true', dest='dry_run') - global_group.add_argument("--skip", action="append", dest="skips", default=[], + global_group.add_argument("--skip", action='append', dest="skips", default=[], help="Skip the given test in all circumstances.") - global_group.add_argument("--no-skips", dest="ignore_skips", action="store_true", default=False, + global_group.add_argument("--no-skips", dest="ignore_skips", action='store_true', default=False, help="""Don't skip any run-test configurations listed in knownfailures.json.""") global_group.add_argument('--no-build-dependencies', @@ -950,6 +956,10 @@ def parse_option(): global_group.set_defaults(build = env.ART_TEST_RUN_TEST_BUILD) global_group.add_argument('--gdb', action='store_true', dest='gdb') global_group.add_argument('--gdb-arg', dest='gdb_arg') + global_group.add_argument('--runtime-option', action='append', dest='runtime_option', + help="""Pass an option to the runtime. Runtime options + starting with a '-' must be separated by a '=', for + example '--runtime-option=-Xjitthreshold:0'.""") global_group.add_argument('--dex2oat-jobs', type=int, dest='dex2oat_jobs', help='Number of dex2oat jobs') global_group.add_argument('-a', '--all', action='store_true', dest='run_all', @@ -993,6 +1003,7 @@ def parse_option(): gdb = True if options['gdb_arg']: gdb_arg = options['gdb_arg'] + runtime_option = options['runtime_option']; timeout = options['timeout'] if options['dex2oat_jobs']: dex2oat_jobs = options['dex2oat_jobs'] -- GitLab From f8655b3411967f7cacd033f41d3c430016751c7d Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 21 Mar 2018 17:53:56 +0000 Subject: [PATCH 116/749] Fix test failures with -Xjitthreshold:0. Namely - 667-jit-jni-stub: fix GenericJNI to respect -Xjitthreshold:0, - 1935-get-set-current-frame-jit: check for OSR, not for being interpreted. However, some failures remain when --gcstress is added. Test: testrunner.py --host --jit --runtime-option=-Xjitthreshold:0 Test: testrunner.py --host Bug: 62611253 Change-Id: I4ca880f6a8b64a1659a27a107fae9933d4174f8d --- .../entrypoints/quick/quick_trampoline_entrypoints.cc | 10 ++++++---- runtime/jit/jit.cc | 7 ++++--- test/1935-get-set-current-frame-jit/expected.txt | 4 ++-- test/1935-get-set-current-frame-jit/src/Main.java | 8 +++++--- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index e9a0808843..c37e9eaa1b 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -2246,10 +2246,6 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod** ArtMethod* called = *sp; DCHECK(called->IsNative()) << called->PrettyMethod(true); Runtime* runtime = Runtime::Current(); - jit::Jit* jit = runtime->GetJit(); - if (jit != nullptr) { - jit->AddSamples(self, called, 1u, /*with_backedges*/ false); - } uint32_t shorty_len = 0; const char* shorty = called->GetShorty(&shorty_len); bool critical_native = called->IsCriticalNative(); @@ -2275,6 +2271,12 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod** self->VerifyStack(); + // We can now walk the stack if needed by JIT GC from MethodEntered() for JIT-on-first-use. + jit::Jit* jit = runtime->GetJit(); + if (jit != nullptr) { + jit->MethodEntered(self, called); + } + uint32_t cookie; uint32_t* sp32; // Skip calling JniMethodStart for @CriticalNative. diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 23cf071d56..813430f0bb 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -718,10 +718,11 @@ void Jit::MethodEntered(Thread* thread, ArtMethod* method) { Runtime* runtime = Runtime::Current(); if (UNLIKELY(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse())) { ArtMethod* np_method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize); - DCHECK(!np_method->IsNative()); if (np_method->IsCompilable()) { - // The compiler requires a ProfilingInfo object. - ProfilingInfo::Create(thread, np_method, /* retry_allocation */ true); + if (!np_method->IsNative()) { + // The compiler requires a ProfilingInfo object for non-native methods. + ProfilingInfo::Create(thread, np_method, /* retry_allocation */ true); + } JitCompileTask compile_task(method, JitCompileTask::kCompile); compile_task.Run(thread); } diff --git a/test/1935-get-set-current-frame-jit/expected.txt b/test/1935-get-set-current-frame-jit/expected.txt index fed993cc1a..cdb8f6a825 100644 --- a/test/1935-get-set-current-frame-jit/expected.txt +++ b/test/1935-get-set-current-frame-jit/expected.txt @@ -1,7 +1,7 @@ JNI_OnLoad called From GetLocalInt(), value is 42 -isInterpreted? true +isInOsrCode? false Value is '42' Setting TARGET to 1337 -isInterpreted? true +isInOsrCode? false Value is '1337' diff --git a/test/1935-get-set-current-frame-jit/src/Main.java b/test/1935-get-set-current-frame-jit/src/Main.java index eb0a6374d2..714a98aaf3 100644 --- a/test/1935-get-set-current-frame-jit/src/Main.java +++ b/test/1935-get-set-current-frame-jit/src/Main.java @@ -64,9 +64,9 @@ public class Main { Main.ensureJitCompiled(IntRunner.class, "run"); i++; } - // We shouldn't be doing OSR since we are using JVMTI and the get/set local will push us to - // interpreter. - System.out.println("isInterpreted? " + Main.isInterpreted()); + // We shouldn't be doing OSR since we are using JVMTI and the get/set prevents OSR. + // Set local will also push us to interpreter but the get local may remain in compiled code. + System.out.println("isInOsrCode? " + (hasJit() && Main.isInOsrCode("run"))); reportValue(TARGET); } public void waitForBusyLoopStart() { while (!inBusyLoop) {} } @@ -159,4 +159,6 @@ public class Main { public static native void ensureJitCompiled(Class k, String f); public static native boolean isInterpreted(); + public static native boolean isInOsrCode(String methodName); + public static native boolean hasJit(); } -- GitLab From 159f596eec01adbb5a1c9654402c137cdb943131 Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Thu, 22 Mar 2018 11:36:47 +0000 Subject: [PATCH 117/749] Revert "Revert "More flexible API enforcement policy support."" This reverts commit d53aa8864e1a73fe9def1f93e76b2a4425d6b313. Reason for revert: Re-submitting after fixing tests. Fixed a bug java_lang_Class.cc (missing "!"). Test: See original change Change-Id: I33f9afce628a86727e400052f4d5979d3536da8c --- runtime/hidden_api.h | 69 ++++++++++++++++----- runtime/native/dalvik_system_ZygoteHooks.cc | 49 ++++++++------- runtime/native/java_lang_Class.cc | 4 +- runtime/runtime.cc | 13 ++-- runtime/runtime.h | 14 +++-- runtime/well_known_classes.cc | 9 +-- test/674-hiddenapi/hiddenapi.cc | 3 +- 7 files changed, 107 insertions(+), 54 deletions(-) diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index f2ea2fdaaa..321d55d9b7 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -27,6 +27,23 @@ namespace art { namespace hiddenapi { +// Hidden API enforcement policy +// This must be kept in sync with ApplicationInfo.ApiEnforcementPolicy in +// frameworks/base/core/java/android/content/pm/ApplicationInfo.java +enum class EnforcementPolicy { + kNoChecks = 0, + kAllLists = 1, // ban anything but whitelist + kDarkGreyAndBlackList = 2, // ban dark grey & blacklist + kBlacklistOnly = 3, // ban blacklist violations only + kMax = kBlacklistOnly, +}; + +inline EnforcementPolicy EnforcementPolicyFromInt(int api_policy_int) { + DCHECK_GE(api_policy_int, 0); + DCHECK_LE(api_policy_int, static_cast(EnforcementPolicy::kMax)); + return static_cast(api_policy_int); +} + enum Action { kAllow, kAllowButWarn, @@ -59,16 +76,38 @@ inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { return os; } +static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags::ApiList apiList) { + return static_cast(policy) == static_cast(apiList); +} + inline Action GetMemberAction(uint32_t access_flags) { - switch (HiddenApiAccessFlags::DecodeFromRuntime(access_flags)) { - case HiddenApiAccessFlags::kWhitelist: - return kAllow; - case HiddenApiAccessFlags::kLightGreylist: - return kAllowButWarn; - case HiddenApiAccessFlags::kDarkGreylist: - return kAllowButWarnAndToast; - case HiddenApiAccessFlags::kBlacklist: - return kDeny; + EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); + if (policy == EnforcementPolicy::kNoChecks) { + // Exit early. Nothing to enforce. + return kAllow; + } + + HiddenApiAccessFlags::ApiList api_list = HiddenApiAccessFlags::DecodeFromRuntime(access_flags); + if (api_list == HiddenApiAccessFlags::kWhitelist) { + return kAllow; + } + // The logic below relies on equality of values in the enums EnforcementPolicy and + // HiddenApiAccessFlags::ApiList, and their ordering. Assert that this is as expected. + static_assert( + EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) && + EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) && + EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist), + "Mismatch between EnforcementPolicy and ApiList enums"); + static_assert( + EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList && + EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly, + "EnforcementPolicy values ordering not correct"); + if (static_cast(policy) > static_cast(api_list)) { + return api_list == HiddenApiAccessFlags::kDarkGreylist + ? kAllowButWarnAndToast + : kAllowButWarn; + } else { + return kDeny; } } @@ -107,12 +146,6 @@ inline bool ShouldBlockAccessToMember(T* member, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); - Runtime* runtime = Runtime::Current(); - - if (!runtime->AreHiddenApiChecksEnabled()) { - // Exit early. Nothing to enforce. - return false; - } Action action = GetMemberAction(member->GetAccessFlags()); if (action == kAllow) { @@ -133,14 +166,16 @@ inline bool ShouldBlockAccessToMember(T* member, // We do this regardless of whether we block the access or not. WarnAboutMemberAccess(member, access_method); - // Block access if on blacklist. if (action == kDeny) { + // Block access return true; } // Allow access to this member but print a warning. DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast); + Runtime* runtime = Runtime::Current(); + // Depending on a runtime flag, we might move the member into whitelist and // skip the warning the next time the member is accessed. if (runtime->ShouldDedupeHiddenApiWarnings()) { @@ -150,7 +185,7 @@ inline bool ShouldBlockAccessToMember(T* member, // If this action requires a UI warning, set the appropriate flag. if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) { - Runtime::Current()->SetPendingHiddenApiWarning(true); + runtime->SetPendingHiddenApiWarning(true); } return false; diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index 89135698e3..cbc2aeb41f 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -162,19 +162,24 @@ static void CollectNonDebuggableClasses() REQUIRES(!Locks::mutator_lock_) { // Must match values in com.android.internal.os.Zygote. enum { - DEBUG_ENABLE_JDWP = 1, - DEBUG_ENABLE_CHECKJNI = 1 << 1, - DEBUG_ENABLE_ASSERT = 1 << 2, - DEBUG_ENABLE_SAFEMODE = 1 << 3, - DEBUG_ENABLE_JNI_LOGGING = 1 << 4, - DEBUG_GENERATE_DEBUG_INFO = 1 << 5, - DEBUG_ALWAYS_JIT = 1 << 6, - DEBUG_NATIVE_DEBUGGABLE = 1 << 7, - DEBUG_JAVA_DEBUGGABLE = 1 << 8, - DISABLE_VERIFIER = 1 << 9, - ONLY_USE_SYSTEM_OAT_FILES = 1 << 10, - ENABLE_HIDDEN_API_CHECKS = 1 << 11, - DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12, + DEBUG_ENABLE_JDWP = 1, + DEBUG_ENABLE_CHECKJNI = 1 << 1, + DEBUG_ENABLE_ASSERT = 1 << 2, + DEBUG_ENABLE_SAFEMODE = 1 << 3, + DEBUG_ENABLE_JNI_LOGGING = 1 << 4, + DEBUG_GENERATE_DEBUG_INFO = 1 << 5, + DEBUG_ALWAYS_JIT = 1 << 6, + DEBUG_NATIVE_DEBUGGABLE = 1 << 7, + DEBUG_JAVA_DEBUGGABLE = 1 << 8, + DISABLE_VERIFIER = 1 << 9, + ONLY_USE_SYSTEM_OAT_FILES = 1 << 10, + DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 11, + HIDDEN_API_ENFORCEMENT_POLICY_MASK = (1 << 12) + | (1 << 13), + + // bits to shift (flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) by to get a value + // corresponding to hiddenapi::EnforcementPolicy + API_ENFORCEMENT_POLICY_SHIFT = CTZ(HIDDEN_API_ENFORCEMENT_POLICY_MASK), }; static uint32_t EnableDebugFeatures(uint32_t runtime_flags) { @@ -285,7 +290,8 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, // Our system thread ID, etc, has changed so reset Thread state. thread->InitAfterFork(); runtime_flags = EnableDebugFeatures(runtime_flags); - bool do_hidden_api_checks = false; + hiddenapi::EnforcementPolicy api_enforcement_policy = hiddenapi::EnforcementPolicy::kNoChecks; + bool dedupe_hidden_api_warnings = true; if ((runtime_flags & DISABLE_VERIFIER) != 0) { Runtime::Current()->DisableVerifier(); @@ -297,10 +303,9 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, runtime_flags &= ~ONLY_USE_SYSTEM_OAT_FILES; } - if ((runtime_flags & ENABLE_HIDDEN_API_CHECKS) != 0) { - do_hidden_api_checks = true; - runtime_flags &= ~ENABLE_HIDDEN_API_CHECKS; - } + api_enforcement_policy = hiddenapi::EnforcementPolicyFromInt( + (runtime_flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT); + runtime_flags &= ~HIDDEN_API_ENFORCEMENT_POLICY_MASK; if (runtime_flags != 0) { LOG(ERROR) << StringPrintf("Unknown bits set in runtime_flags: %#x", runtime_flags); @@ -351,11 +356,13 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, } } + bool do_hidden_api_checks = api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks; DCHECK(!(is_system_server && do_hidden_api_checks)) - << "SystemServer should be forked with ENABLE_HIDDEN_API_CHECKS"; + << "SystemServer should be forked with EnforcementPolicy::kDisable"; DCHECK(!(is_zygote && do_hidden_api_checks)) - << "Child zygote processes should be forked with ENABLE_HIDDEN_API_CHECKS"; - Runtime::Current()->SetHiddenApiChecksEnabled(do_hidden_api_checks); + << "Child zygote processes should be forked with EnforcementPolicy::kDisable"; + Runtime::Current()->SetHiddenApiEnforcementPolicy(api_enforcement_policy); + Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe_hidden_api_warnings); // Clear the hidden API warning flag, in case it was set. Runtime::Current()->SetPendingHiddenApiWarning(false); diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 25d50376de..fc61c9597e 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -89,8 +89,8 @@ static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator // access hidden APIs. This can be *very* expensive. Never call this in a loop. ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - return Runtime::Current()->AreHiddenApiChecksEnabled() && - !IsCallerInBootClassPath(self); + hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); + return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInBootClassPath(self); } // Returns true if the first non-ClassClass caller up the stack should not be diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 7d9d3426fc..53982ae833 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -267,7 +267,7 @@ Runtime::Runtime() oat_file_manager_(nullptr), is_low_memory_mode_(false), safe_mode_(false), - do_hidden_api_checks_(false), + hidden_api_policy_(hiddenapi::EnforcementPolicy::kNoChecks), pending_hidden_api_warning_(false), dedupe_hidden_api_warnings_(true), always_set_hidden_api_warning_flag_(false), @@ -1196,9 +1196,14 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // by default and we only enable them if: // (a) runtime was started with a flag that enables the checks, or // (b) Zygote forked a new process that is not exempt (see ZygoteHooks). - do_hidden_api_checks_ = runtime_options.Exists(Opt::HiddenApiChecks); - DCHECK(!is_zygote_ || !do_hidden_api_checks_) - << "Zygote should not be started with hidden API checks"; + bool do_hidden_api_checks = runtime_options.Exists(Opt::HiddenApiChecks); + DCHECK(!is_zygote_ || !do_hidden_api_checks); + // TODO pass the actual enforcement policy in, rather than just a single bit. + // As is, we're encoding some logic here about which specific policy to use, which would be better + // controlled by the framework. + hidden_api_policy_ = do_hidden_api_checks + ? hiddenapi::EnforcementPolicy::kBlacklistOnly + : hiddenapi::EnforcementPolicy::kNoChecks; no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain); force_native_bridge_ = runtime_options.Exists(Opt::ForceNativeBridge); diff --git a/runtime/runtime.h b/runtime/runtime.h index c7f650ea3f..dba31b2939 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -49,6 +49,10 @@ class AbstractSystemWeakHolder; class Heap; } // namespace gc +namespace hiddenapi { +enum class EnforcementPolicy; +} // namespace hiddenapi + namespace jit { class Jit; class JitOptions; @@ -520,12 +524,12 @@ class Runtime { bool IsVerificationEnabled() const; bool IsVerificationSoftFail() const; - void SetHiddenApiChecksEnabled(bool value) { - do_hidden_api_checks_ = value; + void SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy policy) { + hidden_api_policy_ = policy; } - bool AreHiddenApiChecksEnabled() const { - return do_hidden_api_checks_; + hiddenapi::EnforcementPolicy GetHiddenApiEnforcementPolicy() const { + return hidden_api_policy_; } void SetPendingHiddenApiWarning(bool value) { @@ -990,7 +994,7 @@ class Runtime { bool safe_mode_; // Whether access checks on hidden API should be performed. - bool do_hidden_api_checks_; + hiddenapi::EnforcementPolicy hidden_api_policy_; // Whether the application has used an API which is not restricted but we // should issue a warning about it. diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 67ea64be74..bf36ccf0fa 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -24,6 +24,7 @@ #include #include "entrypoints/quick/quick_entrypoints_enum.h" +#include "hidden_api.h" #include "jni_internal.h" #include "mirror/class.h" #include "mirror/throwable.h" @@ -287,17 +288,17 @@ class ScopedHiddenApiExemption { public: explicit ScopedHiddenApiExemption(Runtime* runtime) : runtime_(runtime), - initially_enabled_(runtime_->AreHiddenApiChecksEnabled()) { - runtime_->SetHiddenApiChecksEnabled(false); + initial_policy_(runtime_->GetHiddenApiEnforcementPolicy()) { + runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks); } ~ScopedHiddenApiExemption() { - runtime_->SetHiddenApiChecksEnabled(initially_enabled_); + runtime_->SetHiddenApiEnforcementPolicy(initial_policy_); } private: Runtime* runtime_; - const bool initially_enabled_; + const hiddenapi::EnforcementPolicy initial_policy_; DISALLOW_COPY_AND_ASSIGN(ScopedHiddenApiExemption); }; diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc index effa37ade4..04c3fbf03a 100644 --- a/test/674-hiddenapi/hiddenapi.cc +++ b/test/674-hiddenapi/hiddenapi.cc @@ -16,6 +16,7 @@ #include "class_linker.h" #include "dex/art_dex_file_loader.h" +#include "hidden_api.h" #include "jni.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" @@ -27,7 +28,7 @@ namespace Test674HiddenApi { extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) { Runtime* runtime = Runtime::Current(); - runtime->SetHiddenApiChecksEnabled(true); + runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly); runtime->SetDedupeHiddenApiWarnings(false); runtime->AlwaysSetHiddenApiWarningFlag(); } -- GitLab From 3f41323cc9da335e9aa4f3fbad90a86caa82ee4d Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 12 Feb 2018 18:39:15 +0000 Subject: [PATCH 118/749] Revert^2 "Compiler changes for bitstring based type checks." Add extra output for debugging failures and re-enable the bitstring type checks. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing --jit Test: testrunner.py --host -t 670-bitstring-type-check Test: Pixel 2 XL boots. Test: testrunner.py --target --optimizing --jit Test: testrunner.py --target -t 670-bitstring-type-check Bug: 64692057 Bug: 26687569 This reverts commit bff7a52e2c6c9e988c3ed1f12a2da0fa5fd37cfb. Change-Id: I090e241983f3ac6ed8394d842e17716087d169ac --- compiler/driver/compiler_driver.cc | 115 +++++++++- compiler/optimizing/code_generator.h | 2 + compiler/optimizing/code_generator_arm64.cc | 77 ++++++- compiler/optimizing/code_generator_arm64.h | 2 + .../optimizing/code_generator_arm_vixl.cc | 121 +++++++++- compiler/optimizing/code_generator_arm_vixl.h | 3 + compiler/optimizing/code_generator_mips.cc | 98 ++++++-- compiler/optimizing/code_generator_mips.h | 1 + compiler/optimizing/code_generator_mips64.cc | 98 ++++++-- compiler/optimizing/code_generator_mips64.h | 1 + compiler/optimizing/code_generator_x86.cc | 62 ++++- compiler/optimizing/code_generator_x86.h | 1 + compiler/optimizing/code_generator_x86_64.cc | 71 +++++- compiler/optimizing/code_generator_x86_64.h | 1 + compiler/optimizing/graph_checker.cc | 92 ++++++-- compiler/optimizing/graph_checker.h | 6 + compiler/optimizing/graph_visualizer.cc | 45 ++-- compiler/optimizing/instruction_builder.cc | 107 +++++---- compiler/optimizing/instruction_builder.h | 7 + compiler/optimizing/instruction_simplifier.cc | 43 ++-- compiler/optimizing/nodes.cc | 2 + compiler/optimizing/nodes.h | 216 ++++++++++++------ .../optimizing/optimizing_compiler_stats.h | 1 + .../prepare_for_register_allocation.cc | 14 ++ .../prepare_for_register_allocation.h | 2 + .../optimizing/reference_type_propagation.cc | 35 ++- compiler/optimizing/sharpening.cc | 69 ++++++ compiler/optimizing/sharpening.h | 27 ++- runtime/arch/arm/quick_entrypoints_arm.S | 4 + runtime/arch/arm64/quick_entrypoints_arm64.S | 4 + runtime/arch/mips/quick_entrypoints_mips.S | 7 + .../arch/mips64/quick_entrypoints_mips64.S | 6 + runtime/arch/x86/quick_entrypoints_x86.S | 5 + .../arch/x86_64/quick_entrypoints_x86_64.S | 5 + runtime/base/arena_allocator.cc | 1 + runtime/base/arena_allocator.h | 1 + .../quick/quick_throw_entrypoints.cc | 23 ++ runtime/subtype_check.h | 23 +- 38 files changed, 1158 insertions(+), 240 deletions(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index bd3a145368..6836f7594b 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -781,7 +781,8 @@ void CompilerDriver::Resolve(jobject class_loader, // TODO: Collect the relevant string indices in parallel, then allocate them sequentially in a // stable order. -static void ResolveConstStrings(Handle dex_cache, +static void ResolveConstStrings(ClassLinker* class_linker, + Handle dex_cache, const DexFile& dex_file, const DexFile::CodeItem* code_item) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -790,7 +791,6 @@ static void ResolveConstStrings(Handle dex_cache, return; } - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) { switch (inst->Opcode()) { case Instruction::CONST_STRING: @@ -838,22 +838,105 @@ static void ResolveConstStrings(CompilerDriver* driver, dex_file->StringByTypeIdx(class_def.class_idx_)); if (!compilation_enabled) { // Compilation is skipped, do not resolve const-string in code of this class. - // TODO: Make sure that inlining honors this. + // FIXME: Make sure that inlining honors this. b/26687569 continue; } // Direct and virtual methods. - int64_t previous_method_idx = -1; while (it.HasNextMethod()) { - uint32_t method_idx = it.GetMemberIndex(); - if (method_idx == previous_method_idx) { - // smali can create dex files with two encoded_methods sharing the same method_idx - // http://code.google.com/p/smali/issues/detail?id=119 - it.Next(); - continue; + ResolveConstStrings(class_linker, dex_cache, *dex_file, it.GetMethodCodeItem()); + it.Next(); + } + DCHECK(!it.HasNext()); + } + } +} + +// Initialize type check bit strings for check-cast and instance-of in the code. Done to have +// deterministic allocation behavior. Right now this is single-threaded for simplicity. +// TODO: Collect the relevant type indices in parallel, then process them sequentially in a +// stable order. + +static void InitializeTypeCheckBitstrings(CompilerDriver* driver, + ClassLinker* class_linker, + Handle dex_cache, + const DexFile& dex_file, + const DexFile::CodeItem* code_item) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (code_item == nullptr) { + // Abstract or native method. + return; + } + + for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) { + switch (inst->Opcode()) { + case Instruction::CHECK_CAST: + case Instruction::INSTANCE_OF: { + dex::TypeIndex type_index( + (inst->Opcode() == Instruction::CHECK_CAST) ? inst->VRegB_21c() : inst->VRegC_22c()); + const char* descriptor = dex_file.StringByTypeIdx(type_index); + // We currently do not use the bitstring type check for array or final (including + // primitive) classes. We may reconsider this in future if it's deemed to be beneficial. + // And we cannot use it for classes outside the boot image as we do not know the runtime + // value of their bitstring when compiling (it may not even get assigned at runtime). + if (descriptor[0] == 'L' && driver->IsImageClass(descriptor)) { + ObjPtr klass = + class_linker->LookupResolvedType(type_index, + dex_cache.Get(), + /* class_loader */ nullptr); + CHECK(klass != nullptr) << descriptor << " should have been previously resolved."; + // Now assign the bitstring if the class is not final. Keep this in sync with sharpening. + if (!klass->IsFinal()) { + MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); + SubtypeCheck>::EnsureAssigned(klass); + } } - previous_method_idx = method_idx; - ResolveConstStrings(dex_cache, *dex_file, it.GetMethodCodeItem()); + break; + } + + default: + break; + } + } +} + +static void InitializeTypeCheckBitstrings(CompilerDriver* driver, + const std::vector& dex_files, + TimingLogger* timings) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + MutableHandle dex_cache(hs.NewHandle(nullptr)); + + for (const DexFile* dex_file : dex_files) { + dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file)); + TimingLogger::ScopedTiming t("Initialize type check bitstrings", timings); + + size_t class_def_count = dex_file->NumClassDefs(); + for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) { + const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); + + const uint8_t* class_data = dex_file->GetClassData(class_def); + if (class_data == nullptr) { + // empty class, probably a marker interface + continue; + } + + ClassDataItemIterator it(*dex_file, class_data); + it.SkipAllFields(); + + bool compilation_enabled = driver->IsClassToCompile( + dex_file->StringByTypeIdx(class_def.class_idx_)); + if (!compilation_enabled) { + // Compilation is skipped, do not look for type checks in code of this class. + // FIXME: Make sure that inlining honors this. b/26687569 + continue; + } + + // Direct and virtual methods. + while (it.HasNextMethod()) { + InitializeTypeCheckBitstrings( + driver, class_linker, dex_cache, *dex_file, it.GetMethodCodeItem()); it.Next(); } DCHECK(!it.HasNext()); @@ -955,6 +1038,14 @@ void CompilerDriver::PreCompile(jobject class_loader, UpdateImageClasses(timings); VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false); + + if (kBitstringSubtypeCheckEnabled && + GetCompilerOptions().IsForceDeterminism() && GetCompilerOptions().IsBootImage()) { + // Initialize type check bit string used by check-cast and instanceof. + // Do this now to have a deterministic image. + // Note: This is done after UpdateImageClasses() at it relies on the image classes to be final. + InitializeTypeCheckBitstrings(this, dex_files, timings); + } } bool CompilerDriver::IsImageClass(const char* descriptor) const { diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index a4873202b2..3bd5e14539 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -438,6 +438,8 @@ class CodeGenerator : public DeletableArenaObject { case TypeCheckKind::kArrayCheck: case TypeCheckKind::kUnresolvedCheck: return false; + case TypeCheckKind::kBitstringCheck: + return true; } LOG(FATAL) << "Unreachable"; UNREACHABLE(); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index a024df8537..273346ab4a 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -2129,6 +2129,26 @@ void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCod __ Bind(slow_path->GetExitLabel()); } +void InstructionCodeGeneratorARM64::GenerateBitstringTypeCheckCompare( + HTypeCheckInstruction* check, vixl::aarch64::Register temp) { + uint32_t path_to_root = check->GetBitstringPathToRoot(); + uint32_t mask = check->GetBitstringMask(); + DCHECK(IsPowerOfTwo(mask + 1)); + size_t mask_bits = WhichPowerOf2(mask + 1); + + if (mask_bits == 16u) { + // Load only the bitstring part of the status word. + __ Ldrh(temp, HeapOperand(temp, mirror::Class::StatusOffset())); + } else { + // /* uint32_t */ temp = temp->status_ + __ Ldr(temp, HeapOperand(temp, mirror::Class::StatusOffset())); + // Extract the bitstring bits. + __ Ubfx(temp, temp, 0, mask_bits); + } + // Compare the bitstring bits to `path_to_root`. + __ Cmp(temp, path_to_root); +} + void CodeGeneratorARM64::GenerateMemoryBarrier(MemBarrierKind kind) { BarrierType type = BarrierAll; @@ -3866,6 +3886,8 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; + case TypeCheckKind::kBitstringCheck: + break; } LocationSummary* locations = @@ -3874,7 +3896,13 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } // The "out" register is used as a temporary, so it overlaps with the inputs. // Note that TypeCheckSlowPathARM64 uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -3887,7 +3915,9 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = InputRegisterAt(instruction, 0); - Register cls = InputRegisterAt(instruction, 1); + Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) + ? Register() + : InputRegisterAt(instruction, 1); Location out_loc = locations->Out(); Register out = OutputRegister(instruction); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -4073,6 +4103,23 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { } break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, out); + __ Cset(out, eq); + if (zero.IsLinked()) { + __ B(&done); + } + break; + } } if (zero.IsLinked()) { @@ -4095,7 +4142,13 @@ void LocationsBuilderARM64::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } // Add temps for read barriers and other uses. One is used by TypeCheckSlowPathARM64. locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -4105,7 +4158,9 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = InputRegisterAt(instruction, 0); - Register cls = InputRegisterAt(instruction, 1); + Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) + ? Register() + : InputRegisterAt(instruction, 1); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); DCHECK_GE(num_temps, 1u); DCHECK_LE(num_temps, 3u); @@ -4286,6 +4341,20 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { __ B(ne, &start_loop); break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + maybe_temp2_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, temp); + __ B(ne, type_check_slow_path->GetEntryLabel()); + break; + } } __ Bind(&done); diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index a8a9802f9a..6a52eecbd3 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -264,6 +264,8 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { private: void GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path, vixl::aarch64::Register class_reg); + void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, + vixl::aarch64::Register temp); void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor); void HandleBinaryOp(HBinaryOperation* instr); diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 6ebcc67b49..b38a006305 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -7523,6 +7523,67 @@ void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck( __ Bind(slow_path->GetExitLabel()); } +void InstructionCodeGeneratorARMVIXL::GenerateBitstringTypeCheckCompare( + HTypeCheckInstruction* check, + vixl32::Register temp, + vixl32::FlagsUpdate flags_update) { + uint32_t path_to_root = check->GetBitstringPathToRoot(); + uint32_t mask = check->GetBitstringMask(); + DCHECK(IsPowerOfTwo(mask + 1)); + size_t mask_bits = WhichPowerOf2(mask + 1); + + // Note that HInstanceOf shall check for zero value in `temp` but HCheckCast needs + // the Z flag for BNE. This is indicated by the `flags_update` parameter. + if (mask_bits == 16u) { + // Load only the bitstring part of the status word. + __ Ldrh(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value())); + // Check if the bitstring bits are equal to `path_to_root`. + if (flags_update == SetFlags) { + __ Cmp(temp, path_to_root); + } else { + __ Sub(temp, temp, path_to_root); + } + } else { + // /* uint32_t */ temp = temp->status_ + __ Ldr(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value())); + if (GetAssembler()->ShifterOperandCanHold(SUB, path_to_root)) { + // Compare the bitstring bits using SUB. + __ Sub(temp, temp, path_to_root); + // Shift out bits that do not contribute to the comparison. + __ Lsl(flags_update, temp, temp, dchecked_integral_cast(32u - mask_bits)); + } else if (IsUint<16>(path_to_root)) { + if (temp.IsLow()) { + // Note: Optimized for size but contains one more dependent instruction than necessary. + // MOVW+SUB(register) would be 8 bytes unless we find a low-reg temporary but the + // macro assembler would use the high reg IP for the constant by default. + // Compare the bitstring bits using SUB. + __ Sub(temp, temp, path_to_root & 0x00ffu); // 16-bit SUB (immediate) T2 + __ Sub(temp, temp, path_to_root & 0xff00u); // 32-bit SUB (immediate) T3 + // Shift out bits that do not contribute to the comparison. + __ Lsl(flags_update, temp, temp, dchecked_integral_cast(32u - mask_bits)); + } else { + // Extract the bitstring bits. + __ Ubfx(temp, temp, 0, mask_bits); + // Check if the bitstring bits are equal to `path_to_root`. + if (flags_update == SetFlags) { + __ Cmp(temp, path_to_root); + } else { + __ Sub(temp, temp, path_to_root); + } + } + } else { + // Shift out bits that do not contribute to the comparison. + __ Lsl(temp, temp, dchecked_integral_cast(32u - mask_bits)); + // Check if the shifted bitstring bits are equal to `path_to_root << (32u - mask_bits)`. + if (flags_update == SetFlags) { + __ Cmp(temp, path_to_root << (32u - mask_bits)); + } else { + __ Sub(temp, temp, path_to_root << (32u - mask_bits)); + } + } + } +} + HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { switch (desired_string_load_kind) { @@ -7714,6 +7775,8 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; + case TypeCheckKind::kBitstringCheck: + break; } LocationSummary* locations = @@ -7722,7 +7785,13 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } // The "out" register is used as a temporary, so it overlaps with the inputs. // Note that TypeCheckSlowPathARM uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -7737,7 +7806,9 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); vixl32::Register obj = InputRegisterAt(instruction, 0); - vixl32::Register cls = InputRegisterAt(instruction, 1); + vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) + ? vixl32::Register() + : InputRegisterAt(instruction, 1); Location out_loc = locations->Out(); vixl32::Register out = OutputRegister(instruction); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -7977,6 +8048,26 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) __ B(slow_path->GetEntryLabel()); break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, out, DontCare); + // If `out` is a low reg and we would have another low reg temp, we could + // optimize this as RSBS+ADC, see GenerateConditionWithZero(). + // + // Also, in some cases when `out` is a low reg and we're loading a constant to IP + // it would make sense to use CMP+MOV+IT+MOV instead of SUB+CLZ+LSR as the code size + // would be the same and we would have fewer direct data dependencies. + codegen_->GenerateConditionWithZero(kCondEQ, out, out); // CLZ+LSR + break; + } } if (done.IsReferenced()) { @@ -7994,7 +8085,13 @@ void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -8003,7 +8100,9 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); vixl32::Register obj = InputRegisterAt(instruction, 0); - vixl32::Register cls = InputRegisterAt(instruction, 1); + vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) + ? vixl32::Register() + : InputRegisterAt(instruction, 1); Location temp_loc = locations->GetTemp(0); vixl32::Register temp = RegisterFrom(temp_loc); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); @@ -8188,6 +8287,20 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { __ B(ne, &start_loop, /* far_target */ false); break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + maybe_temp2_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, temp, SetFlags); + __ B(ne, type_check_slow_path->GetEntryLabel()); + break; + } } if (done.IsReferenced()) { __ Bind(&done); diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 6a07e36022..2114ea1ba1 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -322,6 +322,9 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor); void GenerateClassInitializationCheck(LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg); + void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, + vixl::aarch32::Register temp, + vixl::aarch32::FlagsUpdate flags_update); void GenerateAndConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value); void GenerateOrrConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value); void GenerateEorConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value); diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index be9ff48a6b..25e2eddbfa 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -1950,6 +1950,34 @@ void InstructionCodeGeneratorMIPS::GenerateClassInitializationCheck(SlowPathCode __ Bind(slow_path->GetExitLabel()); } +void InstructionCodeGeneratorMIPS::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, + Register temp) { + uint32_t path_to_root = check->GetBitstringPathToRoot(); + uint32_t mask = check->GetBitstringMask(); + DCHECK(IsPowerOfTwo(mask + 1)); + size_t mask_bits = WhichPowerOf2(mask + 1); + + if (mask_bits == 16u) { + // Load only the bitstring part of the status word. + __ LoadFromOffset( + kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value()); + // Compare the bitstring bits using XOR. + __ Xori(temp, temp, dchecked_integral_cast(path_to_root)); + } else { + // /* uint32_t */ temp = temp->status_ + __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value()); + // Compare the bitstring bits using XOR. + if (IsUint<16>(path_to_root)) { + __ Xori(temp, temp, dchecked_integral_cast(path_to_root)); + } else { + __ LoadConst32(TMP, path_to_root); + __ Xor(temp, temp, TMP); + } + // Shift out bits that do not contribute to the comparison. + __ Sll(temp, temp, 32 - mask_bits); + } +} + void InstructionCodeGeneratorMIPS::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) { __ Sync(0); // Only stype 0 is supported. } @@ -3301,7 +3329,13 @@ void LocationsBuilderMIPS::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -3310,7 +3344,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = obj_loc.AsRegister(); - Register cls = locations->InAt(1).AsRegister(); + Location cls = locations->InAt(1); Location temp_loc = locations->GetTemp(0); Register temp = temp_loc.AsRegister(); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); @@ -3349,7 +3383,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { kWithoutReadBarrier); // Jump to slow path for throwing the exception or doing a // more involved array check. - __ Bne(temp, cls, slow_path->GetEntryLabel()); + __ Bne(temp, cls.AsRegister(), slow_path->GetEntryLabel()); break; } @@ -3375,7 +3409,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { // exception. __ Beqz(temp, slow_path->GetEntryLabel()); // Otherwise, compare the classes. - __ Bne(temp, cls, &loop); + __ Bne(temp, cls.AsRegister(), &loop); break; } @@ -3390,7 +3424,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { // Walk over the class hierarchy to find a match. MipsLabel loop; __ Bind(&loop); - __ Beq(temp, cls, &done); + __ Beq(temp, cls.AsRegister(), &done); // /* HeapReference */ temp = temp->super_class_ GenerateReferenceLoadOneRegister(instruction, temp_loc, @@ -3413,7 +3447,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { maybe_temp2_loc, kWithoutReadBarrier); // Do an exact check. - __ Beq(temp, cls, &done); + __ Beq(temp, cls.AsRegister(), &done); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference */ temp = temp->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -3472,7 +3506,21 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { // Go to next interface. __ Addiu(TMP, TMP, -2); // Compare the classes and continue the loop if they do not match. - __ Bne(AT, cls, &loop); + __ Bne(AT, cls.AsRegister(), &loop); + break; + } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + maybe_temp2_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, temp); + __ Bnez(temp, slow_path->GetEntryLabel()); break; } } @@ -7415,6 +7463,8 @@ void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; + case TypeCheckKind::kBitstringCheck: + break; } LocationSummary* locations = @@ -7423,7 +7473,13 @@ void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } // The output does overlap inputs. // Note that TypeCheckSlowPathMIPS uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -7435,7 +7491,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = obj_loc.AsRegister(); - Register cls = locations->InAt(1).AsRegister(); + Location cls = locations->InAt(1); Location out_loc = locations->Out(); Register out = out_loc.AsRegister(); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -7467,7 +7523,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { maybe_temp_loc, read_barrier_option); // Classes must be equal for the instanceof to succeed. - __ Xor(out, out, cls); + __ Xor(out, out, cls.AsRegister()); __ Sltiu(out, out, 1); break; } @@ -7494,7 +7550,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { read_barrier_option); // If `out` is null, we use it for the result, and jump to `done`. __ Beqz(out, &done); - __ Bne(out, cls, &loop); + __ Bne(out, cls.AsRegister(), &loop); __ LoadConst32(out, 1); break; } @@ -7512,7 +7568,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { // Walk over the class hierarchy to find a match. MipsLabel loop, success; __ Bind(&loop); - __ Beq(out, cls, &success); + __ Beq(out, cls.AsRegister(), &success); // /* HeapReference */ out = out->super_class_ GenerateReferenceLoadOneRegister(instruction, out_loc, @@ -7539,7 +7595,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { read_barrier_option); // Do an exact check. MipsLabel success; - __ Beq(out, cls, &success); + __ Beq(out, cls.AsRegister(), &success); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference */ out = out->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -7571,7 +7627,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS( instruction, /* is_fatal */ false); codegen_->AddSlowPath(slow_path); - __ Bne(out, cls, slow_path->GetEntryLabel()); + __ Bne(out, cls.AsRegister(), slow_path->GetEntryLabel()); __ LoadConst32(out, 1); break; } @@ -7603,6 +7659,20 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { __ B(slow_path->GetEntryLabel()); break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, out); + __ Sltiu(out, out, 1); + break; + } } __ Bind(&done); diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 1f1743ff9e..2e7c736dbd 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -237,6 +237,7 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator { private: void GenerateClassInitializationCheck(SlowPathCodeMIPS* slow_path, Register class_reg); void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); + void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, Register temp); void HandleBinaryOp(HBinaryOperation* operation); void HandleCondition(HCondition* instruction); void HandleShift(HBinaryOperation* operation); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index f8851b4eea..5b07b55cbb 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1794,6 +1794,34 @@ void InstructionCodeGeneratorMIPS64::GenerateClassInitializationCheck(SlowPathCo __ Bind(slow_path->GetExitLabel()); } +void InstructionCodeGeneratorMIPS64::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, + GpuRegister temp) { + uint32_t path_to_root = check->GetBitstringPathToRoot(); + uint32_t mask = check->GetBitstringMask(); + DCHECK(IsPowerOfTwo(mask + 1)); + size_t mask_bits = WhichPowerOf2(mask + 1); + + if (mask_bits == 16u) { + // Load only the bitstring part of the status word. + __ LoadFromOffset( + kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value()); + // Compare the bitstring bits using XOR. + __ Xori(temp, temp, dchecked_integral_cast(path_to_root)); + } else { + // /* uint32_t */ temp = temp->status_ + __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value()); + // Compare the bitstring bits using XOR. + if (IsUint<16>(path_to_root)) { + __ Xori(temp, temp, dchecked_integral_cast(path_to_root)); + } else { + __ LoadConst32(TMP, path_to_root); + __ Xor(temp, temp, TMP); + } + // Shift out bits that do not contribute to the comparison. + __ Sll(temp, temp, 32 - mask_bits); + } +} + void InstructionCodeGeneratorMIPS64::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) { __ Sync(0); // only stype 0 is supported } @@ -2854,7 +2882,13 @@ void LocationsBuilderMIPS64::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -2863,7 +2897,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); GpuRegister obj = obj_loc.AsRegister(); - GpuRegister cls = locations->InAt(1).AsRegister(); + Location cls = locations->InAt(1); Location temp_loc = locations->GetTemp(0); GpuRegister temp = temp_loc.AsRegister(); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); @@ -2902,7 +2936,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { kWithoutReadBarrier); // Jump to slow path for throwing the exception or doing a // more involved array check. - __ Bnec(temp, cls, slow_path->GetEntryLabel()); + __ Bnec(temp, cls.AsRegister(), slow_path->GetEntryLabel()); break; } @@ -2928,7 +2962,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { // exception. __ Beqzc(temp, slow_path->GetEntryLabel()); // Otherwise, compare the classes. - __ Bnec(temp, cls, &loop); + __ Bnec(temp, cls.AsRegister(), &loop); break; } @@ -2943,7 +2977,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { // Walk over the class hierarchy to find a match. Mips64Label loop; __ Bind(&loop); - __ Beqc(temp, cls, &done); + __ Beqc(temp, cls.AsRegister(), &done); // /* HeapReference */ temp = temp->super_class_ GenerateReferenceLoadOneRegister(instruction, temp_loc, @@ -2966,7 +3000,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { maybe_temp2_loc, kWithoutReadBarrier); // Do an exact check. - __ Beqc(temp, cls, &done); + __ Beqc(temp, cls.AsRegister(), &done); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference */ temp = temp->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -3025,7 +3059,21 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { __ Daddiu(temp, temp, 2 * kHeapReferenceSize); __ Addiu(TMP, TMP, -2); // Compare the classes and continue the loop if they do not match. - __ Bnec(AT, cls, &loop); + __ Bnec(AT, cls.AsRegister(), &loop); + break; + } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + maybe_temp2_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, temp); + __ Bnezc(temp, slow_path->GetEntryLabel()); break; } } @@ -5529,6 +5577,8 @@ void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; + case TypeCheckKind::kBitstringCheck: + break; } LocationSummary* locations = @@ -5537,7 +5587,13 @@ void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } // The output does overlap inputs. // Note that TypeCheckSlowPathMIPS64 uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -5549,7 +5605,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); GpuRegister obj = obj_loc.AsRegister(); - GpuRegister cls = locations->InAt(1).AsRegister(); + Location cls = locations->InAt(1); Location out_loc = locations->Out(); GpuRegister out = out_loc.AsRegister(); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -5581,7 +5637,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { maybe_temp_loc, read_barrier_option); // Classes must be equal for the instanceof to succeed. - __ Xor(out, out, cls); + __ Xor(out, out, cls.AsRegister()); __ Sltiu(out, out, 1); break; } @@ -5608,7 +5664,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { read_barrier_option); // If `out` is null, we use it for the result, and jump to `done`. __ Beqzc(out, &done); - __ Bnec(out, cls, &loop); + __ Bnec(out, cls.AsRegister(), &loop); __ LoadConst32(out, 1); break; } @@ -5626,7 +5682,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { // Walk over the class hierarchy to find a match. Mips64Label loop, success; __ Bind(&loop); - __ Beqc(out, cls, &success); + __ Beqc(out, cls.AsRegister(), &success); // /* HeapReference */ out = out->super_class_ GenerateReferenceLoadOneRegister(instruction, out_loc, @@ -5653,7 +5709,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { read_barrier_option); // Do an exact check. Mips64Label success; - __ Beqc(out, cls, &success); + __ Beqc(out, cls.AsRegister(), &success); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference */ out = out->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -5685,7 +5741,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS64( instruction, /* is_fatal */ false); codegen_->AddSlowPath(slow_path); - __ Bnec(out, cls, slow_path->GetEntryLabel()); + __ Bnec(out, cls.AsRegister(), slow_path->GetEntryLabel()); __ LoadConst32(out, 1); break; } @@ -5717,6 +5773,20 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { __ Bc(slow_path->GetEntryLabel()); break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, out); + __ Sltiu(out, out, 1); + break; + } } __ Bind(&done); diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 74c947e5d5..6e69e4611a 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -233,6 +233,7 @@ class InstructionCodeGeneratorMIPS64 : public InstructionCodeGenerator { private: void GenerateClassInitializationCheck(SlowPathCodeMIPS64* slow_path, GpuRegister class_reg); + void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, GpuRegister temp); void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); void HandleBinaryOp(HBinaryOperation* operation); void HandleCondition(HCondition* instruction); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 4818084a72..4053f557d9 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -6571,6 +6571,26 @@ void InstructionCodeGeneratorX86::GenerateClassInitializationCheck( // No need for memory fence, thanks to the X86 memory model. } +void InstructionCodeGeneratorX86::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, + Register temp) { + uint32_t path_to_root = check->GetBitstringPathToRoot(); + uint32_t mask = check->GetBitstringMask(); + DCHECK(IsPowerOfTwo(mask + 1)); + size_t mask_bits = WhichPowerOf2(mask + 1); + + if (mask_bits == 16u) { + // Compare the bitstring in memory. + __ cmpw(Address(temp, mirror::Class::StatusOffset()), Immediate(path_to_root)); + } else { + // /* uint32_t */ temp = temp->status_ + __ movl(temp, Address(temp, mirror::Class::StatusOffset())); + // Compare the bitstring bits using SUB. + __ subl(temp, Immediate(path_to_root)); + // Shift out bits that do not contribute to the comparison. + __ shll(temp, Immediate(32u - mask_bits)); + } +} + HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { switch (desired_string_load_kind) { @@ -6764,6 +6784,8 @@ void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; + case TypeCheckKind::kBitstringCheck: + break; } LocationSummary* locations = @@ -6772,7 +6794,13 @@ void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::Any()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::Any()); + } // Note that TypeCheckSlowPathX86 uses this "out" register too. locations->SetOut(Location::RequiresRegister()); // When read barriers are enabled, we need a temporary register for some cases. @@ -6993,6 +7021,21 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { } break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, out); + __ j(kNotEqual, &zero); + __ movl(out, Immediate(1)); + __ jmp(&done); + break; + } } if (zero.IsLinked()) { @@ -7019,6 +7062,10 @@ void LocationsBuilderX86::VisitCheckCast(HCheckCast* instruction) { // Require a register for the interface check since there is a loop that compares the class to // a memory address. locations->SetInAt(1, Location::RequiresRegister()); + } else if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); } else { locations->SetInAt(1, Location::Any()); } @@ -7238,6 +7285,19 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { __ MaybeUnpoisonHeapReference(cls.AsRegister()); break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, temp); + __ j(kNotEqual, type_check_slow_path->GetEntryLabel()); + break; + } } __ Bind(&done); diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 9c537a7371..6c76e27d35 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -211,6 +211,7 @@ class InstructionCodeGeneratorX86 : public InstructionCodeGenerator { // the suspend call. void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); void GenerateClassInitializationCheck(SlowPathCode* slow_path, Register class_reg); + void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, Register temp); void HandleBitwiseOperation(HBinaryOperation* instruction); void GenerateDivRemIntegral(HBinaryOperation* instruction); void DivRemOneOrMinusOne(HBinaryOperation* instruction); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index c378c5b957..496d79d6c8 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -5716,6 +5716,26 @@ void InstructionCodeGeneratorX86_64::GenerateClassInitializationCheck( // No need for memory fence, thanks to the x86-64 memory model. } +void InstructionCodeGeneratorX86_64::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, + CpuRegister temp) { + uint32_t path_to_root = check->GetBitstringPathToRoot(); + uint32_t mask = check->GetBitstringMask(); + DCHECK(IsPowerOfTwo(mask + 1)); + size_t mask_bits = WhichPowerOf2(mask + 1); + + if (mask_bits == 16u) { + // Compare the bitstring in memory. + __ cmpw(Address(temp, mirror::Class::StatusOffset()), Immediate(path_to_root)); + } else { + // /* uint32_t */ temp = temp->status_ + __ movl(temp, Address(temp, mirror::Class::StatusOffset())); + // Compare the bitstring bits using SUB. + __ subl(temp, Immediate(path_to_root)); + // Shift out bits that do not contribute to the comparison. + __ shll(temp, Immediate(32u - mask_bits)); + } +} + HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind( HLoadClass::LoadKind desired_class_load_kind) { switch (desired_class_load_kind) { @@ -6082,6 +6102,8 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; + case TypeCheckKind::kBitstringCheck: + break; } LocationSummary* locations = @@ -6090,7 +6112,13 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::Any()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::Any()); + } // Note that TypeCheckSlowPathX86_64 uses this "out" register too. locations->SetOut(Location::RequiresRegister()); // When read barriers are enabled, we need a temporary register for @@ -6319,6 +6347,27 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, out); + if (zero.IsLinked()) { + __ j(kNotEqual, &zero); + __ movl(out, Immediate(1)); + __ jmp(&done); + } else { + __ setcc(kEqual, out); + // setcc only sets the low byte. + __ andl(out, Immediate(1)); + } + break; + } } if (zero.IsLinked()) { @@ -6345,6 +6394,10 @@ void LocationsBuilderX86_64::VisitCheckCast(HCheckCast* instruction) { // Require a register for the interface check since there is a loop that compares the class to // a memory address. locations->SetInAt(1, Location::RequiresRegister()); + } else if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); } else { locations->SetInAt(1, Location::Any()); } @@ -6531,7 +6584,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { break; } - case TypeCheckKind::kInterfaceCheck: + case TypeCheckKind::kInterfaceCheck: { // Fast path for the interface check. Try to avoid read barriers to improve the fast path. // We can not get false positives by doing this. // /* HeapReference */ temp = obj->klass_ @@ -6567,6 +6620,20 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { // If `cls` was poisoned above, unpoison it. __ MaybeUnpoisonHeapReference(cls.AsRegister()); break; + } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, temp); + __ j(kNotEqual, type_check_slow_path->GetEntryLabel()); + break; + } } if (done.IsLinked()) { diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index e8d1efe702..9a4c53b524 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -208,6 +208,7 @@ class InstructionCodeGeneratorX86_64 : public InstructionCodeGenerator { // the suspend call. void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor); void GenerateClassInitializationCheck(SlowPathCode* slow_path, CpuRegister class_reg); + void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, CpuRegister temp); void HandleBitwiseOperation(HBinaryOperation* operation); void GenerateRemFP(HRem* rem); void DivRemOneOrMinusOne(HBinaryOperation* instruction); diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index c88baa8610..fbcbe3608e 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -25,6 +25,11 @@ #include "base/bit_vector-inl.h" #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" +#include "handle.h" +#include "mirror/class.h" +#include "obj_ptr-inl.h" +#include "scoped_thread_state_change-inl.h" +#include "subtype_check.h" namespace art { @@ -548,30 +553,85 @@ void GraphChecker::VisitReturnVoid(HReturnVoid* ret) { } } -void GraphChecker::VisitCheckCast(HCheckCast* check) { - VisitInstruction(check); - HInstruction* input = check->InputAt(1); - if (!input->IsLoadClass()) { - AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.", +void GraphChecker::CheckTypeCheckBitstringInput(HTypeCheckInstruction* check, + size_t input_pos, + bool check_value, + uint32_t expected_value, + const char* name) { + if (!check->InputAt(input_pos)->IsIntConstant()) { + AddError(StringPrintf("%s:%d (bitstring) expects a HIntConstant input %zu (%s), not %s:%d.", check->DebugName(), check->GetId(), - input->DebugName(), - input->GetId())); + input_pos, + name, + check->InputAt(2)->DebugName(), + check->InputAt(2)->GetId())); + } else if (check_value) { + uint32_t actual_value = + static_cast(check->InputAt(input_pos)->AsIntConstant()->GetValue()); + if (actual_value != expected_value) { + AddError(StringPrintf("%s:%d (bitstring) has %s 0x%x, not 0x%x as expected.", + check->DebugName(), + check->GetId(), + name, + actual_value, + expected_value)); + } } } -void GraphChecker::VisitInstanceOf(HInstanceOf* instruction) { - VisitInstruction(instruction); - HInstruction* input = instruction->InputAt(1); - if (!input->IsLoadClass()) { - AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.", - instruction->DebugName(), - instruction->GetId(), - input->DebugName(), - input->GetId())); +void GraphChecker::HandleTypeCheckInstruction(HTypeCheckInstruction* check) { + VisitInstruction(check); + HInstruction* input = check->InputAt(1); + if (check->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { + if (!input->IsNullConstant()) { + AddError(StringPrintf("%s:%d (bitstring) expects a HNullConstant as second input, not %s:%d.", + check->DebugName(), + check->GetId(), + input->DebugName(), + input->GetId())); + } + bool check_values = false; + BitString::StorageType expected_path_to_root = 0u; + BitString::StorageType expected_mask = 0u; + { + ScopedObjectAccess soa(Thread::Current()); + ObjPtr klass = check->GetClass().Get(); + MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); + SubtypeCheckInfo::State state = SubtypeCheck>::GetState(klass); + if (state == SubtypeCheckInfo::kAssigned) { + expected_path_to_root = + SubtypeCheck>::GetEncodedPathToRootForTarget(klass); + expected_mask = SubtypeCheck>::GetEncodedPathToRootMask(klass); + check_values = true; + } else { + AddError(StringPrintf("%s:%d (bitstring) references a class with unassigned bitstring.", + check->DebugName(), + check->GetId())); + } + } + CheckTypeCheckBitstringInput( + check, /* input_pos */ 2, check_values, expected_path_to_root, "path_to_root"); + CheckTypeCheckBitstringInput(check, /* input_pos */ 3, check_values, expected_mask, "mask"); + } else { + if (!input->IsLoadClass()) { + AddError(StringPrintf("%s:%d (classic) expects a HLoadClass as second input, not %s:%d.", + check->DebugName(), + check->GetId(), + input->DebugName(), + input->GetId())); + } } } +void GraphChecker::VisitCheckCast(HCheckCast* check) { + HandleTypeCheckInstruction(check); +} + +void GraphChecker::VisitInstanceOf(HInstanceOf* instruction) { + HandleTypeCheckInstruction(instruction); +} + void GraphChecker::HandleLoop(HBasicBlock* loop_header) { int id = loop_header->GetBlockId(); HLoopInformation* loop_information = loop_header->GetLoopInformation(); diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h index 0f0b49d240..dbedc40518 100644 --- a/compiler/optimizing/graph_checker.h +++ b/compiler/optimizing/graph_checker.h @@ -71,6 +71,12 @@ class GraphChecker : public HGraphDelegateVisitor { void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE; void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE; + void CheckTypeCheckBitstringInput(HTypeCheckInstruction* check, + size_t input_pos, + bool check_value, + uint32_t expected_value, + const char* name); + void HandleTypeCheckInstruction(HTypeCheckInstruction* instruction); void HandleLoop(HBasicBlock* loop_header); void HandleBooleanInput(HInstruction* instruction, size_t input_index); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 5ff31cead5..6cb1881d7d 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -390,16 +390,23 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { StartAttributeStream("load_kind") << load_string->GetLoadKind(); } - void VisitCheckCast(HCheckCast* check_cast) OVERRIDE { - StartAttributeStream("check_kind") << check_cast->GetTypeCheckKind(); + void HandleTypeCheckInstruction(HTypeCheckInstruction* check) { + StartAttributeStream("check_kind") << check->GetTypeCheckKind(); StartAttributeStream("must_do_null_check") << std::boolalpha - << check_cast->MustDoNullCheck() << std::noboolalpha; + << check->MustDoNullCheck() << std::noboolalpha; + if (check->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { + StartAttributeStream("path_to_root") << std::hex + << "0x" << check->GetBitstringPathToRoot() << std::dec; + StartAttributeStream("mask") << std::hex << "0x" << check->GetBitstringMask() << std::dec; + } + } + + void VisitCheckCast(HCheckCast* check_cast) OVERRIDE { + HandleTypeCheckInstruction(check_cast); } void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE { - StartAttributeStream("check_kind") << instance_of->GetTypeCheckKind(); - StartAttributeStream("must_do_null_check") << std::boolalpha - << instance_of->MustDoNullCheck() << std::noboolalpha; + HandleTypeCheckInstruction(instance_of); } void VisitArrayLength(HArrayLength* array_length) OVERRIDE { @@ -641,20 +648,32 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { << std::boolalpha << loop_info->IsIrreducible() << std::noboolalpha; } + // For the builder and the inliner, we want to add extra information on HInstructions + // that have reference types, and also HInstanceOf/HCheckcast. if ((IsPass(HGraphBuilder::kBuilderPassName) || IsPass(HInliner::kInlinerPassName)) - && (instruction->GetType() == DataType::Type::kReference)) { - ReferenceTypeInfo info = instruction->IsLoadClass() - ? instruction->AsLoadClass()->GetLoadedClassRTI() - : instruction->GetReferenceTypeInfo(); + && (instruction->GetType() == DataType::Type::kReference || + instruction->IsInstanceOf() || + instruction->IsCheckCast())) { + ReferenceTypeInfo info = (instruction->GetType() == DataType::Type::kReference) + ? instruction->IsLoadClass() + ? instruction->AsLoadClass()->GetLoadedClassRTI() + : instruction->GetReferenceTypeInfo() + : instruction->IsInstanceOf() + ? instruction->AsInstanceOf()->GetTargetClassRTI() + : instruction->AsCheckCast()->GetTargetClassRTI(); ScopedObjectAccess soa(Thread::Current()); if (info.IsValid()) { StartAttributeStream("klass") << mirror::Class::PrettyDescriptor(info.GetTypeHandle().Get()); - StartAttributeStream("can_be_null") - << std::boolalpha << instruction->CanBeNull() << std::noboolalpha; + if (instruction->GetType() == DataType::Type::kReference) { + StartAttributeStream("can_be_null") + << std::boolalpha << instruction->CanBeNull() << std::noboolalpha; + } StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha; - } else if (instruction->IsLoadClass()) { + } else if (instruction->IsLoadClass() || + instruction->IsInstanceOf() || + instruction->IsCheckCast()) { StartAttributeStream("klass") << "unresolved"; } else { // The NullConstant may be added to the graph during other passes that happen between diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index c7aef3779d..9647dd5d41 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1815,29 +1815,6 @@ void HInstructionBuilder::BuildFillWideArrayData(HInstruction* object, } } -static TypeCheckKind ComputeTypeCheckKind(Handle cls) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (cls == nullptr) { - return TypeCheckKind::kUnresolvedCheck; - } else if (cls->IsInterface()) { - return TypeCheckKind::kInterfaceCheck; - } else if (cls->IsArrayClass()) { - if (cls->GetComponentType()->IsObjectClass()) { - return TypeCheckKind::kArrayObjectCheck; - } else if (cls->CannotBeAssignedFromOtherTypes()) { - return TypeCheckKind::kExactCheck; - } else { - return TypeCheckKind::kArrayCheck; - } - } else if (cls->IsFinal()) { - return TypeCheckKind::kExactCheck; - } else if (cls->IsAbstract()) { - return TypeCheckKind::kAbstractClassCheck; - } else { - return TypeCheckKind::kClassHierarchyCheck; - } -} - void HInstructionBuilder::BuildLoadString(dex::StringIndex string_index, uint32_t dex_pc) { HLoadString* load_string = new (allocator_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc); @@ -1852,22 +1829,8 @@ void HInstructionBuilder::BuildLoadString(dex::StringIndex string_index, uint32_ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc) { ScopedObjectAccess soa(Thread::Current()); const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); - Handle class_loader = dex_compilation_unit_->GetClassLoader(); - Handle klass = handles_->NewHandle(compiler_driver_->ResolveClass( - soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_)); - - bool needs_access_check = true; - if (klass != nullptr) { - if (klass->IsPublic()) { - needs_access_check = false; - } else { - ObjPtr compiling_class = GetCompilingClass(); - if (compiling_class != nullptr && compiling_class->CanAccess(klass.Get())) { - needs_access_check = false; - } - } - } - + Handle klass = ResolveClass(soa, type_index); + bool needs_access_check = LoadClassNeedsAccessCheck(klass); return BuildLoadClass(type_index, dex_file, klass, dex_pc, needs_access_check); } @@ -1912,25 +1875,83 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, return load_class; } +Handle HInstructionBuilder::ResolveClass(ScopedObjectAccess& soa, + dex::TypeIndex type_index) { + Handle class_loader = dex_compilation_unit_->GetClassLoader(); + ObjPtr klass = compiler_driver_->ResolveClass( + soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_); + // TODO: Avoid creating excessive handles if the method references the same class repeatedly. + // (Use a map on the local_allocator_.) + return handles_->NewHandle(klass); +} + +bool HInstructionBuilder::LoadClassNeedsAccessCheck(Handle klass) { + if (klass == nullptr) { + return true; + } else if (klass->IsPublic()) { + return false; + } else { + ObjPtr compiling_class = GetCompilingClass(); + return compiling_class == nullptr || !compiling_class->CanAccess(klass.Get()); + } +} + void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, uint8_t destination, uint8_t reference, dex::TypeIndex type_index, uint32_t dex_pc) { HInstruction* object = LoadLocal(reference, DataType::Type::kReference); - HLoadClass* cls = BuildLoadClass(type_index, dex_pc); ScopedObjectAccess soa(Thread::Current()); - TypeCheckKind check_kind = ComputeTypeCheckKind(cls->GetClass()); + const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); + Handle klass = ResolveClass(soa, type_index); + bool needs_access_check = LoadClassNeedsAccessCheck(klass); + TypeCheckKind check_kind = HSharpening::ComputeTypeCheckKind( + klass.Get(), code_generator_, compiler_driver_, needs_access_check); + + HInstruction* class_or_null = nullptr; + HIntConstant* bitstring_path_to_root = nullptr; + HIntConstant* bitstring_mask = nullptr; + if (check_kind == TypeCheckKind::kBitstringCheck) { + // TODO: Allow using the bitstring check also if we need an access check. + DCHECK(!needs_access_check); + class_or_null = graph_->GetNullConstant(dex_pc); + MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); + uint32_t path_to_root = + SubtypeCheck>::GetEncodedPathToRootForTarget(klass.Get()); + uint32_t mask = SubtypeCheck>::GetEncodedPathToRootMask(klass.Get()); + bitstring_path_to_root = graph_->GetIntConstant(static_cast(path_to_root), dex_pc); + bitstring_mask = graph_->GetIntConstant(static_cast(mask), dex_pc); + } else { + class_or_null = BuildLoadClass(type_index, dex_file, klass, dex_pc, needs_access_check); + } + DCHECK(class_or_null != nullptr); + if (instruction.Opcode() == Instruction::INSTANCE_OF) { - AppendInstruction(new (allocator_) HInstanceOf(object, cls, check_kind, dex_pc)); + AppendInstruction(new (allocator_) HInstanceOf(object, + class_or_null, + check_kind, + klass, + dex_pc, + allocator_, + bitstring_path_to_root, + bitstring_mask)); UpdateLocal(destination, current_block_->GetLastInstruction()); } else { DCHECK_EQ(instruction.Opcode(), Instruction::CHECK_CAST); // We emit a CheckCast followed by a BoundType. CheckCast is a statement // which may throw. If it succeeds BoundType sets the new type of `object` // for all subsequent uses. - AppendInstruction(new (allocator_) HCheckCast(object, cls, check_kind, dex_pc)); + AppendInstruction( + new (allocator_) HCheckCast(object, + class_or_null, + check_kind, + klass, + dex_pc, + allocator_, + bitstring_path_to_root, + bitstring_mask)); AppendInstruction(new (allocator_) HBoundType(object, dex_pc)); UpdateLocal(reference, current_block_->GetLastInstruction()); } diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index 4428c53277..f78829232d 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -39,6 +39,7 @@ class DexCompilationUnit; class HBasicBlockBuilder; class Instruction; class OptimizingCompilerStats; +class ScopedObjectAccess; class SsaBuilder; class VariableSizedHandleScope; @@ -232,6 +233,12 @@ class HInstructionBuilder : public ValueObject { bool needs_access_check) REQUIRES_SHARED(Locks::mutator_lock_); + Handle ResolveClass(ScopedObjectAccess& soa, dex::TypeIndex type_index) + REQUIRES_SHARED(Locks::mutator_lock_); + + bool LoadClassNeedsAccessCheck(Handle klass) + REQUIRES_SHARED(Locks::mutator_lock_); + // Returns the outer-most compiling method's class. ObjPtr GetOutermostCompilingClass() const; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 2b6f90540f..fa1d96b89e 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -579,7 +579,9 @@ bool InstructionSimplifierVisitor::CanEnsureNotNullAt(HInstruction* input, HInst // Returns whether doing a type test between the class of `object` against `klass` has // a statically known outcome. The result of the test is stored in `outcome`. -static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bool* outcome) { +static bool TypeCheckHasKnownOutcome(ReferenceTypeInfo class_rti, + HInstruction* object, + /*out*/bool* outcome) { DCHECK(!object->IsNullConstant()) << "Null constants should be special cased"; ReferenceTypeInfo obj_rti = object->GetReferenceTypeInfo(); ScopedObjectAccess soa(Thread::Current()); @@ -589,7 +591,6 @@ static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bo return false; } - ReferenceTypeInfo class_rti = klass->GetLoadedClassRTI(); if (!class_rti.IsValid()) { // Happens when the loaded class is unresolved. return false; @@ -614,8 +615,8 @@ static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bo void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { HInstruction* object = check_cast->InputAt(0); - HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass(); - if (load_class->NeedsAccessCheck()) { + if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck && + check_cast->GetTargetClass()->NeedsAccessCheck()) { // If we need to perform an access check we cannot remove the instruction. return; } @@ -633,15 +634,18 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { // Note: The `outcome` is initialized to please valgrind - the compiler can reorder // the return value check with the `outcome` check, b/27651442 . bool outcome = false; - if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) { + if (TypeCheckHasKnownOutcome(check_cast->GetTargetClassRTI(), object, &outcome)) { if (outcome) { check_cast->GetBlock()->RemoveInstruction(check_cast); MaybeRecordStat(stats_, MethodCompilationStat::kRemovedCheckedCast); - if (!load_class->HasUses()) { - // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. - // However, here we know that it cannot because the checkcast was successfull, hence - // the class was already loaded. - load_class->GetBlock()->RemoveInstruction(load_class); + if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) { + HLoadClass* load_class = check_cast->GetTargetClass(); + if (!load_class->HasUses()) { + // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. + // However, here we know that it cannot because the checkcast was successfull, hence + // the class was already loaded. + load_class->GetBlock()->RemoveInstruction(load_class); + } } } else { // Don't do anything for exceptional cases for now. Ideally we should remove @@ -652,8 +656,8 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { HInstruction* object = instruction->InputAt(0); - HLoadClass* load_class = instruction->InputAt(1)->AsLoadClass(); - if (load_class->NeedsAccessCheck()) { + if (instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck && + instruction->GetTargetClass()->NeedsAccessCheck()) { // If we need to perform an access check we cannot remove the instruction. return; } @@ -676,7 +680,7 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { // Note: The `outcome` is initialized to please valgrind - the compiler can reorder // the return value check with the `outcome` check, b/27651442 . bool outcome = false; - if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) { + if (TypeCheckHasKnownOutcome(instruction->GetTargetClassRTI(), object, &outcome)) { MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf); if (outcome && can_be_null) { // Type test will succeed, we just need a null test. @@ -689,11 +693,14 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { } RecordSimplification(); instruction->GetBlock()->RemoveInstruction(instruction); - if (outcome && !load_class->HasUses()) { - // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. - // However, here we know that it cannot because the instanceof check was successfull, hence - // the class was already loaded. - load_class->GetBlock()->RemoveInstruction(load_class); + if (outcome && instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) { + HLoadClass* load_class = instruction->GetTargetClass(); + if (!load_class->HasUses()) { + // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. + // However, here we know that it cannot because the instanceof check was successfull, hence + // the class was already loaded. + load_class->GetBlock()->RemoveInstruction(load_class); + } } } } diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index d3212cbbc0..f784f8f7f3 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -3103,6 +3103,8 @@ std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs) { return os << "array_object_check"; case TypeCheckKind::kArrayCheck: return os << "array_check"; + case TypeCheckKind::kBitstringCheck: + return os << "bitstring_check"; default: LOG(FATAL) << "Unknown TypeCheckKind: " << static_cast(rhs); UNREACHABLE(); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index a8fcea2097..79d733060b 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -6178,8 +6178,7 @@ class HLoadClass FINAL : public HInstruction { special_input_(HUserRecord(current_method)), type_index_(type_index), dex_file_(dex_file), - klass_(klass), - loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) { + klass_(klass) { // Referrers class should not need access check. We never inline unverified // methods so we can't possibly end up in this situation. DCHECK(!is_referrers_class || !needs_access_check); @@ -6189,6 +6188,7 @@ class HLoadClass FINAL : public HInstruction { SetPackedFlag(needs_access_check); SetPackedFlag(false); SetPackedFlag(false); + SetPackedFlag(false); } bool IsClonable() const OVERRIDE { return true; } @@ -6243,13 +6243,18 @@ class HLoadClass FINAL : public HInstruction { } ReferenceTypeInfo GetLoadedClassRTI() { - return loaded_class_rti_; + if (GetPackedFlag()) { + // Note: The is_exact flag from the return value should not be used. + return ReferenceTypeInfo::CreateUnchecked(klass_, /* is_exact */ true); + } else { + return ReferenceTypeInfo::CreateInvalid(); + } } - void SetLoadedClassRTI(ReferenceTypeInfo rti) { - // Make sure we only set exact types (the loaded class should never be merged). - DCHECK(rti.IsExact()); - loaded_class_rti_ = rti; + // Loaded class RTI is marked as valid by RTP if the klass_ is admissible. + void SetValidLoadedClassRTI() REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(klass_ != nullptr); + SetPackedFlag(true); } dex::TypeIndex GetTypeIndex() const { return type_index_; } @@ -6302,7 +6307,8 @@ class HLoadClass FINAL : public HInstruction { static constexpr size_t kFieldLoadKind = kFlagGenerateClInitCheck + 1; static constexpr size_t kFieldLoadKindSize = MinimumBitsToStore(static_cast(LoadKind::kLast)); - static constexpr size_t kNumberOfLoadClassPackedBits = kFieldLoadKind + kFieldLoadKindSize; + static constexpr size_t kFlagValidLoadedClassRTI = kFieldLoadKind + kFieldLoadKindSize; + static constexpr size_t kNumberOfLoadClassPackedBits = kFlagValidLoadedClassRTI + 1; static_assert(kNumberOfLoadClassPackedBits < kMaxNumberOfPackedBits, "Too many packed fields."); using LoadKindField = BitField; @@ -6329,8 +6335,6 @@ class HLoadClass FINAL : public HInstruction { const DexFile& dex_file_; Handle klass_; - - ReferenceTypeInfo loaded_class_rti_; }; std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs); @@ -6882,72 +6886,159 @@ enum class TypeCheckKind { kInterfaceCheck, // No optimization yet when checking against an interface. kArrayObjectCheck, // Can just check if the array is not primitive. kArrayCheck, // No optimization yet when checking against a generic array. + kBitstringCheck, // Compare the type check bitstring. kLast = kArrayCheck }; std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs); -class HInstanceOf FINAL : public HExpression<2> { +// Note: HTypeCheckInstruction is just a helper class, not an abstract instruction with an +// `IsTypeCheckInstruction()`. (New virtual methods in the HInstruction class have a high cost.) +class HTypeCheckInstruction : public HVariableInputSizeInstruction { public: - HInstanceOf(HInstruction* object, - HLoadClass* target_class, - TypeCheckKind check_kind, - uint32_t dex_pc) - : HExpression(kInstanceOf, - DataType::Type::kBool, - SideEffectsForArchRuntimeCalls(check_kind), - dex_pc) { + HTypeCheckInstruction(InstructionKind kind, + HInstruction* object, + HInstruction* target_class_or_null, + TypeCheckKind check_kind, + Handle klass, + uint32_t dex_pc, + ArenaAllocator* allocator, + HIntConstant* bitstring_path_to_root, + HIntConstant* bitstring_mask, + SideEffects side_effects) + : HVariableInputSizeInstruction( + kind, + side_effects, + dex_pc, + allocator, + /* number_of_inputs */ check_kind == TypeCheckKind::kBitstringCheck ? 4u : 2u, + kArenaAllocTypeCheckInputs), + klass_(klass) { SetPackedField(check_kind); SetPackedFlag(true); + SetPackedFlag(false); SetRawInputAt(0, object); - SetRawInputAt(1, target_class); + SetRawInputAt(1, target_class_or_null); + DCHECK_EQ(check_kind == TypeCheckKind::kBitstringCheck, bitstring_path_to_root != nullptr); + DCHECK_EQ(check_kind == TypeCheckKind::kBitstringCheck, bitstring_mask != nullptr); + if (check_kind == TypeCheckKind::kBitstringCheck) { + DCHECK(target_class_or_null->IsNullConstant()); + SetRawInputAt(2, bitstring_path_to_root); + SetRawInputAt(3, bitstring_mask); + } else { + DCHECK(target_class_or_null->IsLoadClass()); + } } HLoadClass* GetTargetClass() const { + DCHECK_NE(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck); HInstruction* load_class = InputAt(1); DCHECK(load_class->IsLoadClass()); return load_class->AsLoadClass(); } - bool IsClonable() const OVERRIDE { return true; } - bool CanBeMoved() const OVERRIDE { return true; } + uint32_t GetBitstringPathToRoot() const { + DCHECK_EQ(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck); + HInstruction* path_to_root = InputAt(2); + DCHECK(path_to_root->IsIntConstant()); + return static_cast(path_to_root->AsIntConstant()->GetValue()); + } - bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { - return true; + uint32_t GetBitstringMask() const { + DCHECK_EQ(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck); + HInstruction* mask = InputAt(3); + DCHECK(mask->IsIntConstant()); + return static_cast(mask->AsIntConstant()->GetValue()); } - bool NeedsEnvironment() const OVERRIDE { - return CanCallRuntime(GetTypeCheckKind()); + bool IsClonable() const OVERRIDE { return true; } + bool CanBeMoved() const OVERRIDE { return true; } + + bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { + DCHECK(other->IsInstanceOf() || other->IsCheckCast()) << other->DebugName(); + return GetPackedFields() == down_cast(other)->GetPackedFields(); } - // Used only in code generation. bool MustDoNullCheck() const { return GetPackedFlag(); } void ClearMustDoNullCheck() { SetPackedFlag(false); } TypeCheckKind GetTypeCheckKind() const { return GetPackedField(); } bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } - static bool CanCallRuntime(TypeCheckKind check_kind) { - // Mips currently does runtime calls for any other checks. - return check_kind != TypeCheckKind::kExactCheck; + ReferenceTypeInfo GetTargetClassRTI() { + if (GetPackedFlag()) { + // Note: The is_exact flag from the return value should not be used. + return ReferenceTypeInfo::CreateUnchecked(klass_, /* is_exact */ true); + } else { + return ReferenceTypeInfo::CreateInvalid(); + } } - static SideEffects SideEffectsForArchRuntimeCalls(TypeCheckKind check_kind) { - return CanCallRuntime(check_kind) ? SideEffects::CanTriggerGC() : SideEffects::None(); + // Target class RTI is marked as valid by RTP if the klass_ is admissible. + void SetValidTargetClassRTI() REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(klass_ != nullptr); + SetPackedFlag(true); } - DECLARE_INSTRUCTION(InstanceOf); + Handle GetClass() const { + return klass_; + } protected: - DEFAULT_COPY_CONSTRUCTOR(InstanceOf); + DEFAULT_COPY_CONSTRUCTOR(TypeCheckInstruction); private: - static constexpr size_t kFieldTypeCheckKind = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits; static constexpr size_t kFieldTypeCheckKindSize = MinimumBitsToStore(static_cast(TypeCheckKind::kLast)); static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; - static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagMustDoNullCheck + 1; + static constexpr size_t kFlagValidTargetClassRTI = kFlagMustDoNullCheck + 1; + static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagValidTargetClassRTI + 1; static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using TypeCheckKindField = BitField; + + Handle klass_; +}; + +class HInstanceOf FINAL : public HTypeCheckInstruction { + public: + HInstanceOf(HInstruction* object, + HInstruction* target_class_or_null, + TypeCheckKind check_kind, + Handle klass, + uint32_t dex_pc, + ArenaAllocator* allocator, + HIntConstant* bitstring_path_to_root, + HIntConstant* bitstring_mask) + : HTypeCheckInstruction(kInstanceOf, + object, + target_class_or_null, + check_kind, + klass, + dex_pc, + allocator, + bitstring_path_to_root, + bitstring_mask, + SideEffectsForArchRuntimeCalls(check_kind)) {} + + DataType::Type GetType() const OVERRIDE { return DataType::Type::kBool; } + + bool NeedsEnvironment() const OVERRIDE { + return CanCallRuntime(GetTypeCheckKind()); + } + + static bool CanCallRuntime(TypeCheckKind check_kind) { + // Mips currently does runtime calls for any other checks. + return check_kind != TypeCheckKind::kExactCheck; + } + + static SideEffects SideEffectsForArchRuntimeCalls(TypeCheckKind check_kind) { + return CanCallRuntime(check_kind) ? SideEffects::CanTriggerGC() : SideEffects::None(); + } + + DECLARE_INSTRUCTION(InstanceOf); + + protected: + DEFAULT_COPY_CONSTRUCTOR(InstanceOf); }; class HBoundType FINAL : public HExpression<1> { @@ -6997,31 +7088,26 @@ class HBoundType FINAL : public HExpression<1> { ReferenceTypeInfo upper_bound_; }; -class HCheckCast FINAL : public HTemplateInstruction<2> { +class HCheckCast FINAL : public HTypeCheckInstruction { public: HCheckCast(HInstruction* object, - HLoadClass* target_class, + HInstruction* target_class_or_null, TypeCheckKind check_kind, - uint32_t dex_pc) - : HTemplateInstruction(kCheckCast, SideEffects::CanTriggerGC(), dex_pc) { - SetPackedField(check_kind); - SetPackedFlag(true); - SetRawInputAt(0, object); - SetRawInputAt(1, target_class); - } - - HLoadClass* GetTargetClass() const { - HInstruction* load_class = InputAt(1); - DCHECK(load_class->IsLoadClass()); - return load_class->AsLoadClass(); - } - - bool IsClonable() const OVERRIDE { return true; } - bool CanBeMoved() const OVERRIDE { return true; } - - bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { - return true; - } + Handle klass, + uint32_t dex_pc, + ArenaAllocator* allocator, + HIntConstant* bitstring_path_to_root, + HIntConstant* bitstring_mask) + : HTypeCheckInstruction(kCheckCast, + object, + target_class_or_null, + check_kind, + klass, + dex_pc, + allocator, + bitstring_path_to_root, + bitstring_mask, + SideEffects::CanTriggerGC()) {} bool NeedsEnvironment() const OVERRIDE { // Instruction may throw a CheckCastError. @@ -7030,24 +7116,10 @@ class HCheckCast FINAL : public HTemplateInstruction<2> { bool CanThrow() const OVERRIDE { return true; } - bool MustDoNullCheck() const { return GetPackedFlag(); } - void ClearMustDoNullCheck() { SetPackedFlag(false); } - TypeCheckKind GetTypeCheckKind() const { return GetPackedField(); } - bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } - DECLARE_INSTRUCTION(CheckCast); protected: DEFAULT_COPY_CONSTRUCTOR(CheckCast); - - private: - static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits; - static constexpr size_t kFieldTypeCheckKindSize = - MinimumBitsToStore(static_cast(TypeCheckKind::kLast)); - static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; - static constexpr size_t kNumberOfCheckCastPackedBits = kFlagMustDoNullCheck + 1; - static_assert(kNumberOfCheckCastPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - using TypeCheckKindField = BitField; }; /** diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 00194ff1fe..e0a9cfb934 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -99,6 +99,7 @@ enum class MethodCompilationStat { kConstructorFenceRemovedLSE, kConstructorFenceRemovedPFRA, kConstructorFenceRemovedCFRE, + kBitstringTypeCheck, kJitOutOfMemoryForCommit, kLastStat }; diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index f843c008d8..59733397bf 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -34,6 +34,20 @@ void PrepareForRegisterAllocation::Run() { } } +void PrepareForRegisterAllocation::VisitCheckCast(HCheckCast* check_cast) { + // Record only those bitstring type checks that make it to the codegen stage. + if (check_cast->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { + MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck); + } +} + +void PrepareForRegisterAllocation::VisitInstanceOf(HInstanceOf* instance_of) { + // Record only those bitstring type checks that make it to the codegen stage. + if (instance_of->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { + MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck); + } +} + void PrepareForRegisterAllocation::VisitNullCheck(HNullCheck* check) { check->ReplaceWith(check->InputAt(0)); } diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h index 2c64f016c1..f6e4d3ef99 100644 --- a/compiler/optimizing/prepare_for_register_allocation.h +++ b/compiler/optimizing/prepare_for_register_allocation.h @@ -40,6 +40,8 @@ class PrepareForRegisterAllocation : public HGraphDelegateVisitor { "prepare_for_register_allocation"; private: + void VisitCheckCast(HCheckCast* check_cast) OVERRIDE; + void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE; void VisitNullCheck(HNullCheck* check) OVERRIDE; void VisitDivZeroCheck(HDivZeroCheck* check) OVERRIDE; void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE; diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 67a61fc01d..4030883a57 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -87,6 +87,7 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { void VisitDeoptimize(HDeoptimize* deopt) OVERRIDE; void VisitNewInstance(HNewInstance* new_instance) OVERRIDE; void VisitLoadClass(HLoadClass* load_class) OVERRIDE; + void VisitInstanceOf(HInstanceOf* load_class) OVERRIDE; void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE; void VisitLoadString(HLoadString* instr) OVERRIDE; void VisitLoadException(HLoadException* instr) OVERRIDE; @@ -171,6 +172,12 @@ void ReferenceTypePropagation::ValidateTypes() { << "NullCheck " << instr->GetReferenceTypeInfo() << "Input(0) " << instr->InputAt(0)->GetReferenceTypeInfo(); } + } else if (instr->IsInstanceOf()) { + HInstanceOf* iof = instr->AsInstanceOf(); + DCHECK(!iof->GetTargetClassRTI().IsValid() || iof->GetTargetClassRTI().IsExact()); + } else if (instr->IsCheckCast()) { + HCheckCast* check = instr->AsCheckCast(); + DCHECK(!check->GetTargetClassRTI().IsValid() || check->GetTargetClassRTI().IsExact()); } } } @@ -499,8 +506,7 @@ void ReferenceTypePropagation::RTPVisitor::BoundTypeForIfInstanceOf(HBasicBlock* return; } - HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass(); - ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); + ReferenceTypeInfo class_rti = instanceOf->GetTargetClassRTI(); if (!class_rti.IsValid()) { // He have loaded an unresolved class. Don't bother bounding the type. return; @@ -643,15 +649,20 @@ void ReferenceTypePropagation::RTPVisitor::VisitUnresolvedStaticFieldGet( void ReferenceTypePropagation::RTPVisitor::VisitLoadClass(HLoadClass* instr) { ScopedObjectAccess soa(Thread::Current()); - Handle resolved_class = instr->GetClass(); - if (IsAdmissible(resolved_class.Get())) { - instr->SetLoadedClassRTI(ReferenceTypeInfo::Create( - resolved_class, /* is_exact */ true)); + if (IsAdmissible(instr->GetClass().Get())) { + instr->SetValidLoadedClassRTI(); } instr->SetReferenceTypeInfo( ReferenceTypeInfo::Create(handle_cache_->GetClassClassHandle(), /* is_exact */ true)); } +void ReferenceTypePropagation::RTPVisitor::VisitInstanceOf(HInstanceOf* instr) { + ScopedObjectAccess soa(Thread::Current()); + if (IsAdmissible(instr->GetClass().Get())) { + instr->SetValidTargetClassRTI(); + } +} + void ReferenceTypePropagation::RTPVisitor::VisitClinitCheck(HClinitCheck* instr) { instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo()); } @@ -719,8 +730,6 @@ void ReferenceTypePropagation::RTPVisitor::VisitBoundType(HBoundType* instr) { } void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast) { - HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass(); - ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); HBoundType* bound_type = check_cast->GetNext()->AsBoundType(); if (bound_type == nullptr || bound_type->GetUpperBound().IsValid()) { // The next instruction is not an uninitialized BoundType. This must be @@ -729,12 +738,14 @@ void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast } DCHECK_EQ(bound_type->InputAt(0), check_cast->InputAt(0)); - if (class_rti.IsValid()) { + ScopedObjectAccess soa(Thread::Current()); + Handle klass = check_cast->GetClass(); + if (IsAdmissible(klass.Get())) { DCHECK(is_first_run_); - ScopedObjectAccess soa(Thread::Current()); + check_cast->SetValidTargetClassRTI(); // This is the first run of RTP and class is resolved. - bool is_exact = class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes(); - bound_type->SetUpperBound(ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), is_exact), + bool is_exact = klass->CannotBeAssignedFromOtherTypes(); + bound_type->SetUpperBound(ReferenceTypeInfo::Create(klass, is_exact), /* CheckCast succeeds for nulls. */ true); } else { // This is the first run of RTP and class is unresolved. Remove the binding. diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index 7dffb2a378..70b45763af 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -240,6 +240,75 @@ HLoadClass::LoadKind HSharpening::ComputeLoadClassKind( return load_kind; } +static inline bool CanUseTypeCheckBitstring(ObjPtr klass, + CodeGenerator* codegen, + CompilerDriver* compiler_driver) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!klass->IsProxyClass()); + DCHECK(!klass->IsArrayClass()); + + if (Runtime::Current()->UseJitCompilation()) { + // If we're JITting, try to assign a type check bitstring (fall through). + } else if (codegen->GetCompilerOptions().IsBootImage()) { + const char* descriptor = klass->GetDexFile().StringByTypeIdx(klass->GetDexTypeIndex()); + if (!compiler_driver->IsImageClass(descriptor)) { + return false; + } + // If the target is a boot image class, try to assign a type check bitstring (fall through). + // (If --force-determinism, this was already done; repeating is OK and yields the same result.) + } else { + // TODO: Use the bitstring also for AOT app compilation if the target class has a bitstring + // already assigned in the boot image. + return false; + } + + // Try to assign a type check bitstring. + MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); + if ((false) && // FIXME: Inliner does not respect compiler_driver->IsClassToCompile() + // and we're hitting an unassigned bitstring in dex2oat_image_test. b/26687569 + kIsDebugBuild && + codegen->GetCompilerOptions().IsBootImage() && + codegen->GetCompilerOptions().IsForceDeterminism()) { + SubtypeCheckInfo::State old_state = SubtypeCheck>::GetState(klass); + CHECK(old_state == SubtypeCheckInfo::kAssigned || old_state == SubtypeCheckInfo::kOverflowed) + << klass->PrettyDescriptor() << "/" << old_state + << " in " << codegen->GetGraph()->PrettyMethod(); + } + SubtypeCheckInfo::State state = SubtypeCheck>::EnsureAssigned(klass); + return state == SubtypeCheckInfo::kAssigned; +} + +TypeCheckKind HSharpening::ComputeTypeCheckKind(ObjPtr klass, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + bool needs_access_check) { + if (klass == nullptr) { + return TypeCheckKind::kUnresolvedCheck; + } else if (klass->IsInterface()) { + return TypeCheckKind::kInterfaceCheck; + } else if (klass->IsArrayClass()) { + if (klass->GetComponentType()->IsObjectClass()) { + return TypeCheckKind::kArrayObjectCheck; + } else if (klass->CannotBeAssignedFromOtherTypes()) { + return TypeCheckKind::kExactCheck; + } else { + return TypeCheckKind::kArrayCheck; + } + } else if (klass->IsFinal()) { // TODO: Consider using bitstring for final classes. + return TypeCheckKind::kExactCheck; + } else if (kBitstringSubtypeCheckEnabled && + !needs_access_check && + CanUseTypeCheckBitstring(klass, codegen, compiler_driver)) { + // TODO: We should not need the `!needs_access_check` check but getting rid of that + // requires rewriting some optimizations in instruction simplifier. + return TypeCheckKind::kBitstringCheck; + } else if (klass->IsAbstract()) { + return TypeCheckKind::kAbstractClassCheck; + } else { + return TypeCheckKind::kClassHierarchyCheck; + } +} + void HSharpening::ProcessLoadString( HLoadString* load_string, CodeGenerator* codegen, diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h index 6df7d6d91e..fa3e948eeb 100644 --- a/compiler/optimizing/sharpening.h +++ b/compiler/optimizing/sharpening.h @@ -44,12 +44,10 @@ class HSharpening : public HOptimization { static constexpr const char* kSharpeningPassName = "sharpening"; - // Used by the builder. - static void ProcessLoadString(HLoadString* load_string, - CodeGenerator* codegen, - CompilerDriver* compiler_driver, - const DexCompilationUnit& dex_compilation_unit, - VariableSizedHandleScope* handles); + // Used by Sharpening and InstructionSimplifier. + static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, + CodeGenerator* codegen, + CompilerDriver* compiler_driver); // Used by the builder and the inliner. static HLoadClass::LoadKind ComputeLoadClassKind(HLoadClass* load_class, @@ -58,10 +56,19 @@ class HSharpening : public HOptimization { const DexCompilationUnit& dex_compilation_unit) REQUIRES_SHARED(Locks::mutator_lock_); - // Used by Sharpening and InstructionSimplifier. - static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, - CodeGenerator* codegen, - CompilerDriver* compiler_driver); + // Used by the builder. + static TypeCheckKind ComputeTypeCheckKind(ObjPtr klass, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + bool needs_access_check) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Used by the builder. + static void ProcessLoadString(HLoadString* load_string, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + const DexCompilationUnit& dex_compilation_unit, + VariableSizedHandleScope* handles); private: CodeGenerator* codegen_; diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 98214fb684..0fd239a244 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -809,6 +809,9 @@ END art_quick_unlock_object_no_inline .extern artInstanceOfFromCode .extern artThrowClassCastExceptionForObject ENTRY art_quick_check_instance_of + // Type check using the bit string passes null as the target class. In that case just throw. + cbz r1, .Lthrow_class_cast_exception_for_bitstring_check + push {r0-r2, lr} @ save arguments, padding (r2) and link register .cfi_adjust_cfa_offset 16 .cfi_rel_offset r0, 0 @@ -827,6 +830,7 @@ ENTRY art_quick_check_instance_of .cfi_restore r2 .cfi_restore lr +.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r2 @ save all registers as basis for long jump context mov r2, r9 @ pass Thread::Current bl artThrowClassCastExceptionForObject @ (Object*, Class*, Thread*) diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index fb449ed5c7..9ff5ebede3 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1269,6 +1269,9 @@ END art_quick_unlock_object_no_inline .extern artInstanceOfFromCode .extern artThrowClassCastExceptionForObject ENTRY art_quick_check_instance_of + // Type check using the bit string passes null as the target class. In that case just throw. + cbz x1, .Lthrow_class_cast_exception_for_bitstring_check + // Store arguments and link register // Stack needs to be 16B aligned on calls. SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 32 @@ -1294,6 +1297,7 @@ ENTRY art_quick_check_instance_of // Restore RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32 +.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // save all registers as basis for long jump context mov x2, xSELF // pass Thread::Current bl artThrowClassCastExceptionForObject // (Object*, Class*, Thread*) diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index b2f7e10f52..d8fe480719 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1423,6 +1423,10 @@ END art_quick_unlock_object_no_inline .extern artInstanceOfFromCode .extern artThrowClassCastExceptionForObject ENTRY art_quick_check_instance_of + // Type check using the bit string passes null as the target class. In that case just throw. + beqz $a1, .Lthrow_class_cast_exception_for_bitstring_check + nop + addiu $sp, $sp, -32 .cfi_adjust_cfa_offset 32 sw $gp, 16($sp) @@ -1441,12 +1445,15 @@ ENTRY art_quick_check_instance_of jalr $zero, $ra addiu $sp, $sp, 32 .cfi_adjust_cfa_offset -32 + .Lthrow_class_cast_exception: lw $t9, 8($sp) lw $a1, 4($sp) lw $a0, 0($sp) addiu $sp, $sp, 32 .cfi_adjust_cfa_offset -32 + +.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME la $t9, artThrowClassCastExceptionForObject jalr $zero, $t9 # artThrowClassCastException (Object*, Class*, Thread*) diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 58e0e44813..8d2a7bd6c1 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1364,6 +1364,9 @@ END art_quick_unlock_object_no_inline .extern artInstanceOfFromCode .extern artThrowClassCastExceptionForObject ENTRY art_quick_check_instance_of + // Type check using the bit string passes null as the target class. In that case just throw. + beqzc $a1, .Lthrow_class_cast_exception_for_bitstring_check + daddiu $sp, $sp, -32 .cfi_adjust_cfa_offset 32 sd $ra, 24($sp) @@ -1379,12 +1382,15 @@ ENTRY art_quick_check_instance_of jalr $zero, $ra daddiu $sp, $sp, 32 .cfi_adjust_cfa_offset -32 + .Lthrow_class_cast_exception: ld $t9, 16($sp) ld $a1, 8($sp) ld $a0, 0($sp) daddiu $sp, $sp, 32 .cfi_adjust_cfa_offset -32 + +.Lthrow_class_cast_exception_for_bitstring_check: SETUP_GP SETUP_SAVE_ALL_CALLEE_SAVES_FRAME dla $t9, artThrowClassCastExceptionForObject diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 5c4ae4ea12..df43aef94b 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1432,6 +1432,10 @@ DEFINE_FUNCTION art_quick_instance_of END_FUNCTION art_quick_instance_of DEFINE_FUNCTION art_quick_check_instance_of + // Type check using the bit string passes null as the target class. In that case just throw. + testl %ecx, %ecx + jz .Lthrow_class_cast_exception_for_bitstring_check + PUSH eax // alignment padding PUSH ecx // pass arg2 - checked class PUSH eax // pass arg1 - obj @@ -1449,6 +1453,7 @@ DEFINE_FUNCTION art_quick_check_instance_of addl LITERAL(4), %esp CFI_ADJUST_CFA_OFFSET(-4) +.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME ebx, ebx // save all registers as basis for long jump context // Outgoing argument set up PUSH eax // alignment padding diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index a813200606..4f941e1c48 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -1403,6 +1403,10 @@ DEFINE_FUNCTION art_quick_unlock_object_no_inline END_FUNCTION art_quick_unlock_object_no_inline DEFINE_FUNCTION art_quick_check_instance_of + // Type check using the bit string passes null as the target class. In that case just throw. + testl %esi, %esi + jz .Lthrow_class_cast_exception_for_bitstring_check + // We could check the super classes here but that is usually already checked in the caller. PUSH rdi // Save args for exc PUSH rsi @@ -1426,6 +1430,7 @@ DEFINE_FUNCTION art_quick_check_instance_of POP rsi // Pop arguments POP rdi +.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // save all registers as basis for long jump context mov %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current() call SYMBOL(artThrowClassCastExceptionForObject) // (Object* src, Class* dest, Thread*) diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc index 292bde0272..fe0f876d66 100644 --- a/runtime/base/arena_allocator.cc +++ b/runtime/base/arena_allocator.cc @@ -56,6 +56,7 @@ const char* const ArenaAllocatorStatsImpl::kAllocNames[] = { "CtorFenceIns ", "InvokeInputs ", "PhiInputs ", + "TypeCheckIns ", "LoopInfo ", "LIBackEdges ", "TryCatchInf ", diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h index c3011091e8..688f01b71f 100644 --- a/runtime/base/arena_allocator.h +++ b/runtime/base/arena_allocator.h @@ -62,6 +62,7 @@ enum ArenaAllocKind { kArenaAllocConstructorFenceInputs, kArenaAllocInvokeInputs, kArenaAllocPhiInputs, + kArenaAllocTypeCheckInputs, kArenaAllocLoopInfo, kArenaAllocLoopInfoBackEdges, kArenaAllocTryCatchInfo, diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc index 9b0756b529..ba7fb6b9db 100644 --- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc @@ -16,8 +16,11 @@ #include "art_method-inl.h" #include "callee_save_frame.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_instruction-inl.h" #include "common_throws.h" #include "mirror/object-inl.h" +#include "nth_caller_visitor.h" #include "thread.h" #include "well_known_classes.h" @@ -112,6 +115,26 @@ extern "C" NO_RETURN void artThrowClassCastException(mirror::Class* dest_type, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); + if (dest_type == nullptr) { + // Find the target class for check cast using the bitstring check (dest_type == null). + NthCallerVisitor visitor(self, 0u); + visitor.WalkStack(); + DCHECK(visitor.caller != nullptr); + uint32_t dex_pc = visitor.GetDexPc(); + CodeItemDataAccessor accessor(*visitor.caller->GetDexFile(), visitor.caller->GetCodeItem()); + const Instruction& check_cast = accessor.InstructionAt(dex_pc); + DCHECK_EQ(check_cast.Opcode(), Instruction::CHECK_CAST); + dex::TypeIndex type_index(check_cast.VRegB_21c()); + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + dest_type = linker->LookupResolvedType(type_index, visitor.caller).Ptr(); + CHECK(dest_type != nullptr) << "Target class should have been previously resolved: " + << visitor.caller->GetDexFile()->PrettyType(type_index); + CHECK(!dest_type->IsAssignableFrom(src_type)) + << " " << std::hex << dest_type->PrettyDescriptor() << ";" << dest_type->Depth() + << "/" << dest_type->GetField32(mirror::Class::StatusOffset()) + << " <: " << src_type->PrettyDescriptor() << ";" << src_type->Depth() + << "/" << src_type->GetField32(mirror::Class::StatusOffset()); + } DCHECK(!dest_type->IsAssignableFrom(src_type)); ThrowClassCastException(dest_type, src_type); self->QuickDeliverException(); diff --git a/runtime/subtype_check.h b/runtime/subtype_check.h index 3b1d5f8c4a..a8b4dd5348 100644 --- a/runtime/subtype_check.h +++ b/runtime/subtype_check.h @@ -25,7 +25,7 @@ #include "runtime.h" // Build flag for the bitstring subtype check runtime hooks. -constexpr bool kBitstringSubtypeCheckEnabled = false; +constexpr bool kBitstringSubtypeCheckEnabled = true; /** * Any node in a tree can have its path (from the root to the node) represented as a string by @@ -286,6 +286,17 @@ struct SubtypeCheck { return SubtypeCheckInfo::kUninitialized; } + // Retrieve the state of this class's SubtypeCheckInfo. + // + // Cost: O(Depth(Class)). + // + // Returns: The precise SubtypeCheckInfo::State. + static SubtypeCheckInfo::State GetState(ClassPtr klass) + REQUIRES(Locks::subtype_check_lock_) + REQUIRES_SHARED(Locks::mutator_lock_) { + return GetSubtypeCheckInfo(klass).GetState(); + } + // Retrieve the path to root bitstring as a plain uintN_t value that is amenable to // be used by a fast check "encoded_src & mask_target == encoded_target". // @@ -308,8 +319,9 @@ struct SubtypeCheck { static BitString::StorageType GetEncodedPathToRootForTarget(ClassPtr klass) REQUIRES(Locks::subtype_check_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState()); - return GetSubtypeCheckInfo(klass).GetEncodedPathToRoot(); + SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass); + DCHECK_EQ(SubtypeCheckInfo::kAssigned, sci.GetState()); + return sci.GetEncodedPathToRoot(); } // Retrieve the path to root bitstring mask as a plain uintN_t value that is amenable to @@ -321,8 +333,9 @@ struct SubtypeCheck { static BitString::StorageType GetEncodedPathToRootMask(ClassPtr klass) REQUIRES(Locks::subtype_check_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState()); - return GetSubtypeCheckInfo(klass).GetEncodedPathToRootMask(); + SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass); + DCHECK_EQ(SubtypeCheckInfo::kAssigned, sci.GetState()); + return sci.GetEncodedPathToRootMask(); } // Is the source class a subclass of the target? -- GitLab From 69b1cf13e56a0f192fc40fa38382f072683e063c Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 21 Mar 2018 10:44:58 +0000 Subject: [PATCH 119/749] Don't HARD_FAIL when not able to resolve a quickened opcode. We were failing to report locks being held due to this. This means we can also share the code between the non-quickened and quickened instructions. bug: 74521989 Test: 679-locks Change-Id: Ib87441b80f6ab7706c138600740b8bc7e448f1da --- runtime/verifier/method_verifier.cc | 375 ++++------------------------ runtime/verifier/method_verifier.h | 17 +- test/679-locks/expected.txt | 2 + test/679-locks/info.txt | 2 + test/679-locks/run | 18 ++ test/679-locks/src/Main.java | 50 ++++ test/knownfailures.json | 3 +- 7 files changed, 128 insertions(+), 339 deletions(-) create mode 100644 test/679-locks/expected.txt create mode 100644 test/679-locks/info.txt create mode 100644 test/679-locks/run create mode 100644 test/679-locks/src/Main.java diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index b07001e595..cee717610d 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -2765,47 +2765,61 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { break; case Instruction::IGET_BOOLEAN: + case Instruction::IGET_BOOLEAN_QUICK: VerifyISFieldAccess(inst, reg_types_.Boolean(), true, false); break; case Instruction::IGET_BYTE: + case Instruction::IGET_BYTE_QUICK: VerifyISFieldAccess(inst, reg_types_.Byte(), true, false); break; case Instruction::IGET_CHAR: + case Instruction::IGET_CHAR_QUICK: VerifyISFieldAccess(inst, reg_types_.Char(), true, false); break; case Instruction::IGET_SHORT: + case Instruction::IGET_SHORT_QUICK: VerifyISFieldAccess(inst, reg_types_.Short(), true, false); break; case Instruction::IGET: + case Instruction::IGET_QUICK: VerifyISFieldAccess(inst, reg_types_.Integer(), true, false); break; case Instruction::IGET_WIDE: + case Instruction::IGET_WIDE_QUICK: VerifyISFieldAccess(inst, reg_types_.LongLo(), true, false); break; case Instruction::IGET_OBJECT: + case Instruction::IGET_OBJECT_QUICK: VerifyISFieldAccess(inst, reg_types_.JavaLangObject(false), false, false); break; case Instruction::IPUT_BOOLEAN: + case Instruction::IPUT_BOOLEAN_QUICK: VerifyISFieldAccess(inst, reg_types_.Boolean(), true, false); break; case Instruction::IPUT_BYTE: + case Instruction::IPUT_BYTE_QUICK: VerifyISFieldAccess(inst, reg_types_.Byte(), true, false); break; case Instruction::IPUT_CHAR: + case Instruction::IPUT_CHAR_QUICK: VerifyISFieldAccess(inst, reg_types_.Char(), true, false); break; case Instruction::IPUT_SHORT: + case Instruction::IPUT_SHORT_QUICK: VerifyISFieldAccess(inst, reg_types_.Short(), true, false); break; case Instruction::IPUT: + case Instruction::IPUT_QUICK: VerifyISFieldAccess(inst, reg_types_.Integer(), true, false); break; case Instruction::IPUT_WIDE: + case Instruction::IPUT_WIDE_QUICK: VerifyISFieldAccess(inst, reg_types_.LongLo(), true, false); break; case Instruction::IPUT_OBJECT: + case Instruction::IPUT_OBJECT_QUICK: VerifyISFieldAccess(inst, reg_types_.JavaLangObject(false), false, false); break; @@ -2859,9 +2873,12 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::INVOKE_VIRTUAL: case Instruction::INVOKE_VIRTUAL_RANGE: case Instruction::INVOKE_SUPER: - case Instruction::INVOKE_SUPER_RANGE: { + case Instruction::INVOKE_SUPER_RANGE: + case Instruction::INVOKE_VIRTUAL_QUICK: + case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE || - inst->Opcode() == Instruction::INVOKE_SUPER_RANGE); + inst->Opcode() == Instruction::INVOKE_SUPER_RANGE || + inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK); bool is_super = (inst->Opcode() == Instruction::INVOKE_SUPER || inst->Opcode() == Instruction::INVOKE_SUPER_RANGE); MethodType type = is_super ? METHOD_SUPER : METHOD_VIRTUAL; @@ -2881,7 +2898,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } } if (return_type == nullptr) { - uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + uint32_t method_idx = GetMethodIdxOfInvoke(inst); const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); dex::TypeIndex return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; @@ -3368,67 +3385,6 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } } break; - // Note: the following instructions encode offsets derived from class linking. - // As such they use Class*/Field*/Executable* as these offsets only have - // meaning if the class linking and resolution were successful. - case Instruction::IGET_QUICK: - VerifyQuickFieldAccess(inst, reg_types_.Integer(), true); - break; - case Instruction::IGET_WIDE_QUICK: - VerifyQuickFieldAccess(inst, reg_types_.LongLo(), true); - break; - case Instruction::IGET_OBJECT_QUICK: - VerifyQuickFieldAccess(inst, reg_types_.JavaLangObject(false), false); - break; - case Instruction::IGET_BOOLEAN_QUICK: - VerifyQuickFieldAccess(inst, reg_types_.Boolean(), true); - break; - case Instruction::IGET_BYTE_QUICK: - VerifyQuickFieldAccess(inst, reg_types_.Byte(), true); - break; - case Instruction::IGET_CHAR_QUICK: - VerifyQuickFieldAccess(inst, reg_types_.Char(), true); - break; - case Instruction::IGET_SHORT_QUICK: - VerifyQuickFieldAccess(inst, reg_types_.Short(), true); - break; - case Instruction::IPUT_QUICK: - VerifyQuickFieldAccess(inst, reg_types_.Integer(), true); - break; - case Instruction::IPUT_BOOLEAN_QUICK: - VerifyQuickFieldAccess(inst, reg_types_.Boolean(), true); - break; - case Instruction::IPUT_BYTE_QUICK: - VerifyQuickFieldAccess(inst, reg_types_.Byte(), true); - break; - case Instruction::IPUT_CHAR_QUICK: - VerifyQuickFieldAccess(inst, reg_types_.Char(), true); - break; - case Instruction::IPUT_SHORT_QUICK: - VerifyQuickFieldAccess(inst, reg_types_.Short(), true); - break; - case Instruction::IPUT_WIDE_QUICK: - VerifyQuickFieldAccess(inst, reg_types_.LongLo(), true); - break; - case Instruction::IPUT_OBJECT_QUICK: - VerifyQuickFieldAccess(inst, reg_types_.JavaLangObject(false), false); - break; - case Instruction::INVOKE_VIRTUAL_QUICK: - case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { - bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK); - ArtMethod* called_method = VerifyInvokeVirtualQuickArgs(inst, is_range); - if (called_method != nullptr) { - const char* descriptor = called_method->GetReturnTypeDescriptor(); - const RegType& return_type = reg_types_.FromDescriptor(GetClassLoader(), descriptor, false); - if (!return_type.IsLowHalf()) { - work_line_->SetResultRegisterType(this, return_type); - } else { - work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_)); - } - just_set_result = true; - } - break; - } /* These should never appear during verification. */ case Instruction::UNUSED_3E ... Instruction::UNUSED_43: @@ -3995,7 +3951,7 @@ ArtMethod* MethodVerifier::VerifyInvocationArgsFromIterator( } } else { // Check whether the name of the called method is "" - const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + const uint32_t method_idx = GetMethodIdxOfInvoke(inst); if (strcmp(dex_file_->GetMethodName(dex_file_->GetMethodId(method_idx)), "") != 0) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'this' arg must be initialized"; return nullptr; @@ -4017,7 +3973,7 @@ ArtMethod* MethodVerifier::VerifyInvocationArgsFromIterator( res_method_class = &FromClass(klass->GetDescriptor(&temp), klass, klass->CannotBeAssignedFromOtherTypes()); } else { - const uint32_t method_idx = inst->VRegB(); + const uint32_t method_idx = GetMethodIdxOfInvoke(inst); const dex::TypeIndex class_idx = dex_file_->GetMethodId(method_idx).class_idx_; res_method_class = ®_types_.FromDescriptor( GetClassLoader(), @@ -4108,7 +4064,7 @@ void MethodVerifier::VerifyInvocationArgsUnresolvedMethod(const Instruction* ins // As the method may not have been resolved, make this static check against what we expect. // The main reason for this code block is to fail hard when we find an illegal use, e.g., // wrong number of arguments or wrong primitive types, even if the method could not be resolved. - const uint32_t method_idx = inst->VRegB(); + const uint32_t method_idx = GetMethodIdxOfInvoke(inst); DexFileParameterIterator it(*dex_file_, dex_file_->GetProtoId(dex_file_->GetMethodId(method_idx).proto_idx_)); VerifyInvocationArgsFromIterator(&it, inst, method_type, is_range, nullptr); @@ -4181,7 +4137,7 @@ ArtMethod* MethodVerifier::VerifyInvocationArgs( const Instruction* inst, MethodType method_type, bool is_range) { // Resolve the method. This could be an abstract or concrete method depending on what sort of call // we're making. - const uint32_t method_idx = inst->VRegB(); + const uint32_t method_idx = GetMethodIdxOfInvoke(inst); ArtMethod* res_method = ResolveMethodAndCheckAccess(method_idx, method_type); if (res_method == nullptr) { // error or class is unresolved // Check what we can statically. @@ -4334,122 +4290,34 @@ bool MethodVerifier::CheckSignaturePolymorphicReceiver(const Instruction* inst) return true; } -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); - } - - 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, is_range); - if (res_method == nullptr) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer method from " << inst->Name(); - return nullptr; - } - if (FailOrAbort(!res_method->IsDirect(), - "Quick-invoked method is direct at ", - work_insn_idx_)) { - return nullptr; - } - if (FailOrAbort(!res_method->IsStatic(), - "Quick-invoked method is static at ", - work_insn_idx_)) { - return nullptr; - } - - // We use vAA as our expected arg count, rather than res_method->insSize, because we need to - // match the call to the signature. Also, we might be calling through an abstract method - // definition (which doesn't have register count values). - const RegType& actual_arg_type = work_line_->GetInvocationThis(this, inst); - if (actual_arg_type.IsConflict()) { // GetInvocationThis failed. - return nullptr; - } - const size_t expected_args = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c(); - /* caught by static verifier */ - DCHECK(is_range || expected_args <= 5); - if (expected_args > code_item_accessor_.OutsSize()) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid argument count (" << expected_args - << ") exceeds outsSize (" << code_item_accessor_.OutsSize() << ")"; - return nullptr; - } - - /* - * Check the "this" argument, which must be an instance of the class that declared the method. - * For an interface class, we don't do the full interface merge (see JoinClass), so we can't do a - * rigorous check here (which is okay since we have to do it at runtime). - */ - // Note: given an uninitialized type, this should always fail. Constructors aren't virtual. - if (actual_arg_type.IsUninitializedTypes() && !res_method->IsConstructor()) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'this' arg must be initialized"; - return nullptr; - } - if (!actual_arg_type.IsZeroOrNull()) { - mirror::Class* klass = res_method->GetDeclaringClass(); - std::string temp; - const RegType& res_method_class = - FromClass(klass->GetDescriptor(&temp), klass, klass->CannotBeAssignedFromOtherTypes()); - if (!res_method_class.IsAssignableFrom(actual_arg_type, this)) { - Fail(actual_arg_type.IsUninitializedTypes() // Just overcautious - should have never - ? VERIFY_ERROR_BAD_CLASS_HARD // quickened this. - : actual_arg_type.IsUnresolvedTypes() - ? VERIFY_ERROR_NO_CLASS - : VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type - << "' not instance of '" << res_method_class << "'"; - return nullptr; - } - } - /* - * Process the target method's signature. This signature may or may not - * have been verified, so we can't assume it's properly formed. - */ - const DexFile::TypeList* params = res_method->GetParameterTypeList(); - size_t params_size = params == nullptr ? 0 : params->Size(); - uint32_t arg[5]; - if (!is_range) { - inst->GetVarArgs(arg); - } - size_t actual_args = 1; - for (size_t param_index = 0; param_index < params_size; param_index++) { - if (actual_args >= expected_args) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invalid call to '" - << res_method->PrettyMethod() - << "'. Expected " << expected_args - << " arguments, processing argument " << actual_args - << " (where longs/doubles count twice)."; - return nullptr; - } - const char* descriptor = - res_method->GetTypeDescriptorFromTypeIdx(params->GetTypeItem(param_index).type_idx_); - if (descriptor == nullptr) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation of " - << res_method->PrettyMethod() - << " missing signature component"; - return nullptr; +uint16_t MethodVerifier::GetMethodIdxOfInvoke(const Instruction* inst) { + switch (inst->Opcode()) { + case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: + case Instruction::INVOKE_VIRTUAL_QUICK: { + DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_) + << dex_file_->PrettyMethod(dex_method_idx_, true) << "@" << work_insn_idx_; + DCHECK(method_being_verified_ != nullptr); + uint16_t method_idx = method_being_verified_->GetIndexFromQuickening(work_insn_idx_); + CHECK_NE(method_idx, DexFile::kDexNoIndex16); + return method_idx; } - const RegType& reg_type = reg_types_.FromDescriptor(GetClassLoader(), descriptor, false); - uint32_t get_reg = is_range ? inst->VRegC_3rc() + actual_args : arg[actual_args]; - if (!work_line_->VerifyRegisterType(this, get_reg, reg_type)) { - return res_method; + default: { + return inst->VRegB(); } - actual_args = reg_type.IsLongOrDoubleTypes() ? actual_args + 2 : actual_args + 1; } - if (actual_args != expected_args) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation of " - << res_method->PrettyMethod() << " expected " - << expected_args << " arguments, found " << actual_args; - return nullptr; +} + +uint16_t MethodVerifier::GetFieldIdxOfFieldAccess(const Instruction* inst, bool is_static) { + if (is_static) { + return inst->VRegB_21c(); + } else if (inst->IsQuickened()) { + DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_); + DCHECK(method_being_verified_ != nullptr); + uint16_t field_idx = method_being_verified_->GetIndexFromQuickening(work_insn_idx_); + CHECK_NE(field_idx, DexFile::kDexNoIndex16); + return field_idx; } else { - return res_method; + return inst->VRegC_22c(); } } @@ -4819,7 +4687,7 @@ ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_id template void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType& insn_type, bool is_primitive, bool is_static) { - uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c(); + uint32_t field_idx = GetFieldIdxOfFieldAccess(inst, is_static); ArtField* field; if (is_static) { field = GetStaticField(field_idx); @@ -4972,151 +4840,6 @@ void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType& } } -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 field; -} - -template -void MethodVerifier::VerifyQuickFieldAccess(const Instruction* inst, const RegType& insn_type, - bool is_primitive) { - DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_); - - ArtField* field = GetQuickAccessedField(); - if (field == nullptr) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer field from " << inst->Name(); - return; - } - - // For an IPUT_QUICK, we now test for final flag of the field. - if (kAccType == FieldAccessType::kAccPut) { - if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) { - Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << field->PrettyField() - << " from other class " << GetDeclaringClass(); - return; - } - } - - // Get the field type. - const RegType* field_type; - { - ObjPtr field_type_class = - can_load_classes_ ? field->ResolveType() : field->LookupResolvedType(); - - if (field_type_class != nullptr) { - field_type = &FromClass(field->GetTypeDescriptor(), - field_type_class.Ptr(), - field_type_class->CannotBeAssignedFromOtherTypes()); - } else { - Thread* self = Thread::Current(); - DCHECK(!can_load_classes_ || self->IsExceptionPending()); - self->ClearException(); - field_type = ®_types_.FromDescriptor(field->GetDeclaringClass()->GetClassLoader(), - field->GetTypeDescriptor(), - false); - } - if (field_type == nullptr) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer field type from " << inst->Name(); - return; - } - } - - const uint32_t vregA = inst->VRegA_22c(); - static_assert(kAccType == FieldAccessType::kAccPut || kAccType == FieldAccessType::kAccGet, - "Unexpected third access type"); - if (kAccType == FieldAccessType::kAccPut) { - if (is_primitive) { - // Primitive field assignability rules are weaker than regular assignability rules - bool instruction_compatible; - bool value_compatible; - const RegType& value_type = work_line_->GetRegisterType(this, vregA); - if (field_type->IsIntegralTypes()) { - instruction_compatible = insn_type.IsIntegralTypes(); - value_compatible = value_type.IsIntegralTypes(); - } else if (field_type->IsFloat()) { - instruction_compatible = insn_type.IsInteger(); // no [is]put-float, so expect [is]put-int - value_compatible = value_type.IsFloatTypes(); - } else if (field_type->IsLong()) { - instruction_compatible = insn_type.IsLong(); - value_compatible = value_type.IsLongTypes(); - } else if (field_type->IsDouble()) { - instruction_compatible = insn_type.IsLong(); // no [is]put-double, so expect [is]put-long - value_compatible = value_type.IsDoubleTypes(); - } else { - instruction_compatible = false; // reference field with primitive store - value_compatible = false; // unused - } - if (!instruction_compatible) { - // This is a global failure rather than a class change failure as the instructions and - // the descriptors for the type should have been consistent within the same file at - // compile time - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << ArtField::PrettyField(field) - << " to be of type '" << insn_type - << "' but found type '" << *field_type - << "' in put"; - return; - } - if (!value_compatible) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << vregA - << " of type " << value_type - << " but expected " << *field_type - << " for store to " << ArtField::PrettyField(field) << " in put"; - return; - } - } else { - if (!insn_type.IsAssignableFrom(*field_type, this)) { - Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << ArtField::PrettyField(field) - << " to be compatible with type '" << insn_type - << "' but found type '" << *field_type - << "' in put-object"; - return; - } - work_line_->VerifyRegisterType(this, vregA, *field_type); - } - } else if (kAccType == FieldAccessType::kAccGet) { - if (is_primitive) { - if (field_type->Equals(insn_type) || - (field_type->IsFloat() && insn_type.IsIntegralTypes()) || - (field_type->IsDouble() && insn_type.IsLongTypes())) { - // expected that read is of the correct primitive type or that int reads are reading - // floats or long reads are reading doubles - } else { - // This is a global failure rather than a class change failure as the instructions and - // the descriptors for the type should have been consistent within the same file at - // compile time - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << ArtField::PrettyField(field) - << " to be of type '" << insn_type - << "' but found type '" << *field_type << "' in Get"; - return; - } - } else { - if (!insn_type.IsAssignableFrom(*field_type, this)) { - Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << ArtField::PrettyField(field) - << " to be compatible with type '" << insn_type - << "' but found type '" << *field_type - << "' in get-object"; - work_line_->SetRegisterType(this, vregA, reg_types_.Conflict()); - return; - } - } - if (!field_type->IsLowHalf()) { - work_line_->SetRegisterType(this, vregA, *field_type); - } else { - work_line_->SetRegisterTypeWide(this, vregA, *field_type, field_type->HighHalf(®_types_)); - } - } else { - LOG(FATAL) << "Unexpected case."; - } -} - bool MethodVerifier::CheckNotMoveException(const uint16_t* insns, int insn_idx) { if ((insns[insn_idx] & 0xff) == Instruction::MOVE_EXCEPTION) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid use of move-exception"; diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 9237a8b44b..531d3dabfa 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -209,12 +209,12 @@ 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, bool is_range) + // Returns the method index of an invoke instruction. + uint16_t GetMethodIdxOfInvoke(const Instruction* inst) + REQUIRES_SHARED(Locks::mutator_lock_); + // Returns the field index of a field access instruction. + uint16_t GetFieldIdxOfFieldAccess(const Instruction* inst, bool is_static) 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* GetQuickAccessedField() REQUIRES_SHARED(Locks::mutator_lock_); uint32_t GetEncounteredFailureTypes() { return encountered_failure_types_; @@ -575,10 +575,6 @@ class MethodVerifier { bool is_primitive, bool is_static) REQUIRES_SHARED(Locks::mutator_lock_); - template - void VerifyQuickFieldAccess(const Instruction* inst, const RegType& insn_type, bool is_primitive) - REQUIRES_SHARED(Locks::mutator_lock_); - enum class CheckAccess { // private. kYes, kNo, @@ -642,9 +638,6 @@ class MethodVerifier { ArtMethod* res_method) REQUIRES_SHARED(Locks::mutator_lock_); - ArtMethod* VerifyInvokeVirtualQuickArgs(const Instruction* inst, bool is_range) - REQUIRES_SHARED(Locks::mutator_lock_); - /* * Verify the arguments present for a call site. Returns "true" if all is well, "false" otherwise. */ diff --git a/test/679-locks/expected.txt b/test/679-locks/expected.txt new file mode 100644 index 0000000000..85a20bea2f --- /dev/null +++ b/test/679-locks/expected.txt @@ -0,0 +1,2 @@ +JNI_OnLoad called +MyString diff --git a/test/679-locks/info.txt b/test/679-locks/info.txt new file mode 100644 index 0000000000..7ada4900c6 --- /dev/null +++ b/test/679-locks/info.txt @@ -0,0 +1,2 @@ +Ensure FindLocksAtDexPc is able to pass through quickened instructions related +to unresolved classes. diff --git a/test/679-locks/run b/test/679-locks/run new file mode 100644 index 0000000000..0cc87f3168 --- /dev/null +++ b/test/679-locks/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/679-locks/src/Main.java b/test/679-locks/src/Main.java new file mode 100644 index 0000000000..fbc8c53833 --- /dev/null +++ b/test/679-locks/src/Main.java @@ -0,0 +1,50 @@ +/* + * 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. + */ + +class NotLoaded { + public void foo() {} +} + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[0]); + TestSync.run(); + } + + public static void run() { + testVisitLocks(); + } + + static Object myStatic; + + // Note: declared in 167-visit-locks. + public static native void testVisitLocks(); +} + +// 167-visit-locks/visit-locks.cc looks at the locks held in TestSync.run(). +class TestSync { + public static void run() { + Object o = Main.myStatic; + if (o != null) { + if (o instanceof NotLoaded) { + ((NotLoaded)o).foo(); + } + } + synchronized ("MyString") { + Main.testVisitLocks(); + } + } +} diff --git a/test/knownfailures.json b/test/knownfailures.json index a7e76d131e..22c370a61f 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -954,7 +954,8 @@ }, { "tests": ["616-cha-unloading", - "678-quickening"], + "678-quickening", + "679-locks"], "variant": "jvm", "description": ["Doesn't run on RI."] }, -- GitLab From 071d43576b4a9ede8ef2a4289c5d79b1ae612c7a Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Wed, 21 Mar 2018 14:07:12 -0700 Subject: [PATCH 120/749] Code sinking bug fix. Rationale: Don't move an instruction that can throw into a block that may catch. Bug: b/75971227 Test: test-art-host Change-Id: I29b8f32854aa83e2fb5018d77b9ee12787efbef3 --- compiler/optimizing/code_sinking.cc | 5 ++ test/680-sink-regression/expected.txt | 1 + test/680-sink-regression/info.txt | 1 + test/680-sink-regression/src/Main.java | 87 ++++++++++++++++++++++++++ 4 files changed, 94 insertions(+) create mode 100644 test/680-sink-regression/expected.txt create mode 100644 test/680-sink-regression/info.txt create mode 100644 test/680-sink-regression/src/Main.java diff --git a/compiler/optimizing/code_sinking.cc b/compiler/optimizing/code_sinking.cc index f4760d661f..2e31d35584 100644 --- a/compiler/optimizing/code_sinking.cc +++ b/compiler/optimizing/code_sinking.cc @@ -214,6 +214,11 @@ static HInstruction* FindIdealPosition(HInstruction* instruction, DCHECK(target_block != nullptr); } + // Bail if the instruction can throw and we are about to move into a catch block. + if (instruction->CanThrow() && target_block->GetTryCatchInformation() != nullptr) { + return nullptr; + } + // Find insertion position. No need to filter anymore, as we have found a // target block. HInstruction* insert_pos = nullptr; diff --git a/test/680-sink-regression/expected.txt b/test/680-sink-regression/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/680-sink-regression/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/680-sink-regression/info.txt b/test/680-sink-regression/info.txt new file mode 100644 index 0000000000..547e3b801a --- /dev/null +++ b/test/680-sink-regression/info.txt @@ -0,0 +1 @@ +Regression test for code sinking with exceptions (b/75971227). diff --git a/test/680-sink-regression/src/Main.java b/test/680-sink-regression/src/Main.java new file mode 100644 index 0000000000..642c3abdf4 --- /dev/null +++ b/test/680-sink-regression/src/Main.java @@ -0,0 +1,87 @@ +/* + * 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.io.*; + +/** + * Regression test for b/75971227 (code sinking with exceptions). + */ +public class Main { + + public static class N { + int x; + } + + private int f; + + public int doit(N n1) throws FileNotFoundException { + int x = 1; + N n3 = new N(); + try { + if (n1.x == 0) { + f = 11; + x = 3; + } else { + f = x; + } + throw new FileNotFoundException("n3" + n3.x); + } catch (NullPointerException e) { + } + return x; + } + + + public static void main(String[] args) { + N n = new N(); + Main t = new Main(); + int x = 0; + + // Main 1, null pointer argument. + t.f = 0; + try { + x = t.doit(null); + } catch (FileNotFoundException e) { + x = -1; + } + if (x != 1 || t.f != 0) { + throw new Error("Main 1: x=" + x + " f=" + t.f); + } + + // Main 2, n.x is 0. + n.x = 0; + try { + x = t.doit(n); + } catch (FileNotFoundException e) { + x = -1; + } + if (x != -1 || t.f != 11) { + throw new Error("Main 2: x=" + x + " f=" + t.f); + } + + // Main 3, n.x is not 0. + n.x = 1; + try { + x = t.doit(n); + } catch (FileNotFoundException e) { + x = -1; + } + if (x != -1 || t.f != 1) { + throw new Error("Main 3: x=" + x + " f=" + t.f); + } + + System.out.println("passed"); + } +} -- GitLab From 2286da2d2ff87658a703b5098c106bbcd3b7d218 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Thu, 22 Mar 2018 10:50:22 -0700 Subject: [PATCH 121/749] Refined recognizing integral MIN-MAX-ABS. Rationale: More contextual information is always better. Bug: b/74026074 Test: test-art-host,target Change-Id: I670579423a181b6b6baf1db2440fd56a33ce8771 --- compiler/optimizing/instruction_simplifier.cc | 10 +- test/679-checker-minmax/src/Main.java | 102 ++++++++++ test/681-checker-abs/expected.txt | 1 + test/681-checker-abs/info.txt | 1 + test/681-checker-abs/src/Main.java | 184 ++++++++++++++++++ 5 files changed, 293 insertions(+), 5 deletions(-) create mode 100644 test/681-checker-abs/expected.txt create mode 100644 test/681-checker-abs/info.txt create mode 100644 test/681-checker-abs/src/Main.java diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 2b6f90540f..0b2297d157 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -852,7 +852,7 @@ void InstructionSimplifierVisitor::VisitBooleanNot(HBooleanNot* bool_not) { static HInstruction* NewIntegralAbs(ArenaAllocator* allocator, HInstruction* x, HInstruction* cursor) { - DataType::Type type = x->GetType(); + DataType::Type type = DataType::Kind(x->GetType()); DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64); HAbs* abs = new (allocator) HAbs(type, x, cursor->GetDexPc()); cursor->GetBlock()->InsertInstructionBefore(abs, cursor); @@ -865,7 +865,7 @@ static HInstruction* NewIntegralMinMax(ArenaAllocator* allocator, HInstruction* y, HInstruction* cursor, bool is_min) { - DataType::Type type = x->GetType(); + DataType::Type type = DataType::Kind(x->GetType()); DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64); HBinaryOperation* minmax = nullptr; if (is_min) { @@ -939,9 +939,9 @@ void InstructionSimplifierVisitor::VisitSelect(HSelect* select) { DataType::Type t_type = true_value->GetType(); DataType::Type f_type = false_value->GetType(); // Here we have a b ? true_value : false_value. - // 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)) { + // Test if both values are compatible integral types (resulting + // MIN/MAX/ABS type will be int or long, like the condition). + if (DataType::IsIntegralType(t_type) && DataType::Kind(t_type) == DataType::Kind(f_type)) { // Try to replace typical integral MIN/MAX/ABS constructs. if ((cmp == kCondLT || cmp == kCondLE || cmp == kCondGT || cmp == kCondGE) && ((a == true_value && b == false_value) || diff --git a/test/679-checker-minmax/src/Main.java b/test/679-checker-minmax/src/Main.java index d016de6686..38085bbd7b 100644 --- a/test/679-checker-minmax/src/Main.java +++ b/test/679-checker-minmax/src/Main.java @@ -79,6 +79,51 @@ public class Main { return a >= b ? b : a; } + /// CHECK-START: int Main.min5(short, short) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> LessThan [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.min5(short, short) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> Min + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.min5(short, short) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int min5(short a, short b) { + return a >= b ? b : a; + } + + /// CHECK-START: int Main.min6(byte, byte) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> LessThan [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.min6(byte, byte) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> Min + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.min6(byte, byte) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int min6(byte a, byte b) { + return a >= b ? b : a; + } + + /// CHECK-START: long Main.min7(long, long) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> LessThan [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: long Main.min7(long, long) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> Min + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: long Main.min7(long, long) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static long min7(long a, long b) { + return a >= b ? b : a; + } + /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_inlining (before) /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] @@ -139,15 +184,66 @@ public class Main { return a >= b ? a : b; } + /// CHECK-START: int Main.max5(short, short) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> LessThan [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.max5(short, short) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> Max + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.max5(short, short) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int max5(short a, short b) { + return a >= b ? a : b; + } + + /// CHECK-START: int Main.max6(byte, byte) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> LessThan [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.max6(byte, byte) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> Max + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.max6(byte, byte) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int max6(byte a, byte b) { + return a >= b ? a : b; + } + + /// CHECK-START: long Main.max7(long, long) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> LessThan [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: long Main.max7(long, long) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> Max + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: long Main.max7(long, long) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static long max7(long a, long 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(10, min5((short) 10, (short) 20)); + expectEquals(10, min6((byte) 10, (byte) 20)); + expectEquals(10L, min7(10L, 20L)); expectEquals(20, max1(10, 20)); expectEquals(20, max2(10, 20)); expectEquals(20, max3(10, 20)); expectEquals(20, max4(10, 20)); + expectEquals(20, max5((short) 10, (short) 20)); + expectEquals(20, max6((byte) 10, (byte) 20)); + expectEquals(20L, max7(10L, 20L)); System.out.println("passed"); } @@ -156,4 +252,10 @@ public class Main { throw new Error("Expected: " + expected + ", found: " + result); } } + + private static void expectEquals(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } } diff --git a/test/681-checker-abs/expected.txt b/test/681-checker-abs/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/681-checker-abs/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/681-checker-abs/info.txt b/test/681-checker-abs/info.txt new file mode 100644 index 0000000000..d36e76e504 --- /dev/null +++ b/test/681-checker-abs/info.txt @@ -0,0 +1 @@ +Functional tests on detecting abs. diff --git a/test/681-checker-abs/src/Main.java b/test/681-checker-abs/src/Main.java new file mode 100644 index 0000000000..8064b1dac1 --- /dev/null +++ b/test/681-checker-abs/src/Main.java @@ -0,0 +1,184 @@ +/* + * 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 abs. + */ +public class Main { + + /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] + /// CHECK-DAG: <> [<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> Abs [<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int abs1(int a) { + return a < 0 ? -a : a; + } + + /// CHECK-START: int Main.abs2(int) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> GreaterThan [<>,<>] + /// CHECK-DAG: <> [<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.abs2(int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> Abs [<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.abs2(int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int abs2(int a) { + return a <= 0 ? -a : a; + } + + /// CHECK-START: int Main.abs3(int) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> LessThanOrEqual [<>,<>] + /// CHECK-DAG: <> [<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.abs3(int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> Abs [<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.abs3(int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int abs3(int a) { + return a > 0 ? a : -a; + } + + /// CHECK-START: int Main.abs4(int) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> LessThan [<>,<>] + /// CHECK-DAG: <> [<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.abs4(int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> Abs [<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.abs4(int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int abs4(int a) { + return a >= 0 ? a : -a; + } + + /// CHECK-START: int Main.abs5(short) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> LessThan [<>,<>] + /// CHECK-DAG: <> [<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.abs5(short) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> Abs [<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.abs5(short) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int abs5(short a) { + return a >= 0 ? a : -a; + } + + /// CHECK-START: int Main.abs6(byte) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> LessThan [<>,<>] + /// CHECK-DAG: <> [<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.abs6(byte) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> Abs [<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.abs6(byte) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int abs6(byte a) { + return a >= 0 ? a : -a; + } + + /// CHECK-START: long Main.abs7(long) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> LongConstant 0 + /// CHECK-DAG: <> LessThan [<>,<>] + /// CHECK-DAG: <> [<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: long Main.abs7(long) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> Abs [<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: long Main.abs7(long) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static long abs7(long a) { + return a >= 0 ? a : -a; + } + + public static void main(String[] args) { + expectEquals(10, abs1(-10)); + expectEquals(20, abs1(20)); + expectEquals(10, abs2(-10)); + expectEquals(20, abs2(20)); + expectEquals(10, abs3(-10)); + expectEquals(20, abs3(20)); + expectEquals(10, abs4(-10)); + expectEquals(20, abs4(20)); + expectEquals(10, abs4((short) -10)); + expectEquals(20, abs4((short) 20)); + expectEquals(10, abs6((byte) -10)); + expectEquals(20, abs6((byte) 20)); + expectEquals(10L, abs7(-10L)); + expectEquals(20L, abs7(20L)); + System.out.println("passed"); + } + + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + private static void expectEquals(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} -- GitLab From 14e7badae4f118ecd8481913c927316737fd21fa Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 22 Mar 2018 14:33:20 -0700 Subject: [PATCH 122/749] Verify dex files in zip and from files are not CompactDex Since there is no CompactDex verifier, dex2oat may crash for invalid CompactDex files in the APK or directly as files. Disallow opening these to prevent crashes. Bug: 75967391 Bug: 63756964 Test: test-art-host-gtest Change-Id: Ifc86f7bc2a478201473aad6481bf1e3435a910ae --- dex2oat/dex2oat_test.cc | 47 ++++++++++++++++++++++++++++++ runtime/dex/art_dex_file_loader.cc | 17 +++++++++++ 2 files changed, 64 insertions(+) diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 5e9782aadf..0cd39ac11b 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -2046,4 +2046,51 @@ TEST_F(Dex2oatTest, CompactDexInvalidSource) { ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) != 0) << status << " " << output_; } +// Test that dex2oat with a CompactDex file in the APK fails. +TEST_F(Dex2oatTest, CompactDexInZip) { + CompactDexFile::Header header = {}; + CompactDexFile::WriteMagic(header.magic_); + CompactDexFile::WriteCurrentVersion(header.magic_); + header.file_size_ = sizeof(CompactDexFile::Header); + header.data_off_ = 10 * MB; + header.map_off_ = 10 * MB; + header.class_defs_off_ = 10 * MB; + header.class_defs_size_ = 10000; + // Create a zip containing the invalid dex. + ScratchFile invalid_dex_zip; + { + FILE* file = fdopen(invalid_dex_zip.GetFd(), "w+b"); + ZipWriter writer(file); + writer.StartEntry("classes.dex", ZipWriter::kCompress); + ASSERT_GE(writer.WriteBytes(&header, sizeof(header)), 0); + writer.FinishEntry(); + writer.Finish(); + ASSERT_EQ(invalid_dex_zip.GetFile()->Flush(), 0); + } + // Create the dex file directly. + ScratchFile invalid_dex; + { + ASSERT_GE(invalid_dex.GetFile()->WriteFully(&header, sizeof(header)), 0); + ASSERT_EQ(invalid_dex.GetFile()->Flush(), 0); + } + std::string error_msg; + int status = 0u; + + status = GenerateOdexForTestWithStatus( + { invalid_dex_zip.GetFilename() }, + GetOdexDir() + "/output_apk.odex", + CompilerFilter::kQuicken, + &error_msg, + { "--compact-dex-level=fast" }); + ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) != 0) << status << " " << output_; + + status = GenerateOdexForTestWithStatus( + { invalid_dex.GetFilename() }, + GetOdexDir() + "/output.odex", + CompilerFilter::kQuicken, + &error_msg, + { "--compact-dex-level=fast" }); + ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) != 0) << status << " " << output_; +} + } // namespace art diff --git a/runtime/dex/art_dex_file_loader.cc b/runtime/dex/art_dex_file_loader.cc index c456764834..9802c6904b 100644 --- a/runtime/dex/art_dex_file_loader.cc +++ b/runtime/dex/art_dex_file_loader.cc @@ -205,6 +205,12 @@ std::unique_ptr ArtDexFileLoader::Open(const std::string& locatio error_msg, std::make_unique(std::move(map)), /*verify_result*/ nullptr); + // Opening CompactDex is only supported from vdex files. + if (dex_file != nullptr && dex_file->IsCompactDexFile()) { + *error_msg = StringPrintf("Opening CompactDex file '%s' is only supported from vdex files", + location.c_str()); + return nullptr; + } return dex_file; } @@ -329,6 +335,12 @@ std::unique_ptr ArtDexFileLoader::OpenFile(int fd, std::make_unique(std::move(map)), /*verify_result*/ nullptr); + // Opening CompactDex is only supported from vdex files. + if (dex_file != nullptr && dex_file->IsCompactDexFile()) { + *error_msg = StringPrintf("Opening CompactDex file '%s' is only supported from vdex files", + location.c_str()); + return nullptr; + } return dex_file; } @@ -397,6 +409,11 @@ std::unique_ptr ArtDexFileLoader::OpenOneDexFileFromZip( error_msg, std::make_unique(std::move(map)), &verify_result); + if (dex_file != nullptr && dex_file->IsCompactDexFile()) { + *error_msg = StringPrintf("Opening CompactDex file '%s' is only supported from vdex files", + location.c_str()); + return nullptr; + } if (dex_file == nullptr) { if (verify_result == VerifyResult::kVerifyNotAttempted) { *error_code = ZipOpenErrorCode::kDexFileError; -- GitLab From 597d7f650b0656fcb3985b01f53284717b41e5cc Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Thu, 22 Mar 2018 11:36:47 +0000 Subject: [PATCH 123/749] More flexible API enforcement policy support. This CL adds the ability to configure which banned API lists to enforce, defined by new enum hiddenapi::ApiEnforcementPolicy. Currently, the policy can be set at zygote fork time, but not at dex optimization time where blacklist enforcement is still assumed. As such, making the policy more strict will not work as expected yet. This will be improved in a follow up CL. Test: art tests pass Test: Device boots BUG: 73337509 (cherry-picked from commit 159f596eec01adbb5a1c9654402c137cdb943131) Change-Id: I6c319bb8a3000cb1d3c4693b4fb196e749c36d96 Merged-In: I33f9afce628a86727e400052f4d5979d3536da8c --- runtime/hidden_api.h | 69 ++++++++++++++++----- runtime/native/dalvik_system_ZygoteHooks.cc | 49 ++++++++------- runtime/native/java_lang_Class.cc | 4 +- runtime/runtime.cc | 13 ++-- runtime/runtime.h | 14 +++-- runtime/well_known_classes.cc | 9 +-- test/674-hiddenapi/hiddenapi.cc | 3 +- 7 files changed, 107 insertions(+), 54 deletions(-) diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index f2ea2fdaaa..321d55d9b7 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -27,6 +27,23 @@ namespace art { namespace hiddenapi { +// Hidden API enforcement policy +// This must be kept in sync with ApplicationInfo.ApiEnforcementPolicy in +// frameworks/base/core/java/android/content/pm/ApplicationInfo.java +enum class EnforcementPolicy { + kNoChecks = 0, + kAllLists = 1, // ban anything but whitelist + kDarkGreyAndBlackList = 2, // ban dark grey & blacklist + kBlacklistOnly = 3, // ban blacklist violations only + kMax = kBlacklistOnly, +}; + +inline EnforcementPolicy EnforcementPolicyFromInt(int api_policy_int) { + DCHECK_GE(api_policy_int, 0); + DCHECK_LE(api_policy_int, static_cast(EnforcementPolicy::kMax)); + return static_cast(api_policy_int); +} + enum Action { kAllow, kAllowButWarn, @@ -59,16 +76,38 @@ inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { return os; } +static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags::ApiList apiList) { + return static_cast(policy) == static_cast(apiList); +} + inline Action GetMemberAction(uint32_t access_flags) { - switch (HiddenApiAccessFlags::DecodeFromRuntime(access_flags)) { - case HiddenApiAccessFlags::kWhitelist: - return kAllow; - case HiddenApiAccessFlags::kLightGreylist: - return kAllowButWarn; - case HiddenApiAccessFlags::kDarkGreylist: - return kAllowButWarnAndToast; - case HiddenApiAccessFlags::kBlacklist: - return kDeny; + EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); + if (policy == EnforcementPolicy::kNoChecks) { + // Exit early. Nothing to enforce. + return kAllow; + } + + HiddenApiAccessFlags::ApiList api_list = HiddenApiAccessFlags::DecodeFromRuntime(access_flags); + if (api_list == HiddenApiAccessFlags::kWhitelist) { + return kAllow; + } + // The logic below relies on equality of values in the enums EnforcementPolicy and + // HiddenApiAccessFlags::ApiList, and their ordering. Assert that this is as expected. + static_assert( + EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) && + EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) && + EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist), + "Mismatch between EnforcementPolicy and ApiList enums"); + static_assert( + EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList && + EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly, + "EnforcementPolicy values ordering not correct"); + if (static_cast(policy) > static_cast(api_list)) { + return api_list == HiddenApiAccessFlags::kDarkGreylist + ? kAllowButWarnAndToast + : kAllowButWarn; + } else { + return kDeny; } } @@ -107,12 +146,6 @@ inline bool ShouldBlockAccessToMember(T* member, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); - Runtime* runtime = Runtime::Current(); - - if (!runtime->AreHiddenApiChecksEnabled()) { - // Exit early. Nothing to enforce. - return false; - } Action action = GetMemberAction(member->GetAccessFlags()); if (action == kAllow) { @@ -133,14 +166,16 @@ inline bool ShouldBlockAccessToMember(T* member, // We do this regardless of whether we block the access or not. WarnAboutMemberAccess(member, access_method); - // Block access if on blacklist. if (action == kDeny) { + // Block access return true; } // Allow access to this member but print a warning. DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast); + Runtime* runtime = Runtime::Current(); + // Depending on a runtime flag, we might move the member into whitelist and // skip the warning the next time the member is accessed. if (runtime->ShouldDedupeHiddenApiWarnings()) { @@ -150,7 +185,7 @@ inline bool ShouldBlockAccessToMember(T* member, // If this action requires a UI warning, set the appropriate flag. if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) { - Runtime::Current()->SetPendingHiddenApiWarning(true); + runtime->SetPendingHiddenApiWarning(true); } return false; diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index 89135698e3..cbc2aeb41f 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -162,19 +162,24 @@ static void CollectNonDebuggableClasses() REQUIRES(!Locks::mutator_lock_) { // Must match values in com.android.internal.os.Zygote. enum { - DEBUG_ENABLE_JDWP = 1, - DEBUG_ENABLE_CHECKJNI = 1 << 1, - DEBUG_ENABLE_ASSERT = 1 << 2, - DEBUG_ENABLE_SAFEMODE = 1 << 3, - DEBUG_ENABLE_JNI_LOGGING = 1 << 4, - DEBUG_GENERATE_DEBUG_INFO = 1 << 5, - DEBUG_ALWAYS_JIT = 1 << 6, - DEBUG_NATIVE_DEBUGGABLE = 1 << 7, - DEBUG_JAVA_DEBUGGABLE = 1 << 8, - DISABLE_VERIFIER = 1 << 9, - ONLY_USE_SYSTEM_OAT_FILES = 1 << 10, - ENABLE_HIDDEN_API_CHECKS = 1 << 11, - DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12, + DEBUG_ENABLE_JDWP = 1, + DEBUG_ENABLE_CHECKJNI = 1 << 1, + DEBUG_ENABLE_ASSERT = 1 << 2, + DEBUG_ENABLE_SAFEMODE = 1 << 3, + DEBUG_ENABLE_JNI_LOGGING = 1 << 4, + DEBUG_GENERATE_DEBUG_INFO = 1 << 5, + DEBUG_ALWAYS_JIT = 1 << 6, + DEBUG_NATIVE_DEBUGGABLE = 1 << 7, + DEBUG_JAVA_DEBUGGABLE = 1 << 8, + DISABLE_VERIFIER = 1 << 9, + ONLY_USE_SYSTEM_OAT_FILES = 1 << 10, + DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 11, + HIDDEN_API_ENFORCEMENT_POLICY_MASK = (1 << 12) + | (1 << 13), + + // bits to shift (flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) by to get a value + // corresponding to hiddenapi::EnforcementPolicy + API_ENFORCEMENT_POLICY_SHIFT = CTZ(HIDDEN_API_ENFORCEMENT_POLICY_MASK), }; static uint32_t EnableDebugFeatures(uint32_t runtime_flags) { @@ -285,7 +290,8 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, // Our system thread ID, etc, has changed so reset Thread state. thread->InitAfterFork(); runtime_flags = EnableDebugFeatures(runtime_flags); - bool do_hidden_api_checks = false; + hiddenapi::EnforcementPolicy api_enforcement_policy = hiddenapi::EnforcementPolicy::kNoChecks; + bool dedupe_hidden_api_warnings = true; if ((runtime_flags & DISABLE_VERIFIER) != 0) { Runtime::Current()->DisableVerifier(); @@ -297,10 +303,9 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, runtime_flags &= ~ONLY_USE_SYSTEM_OAT_FILES; } - if ((runtime_flags & ENABLE_HIDDEN_API_CHECKS) != 0) { - do_hidden_api_checks = true; - runtime_flags &= ~ENABLE_HIDDEN_API_CHECKS; - } + api_enforcement_policy = hiddenapi::EnforcementPolicyFromInt( + (runtime_flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT); + runtime_flags &= ~HIDDEN_API_ENFORCEMENT_POLICY_MASK; if (runtime_flags != 0) { LOG(ERROR) << StringPrintf("Unknown bits set in runtime_flags: %#x", runtime_flags); @@ -351,11 +356,13 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, } } + bool do_hidden_api_checks = api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks; DCHECK(!(is_system_server && do_hidden_api_checks)) - << "SystemServer should be forked with ENABLE_HIDDEN_API_CHECKS"; + << "SystemServer should be forked with EnforcementPolicy::kDisable"; DCHECK(!(is_zygote && do_hidden_api_checks)) - << "Child zygote processes should be forked with ENABLE_HIDDEN_API_CHECKS"; - Runtime::Current()->SetHiddenApiChecksEnabled(do_hidden_api_checks); + << "Child zygote processes should be forked with EnforcementPolicy::kDisable"; + Runtime::Current()->SetHiddenApiEnforcementPolicy(api_enforcement_policy); + Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe_hidden_api_warnings); // Clear the hidden API warning flag, in case it was set. Runtime::Current()->SetPendingHiddenApiWarning(false); diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 25d50376de..fc61c9597e 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -89,8 +89,8 @@ static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator // access hidden APIs. This can be *very* expensive. Never call this in a loop. ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - return Runtime::Current()->AreHiddenApiChecksEnabled() && - !IsCallerInBootClassPath(self); + hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); + return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInBootClassPath(self); } // Returns true if the first non-ClassClass caller up the stack should not be diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 7d9d3426fc..53982ae833 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -267,7 +267,7 @@ Runtime::Runtime() oat_file_manager_(nullptr), is_low_memory_mode_(false), safe_mode_(false), - do_hidden_api_checks_(false), + hidden_api_policy_(hiddenapi::EnforcementPolicy::kNoChecks), pending_hidden_api_warning_(false), dedupe_hidden_api_warnings_(true), always_set_hidden_api_warning_flag_(false), @@ -1196,9 +1196,14 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // by default and we only enable them if: // (a) runtime was started with a flag that enables the checks, or // (b) Zygote forked a new process that is not exempt (see ZygoteHooks). - do_hidden_api_checks_ = runtime_options.Exists(Opt::HiddenApiChecks); - DCHECK(!is_zygote_ || !do_hidden_api_checks_) - << "Zygote should not be started with hidden API checks"; + bool do_hidden_api_checks = runtime_options.Exists(Opt::HiddenApiChecks); + DCHECK(!is_zygote_ || !do_hidden_api_checks); + // TODO pass the actual enforcement policy in, rather than just a single bit. + // As is, we're encoding some logic here about which specific policy to use, which would be better + // controlled by the framework. + hidden_api_policy_ = do_hidden_api_checks + ? hiddenapi::EnforcementPolicy::kBlacklistOnly + : hiddenapi::EnforcementPolicy::kNoChecks; no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain); force_native_bridge_ = runtime_options.Exists(Opt::ForceNativeBridge); diff --git a/runtime/runtime.h b/runtime/runtime.h index c7f650ea3f..dba31b2939 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -49,6 +49,10 @@ class AbstractSystemWeakHolder; class Heap; } // namespace gc +namespace hiddenapi { +enum class EnforcementPolicy; +} // namespace hiddenapi + namespace jit { class Jit; class JitOptions; @@ -520,12 +524,12 @@ class Runtime { bool IsVerificationEnabled() const; bool IsVerificationSoftFail() const; - void SetHiddenApiChecksEnabled(bool value) { - do_hidden_api_checks_ = value; + void SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy policy) { + hidden_api_policy_ = policy; } - bool AreHiddenApiChecksEnabled() const { - return do_hidden_api_checks_; + hiddenapi::EnforcementPolicy GetHiddenApiEnforcementPolicy() const { + return hidden_api_policy_; } void SetPendingHiddenApiWarning(bool value) { @@ -990,7 +994,7 @@ class Runtime { bool safe_mode_; // Whether access checks on hidden API should be performed. - bool do_hidden_api_checks_; + hiddenapi::EnforcementPolicy hidden_api_policy_; // Whether the application has used an API which is not restricted but we // should issue a warning about it. diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 67ea64be74..bf36ccf0fa 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -24,6 +24,7 @@ #include #include "entrypoints/quick/quick_entrypoints_enum.h" +#include "hidden_api.h" #include "jni_internal.h" #include "mirror/class.h" #include "mirror/throwable.h" @@ -287,17 +288,17 @@ class ScopedHiddenApiExemption { public: explicit ScopedHiddenApiExemption(Runtime* runtime) : runtime_(runtime), - initially_enabled_(runtime_->AreHiddenApiChecksEnabled()) { - runtime_->SetHiddenApiChecksEnabled(false); + initial_policy_(runtime_->GetHiddenApiEnforcementPolicy()) { + runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks); } ~ScopedHiddenApiExemption() { - runtime_->SetHiddenApiChecksEnabled(initially_enabled_); + runtime_->SetHiddenApiEnforcementPolicy(initial_policy_); } private: Runtime* runtime_; - const bool initially_enabled_; + const hiddenapi::EnforcementPolicy initial_policy_; DISALLOW_COPY_AND_ASSIGN(ScopedHiddenApiExemption); }; diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc index effa37ade4..04c3fbf03a 100644 --- a/test/674-hiddenapi/hiddenapi.cc +++ b/test/674-hiddenapi/hiddenapi.cc @@ -16,6 +16,7 @@ #include "class_linker.h" #include "dex/art_dex_file_loader.h" +#include "hidden_api.h" #include "jni.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" @@ -27,7 +28,7 @@ namespace Test674HiddenApi { extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) { Runtime* runtime = Runtime::Current(); - runtime->SetHiddenApiChecksEnabled(true); + runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly); runtime->SetDedupeHiddenApiWarnings(false); runtime->AlwaysSetHiddenApiWarningFlag(); } -- GitLab From 88591fe82f499de10591f5b77efac71f8954eae2 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Tue, 6 Mar 2018 13:35:43 +0000 Subject: [PATCH 124/749] ART: Simplify atomic.h Prefer std::atomic operations over wrappers in atomic.h. Exceptions are cases that relate to the Java data memory operations and CAS operations. Bug: 71621075 Test: art/test.py --host -j32 Test: art/test.py --target --64 -j4 Change-Id: I9a157e9dede852c1b2aa67d22e3e604a68a9ef1c --- compiler/driver/compiler_driver.cc | 9 +- compiler/utils/atomic_dex_ref_map-inl.h | 10 +- libartbase/base/allocator.cc | 4 +- libartbase/base/allocator.h | 6 +- libartbase/base/atomic.h | 156 ++---------------- runtime/barrier_test.cc | 10 +- runtime/base/mutex-inl.h | 16 +- runtime/base/mutex.cc | 123 +++++++------- runtime/base/mutex.h | 2 +- runtime/class_table-inl.h | 4 +- runtime/class_table.h | 6 +- runtime/gc/accounting/atomic_stack.h | 45 ++--- runtime/gc/accounting/bitmap-inl.h | 2 +- runtime/gc/accounting/card_table-inl.h | 2 +- runtime/gc/accounting/space_bitmap-inl.h | 15 +- runtime/gc/accounting/space_bitmap.cc | 5 +- runtime/gc/collector/concurrent_copying-inl.h | 4 +- runtime/gc/collector/concurrent_copying.cc | 97 +++++------ runtime/gc/collector/mark_sweep.cc | 70 ++++---- runtime/gc/heap-inl.h | 6 +- runtime/gc/heap.cc | 88 +++++----- runtime/gc/heap.h | 10 +- runtime/gc/space/bump_pointer_space-inl.h | 16 +- runtime/gc/space/bump_pointer_space.cc | 12 +- runtime/gc/space/bump_pointer_space.h | 4 +- runtime/gc/space/image_space.cc | 2 +- runtime/gc/space/region_space-inl.h | 8 +- runtime/gc/space/region_space.cc | 6 +- runtime/gc/space/region_space.h | 16 +- runtime/gc/space/space.h | 4 +- runtime/gc/space/zygote_space.cc | 2 +- runtime/gc/space/zygote_space.h | 2 +- runtime/gc/task_processor_test.cc | 20 +-- runtime/java_vm_ext.cc | 8 +- runtime/jdwp/jdwp_handler.cc | 4 +- runtime/jdwp/jdwp_main.cc | 2 +- runtime/jit/jit_code_cache.cc | 10 +- runtime/mirror/dex_cache-inl.h | 2 +- runtime/mirror/object-inl.h | 6 +- runtime/mirror/object.cc | 19 ++- runtime/mirror/object.h | 4 +- runtime/mirror/object_reference.h | 4 +- runtime/monitor.cc | 4 +- runtime/monitor.h | 2 +- runtime/read_barrier-inl.h | 2 +- runtime/thread-inl.h | 1 + runtime/thread.cc | 20 +-- runtime/thread.h | 6 +- runtime/thread_list.cc | 6 +- runtime/thread_pool_test.cc | 8 +- runtime/trace.cc | 16 +- 51 files changed, 404 insertions(+), 502 deletions(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index bd3a145368..83532fdd45 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1555,7 +1555,7 @@ class ParallelCompilationManager { self->AssertNoPendingException(); CHECK_GT(work_units, 0U); - index_.StoreRelaxed(begin); + index_.store(begin, std::memory_order_relaxed); for (size_t i = 0; i < work_units; ++i) { thread_pool_->AddTask(self, new ForAllClosureLambda(this, end, fn)); } @@ -1573,7 +1573,7 @@ class ParallelCompilationManager { } size_t NextIndex() { - return index_.FetchAndAddSequentiallyConsistent(1); + return index_.fetch_add(1, std::memory_order_seq_cst); } private: @@ -2837,7 +2837,8 @@ void CompilerDriver::AddCompiledMethod(const MethodReference& method_ref, /*expected*/ nullptr, compiled_method); CHECK(result == MethodTable::kInsertResultSuccess); - non_relative_linker_patch_count_.FetchAndAddRelaxed(non_relative_linker_patch_count); + non_relative_linker_patch_count_.fetch_add(non_relative_linker_patch_count, + std::memory_order_relaxed); DCHECK(GetCompiledMethod(method_ref) != nullptr) << method_ref.PrettyMethod(); } @@ -2948,7 +2949,7 @@ bool CompilerDriver::IsMethodVerifiedWithoutFailures(uint32_t method_idx, } size_t CompilerDriver::GetNonRelativeLinkerPatchCount() const { - return non_relative_linker_patch_count_.LoadRelaxed(); + return non_relative_linker_patch_count_.load(std::memory_order_relaxed); } void CompilerDriver::SetRequiresConstructorBarrier(Thread* self, diff --git a/compiler/utils/atomic_dex_ref_map-inl.h b/compiler/utils/atomic_dex_ref_map-inl.h index 7977e8201f..4bd323dadb 100644 --- a/compiler/utils/atomic_dex_ref_map-inl.h +++ b/compiler/utils/atomic_dex_ref_map-inl.h @@ -70,7 +70,7 @@ inline bool AtomicDexRefMap::Get(const DexFileRefer if (array == nullptr) { return false; } - *out = (*array)[ref.index].LoadRelaxed(); + *out = (*array)[ref.index].load(std::memory_order_relaxed); return true; } @@ -81,8 +81,8 @@ inline bool AtomicDexRefMap::Remove(const DexFileRe if (array == nullptr) { return false; } - *out = (*array)[ref.index].LoadRelaxed(); - (*array)[ref.index].StoreSequentiallyConsistent(nullptr); + *out = (*array)[ref.index].load(std::memory_order_relaxed); + (*array)[ref.index].store(nullptr, std::memory_order_seq_cst); return true; } @@ -121,7 +121,7 @@ inline void AtomicDexRefMap::Visit(const Visitor& v const DexFile* dex_file = pair.first; const ElementArray& elements = pair.second; for (size_t i = 0; i < elements.size(); ++i) { - visitor(DexFileReference(dex_file, i), elements[i].LoadRelaxed()); + visitor(DexFileReference(dex_file, i), elements[i].load(std::memory_order_relaxed)); } } } @@ -130,7 +130,7 @@ template inline void AtomicDexRefMap::ClearEntries() { for (auto& it : arrays_) { for (auto& element : it.second) { - element.StoreRelaxed(nullptr); + element.store(nullptr, std::memory_order_relaxed); } } } diff --git a/libartbase/base/allocator.cc b/libartbase/base/allocator.cc index a42414507b..17da789b36 100644 --- a/libartbase/base/allocator.cc +++ b/libartbase/base/allocator.cc @@ -83,9 +83,9 @@ void Dump(std::ostream& os) { if (kEnableTrackingAllocator) { os << "Dumping native memory usage\n"; for (size_t i = 0; i < kAllocatorTagCount; ++i) { - uint64_t bytes_used = g_bytes_used[i].LoadRelaxed(); + uint64_t bytes_used = g_bytes_used[i].load(std::memory_order_relaxed); uint64_t max_bytes_used = g_max_bytes_used[i]; - uint64_t total_bytes_used = g_total_bytes_used[i].LoadRelaxed(); + uint64_t total_bytes_used = g_total_bytes_used[i].load(std::memory_order_relaxed); if (total_bytes_used != 0) { os << static_cast(i) << " active=" << bytes_used << " max=" << max_bytes_used << " total=" << total_bytes_used << "\n"; diff --git a/libartbase/base/allocator.h b/libartbase/base/allocator.h index d92fe193e6..7ddbacf716 100644 --- a/libartbase/base/allocator.h +++ b/libartbase/base/allocator.h @@ -84,15 +84,15 @@ extern Atomic g_total_bytes_used[kAllocatorTagCount]; void Dump(std::ostream& os); inline void RegisterAllocation(AllocatorTag tag, size_t bytes) { - g_total_bytes_used[tag].FetchAndAddSequentiallyConsistent(bytes); - size_t new_bytes = g_bytes_used[tag].FetchAndAddSequentiallyConsistent(bytes) + bytes; + g_total_bytes_used[tag].fetch_add(bytes, std::memory_order_seq_cst); + size_t new_bytes = g_bytes_used[tag].fetch_add(bytes, std::memory_order_seq_cst) + bytes; if (g_max_bytes_used[tag] < new_bytes) { g_max_bytes_used[tag] = new_bytes; } } inline void RegisterFree(AllocatorTag tag, size_t bytes) { - g_bytes_used[tag].FetchAndSubSequentiallyConsistent(bytes); + g_bytes_used[tag].fetch_sub(bytes, std::memory_order_seq_cst); } } // namespace TrackedAllocators diff --git a/libartbase/base/atomic.h b/libartbase/base/atomic.h index fd34cc6143..f736667ca8 100644 --- a/libartbase/base/atomic.h +++ b/libartbase/base/atomic.h @@ -35,94 +35,28 @@ class PACKED(sizeof(T)) Atomic : public std::atomic { explicit Atomic(T value) : std::atomic(value) { } - // Load from memory without ordering or synchronization constraints. - T LoadRelaxed() const { - return this->load(std::memory_order_relaxed); - } - - // Load from memory with acquire ordering. - T LoadAcquire() const { - return this->load(std::memory_order_acquire); - } - - // Word tearing allowed, but may race. - // TODO: Optimize? - // There has been some discussion of eventually disallowing word - // tearing for Java data loads. + // Load data from an atomic variable with Java data memory order semantics. + // + // Promises memory access semantics of ordinary Java data. + // Does not order other memory accesses. + // Long and double accesses may be performed 32 bits at a time. + // There are no "cache coherence" guarantees; e.g. loads from the same location may be reordered. + // In contrast to normal C++ accesses, racing accesses are allowed. T LoadJavaData() const { return this->load(std::memory_order_relaxed); } - // Load from memory with a total ordering. - // Corresponds exactly to a Java volatile load. - T LoadSequentiallyConsistent() const { - return this->load(std::memory_order_seq_cst); - } - - // Store to memory without ordering or synchronization constraints. - void StoreRelaxed(T desired_value) { - this->store(desired_value, std::memory_order_relaxed); - } - - // Word tearing allowed, but may race. + // Store data in an atomic variable with Java data memory ordering semantics. + // + // Promises memory access semantics of ordinary Java data. + // Does not order other memory accesses. + // Long and double accesses may be performed 32 bits at a time. + // There are no "cache coherence" guarantees; e.g. loads from the same location may be reordered. + // In contrast to normal C++ accesses, racing accesses are allowed. void StoreJavaData(T desired_value) { this->store(desired_value, std::memory_order_relaxed); } - // Store to memory with release ordering. - void StoreRelease(T desired_value) { - this->store(desired_value, std::memory_order_release); - } - - // Store to memory with a total ordering. - void StoreSequentiallyConsistent(T desired_value) { - this->store(desired_value, std::memory_order_seq_cst); - } - - // Atomically replace the value with desired_value. - T ExchangeRelaxed(T desired_value) { - return this->exchange(desired_value, std::memory_order_relaxed); - } - - // Atomically replace the value with desired_value. - T ExchangeSequentiallyConsistent(T desired_value) { - return this->exchange(desired_value, std::memory_order_seq_cst); - } - - // Atomically replace the value with desired_value. - T ExchangeAcquire(T desired_value) { - return this->exchange(desired_value, std::memory_order_acquire); - } - - // Atomically replace the value with desired_value. - T ExchangeRelease(T desired_value) { - return this->exchange(desired_value, std::memory_order_release); - } - - // Atomically replace the value with desired_value if it matches the expected_value. - // Participates in total ordering of atomic operations. Returns true on success, false otherwise. - // If the value does not match, updates the expected_value argument with the value that was - // atomically read for the failed comparison. - bool CompareAndExchangeStrongSequentiallyConsistent(T* expected_value, T desired_value) { - return this->compare_exchange_strong(*expected_value, desired_value, std::memory_order_seq_cst); - } - - // Atomically replace the value with desired_value if it matches the expected_value. - // Participates in total ordering of atomic operations. Returns true on success, false otherwise. - // If the value does not match, updates the expected_value argument with the value that was - // atomically read for the failed comparison. - bool CompareAndExchangeStrongAcquire(T* expected_value, T desired_value) { - return this->compare_exchange_strong(*expected_value, desired_value, std::memory_order_acquire); - } - - // Atomically replace the value with desired_value if it matches the expected_value. - // Participates in total ordering of atomic operations. Returns true on success, false otherwise. - // If the value does not match, updates the expected_value argument with the value that was - // atomically read for the failed comparison. - bool CompareAndExchangeStrongRelease(T* expected_value, T desired_value) { - return this->compare_exchange_strong(*expected_value, desired_value, std::memory_order_release); - } - // Atomically replace the value with desired_value if it matches the expected_value. // Participates in total ordering of atomic operations. bool CompareAndSetStrongSequentiallyConsistent(T expected_value, T desired_value) { @@ -166,66 +100,8 @@ class PACKED(sizeof(T)) Atomic : public std::atomic { return this->compare_exchange_weak(expected_value, desired_value, std::memory_order_release); } - T FetchAndAddSequentiallyConsistent(const T value) { - return this->fetch_add(value, std::memory_order_seq_cst); // Return old_value. - } - - T FetchAndAddRelaxed(const T value) { - return this->fetch_add(value, std::memory_order_relaxed); // Return old_value. - } - - T FetchAndAddAcquire(const T value) { - return this->fetch_add(value, std::memory_order_acquire); // Return old_value. - } - - T FetchAndAddRelease(const T value) { - return this->fetch_add(value, std::memory_order_acquire); // Return old_value. - } - - T FetchAndSubSequentiallyConsistent(const T value) { - return this->fetch_sub(value, std::memory_order_seq_cst); // Return old value. - } - - T FetchAndSubRelaxed(const T value) { - return this->fetch_sub(value, std::memory_order_relaxed); // Return old value. - } - - T FetchAndBitwiseAndSequentiallyConsistent(const T value) { - return this->fetch_and(value, std::memory_order_seq_cst); // Return old_value. - } - - T FetchAndBitwiseAndAcquire(const T value) { - return this->fetch_and(value, std::memory_order_acquire); // Return old_value. - } - - T FetchAndBitwiseAndRelease(const T value) { - return this->fetch_and(value, std::memory_order_release); // Return old_value. - } - - T FetchAndBitwiseOrSequentiallyConsistent(const T value) { - return this->fetch_or(value, std::memory_order_seq_cst); // Return old_value. - } - - T FetchAndBitwiseOrAcquire(const T value) { - return this->fetch_or(value, std::memory_order_acquire); // Return old_value. - } - - T FetchAndBitwiseOrRelease(const T value) { - return this->fetch_or(value, std::memory_order_release); // Return old_value. - } - - T FetchAndBitwiseXorSequentiallyConsistent(const T value) { - return this->fetch_xor(value, std::memory_order_seq_cst); // Return old_value. - } - - T FetchAndBitwiseXorAcquire(const T value) { - return this->fetch_xor(value, std::memory_order_acquire); // Return old_value. - } - - T FetchAndBitwiseXorRelease(const T value) { - return this->fetch_xor(value, std::memory_order_release); // Return old_value. - } - + // Returns the address of the current atomic variable. This is only used by futex() which is + // declared to take a volatile address (see base/mutex-inl.h). volatile T* Address() { return reinterpret_cast(this); } diff --git a/runtime/barrier_test.cc b/runtime/barrier_test.cc index 04bb6bab1e..88075ba368 100644 --- a/runtime/barrier_test.cc +++ b/runtime/barrier_test.cc @@ -69,18 +69,18 @@ TEST_F(BarrierTest, CheckWait) { thread_pool.AddTask(self, new CheckWaitTask(&barrier, &count1, &count2)); } thread_pool.StartWorkers(self); - while (count1.LoadRelaxed() != num_threads) { + while (count1.load(std::memory_order_relaxed) != num_threads) { timeout_barrier.Increment(self, 1, 100); // sleep 100 msecs } // Count 2 should still be zero since no thread should have gone past the barrier. - EXPECT_EQ(0, count2.LoadRelaxed()); + EXPECT_EQ(0, count2.load(std::memory_order_relaxed)); // Perform one additional Wait(), allowing pool threads to proceed. barrier.Wait(self); // Wait for all the threads to finish. thread_pool.Wait(self, true, false); // Both counts should be equal to num_threads now. - EXPECT_EQ(count1.LoadRelaxed(), num_threads); - EXPECT_EQ(count2.LoadRelaxed(), num_threads); + EXPECT_EQ(count1.load(std::memory_order_relaxed), num_threads); + EXPECT_EQ(count2.load(std::memory_order_relaxed), num_threads); timeout_barrier.Init(self, 0); // Reset to zero for destruction. } @@ -124,7 +124,7 @@ TEST_F(BarrierTest, CheckPass) { // Wait for all the tasks to complete using the barrier. barrier.Increment(self, expected_total_tasks); // The total number of completed tasks should be equal to expected_total_tasks. - EXPECT_EQ(count.LoadRelaxed(), expected_total_tasks); + EXPECT_EQ(count.load(std::memory_order_relaxed), expected_total_tasks); } } // namespace art diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h index d6dbab4606..dfa14b91f0 100644 --- a/runtime/base/mutex-inl.h +++ b/runtime/base/mutex-inl.h @@ -161,7 +161,7 @@ inline void ReaderWriterMutex::SharedLock(Thread* self) { #if ART_USE_FUTEXES bool done = false; do { - int32_t cur_state = state_.LoadRelaxed(); + int32_t cur_state = state_.load(std::memory_order_relaxed); if (LIKELY(cur_state >= 0)) { // Add as an extra reader. done = state_.CompareAndSetWeakAcquire(cur_state, cur_state + 1); @@ -185,7 +185,7 @@ inline void ReaderWriterMutex::SharedUnlock(Thread* self) { #if ART_USE_FUTEXES bool done = false; do { - int32_t cur_state = state_.LoadRelaxed(); + int32_t cur_state = state_.load(std::memory_order_relaxed); if (LIKELY(cur_state > 0)) { // Reduce state by 1 and impose lock release load/store ordering. // Note, the relaxed loads below musn't reorder before the CompareAndSet. @@ -193,8 +193,8 @@ inline void ReaderWriterMutex::SharedUnlock(Thread* self) { // a status bit into the state on contention. done = state_.CompareAndSetWeakSequentiallyConsistent(cur_state, cur_state - 1); if (done && (cur_state - 1) == 0) { // Weak CAS may fail spuriously. - if (num_pending_writers_.LoadRelaxed() > 0 || - num_pending_readers_.LoadRelaxed() > 0) { + if (num_pending_writers_.load(std::memory_order_relaxed) > 0 || + num_pending_readers_.load(std::memory_order_relaxed) > 0) { // Wake any exclusive waiters as there are now no readers. futex(state_.Address(), FUTEX_WAKE, -1, nullptr, nullptr, 0); } @@ -221,7 +221,7 @@ inline bool Mutex::IsExclusiveHeld(const Thread* self) const { } inline pid_t Mutex::GetExclusiveOwnerTid() const { - return exclusive_owner_.LoadRelaxed(); + return exclusive_owner_.load(std::memory_order_relaxed); } inline void Mutex::AssertExclusiveHeld(const Thread* self) const { @@ -248,16 +248,16 @@ inline bool ReaderWriterMutex::IsExclusiveHeld(const Thread* self) const { inline pid_t ReaderWriterMutex::GetExclusiveOwnerTid() const { #if ART_USE_FUTEXES - int32_t state = state_.LoadRelaxed(); + int32_t state = state_.load(std::memory_order_relaxed); if (state == 0) { return 0; // No owner. } else if (state > 0) { return -1; // Shared. } else { - return exclusive_owner_.LoadRelaxed(); + return exclusive_owner_.load(std::memory_order_relaxed); } #else - return exclusive_owner_.LoadRelaxed(); + return exclusive_owner_.load(std::memory_order_relaxed); #endif } diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index a1f30b6794..73b464119e 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -128,15 +128,15 @@ class ScopedAllMutexesLock FINAL { public: explicit ScopedAllMutexesLock(const BaseMutex* mutex) : mutex_(mutex) { for (uint32_t i = 0; - !gAllMutexData->all_mutexes_guard.CompareAndSetWeakAcquire(0, mutex); + !gAllMutexData->all_mutexes_guard.CompareAndSetWeakAcquire(nullptr, mutex); ++i) { BackOff(i); } } ~ScopedAllMutexesLock() { - DCHECK_EQ(gAllMutexData->all_mutexes_guard.LoadRelaxed(), mutex_); - gAllMutexData->all_mutexes_guard.StoreRelease(0); + DCHECK_EQ(gAllMutexData->all_mutexes_guard.load(std::memory_order_relaxed), mutex_); + gAllMutexData->all_mutexes_guard.store(nullptr, std::memory_order_release); } private: @@ -147,15 +147,17 @@ class Locks::ScopedExpectedMutexesOnWeakRefAccessLock FINAL { public: explicit ScopedExpectedMutexesOnWeakRefAccessLock(const BaseMutex* mutex) : mutex_(mutex) { for (uint32_t i = 0; - !Locks::expected_mutexes_on_weak_ref_access_guard_.CompareAndSetWeakAcquire(0, mutex); + !Locks::expected_mutexes_on_weak_ref_access_guard_.CompareAndSetWeakAcquire(nullptr, + mutex); ++i) { BackOff(i); } } ~ScopedExpectedMutexesOnWeakRefAccessLock() { - DCHECK_EQ(Locks::expected_mutexes_on_weak_ref_access_guard_.LoadRelaxed(), mutex_); - Locks::expected_mutexes_on_weak_ref_access_guard_.StoreRelease(0); + DCHECK_EQ(Locks::expected_mutexes_on_weak_ref_access_guard_.load(std::memory_order_relaxed), + mutex_); + Locks::expected_mutexes_on_weak_ref_access_guard_.store(nullptr, std::memory_order_release); } private: @@ -293,7 +295,7 @@ void BaseMutex::CheckSafeToWait(Thread* self) { void BaseMutex::ContentionLogData::AddToWaitTime(uint64_t value) { if (kLogLockContentions) { // Atomically add value to wait_time. - wait_time.FetchAndAddSequentiallyConsistent(value); + wait_time.fetch_add(value, std::memory_order_seq_cst); } } @@ -306,19 +308,19 @@ void BaseMutex::RecordContention(uint64_t blocked_tid, data->AddToWaitTime(nano_time_blocked); ContentionLogEntry* log = data->contention_log; // This code is intentionally racy as it is only used for diagnostics. - uint32_t slot = data->cur_content_log_entry.LoadRelaxed(); + int32_t slot = data->cur_content_log_entry.load(std::memory_order_relaxed); if (log[slot].blocked_tid == blocked_tid && log[slot].owner_tid == blocked_tid) { ++log[slot].count; } else { uint32_t new_slot; do { - slot = data->cur_content_log_entry.LoadRelaxed(); + slot = data->cur_content_log_entry.load(std::memory_order_relaxed); new_slot = (slot + 1) % kContentionLogSize; } while (!data->cur_content_log_entry.CompareAndSetWeakRelaxed(slot, new_slot)); log[new_slot].blocked_tid = blocked_tid; log[new_slot].owner_tid = owner_tid; - log[new_slot].count.StoreRelaxed(1); + log[new_slot].count.store(1, std::memory_order_relaxed); } } } @@ -327,8 +329,8 @@ void BaseMutex::DumpContention(std::ostream& os) const { if (kLogLockContentions) { const ContentionLogData* data = contention_log_data_; const ContentionLogEntry* log = data->contention_log; - uint64_t wait_time = data->wait_time.LoadRelaxed(); - uint32_t contention_count = data->contention_count.LoadRelaxed(); + uint64_t wait_time = data->wait_time.load(std::memory_order_relaxed); + uint32_t contention_count = data->contention_count.load(std::memory_order_relaxed); if (contention_count == 0) { os << "never contended"; } else { @@ -340,7 +342,7 @@ void BaseMutex::DumpContention(std::ostream& os) const { for (size_t i = 0; i < kContentionLogSize; ++i) { uint64_t blocked_tid = log[i].blocked_tid; uint64_t owner_tid = log[i].owner_tid; - uint32_t count = log[i].count.LoadRelaxed(); + uint32_t count = log[i].count.load(std::memory_order_relaxed); if (count > 0) { auto it = most_common_blocked.find(blocked_tid); if (it != most_common_blocked.end()) { @@ -386,8 +388,8 @@ void BaseMutex::DumpContention(std::ostream& os) const { Mutex::Mutex(const char* name, LockLevel level, bool recursive) : BaseMutex(name, level), exclusive_owner_(0), recursive_(recursive), recursion_count_(0) { #if ART_USE_FUTEXES - DCHECK_EQ(0, state_.LoadRelaxed()); - DCHECK_EQ(0, num_contenders_.LoadRelaxed()); + DCHECK_EQ(0, state_.load(std::memory_order_relaxed)); + DCHECK_EQ(0, num_contenders_.load(std::memory_order_relaxed)); #else CHECK_MUTEX_CALL(pthread_mutex_init, (&mutex_, nullptr)); #endif @@ -402,7 +404,7 @@ static bool IsSafeToCallAbortSafe() { Mutex::~Mutex() { bool safe_to_call_abort = Locks::IsSafeToCallAbortRacy(); #if ART_USE_FUTEXES - if (state_.LoadRelaxed() != 0) { + if (state_.load(std::memory_order_relaxed) != 0) { LOG(safe_to_call_abort ? FATAL : WARNING) << "destroying mutex with owner: " << GetExclusiveOwnerTid(); } else { @@ -410,7 +412,7 @@ Mutex::~Mutex() { LOG(safe_to_call_abort ? FATAL : WARNING) << "unexpectedly found an owner on unlocked mutex " << name_; } - if (num_contenders_.LoadSequentiallyConsistent() != 0) { + if (num_contenders_.load(std::memory_order_seq_cst) != 0) { LOG(safe_to_call_abort ? FATAL : WARNING) << "unexpectedly found a contender on mutex " << name_; } @@ -436,7 +438,7 @@ void Mutex::ExclusiveLock(Thread* self) { #if ART_USE_FUTEXES bool done = false; do { - int32_t cur_state = state_.LoadRelaxed(); + int32_t cur_state = state_.load(std::memory_order_relaxed); if (LIKELY(cur_state == 0)) { // Change state from 0 to 1 and impose load/store ordering appropriate for lock acquisition. done = state_.CompareAndSetWeakAcquire(0 /* cur_state */, 1 /* new state */); @@ -457,12 +459,12 @@ void Mutex::ExclusiveLock(Thread* self) { num_contenders_--; } } while (!done); - DCHECK_EQ(state_.LoadRelaxed(), 1); + DCHECK_EQ(state_.load(std::memory_order_relaxed), 1); #else CHECK_MUTEX_CALL(pthread_mutex_lock, (&mutex_)); #endif DCHECK_EQ(GetExclusiveOwnerTid(), 0); - exclusive_owner_.StoreRelaxed(SafeGetTid(self)); + exclusive_owner_.store(SafeGetTid(self), std::memory_order_relaxed); RegisterAsLocked(self); } recursion_count_++; @@ -482,7 +484,7 @@ bool Mutex::ExclusiveTryLock(Thread* self) { #if ART_USE_FUTEXES bool done = false; do { - int32_t cur_state = state_.LoadRelaxed(); + int32_t cur_state = state_.load(std::memory_order_relaxed); if (cur_state == 0) { // Change state from 0 to 1 and impose load/store ordering appropriate for lock acquisition. done = state_.CompareAndSetWeakAcquire(0 /* cur_state */, 1 /* new state */); @@ -490,7 +492,7 @@ bool Mutex::ExclusiveTryLock(Thread* self) { return false; } } while (!done); - DCHECK_EQ(state_.LoadRelaxed(), 1); + DCHECK_EQ(state_.load(std::memory_order_relaxed), 1); #else int result = pthread_mutex_trylock(&mutex_); if (result == EBUSY) { @@ -502,7 +504,7 @@ bool Mutex::ExclusiveTryLock(Thread* self) { } #endif DCHECK_EQ(GetExclusiveOwnerTid(), 0); - exclusive_owner_.StoreRelaxed(SafeGetTid(self)); + exclusive_owner_.store(SafeGetTid(self), std::memory_order_relaxed); RegisterAsLocked(self); } recursion_count_++; @@ -539,10 +541,10 @@ void Mutex::ExclusiveUnlock(Thread* self) { #if ART_USE_FUTEXES bool done = false; do { - int32_t cur_state = state_.LoadRelaxed(); + int32_t cur_state = state_.load(std::memory_order_relaxed); if (LIKELY(cur_state == 1)) { // We're no longer the owner. - exclusive_owner_.StoreRelaxed(0); + exclusive_owner_.store(0 /* pid */, std::memory_order_relaxed); // Change state to 0 and impose load/store ordering appropriate for lock release. // Note, the relaxed loads below mustn't reorder before the CompareAndSet. // TODO: the ordering here is non-trivial as state is split across 3 fields, fix by placing @@ -550,7 +552,7 @@ void Mutex::ExclusiveUnlock(Thread* self) { done = state_.CompareAndSetWeakSequentiallyConsistent(cur_state, 0 /* new state */); if (LIKELY(done)) { // Spurious fail? // Wake a contender. - if (UNLIKELY(num_contenders_.LoadRelaxed() > 0)) { + if (UNLIKELY(num_contenders_.load(std::memory_order_relaxed) > 0)) { futex(state_.Address(), FUTEX_WAKE, 1, nullptr, nullptr, 0); } } @@ -569,7 +571,7 @@ void Mutex::ExclusiveUnlock(Thread* self) { } } while (!done); #else - exclusive_owner_.StoreRelaxed(0); + exclusive_owner_.store(0 /* pid */, std::memory_order_relaxed); CHECK_MUTEX_CALL(pthread_mutex_unlock, (&mutex_)); #endif } @@ -593,7 +595,7 @@ void Mutex::WakeupToRespondToEmptyCheckpoint() { #if ART_USE_FUTEXES // Wake up all the waiters so they will respond to the emtpy checkpoint. DCHECK(should_respond_to_empty_checkpoint_request_); - if (UNLIKELY(num_contenders_.LoadRelaxed() > 0)) { + if (UNLIKELY(num_contenders_.load(std::memory_order_relaxed) > 0)) { futex(state_.Address(), FUTEX_WAKE, -1, nullptr, nullptr, 0); } #else @@ -610,15 +612,15 @@ ReaderWriterMutex::ReaderWriterMutex(const char* name, LockLevel level) #if !ART_USE_FUTEXES CHECK_MUTEX_CALL(pthread_rwlock_init, (&rwlock_, nullptr)); #endif - exclusive_owner_.StoreRelaxed(0); + exclusive_owner_.store(0 /* pid */, std::memory_order_relaxed); } ReaderWriterMutex::~ReaderWriterMutex() { #if ART_USE_FUTEXES - CHECK_EQ(state_.LoadRelaxed(), 0); + CHECK_EQ(state_.load(std::memory_order_relaxed), 0); CHECK_EQ(GetExclusiveOwnerTid(), 0); - CHECK_EQ(num_pending_readers_.LoadRelaxed(), 0); - CHECK_EQ(num_pending_writers_.LoadRelaxed(), 0); + CHECK_EQ(num_pending_readers_.load(std::memory_order_relaxed), 0); + CHECK_EQ(num_pending_writers_.load(std::memory_order_relaxed), 0); #else // We can't use CHECK_MUTEX_CALL here because on shutdown a suspended daemon thread // may still be using locks. @@ -637,7 +639,7 @@ void ReaderWriterMutex::ExclusiveLock(Thread* self) { #if ART_USE_FUTEXES bool done = false; do { - int32_t cur_state = state_.LoadRelaxed(); + int32_t cur_state = state_.load(std::memory_order_relaxed); if (LIKELY(cur_state == 0)) { // Change state from 0 to -1 and impose load/store ordering appropriate for lock acquisition. done = state_.CompareAndSetWeakAcquire(0 /* cur_state*/, -1 /* new state */); @@ -658,12 +660,12 @@ void ReaderWriterMutex::ExclusiveLock(Thread* self) { --num_pending_writers_; } } while (!done); - DCHECK_EQ(state_.LoadRelaxed(), -1); + DCHECK_EQ(state_.load(std::memory_order_relaxed), -1); #else CHECK_MUTEX_CALL(pthread_rwlock_wrlock, (&rwlock_)); #endif DCHECK_EQ(GetExclusiveOwnerTid(), 0); - exclusive_owner_.StoreRelaxed(SafeGetTid(self)); + exclusive_owner_.store(SafeGetTid(self), std::memory_order_relaxed); RegisterAsLocked(self); AssertExclusiveHeld(self); } @@ -676,10 +678,10 @@ void ReaderWriterMutex::ExclusiveUnlock(Thread* self) { #if ART_USE_FUTEXES bool done = false; do { - int32_t cur_state = state_.LoadRelaxed(); + int32_t cur_state = state_.load(std::memory_order_relaxed); if (LIKELY(cur_state == -1)) { // We're no longer the owner. - exclusive_owner_.StoreRelaxed(0); + exclusive_owner_.store(0 /* pid */, std::memory_order_relaxed); // Change state from -1 to 0 and impose load/store ordering appropriate for lock release. // Note, the relaxed loads below musn't reorder before the CompareAndSet. // TODO: the ordering here is non-trivial as state is split across 3 fields, fix by placing @@ -687,8 +689,8 @@ void ReaderWriterMutex::ExclusiveUnlock(Thread* self) { done = state_.CompareAndSetWeakSequentiallyConsistent(-1 /* cur_state*/, 0 /* new state */); if (LIKELY(done)) { // Weak CAS may fail spuriously. // Wake any waiters. - if (UNLIKELY(num_pending_readers_.LoadRelaxed() > 0 || - num_pending_writers_.LoadRelaxed() > 0)) { + if (UNLIKELY(num_pending_readers_.load(std::memory_order_relaxed) > 0 || + num_pending_writers_.load(std::memory_order_relaxed) > 0)) { futex(state_.Address(), FUTEX_WAKE, -1, nullptr, nullptr, 0); } } @@ -697,7 +699,7 @@ void ReaderWriterMutex::ExclusiveUnlock(Thread* self) { } } while (!done); #else - exclusive_owner_.StoreRelaxed(0); + exclusive_owner_.store(0 /* pid */, std::memory_order_relaxed); CHECK_MUTEX_CALL(pthread_rwlock_unlock, (&rwlock_)); #endif } @@ -710,7 +712,7 @@ bool ReaderWriterMutex::ExclusiveLockWithTimeout(Thread* self, int64_t ms, int32 timespec end_abs_ts; InitTimeSpec(true, CLOCK_MONOTONIC, ms, ns, &end_abs_ts); do { - int32_t cur_state = state_.LoadRelaxed(); + int32_t cur_state = state_.load(std::memory_order_relaxed); if (cur_state == 0) { // Change state from 0 to -1 and impose load/store ordering appropriate for lock acquisition. done = state_.CompareAndSetWeakAcquire(0 /* cur_state */, -1 /* new state */); @@ -753,7 +755,7 @@ bool ReaderWriterMutex::ExclusiveLockWithTimeout(Thread* self, int64_t ms, int32 PLOG(FATAL) << "pthread_rwlock_timedwrlock failed for " << name_; } #endif - exclusive_owner_.StoreRelaxed(SafeGetTid(self)); + exclusive_owner_.store(SafeGetTid(self), std::memory_order_relaxed); RegisterAsLocked(self); AssertSharedHeld(self); return true; @@ -782,7 +784,7 @@ bool ReaderWriterMutex::SharedTryLock(Thread* self) { #if ART_USE_FUTEXES bool done = false; do { - int32_t cur_state = state_.LoadRelaxed(); + int32_t cur_state = state_.load(std::memory_order_relaxed); if (cur_state >= 0) { // Add as an extra reader and impose load/store ordering appropriate for lock acquisition. done = state_.CompareAndSetWeakAcquire(cur_state, cur_state + 1); @@ -822,9 +824,9 @@ void ReaderWriterMutex::Dump(std::ostream& os) const { << " level=" << static_cast(level_) << " owner=" << GetExclusiveOwnerTid() #if ART_USE_FUTEXES - << " state=" << state_.LoadSequentiallyConsistent() - << " num_pending_writers=" << num_pending_writers_.LoadSequentiallyConsistent() - << " num_pending_readers=" << num_pending_readers_.LoadSequentiallyConsistent() + << " state=" << state_.load(std::memory_order_seq_cst) + << " num_pending_writers=" << num_pending_writers_.load(std::memory_order_seq_cst) + << " num_pending_readers=" << num_pending_readers_.load(std::memory_order_seq_cst) #endif << " "; DumpContention(os); @@ -844,8 +846,8 @@ void ReaderWriterMutex::WakeupToRespondToEmptyCheckpoint() { #if ART_USE_FUTEXES // Wake up all the waiters so they will respond to the emtpy checkpoint. DCHECK(should_respond_to_empty_checkpoint_request_); - if (UNLIKELY(num_pending_readers_.LoadRelaxed() > 0 || - num_pending_writers_.LoadRelaxed() > 0)) { + if (UNLIKELY(num_pending_readers_.load(std::memory_order_relaxed) > 0 || + num_pending_writers_.load(std::memory_order_relaxed) > 0)) { futex(state_.Address(), FUTEX_WAKE, -1, nullptr, nullptr, 0); } #else @@ -856,7 +858,7 @@ void ReaderWriterMutex::WakeupToRespondToEmptyCheckpoint() { ConditionVariable::ConditionVariable(const char* name, Mutex& guard) : name_(name), guard_(guard) { #if ART_USE_FUTEXES - DCHECK_EQ(0, sequence_.LoadRelaxed()); + DCHECK_EQ(0, sequence_.load(std::memory_order_relaxed)); num_waiters_ = 0; #else pthread_condattr_t cond_attrs; @@ -899,7 +901,7 @@ void ConditionVariable::Broadcast(Thread* self) { sequence_++; // Indicate the broadcast occurred. bool done = false; do { - int32_t cur_sequence = sequence_.LoadRelaxed(); + int32_t cur_sequence = sequence_.load(std::memory_order_relaxed); // Requeue waiters onto mutex. The waiter holds the contender count on the mutex high ensuring // mutex unlocks will awaken the requeued waiter thread. done = futex(sequence_.Address(), FUTEX_CMP_REQUEUE, 0, @@ -948,7 +950,7 @@ void ConditionVariable::WaitHoldingLocks(Thread* self) { // Ensure the Mutex is contended so that requeued threads are awoken. guard_.num_contenders_++; guard_.recursion_count_ = 1; - int32_t cur_sequence = sequence_.LoadRelaxed(); + int32_t cur_sequence = sequence_.load(std::memory_order_relaxed); guard_.ExclusiveUnlock(self); if (futex(sequence_.Address(), FUTEX_WAIT, cur_sequence, nullptr, nullptr, 0) != 0) { // Futex failed, check it is an expected error. @@ -974,14 +976,14 @@ void ConditionVariable::WaitHoldingLocks(Thread* self) { CHECK_GE(num_waiters_, 0); num_waiters_--; // We awoke and so no longer require awakes from the guard_'s unlock. - CHECK_GE(guard_.num_contenders_.LoadRelaxed(), 0); + CHECK_GE(guard_.num_contenders_.load(std::memory_order_relaxed), 0); guard_.num_contenders_--; #else pid_t old_owner = guard_.GetExclusiveOwnerTid(); - guard_.exclusive_owner_.StoreRelaxed(0); + guard_.exclusive_owner_.store(0 /* pid */, std::memory_order_relaxed); guard_.recursion_count_ = 0; CHECK_MUTEX_CALL(pthread_cond_wait, (&cond_, &guard_.mutex_)); - guard_.exclusive_owner_.StoreRelaxed(old_owner); + guard_.exclusive_owner_.store(old_owner, std::memory_order_relaxed); #endif guard_.recursion_count_ = old_recursion_count; } @@ -999,7 +1001,7 @@ bool ConditionVariable::TimedWait(Thread* self, int64_t ms, int32_t ns) { // Ensure the Mutex is contended so that requeued threads are awoken. guard_.num_contenders_++; guard_.recursion_count_ = 1; - int32_t cur_sequence = sequence_.LoadRelaxed(); + int32_t cur_sequence = sequence_.load(std::memory_order_relaxed); guard_.ExclusiveUnlock(self); if (futex(sequence_.Address(), FUTEX_WAIT, cur_sequence, &rel_ts, nullptr, 0) != 0) { if (errno == ETIMEDOUT) { @@ -1015,7 +1017,7 @@ bool ConditionVariable::TimedWait(Thread* self, int64_t ms, int32_t ns) { CHECK_GE(num_waiters_, 0); num_waiters_--; // We awoke and so no longer require awakes from the guard_'s unlock. - CHECK_GE(guard_.num_contenders_.LoadRelaxed(), 0); + CHECK_GE(guard_.num_contenders_.load(std::memory_order_relaxed), 0); guard_.num_contenders_--; #else #if !defined(__APPLE__) @@ -1024,7 +1026,7 @@ bool ConditionVariable::TimedWait(Thread* self, int64_t ms, int32_t ns) { int clock = CLOCK_REALTIME; #endif pid_t old_owner = guard_.GetExclusiveOwnerTid(); - guard_.exclusive_owner_.StoreRelaxed(0); + guard_.exclusive_owner_.store(0 /* pid */, std::memory_order_relaxed); guard_.recursion_count_ = 0; timespec ts; InitTimeSpec(true, clock, ms, ns, &ts); @@ -1035,7 +1037,7 @@ bool ConditionVariable::TimedWait(Thread* self, int64_t ms, int32_t ns) { errno = rc; PLOG(FATAL) << "TimedWait failed for " << name_; } - guard_.exclusive_owner_.StoreRelaxed(old_owner); + guard_.exclusive_owner_.store(old_owner, std::memory_order_relaxed); #endif guard_.recursion_count_ = old_recursion_count; return timed_out; @@ -1254,12 +1256,13 @@ void Locks::InitConditions() { } void Locks::SetClientCallback(ClientCallback* safe_to_call_abort_cb) { - safe_to_call_abort_callback.StoreRelease(safe_to_call_abort_cb); + safe_to_call_abort_callback.store(safe_to_call_abort_cb, std::memory_order_release); } // Helper to allow checking shutdown while ignoring locking requirements. bool Locks::IsSafeToCallAbortRacy() { - Locks::ClientCallback* safe_to_call_abort_cb = safe_to_call_abort_callback.LoadAcquire(); + Locks::ClientCallback* safe_to_call_abort_cb = + safe_to_call_abort_callback.load(std::memory_order_acquire); return safe_to_call_abort_cb != nullptr && safe_to_call_abort_cb(); } diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 437661798f..b0eb23d327 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -224,7 +224,7 @@ class BaseMutex { public: bool HasEverContended() const { if (kLogLockContentions) { - return contention_log_data_->contention_count.LoadSequentiallyConsistent() > 0; + return contention_log_data_->contention_count.load(std::memory_order_seq_cst) > 0; } return false; } diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h index c59e2e881d..5da5470c1a 100644 --- a/runtime/class_table-inl.h +++ b/runtime/class_table-inl.h @@ -88,7 +88,7 @@ bool ClassTable::Visit(const Visitor& visitor) { template inline mirror::Class* ClassTable::TableSlot::Read() const { - const uint32_t before = data_.LoadRelaxed(); + const uint32_t before = data_.load(std::memory_order_relaxed); ObjPtr const before_ptr(ExtractPtr(before)); ObjPtr const after_ptr( GcRoot(before_ptr).Read()); @@ -102,7 +102,7 @@ inline mirror::Class* ClassTable::TableSlot::Read() const { template inline void ClassTable::TableSlot::VisitRoot(const Visitor& visitor) const { - const uint32_t before = data_.LoadRelaxed(); + const uint32_t before = data_.load(std::memory_order_relaxed); ObjPtr before_ptr(ExtractPtr(before)); GcRoot root(before_ptr); visitor.VisitRoot(root.AddressWithoutBarrier()); diff --git a/runtime/class_table.h b/runtime/class_table.h index 3e90fe2768..0b08041dbd 100644 --- a/runtime/class_table.h +++ b/runtime/class_table.h @@ -53,14 +53,14 @@ class ClassTable { public: TableSlot() : data_(0u) {} - TableSlot(const TableSlot& copy) : data_(copy.data_.LoadRelaxed()) {} + TableSlot(const TableSlot& copy) : data_(copy.data_.load(std::memory_order_relaxed)) {} explicit TableSlot(ObjPtr klass); TableSlot(ObjPtr klass, uint32_t descriptor_hash); TableSlot& operator=(const TableSlot& copy) { - data_.StoreRelaxed(copy.data_.LoadRelaxed()); + data_.store(copy.data_.load(std::memory_order_relaxed), std::memory_order_relaxed); return *this; } @@ -69,7 +69,7 @@ class ClassTable { } uint32_t Hash() const { - return MaskHash(data_.LoadRelaxed()); + return MaskHash(data_.load(std::memory_order_relaxed)); } static uint32_t MaskHash(uint32_t hash) { diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h index 6b103bfe1b..7a4bd87b12 100644 --- a/runtime/gc/accounting/atomic_stack.h +++ b/runtime/gc/accounting/atomic_stack.h @@ -74,8 +74,8 @@ class AtomicStack { void Reset() { DCHECK(mem_map_.get() != nullptr); DCHECK(begin_ != nullptr); - front_index_.StoreRelaxed(0); - back_index_.StoreRelaxed(0); + front_index_.store(0, std::memory_order_relaxed); + back_index_.store(0, std::memory_order_relaxed); debug_is_sorted_ = true; mem_map_->MadviseDontNeedAndZero(); } @@ -103,7 +103,7 @@ class AtomicStack { int32_t index; int32_t new_index; do { - index = back_index_.LoadRelaxed(); + index = back_index_.load(std::memory_order_relaxed); new_index = index + num_slots; if (UNLIKELY(static_cast(new_index) >= growth_limit_)) { // Stack overflow. @@ -134,31 +134,32 @@ class AtomicStack { if (kIsDebugBuild) { debug_is_sorted_ = false; } - const int32_t index = back_index_.LoadRelaxed(); + const int32_t index = back_index_.load(std::memory_order_relaxed); DCHECK_LT(static_cast(index), growth_limit_); - back_index_.StoreRelaxed(index + 1); + back_index_.store(index + 1, std::memory_order_relaxed); begin_[index].Assign(value); } T* PopBack() REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK_GT(back_index_.LoadRelaxed(), front_index_.LoadRelaxed()); + DCHECK_GT(back_index_.load(std::memory_order_relaxed), + front_index_.load(std::memory_order_relaxed)); // Decrement the back index non atomically. - back_index_.StoreRelaxed(back_index_.LoadRelaxed() - 1); - return begin_[back_index_.LoadRelaxed()].AsMirrorPtr(); + back_index_.store(back_index_.load(std::memory_order_relaxed) - 1, std::memory_order_relaxed); + return begin_[back_index_.load(std::memory_order_relaxed)].AsMirrorPtr(); } // Take an item from the front of the stack. T PopFront() { - int32_t index = front_index_.LoadRelaxed(); - DCHECK_LT(index, back_index_.LoadRelaxed()); - front_index_.StoreRelaxed(index + 1); + int32_t index = front_index_.load(std::memory_order_relaxed); + DCHECK_LT(index, back_index_.load(std::memory_order_relaxed)); + front_index_.store(index + 1, std::memory_order_relaxed); return begin_[index]; } // Pop a number of elements. void PopBackCount(int32_t n) { DCHECK_GE(Size(), static_cast(n)); - back_index_.StoreRelaxed(back_index_.LoadRelaxed() - n); + back_index_.store(back_index_.load(std::memory_order_relaxed) - n, std::memory_order_relaxed); } bool IsEmpty() const { @@ -170,15 +171,17 @@ class AtomicStack { } size_t Size() const { - DCHECK_LE(front_index_.LoadRelaxed(), back_index_.LoadRelaxed()); - return back_index_.LoadRelaxed() - front_index_.LoadRelaxed(); + DCHECK_LE(front_index_.load(std::memory_order_relaxed), + back_index_.load(std::memory_order_relaxed)); + return + back_index_.load(std::memory_order_relaxed) - front_index_.load(std::memory_order_relaxed); } StackReference* Begin() const { - return begin_ + front_index_.LoadRelaxed(); + return begin_ + front_index_.load(std::memory_order_relaxed); } StackReference* End() const { - return begin_ + back_index_.LoadRelaxed(); + return begin_ + back_index_.load(std::memory_order_relaxed); } size_t Capacity() const { @@ -193,11 +196,11 @@ class AtomicStack { } void Sort() { - int32_t start_back_index = back_index_.LoadRelaxed(); - int32_t start_front_index = front_index_.LoadRelaxed(); + int32_t start_back_index = back_index_.load(std::memory_order_relaxed); + int32_t start_front_index = front_index_.load(std::memory_order_relaxed); std::sort(Begin(), End(), ObjectComparator()); - CHECK_EQ(start_back_index, back_index_.LoadRelaxed()); - CHECK_EQ(start_front_index, front_index_.LoadRelaxed()); + CHECK_EQ(start_back_index, back_index_.load(std::memory_order_relaxed)); + CHECK_EQ(start_front_index, front_index_.load(std::memory_order_relaxed)); if (kIsDebugBuild) { debug_is_sorted_ = true; } @@ -236,7 +239,7 @@ class AtomicStack { } int32_t index; do { - index = back_index_.LoadRelaxed(); + index = back_index_.load(std::memory_order_relaxed); if (UNLIKELY(static_cast(index) >= limit)) { // Stack overflow. return false; diff --git a/runtime/gc/accounting/bitmap-inl.h b/runtime/gc/accounting/bitmap-inl.h index a71b212af3..a4273e5ff6 100644 --- a/runtime/gc/accounting/bitmap-inl.h +++ b/runtime/gc/accounting/bitmap-inl.h @@ -37,7 +37,7 @@ inline bool Bitmap::AtomicTestAndSetBit(uintptr_t bit_index) { auto* atomic_entry = reinterpret_cast*>(&bitmap_begin_[word_index]); uintptr_t old_word; do { - old_word = atomic_entry->LoadRelaxed(); + old_word = atomic_entry->load(std::memory_order_relaxed); // Fast path: The bit is already set. if ((old_word & word_mask) != 0) { DCHECK(TestBit(bit_index)); diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h index 14f5d0e1c6..d9c0418f4a 100644 --- a/runtime/gc/accounting/card_table-inl.h +++ b/runtime/gc/accounting/card_table-inl.h @@ -43,7 +43,7 @@ static inline bool byte_cas(uint8_t old_value, uint8_t new_value, uint8_t* addre Atomic* word_atomic = reinterpret_cast*>(address); // Word with the byte we are trying to cas cleared. - const uintptr_t cur_word = word_atomic->LoadRelaxed() & + const uintptr_t cur_word = word_atomic->load(std::memory_order_relaxed) & ~(static_cast(0xFF) << shift_in_bits); const uintptr_t old_word = cur_word | (static_cast(old_value) << shift_in_bits); const uintptr_t new_word = cur_word | (static_cast(new_value) << shift_in_bits); diff --git a/runtime/gc/accounting/space_bitmap-inl.h b/runtime/gc/accounting/space_bitmap-inl.h index 384e3c2f4c..d460e00075 100644 --- a/runtime/gc/accounting/space_bitmap-inl.h +++ b/runtime/gc/accounting/space_bitmap-inl.h @@ -41,7 +41,7 @@ inline bool SpaceBitmap::AtomicTestAndSet(const mirror::Object* obj) DCHECK_LT(index, bitmap_size_ / sizeof(intptr_t)) << " bitmap_size_ = " << bitmap_size_; uintptr_t old_word; do { - old_word = atomic_entry->LoadRelaxed(); + old_word = atomic_entry->load(std::memory_order_relaxed); // Fast path: The bit is already set. if ((old_word & mask) != 0) { DCHECK(Test(obj)); @@ -59,7 +59,8 @@ inline bool SpaceBitmap::Test(const mirror::Object* obj) const { DCHECK(bitmap_begin_ != nullptr); DCHECK_GE(addr, heap_begin_); const uintptr_t offset = addr - heap_begin_; - return (bitmap_begin_[OffsetToIndex(offset)].LoadRelaxed() & OffsetToMask(offset)) != 0; + size_t index = OffsetToIndex(offset); + return (bitmap_begin_[index].load(std::memory_order_relaxed) & OffsetToMask(offset)) != 0; } template @@ -119,7 +120,7 @@ inline void SpaceBitmap::VisitMarkedRange(uintptr_t visit_begin, // Traverse the middle, full part. for (size_t i = index_start + 1; i < index_end; ++i) { - uintptr_t w = bitmap_begin_[i].LoadRelaxed(); + uintptr_t w = bitmap_begin_[i].load(std::memory_order_relaxed); if (w != 0) { const uintptr_t ptr_base = IndexToOffset(i) + heap_begin_; // Iterate on the bits set in word `w`, from the least to the most significant bit. @@ -168,7 +169,7 @@ void SpaceBitmap::Walk(Visitor&& visitor) { uintptr_t end = OffsetToIndex(HeapLimit() - heap_begin_ - 1); Atomic* bitmap_begin = bitmap_begin_; for (uintptr_t i = 0; i <= end; ++i) { - uintptr_t w = bitmap_begin[i].LoadRelaxed(); + uintptr_t w = bitmap_begin[i].load(std::memory_order_relaxed); if (w != 0) { uintptr_t ptr_base = IndexToOffset(i) + heap_begin_; do { @@ -192,7 +193,7 @@ inline bool SpaceBitmap::Modify(const mirror::Object* obj) { const uintptr_t mask = OffsetToMask(offset); DCHECK_LT(index, bitmap_size_ / sizeof(intptr_t)) << " bitmap_size_ = " << bitmap_size_; Atomic* atomic_entry = &bitmap_begin_[index]; - uintptr_t old_word = atomic_entry->LoadRelaxed(); + uintptr_t old_word = atomic_entry->load(std::memory_order_relaxed); if (kSetBit) { // Check the bit before setting the word incase we are trying to mark a read only bitmap // like an image space bitmap. This bitmap is mapped as read only and will fault if we @@ -200,10 +201,10 @@ inline bool SpaceBitmap::Modify(const mirror::Object* obj) { // occur if we check before setting the bit. This also prevents dirty pages that would // occur if the bitmap was read write and we did not check the bit. if ((old_word & mask) == 0) { - atomic_entry->StoreRelaxed(old_word | mask); + atomic_entry->store(old_word | mask, std::memory_order_relaxed); } } else { - atomic_entry->StoreRelaxed(old_word & ~mask); + atomic_entry->store(old_word & ~mask, std::memory_order_relaxed); } DCHECK_EQ(Test(obj), kSetBit); return (old_word & mask) != 0; diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc index 0247564a8c..d84288f676 100644 --- a/runtime/gc/accounting/space_bitmap.cc +++ b/runtime/gc/accounting/space_bitmap.cc @@ -145,7 +145,7 @@ void SpaceBitmap::CopyFrom(SpaceBitmap* source_bitmap) { Atomic* const src = source_bitmap->Begin(); Atomic* const dest = Begin(); for (size_t i = 0; i < count; ++i) { - dest[i].StoreRelaxed(src[i].LoadRelaxed()); + dest[i].store(src[i].load(std::memory_order_relaxed), std::memory_order_relaxed); } } @@ -184,7 +184,8 @@ void SpaceBitmap::SweepWalk(const SpaceBitmap& live_bitm Atomic* live = live_bitmap.bitmap_begin_; Atomic* mark = mark_bitmap.bitmap_begin_; for (size_t i = start; i <= end; i++) { - uintptr_t garbage = live[i].LoadRelaxed() & ~mark[i].LoadRelaxed(); + uintptr_t garbage = + live[i].load(std::memory_order_relaxed) & ~mark[i].load(std::memory_order_relaxed); if (UNLIKELY(garbage != 0)) { uintptr_t ptr_base = IndexToOffset(i) + live_bitmap.heap_begin_; do { diff --git a/runtime/gc/collector/concurrent_copying-inl.h b/runtime/gc/collector/concurrent_copying-inl.h index 56983be8fa..6e345fb2f2 100644 --- a/runtime/gc/collector/concurrent_copying-inl.h +++ b/runtime/gc/collector/concurrent_copying-inl.h @@ -78,13 +78,13 @@ inline mirror::Object* ConcurrentCopying::MarkImmuneSpace(mirror::Object* ref) { if (kIsDebugBuild) { if (Thread::Current() == thread_running_gc_) { DCHECK(!kGrayImmuneObject || - updated_all_immune_objects_.LoadRelaxed() || + updated_all_immune_objects_.load(std::memory_order_relaxed) || gc_grays_immune_objects_); } else { DCHECK(kGrayImmuneObject); } } - if (!kGrayImmuneObject || updated_all_immune_objects_.LoadRelaxed()) { + if (!kGrayImmuneObject || updated_all_immune_objects_.load(std::memory_order_relaxed)) { return ref; } // This may or may not succeed, which is ok because the object may already be gray. diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index b10c504dd5..bb5167f15d 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -291,14 +291,14 @@ void ConcurrentCopying::InitializePhase() { rb_mark_bit_stack_full_ = false; mark_from_read_barrier_measurements_ = measure_read_barrier_slow_path_; if (measure_read_barrier_slow_path_) { - rb_slow_path_ns_.StoreRelaxed(0); - rb_slow_path_count_.StoreRelaxed(0); - rb_slow_path_count_gc_.StoreRelaxed(0); + rb_slow_path_ns_.store(0, std::memory_order_relaxed); + rb_slow_path_count_.store(0, std::memory_order_relaxed); + rb_slow_path_count_gc_.store(0, std::memory_order_relaxed); } immune_spaces_.Reset(); - bytes_moved_.StoreRelaxed(0); - objects_moved_.StoreRelaxed(0); + bytes_moved_.store(0, std::memory_order_relaxed); + objects_moved_.store(0, std::memory_order_relaxed); GcCause gc_cause = GetCurrentIteration()->GetGcCause(); if (gc_cause == kGcCauseExplicit || gc_cause == kGcCauseCollectorTransition || @@ -308,7 +308,7 @@ void ConcurrentCopying::InitializePhase() { force_evacuate_all_ = false; } if (kUseBakerReadBarrier) { - updated_all_immune_objects_.StoreRelaxed(false); + updated_all_immune_objects_.store(false, std::memory_order_relaxed); // GC may gray immune objects in the thread flip. gc_grays_immune_objects_ = true; if (kIsDebugBuild) { @@ -350,7 +350,7 @@ class ConcurrentCopying::ThreadFlipVisitor : public Closure, public RootVisitor concurrent_copying_->region_space_->RevokeThreadLocalBuffers(thread); reinterpret_cast*>( &concurrent_copying_->from_space_num_objects_at_first_pause_)-> - FetchAndAddSequentiallyConsistent(thread_local_objects); + fetch_add(thread_local_objects, std::memory_order_seq_cst); } else { concurrent_copying_->region_space_->RevokeThreadLocalBuffers(thread); } @@ -430,7 +430,8 @@ class ConcurrentCopying::FlipCallback : public Closure { cc->from_space_num_bytes_at_first_pause_ = cc->region_space_->GetBytesAllocated(); } cc->is_marking_ = true; - cc->mark_stack_mode_.StoreRelaxed(ConcurrentCopying::kMarkStackModeThreadLocal); + cc->mark_stack_mode_.store(ConcurrentCopying::kMarkStackModeThreadLocal, + std::memory_order_relaxed); if (kIsDebugBuild) { cc->region_space_->AssertAllRegionLiveBytesZeroOrCleared(); } @@ -728,7 +729,7 @@ void ConcurrentCopying::GrayAllNewlyDirtyImmuneObjects() { } // Since all of the objects that may point to other spaces are gray, we can avoid all the read // barriers in the immune spaces. - updated_all_immune_objects_.StoreRelaxed(true); + updated_all_immune_objects_.store(true, std::memory_order_relaxed); } void ConcurrentCopying::SwapStacks() { @@ -816,7 +817,7 @@ void ConcurrentCopying::MarkingPhase() { if (kUseBakerReadBarrier) { // This release fence makes the field updates in the above loop visible before allowing mutator // getting access to immune objects without graying it first. - updated_all_immune_objects_.StoreRelease(true); + updated_all_immune_objects_.store(true, std::memory_order_release); // Now whiten immune objects concurrently accessed and grayed by mutators. We can't do this in // the above loop because we would incorrectly disable the read barrier by whitening an object // which may point to an unscanned, white object, breaking the to-space invariant. @@ -1018,8 +1019,8 @@ void ConcurrentCopying::DisableMarking() { heap_->rb_table_->ClearAll(); DCHECK(heap_->rb_table_->IsAllCleared()); } - is_mark_stack_push_disallowed_.StoreSequentiallyConsistent(1); - mark_stack_mode_.StoreSequentiallyConsistent(kMarkStackModeOff); + is_mark_stack_push_disallowed_.store(1, std::memory_order_seq_cst); + mark_stack_mode_.store(kMarkStackModeOff, std::memory_order_seq_cst); } void ConcurrentCopying::PushOntoFalseGrayStack(mirror::Object* ref) { @@ -1069,11 +1070,11 @@ void ConcurrentCopying::ExpandGcMarkStack() { } void ConcurrentCopying::PushOntoMarkStack(mirror::Object* to_ref) { - CHECK_EQ(is_mark_stack_push_disallowed_.LoadRelaxed(), 0) + CHECK_EQ(is_mark_stack_push_disallowed_.load(std::memory_order_relaxed), 0) << " " << to_ref << " " << mirror::Object::PrettyTypeOf(to_ref); Thread* self = Thread::Current(); // TODO: pass self as an argument from call sites? CHECK(thread_running_gc_ != nullptr); - MarkStackMode mark_stack_mode = mark_stack_mode_.LoadRelaxed(); + MarkStackMode mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed); if (LIKELY(mark_stack_mode == kMarkStackModeThreadLocal)) { if (LIKELY(self == thread_running_gc_)) { // If GC-running thread, use the GC mark stack instead of a thread-local mark stack. @@ -1412,7 +1413,7 @@ bool ConcurrentCopying::ProcessMarkStackOnce() { CHECK(self == thread_running_gc_); CHECK(self->GetThreadLocalMarkStack() == nullptr); size_t count = 0; - MarkStackMode mark_stack_mode = mark_stack_mode_.LoadRelaxed(); + MarkStackMode mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed); if (mark_stack_mode == kMarkStackModeThreadLocal) { // Process the thread-local mark stacks and the GC mark stack. count += ProcessThreadLocalMarkStacks(/* disable_weak_ref_access */ false, @@ -1597,10 +1598,10 @@ void ConcurrentCopying::SwitchToSharedMarkStackMode() { CHECK(thread_running_gc_ != nullptr); CHECK_EQ(self, thread_running_gc_); CHECK(self->GetThreadLocalMarkStack() == nullptr); - MarkStackMode before_mark_stack_mode = mark_stack_mode_.LoadRelaxed(); + MarkStackMode before_mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed); CHECK_EQ(static_cast(before_mark_stack_mode), static_cast(kMarkStackModeThreadLocal)); - mark_stack_mode_.StoreRelaxed(kMarkStackModeShared); + mark_stack_mode_.store(kMarkStackModeShared, std::memory_order_relaxed); DisableWeakRefAccessCallback dwrac(this); // Process the thread local mark stacks one last time after switching to the shared mark stack // mode and disable weak ref accesses. @@ -1615,10 +1616,10 @@ void ConcurrentCopying::SwitchToGcExclusiveMarkStackMode() { CHECK(thread_running_gc_ != nullptr); CHECK_EQ(self, thread_running_gc_); CHECK(self->GetThreadLocalMarkStack() == nullptr); - MarkStackMode before_mark_stack_mode = mark_stack_mode_.LoadRelaxed(); + MarkStackMode before_mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed); CHECK_EQ(static_cast(before_mark_stack_mode), static_cast(kMarkStackModeShared)); - mark_stack_mode_.StoreRelaxed(kMarkStackModeGcExclusive); + mark_stack_mode_.store(kMarkStackModeGcExclusive, std::memory_order_relaxed); QuasiAtomic::ThreadFenceForConstructor(); if (kVerboseMode) { LOG(INFO) << "Switched to GC exclusive mark stack mode"; @@ -1630,7 +1631,7 @@ void ConcurrentCopying::CheckEmptyMarkStack() { CHECK(thread_running_gc_ != nullptr); CHECK_EQ(self, thread_running_gc_); CHECK(self->GetThreadLocalMarkStack() == nullptr); - MarkStackMode mark_stack_mode = mark_stack_mode_.LoadRelaxed(); + MarkStackMode mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed); if (mark_stack_mode == kMarkStackModeThreadLocal) { // Thread-local mark stack mode. RevokeThreadLocalMarkStacks(false, nullptr); @@ -1738,9 +1739,9 @@ void ConcurrentCopying::ReclaimPhase() { } IssueEmptyCheckpoint(); // Disable the check. - is_mark_stack_push_disallowed_.StoreSequentiallyConsistent(0); + is_mark_stack_push_disallowed_.store(0, std::memory_order_seq_cst); if (kUseBakerReadBarrier) { - updated_all_immune_objects_.StoreSequentiallyConsistent(false); + updated_all_immune_objects_.store(false, std::memory_order_seq_cst); } CheckEmptyMarkStack(); } @@ -1753,10 +1754,10 @@ void ConcurrentCopying::ReclaimPhase() { const uint64_t from_objects = region_space_->GetObjectsAllocatedInFromSpace(); const uint64_t unevac_from_bytes = region_space_->GetBytesAllocatedInUnevacFromSpace(); const uint64_t unevac_from_objects = region_space_->GetObjectsAllocatedInUnevacFromSpace(); - uint64_t to_bytes = bytes_moved_.LoadSequentiallyConsistent(); - cumulative_bytes_moved_.FetchAndAddRelaxed(to_bytes); - uint64_t to_objects = objects_moved_.LoadSequentiallyConsistent(); - cumulative_objects_moved_.FetchAndAddRelaxed(to_objects); + uint64_t to_bytes = bytes_moved_.load(std::memory_order_seq_cst); + cumulative_bytes_moved_.fetch_add(to_bytes, std::memory_order_relaxed); + uint64_t to_objects = objects_moved_.load(std::memory_order_seq_cst); + cumulative_objects_moved_.fetch_add(to_objects, std::memory_order_relaxed); if (kEnableFromSpaceAccountingCheck) { CHECK_EQ(from_space_num_objects_at_first_pause_, from_objects + unevac_from_objects); CHECK_EQ(from_space_num_bytes_at_first_pause_, from_bytes + unevac_from_bytes); @@ -1787,12 +1788,12 @@ void ConcurrentCopying::ReclaimPhase() { << " unevac_from_space size=" << region_space_->UnevacFromSpaceSize() << " to_space size=" << region_space_->ToSpaceSize(); LOG(INFO) << "(before) num_bytes_allocated=" - << heap_->num_bytes_allocated_.LoadSequentiallyConsistent(); + << heap_->num_bytes_allocated_.load(std::memory_order_seq_cst); } RecordFree(ObjectBytePair(freed_objects, freed_bytes)); if (kVerboseMode) { LOG(INFO) << "(after) num_bytes_allocated=" - << heap_->num_bytes_allocated_.LoadSequentiallyConsistent(); + << heap_->num_bytes_allocated_.load(std::memory_order_seq_cst); } } @@ -2042,7 +2043,7 @@ void ConcurrentCopying::AssertToSpaceInvariantInNonMovingSpace(mirror::Object* o if (Thread::Current() == thread_running_gc_ && !gc_grays_immune_objects_) { return; } - bool updated_all_immune_objects = updated_all_immune_objects_.LoadSequentiallyConsistent(); + bool updated_all_immune_objects = updated_all_immune_objects_.load(std::memory_order_seq_cst); CHECK(updated_all_immune_objects || ref->GetReadBarrierState() == ReadBarrier::GrayState()) << "Unmarked immune space ref. obj=" << obj << " rb_state=" << (obj != nullptr ? obj->GetReadBarrierState() : 0U) @@ -2165,7 +2166,7 @@ inline void ConcurrentCopying::VisitRoots( mirror::Object* expected_ref = ref; mirror::Object* new_ref = to_ref; do { - if (expected_ref != addr->LoadRelaxed()) { + if (expected_ref != addr->load(std::memory_order_relaxed)) { // It was updated by the mutator. break; } @@ -2184,7 +2185,7 @@ inline void ConcurrentCopying::MarkRoot(mirror::CompressedReference::FromMirrorPtr(to_ref); // If the cas fails, then it was updated by the mutator. do { - if (ref != addr->LoadRelaxed().AsMirrorPtr()) { + if (ref != addr->load(std::memory_order_relaxed).AsMirrorPtr()) { // It was updated by the mutator. break; } @@ -2378,8 +2379,9 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, fall_back_to_non_moving = true; if (kVerboseMode) { LOG(INFO) << "Out of memory in the to-space. Fall back to non-moving. skipped_bytes=" - << to_space_bytes_skipped_.LoadSequentiallyConsistent() - << " skipped_objects=" << to_space_objects_skipped_.LoadSequentiallyConsistent(); + << to_space_bytes_skipped_.load(std::memory_order_seq_cst) + << " skipped_objects=" + << to_space_objects_skipped_.load(std::memory_order_seq_cst); } fall_back_to_non_moving = true; to_ref = heap_->non_moving_space_->Alloc(Thread::Current(), obj_size, @@ -2431,9 +2433,9 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, region_space_->FreeLarge(to_ref, bytes_allocated); } else { // Record the lost copy for later reuse. - heap_->num_bytes_allocated_.FetchAndAddSequentiallyConsistent(bytes_allocated); - to_space_bytes_skipped_.FetchAndAddSequentiallyConsistent(bytes_allocated); - to_space_objects_skipped_.FetchAndAddSequentiallyConsistent(1); + heap_->num_bytes_allocated_.fetch_add(bytes_allocated, std::memory_order_seq_cst); + to_space_bytes_skipped_.fetch_add(bytes_allocated, std::memory_order_seq_cst); + to_space_objects_skipped_.fetch_add(1, std::memory_order_seq_cst); MutexLock mu(Thread::Current(), skipped_blocks_lock_); skipped_blocks_map_.insert(std::make_pair(bytes_allocated, reinterpret_cast(to_ref))); @@ -2477,8 +2479,8 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, bool success = from_ref->CasLockWordWeakRelaxed(old_lock_word, new_lock_word); if (LIKELY(success)) { // The CAS succeeded. - objects_moved_.FetchAndAddRelaxed(1); - bytes_moved_.FetchAndAddRelaxed(region_space_alloc_size); + objects_moved_.fetch_add(1, std::memory_order_relaxed); + bytes_moved_.fetch_add(region_space_alloc_size, std::memory_order_relaxed); if (LIKELY(!fall_back_to_non_moving)) { DCHECK(region_space_->IsInToSpace(to_ref)); } else { @@ -2704,9 +2706,10 @@ void ConcurrentCopying::FinishPhase() { } if (measure_read_barrier_slow_path_) { MutexLock mu(self, rb_slow_path_histogram_lock_); - rb_slow_path_time_histogram_.AdjustAndAddValue(rb_slow_path_ns_.LoadRelaxed()); - rb_slow_path_count_total_ += rb_slow_path_count_.LoadRelaxed(); - rb_slow_path_count_gc_total_ += rb_slow_path_count_gc_.LoadRelaxed(); + rb_slow_path_time_histogram_.AdjustAndAddValue( + rb_slow_path_ns_.load(std::memory_order_relaxed)); + rb_slow_path_count_total_ += rb_slow_path_count_.load(std::memory_order_relaxed); + rb_slow_path_count_gc_total_ += rb_slow_path_count_gc_.load(std::memory_order_relaxed); } } @@ -2760,15 +2763,15 @@ void ConcurrentCopying::RevokeAllThreadLocalBuffers() { mirror::Object* ConcurrentCopying::MarkFromReadBarrierWithMeasurements(mirror::Object* from_ref) { if (Thread::Current() != thread_running_gc_) { - rb_slow_path_count_.FetchAndAddRelaxed(1u); + rb_slow_path_count_.fetch_add(1u, std::memory_order_relaxed); } else { - rb_slow_path_count_gc_.FetchAndAddRelaxed(1u); + rb_slow_path_count_gc_.fetch_add(1u, std::memory_order_relaxed); } ScopedTrace tr(__FUNCTION__); const uint64_t start_time = measure_read_barrier_slow_path_ ? NanoTime() : 0u; mirror::Object* ret = Mark(from_ref); if (measure_read_barrier_slow_path_) { - rb_slow_path_ns_.FetchAndAddRelaxed(NanoTime() - start_time); + rb_slow_path_ns_.fetch_add(NanoTime() - start_time, std::memory_order_relaxed); } return ret; } @@ -2787,8 +2790,10 @@ void ConcurrentCopying::DumpPerformanceInfo(std::ostream& os) { if (rb_slow_path_count_gc_total_ > 0) { os << "GC slow path count " << rb_slow_path_count_gc_total_ << "\n"; } - os << "Cumulative bytes moved " << cumulative_bytes_moved_.LoadRelaxed() << "\n"; - os << "Cumulative objects moved " << cumulative_objects_moved_.LoadRelaxed() << "\n"; + os << "Cumulative bytes moved " + << cumulative_bytes_moved_.load(std::memory_order_relaxed) << "\n"; + os << "Cumulative objects moved " + << cumulative_objects_moved_.load(std::memory_order_relaxed) << "\n"; os << "Peak regions allocated " << region_space_->GetMaxPeakNumNonFreeRegions() << " (" diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 9ab965ec78..23359640fe 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -116,21 +116,21 @@ void MarkSweep::InitializePhase() { mark_stack_ = heap_->GetMarkStack(); DCHECK(mark_stack_ != nullptr); immune_spaces_.Reset(); - no_reference_class_count_.StoreRelaxed(0); - normal_count_.StoreRelaxed(0); - class_count_.StoreRelaxed(0); - object_array_count_.StoreRelaxed(0); - other_count_.StoreRelaxed(0); - reference_count_.StoreRelaxed(0); - large_object_test_.StoreRelaxed(0); - large_object_mark_.StoreRelaxed(0); - overhead_time_ .StoreRelaxed(0); - work_chunks_created_.StoreRelaxed(0); - work_chunks_deleted_.StoreRelaxed(0); - mark_null_count_.StoreRelaxed(0); - mark_immune_count_.StoreRelaxed(0); - mark_fastpath_count_.StoreRelaxed(0); - mark_slowpath_count_.StoreRelaxed(0); + no_reference_class_count_.store(0, std::memory_order_relaxed); + normal_count_.store(0, std::memory_order_relaxed); + class_count_.store(0, std::memory_order_relaxed); + object_array_count_.store(0, std::memory_order_relaxed); + other_count_.store(0, std::memory_order_relaxed); + reference_count_.store(0, std::memory_order_relaxed); + large_object_test_.store(0, std::memory_order_relaxed); + large_object_mark_.store(0, std::memory_order_relaxed); + overhead_time_ .store(0, std::memory_order_relaxed); + work_chunks_created_.store(0, std::memory_order_relaxed); + work_chunks_deleted_.store(0, std::memory_order_relaxed); + mark_null_count_.store(0, std::memory_order_relaxed); + mark_immune_count_.store(0, std::memory_order_relaxed); + mark_fastpath_count_.store(0, std::memory_order_relaxed); + mark_slowpath_count_.store(0, std::memory_order_relaxed); { // TODO: I don't think we should need heap bitmap lock to Get the mark bitmap. ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); @@ -724,7 +724,7 @@ class MarkSweep::MarkStackTask : public Task { if (kUseFinger) { std::atomic_thread_fence(std::memory_order_seq_cst); if (reinterpret_cast(ref) >= - static_cast(mark_sweep_->atomic_finger_.LoadRelaxed())) { + static_cast(mark_sweep_->atomic_finger_.load(std::memory_order_relaxed))) { return; } } @@ -1046,7 +1046,7 @@ void MarkSweep::RecursiveMark() { // This function does not handle heap end increasing, so we must use the space end. uintptr_t begin = reinterpret_cast(space->Begin()); uintptr_t end = reinterpret_cast(space->End()); - atomic_finger_.StoreRelaxed(AtomicInteger::MaxValue()); + atomic_finger_.store(AtomicInteger::MaxValue(), std::memory_order_relaxed); // Create a few worker tasks. const size_t n = thread_count * 2; @@ -1405,8 +1405,8 @@ void MarkSweep::ProcessMarkStackParallel(size_t thread_count) { thread_pool->Wait(self, true, true); thread_pool->StopWorkers(self); mark_stack_->Reset(); - CHECK_EQ(work_chunks_created_.LoadSequentiallyConsistent(), - work_chunks_deleted_.LoadSequentiallyConsistent()) + CHECK_EQ(work_chunks_created_.load(std::memory_order_seq_cst), + work_chunks_deleted_.load(std::memory_order_seq_cst)) << " some of the work chunks were leaked"; } @@ -1462,28 +1462,32 @@ void MarkSweep::FinishPhase() { if (kCountScannedTypes) { VLOG(gc) << "MarkSweep scanned" - << " no reference objects=" << no_reference_class_count_.LoadRelaxed() - << " normal objects=" << normal_count_.LoadRelaxed() - << " classes=" << class_count_.LoadRelaxed() - << " object arrays=" << object_array_count_.LoadRelaxed() - << " references=" << reference_count_.LoadRelaxed() - << " other=" << other_count_.LoadRelaxed(); + << " no reference objects=" << no_reference_class_count_.load(std::memory_order_relaxed) + << " normal objects=" << normal_count_.load(std::memory_order_relaxed) + << " classes=" << class_count_.load(std::memory_order_relaxed) + << " object arrays=" << object_array_count_.load(std::memory_order_relaxed) + << " references=" << reference_count_.load(std::memory_order_relaxed) + << " other=" << other_count_.load(std::memory_order_relaxed); } if (kCountTasks) { - VLOG(gc) << "Total number of work chunks allocated: " << work_chunks_created_.LoadRelaxed(); + VLOG(gc) + << "Total number of work chunks allocated: " + << work_chunks_created_.load(std::memory_order_relaxed); } if (kMeasureOverhead) { - VLOG(gc) << "Overhead time " << PrettyDuration(overhead_time_.LoadRelaxed()); + VLOG(gc) << "Overhead time " << PrettyDuration(overhead_time_.load(std::memory_order_relaxed)); } if (kProfileLargeObjects) { - VLOG(gc) << "Large objects tested " << large_object_test_.LoadRelaxed() - << " marked " << large_object_mark_.LoadRelaxed(); + VLOG(gc) + << "Large objects tested " << large_object_test_.load(std::memory_order_relaxed) + << " marked " << large_object_mark_.load(std::memory_order_relaxed); } if (kCountMarkedObjects) { - VLOG(gc) << "Marked: null=" << mark_null_count_.LoadRelaxed() - << " immune=" << mark_immune_count_.LoadRelaxed() - << " fastpath=" << mark_fastpath_count_.LoadRelaxed() - << " slowpath=" << mark_slowpath_count_.LoadRelaxed(); + VLOG(gc) + << "Marked: null=" << mark_null_count_.load(std::memory_order_relaxed) + << " immune=" << mark_immune_count_.load(std::memory_order_relaxed) + << " fastpath=" << mark_fastpath_count_.load(std::memory_order_relaxed) + << " slowpath=" << mark_slowpath_count_.load(std::memory_order_relaxed); } CHECK(mark_stack_->IsEmpty()); // Ensure that the mark stack is empty. mark_stack_->Reset(); diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 41ee18350d..948d23303c 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -156,7 +156,7 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, pre_fence_visitor(obj, usable_size); QuasiAtomic::ThreadFenceForConstructor(); size_t num_bytes_allocated_before = - num_bytes_allocated_.FetchAndAddRelaxed(bytes_tl_bulk_allocated); + num_bytes_allocated_.fetch_add(bytes_tl_bulk_allocated, std::memory_order_relaxed); new_num_bytes_allocated = num_bytes_allocated_before + bytes_tl_bulk_allocated; if (bytes_tl_bulk_allocated > 0) { // Only trace when we get an increase in the number of bytes allocated. This happens when @@ -187,7 +187,7 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, DCHECK(allocation_records_ != nullptr); allocation_records_->RecordAllocation(self, &obj, bytes_allocated); } - AllocationListener* l = alloc_listener_.LoadSequentiallyConsistent(); + AllocationListener* l = alloc_listener_.load(std::memory_order_seq_cst); if (l != nullptr) { // Same as above. We assume that a listener that was once stored will never be deleted. // Otherwise we'd have to perform this under a lock. @@ -393,7 +393,7 @@ inline bool Heap::ShouldAllocLargeObject(ObjPtr c, size_t byte_co inline bool Heap::IsOutOfMemoryOnAllocation(AllocatorType allocator_type, size_t alloc_size, bool grow) { - size_t new_footprint = num_bytes_allocated_.LoadSequentiallyConsistent() + alloc_size; + size_t new_footprint = num_bytes_allocated_.load(std::memory_order_seq_cst) + alloc_size; if (UNLIKELY(new_footprint > max_allowed_footprint_)) { if (UNLIKELY(new_footprint > growth_limit_)) { return true; diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index a725ec40b6..52afb3850c 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -549,7 +549,7 @@ Heap::Heap(size_t initial_size, AddRememberedSet(non_moving_space_rem_set); } // TODO: Count objects in the image space here? - num_bytes_allocated_.StoreRelaxed(0); + num_bytes_allocated_.store(0, std::memory_order_relaxed); mark_stack_.reset(accounting::ObjectStack::Create("mark stack", kDefaultMarkStackSize, kDefaultMarkStackSize)); const size_t alloc_stack_capacity = max_allocation_stack_size_ + kAllocationStackReserveSize; @@ -1053,7 +1053,8 @@ void Heap::DumpGcPerformanceInfo(std::ostream& os) { } os << "Registered native bytes allocated: " - << old_native_bytes_allocated_.LoadRelaxed() + new_native_bytes_allocated_.LoadRelaxed() + << (old_native_bytes_allocated_.load(std::memory_order_relaxed) + + new_native_bytes_allocated_.load(std::memory_order_relaxed)) << "\n"; BaseMutex::DumpAll(os); @@ -1120,11 +1121,7 @@ void Heap::DumpBlockingGcCountRateHistogram(std::ostream& os) const { ALWAYS_INLINE static inline AllocationListener* GetAndOverwriteAllocationListener( Atomic* storage, AllocationListener* new_value) { - AllocationListener* old; - do { - old = storage->LoadSequentiallyConsistent(); - } while (!storage->CompareAndSetStrongSequentiallyConsistent(old, new_value)); - return old; + return storage->exchange(new_value); } Heap::~Heap() { @@ -1142,12 +1139,11 @@ Heap::~Heap() { delete thread_flip_lock_; delete pending_task_lock_; delete backtrace_lock_; - if (unique_backtrace_count_.LoadRelaxed() != 0 || seen_backtrace_count_.LoadRelaxed() != 0) { - LOG(INFO) << "gc stress unique=" << unique_backtrace_count_.LoadRelaxed() - << " total=" << seen_backtrace_count_.LoadRelaxed() + - unique_backtrace_count_.LoadRelaxed(); + uint64_t unique_count = unique_backtrace_count_.load(std::memory_order_relaxed); + uint64_t seen_count = seen_backtrace_count_.load(std::memory_order_relaxed); + if (unique_count != 0 || seen_count != 0) { + LOG(INFO) << "gc stress unique=" << unique_count << " total=" << (unique_count + seen_count); } - VLOG(heap) << "Finished ~Heap()"; } @@ -1493,7 +1489,7 @@ void Heap::VerifyObjectBody(ObjPtr obj) { } // Ignore early dawn of the universe verifications. - if (UNLIKELY(static_cast(num_bytes_allocated_.LoadRelaxed()) < 10 * KB)) { + if (UNLIKELY(num_bytes_allocated_.load(std::memory_order_relaxed) < 10 * KB)) { return; } CHECK_ALIGNED(obj.Ptr(), kObjectAlignment) << "Object isn't aligned"; @@ -1525,9 +1521,10 @@ void Heap::RecordFree(uint64_t freed_objects, int64_t freed_bytes) { // Use signed comparison since freed bytes can be negative when background compaction foreground // transitions occurs. This is caused by the moving objects from a bump pointer space to a // free list backed space typically increasing memory footprint due to padding and binning. - DCHECK_LE(freed_bytes, static_cast(num_bytes_allocated_.LoadRelaxed())); + DCHECK_LE(freed_bytes, + static_cast(num_bytes_allocated_.load(std::memory_order_relaxed))); // Note: This relies on 2s complement for handling negative freed_bytes. - num_bytes_allocated_.FetchAndSubSequentiallyConsistent(static_cast(freed_bytes)); + num_bytes_allocated_.fetch_sub(static_cast(freed_bytes)); if (Runtime::Current()->HasStatsEnabled()) { RuntimeStats* thread_stats = Thread::Current()->GetStats(); thread_stats->freed_objects += freed_objects; @@ -1544,10 +1541,10 @@ void Heap::RecordFreeRevoke() { // ahead-of-time, bulk counting of bytes allocated in rosalloc thread-local buffers. // If there's a concurrent revoke, ok to not necessarily reset num_bytes_freed_revoke_ // all the way to zero exactly as the remainder will be subtracted at the next GC. - size_t bytes_freed = num_bytes_freed_revoke_.LoadSequentiallyConsistent(); - CHECK_GE(num_bytes_freed_revoke_.FetchAndSubSequentiallyConsistent(bytes_freed), + size_t bytes_freed = num_bytes_freed_revoke_.load(); + CHECK_GE(num_bytes_freed_revoke_.fetch_sub(bytes_freed), bytes_freed) << "num_bytes_freed_revoke_ underflow"; - CHECK_GE(num_bytes_allocated_.FetchAndSubSequentiallyConsistent(bytes_freed), + CHECK_GE(num_bytes_allocated_.fetch_sub(bytes_freed), bytes_freed) << "num_bytes_allocated_ underflow"; GetCurrentGcIteration()->SetFreedRevoke(bytes_freed); } @@ -1703,13 +1700,13 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, // Always print that we ran homogeneous space compation since this can cause jank. VLOG(heap) << "Ran heap homogeneous space compaction, " << " requested defragmentation " - << count_requested_homogeneous_space_compaction_.LoadSequentiallyConsistent() + << count_requested_homogeneous_space_compaction_.load() << " performed defragmentation " - << count_performed_homogeneous_space_compaction_.LoadSequentiallyConsistent() + << count_performed_homogeneous_space_compaction_.load() << " ignored homogeneous space compaction " - << count_ignored_homogeneous_space_compaction_.LoadSequentiallyConsistent() + << count_ignored_homogeneous_space_compaction_.load() << " delayed count = " - << count_delayed_oom_.LoadSequentiallyConsistent(); + << count_delayed_oom_.load(); } break; } @@ -1972,7 +1969,7 @@ void Heap::TransitionCollector(CollectorType collector_type) { VLOG(heap) << "TransitionCollector: " << static_cast(collector_type_) << " -> " << static_cast(collector_type); uint64_t start_time = NanoTime(); - uint32_t before_allocated = num_bytes_allocated_.LoadSequentiallyConsistent(); + uint32_t before_allocated = num_bytes_allocated_.load(); Runtime* const runtime = Runtime::Current(); Thread* const self = Thread::Current(); ScopedThreadStateChange tsc(self, kWaitingPerformingGc); @@ -2110,7 +2107,7 @@ void Heap::TransitionCollector(CollectorType collector_type) { ScopedObjectAccess soa(self); soa.Vm()->UnloadNativeLibraries(); } - int32_t after_allocated = num_bytes_allocated_.LoadSequentiallyConsistent(); + int32_t after_allocated = num_bytes_allocated_.load(std::memory_order_seq_cst); int32_t delta_allocated = before_allocated - after_allocated; std::string saved_str; if (delta_allocated >= 0) { @@ -2559,7 +2556,9 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, // Move all bytes from new_native_bytes_allocated_ to // old_native_bytes_allocated_ now that GC has been triggered, resetting // new_native_bytes_allocated_ to zero in the process. - old_native_bytes_allocated_.FetchAndAddRelaxed(new_native_bytes_allocated_.ExchangeRelaxed(0)); + old_native_bytes_allocated_.fetch_add( + new_native_bytes_allocated_.exchange(0, std::memory_order_relaxed), + std::memory_order_relaxed); } DCHECK_LT(gc_type, collector::kGcTypeMax); @@ -2759,7 +2758,7 @@ class VerifyReferenceVisitor : public SingleRootVisitor { : heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {} size_t GetFailureCount() const { - return fail_count_->LoadSequentiallyConsistent(); + return fail_count_->load(std::memory_order_seq_cst); } void operator()(ObjPtr klass ATTRIBUTE_UNUSED, ObjPtr ref) const @@ -2811,7 +2810,7 @@ class VerifyReferenceVisitor : public SingleRootVisitor { // Verify that the reference is live. return true; } - if (fail_count_->FetchAndAddSequentiallyConsistent(1) == 0) { + if (fail_count_->fetch_add(1, std::memory_order_seq_cst) == 0) { // Print message on only on first failure to prevent spam. LOG(ERROR) << "!!!!!!!!!!!!!!Heap corruption detected!!!!!!!!!!!!!!!!!!!"; } @@ -2924,7 +2923,7 @@ class VerifyObjectVisitor { } size_t GetFailureCount() const { - return fail_count_->LoadSequentiallyConsistent(); + return fail_count_->load(std::memory_order_seq_cst); } private: @@ -3605,7 +3604,7 @@ static bool CanAddHeapTask(Thread* self) REQUIRES(!Locks::runtime_shutdown_lock_ } void Heap::ClearConcurrentGCRequest() { - concurrent_gc_pending_.StoreRelaxed(false); + concurrent_gc_pending_.store(false, std::memory_order_relaxed); } void Heap::RequestConcurrentGC(Thread* self, GcCause cause, bool force_full) { @@ -3732,8 +3731,9 @@ void Heap::RevokeThreadLocalBuffers(Thread* thread) { if (rosalloc_space_ != nullptr) { size_t freed_bytes_revoke = rosalloc_space_->RevokeThreadLocalBuffers(thread); if (freed_bytes_revoke > 0U) { - num_bytes_freed_revoke_.FetchAndAddSequentiallyConsistent(freed_bytes_revoke); - CHECK_GE(num_bytes_allocated_.LoadRelaxed(), num_bytes_freed_revoke_.LoadRelaxed()); + num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst); + CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed), + num_bytes_freed_revoke_.load(std::memory_order_relaxed)); } } if (bump_pointer_space_ != nullptr) { @@ -3748,8 +3748,9 @@ void Heap::RevokeRosAllocThreadLocalBuffers(Thread* thread) { if (rosalloc_space_ != nullptr) { size_t freed_bytes_revoke = rosalloc_space_->RevokeThreadLocalBuffers(thread); if (freed_bytes_revoke > 0U) { - num_bytes_freed_revoke_.FetchAndAddSequentiallyConsistent(freed_bytes_revoke); - CHECK_GE(num_bytes_allocated_.LoadRelaxed(), num_bytes_freed_revoke_.LoadRelaxed()); + num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst); + CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed), + num_bytes_freed_revoke_.load(std::memory_order_relaxed)); } } } @@ -3758,8 +3759,9 @@ void Heap::RevokeAllThreadLocalBuffers() { if (rosalloc_space_ != nullptr) { size_t freed_bytes_revoke = rosalloc_space_->RevokeAllThreadLocalBuffers(); if (freed_bytes_revoke > 0U) { - num_bytes_freed_revoke_.FetchAndAddSequentiallyConsistent(freed_bytes_revoke); - CHECK_GE(num_bytes_allocated_.LoadRelaxed(), num_bytes_freed_revoke_.LoadRelaxed()); + num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst); + CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed), + num_bytes_freed_revoke_.load(std::memory_order_relaxed)); } } if (bump_pointer_space_ != nullptr) { @@ -3771,7 +3773,7 @@ void Heap::RevokeAllThreadLocalBuffers() { } bool Heap::IsGCRequestPending() const { - return concurrent_gc_pending_.LoadRelaxed(); + return concurrent_gc_pending_.load(std::memory_order_relaxed); } void Heap::RunFinalization(JNIEnv* env, uint64_t timeout) { @@ -3781,7 +3783,7 @@ void Heap::RunFinalization(JNIEnv* env, uint64_t timeout) { } void Heap::RegisterNativeAllocation(JNIEnv* env, size_t bytes) { - size_t old_value = new_native_bytes_allocated_.FetchAndAddRelaxed(bytes); + size_t old_value = new_native_bytes_allocated_.fetch_add(bytes, std::memory_order_relaxed); if (old_value > NativeAllocationGcWatermark() * HeapGrowthMultiplier() && !IsGCRequestPending()) { @@ -3803,12 +3805,12 @@ void Heap::RegisterNativeFree(JNIEnv*, size_t bytes) { size_t allocated; size_t new_freed_bytes; do { - allocated = new_native_bytes_allocated_.LoadRelaxed(); + allocated = new_native_bytes_allocated_.load(std::memory_order_relaxed); new_freed_bytes = std::min(allocated, bytes); } while (!new_native_bytes_allocated_.CompareAndSetWeakRelaxed(allocated, allocated - new_freed_bytes)); if (new_freed_bytes < bytes) { - old_native_bytes_allocated_.FetchAndSubRelaxed(bytes - new_freed_bytes); + old_native_bytes_allocated_.fetch_sub(bytes - new_freed_bytes, std::memory_order_relaxed); } } @@ -3942,9 +3944,9 @@ void Heap::CheckGcStressMode(Thread* self, ObjPtr* obj) { StackHandleScope<1> hs(self); auto h = hs.NewHandleWrapper(obj); CollectGarbage(/* clear_soft_references */ false); - unique_backtrace_count_.FetchAndAddSequentiallyConsistent(1); + unique_backtrace_count_.fetch_add(1, std::memory_order_seq_cst); } else { - seen_backtrace_count_.FetchAndAddSequentiallyConsistent(1); + seen_backtrace_count_.fetch_add(1, std::memory_order_seq_cst); } } } @@ -4020,11 +4022,11 @@ void Heap::RemoveAllocationListener() { } void Heap::SetGcPauseListener(GcPauseListener* l) { - gc_pause_listener_.StoreRelaxed(l); + gc_pause_listener_.store(l, std::memory_order_relaxed); } void Heap::RemoveGcPauseListener() { - gc_pause_listener_.StoreRelaxed(nullptr); + gc_pause_listener_.store(nullptr, std::memory_order_relaxed); } mirror::Object* Heap::AllocWithNewTLAB(Thread* self, diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 021fe58cf0..9af57d17e5 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -496,7 +496,7 @@ class Heap { // Returns the number of bytes currently allocated. size_t GetBytesAllocated() const { - return num_bytes_allocated_.LoadSequentiallyConsistent(); + return num_bytes_allocated_.load(std::memory_order_seq_cst); } // Returns the number of objects currently allocated. @@ -546,7 +546,7 @@ class Heap { // Returns how much free memory we have until we need to grow the heap to perform an allocation. // Similar to GetFreeMemoryUntilGC. Implements java.lang.Runtime.freeMemory. size_t GetFreeMemory() const { - size_t byte_allocated = num_bytes_allocated_.LoadSequentiallyConsistent(); + size_t byte_allocated = num_bytes_allocated_.load(std::memory_order_seq_cst); size_t total_memory = GetTotalMemory(); // Make sure we don't get a negative number. return total_memory - std::min(total_memory, byte_allocated); @@ -775,11 +775,11 @@ class Heap { // Allocation tracking support // Callers to this function use double-checked locking to ensure safety on allocation_records_ bool IsAllocTrackingEnabled() const { - return alloc_tracking_enabled_.LoadRelaxed(); + return alloc_tracking_enabled_.load(std::memory_order_relaxed); } void SetAllocTrackingEnabled(bool enabled) REQUIRES(Locks::alloc_tracker_lock_) { - alloc_tracking_enabled_.StoreRelaxed(enabled); + alloc_tracking_enabled_.store(enabled, std::memory_order_relaxed); } AllocRecordObjectMap* GetAllocationRecords() const @@ -825,7 +825,7 @@ class Heap { void SetGcPauseListener(GcPauseListener* l); // Get the currently installed gc pause listener, or null. GcPauseListener* GetGcPauseListener() { - return gc_pause_listener_.LoadAcquire(); + return gc_pause_listener_.load(std::memory_order_acquire); } // Remove a gc pause listener. Note: the listener must not be deleted, as for performance // reasons, we assume it stays valid when we read it (so that we don't require a lock). diff --git a/runtime/gc/space/bump_pointer_space-inl.h b/runtime/gc/space/bump_pointer_space-inl.h index 9ebb131ad1..4c585497ec 100644 --- a/runtime/gc/space/bump_pointer_space-inl.h +++ b/runtime/gc/space/bump_pointer_space-inl.h @@ -46,16 +46,18 @@ inline mirror::Object* BumpPointerSpace::AllocThreadUnsafe(Thread* self, size_t size_t* bytes_tl_bulk_allocated) { Locks::mutator_lock_->AssertExclusiveHeld(self); num_bytes = RoundUp(num_bytes, kAlignment); - uint8_t* end = end_.LoadRelaxed(); + uint8_t* end = end_.load(std::memory_order_relaxed); if (end + num_bytes > growth_end_) { return nullptr; } mirror::Object* obj = reinterpret_cast(end); - end_.StoreRelaxed(end + num_bytes); + end_.store(end + num_bytes, std::memory_order_relaxed); *bytes_allocated = num_bytes; // Use the CAS free versions as an optimization. - objects_allocated_.StoreRelaxed(objects_allocated_.LoadRelaxed() + 1); - bytes_allocated_.StoreRelaxed(bytes_allocated_.LoadRelaxed() + num_bytes); + objects_allocated_.store(objects_allocated_.load(std::memory_order_relaxed) + 1, + std::memory_order_relaxed); + bytes_allocated_.store(bytes_allocated_.load(std::memory_order_relaxed) + num_bytes, + std::memory_order_relaxed); if (UNLIKELY(usable_size != nullptr)) { *usable_size = num_bytes; } @@ -68,7 +70,7 @@ inline mirror::Object* BumpPointerSpace::AllocNonvirtualWithoutAccounting(size_t uint8_t* old_end; uint8_t* new_end; do { - old_end = end_.LoadRelaxed(); + old_end = end_.load(std::memory_order_relaxed); new_end = old_end + num_bytes; // If there is no more room in the region, we are out of memory. if (UNLIKELY(new_end > growth_end_)) { @@ -81,8 +83,8 @@ inline mirror::Object* BumpPointerSpace::AllocNonvirtualWithoutAccounting(size_t inline mirror::Object* BumpPointerSpace::AllocNonvirtual(size_t num_bytes) { mirror::Object* ret = AllocNonvirtualWithoutAccounting(num_bytes); if (ret != nullptr) { - objects_allocated_.FetchAndAddSequentiallyConsistent(1); - bytes_allocated_.FetchAndAddSequentiallyConsistent(num_bytes); + objects_allocated_.fetch_add(1, std::memory_order_seq_cst); + bytes_allocated_.fetch_add(num_bytes, std::memory_order_seq_cst); } return ret; } diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc index ce0e0f3630..e95da01d8c 100644 --- a/runtime/gc/space/bump_pointer_space.cc +++ b/runtime/gc/space/bump_pointer_space.cc @@ -72,8 +72,8 @@ void BumpPointerSpace::Clear() { // Reset the end of the space back to the beginning, we move the end forward as we allocate // objects. SetEnd(Begin()); - objects_allocated_.StoreRelaxed(0); - bytes_allocated_.StoreRelaxed(0); + objects_allocated_.store(0, std::memory_order_relaxed); + bytes_allocated_.store(0, std::memory_order_relaxed); growth_end_ = Limit(); { MutexLock mu(Thread::Current(), block_lock_); @@ -160,7 +160,7 @@ accounting::ContinuousSpaceBitmap::SweepCallback* BumpPointerSpace::GetSweepCall uint64_t BumpPointerSpace::GetBytesAllocated() { // Start out pre-determined amount (blocks which are not being allocated into). - uint64_t total = static_cast(bytes_allocated_.LoadRelaxed()); + uint64_t total = static_cast(bytes_allocated_.load(std::memory_order_relaxed)); Thread* self = Thread::Current(); MutexLock mu(self, *Locks::runtime_shutdown_lock_); MutexLock mu2(self, *Locks::thread_list_lock_); @@ -178,7 +178,7 @@ uint64_t BumpPointerSpace::GetBytesAllocated() { uint64_t BumpPointerSpace::GetObjectsAllocated() { // Start out pre-determined amount (blocks which are not being allocated into). - uint64_t total = static_cast(objects_allocated_.LoadRelaxed()); + uint64_t total = static_cast(objects_allocated_.load(std::memory_order_relaxed)); Thread* self = Thread::Current(); MutexLock mu(self, *Locks::runtime_shutdown_lock_); MutexLock mu2(self, *Locks::thread_list_lock_); @@ -195,8 +195,8 @@ uint64_t BumpPointerSpace::GetObjectsAllocated() { } void BumpPointerSpace::RevokeThreadLocalBuffersLocked(Thread* thread) { - objects_allocated_.FetchAndAddSequentiallyConsistent(thread->GetThreadLocalObjectsAllocated()); - bytes_allocated_.FetchAndAddSequentiallyConsistent(thread->GetThreadLocalBytesAllocated()); + objects_allocated_.fetch_add(thread->GetThreadLocalObjectsAllocated(), std::memory_order_seq_cst); + bytes_allocated_.fetch_add(thread->GetThreadLocalBytesAllocated(), std::memory_order_seq_cst); thread->SetTlab(nullptr, nullptr, nullptr); } diff --git a/runtime/gc/space/bump_pointer_space.h b/runtime/gc/space/bump_pointer_space.h index 7b43362c2d..5ba13ca3ff 100644 --- a/runtime/gc/space/bump_pointer_space.h +++ b/runtime/gc/space/bump_pointer_space.h @@ -155,8 +155,8 @@ class BumpPointerSpace FINAL : public ContinuousMemMapAllocSpace { // Record objects / bytes freed. void RecordFree(int32_t objects, int32_t bytes) { - objects_allocated_.FetchAndSubSequentiallyConsistent(objects); - bytes_allocated_.FetchAndSubSequentiallyConsistent(bytes); + objects_allocated_.fetch_sub(objects, std::memory_order_seq_cst); + bytes_allocated_.fetch_sub(bytes, std::memory_order_seq_cst); } void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) OVERRIDE diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index c100bc0c75..e2154b8e4d 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -672,7 +672,7 @@ class ImageSpaceLoader { // Loaded the map, use the image header from the file now in case we patch it with // RelocateInPlace. image_header = reinterpret_cast(map->Begin()); - const uint32_t bitmap_index = ImageSpace::bitmap_index_.FetchAndAddSequentiallyConsistent(1); + const uint32_t bitmap_index = ImageSpace::bitmap_index_.fetch_add(1, std::memory_order_seq_cst); std::string bitmap_name(StringPrintf("imagespace %s live-bitmap %u", image_filename, bitmap_index)); diff --git a/runtime/gc/space/region_space-inl.h b/runtime/gc/space/region_space-inl.h index 410931cbe5..7072a7e4cc 100644 --- a/runtime/gc/space/region_space-inl.h +++ b/runtime/gc/space/region_space-inl.h @@ -100,13 +100,13 @@ inline mirror::Object* RegionSpace::Region::Alloc(size_t num_bytes, uint8_t* old_top; uint8_t* new_top; do { - old_top = top_.LoadRelaxed(); + old_top = top_.load(std::memory_order_relaxed); new_top = old_top + num_bytes; if (UNLIKELY(new_top > end_)) { return nullptr; } } while (!top_.CompareAndSetWeakRelaxed(old_top, new_top)); - objects_allocated_.FetchAndAddRelaxed(1); + objects_allocated_.fetch_add(1, std::memory_order_relaxed); DCHECK_LE(Top(), end_); DCHECK_LT(old_top, end_); DCHECK_LE(new_top, end_); @@ -365,11 +365,11 @@ inline size_t RegionSpace::Region::BytesAllocated() const { inline size_t RegionSpace::Region::ObjectsAllocated() const { if (IsLarge()) { DCHECK_LT(begin_ + kRegionSize, Top()); - DCHECK_EQ(objects_allocated_.LoadRelaxed(), 0U); + DCHECK_EQ(objects_allocated_.load(std::memory_order_relaxed), 0U); return 1; } else if (IsLargeTail()) { DCHECK_EQ(begin_, Top()); - DCHECK_EQ(objects_allocated_.LoadRelaxed(), 0U); + DCHECK_EQ(objects_allocated_.load(std::memory_order_relaxed), 0U); return 0; } else { DCHECK(IsAllocated()) << "state=" << state_; diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc index 8d94c86701..5ea434a318 100644 --- a/runtime/gc/space/region_space.cc +++ b/runtime/gc/space/region_space.cc @@ -489,7 +489,7 @@ void RegionSpace::DumpNonFreeRegions(std::ostream& os) { void RegionSpace::RecordAlloc(mirror::Object* ref) { CHECK(ref != nullptr); Region* r = RefToRegion(ref); - r->objects_allocated_.FetchAndAddSequentiallyConsistent(1); + r->objects_allocated_.fetch_add(1, std::memory_order_seq_cst); } bool RegionSpace::AllocNewTlab(Thread* self, size_t min_bytes) { @@ -589,10 +589,10 @@ size_t RegionSpace::AllocationSizeNonvirtual(mirror::Object* obj, size_t* usable } void RegionSpace::Region::Clear(bool zero_and_release_pages) { - top_.StoreRelaxed(begin_); + top_.store(begin_, std::memory_order_relaxed); state_ = RegionState::kRegionStateFree; type_ = RegionType::kRegionTypeNone; - objects_allocated_.StoreRelaxed(0); + objects_allocated_.store(0, std::memory_order_relaxed); alloc_time_ = 0; live_bytes_ = static_cast(-1); if (zero_and_release_pages) { diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h index d63257d928..6a1371af10 100644 --- a/runtime/gc/space/region_space.h +++ b/runtime/gc/space/region_space.h @@ -300,11 +300,11 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { void Init(size_t idx, uint8_t* begin, uint8_t* end) { idx_ = idx; begin_ = begin; - top_.StoreRelaxed(begin); + top_.store(begin, std::memory_order_relaxed); end_ = end; state_ = RegionState::kRegionStateFree; type_ = RegionType::kRegionTypeNone; - objects_allocated_.StoreRelaxed(0); + objects_allocated_.store(0, std::memory_order_relaxed); alloc_time_ = 0; live_bytes_ = static_cast(-1); is_newly_allocated_ = false; @@ -334,7 +334,7 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { if (is_free) { DCHECK(IsInNoSpace()); DCHECK_EQ(begin_, Top()); - DCHECK_EQ(objects_allocated_.LoadRelaxed(), 0U); + DCHECK_EQ(objects_allocated_.load(std::memory_order_relaxed), 0U); } return is_free; } @@ -461,11 +461,11 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { } ALWAYS_INLINE uint8_t* Top() const { - return top_.LoadRelaxed(); + return top_.load(std::memory_order_relaxed); } void SetTop(uint8_t* new_top) { - top_.StoreRelaxed(new_top); + top_.store(new_top, std::memory_order_relaxed); } uint8_t* End() const { @@ -480,10 +480,10 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { void RecordThreadLocalAllocations(size_t num_objects, size_t num_bytes) { DCHECK(IsAllocated()); - DCHECK_EQ(objects_allocated_.LoadRelaxed(), 0U); + DCHECK_EQ(objects_allocated_.load(std::memory_order_relaxed), 0U); DCHECK_EQ(Top(), end_); - objects_allocated_.StoreRelaxed(num_objects); - top_.StoreRelaxed(begin_ + num_bytes); + objects_allocated_.store(num_objects, std::memory_order_relaxed); + top_.store(begin_ + num_bytes, std::memory_order_relaxed); DCHECK_LE(Top(), end_); } diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h index 7af19fae61..bc3ab48cf4 100644 --- a/runtime/gc/space/space.h +++ b/runtime/gc/space/space.h @@ -272,7 +272,7 @@ class ContinuousSpace : public Space { // Current address at which the space ends, which may vary as the space is filled. uint8_t* End() const { - return end_.LoadRelaxed(); + return end_.load(std::memory_order_relaxed); } // The end of the address range covered by the space. @@ -283,7 +283,7 @@ class ContinuousSpace : public Space { // Change the end of the space. Be careful with use since changing the end of a space to an // invalid value may break the GC. void SetEnd(uint8_t* end) { - end_.StoreRelaxed(end); + end_.store(end, std::memory_order_relaxed); } void SetLimit(uint8_t* limit) { diff --git a/runtime/gc/space/zygote_space.cc b/runtime/gc/space/zygote_space.cc index cde155fb22..8c73ef9116 100644 --- a/runtime/gc/space/zygote_space.cc +++ b/runtime/gc/space/zygote_space.cc @@ -122,7 +122,7 @@ void ZygoteSpace::SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* ar // Need to mark the card since this will update the mod-union table next GC cycle. card_table->MarkCard(ptrs[i]); } - zygote_space->objects_allocated_.FetchAndSubSequentiallyConsistent(num_ptrs); + zygote_space->objects_allocated_.fetch_sub(num_ptrs, std::memory_order_seq_cst); } } // namespace space diff --git a/runtime/gc/space/zygote_space.h b/runtime/gc/space/zygote_space.h index 08231017e7..10c1398001 100644 --- a/runtime/gc/space/zygote_space.h +++ b/runtime/gc/space/zygote_space.h @@ -67,7 +67,7 @@ class ZygoteSpace FINAL : public ContinuousMemMapAllocSpace { } uint64_t GetObjectsAllocated() { - return objects_allocated_.LoadSequentiallyConsistent(); + return objects_allocated_.load(std::memory_order_seq_cst); } void Clear() OVERRIDE; diff --git a/runtime/gc/task_processor_test.cc b/runtime/gc/task_processor_test.cc index 77b40e4593..38581ce807 100644 --- a/runtime/gc/task_processor_test.cc +++ b/runtime/gc/task_processor_test.cc @@ -37,7 +37,7 @@ class RecursiveTask : public HeapTask { if (max_recursion_ > 0) { task_processor_->AddTask(self, new RecursiveTask(task_processor_, counter_, max_recursion_ - 1)); - counter_->FetchAndAddSequentiallyConsistent(1U); + counter_->fetch_add(1U, std::memory_order_seq_cst); } } @@ -54,7 +54,7 @@ class WorkUntilDoneTask : public SelfDeletingTask { } virtual void Run(Thread* self) OVERRIDE { task_processor_->RunAllTasks(self); - done_running_->StoreSequentiallyConsistent(true); + done_running_->store(true, std::memory_order_seq_cst); } private: @@ -76,7 +76,7 @@ TEST_F(TaskProcessorTest, Interrupt) { thread_pool.StartWorkers(self); ASSERT_FALSE(done_running); // Wait until all the tasks are done, but since we didn't interrupt, done_running should be 0. - while (counter.LoadSequentiallyConsistent() != kRecursion) { + while (counter.load(std::memory_order_seq_cst) != kRecursion) { usleep(10); } ASSERT_FALSE(done_running); @@ -84,11 +84,11 @@ TEST_F(TaskProcessorTest, Interrupt) { thread_pool.Wait(self, true, false); // After the interrupt and wait, the WorkUntilInterruptedTasktask should have terminated and // set done_running_ to true. - ASSERT_TRUE(done_running.LoadSequentiallyConsistent()); + ASSERT_TRUE(done_running.load(std::memory_order_seq_cst)); // Test that we finish remaining tasks before returning from RunTasksUntilInterrupted. - counter.StoreSequentiallyConsistent(0); - done_running.StoreSequentiallyConsistent(false); + counter.store(0, std::memory_order_seq_cst); + done_running.store(false, std::memory_order_seq_cst); // Self interrupt before any of the other tasks run, but since we added them we should keep on // working until all the tasks are completed. task_processor.Stop(self); @@ -96,8 +96,8 @@ TEST_F(TaskProcessorTest, Interrupt) { thread_pool.AddTask(self, new WorkUntilDoneTask(&task_processor, &done_running)); thread_pool.StartWorkers(self); thread_pool.Wait(self, true, false); - ASSERT_TRUE(done_running.LoadSequentiallyConsistent()); - ASSERT_EQ(counter.LoadSequentiallyConsistent(), kRecursion); + ASSERT_TRUE(done_running.load(std::memory_order_seq_cst)); + ASSERT_EQ(counter.load(std::memory_order_seq_cst), kRecursion); } class TestOrderTask : public HeapTask { @@ -137,10 +137,10 @@ TEST_F(TaskProcessorTest, Ordering) { Atomic done_running(false); // Add a task which will wait until interrupted to the thread pool. thread_pool.AddTask(self, new WorkUntilDoneTask(&task_processor, &done_running)); - ASSERT_FALSE(done_running.LoadSequentiallyConsistent()); + ASSERT_FALSE(done_running.load(std::memory_order_seq_cst)); thread_pool.StartWorkers(self); thread_pool.Wait(self, true, false); - ASSERT_TRUE(done_running.LoadSequentiallyConsistent()); + ASSERT_TRUE(done_running.load(std::memory_order_seq_cst)); ASSERT_EQ(counter, kNumTasks); } diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc index da4c4b2fa4..8fe68bd318 100644 --- a/runtime/java_vm_ext.cc +++ b/runtime/java_vm_ext.cc @@ -736,14 +736,14 @@ void JavaVMExt::DisallowNewWeakGlobals() { // mutator lock exclusively held so that we don't have any threads in the middle of // DecodeWeakGlobal. Locks::mutator_lock_->AssertExclusiveHeld(self); - allow_accessing_weak_globals_.StoreSequentiallyConsistent(false); + allow_accessing_weak_globals_.store(false, std::memory_order_seq_cst); } void JavaVMExt::AllowNewWeakGlobals() { CHECK(!kUseReadBarrier); Thread* self = Thread::Current(); MutexLock mu(self, *Locks::jni_weak_globals_lock_); - allow_accessing_weak_globals_.StoreSequentiallyConsistent(true); + allow_accessing_weak_globals_.store(true, std::memory_order_seq_cst); weak_globals_add_condition_.Broadcast(self); } @@ -770,7 +770,7 @@ inline bool JavaVMExt::MayAccessWeakGlobalsUnlocked(Thread* self) const { DCHECK(self != nullptr); return kUseReadBarrier ? self->GetWeakRefAccessEnabled() : - allow_accessing_weak_globals_.LoadSequentiallyConsistent(); + allow_accessing_weak_globals_.load(std::memory_order_seq_cst); } ObjPtr JavaVMExt::DecodeWeakGlobal(Thread* self, IndirectRef ref) { @@ -809,7 +809,7 @@ ObjPtr JavaVMExt::DecodeWeakGlobalDuringShutdown(Thread* self, I } // self can be null during a runtime shutdown. ~Runtime()->~ClassLinker()->DecodeWeakGlobal(). if (!kUseReadBarrier) { - DCHECK(allow_accessing_weak_globals_.LoadSequentiallyConsistent()); + DCHECK(allow_accessing_weak_globals_.load(std::memory_order_seq_cst)); } return weak_globals_.SynchronizedGet(ref); } diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc index 291a983e75..1e61ba0f2d 100644 --- a/runtime/jdwp/jdwp_handler.cc +++ b/runtime/jdwp/jdwp_handler.cc @@ -1625,7 +1625,7 @@ size_t JdwpState::ProcessRequest(Request* request, ExpandBuf* pReply, bool* skip * so waitForDebugger() doesn't return if we stall for a bit here. */ Dbg::GoActive(); - last_activity_time_ms_.StoreSequentiallyConsistent(0); + last_activity_time_ms_.store(0, std::memory_order_seq_cst); } /* @@ -1703,7 +1703,7 @@ size_t JdwpState::ProcessRequest(Request* request, ExpandBuf* pReply, bool* skip * the initial setup. Only update if this is a non-DDMS packet. */ if (request->GetCommandSet() != kJDWPDdmCmdSet) { - last_activity_time_ms_.StoreSequentiallyConsistent(MilliTime()); + last_activity_time_ms_.store(MilliTime(), std::memory_order_seq_cst); } return replyLength; diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc index 557b032154..447e3bf45b 100644 --- a/runtime/jdwp/jdwp_main.cc +++ b/runtime/jdwp/jdwp_main.cc @@ -729,7 +729,7 @@ int64_t JdwpState::LastDebuggerActivity() { return -1; } - int64_t last = last_activity_time_ms_.LoadSequentiallyConsistent(); + int64_t last = last_activity_time_ms_.load(std::memory_order_seq_cst); /* initializing or in the middle of something? */ if (last == 0) { diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index b2d58da80e..1c4b93eb48 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -623,7 +623,7 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { bool JitCodeCache::IsWeakAccessEnabled(Thread* self) const { return kUseReadBarrier ? self->GetWeakRefAccessEnabled() - : is_weak_access_enabled_.LoadSequentiallyConsistent(); + : is_weak_access_enabled_.load(std::memory_order_seq_cst); } void JitCodeCache::WaitUntilInlineCacheAccessible(Thread* self) { @@ -645,13 +645,13 @@ void JitCodeCache::BroadcastForInlineCacheAccess() { void JitCodeCache::AllowInlineCacheAccess() { DCHECK(!kUseReadBarrier); - is_weak_access_enabled_.StoreSequentiallyConsistent(true); + is_weak_access_enabled_.store(true, std::memory_order_seq_cst); BroadcastForInlineCacheAccess(); } void JitCodeCache::DisallowInlineCacheAccess() { DCHECK(!kUseReadBarrier); - is_weak_access_enabled_.StoreSequentiallyConsistent(false); + is_weak_access_enabled_.store(false, std::memory_order_seq_cst); } void JitCodeCache::CopyInlineCacheInto(const InlineCache& ic, @@ -820,7 +820,7 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, // code. GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr)); } - last_update_time_ns_.StoreRelease(NanoTime()); + last_update_time_ns_.store(NanoTime(), std::memory_order_release); VLOG(jit) << "JIT added (osr=" << std::boolalpha << osr << std::noboolalpha << ") " << ArtMethod::PrettyMethod(method) << "@" << method @@ -1647,7 +1647,7 @@ void JitCodeCache::GetProfiledMethods(const std::set& dex_base_loca } uint64_t JitCodeCache::GetLastUpdateTimeNs() const { - return last_update_time_ns_.LoadAcquire(); + return last_update_time_ns_.load(std::memory_order_acquire); } bool JitCodeCache::IsOsrCompiled(ArtMethod* method) { diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index 3ffedcac4b..7a4876c412 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -154,7 +154,7 @@ inline CallSite* DexCache::GetResolvedCallSite(uint32_t call_site_idx) { GcRoot& target = GetResolvedCallSites()[call_site_idx]; Atomic>& ref = reinterpret_cast>&>(target); - return ref.LoadSequentiallyConsistent().Read(); + return ref.load(std::memory_order_seq_cst).Read(); } inline CallSite* DexCache::SetResolvedCallSite(uint32_t call_site_idx, CallSite* call_site) { diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index 55dd51427c..c7561f4278 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -673,7 +673,7 @@ template inline kSize Object::GetFieldAcquire(MemberOffset field_offset) { const uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); const kSize* addr = reinterpret_cast(raw_addr); - return reinterpret_cast*>(addr)->LoadAcquire(); + return reinterpret_cast*>(addr)->load(std::memory_order_acquire); } template @@ -956,7 +956,7 @@ inline ObjPtr Object::CompareAndExchangeFieldObject(MemberOffset field_o uint32_t new_ref(PtrCompression::Compress(new_value)); uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); Atomic* atomic_addr = reinterpret_cast*>(raw_addr); - bool success = atomic_addr->CompareAndExchangeStrongSequentiallyConsistent(&old_ref, new_ref); + bool success = atomic_addr->compare_exchange_strong(old_ref, new_ref, std::memory_order_seq_cst); ObjPtr witness_value(PtrCompression::Decompress(old_ref)); if (kIsDebugBuild) { // Ensure caller has done read barrier on the reference field so it's in the to-space. @@ -986,7 +986,7 @@ inline ObjPtr Object::ExchangeFieldObject(MemberOffset field_offset, uint32_t new_ref(PtrCompression::Compress(new_value)); uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); Atomic* atomic_addr = reinterpret_cast*>(raw_addr); - uint32_t old_ref = atomic_addr->ExchangeSequentiallyConsistent(new_ref); + uint32_t old_ref = atomic_addr->exchange(new_ref, std::memory_order_seq_cst); ObjPtr old_value(PtrCompression::Decompress(old_ref)); if (kIsDebugBuild) { // Ensure caller has done read barrier on the reference field so it's in the to-space. diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index f274cfc2fa..0e03e3741c 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -87,16 +87,18 @@ Object* Object::CopyObject(ObjPtr dest, DCHECK_ALIGNED(dst_bytes, sizeof(uintptr_t)); // Use word sized copies to begin. while (num_bytes >= sizeof(uintptr_t)) { - reinterpret_cast*>(dst_bytes)->StoreRelaxed( - reinterpret_cast*>(src_bytes)->LoadRelaxed()); + reinterpret_cast*>(dst_bytes)->store( + reinterpret_cast*>(src_bytes)->load(std::memory_order_relaxed), + std::memory_order_relaxed); src_bytes += sizeof(uintptr_t); dst_bytes += sizeof(uintptr_t); num_bytes -= sizeof(uintptr_t); } // Copy possible 32 bit word. if (sizeof(uintptr_t) != sizeof(uint32_t) && num_bytes >= sizeof(uint32_t)) { - reinterpret_cast*>(dst_bytes)->StoreRelaxed( - reinterpret_cast*>(src_bytes)->LoadRelaxed()); + reinterpret_cast*>(dst_bytes)->store( + reinterpret_cast*>(src_bytes)->load(std::memory_order_relaxed), + std::memory_order_relaxed); src_bytes += sizeof(uint32_t); dst_bytes += sizeof(uint32_t); num_bytes -= sizeof(uint32_t); @@ -104,8 +106,9 @@ Object* Object::CopyObject(ObjPtr dest, // Copy remaining bytes, avoid going past the end of num_bytes since there may be a redzone // there. while (num_bytes > 0) { - reinterpret_cast*>(dst_bytes)->StoreRelaxed( - reinterpret_cast*>(src_bytes)->LoadRelaxed()); + reinterpret_cast*>(dst_bytes)->store( + reinterpret_cast*>(src_bytes)->load(std::memory_order_relaxed), + std::memory_order_relaxed); src_bytes += sizeof(uint8_t); dst_bytes += sizeof(uint8_t); num_bytes -= sizeof(uint8_t); @@ -173,7 +176,7 @@ Object* Object::Clone(Thread* self) { uint32_t Object::GenerateIdentityHashCode() { uint32_t expected_value, new_value; do { - expected_value = hash_code_seed.LoadRelaxed(); + expected_value = hash_code_seed.load(std::memory_order_relaxed); new_value = expected_value * 1103515245 + 12345; } while (!hash_code_seed.CompareAndSetWeakRelaxed(expected_value, new_value) || (expected_value & LockWord::kHashMask) == 0); @@ -181,7 +184,7 @@ uint32_t Object::GenerateIdentityHashCode() { } void Object::SetHashCodeSeed(uint32_t new_seed) { - hash_code_seed.StoreRelaxed(new_seed); + hash_code_seed.store(new_seed, std::memory_order_relaxed); } int32_t Object::IdentityHashCode() { diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index 95f82cb147..d00c90bcc0 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -730,7 +730,7 @@ class MANAGED LOCKABLE Object { uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); kSize* addr = reinterpret_cast(raw_addr); if (kIsVolatile) { - reinterpret_cast*>(addr)->StoreSequentiallyConsistent(new_value); + reinterpret_cast*>(addr)->store(new_value, std::memory_order_seq_cst); } else { reinterpret_cast*>(addr)->StoreJavaData(new_value); } @@ -742,7 +742,7 @@ class MANAGED LOCKABLE Object { const uint8_t* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); const kSize* addr = reinterpret_cast(raw_addr); if (kIsVolatile) { - return reinterpret_cast*>(addr)->LoadSequentiallyConsistent(); + return reinterpret_cast*>(addr)->load(std::memory_order_seq_cst); } else { return reinterpret_cast*>(addr)->LoadJavaData(); } diff --git a/runtime/mirror/object_reference.h b/runtime/mirror/object_reference.h index cf1f85d236..356fef0d26 100644 --- a/runtime/mirror/object_reference.h +++ b/runtime/mirror/object_reference.h @@ -110,13 +110,13 @@ class MANAGED HeapReference { template MirrorType* AsMirrorPtr() const REQUIRES_SHARED(Locks::mutator_lock_) { return Compression::Decompress( - kIsVolatile ? reference_.LoadSequentiallyConsistent() : reference_.LoadJavaData()); + kIsVolatile ? reference_.load(std::memory_order_seq_cst) : reference_.LoadJavaData()); } template void Assign(MirrorType* other) REQUIRES_SHARED(Locks::mutator_lock_) { if (kIsVolatile) { - reference_.StoreSequentiallyConsistent(Compression::Compress(other)); + reference_.store(Compression::Compress(other), std::memory_order_seq_cst); } else { reference_.StoreJavaData(Compression::Compress(other)); } diff --git a/runtime/monitor.cc b/runtime/monitor.cc index 2a938da15b..e110763300 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -140,7 +140,7 @@ int32_t Monitor::GetHashCode() { } } DCHECK(HasHashCode()); - return hash_code_.LoadRelaxed(); + return hash_code_.load(std::memory_order_relaxed); } bool Monitor::Install(Thread* self) { @@ -155,7 +155,7 @@ bool Monitor::Install(Thread* self) { break; } case LockWord::kHashCode: { - CHECK_EQ(hash_code_.LoadRelaxed(), static_cast(lw.GetHashCode())); + CHECK_EQ(hash_code_.load(std::memory_order_relaxed), static_cast(lw.GetHashCode())); break; } case LockWord::kFatLocked: { diff --git a/runtime/monitor.h b/runtime/monitor.h index 384ebbedaa..6b7604ec8a 100644 --- a/runtime/monitor.h +++ b/runtime/monitor.h @@ -130,7 +130,7 @@ class Monitor { bool IsLocked() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!monitor_lock_); bool HasHashCode() const { - return hash_code_.LoadRelaxed() != 0; + return hash_code_.load(std::memory_order_relaxed) != 0; } MonitorId GetMonitorId() const { diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h index 58f6c04c3e..5035ba077c 100644 --- a/runtime/read_barrier-inl.h +++ b/runtime/read_barrier-inl.h @@ -130,7 +130,7 @@ inline MirrorType* ReadBarrier::BarrierForRoot(MirrorType** root, ref = reinterpret_cast(Mark(old_ref)); // Update the field atomically. This may fail if mutator updates before us, but it's ok. if (ref != old_ref) { - Atomic* atomic_root = reinterpret_cast*>(root); + Atomic* atomic_root = reinterpret_cast*>(root); atomic_root->CompareAndSetStrongRelaxed(old_ref, ref); } } diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index 2f6f50e31e..e34f32e0bf 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -251,6 +251,7 @@ inline ThreadState Thread::TransitionFromSuspendedToRunnable() { union StateAndFlags new_state_and_flags; new_state_and_flags.as_int = old_state_and_flags.as_int; new_state_and_flags.as_struct.state = kRunnable; + // CAS the value with a memory barrier. if (LIKELY(tls32_.state_and_flags.as_atomic_int.CompareAndSetWeakAcquire( old_state_and_flags.as_int, diff --git a/runtime/thread.cc b/runtime/thread.cc index 5b03c2d884..af611152c6 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1280,7 +1280,7 @@ bool Thread::ModifySuspendCountInternal(Thread* self, AtomicClearFlag(kSuspendRequest); } else { // Two bits might be set simultaneously. - tls32_.state_and_flags.as_atomic_int.FetchAndBitwiseOrSequentiallyConsistent(flags); + tls32_.state_and_flags.as_atomic_int.fetch_or(flags, std::memory_order_seq_cst); TriggerSuspend(); } return true; @@ -1318,7 +1318,7 @@ bool Thread::PassActiveSuspendBarriers(Thread* self) { if (pending_threads != nullptr) { bool done = false; do { - int32_t cur_val = pending_threads->LoadRelaxed(); + int32_t cur_val = pending_threads->load(std::memory_order_relaxed); CHECK_GT(cur_val, 0) << "Unexpected value for PassActiveSuspendBarriers(): " << cur_val; // Reduce value by 1. done = pending_threads->CompareAndSetWeakRelaxed(cur_val, cur_val - 1); @@ -1558,7 +1558,7 @@ Closure* Thread::GetFlipFunction() { Atomic* atomic_func = reinterpret_cast*>(&tlsPtr_.flip_function); Closure* func; do { - func = atomic_func->LoadRelaxed(); + func = atomic_func->load(std::memory_order_relaxed); if (func == nullptr) { return nullptr; } @@ -1570,7 +1570,7 @@ Closure* Thread::GetFlipFunction() { void Thread::SetFlipFunction(Closure* function) { CHECK(function != nullptr); Atomic* atomic_func = reinterpret_cast*>(&tlsPtr_.flip_function); - atomic_func->StoreSequentiallyConsistent(function); + atomic_func->store(function, std::memory_order_seq_cst); } void Thread::FullSuspendCheck() { @@ -2102,7 +2102,7 @@ Thread::Thread(bool daemon) "art::Thread has a size which is not a multiple of 4."); tls32_.state_and_flags.as_struct.flags = 0; tls32_.state_and_flags.as_struct.state = kNative; - tls32_.interrupted.StoreRelaxed(false); + tls32_.interrupted.store(false, std::memory_order_relaxed); memset(&tlsPtr_.held_mutexes[0], 0, sizeof(tlsPtr_.held_mutexes)); std::fill(tlsPtr_.rosalloc_runs, tlsPtr_.rosalloc_runs + kNumRosAllocThreadLocalSizeBracketsInThread, @@ -2397,24 +2397,24 @@ bool Thread::IsJWeakCleared(jweak obj) const { bool Thread::Interrupted() { DCHECK_EQ(Thread::Current(), this); // No other thread can concurrently reset the interrupted flag. - bool interrupted = tls32_.interrupted.LoadSequentiallyConsistent(); + bool interrupted = tls32_.interrupted.load(std::memory_order_seq_cst); if (interrupted) { - tls32_.interrupted.StoreSequentiallyConsistent(false); + tls32_.interrupted.store(false, std::memory_order_seq_cst); } return interrupted; } // Implements java.lang.Thread.isInterrupted. bool Thread::IsInterrupted() { - return tls32_.interrupted.LoadSequentiallyConsistent(); + return tls32_.interrupted.load(std::memory_order_seq_cst); } void Thread::Interrupt(Thread* self) { MutexLock mu(self, *wait_mutex_); - if (tls32_.interrupted.LoadSequentiallyConsistent()) { + if (tls32_.interrupted.load(std::memory_order_seq_cst)) { return; } - tls32_.interrupted.StoreSequentiallyConsistent(true); + tls32_.interrupted.store(true, std::memory_order_seq_cst); NotifyLocked(self); } diff --git a/runtime/thread.h b/runtime/thread.h index 6549fc1a1f..a3b0113453 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -541,7 +541,7 @@ class Thread { bool IsInterrupted(); void Interrupt(Thread* self) REQUIRES(!*wait_mutex_); void SetInterrupted(bool i) { - tls32_.interrupted.StoreSequentiallyConsistent(i); + tls32_.interrupted.store(i, std::memory_order_seq_cst); } void Notify() REQUIRES(!*wait_mutex_); @@ -1095,11 +1095,11 @@ class Thread { } void AtomicSetFlag(ThreadFlag flag) { - tls32_.state_and_flags.as_atomic_int.FetchAndBitwiseOrSequentiallyConsistent(flag); + tls32_.state_and_flags.as_atomic_int.fetch_or(flag, std::memory_order_seq_cst); } void AtomicClearFlag(ThreadFlag flag) { - tls32_.state_and_flags.as_atomic_int.FetchAndBitwiseAndSequentiallyConsistent(-1 ^ flag); + tls32_.state_and_flags.as_atomic_int.fetch_and(-1 ^ flag, std::memory_order_seq_cst); } void ResetQuickAllocEntryPointsForThread(bool is_marking); diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 8095ef57c7..44af867d60 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -732,7 +732,7 @@ void ThreadList::SuspendAllInternal(Thread* self, if (reason == SuspendReason::kForDebugger) { ++debug_suspend_all_count_; } - pending_threads.StoreRelaxed(list_.size() - num_ignored); + pending_threads.store(list_.size() - num_ignored, std::memory_order_relaxed); // Increment everybody's suspend count (except those that should be ignored). for (const auto& thread : list_) { if (thread == ignore1 || thread == ignore2) { @@ -748,7 +748,7 @@ void ThreadList::SuspendAllInternal(Thread* self, if (thread->IsSuspended()) { // Only clear the counter for the current thread. thread->ClearSuspendBarrier(&pending_threads); - pending_threads.FetchAndSubSequentiallyConsistent(1); + pending_threads.fetch_sub(1, std::memory_order_seq_cst); } } } @@ -761,7 +761,7 @@ void ThreadList::SuspendAllInternal(Thread* self, #endif const uint64_t start_time = NanoTime(); while (true) { - int32_t cur_val = pending_threads.LoadRelaxed(); + int32_t cur_val = pending_threads.load(std::memory_order_relaxed); if (LIKELY(cur_val > 0)) { #if ART_USE_FUTEXES if (futex(pending_threads.Address(), FUTEX_WAIT, cur_val, &wait_timeout, nullptr, 0) != 0) { diff --git a/runtime/thread_pool_test.cc b/runtime/thread_pool_test.cc index 895a108af0..d7842002ee 100644 --- a/runtime/thread_pool_test.cc +++ b/runtime/thread_pool_test.cc @@ -71,7 +71,7 @@ TEST_F(ThreadPoolTest, CheckRun) { // Wait for tasks to complete. thread_pool.Wait(self, true, false); // Make sure that we finished all the work. - EXPECT_EQ(num_tasks, count.LoadSequentiallyConsistent()); + EXPECT_EQ(num_tasks, count.load(std::memory_order_seq_cst)); } TEST_F(ThreadPoolTest, StopStart) { @@ -84,7 +84,7 @@ TEST_F(ThreadPoolTest, StopStart) { } usleep(200); // Check that no threads started prematurely. - EXPECT_EQ(0, count.LoadSequentiallyConsistent()); + EXPECT_EQ(0, count.load(std::memory_order_seq_cst)); // Signal the threads to start processing tasks. thread_pool.StartWorkers(self); usleep(200); @@ -93,7 +93,7 @@ TEST_F(ThreadPoolTest, StopStart) { thread_pool.AddTask(self, new CountTask(&bad_count)); usleep(200); // Ensure that the task added after the workers were stopped doesn't get run. - EXPECT_EQ(0, bad_count.LoadSequentiallyConsistent()); + EXPECT_EQ(0, bad_count.load(std::memory_order_seq_cst)); // Allow tasks to finish up and delete themselves. thread_pool.StartWorkers(self); thread_pool.Wait(self, false, false); @@ -157,7 +157,7 @@ TEST_F(ThreadPoolTest, RecursiveTest) { thread_pool.AddTask(self, new TreeTask(&thread_pool, &count, depth)); thread_pool.StartWorkers(self); thread_pool.Wait(self, true, false); - EXPECT_EQ((1 << depth) - 1, count.LoadSequentiallyConsistent()); + EXPECT_EQ((1 << depth) - 1, count.load(std::memory_order_seq_cst)); } class PeerTask : public Task { diff --git a/runtime/trace.cc b/runtime/trace.cc index 91d2b3779e..bea510ab61 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -675,7 +675,7 @@ Trace::Trace(File* trace_file, static_assert(18 <= kMinBufSize, "Minimum buffer size not large enough for trace header"); // Update current offset. - cur_offset_.StoreRelaxed(kTraceHeaderLength); + cur_offset_.store(kTraceHeaderLength, std::memory_order_relaxed); if (output_mode == TraceOutputMode::kStreaming) { streaming_lock_ = new Mutex("tracing lock", LockLevel::kTracingStreamingLock); @@ -717,7 +717,7 @@ void Trace::FinishTracing() { // Clean up. STLDeleteValues(&seen_methods_); } else { - final_offset = cur_offset_.LoadRelaxed(); + final_offset = cur_offset_.load(std::memory_order_relaxed); GetVisitedMethods(final_offset, &visited_methods); } @@ -944,7 +944,7 @@ std::string Trace::GetMethodLine(ArtMethod* method) { } void Trace::WriteToBuf(const uint8_t* src, size_t src_size) { - int32_t old_offset = cur_offset_.LoadRelaxed(); + int32_t old_offset = cur_offset_.load(std::memory_order_relaxed); int32_t new_offset = old_offset + static_cast(src_size); if (dchecked_integral_cast(new_offset) > buffer_size_) { // Flush buffer. @@ -957,24 +957,24 @@ void Trace::WriteToBuf(const uint8_t* src, size_t src_size) { if (!trace_file_->WriteFully(src, src_size)) { PLOG(WARNING) << "Failed streaming a tracing event."; } - cur_offset_.StoreRelease(0); // Buffer is empty now. + cur_offset_.store(0, std::memory_order_release); // Buffer is empty now. return; } old_offset = 0; new_offset = static_cast(src_size); } - cur_offset_.StoreRelease(new_offset); + cur_offset_.store(new_offset, std::memory_order_release); // Fill in data. memcpy(buf_.get() + old_offset, src, src_size); } void Trace::FlushBuf() { - int32_t offset = cur_offset_.LoadRelaxed(); + int32_t offset = cur_offset_.load(std::memory_order_relaxed); if (!trace_file_->WriteFully(buf_.get(), offset)) { PLOG(WARNING) << "Failed flush the remaining data in streaming."; } - cur_offset_.StoreRelease(0); + cur_offset_.store(0, std::memory_order_release); } void Trace::LogMethodTraceEvent(Thread* thread, ArtMethod* method, @@ -990,7 +990,7 @@ void Trace::LogMethodTraceEvent(Thread* thread, ArtMethod* method, // We do a busy loop here trying to acquire the next offset. if (trace_output_mode_ != TraceOutputMode::kStreaming) { do { - old_offset = cur_offset_.LoadRelaxed(); + old_offset = cur_offset_.load(std::memory_order_relaxed); new_offset = old_offset + GetRecordSize(clock_source_); if (static_cast(new_offset) > buffer_size_) { overflow_ = true; -- GitLab From 19a7d4f1719c693a5f94e010280eb4aeea9d6b42 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Fri, 23 Mar 2018 10:05:49 -0700 Subject: [PATCH 125/749] Fix incorrect null checks in 2 JVMTI functions The functions GetThreadListStackTraces and GetOwnedMonitorInfo would incorrectly fail to check all their arguments for null-pointers. This could cause crashes if called with incorrect arguments. Bug: 76204023 Test: ./test.py --host -j50 Change-Id: I0be69a63b9bbb0a6aaf1092fde725332ae3296e8 --- openjdkjvmti/ti_stack.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc index 373944f179..b96374d43a 100644 --- a/openjdkjvmti/ti_stack.cc +++ b/openjdkjvmti/ti_stack.cc @@ -484,7 +484,7 @@ jvmtiError StackUtil::GetThreadListStackTraces(jvmtiEnv* env, *stack_info_ptr = nullptr; return ERR(NONE); } - if (stack_info_ptr == nullptr || stack_info_ptr == nullptr) { + if (thread_list == nullptr || stack_info_ptr == nullptr) { return ERR(NULL_POINTER); } @@ -971,7 +971,7 @@ jvmtiError StackUtil::GetOwnedMonitorInfo(jvmtiEnv* env, jthread thread, jint* owned_monitor_count_ptr, jobject** owned_monitors_ptr) { - if (owned_monitors_ptr == nullptr || owned_monitors_ptr == nullptr) { + if (owned_monitor_count_ptr == nullptr || owned_monitors_ptr == nullptr) { return ERR(NULL_POINTER); } auto handle_fun = [&] (art::ScopedObjectAccess& soa, MonitorVisitor& visitor) -- GitLab From 318afe6c3ac1e734adbd769bbf22b8c7e373e80b Mon Sep 17 00:00:00 2001 From: Alex Light Date: Thu, 22 Mar 2018 16:50:10 -0700 Subject: [PATCH 126/749] Remove unneeded ScopedGCCriticalSections from openjdkjvmti. We used ScopedGCCriticalSections in many parts of the openjdkjvmti often unnecessarily. We removed a totally unneeded GCCriticalSection that was acquired when modifying the instrumentation listeners. We also removed RequestGCSafeSynchronousCheckpoint and the change to use GcRoots instead. We added RequestGCSafeSynchronousCheckpoint as a way to prevent the GC from running when we are doing some JVMTI operations on other threads. This could interact with running GCs in non-trivial ways, potentially causing deadlocks in some situations. This changes the code to instead use read-barriers and GcRoots to ensure that we do not read data from the wrong gc space. In order for this to work correctly we need to make sure that we are only ever reading the GcRoots from the thread that eventually needs the reference. This required some re-writing of the checkpoint closures since they would often just call AddLocalReference on non-local Thread objects. Changes to Thread::RequestSynchronousCheckpoint and art::Barrier were needed in order to allow this all to work since we needed to ensure that the requesting thread did not suspend as the checkpoint was being run. This is a partial revert of commit 7585b91bfc77b8. Bug: 67838964 Bug: 76003243 Test: use gapid Test: ./test.py --host -j50 Test: ./art/tools/run-libjdwp-tests.sh --mode=host Change-Id: I26d871089829639eccb973cecc315194f7bcf681 --- openjdkjvmti/events.cc | 3 -- openjdkjvmti/ti_method.cc | 89 +++++++++++++++++++++++-------------- openjdkjvmti/ti_monitor.cc | 31 ++++++++----- openjdkjvmti/ti_stack.cc | 90 +++++++++++++++++++++++--------------- openjdkjvmti/ti_thread.cc | 27 +----------- openjdkjvmti/ti_thread.h | 10 ----- runtime/barrier.cc | 10 ++++- runtime/barrier.h | 9 +++- runtime/thread.cc | 16 ++++--- runtime/thread.h | 19 +++++++- 10 files changed, 173 insertions(+), 131 deletions(-) diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index 07b1529adb..de678711fc 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -940,9 +940,6 @@ void EventHandler::SetupTraceListener(JvmtiMethodTraceListener* listener, } art::ScopedThreadStateChange stsc(art::Thread::Current(), art::ThreadState::kNative); art::instrumentation::Instrumentation* instr = art::Runtime::Current()->GetInstrumentation(); - art::gc::ScopedGCCriticalSection gcs(art::Thread::Current(), - art::gc::kGcCauseInstrumentation, - art::gc::kCollectorTypeInstrumentation); art::ScopedSuspendAll ssa("jvmti method tracing installation"); if (enable) { instr->AddListener(listener, new_events); diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index 83d64ef1d8..bf2e6cd104 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -42,6 +42,7 @@ #include "dex/dex_file_types.h" #include "dex/modifiers.h" #include "events-inl.h" +#include "gc_root-inl.h" #include "jit/jit.h" #include "jni_internal.h" #include "mirror/class-inl.h" @@ -546,13 +547,12 @@ jvmtiError MethodUtil::IsMethodSynthetic(jvmtiEnv* env, jmethodID m, jboolean* i class CommonLocalVariableClosure : public art::Closure { public: - CommonLocalVariableClosure(art::Thread* caller, - jint depth, - jint slot) - : result_(ERR(INTERNAL)), caller_(caller), depth_(depth), slot_(slot) {} + CommonLocalVariableClosure(jint depth, jint slot) + : result_(ERR(INTERNAL)), depth_(depth), slot_(slot) {} void Run(art::Thread* self) OVERRIDE REQUIRES(art::Locks::mutator_lock_) { art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current()); + art::ScopedAssertNoThreadSuspension sants("CommonLocalVariableClosure::Run"); std::unique_ptr context(art::Context::Create()); FindFrameAtDepthVisitor visitor(self, context.get(), depth_); visitor.WalkStack(); @@ -597,17 +597,17 @@ class CommonLocalVariableClosure : public art::Closure { } } - jvmtiError GetResult() const { + virtual jvmtiError GetResult() { return result_; } protected: virtual jvmtiError Execute(art::ArtMethod* method, art::StackVisitor& visitor) - REQUIRES(art::Locks::mutator_lock_) = 0; + REQUIRES_SHARED(art::Locks::mutator_lock_) = 0; virtual jvmtiError GetTypeError(art::ArtMethod* method, art::Primitive::Type type, const std::string& descriptor) - REQUIRES(art::Locks::mutator_lock_) = 0; + REQUIRES_SHARED(art::Locks::mutator_lock_) = 0; jvmtiError GetSlotType(art::ArtMethod* method, uint32_t dex_pc, @@ -674,25 +674,35 @@ class CommonLocalVariableClosure : public art::Closure { } jvmtiError result_; - art::Thread* caller_; jint depth_; jint slot_; }; class GetLocalVariableClosure : public CommonLocalVariableClosure { public: - GetLocalVariableClosure(art::Thread* caller, - jint depth, + GetLocalVariableClosure(jint depth, jint slot, art::Primitive::Type type, jvalue* val) - : CommonLocalVariableClosure(caller, depth, slot), type_(type), val_(val) {} + : CommonLocalVariableClosure(depth, slot), + type_(type), + val_(val), + obj_val_(nullptr) {} + + virtual jvmtiError GetResult() REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (result_ == OK && type_ == art::Primitive::kPrimNot) { + val_->l = obj_val_.IsNull() + ? nullptr + : art::Thread::Current()->GetJniEnv()->AddLocalReference(obj_val_.Read()); + } + return CommonLocalVariableClosure::GetResult(); + } protected: jvmtiError GetTypeError(art::ArtMethod* method ATTRIBUTE_UNUSED, art::Primitive::Type slot_type, const std::string& descriptor ATTRIBUTE_UNUSED) - OVERRIDE REQUIRES(art::Locks::mutator_lock_) { + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { switch (slot_type) { case art::Primitive::kPrimByte: case art::Primitive::kPrimChar: @@ -712,7 +722,7 @@ class GetLocalVariableClosure : public CommonLocalVariableClosure { } jvmtiError Execute(art::ArtMethod* method, art::StackVisitor& visitor) - OVERRIDE REQUIRES(art::Locks::mutator_lock_) { + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { switch (type_) { case art::Primitive::kPrimNot: { uint32_t ptr_val; @@ -722,8 +732,8 @@ class GetLocalVariableClosure : public CommonLocalVariableClosure { &ptr_val)) { return ERR(OPAQUE_FRAME); } - art::ObjPtr obj(reinterpret_cast(ptr_val)); - val_->l = obj.IsNull() ? nullptr : caller_->GetJniEnv()->AddLocalReference(obj); + obj_val_ = art::GcRoot( + reinterpret_cast(ptr_val)); break; } case art::Primitive::kPrimInt: @@ -760,6 +770,7 @@ class GetLocalVariableClosure : public CommonLocalVariableClosure { private: art::Primitive::Type type_; jvalue* val_; + art::GcRoot obj_val_; }; jvmtiError MethodUtil::GetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED, @@ -782,9 +793,12 @@ jvmtiError MethodUtil::GetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED, art::Locks::thread_list_lock_->ExclusiveUnlock(self); return err; } - GetLocalVariableClosure c(self, depth, slot, type, val); - // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. - if (!ThreadUtil::RequestGCSafeSynchronousCheckpoint(target, &c)) { + art::ScopedAssertNoThreadSuspension sants("Performing GetLocalVariable"); + GetLocalVariableClosure c(depth, slot, type, val); + // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. We + // need to avoid suspending as we wait for the checkpoint to occur since we are (potentially) + // transfering a GcRoot across threads. + if (!target->RequestSynchronousCheckpoint(&c, art::ThreadState::kRunnable)) { return ERR(THREAD_NOT_ALIVE); } else { return c.GetResult(); @@ -798,13 +812,13 @@ class SetLocalVariableClosure : public CommonLocalVariableClosure { jint slot, art::Primitive::Type type, jvalue val) - : CommonLocalVariableClosure(caller, depth, slot), type_(type), val_(val) {} + : CommonLocalVariableClosure(depth, slot), caller_(caller), type_(type), val_(val) {} protected: jvmtiError GetTypeError(art::ArtMethod* method, art::Primitive::Type slot_type, const std::string& descriptor) - OVERRIDE REQUIRES(art::Locks::mutator_lock_) { + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { switch (slot_type) { case art::Primitive::kPrimNot: { if (type_ != art::Primitive::kPrimNot) { @@ -840,7 +854,7 @@ class SetLocalVariableClosure : public CommonLocalVariableClosure { } jvmtiError Execute(art::ArtMethod* method, art::StackVisitor& visitor) - OVERRIDE REQUIRES(art::Locks::mutator_lock_) { + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { switch (type_) { case art::Primitive::kPrimNot: { uint32_t ptr_val; @@ -887,6 +901,7 @@ class SetLocalVariableClosure : public CommonLocalVariableClosure { } private: + art::Thread* caller_; art::Primitive::Type type_; jvalue val_; }; @@ -913,7 +928,7 @@ jvmtiError MethodUtil::SetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED, } SetLocalVariableClosure c(self, depth, slot, type, val); // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. - if (!ThreadUtil::RequestGCSafeSynchronousCheckpoint(target, &c)) { + if (!target->RequestSynchronousCheckpoint(&c)) { return ERR(THREAD_NOT_ALIVE); } else { return c.GetResult(); @@ -922,13 +937,13 @@ jvmtiError MethodUtil::SetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED, class GetLocalInstanceClosure : public art::Closure { public: - GetLocalInstanceClosure(art::Thread* caller, jint depth, jobject* val) + explicit GetLocalInstanceClosure(jint depth) : result_(ERR(INTERNAL)), - caller_(caller), depth_(depth), - val_(val) {} + val_(nullptr) {} void Run(art::Thread* self) OVERRIDE REQUIRES(art::Locks::mutator_lock_) { + art::ScopedAssertNoThreadSuspension sants("GetLocalInstanceClosure::Run"); art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current()); std::unique_ptr context(art::Context::Create()); FindFrameAtDepthVisitor visitor(self, context.get(), depth_); @@ -939,19 +954,22 @@ class GetLocalInstanceClosure : public art::Closure { return; } result_ = OK; - art::ObjPtr obj = visitor.GetThisObject(); - *val_ = obj.IsNull() ? nullptr : caller_->GetJniEnv()->AddLocalReference(obj); + val_ = art::GcRoot(visitor.GetThisObject()); } - jvmtiError GetResult() const { + jvmtiError GetResult(jobject* data_out) REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (result_ == OK) { + *data_out = val_.IsNull() + ? nullptr + : art::Thread::Current()->GetJniEnv()->AddLocalReference(val_.Read()); + } return result_; } private: jvmtiError result_; - art::Thread* caller_; jint depth_; - jobject* val_; + art::GcRoot val_; }; jvmtiError MethodUtil::GetLocalInstance(jvmtiEnv* env ATTRIBUTE_UNUSED, @@ -970,12 +988,15 @@ jvmtiError MethodUtil::GetLocalInstance(jvmtiEnv* env ATTRIBUTE_UNUSED, art::Locks::thread_list_lock_->ExclusiveUnlock(self); return err; } - GetLocalInstanceClosure c(self, depth, data); - // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. - if (!ThreadUtil::RequestGCSafeSynchronousCheckpoint(target, &c)) { + art::ScopedAssertNoThreadSuspension sants("Performing GetLocalInstance"); + GetLocalInstanceClosure c(depth); + // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. We + // need to avoid suspending as we wait for the checkpoint to occur since we are (potentially) + // transfering a GcRoot across threads. + if (!target->RequestSynchronousCheckpoint(&c, art::ThreadState::kRunnable)) { return ERR(THREAD_NOT_ALIVE); } else { - return c.GetResult(); + return c.GetResult(data); } } diff --git a/openjdkjvmti/ti_monitor.cc b/openjdkjvmti/ti_monitor.cc index 94408ba186..1cfc64a61d 100644 --- a/openjdkjvmti/ti_monitor.cc +++ b/openjdkjvmti/ti_monitor.cc @@ -37,6 +37,7 @@ #include #include "art_jvmti.h" +#include "gc_root-inl.h" #include "monitor.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" @@ -351,19 +352,17 @@ jvmtiError MonitorUtil::GetCurrentContendedMonitor(jvmtiEnv* env ATTRIBUTE_UNUSE } struct GetContendedMonitorClosure : public art::Closure { public: - explicit GetContendedMonitorClosure(art::Thread* current, jobject* out) - : result_thread_(current), out_(out) {} + GetContendedMonitorClosure() : out_(nullptr) {} void Run(art::Thread* target_thread) REQUIRES_SHARED(art::Locks::mutator_lock_) { + art::ScopedAssertNoThreadSuspension sants("GetContendedMonitorClosure::Run"); switch (target_thread->GetState()) { // These three we are actually currently waiting on a monitor and have sent the appropriate // events (if anyone is listening). case art::kBlocked: case art::kTimedWaiting: case art::kWaiting: { - art::mirror::Object* mon = art::Monitor::GetContendedMonitor(target_thread); - *out_ = (mon == nullptr) ? nullptr - : result_thread_->GetJniEnv()->AddLocalReference(mon); + out_ = art::GcRoot(art::Monitor::GetContendedMonitor(target_thread)); return; } case art::kTerminated: @@ -390,22 +389,30 @@ jvmtiError MonitorUtil::GetCurrentContendedMonitor(jvmtiEnv* env ATTRIBUTE_UNUSE case art::kStarting: case art::kNative: case art::kSuspended: { - // We aren't currently (explicitly) waiting for a monitor anything so just return null. - *out_ = nullptr; + // We aren't currently (explicitly) waiting for a monitor so just return null. return; } } } + jobject GetResult() REQUIRES_SHARED(art::Locks::mutator_lock_) { + return out_.IsNull() + ? nullptr + : art::Thread::Current()->GetJniEnv()->AddLocalReference(out_.Read()); + } + private: - art::Thread* result_thread_; - jobject* out_; + art::GcRoot out_; }; - GetContendedMonitorClosure closure(self, monitor); - // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. - if (!ThreadUtil::RequestGCSafeSynchronousCheckpoint(target, &closure)) { + art::ScopedAssertNoThreadSuspension sants("Performing GetCurrentContendedMonitor"); + GetContendedMonitorClosure closure; + // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. We + // need to avoid suspending as we wait for the checkpoint to occur since we are (potentially) + // transfering a GcRoot across threads. + if (!target->RequestSynchronousCheckpoint(&closure, art::ThreadState::kRunnable)) { return ERR(THREAD_NOT_ALIVE); } + *monitor = closure.GetResult(); return OK; } diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc index 373944f179..3936f7abeb 100644 --- a/openjdkjvmti/ti_stack.cc +++ b/openjdkjvmti/ti_stack.cc @@ -258,7 +258,7 @@ jvmtiError StackUtil::GetStackTrace(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED, static_cast(start_depth), static_cast(max_frame_count)); // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. - if (!ThreadUtil::RequestGCSafeSynchronousCheckpoint(thread, &closure)) { + if (!thread->RequestSynchronousCheckpoint(&closure)) { return ERR(THREAD_NOT_ALIVE); } *count_ptr = static_cast(closure.index); @@ -269,7 +269,7 @@ jvmtiError StackUtil::GetStackTrace(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED, } else { GetStackTraceVectorClosure closure(0, 0); // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. - if (!ThreadUtil::RequestGCSafeSynchronousCheckpoint(thread, &closure)) { + if (!thread->RequestSynchronousCheckpoint(&closure)) { return ERR(THREAD_NOT_ALIVE); } @@ -713,7 +713,7 @@ jvmtiError StackUtil::GetFrameCount(jvmtiEnv* env ATTRIBUTE_UNUSED, GetFrameCountClosure closure; // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. - if (!ThreadUtil::RequestGCSafeSynchronousCheckpoint(thread, &closure)) { + if (!thread->RequestSynchronousCheckpoint(&closure)) { return ERR(THREAD_NOT_ALIVE); } @@ -803,7 +803,7 @@ jvmtiError StackUtil::GetFrameLocation(jvmtiEnv* env ATTRIBUTE_UNUSED, GetLocationClosure closure(static_cast(depth)); // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. - if (!ThreadUtil::RequestGCSafeSynchronousCheckpoint(thread, &closure)) { + if (!thread->RequestSynchronousCheckpoint(&closure)) { return ERR(THREAD_NOT_ALIVE); } @@ -882,8 +882,8 @@ struct MonitorVisitor : public art::StackVisitor, public art::SingleRootVisitor template struct MonitorInfoClosure : public art::Closure { public: - MonitorInfoClosure(art::ScopedObjectAccess& soa, Fn handle_results) - : soa_(soa), err_(OK), handle_results_(handle_results) {} + explicit MonitorInfoClosure(Fn handle_results) + : err_(OK), handle_results_(handle_results) {} void Run(art::Thread* target) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current()); @@ -893,7 +893,7 @@ struct MonitorInfoClosure : public art::Closure { // Find any other monitors, including ones acquired in native code. art::RootInfo root_info(art::kRootVMInternal); target->GetJniEnv()->VisitMonitorRoots(&visitor, root_info); - err_ = handle_results_(soa_, visitor); + err_ = handle_results_(visitor); } jvmtiError GetError() { @@ -901,17 +901,18 @@ struct MonitorInfoClosure : public art::Closure { } private: - art::ScopedObjectAccess& soa_; jvmtiError err_; Fn handle_results_; }; template -static jvmtiError GetOwnedMonitorInfoCommon(jthread thread, Fn handle_results) { +static jvmtiError GetOwnedMonitorInfoCommon(const art::ScopedObjectAccessAlreadyRunnable& soa, + jthread thread, + Fn handle_results) + REQUIRES_SHARED(art::Locks::mutator_lock_) { art::Thread* self = art::Thread::Current(); - art::ScopedObjectAccess soa(self); - MonitorInfoClosure closure(soa, handle_results); + MonitorInfoClosure closure(handle_results); bool called_method = false; { art::Locks::thread_list_lock_->ExclusiveLock(self); @@ -924,7 +925,7 @@ static jvmtiError GetOwnedMonitorInfoCommon(jthread thread, Fn handle_results) { if (target != self) { called_method = true; // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. - if (!ThreadUtil::RequestGCSafeSynchronousCheckpoint(target, &closure)) { + if (!target->RequestSynchronousCheckpoint(&closure)) { return ERR(THREAD_NOT_ALIVE); } } else { @@ -948,23 +949,33 @@ jvmtiError StackUtil::GetOwnedMonitorStackDepthInfo(jvmtiEnv* env, if (info_cnt == nullptr || info_ptr == nullptr) { return ERR(NULL_POINTER); } - auto handle_fun = [&] (art::ScopedObjectAccess& soa, MonitorVisitor& visitor) - REQUIRES_SHARED(art::Locks::mutator_lock_) { - auto nbytes = sizeof(jvmtiMonitorStackDepthInfo) * visitor.monitors.size(); - jvmtiError err = env->Allocate(nbytes, reinterpret_cast(info_ptr)); - if (err != OK) { - return err; - } - *info_cnt = visitor.monitors.size(); + art::ScopedObjectAccess soa(art::Thread::Current()); + std::vector> mons; + std::vector depths; + auto handle_fun = [&] (MonitorVisitor& visitor) REQUIRES_SHARED(art::Locks::mutator_lock_) { for (size_t i = 0; i < visitor.monitors.size(); i++) { - (*info_ptr)[i] = { - soa.Env()->AddLocalReference(visitor.monitors[i].Get()), - visitor.stack_depths[i] - }; + mons.push_back(art::GcRoot(visitor.monitors[i].Get())); + depths.push_back(visitor.stack_depths[i]); } return OK; }; - return GetOwnedMonitorInfoCommon(thread, handle_fun); + jvmtiError err = GetOwnedMonitorInfoCommon(soa, thread, handle_fun); + if (err != OK) { + return err; + } + auto nbytes = sizeof(jvmtiMonitorStackDepthInfo) * mons.size(); + err = env->Allocate(nbytes, reinterpret_cast(info_ptr)); + if (err != OK) { + return err; + } + *info_cnt = mons.size(); + for (uint32_t i = 0; i < mons.size(); i++) { + (*info_ptr)[i] = { + soa.AddLocalReference(mons[i].Read()), + static_cast(depths[i]) + }; + } + return err; } jvmtiError StackUtil::GetOwnedMonitorInfo(jvmtiEnv* env, @@ -974,21 +985,28 @@ jvmtiError StackUtil::GetOwnedMonitorInfo(jvmtiEnv* env, if (owned_monitors_ptr == nullptr || owned_monitors_ptr == nullptr) { return ERR(NULL_POINTER); } - auto handle_fun = [&] (art::ScopedObjectAccess& soa, MonitorVisitor& visitor) - REQUIRES_SHARED(art::Locks::mutator_lock_) { - auto nbytes = sizeof(jobject) * visitor.monitors.size(); - jvmtiError err = env->Allocate(nbytes, reinterpret_cast(owned_monitors_ptr)); - if (err != OK) { - return err; - } - *owned_monitor_count_ptr = visitor.monitors.size(); + art::ScopedObjectAccess soa(art::Thread::Current()); + std::vector> mons; + auto handle_fun = [&] (MonitorVisitor& visitor) REQUIRES_SHARED(art::Locks::mutator_lock_) { for (size_t i = 0; i < visitor.monitors.size(); i++) { - (*owned_monitors_ptr)[i] = - soa.Env()->AddLocalReference(visitor.monitors[i].Get()); + mons.push_back(art::GcRoot(visitor.monitors[i].Get())); } return OK; }; - return GetOwnedMonitorInfoCommon(thread, handle_fun); + jvmtiError err = GetOwnedMonitorInfoCommon(soa, thread, handle_fun); + if (err != OK) { + return err; + } + auto nbytes = sizeof(jobject) * mons.size(); + err = env->Allocate(nbytes, reinterpret_cast(owned_monitors_ptr)); + if (err != OK) { + return err; + } + *owned_monitor_count_ptr = mons.size(); + for (uint32_t i = 0; i < mons.size(); i++) { + (*owned_monitors_ptr)[i] = soa.AddLocalReference(mons[i].Read()); + } + return err; } jvmtiError StackUtil::NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth) { diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc index 555c5a725b..414139c7b4 100644 --- a/openjdkjvmti/ti_thread.cc +++ b/openjdkjvmti/ti_thread.cc @@ -1077,7 +1077,7 @@ jvmtiError ThreadUtil::StopThread(jvmtiEnv* env ATTRIBUTE_UNUSED, }; StopThreadClosure c(exc); // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. - if (RequestGCSafeSynchronousCheckpoint(target, &c)) { + if (target->RequestSynchronousCheckpoint(&c)) { return OK; } else { // Something went wrong, probably the thread died. @@ -1100,29 +1100,4 @@ jvmtiError ThreadUtil::InterruptThread(jvmtiEnv* env ATTRIBUTE_UNUSED, jthread t return OK; } -class GcCriticalSectionClosure : public art::Closure { - public: - explicit GcCriticalSectionClosure(art::Closure* wrapped) : wrapped_(wrapped) {} - - void Run(art::Thread* self) OVERRIDE { - if (art::kIsDebugBuild) { - art::Locks::thread_list_lock_->AssertNotHeld(art::Thread::Current()); - } - // This might block as it waits for any in-progress GCs to finish but this is fine since we - // released the Thread-list-lock prior to calling this in RequestSynchronousCheckpoint. - art::gc::ScopedGCCriticalSection sgccs(art::Thread::Current(), - art::gc::kGcCauseDebugger, - art::gc::kCollectorTypeDebugger); - wrapped_->Run(self); - } - - private: - art::Closure* wrapped_; -}; - -bool ThreadUtil::RequestGCSafeSynchronousCheckpoint(art::Thread* thr, art::Closure* function) { - GcCriticalSectionClosure gccsc(function); - return thr->RequestSynchronousCheckpoint(&gccsc); -} - } // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_thread.h b/openjdkjvmti/ti_thread.h index 341bffe51e..c6b6af1035 100644 --- a/openjdkjvmti/ti_thread.h +++ b/openjdkjvmti/ti_thread.h @@ -134,16 +134,6 @@ class ThreadUtil { REQUIRES(!art::Locks::user_code_suspension_lock_, !art::Locks::thread_suspend_count_lock_); - // This will request a synchronous checkpoint in such a way as to prevent gc races if a local - // variable is taken from one thread's stack and placed in the stack of another thread. - // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. This is - // due to the fact that Thread::Current() needs to go to sleep to allow the targeted thread to - // execute the checkpoint for us if it is Runnable. - static bool RequestGCSafeSynchronousCheckpoint(art::Thread* thr, art::Closure* function) - REQUIRES_SHARED(art::Locks::mutator_lock_) - RELEASE(art::Locks::thread_list_lock_) - REQUIRES(!art::Locks::thread_suspend_count_lock_); - private: // We need to make sure only one thread tries to suspend threads at a time so we can get the // 'suspend-only-once' behavior the spec requires. Internally, ART considers suspension to be a diff --git a/runtime/barrier.cc b/runtime/barrier.cc index 4329a5a245..8d3cf450f3 100644 --- a/runtime/barrier.cc +++ b/runtime/barrier.cc @@ -31,6 +31,9 @@ Barrier::Barrier(int count) condition_("GC barrier condition", lock_) { } +template void Barrier::Increment(Thread* self, int delta); +template void Barrier::Increment(Thread* self, int delta); + void Barrier::Pass(Thread* self) { MutexLock mu(self, lock_); SetCountLocked(self, count_ - 1); @@ -45,6 +48,7 @@ void Barrier::Init(Thread* self, int count) { SetCountLocked(self, count); } +template void Barrier::Increment(Thread* self, int delta) { MutexLock mu(self, lock_); SetCountLocked(self, count_ + delta); @@ -57,7 +61,11 @@ void Barrier::Increment(Thread* self, int delta) { // be decremented to zero and a Broadcast will be made on the // condition variable, thus waking this up. while (count_ != 0) { - condition_.Wait(self); + if (locks == kAllowHoldingLocks) { + condition_.WaitHoldingLocks(self); + } else { + condition_.Wait(self); + } } } diff --git a/runtime/barrier.h b/runtime/barrier.h index d7c4661b99..8a38c4c310 100644 --- a/runtime/barrier.h +++ b/runtime/barrier.h @@ -35,6 +35,11 @@ namespace art { // TODO: Maybe give this a better name. class Barrier { public: + enum LockHandling { + kAllowHoldingLocks, + kDisallowHoldingLocks, + }; + explicit Barrier(int count); virtual ~Barrier(); @@ -50,7 +55,9 @@ class Barrier { // If these calls are made in that situation, the offending thread is likely to go back // to sleep, resulting in a deadlock. - // Increment the count by delta, wait on condition if count is non zero. + // Increment the count by delta, wait on condition if count is non zero. If LockHandling is + // kAllowHoldingLocks we will not check that all locks are released when waiting. + template void Increment(Thread* self, int delta) REQUIRES(!lock_); // Increment the count by delta, wait on condition if count is non zero, with a timeout. Returns diff --git a/runtime/thread.cc b/runtime/thread.cc index 5b03c2d884..c0deda82db 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1438,8 +1438,12 @@ class BarrierClosure : public Closure { barrier_.Pass(self); } - void Wait(Thread* self) { - barrier_.Increment(self, 1); + void Wait(Thread* self, ThreadState suspend_state) { + if (suspend_state != ThreadState::kRunnable) { + barrier_.Increment(self, 1); + } else { + barrier_.Increment(self, 1); + } } private: @@ -1448,7 +1452,7 @@ class BarrierClosure : public Closure { }; // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. -bool Thread::RequestSynchronousCheckpoint(Closure* function) { +bool Thread::RequestSynchronousCheckpoint(Closure* function, ThreadState suspend_state) { Thread* self = Thread::Current(); if (this == Thread::Current()) { Locks::thread_list_lock_->AssertExclusiveHeld(self); @@ -1496,8 +1500,8 @@ bool Thread::RequestSynchronousCheckpoint(Closure* function) { // Relinquish the thread-list lock. We should not wait holding any locks. We cannot // reacquire it since we don't know if 'this' hasn't been deleted yet. Locks::thread_list_lock_->ExclusiveUnlock(self); - ScopedThreadSuspension sts(self, ThreadState::kWaiting); - barrier_closure.Wait(self); + ScopedThreadStateChange sts(self, suspend_state); + barrier_closure.Wait(self, suspend_state); return true; } // Fall-through. @@ -1521,7 +1525,7 @@ bool Thread::RequestSynchronousCheckpoint(Closure* function) { // that we can call ModifySuspendCount without racing against ThreadList::Unregister. ScopedThreadListLockUnlock stllu(self); { - ScopedThreadSuspension sts(self, ThreadState::kWaiting); + ScopedThreadStateChange sts(self, suspend_state); while (GetState() == ThreadState::kRunnable) { // We became runnable again. Wait till the suspend triggered in ModifySuspendCount // moves us to suspended. diff --git a/runtime/thread.h b/runtime/thread.h index 6549fc1a1f..9adae96a9c 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -263,16 +263,31 @@ class Thread { WARN_UNUSED REQUIRES(Locks::thread_suspend_count_lock_); + // Requests a checkpoint closure to run on another thread. The closure will be run when the thread + // gets suspended. This will return true if the closure was added and will (eventually) be + // executed. It returns false otherwise. + // + // Since multiple closures can be queued and some closures can delay other threads from running no + // closure should attempt to suspend another thread while running. + // TODO We should add some debug option that verifies this. bool RequestCheckpoint(Closure* function) REQUIRES(Locks::thread_suspend_count_lock_); // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. This is // due to the fact that Thread::Current() needs to go to sleep to allow the targeted thread to - // execute the checkpoint for us if it is Runnable. - bool RequestSynchronousCheckpoint(Closure* function) + // execute the checkpoint for us if it is Runnable. The suspend_state is the state that the thread + // will go into while it is awaiting the checkpoint to be run. + // NB Passing ThreadState::kRunnable may cause the current thread to wait in a condition variable + // while holding the mutator_lock_. Callers should ensure that this will not cause any problems + // for the closure or the rest of the system. + // NB Since multiple closures can be queued and some closures can delay other threads from running + // no closure should attempt to suspend another thread while running. + bool RequestSynchronousCheckpoint(Closure* function, + ThreadState suspend_state = ThreadState::kWaiting) REQUIRES_SHARED(Locks::mutator_lock_) RELEASE(Locks::thread_list_lock_) REQUIRES(!Locks::thread_suspend_count_lock_); + bool RequestEmptyCheckpoint() REQUIRES(Locks::thread_suspend_count_lock_); -- GitLab From 0933cc54c52054ab384d9a426448cd2004aa8968 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 23 Mar 2018 14:25:08 -0700 Subject: [PATCH 127/749] Disable encoded static initialization Encoded static initialization currently requires fixing up intered strings. This fix up process causes app startup to be ~10ms longer. Disable the initialization and fixup process until there is a faster way to fix up the interned strings. Bug: 70734839 Test: test-art-host Change-Id: I847d0bd9a362243241c93b115081a67faab7bfc1 --- compiler/driver/compiler_driver.cc | 1 + runtime/class_linker.cc | 2 +- runtime/class_linker.h | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index bd3a145368..53604761d1 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -2317,6 +2317,7 @@ class InitializeClassVisitor : public CompilationVisitor { // The boot image case doesn't need to recursively initialize the dependencies with // special logic since the class linker already does this. can_init_static_fields = + ClassLinker::kAppImageMayContainStrings && !soa.Self()->IsExceptionPending() && is_superclass_initialized && NoClinitInDependency(klass, soa.Self(), &class_loader); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 1d72b46f6c..8b64b8def0 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1340,7 +1340,7 @@ void AppImageClassLoadersAndDexCachesHelper::Update( } } } - { + if (ClassLinker::kAppImageMayContainStrings) { // Fixup all the literal strings happens at app images which are supposed to be interned. ScopedTrace timing("Fixup String Intern in image and dex_cache"); const auto& image_header = space->GetImageHeader(); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index d05e78fb40..2f6b754521 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -152,6 +152,8 @@ class ClassLinker { kClassRootsMax, }; + static constexpr bool kAppImageMayContainStrings = false; + explicit ClassLinker(InternTable* intern_table); virtual ~ClassLinker(); -- GitLab From e826477ad8b5b7fa06c1ba5a86d4536bfbfc322b Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 22 Mar 2018 22:16:41 +0000 Subject: [PATCH 128/749] [veridex] Resolve all type_id/method_id/field_id in app dex files. Change-Id: I6e28d70b6d3807944ccebc0f429c1b9b1d4b1eeb --- tools/veridex/resolver.cc | 241 ++++++++++++++++++++++++++++++++++++++ tools/veridex/resolver.h | 41 ++++++- tools/veridex/veridex.cc | 69 +++++++++-- tools/veridex/veridex.h | 18 +++ 4 files changed, 357 insertions(+), 12 deletions(-) diff --git a/tools/veridex/resolver.cc b/tools/veridex/resolver.cc index c0705e5ea8..82978215a6 100644 --- a/tools/veridex/resolver.cc +++ b/tools/veridex/resolver.cc @@ -55,4 +55,245 @@ void VeridexResolver::Run() { } } +static bool HasSameNameAndSignature(const DexFile& dex_file, + const DexFile::MethodId& method_id, + const char* method_name, + const Signature& signature) { + return strcmp(method_name, dex_file.GetMethodName(method_id)) == 0 && + dex_file.GetMethodSignature(method_id) == signature; +} + +static bool HasSameNameAndType(const DexFile& dex_file, + const DexFile::FieldId& field_id, + const char* field_name, + const char* field_type) { + return strcmp(field_name, dex_file.GetFieldName(field_id)) == 0 && + strcmp(field_type, dex_file.GetFieldTypeDescriptor(field_id)) == 0; +} + +VeriClass* VeridexResolver::GetVeriClass(dex::TypeIndex index) { + CHECK_LT(index.index_, dex_file_.NumTypeIds()); + // Lookup in our local cache. + VeriClass* cls = &type_infos_[index.index_]; + if (cls->IsUninitialized()) { + // Class is defined in another dex file. Lookup in the global cache. + std::string name(dex_file_.StringByTypeIdx(index)); + auto existing = type_map_.find(name); + if (existing == type_map_.end()) { + // Class hasn't been defined, so check if it's an array class. + size_t last_array = name.find_last_of('['); + if (last_array == std::string::npos) { + // There is no such class. + return nullptr; + } else { + // Class is an array class. Check if its most enclosed component type (which is not + // an array class) has been defined. + std::string klass_name = name.substr(last_array + 1); + existing = type_map_.find(klass_name); + if (existing == type_map_.end()) { + // There is no such class, so there is no such array. + return nullptr; + } else { + // Create the type, and cache it locally and globally. + type_infos_[index.index_] = VeriClass( + existing->second->GetKind(), last_array + 1, existing->second->GetClassDef()); + cls = &(type_infos_[index.index_]); + type_map_[name] = cls; + } + } + } else { + // Cache the found class. + cls = existing->second; + type_infos_[index.index_] = *cls; + } + } + return cls; +} + +VeridexResolver* VeridexResolver::GetResolverOf(const VeriClass& kls) const { + auto resolver_it = dex_resolvers_.lower_bound(reinterpret_cast(kls.GetClassDef())); + --resolver_it; + + // Check the class def pointer is indeed in the mapped dex file range. + const DexFile& dex_file = resolver_it->second->dex_file_; + CHECK_LT(reinterpret_cast(dex_file.Begin()), + reinterpret_cast(kls.GetClassDef())); + CHECK_GT(reinterpret_cast(dex_file.Begin()) + dex_file.Size(), + reinterpret_cast(kls.GetClassDef())); + return resolver_it->second; +} + +VeriMethod VeridexResolver::LookupMethodIn(const VeriClass& kls, + const char* method_name, + const Signature& method_signature) { + if (kls.IsPrimitive()) { + // Primitive classes don't have methods. + return nullptr; + } + if (kls.IsArray()) { + // Array classes don't have methods, but inherit the ones in j.l.Object. + return LookupMethodIn(*VeriClass::object_, method_name, method_signature); + } + // Get the resolver where `kls` is from. + VeridexResolver* resolver = GetResolverOf(kls); + + // Look at methods declared in `kls`. + const DexFile& other_dex_file = resolver->dex_file_; + const uint8_t* class_data = other_dex_file.GetClassData(*kls.GetClassDef()); + if (class_data != nullptr) { + ClassDataItemIterator it(other_dex_file, class_data); + it.SkipAllFields(); + for (; it.HasNextMethod(); it.Next()) { + const DexFile::MethodId& other_method_id = other_dex_file.GetMethodId(it.GetMemberIndex()); + if (HasSameNameAndSignature(other_dex_file, + other_method_id, + method_name, + method_signature)) { + return it.DataPointer(); + } + } + } + + // Look at methods in `kls`'s super class hierarchy. + if (kls.GetClassDef()->superclass_idx_.IsValid()) { + VeriClass* super = resolver->GetVeriClass(kls.GetClassDef()->superclass_idx_); + if (super != nullptr) { + VeriMethod super_method = resolver->LookupMethodIn(*super, method_name, method_signature); + if (super_method != nullptr) { + return super_method; + } + } + } + + // Look at methods in `kls`'s interface hierarchy. + const DexFile::TypeList* interfaces = other_dex_file.GetInterfacesList(*kls.GetClassDef()); + if (interfaces != nullptr) { + for (size_t i = 0; i < interfaces->Size(); i++) { + dex::TypeIndex idx = interfaces->GetTypeItem(i).type_idx_; + VeriClass* itf = resolver->GetVeriClass(idx); + if (itf != nullptr) { + VeriMethod itf_method = resolver->LookupMethodIn(*itf, method_name, method_signature); + if (itf_method != nullptr) { + return itf_method; + } + } + } + } + return nullptr; +} + +VeriField VeridexResolver::LookupFieldIn(const VeriClass& kls, + const char* field_name, + const char* field_type) { + if (kls.IsPrimitive()) { + // Primitive classes don't have fields. + return nullptr; + } + if (kls.IsArray()) { + // Array classes don't have fields. + return nullptr; + } + // Get the resolver where `kls` is from. + VeridexResolver* resolver = GetResolverOf(kls); + + // Look at fields declared in `kls`. + const DexFile& other_dex_file = resolver->dex_file_; + const uint8_t* class_data = other_dex_file.GetClassData(*kls.GetClassDef()); + if (class_data != nullptr) { + ClassDataItemIterator it(other_dex_file, class_data); + for (; it.HasNextStaticField() || it.HasNextInstanceField(); it.Next()) { + const DexFile::FieldId& other_field_id = other_dex_file.GetFieldId(it.GetMemberIndex()); + if (HasSameNameAndType(other_dex_file, + other_field_id, + field_name, + field_type)) { + return it.DataPointer(); + } + } + } + + // Look at fields in `kls`'s interface hierarchy. + const DexFile::TypeList* interfaces = other_dex_file.GetInterfacesList(*kls.GetClassDef()); + if (interfaces != nullptr) { + for (size_t i = 0; i < interfaces->Size(); i++) { + dex::TypeIndex idx = interfaces->GetTypeItem(i).type_idx_; + VeriClass* itf = resolver->GetVeriClass(idx); + if (itf != nullptr) { + VeriField itf_field = resolver->LookupFieldIn(*itf, field_name, field_type); + if (itf_field != nullptr) { + return itf_field; + } + } + } + } + + // Look at fields in `kls`'s super class hierarchy. + if (kls.GetClassDef()->superclass_idx_.IsValid()) { + VeriClass* super = resolver->GetVeriClass(kls.GetClassDef()->superclass_idx_); + if (super != nullptr) { + VeriField super_field = resolver->LookupFieldIn(*super, field_name, field_type); + if (super_field != nullptr) { + return super_field; + } + } + } + return nullptr; +} + +VeriMethod VeridexResolver::GetMethod(uint32_t method_index) { + VeriMethod method_info = method_infos_[method_index]; + if (method_info == nullptr) { + // Method is defined in another dex file. + const DexFile::MethodId& method_id = dex_file_.GetMethodId(method_index); + VeriClass* kls = GetVeriClass(method_id.class_idx_); + if (kls == nullptr) { + return nullptr; + } + // Class found, now lookup the method in it. + method_info = LookupMethodIn(*kls, + dex_file_.GetMethodName(method_id), + dex_file_.GetMethodSignature(method_id)); + method_infos_[method_index] = method_info; + } + return method_info; +} + +VeriField VeridexResolver::GetField(uint32_t field_index) { + VeriField field_info = field_infos_[field_index]; + if (field_info == nullptr) { + // Field is defined in another dex file. + const DexFile::FieldId& field_id = dex_file_.GetFieldId(field_index); + VeriClass* kls = GetVeriClass(field_id.class_idx_); + if (kls == nullptr) { + return nullptr; + } + // Class found, now lookup the field in it. + field_info = LookupFieldIn(*kls, + dex_file_.GetFieldName(field_id), + dex_file_.GetFieldTypeDescriptor(field_id)); + field_infos_[field_index] = field_info; + } + return field_info; +} + +void VeridexResolver::ResolveAll() { + for (uint32_t i = 0; i < dex_file_.NumTypeIds(); ++i) { + if (GetVeriClass(dex::TypeIndex(i)) == nullptr) { + LOG(WARNING) << "Unresolved " << dex_file_.PrettyType(dex::TypeIndex(i)); + } + } + + for (uint32_t i = 0; i < dex_file_.NumMethodIds(); ++i) { + if (GetMethod(i) == nullptr) { + LOG(WARNING) << "Unresolved: " << dex_file_.PrettyMethod(i); + } + } + + for (uint32_t i = 0; i < dex_file_.NumFieldIds(); ++i) { + if (GetField(i) == nullptr) { + LOG(WARNING) << "Unresolved: " << dex_file_.PrettyField(i); + } + } +} + } // namespace art diff --git a/tools/veridex/resolver.h b/tools/veridex/resolver.h index 4e0c5b3732..ae94dadb28 100644 --- a/tools/veridex/resolver.h +++ b/tools/veridex/resolver.h @@ -22,20 +22,59 @@ namespace art { +class VeridexResolver; + +/** + * Map from the start of a dex file (ie DexFile::Begin()), to + * its corresponding resolver. + */ +using DexResolverMap = std::map; + class VeridexResolver { public: - VeridexResolver(const DexFile& dex_file, TypeMap& type_map) + VeridexResolver(const DexFile& dex_file, + const DexResolverMap& dex_resolvers, + TypeMap& type_map) : dex_file_(dex_file), type_map_(type_map), + dex_resolvers_(dex_resolvers), type_infos_(dex_file.NumTypeIds(), VeriClass()), method_infos_(dex_file.NumMethodIds(), nullptr), field_infos_(dex_file.NumFieldIds(), nullptr) {} + // Run on the defined classes of that dex file and populate our + // local type cache. void Run(); + // Return the class declared at `index`. + VeriClass* GetVeriClass(dex::TypeIndex index); + + // Return the method declared at `method_index`. + VeriMethod GetMethod(uint32_t method_index); + + // Return the field declared at `field_index`. + VeriField GetField(uint32_t field_index); + + // Do a JLS lookup in `kls` to find a method. + VeriMethod LookupMethodIn(const VeriClass& kls, + const char* method_name, + const Signature& method_signature); + + // Do a JLS lookup in `kls` to find a field. + VeriField LookupFieldIn(const VeriClass& kls, + const char* field_name, + const char* field_type); + + // Resolve all type_id/method_id/field_id. + void ResolveAll(); + private: + // Return the resolver where `kls` is from. + VeridexResolver* GetResolverOf(const VeriClass& kls) const; + const DexFile& dex_file_; TypeMap& type_map_; + const DexResolverMap& dex_resolvers_; std::vector type_infos_; std::vector method_infos_; std::vector field_infos_; diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc index 0370a0329c..9287211a3c 100644 --- a/tools/veridex/veridex.cc +++ b/tools/veridex/veridex.cc @@ -26,6 +26,28 @@ namespace art { +static VeriClass z_(Primitive::Type::kPrimBoolean, 0, nullptr); +static VeriClass b_(Primitive::Type::kPrimByte, 0, nullptr); +static VeriClass c_(Primitive::Type::kPrimChar, 0, nullptr); +static VeriClass s_(Primitive::Type::kPrimShort, 0, nullptr); +static VeriClass i_(Primitive::Type::kPrimInt, 0, nullptr); +static VeriClass f_(Primitive::Type::kPrimFloat, 0, nullptr); +static VeriClass d_(Primitive::Type::kPrimDouble, 0, nullptr); +static VeriClass j_(Primitive::Type::kPrimLong, 0, nullptr); +static VeriClass v_(Primitive::Type::kPrimVoid, 0, nullptr); + +VeriClass* VeriClass::boolean_ = &z_; +VeriClass* VeriClass::byte_ = &b_; +VeriClass* VeriClass::char_ = &c_; +VeriClass* VeriClass::short_ = &s_; +VeriClass* VeriClass::integer_ = &i_; +VeriClass* VeriClass::float_ = &f_; +VeriClass* VeriClass::double_ = &d_; +VeriClass* VeriClass::long_ = &j_; +VeriClass* VeriClass::void_ = &v_; +// Will be set after boot classpath has been resolved. +VeriClass* VeriClass::object_ = nullptr; + struct VeridexOptions { const char* dex_file = nullptr; const char* core_stubs = nullptr; @@ -114,14 +136,35 @@ class Veridex { // Resolve classes/methods/fields defined in each dex file. - // Cache of types we've seen. This is used in case of duplicate classes. + // Cache of types we've seen, for quick class name lookups. TypeMap type_map; - - std::vector boot_resolvers; - Resolve(boot_dex_files, type_map, &boot_resolvers); - - std::vector app_resolvers; - Resolve(app_dex_files, type_map, &app_resolvers); + // Add internally defined primitives. + type_map["Z"] = VeriClass::boolean_; + type_map["B"] = VeriClass::byte_; + type_map["S"] = VeriClass::short_; + type_map["C"] = VeriClass::char_; + type_map["I"] = VeriClass::integer_; + type_map["F"] = VeriClass::float_; + type_map["D"] = VeriClass::double_; + type_map["J"] = VeriClass::long_; + type_map["V"] = VeriClass::void_; + + // Cache of resolvers, to easily query address in memory to VeridexResolver. + DexResolverMap resolver_map; + + std::vector> boot_resolvers; + Resolve(boot_dex_files, resolver_map, type_map, &boot_resolvers); + + // Now that boot classpath has been resolved, fill j.l.Object. + VeriClass::object_ = type_map["Ljava/lang/Object;"]; + + std::vector> app_resolvers; + Resolve(app_dex_files, resolver_map, type_map, &app_resolvers); + + // Resolve all type_id/method_id/field_id of app dex files. + for (const std::unique_ptr& resolver : app_resolvers) { + resolver->ResolveAll(); + } return 0; } @@ -159,14 +202,18 @@ class Veridex { } static void Resolve(const std::vector>& dex_files, + DexResolverMap& resolver_map, TypeMap& type_map, - std::vector* resolvers) { + std::vector>* resolvers) { for (const std::unique_ptr& dex_file : dex_files) { - resolvers->push_back(VeridexResolver(*dex_file.get(), type_map)); + VeridexResolver* resolver = + new VeridexResolver(*dex_file.get(), resolver_map, type_map); + resolvers->emplace_back(resolver); + resolver_map[reinterpret_cast(dex_file->Begin())] = resolver; } - for (VeridexResolver& resolver : *resolvers) { - resolver.Run(); + for (const std::unique_ptr& resolver : *resolvers) { + resolver->Run(); } } }; diff --git a/tools/veridex/veridex.h b/tools/veridex/veridex.h index bbff254f0a..0c928ab166 100644 --- a/tools/veridex/veridex.h +++ b/tools/veridex/veridex.h @@ -47,6 +47,21 @@ class VeriClass { return dimensions_ != 0; } + Primitive::Type GetKind() const { return kind_; } + uint8_t GetDimensions() const { return dimensions_; } + const DexFile::ClassDef* GetClassDef() const { return class_def_; } + + static VeriClass* object_; + static VeriClass* boolean_; + static VeriClass* byte_; + static VeriClass* char_; + static VeriClass* short_; + static VeriClass* integer_; + static VeriClass* float_; + static VeriClass* double_; + static VeriClass* long_; + static VeriClass* void_; + private: Primitive::Type kind_; uint8_t dimensions_; @@ -65,6 +80,9 @@ using VeriField = const uint8_t*; */ using VeriMethod = const uint8_t*; +/** + * Map from name to VeriClass to quickly lookup classes. + */ using TypeMap = std::map; } // namespace art -- GitLab From 534a0a1807a4aa776b4bdfa894e566124b98979a Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Sat, 24 Mar 2018 20:02:25 +0000 Subject: [PATCH 129/749] [veridex] Log when linking against private APIs. Change-Id: Ib1f255f74ed8803fcf240c6d296f0b8813bb8554 --- tools/veridex/Android.bp | 1 + tools/veridex/hidden_api.cc | 79 +++++++++++++++++++++++++++++++++++++ tools/veridex/hidden_api.h | 63 +++++++++++++++++++++++++++++ tools/veridex/resolver.cc | 13 ++++-- tools/veridex/resolver.h | 6 ++- tools/veridex/veridex.cc | 4 +- 6 files changed, 160 insertions(+), 6 deletions(-) create mode 100644 tools/veridex/hidden_api.cc create mode 100644 tools/veridex/hidden_api.h diff --git a/tools/veridex/Android.bp b/tools/veridex/Android.bp index 31ff682828..ff181c89a7 100644 --- a/tools/veridex/Android.bp +++ b/tools/veridex/Android.bp @@ -16,6 +16,7 @@ art_cc_binary { name: "veridex", host_supported: true, srcs: [ + "hidden_api.cc", "resolver.cc", "veridex.cc", ], diff --git a/tools/veridex/hidden_api.cc b/tools/veridex/hidden_api.cc new file mode 100644 index 0000000000..33e499bfc3 --- /dev/null +++ b/tools/veridex/hidden_api.cc @@ -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. + */ + +#include "hidden_api.h" + +#include +#include + +#include "dex/dex_file-inl.h" + +namespace art { + +std::string HiddenApi::GetApiMethodName(const DexFile& dex_file, uint32_t method_index) { + std::stringstream ss; + const DexFile::MethodId& method_id = dex_file.GetMethodId(method_index); + ss << dex_file.StringByTypeIdx(method_id.class_idx_) + << "->" + << dex_file.GetMethodName(method_id) + << dex_file.GetMethodSignature(method_id).ToString(); + return ss.str(); +} + +std::string HiddenApi::GetApiFieldName(const DexFile& dex_file, uint32_t field_index) { + std::stringstream ss; + const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); + ss << dex_file.StringByTypeIdx(field_id.class_idx_) + << "->" + << dex_file.GetFieldName(field_id) + << ":" + << dex_file.GetFieldTypeDescriptor(field_id); + return ss.str(); +} + +bool HiddenApi::LogIfIn(const std::string& name, + const std::set& list, + const std::string& log, + const std::string& access_kind) { + if (list.find(name) != list.end()) { + LOG(WARNING) << std::string(log) << " usage found " << name << " (" << access_kind << ")"; + return true; + } + return false; +} + +void HiddenApi::FillList(const char* filename, std::set& entries) { + if (filename == nullptr) { + return; + } + std::ifstream in(filename); + std::string str; + while (std::getline(in, str)) { + entries.insert(str); + size_t pos = str.find("->"); + if (pos != std::string::npos) { + // Add the class name. + entries.insert(str.substr(0, pos)); + pos = str.find('('); + if (pos != std::string::npos) { + // Add the class->method name (so stripping the signature). + entries.insert(str.substr(0, pos)); + } + } + } +} + +} // namespace art diff --git a/tools/veridex/hidden_api.h b/tools/veridex/hidden_api.h new file mode 100644 index 0000000000..282e7cf8e8 --- /dev/null +++ b/tools/veridex/hidden_api.h @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#ifndef ART_TOOLS_VERIDEX_HIDDEN_API_H_ +#define ART_TOOLS_VERIDEX_HIDDEN_API_H_ + +#include +#include + +namespace art { + +class DexFile; + +/** + * Helper class for logging if a method/field is in a hidden API list. + */ +class HiddenApi { + public: + HiddenApi(const char* blacklist, const char* dark_greylist, const char* light_greylist) { + FillList(light_greylist, light_greylist_); + FillList(dark_greylist, dark_greylist_); + FillList(blacklist, blacklist_); + } + + bool LogIfInList(const std::string& name, const char* access_kind) const { + return LogIfIn(name, blacklist_, "Blacklist", access_kind) || + LogIfIn(name, dark_greylist_, "Dark greylist", access_kind) || + LogIfIn(name, light_greylist_, "Light greylist", access_kind); + } + + static std::string GetApiMethodName(const DexFile& dex_file, uint32_t method_index); + + static std::string GetApiFieldName(const DexFile& dex_file, uint32_t field_index); + + private: + static bool LogIfIn(const std::string& name, + const std::set& list, + const std::string& log, + const std::string& access_kind); + + static void FillList(const char* filename, std::set& entries); + + std::set blacklist_; + std::set light_greylist_; + std::set dark_greylist_; +}; + +} // namespace art + +#endif // ART_TOOLS_VERIDEX_HIDDEN_API_H_ diff --git a/tools/veridex/resolver.cc b/tools/veridex/resolver.cc index 82978215a6..6ab872ed6f 100644 --- a/tools/veridex/resolver.cc +++ b/tools/veridex/resolver.cc @@ -18,6 +18,7 @@ #include "dex/dex_file-inl.h" #include "dex/primitive.h" +#include "hidden_api.h" #include "veridex.h" namespace art { @@ -276,8 +277,10 @@ VeriField VeridexResolver::GetField(uint32_t field_index) { return field_info; } -void VeridexResolver::ResolveAll() { +void VeridexResolver::ResolveAll(const HiddenApi& hidden_api) { for (uint32_t i = 0; i < dex_file_.NumTypeIds(); ++i) { + // Note: we don't look at HiddenApi for types, as the lists don't contain + // classes. if (GetVeriClass(dex::TypeIndex(i)) == nullptr) { LOG(WARNING) << "Unresolved " << dex_file_.PrettyType(dex::TypeIndex(i)); } @@ -285,13 +288,17 @@ void VeridexResolver::ResolveAll() { for (uint32_t i = 0; i < dex_file_.NumMethodIds(); ++i) { if (GetMethod(i) == nullptr) { - LOG(WARNING) << "Unresolved: " << dex_file_.PrettyMethod(i); + if (!hidden_api.LogIfInList(HiddenApi::GetApiMethodName(dex_file_, i), "Linking")) { + LOG(WARNING) << "Unresolved: " << dex_file_.PrettyMethod(i); + } } } for (uint32_t i = 0; i < dex_file_.NumFieldIds(); ++i) { if (GetField(i) == nullptr) { - LOG(WARNING) << "Unresolved: " << dex_file_.PrettyField(i); + if (!hidden_api.LogIfInList(HiddenApi::GetApiFieldName(dex_file_, i), "Linking")) { + LOG(WARNING) << "Unresolved: " << dex_file_.PrettyField(i); + } } } } diff --git a/tools/veridex/resolver.h b/tools/veridex/resolver.h index ae94dadb28..82f6aaeddd 100644 --- a/tools/veridex/resolver.h +++ b/tools/veridex/resolver.h @@ -22,6 +22,7 @@ namespace art { +class HiddenApi; class VeridexResolver; /** @@ -65,8 +66,9 @@ class VeridexResolver { const char* field_name, const char* field_type); - // Resolve all type_id/method_id/field_id. - void ResolveAll(); + // Resolve all type_id/method_id/field_id. Log for unresolved + // entities, or entities part of a hidden API list. + void ResolveAll(const HiddenApi& hidden_api); private: // Return the resolver where `kls` is from. diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc index 9287211a3c..c5203fea66 100644 --- a/tools/veridex/veridex.cc +++ b/tools/veridex/veridex.cc @@ -20,6 +20,7 @@ #include "dex/dex_file.h" #include "dex/dex_file_loader.h" +#include "hidden_api.h" #include "resolver.h" #include @@ -162,8 +163,9 @@ class Veridex { Resolve(app_dex_files, resolver_map, type_map, &app_resolvers); // Resolve all type_id/method_id/field_id of app dex files. + HiddenApi hidden_api(options.blacklist, options.dark_greylist, options.light_greylist); for (const std::unique_ptr& resolver : app_resolvers) { - resolver->ResolveAll(); + resolver->ResolveAll(hidden_api); } return 0; -- GitLab From eefabd28c21e6ae02ca05e97bd22f9e23cbf98a5 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Thu, 22 Mar 2018 16:27:18 +0000 Subject: [PATCH 130/749] Allow hidden API access from system libraries Libraries like RemoteDisplay provide an APK that an app loads into its process and which accesses internal APIs on the app's behalf, without exposing the internals to the app. These libraries are considered part of the platform, but were not exempt from hidden API checks because they are not loaded with the boot strap class loader. This patch adds a new flag to DexFile class which the constructor sets to true of the canonical location of the newly loaded dex file starts with "${ANDROID_ROOT}/framework/". Hidden API enforcement then checks this flag when determining whether the caller of a hidden class member is allowed to access it or not. Bug: 64382372 Bug: 76138670 Bug: 76165623 Bug: 76112393 Test: art_dex_file_loader_test gtest Test: art/test.py --gtest Change-Id: If062bd668d7ba494bbb7b828e40932748d173b9a --- libdexfile/dex/dex_file.cc | 3 +- libdexfile/dex/dex_file.h | 11 +++ runtime/base/file_utils.cc | 7 ++ runtime/base/file_utils.h | 3 + runtime/class_linker.cc | 12 ++-- runtime/dex/art_dex_file_loader.cc | 36 ++++++++++ runtime/dex/art_dex_file_loader.h | 13 ++++ runtime/dex/art_dex_file_loader_test.cc | 90 +++++++++++++++++++++++- runtime/hidden_api.h | 43 ++++++++--- runtime/interpreter/unstarted_runtime.cc | 1 + runtime/jni_internal.cc | 9 +-- runtime/native/java_lang_Class.cc | 10 +-- 12 files changed, 210 insertions(+), 28 deletions(-) diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc index 4613b40457..1f471106df 100644 --- a/libdexfile/dex/dex_file.cc +++ b/libdexfile/dex/dex_file.cc @@ -121,7 +121,8 @@ DexFile::DexFile(const uint8_t* base, num_call_site_ids_(0), oat_dex_file_(oat_dex_file), container_(std::move(container)), - is_compact_dex_(is_compact_dex) { + is_compact_dex_(is_compact_dex), + is_platform_dex_(false) { CHECK(begin_ != nullptr) << GetLocation(); CHECK_GT(size_, 0U) << GetLocation(); // Check base (=header) alignment. diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index aeb49d2c25..683a8243ed 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -992,6 +992,14 @@ class DexFile { ALWAYS_INLINE const StandardDexFile* AsStandardDexFile() const; ALWAYS_INLINE const CompactDexFile* AsCompactDexFile() const; + ALWAYS_INLINE bool IsPlatformDexFile() const { + return is_platform_dex_; + } + + ALWAYS_INLINE void SetIsPlatformDexFile() { + is_platform_dex_ = true; + } + bool IsInMainSection(const void* addr) const { return Begin() <= addr && addr < Begin() + Size(); } @@ -1094,6 +1102,9 @@ class DexFile { // If the dex file is a compact dex file. If false then the dex file is a standard dex file. const bool is_compact_dex_; + // If the dex file is located in /system/framework/. + bool is_platform_dex_; + friend class DexFileLoader; friend class DexFileVerifierTest; friend class OatWriter; diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc index 1cb3b9c380..36caa7fcfb 100644 --- a/runtime/base/file_utils.cc +++ b/runtime/base/file_utils.cc @@ -319,4 +319,11 @@ bool LocationIsOnSystem(const char* location) { return path != nullptr && android::base::StartsWith(path.get(), GetAndroidRoot().c_str()); } +bool LocationIsOnSystemFramework(const char* location) { + UniqueCPtr path(realpath(location, nullptr)); + std::string framework_path = GetAndroidRoot() + "/framework/"; + return path != nullptr && + android::base::StartsWith(path.get(), framework_path.c_str()); +} + } // namespace art diff --git a/runtime/base/file_utils.h b/runtime/base/file_utils.h index 7f691d546b..8adb4f7bf8 100644 --- a/runtime/base/file_utils.h +++ b/runtime/base/file_utils.h @@ -75,6 +75,9 @@ std::string ReplaceFileExtension(const std::string& filename, const std::string& // Return whether the location is on system (i.e. android root). bool LocationIsOnSystem(const char* location); +// Return whether the location is on system/framework (i.e. android_root/framework). +bool LocationIsOnSystemFramework(const char* location); + } // namespace art #endif // ART_RUNTIME_BASE_FILE_UTILS_H_ diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 1d72b46f6c..32cefbdfc5 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -7935,7 +7935,8 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr klass, } DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); if (resolved != nullptr && - hiddenapi::ShouldBlockAccessToMember(resolved, class_loader, hiddenapi::kLinking)) { + hiddenapi::ShouldBlockAccessToMember( + resolved, class_loader, dex_cache, hiddenapi::kLinking)) { resolved = nullptr; } if (resolved != nullptr) { @@ -8077,7 +8078,8 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx, resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_); } if (resolved != nullptr && - hiddenapi::ShouldBlockAccessToMember(resolved, class_loader.Get(), hiddenapi::kLinking)) { + hiddenapi::ShouldBlockAccessToMember( + resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) { resolved = nullptr; } return resolved; @@ -8156,7 +8158,8 @@ ArtField* ClassLinker::ResolveField(uint32_t field_idx, } if (resolved == nullptr || - hiddenapi::ShouldBlockAccessToMember(resolved, class_loader.Get(), hiddenapi::kLinking)) { + hiddenapi::ShouldBlockAccessToMember( + resolved, class_loader.Get(), dex_cache.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); @@ -8189,7 +8192,8 @@ ArtField* ClassLinker::ResolveFieldJLS(uint32_t field_idx, 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)) { + hiddenapi::ShouldBlockAccessToMember( + resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) { resolved = nullptr; } if (resolved != nullptr) { diff --git a/runtime/dex/art_dex_file_loader.cc b/runtime/dex/art_dex_file_loader.cc index c456764834..cf9a814a6b 100644 --- a/runtime/dex/art_dex_file_loader.cc +++ b/runtime/dex/art_dex_file_loader.cc @@ -22,6 +22,7 @@ #include "android-base/stringprintf.h" #include "base/file_magic.h" +#include "base/file_utils.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/unix_file/fd_file.h" @@ -488,4 +489,39 @@ bool ArtDexFileLoader::OpenAllDexFilesFromZip( } } +std::unique_ptr ArtDexFileLoader::OpenCommon(const uint8_t* base, + size_t size, + const uint8_t* data_base, + size_t data_size, + const std::string& location, + uint32_t location_checksum, + const OatDexFile* oat_dex_file, + bool verify, + bool verify_checksum, + std::string* error_msg, + std::unique_ptr container, + VerifyResult* verify_result) { + std::unique_ptr dex_file = DexFileLoader::OpenCommon(base, + size, + data_base, + data_size, + location, + location_checksum, + oat_dex_file, + verify, + verify_checksum, + error_msg, + std::move(container), + verify_result); + + // Check if this dex file is located in the framework directory. + // If it is, set a flag on the dex file. This is used by hidden API + // policy decision logic. + if (dex_file != nullptr && LocationIsOnSystemFramework(location.c_str())) { + dex_file->SetIsPlatformDexFile(); + } + + return dex_file; +} + } // namespace art diff --git a/runtime/dex/art_dex_file_loader.h b/runtime/dex/art_dex_file_loader.h index 7c7a59b37f..7577945632 100644 --- a/runtime/dex/art_dex_file_loader.h +++ b/runtime/dex/art_dex_file_loader.h @@ -120,6 +120,19 @@ class ArtDexFileLoader : public DexFileLoader { bool verify_checksum, std::string* error_msg, ZipOpenErrorCode* error_code) const; + + static std::unique_ptr OpenCommon(const uint8_t* base, + size_t size, + const uint8_t* data_base, + size_t data_size, + const std::string& location, + uint32_t location_checksum, + const OatDexFile* oat_dex_file, + bool verify, + bool verify_checksum, + std::string* error_msg, + std::unique_ptr container, + VerifyResult* verify_result); }; } // namespace art diff --git a/runtime/dex/art_dex_file_loader_test.cc b/runtime/dex/art_dex_file_loader_test.cc index 6e2cfec381..3e0d6662c7 100644 --- a/runtime/dex/art_dex_file_loader_test.cc +++ b/runtime/dex/art_dex_file_loader_test.cc @@ -16,9 +16,11 @@ #include +#include #include #include "art_dex_file_loader.h" +#include "base/file_utils.h" #include "base/os.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" @@ -35,7 +37,40 @@ namespace art { -class ArtDexFileLoaderTest : public CommonRuntimeTest {}; +static void Copy(const std::string& src, const std::string& dst) { + std::ifstream src_stream(src, std::ios::binary); + std::ofstream dst_stream(dst, std::ios::binary); + dst_stream << src_stream.rdbuf(); +} + +class ArtDexFileLoaderTest : public CommonRuntimeTest { + public: + virtual void SetUp() { + CommonRuntimeTest::SetUp(); + + std::string dex_location = GetTestDexFileName("Main"); + + data_location_path_ = android_data_ + "/foo.jar"; + system_location_path_ = GetAndroidRoot() + "/foo.jar"; + system_framework_location_path_ = GetAndroidRoot() + "/framework/foo.jar"; + + Copy(dex_location, data_location_path_); + Copy(dex_location, system_location_path_); + Copy(dex_location, system_framework_location_path_); + } + + virtual void TearDown() { + remove(data_location_path_.c_str()); + remove(system_location_path_.c_str()); + remove(system_framework_location_path_.c_str()); + CommonRuntimeTest::TearDown(); + } + + protected: + std::string data_location_path_; + std::string system_location_path_; + std::string system_framework_location_path_; +}; // TODO: Port OpenTestDexFile(s) need to be ported to use non-ART utilities, and // the tests that depend upon them should be moved to dex_file_loader_test.cc @@ -304,4 +339,57 @@ TEST_F(ArtDexFileLoaderTest, GetDexCanonicalLocation) { ASSERT_EQ(0, unlink(dex_location_sym.c_str())); } +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { + ArtDexFileLoader loader; + bool success; + std::string error_msg; + std::vector> dex_files; + + // Load file from a non-system directory and check that it is not flagged as framework. + ASSERT_FALSE(LocationIsOnSystemFramework(data_location_path_.c_str())); + success = loader.Open(data_location_path_.c_str(), + data_location_path_, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success); + ASSERT_GE(dex_files.size(), 1u); + for (std::unique_ptr& dex_file : dex_files) { + ASSERT_FALSE(dex_file->IsPlatformDexFile()); + } + + dex_files.clear(); + + // Load file from a system, non-framework directory and check that it is not flagged as framework. + ASSERT_FALSE(LocationIsOnSystemFramework(system_location_path_.c_str())); + success = loader.Open(system_location_path_.c_str(), + system_location_path_, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success); + ASSERT_GE(dex_files.size(), 1u); + for (std::unique_ptr& dex_file : dex_files) { + ASSERT_FALSE(dex_file->IsPlatformDexFile()); + } + + dex_files.clear(); + + // Load file from a system/framework directory and check that it is flagged as a framework dex. + ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_location_path_.c_str())); + success = loader.Open(system_framework_location_path_.c_str(), + system_framework_location_path_, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success); + ASSERT_GE(dex_files.size(), 1u); + for (std::unique_ptr& dex_file : dex_files) { + ASSERT_TRUE(dex_file->IsPlatformDexFile()); + } +} + } // namespace art diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index 5c6b4b56bc..dbe776e050 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -130,15 +130,15 @@ inline void WarnAboutMemberAccess(ArtMethod* method, AccessMethod access_method) } // Returns true if access to `member` should be denied to the caller of the -// reflective query. The decision is based on whether the caller is in boot -// 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. +// reflective query. The decision is based on whether the caller is in the +// platform or not. Because different users of this function determine this +// in a different way, `fn_caller_in_platform(self)` is called and should +// return true if the caller is located in the platform. // This function might print warnings into the log if the member is hidden. template inline bool ShouldBlockAccessToMember(T* member, Thread* self, - std::function fn_caller_in_boot, + std::function fn_caller_in_platform, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); @@ -149,14 +149,14 @@ inline bool ShouldBlockAccessToMember(T* member, return false; } - // Member is hidden. Walk the stack to find the caller. + // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access. // This can be *very* expensive. Save it for last. - if (fn_caller_in_boot(self)) { - // Caller in boot class path. Exit. + if (fn_caller_in_platform(self)) { + // Caller in the platform. Exit. return false; } - // Member is hidden and we are not in the boot class path. + // Member is hidden and caller is not in the platform. // Print a log message with information about this class member access. // We do this regardless of whether we block the access or not. @@ -187,18 +187,39 @@ inline bool ShouldBlockAccessToMember(T* member, return false; } +// Returns true if the caller is either loaded by the boot strap class loader or comes from +// a dex file located in ${ANDROID_ROOT}/framework/. +inline bool IsCallerInPlatformDex(ObjPtr caller_class_loader, + ObjPtr caller_dex_cache) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (caller_class_loader.IsNull()) { + return true; + } else if (caller_dex_cache.IsNull()) { + return false; + } else { + const DexFile* caller_dex_file = caller_dex_cache->GetDexFile(); + return caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile(); + } +} + +inline bool IsCallerInPlatformDex(ObjPtr caller) + REQUIRES_SHARED(Locks::mutator_lock_) { + return !caller.IsNull() && IsCallerInPlatformDex(caller->GetClassLoader(), caller->GetDexCache()); +} + // 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 inline bool ShouldBlockAccessToMember(T* member, ObjPtr caller_class_loader, + ObjPtr caller_dex_cache, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { - bool caller_in_boot = (caller_class_loader.IsNull()); + bool caller_in_platform = IsCallerInPlatformDex(caller_class_loader, caller_dex_cache); return ShouldBlockAccessToMember(member, /* thread */ nullptr, - [caller_in_boot] (Thread*) { return caller_in_boot; }, + [caller_in_platform] (Thread*) { return caller_in_platform; }, access_method); } diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 76df65f730..4a2dd3bc3f 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -184,6 +184,7 @@ static ALWAYS_INLINE bool ShouldBlockAccessToMember(T* member, ShadowFrame* fram return hiddenapi::ShouldBlockAccessToMember( member, frame->GetMethod()->GetDeclaringClass()->GetClassLoader(), + frame->GetMethod()->GetDeclaringClass()->GetDexCache(), hiddenapi::kReflection); // all uses in this file are from reflection } diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index b78fcacc08..f309581735 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -80,18 +80,15 @@ namespace art { // things not rendering correctly. E.g. b/16858794 static constexpr bool kWarnJniAbort = false; -static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr klass = GetCallingClass(self, /* num_frames */ 1); - // If `klass` is null, it is an unattached native thread. Assume this is - // *not* boot class path. - return klass != nullptr && klass->IsBootStrapClassLoaded(); +static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { + return hiddenapi::IsCallerInPlatformDex(GetCallingClass(self, /* num_frames */ 1)); } template ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { return hiddenapi::ShouldBlockAccessToMember( - member, self, IsCallerInBootClassPath, hiddenapi::kJNI); + member, self, IsCallerInPlatformDex, hiddenapi::kJNI); } // Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index fc61c9597e..ad05856eaf 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -49,8 +49,8 @@ namespace art { -// Returns true if the first non-ClassClass caller up the stack is in boot class path. -static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { +// Returns true if the first non-ClassClass caller up the stack is in a platform dex file. +static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { // Walk the stack and find the first frame not from java.lang.Class. // This is very expensive. Save this till the last. struct FirstNonClassClassCallerVisitor : public StackVisitor { @@ -82,7 +82,7 @@ static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator FirstNonClassClassCallerVisitor visitor(self); visitor.WalkStack(); return visitor.caller != nullptr && - visitor.caller->GetDeclaringClass()->IsBootStrapClassLoaded(); + hiddenapi::IsCallerInPlatformDex(visitor.caller->GetDeclaringClass()); } // Returns true if the first non-ClassClass caller up the stack is not allowed to @@ -90,7 +90,7 @@ static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); - return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInBootClassPath(self); + return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInPlatformDex(self); } // Returns true if the first non-ClassClass caller up the stack should not be @@ -99,7 +99,7 @@ template ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { return hiddenapi::ShouldBlockAccessToMember( - member, self, IsCallerInBootClassPath, hiddenapi::kReflection); + member, self, IsCallerInPlatformDex, hiddenapi::kReflection); } // Returns true if a class member should be discoverable with reflection given -- GitLab From 51abdda0b6756811091edd596ac1f40fcd2ed039 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 26 Mar 2018 10:00:37 +0100 Subject: [PATCH 131/749] Blacklist new --jvm failing tests. bug: 76399183 Change-Id: I88a09ca7d99315262464bb5d9b942fabe3de666e --- test/knownfailures.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/knownfailures.json b/test/knownfailures.json index 22c370a61f..80b262d97e 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -959,6 +959,13 @@ "variant": "jvm", "description": ["Doesn't run on RI."] }, + { + "tests": ["121-modifiers", + "1929-exception-catch-exception"], + "variant": "jvm", + "bug": "b/76399183", + "description": ["New failures to be investigated."] + }, { "tests": ["616-cha-unloading"], "variant": "trace", -- GitLab From fe94875d94ef8c06f6322021d501d58bd64c1606 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 23 Mar 2018 18:11:43 +0000 Subject: [PATCH 132/749] ART: Fix infinite recursion for deopt at dex pc 0. Previously, the interpreter checked for dex pc 0 to see if the method was just entered. If we deopt at dex pc 0, the instrumentation would emit an erroneous MethodEnteredEvent and the JIT would have received a MethodEntered() call. For JIT-on-first-use, the method would be compiled the same way as before, leading to the same deopt until stack overflow. We fix this by using a new `from_deoptimize` flag passed by the caller. Test: 680-checker-deopt-dex-pc-0 Test: testrunner.py --host \ --jit --runtime-option=-Xjitthreshold:0 Bug: 62611253 Change-Id: I50b88f15484aeae16e1375a1d80f6563fb9066e7 --- compiler/optimizing/graph_visualizer.cc | 5 ++ runtime/interpreter/interpreter.cc | 13 +++- test/680-checker-deopt-dex-pc-0/expected.txt | 2 + test/680-checker-deopt-dex-pc-0/info.txt | 2 + test/680-checker-deopt-dex-pc-0/src/Main.java | 59 +++++++++++++++++++ 5 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 test/680-checker-deopt-dex-pc-0/expected.txt create mode 100644 test/680-checker-deopt-dex-pc-0/info.txt create mode 100644 test/680-checker-deopt-dex-pc-0/src/Main.java diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 5ff31cead5..719904d780 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -576,6 +576,11 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { } StartAttributeStream() << input_list; } + if (instruction->GetDexPc() != kNoDexPc) { + StartAttributeStream("dex_pc") << instruction->GetDexPc(); + } else { + StartAttributeStream("dex_pc") << "n/a"; + } instruction->Accept(this); if (instruction->HasEnvironment()) { StringList envs; diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 735c0e815a..f23304c391 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -243,11 +243,13 @@ static inline JValue Execute( const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, - bool stay_in_interpreter = false) REQUIRES_SHARED(Locks::mutator_lock_) { + bool stay_in_interpreter = false, + bool from_deoptimize = false) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(!shadow_frame.GetMethod()->IsAbstract()); DCHECK(!shadow_frame.GetMethod()->IsNative()); - if (LIKELY(shadow_frame.GetDexPC() == 0)) { // Entering the method, but not via deoptimization. + if (LIKELY(!from_deoptimize)) { // Entering the method, but not via deoptimization. if (kIsDebugBuild) { + CHECK_EQ(shadow_frame.GetDexPC(), 0u); self->AssertNoPendingException(); } instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); @@ -568,7 +570,12 @@ void EnterInterpreterFromDeoptimize(Thread* self, } if (new_dex_pc != dex::kDexNoIndex) { shadow_frame->SetDexPC(new_dex_pc); - value = Execute(self, accessor, *shadow_frame, value); + value = Execute(self, + accessor, + *shadow_frame, + value, + /* stay_in_interpreter */ true, + /* from_deoptimize */ true); } ShadowFrame* old_frame = shadow_frame; shadow_frame = shadow_frame->GetLink(); diff --git a/test/680-checker-deopt-dex-pc-0/expected.txt b/test/680-checker-deopt-dex-pc-0/expected.txt new file mode 100644 index 0000000000..805857dc65 --- /dev/null +++ b/test/680-checker-deopt-dex-pc-0/expected.txt @@ -0,0 +1,2 @@ +JNI_OnLoad called +passed diff --git a/test/680-checker-deopt-dex-pc-0/info.txt b/test/680-checker-deopt-dex-pc-0/info.txt new file mode 100644 index 0000000000..8eae156b22 --- /dev/null +++ b/test/680-checker-deopt-dex-pc-0/info.txt @@ -0,0 +1,2 @@ +Regression test for deoptimization at dex pc 0 causing infinite recursion +for JIT-at-first-use. diff --git a/test/680-checker-deopt-dex-pc-0/src/Main.java b/test/680-checker-deopt-dex-pc-0/src/Main.java new file mode 100644 index 0000000000..d5a6a9015c --- /dev/null +++ b/test/680-checker-deopt-dex-pc-0/src/Main.java @@ -0,0 +1,59 @@ +/* + * 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 Main { + // We run this test for AOT to verify that there is a HDeoptimize with dex pc 0. + /// CHECK-START: int Main.$noinline$getInt(byte[], int) BCE (after) + /// CHECK: Deoptimize dex_pc:0 + public static int $noinline$getInt(byte[] array, int offset) { + // The aget for `array[offset]` is at dex pc 0, so the Deoptimize + // from dynamic BCE shall also be at dex pc 0. + return ((array[offset ] & 0xFF) << 0) + + ((array[offset + 1] & 0xFF) << 8) + + ((array[offset + 2] & 0xFF) << 16) + + ((array[offset + 3] & 0xFF) << 24); + } + + public static void main(String[] args) { + System.loadLibrary(args[0]); + if (hasJit()) { + byte[] array = { 0, 1, 2, 3 }; + while (!hasJitCompiledEntrypoint(Main.class, "$noinline$getInt")) { + for (int i = 0; i < 10000; ++i) { + if ($noinline$getInt(array, 0) != 0x03020100) { + throw new Error(); + } + } + try { + Thread.sleep(200); + } catch (InterruptedException ignored) {} + } + try { + // The HDeoptimize at dex pc 0 was previously handled poorly as the dex pc 0 + // was used to detect whether we entered the method. This meant that the + // instrumentation would have reported MethodEnteredEvent and we would have + // told JIT that the method was entered. With JIT-on-first-use we would also + // immediatelly recompile the method and run the compiled code leading to + // a an infinite deoptimization recursion, yielding StackOverflowError. + $noinline$getInt(array, 1); + } catch (ArrayIndexOutOfBoundsException ignored) {} + } + System.out.println("passed"); + } + + public static native boolean hasJit(); + public native static boolean hasJitCompiledEntrypoint(Class cls, String methodName); +} -- GitLab From 4b2b54701cbe2e9b4250ebe72a0780245454e75f Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 26 Mar 2018 11:41:51 +0100 Subject: [PATCH 133/749] Bump image version for bitstring type check. Enabling the bitstring type check requires rebuilding the boot image. This should have been done in https://android-review.googlesource.com/615367 . Test: Rely on TreeHugger. Bug: 64692057 Change-Id: I7c0cdbb1cd1e1b0b30469b9f808842b4c38585c7 --- runtime/image.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/image.cc b/runtime/image.cc index 56fee9d510..5af3e5451b 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -26,7 +26,7 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '6', '\0' }; // No image tables in .bss. +const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '7', '\0' }; // R^2 Bitstring type check. ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, -- GitLab From 3377825e39c4543b36acc8818109a6055f0ba2c1 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 26 Mar 2018 12:24:48 +0100 Subject: [PATCH 134/749] Blacklist 596-app-image. bug: 70734839 Change-Id: I1c391381d8bf6b69b13ea92317b7025a062ff60f --- test/knownfailures.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/knownfailures.json b/test/knownfailures.json index 80b262d97e..d390c4c049 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -276,7 +276,8 @@ }, { "tests": "596-app-images", - "variant": "npictest" + "description": "Code being tested has been disabled", + "bug": "b/70734839" }, { "tests": "055-enum-performance", -- GitLab From 0117908dab58511fd64da161369f538fc587a51b Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Mon, 26 Mar 2018 13:53:21 +0100 Subject: [PATCH 135/749] ART: Remove unused atomic field from JitCodeCache Bug: 31023171 Test: art/test.py --host -j32 Change-Id: If72ecbe4ddeaa6b404226579c92cac1e24dbdd98 --- runtime/jit/jit_code_cache.cc | 6 ------ runtime/jit/jit_code_cache.h | 4 ---- 2 files changed, 10 deletions(-) diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 1c4b93eb48..5618b6ebf9 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -248,7 +248,6 @@ JitCodeCache::JitCodeCache(MemMap* code_map, code_end_(initial_code_capacity), data_end_(initial_data_capacity), last_collection_increased_code_cache_(false), - last_update_time_ns_(0), garbage_collect_code_(garbage_collect_code), used_memory_for_data_(0), used_memory_for_code_(0), @@ -820,7 +819,6 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, // code. GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr)); } - last_update_time_ns_.store(NanoTime(), std::memory_order_release); VLOG(jit) << "JIT added (osr=" << std::boolalpha << osr << std::noboolalpha << ") " << ArtMethod::PrettyMethod(method) << "@" << method @@ -1646,10 +1644,6 @@ void JitCodeCache::GetProfiledMethods(const std::set& dex_base_loca } } -uint64_t JitCodeCache::GetLastUpdateTimeNs() const { - return last_update_time_ns_.load(std::memory_order_acquire); -} - bool JitCodeCache::IsOsrCompiled(ArtMethod* method) { MutexLock mu(Thread::Current(), lock_); return osr_code_map_.find(method) != osr_code_map_.end(); diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index dfa7ac0970..f1c99fb85a 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -407,10 +407,6 @@ class JitCodeCache { // Whether the last collection round increased the code cache. bool last_collection_increased_code_cache_ GUARDED_BY(lock_); - // Last time the the code_cache was updated. - // It is atomic to avoid locking when reading it. - Atomic last_update_time_ns_; - // Whether we can do garbage collection. Not 'const' as tests may override this. bool garbage_collect_code_; -- GitLab From 4a01cc3fd2a300e5279b69fc708f8737c715f597 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Mon, 26 Mar 2018 15:46:18 +0100 Subject: [PATCH 136/749] ART: Relax heap verification failure count Changes the atomic fail_count variable used in heap verification to be a plain variable. It is used thread private state and can be updated and read with the default C++ memory ordering. Bug: 31023171 Test: art/test.py --host -j32 Change-Id: I16ad3e138797b9900ecf719ab22c0b2b03e53c33 --- runtime/gc/heap.cc | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 52afb3850c..247e25c4da 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -2753,12 +2753,10 @@ class ScanVisitor { // Verify a reference from an object. class VerifyReferenceVisitor : public SingleRootVisitor { public: - VerifyReferenceVisitor(Heap* heap, Atomic* fail_count, bool verify_referent) + VerifyReferenceVisitor(Thread* self, Heap* heap, size_t* fail_count, bool verify_referent) REQUIRES_SHARED(Locks::mutator_lock_) - : heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {} - - size_t GetFailureCount() const { - return fail_count_->load(std::memory_order_seq_cst); + : self_(self), heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) { + CHECK_EQ(self_, Thread::Current()); } void operator()(ObjPtr klass ATTRIBUTE_UNUSED, ObjPtr ref) const @@ -2810,8 +2808,10 @@ class VerifyReferenceVisitor : public SingleRootVisitor { // Verify that the reference is live. return true; } - if (fail_count_->fetch_add(1, std::memory_order_seq_cst) == 0) { - // Print message on only on first failure to prevent spam. + CHECK_EQ(self_, Thread::Current()); // fail_count_ is private to the calling thread. + *fail_count_ += 1; + if (*fail_count_ == 1) { + // Only print message for the first failure to prevent spam. LOG(ERROR) << "!!!!!!!!!!!!!!Heap corruption detected!!!!!!!!!!!!!!!!!!!"; } if (obj != nullptr) { @@ -2897,38 +2897,41 @@ class VerifyReferenceVisitor : public SingleRootVisitor { return false; } + Thread* const self_; Heap* const heap_; - Atomic* const fail_count_; + size_t* const fail_count_; const bool verify_referent_; }; // Verify all references within an object, for use with HeapBitmap::Visit. class VerifyObjectVisitor { public: - VerifyObjectVisitor(Heap* heap, Atomic* fail_count, bool verify_referent) - : heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {} + VerifyObjectVisitor(Thread* self, Heap* heap, size_t* fail_count, bool verify_referent) + : self_(self), heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {} void operator()(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { // Note: we are verifying the references in obj but not obj itself, this is because obj must // be live or else how did we find it in the live bitmap? - VerifyReferenceVisitor visitor(heap_, fail_count_, verify_referent_); + VerifyReferenceVisitor visitor(self_, heap_, fail_count_, verify_referent_); // The class doesn't count as a reference but we should verify it anyways. obj->VisitReferences(visitor, visitor); } void VerifyRoots() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::heap_bitmap_lock_) { ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - VerifyReferenceVisitor visitor(heap_, fail_count_, verify_referent_); + VerifyReferenceVisitor visitor(self_, heap_, fail_count_, verify_referent_); Runtime::Current()->VisitRoots(&visitor); } - size_t GetFailureCount() const { - return fail_count_->load(std::memory_order_seq_cst); + uint32_t GetFailureCount() const REQUIRES(Locks::mutator_lock_) { + CHECK_EQ(self_, Thread::Current()); + return *fail_count_; } private: + Thread* const self_; Heap* const heap_; - Atomic* const fail_count_; + size_t* const fail_count_; const bool verify_referent_; }; @@ -2980,8 +2983,8 @@ size_t Heap::VerifyHeapReferences(bool verify_referents) { // Since we sorted the allocation stack content, need to revoke all // thread-local allocation stacks. RevokeAllThreadLocalAllocationStacks(self); - Atomic fail_count_(0); - VerifyObjectVisitor visitor(this, &fail_count_, verify_referents); + size_t fail_count = 0; + VerifyObjectVisitor visitor(self, this, &fail_count, verify_referents); // Verify objects in the allocation stack since these will be objects which were: // 1. Allocated prior to the GC (pre GC verification). // 2. Allocated during the GC (pre sweep GC verification). -- GitLab From 121f2038e9c8afe12f8f4096b7c84a167e7adea5 Mon Sep 17 00:00:00 2001 From: Artem Serov Date: Mon, 23 Oct 2017 19:19:06 +0100 Subject: [PATCH 137/749] ART: Implement scalar loop unrolling. Implement scalar loop unrolling for small loops (on arm64) with known trip count to reduce loop check and branch penalty and to provide more opportunities for instruction scheduling. Note: this functionality is turned off by default now. Test: cloner_test.cc Test: test-art-target, test-art-host Change-Id: Ic27fd8fb0bc0d7b69251252da37b8b510bc30acc --- compiler/Android.bp | 1 + compiler/optimizing/induction_var_range.cc | 28 +++-- compiler/optimizing/induction_var_range.h | 18 ++- compiler/optimizing/loop_analysis.cc | 120 ++++++++++++++++++ compiler/optimizing/loop_analysis.h | 139 +++++++++++++++++++++ compiler/optimizing/loop_optimization.cc | 127 +++++++++++++------ compiler/optimizing/loop_optimization.h | 25 +++- compiler/optimizing/superblock_cloner.cc | 2 +- compiler/optimizing/superblock_cloner.h | 1 - 9 files changed, 408 insertions(+), 53 deletions(-) create mode 100644 compiler/optimizing/loop_analysis.cc create mode 100644 compiler/optimizing/loop_analysis.h diff --git a/compiler/Android.bp b/compiler/Android.bp index c4d538fc88..062eb881e8 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -70,6 +70,7 @@ art_cc_defaults { "optimizing/load_store_analysis.cc", "optimizing/load_store_elimination.cc", "optimizing/locations.cc", + "optimizing/loop_analysis.cc", "optimizing/loop_optimization.cc", "optimizing/nodes.cc", "optimizing/optimization.cc", diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index 0a310ca940..55eca2316a 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -352,13 +352,15 @@ void InductionVarRange::Replace(HInstruction* instruction, } bool InductionVarRange::IsFinite(HLoopInformation* loop, /*out*/ int64_t* trip_count) const { - HInductionVarAnalysis::InductionInfo *trip = - induction_analysis_->LookupInfo(loop, GetLoopControl(loop)); - if (trip != nullptr && !IsUnsafeTripCount(trip)) { - IsConstant(trip->op_a, kExact, trip_count); - return true; - } - return false; + bool is_constant_unused = false; + return CheckForFiniteAndConstantProps(loop, &is_constant_unused, trip_count); +} + +bool InductionVarRange::HasKnownTripCount(HLoopInformation* loop, + /*out*/ int64_t* trip_count) const { + bool is_constant = false; + CheckForFiniteAndConstantProps(loop, &is_constant, trip_count); + return is_constant; } bool InductionVarRange::IsUnitStride(HInstruction* context, @@ -417,6 +419,18 @@ HInstruction* InductionVarRange::GenerateTripCount(HLoopInformation* loop, // Private class methods. // +bool InductionVarRange::CheckForFiniteAndConstantProps(HLoopInformation* loop, + /*out*/ bool* is_constant, + /*out*/ int64_t* trip_count) const { + HInductionVarAnalysis::InductionInfo *trip = + induction_analysis_->LookupInfo(loop, GetLoopControl(loop)); + if (trip != nullptr && !IsUnsafeTripCount(trip)) { + *is_constant = IsConstant(trip->op_a, kExact, trip_count); + return true; + } + return false; +} + bool InductionVarRange::IsConstant(HInductionVarAnalysis::InductionInfo* info, ConstantRequest request, /*out*/ int64_t* value) const { diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h index 0b980f596a..906dc6bb7b 100644 --- a/compiler/optimizing/induction_var_range.h +++ b/compiler/optimizing/induction_var_range.h @@ -161,9 +161,15 @@ class InductionVarRange { } /** - * Checks if header logic of a loop terminates. Sets trip-count tc if known. + * Checks if header logic of a loop terminates. If trip count is known sets 'trip_count' to its + * value. */ - bool IsFinite(HLoopInformation* loop, /*out*/ int64_t* tc) const; + bool IsFinite(HLoopInformation* loop, /*out*/ int64_t* trip_count) const; + + /** + * Checks if a trip count is known for the loop and sets 'trip_count' to its value in this case. + */ + bool HasKnownTripCount(HLoopInformation* loop, /*out*/ int64_t* trip_count) const; /** * Checks if the given instruction is a unit stride induction inside the closest enveloping @@ -193,6 +199,14 @@ class InductionVarRange { kAtLeast }; + /** + * Checks if header logic of a loop terminates. If trip count is known (constant) sets + * 'is_constant' to true and 'trip_count' to the trip count value. + */ + bool CheckForFiniteAndConstantProps(HLoopInformation* loop, + /*out*/ bool* is_constant, + /*out*/ int64_t* trip_count) const; + /** * Returns true if exact or upper/lower bound on the given induction * information is known as a 64-bit constant, which is returned in value. diff --git a/compiler/optimizing/loop_analysis.cc b/compiler/optimizing/loop_analysis.cc new file mode 100644 index 0000000000..cd3bdaf016 --- /dev/null +++ b/compiler/optimizing/loop_analysis.cc @@ -0,0 +1,120 @@ +/* + * 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. + */ + +#include "loop_analysis.h" + +namespace art { + +void LoopAnalysis::CalculateLoopBasicProperties(HLoopInformation* loop_info, + LoopAnalysisInfo* analysis_results) { + for (HBlocksInLoopIterator block_it(*loop_info); + !block_it.Done(); + block_it.Advance()) { + HBasicBlock* block = block_it.Current(); + + for (HBasicBlock* successor : block->GetSuccessors()) { + if (!loop_info->Contains(*successor)) { + analysis_results->exits_num_++; + } + } + + for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { + HInstruction* instruction = it.Current(); + if (MakesScalarUnrollingNonBeneficial(instruction)) { + analysis_results->has_instructions_preventing_scalar_unrolling_ = true; + } + analysis_results->instr_num_++; + } + analysis_results->bb_num_++; + } +} + +class Arm64LoopHelper : public ArchDefaultLoopHelper { + public: + // Scalar loop unrolling parameters and heuristics. + // + // Maximum possible unrolling factor. + static constexpr uint32_t kArm64ScalarMaxUnrollFactor = 2; + // Loop's maximum instruction count. Loops with higher count will not be peeled/unrolled. + static constexpr uint32_t kArm64ScalarHeuristicMaxBodySizeInstr = 40; + // Loop's maximum basic block count. Loops with higher count will not be peeled/unrolled. + static constexpr uint32_t kArm64ScalarHeuristicMaxBodySizeBlocks = 8; + + // SIMD loop unrolling parameters and heuristics. + // + // Maximum possible unrolling factor. + static constexpr uint32_t kArm64SimdMaxUnrollFactor = 8; + // Loop's maximum instruction count. Loops with higher count will not be unrolled. + static constexpr uint32_t kArm64SimdHeuristicMaxBodySizeInstr = 50; + + bool IsLoopTooBigForScalarUnrolling(LoopAnalysisInfo* loop_analysis_info) const OVERRIDE { + size_t instr_num = loop_analysis_info->GetNumberOfInstructions(); + size_t bb_num = loop_analysis_info->GetNumberOfBasicBlocks(); + return (instr_num >= kArm64ScalarHeuristicMaxBodySizeInstr || + bb_num >= kArm64ScalarHeuristicMaxBodySizeBlocks); + } + + uint32_t GetScalarUnrollingFactor(HLoopInformation* loop_info ATTRIBUTE_UNUSED, + uint64_t trip_count) const OVERRIDE { + uint32_t desired_unrolling_factor = kArm64ScalarMaxUnrollFactor; + if (trip_count < desired_unrolling_factor || trip_count % desired_unrolling_factor != 0) { + return kNoUnrollingFactor; + } + + return desired_unrolling_factor; + } + + uint32_t GetSIMDUnrollingFactor(HBasicBlock* block, + int64_t trip_count, + uint32_t max_peel, + uint32_t vector_length) const OVERRIDE { + // Don't unroll with insufficient iterations. + // TODO: Unroll loops with unknown trip count. + DCHECK_NE(vector_length, 0u); + if (trip_count < (2 * vector_length + max_peel)) { + return kNoUnrollingFactor; + } + // Don't unroll for large loop body size. + uint32_t instruction_count = block->GetInstructions().CountSize(); + if (instruction_count >= kArm64SimdHeuristicMaxBodySizeInstr) { + return kNoUnrollingFactor; + } + // Find a beneficial unroll factor with the following restrictions: + // - At least one iteration of the transformed loop should be executed. + // - The loop body shouldn't be "too big" (heuristic). + + uint32_t uf1 = kArm64SimdHeuristicMaxBodySizeInstr / instruction_count; + uint32_t uf2 = (trip_count - max_peel) / vector_length; + uint32_t unroll_factor = + TruncToPowerOfTwo(std::min({uf1, uf2, kArm64SimdMaxUnrollFactor})); + DCHECK_GE(unroll_factor, 1u); + return unroll_factor; + } +}; + +ArchDefaultLoopHelper* ArchDefaultLoopHelper::Create(InstructionSet isa, + ArenaAllocator* allocator) { + switch (isa) { + case InstructionSet::kArm64: { + return new (allocator) Arm64LoopHelper; + } + default: { + return new (allocator) ArchDefaultLoopHelper; + } + } +} + +} // namespace art diff --git a/compiler/optimizing/loop_analysis.h b/compiler/optimizing/loop_analysis.h new file mode 100644 index 0000000000..bad406f10b --- /dev/null +++ b/compiler/optimizing/loop_analysis.h @@ -0,0 +1,139 @@ +/* + * 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. + */ + +#ifndef ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_ +#define ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_ + +#include "nodes.h" + +namespace art { + +class LoopAnalysis; + +// No loop unrolling factor (just one copy of the loop-body). +static constexpr uint32_t kNoUnrollingFactor = 1; + +// Class to hold cached information on properties of the loop. +class LoopAnalysisInfo : public ValueObject { + public: + explicit LoopAnalysisInfo(HLoopInformation* loop_info) + : bb_num_(0), + instr_num_(0), + exits_num_(0), + has_instructions_preventing_scalar_unrolling_(false), + loop_info_(loop_info) {} + + size_t GetNumberOfBasicBlocks() const { return bb_num_; } + size_t GetNumberOfInstructions() const { return instr_num_; } + size_t GetNumberOfExits() const { return exits_num_; } + + bool HasInstructionsPreventingScalarUnrolling() const { + return has_instructions_preventing_scalar_unrolling_; + } + + const HLoopInformation* GetLoopInfo() const { return loop_info_; } + + private: + // Number of basic blocks in the loop body. + size_t bb_num_; + // Number of instructions in the loop body. + size_t instr_num_; + // Number of loop's exits. + size_t exits_num_; + // Whether the loop has instructions which make scalar loop unrolling non-beneficial. + bool has_instructions_preventing_scalar_unrolling_; + + // Corresponding HLoopInformation. + const HLoopInformation* loop_info_; + + friend class LoopAnalysis; +}; + +// Placeholder class for methods and routines used to analyse loops, calculate loop properties +// and characteristics. +class LoopAnalysis : public ValueObject { + public: + // Calculates loops basic properties like body size, exits number, etc. and fills + // 'analysis_results' with this information. + static void CalculateLoopBasicProperties(HLoopInformation* loop_info, + LoopAnalysisInfo* analysis_results); + + private: + // Returns whether an instruction makes scalar loop unrolling non-beneficial. + // + // If in the loop body we have a dex/runtime call then its contribution to the whole + // loop performance will probably prevail. So unrolling optimization will not bring + // any noticeable performance improvement however will increase the code size. + static bool MakesScalarUnrollingNonBeneficial(HInstruction* instruction) { + return (instruction->IsNewArray() || + instruction->IsNewInstance() || + instruction->IsUnresolvedInstanceFieldGet() || + instruction->IsUnresolvedInstanceFieldSet() || + instruction->IsUnresolvedStaticFieldGet() || + instruction->IsUnresolvedStaticFieldSet() || + // TODO: Unroll loops with intrinsified invokes. + instruction->IsInvoke() || + // TODO: Unroll loops with ClinitChecks. + instruction->IsClinitCheck()); + } +}; + +// +// Helper class which holds target-dependent methods and constants needed for loop optimizations. +// +// To support peeling/unrolling for a new architecture one needs to create new helper class, +// inherit it from this and add implementation for the following methods. +// +class ArchDefaultLoopHelper : public ArenaObject { + public: + virtual ~ArchDefaultLoopHelper() {} + + // Creates an instance of specialised helper for the target or default helper if the target + // doesn't support loop peeling and unrolling. + static ArchDefaultLoopHelper* Create(InstructionSet isa, ArenaAllocator* allocator); + + // Returns whether the loop is too big for loop unrolling by checking its total number of + // basic blocks and instructions. + // + // If the loop body has too many instructions then unrolling optimization will not bring + // any noticeable performance improvement however will increase the code size. + // + // Returns 'true' by default, should be overridden by particular target loop helper. + virtual bool IsLoopTooBigForScalarUnrolling( + LoopAnalysisInfo* loop_analysis_info ATTRIBUTE_UNUSED) const { return true; } + + // Returns optimal scalar unrolling factor for the loop. + // + // Returns kNoUnrollingFactor by default, should be overridden by particular target loop helper. + virtual uint32_t GetScalarUnrollingFactor(HLoopInformation* loop_info ATTRIBUTE_UNUSED, + uint64_t trip_count ATTRIBUTE_UNUSED) const { + return kNoUnrollingFactor; + } + + // Returns optimal SIMD unrolling factor for the loop. + // + // Returns kNoUnrollingFactor by default, should be overridden by particular target loop helper. + virtual uint32_t GetSIMDUnrollingFactor(HBasicBlock* block ATTRIBUTE_UNUSED, + int64_t trip_count ATTRIBUTE_UNUSED, + uint32_t max_peel ATTRIBUTE_UNUSED, + uint32_t vector_length ATTRIBUTE_UNUSED) const { + return kNoUnrollingFactor; + } +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_ diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index e1fb7ac17e..69080340e4 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -33,8 +33,8 @@ namespace art { // Enables vectorization (SIMDization) in the loop optimizer. static constexpr bool kEnableVectorization = true; -// No loop unrolling factor (just one copy of the loop-body). -static constexpr uint32_t kNoUnrollingFactor = 1; +// Enables scalar loop unrolling in the loop optimizer. +static constexpr bool kEnableScalarUnrolling = false; // // Static helpers. @@ -480,7 +480,11 @@ HLoopOptimization::HLoopOptimization(HGraph* graph, vector_preheader_(nullptr), vector_header_(nullptr), vector_body_(nullptr), - vector_index_(nullptr) { + vector_index_(nullptr), + arch_loop_helper_(ArchDefaultLoopHelper::Create(compiler_driver_ != nullptr + ? compiler_driver_->GetInstructionSet() + : InstructionSet::kNone, + global_allocator_)) { } void HLoopOptimization::Run() { @@ -691,7 +695,7 @@ void HLoopOptimization::SimplifyBlocks(LoopNode* node) { } } -bool HLoopOptimization::OptimizeInnerLoop(LoopNode* node) { +bool HLoopOptimization::TryOptimizeInnerLoopFinite(LoopNode* node) { HBasicBlock* header = node->loop_info->GetHeader(); HBasicBlock* preheader = node->loop_info->GetPreHeader(); // Ensure loop header logic is finite. @@ -761,6 +765,83 @@ bool HLoopOptimization::OptimizeInnerLoop(LoopNode* node) { return false; } +bool HLoopOptimization::OptimizeInnerLoop(LoopNode* node) { + return TryOptimizeInnerLoopFinite(node) || + TryUnrollingForBranchPenaltyReduction(node); +} + +void HLoopOptimization::PeelOrUnrollOnce(LoopNode* loop_node, + bool do_unrolling, + SuperblockCloner::HBasicBlockMap* bb_map, + SuperblockCloner::HInstructionMap* hir_map) { + // TODO: peel loop nests. + DCHECK(loop_node->inner == nullptr); + + // Check that loop info is up-to-date. + HLoopInformation* loop_info = loop_node->loop_info; + HBasicBlock* header = loop_info->GetHeader(); + DCHECK(loop_info == header->GetLoopInformation()); + + PeelUnrollHelper helper(loop_info, bb_map, hir_map); + DCHECK(helper.IsLoopClonable()); + HBasicBlock* new_header = do_unrolling ? helper.DoUnrolling() : helper.DoPeeling(); + DCHECK(header == new_header); + DCHECK(loop_info == new_header->GetLoopInformation()); +} + +// +// Loop unrolling: generic part methods. +// + +bool HLoopOptimization::TryUnrollingForBranchPenaltyReduction(LoopNode* loop_node) { + // Don't run peeling/unrolling if compiler_driver_ is nullptr (i.e., running under tests) + // as InstructionSet is needed. + if (!kEnableScalarUnrolling || compiler_driver_ == nullptr) { + return false; + } + + HLoopInformation* loop_info = loop_node->loop_info; + int64_t trip_count = 0; + // Only unroll loops with a known tripcount. + if (!induction_range_.HasKnownTripCount(loop_info, &trip_count)) { + return false; + } + + uint32_t unrolling_factor = arch_loop_helper_->GetScalarUnrollingFactor(loop_info, trip_count); + if (unrolling_factor == kNoUnrollingFactor) { + return false; + } + + LoopAnalysisInfo loop_analysis_info(loop_info); + LoopAnalysis::CalculateLoopBasicProperties(loop_info, &loop_analysis_info); + + // Check "IsLoopClonable" last as it can be time-consuming. + if (arch_loop_helper_->IsLoopTooBigForScalarUnrolling(&loop_analysis_info) || + (loop_analysis_info.GetNumberOfExits() > 1) || + loop_analysis_info.HasInstructionsPreventingScalarUnrolling() || + !PeelUnrollHelper::IsLoopClonable(loop_info)) { + return false; + } + + // TODO: support other unrolling factors. + DCHECK_EQ(unrolling_factor, 2u); + + // Perform unrolling. + ArenaAllocator* arena = loop_info->GetHeader()->GetGraph()->GetAllocator(); + SuperblockCloner::HBasicBlockMap bb_map( + std::less(), arena->Adapter(kArenaAllocSuperblockCloner)); + SuperblockCloner::HInstructionMap hir_map( + std::less(), arena->Adapter(kArenaAllocSuperblockCloner)); + PeelOrUnrollOnce(loop_node, /* unrolling */ true, &bb_map, &hir_map); + + // Remove the redundant loop check after unrolling. + HIf* copy_hif = bb_map.Get(loop_info->GetHeader())->GetLastInstruction()->AsIf(); + int32_t constant = loop_info->Contains(*copy_hif->IfTrueSuccessor()) ? 1 : 0; + copy_hif->ReplaceInput(graph_->GetIntConstant(constant), 0u); + + return true; +} + // // Loop vectorization. The implementation is based on the book by Aart J.C. Bik: // "The Software Vectorization Handbook. Applying Multimedia Extensions for Maximum Performance." @@ -891,7 +972,8 @@ void HLoopOptimization::Vectorize(LoopNode* node, HBasicBlock* preheader = node->loop_info->GetPreHeader(); // Pick a loop unrolling factor for the vector loop. - uint32_t unroll = GetUnrollingFactor(block, trip_count); + uint32_t unroll = arch_loop_helper_->GetSIMDUnrollingFactor( + block, trip_count, MaxNumberPeeled(), vector_length_); uint32_t chunk = vector_length_ * unroll; DCHECK(trip_count == 0 || (trip_count >= MaxNumberPeeled() + chunk)); @@ -2174,41 +2256,6 @@ bool HLoopOptimization::IsVectorizationProfitable(int64_t trip_count) { return true; } -static constexpr uint32_t ARM64_SIMD_MAXIMUM_UNROLL_FACTOR = 8; -static constexpr uint32_t ARM64_SIMD_HEURISTIC_MAX_BODY_SIZE = 50; - -uint32_t HLoopOptimization::GetUnrollingFactor(HBasicBlock* block, int64_t trip_count) { - uint32_t max_peel = MaxNumberPeeled(); - switch (compiler_driver_->GetInstructionSet()) { - case InstructionSet::kArm64: { - // Don't unroll with insufficient iterations. - // TODO: Unroll loops with unknown trip count. - DCHECK_NE(vector_length_, 0u); - if (trip_count < (2 * vector_length_ + max_peel)) { - return kNoUnrollingFactor; - } - // Don't unroll for large loop body size. - uint32_t instruction_count = block->GetInstructions().CountSize(); - if (instruction_count >= ARM64_SIMD_HEURISTIC_MAX_BODY_SIZE) { - return kNoUnrollingFactor; - } - // Find a beneficial unroll factor with the following restrictions: - // - At least one iteration of the transformed loop should be executed. - // - The loop body shouldn't be "too big" (heuristic). - uint32_t uf1 = ARM64_SIMD_HEURISTIC_MAX_BODY_SIZE / instruction_count; - uint32_t uf2 = (trip_count - max_peel) / vector_length_; - uint32_t unroll_factor = - TruncToPowerOfTwo(std::min({uf1, uf2, ARM64_SIMD_MAXIMUM_UNROLL_FACTOR})); - DCHECK_GE(unroll_factor, 1u); - return unroll_factor; - } - case InstructionSet::kX86: - case InstructionSet::kX86_64: - default: - return kNoUnrollingFactor; - } -} - // // Helpers. // diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h index 9414e5a0c6..0120cffa56 100644 --- a/compiler/optimizing/loop_optimization.h +++ b/compiler/optimizing/loop_optimization.h @@ -20,12 +20,15 @@ #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" #include "induction_var_range.h" +#include "loop_analysis.h" #include "nodes.h" #include "optimization.h" +#include "superblock_cloner.h" namespace art { class CompilerDriver; +class ArchDefaultLoopHelper; /** * Loop optimizations. Builds a loop hierarchy and applies optimizations to @@ -135,10 +138,26 @@ class HLoopOptimization : public HOptimization { void SimplifyInduction(LoopNode* node); void SimplifyBlocks(LoopNode* node); - // Performs optimizations specific to inner loop (empty loop removal, + // Performs optimizations specific to inner loop with finite header logic (empty loop removal, // unrolling, vectorization). Returns true if anything changed. + bool TryOptimizeInnerLoopFinite(LoopNode* node); + + // Performs optimizations specific to inner loop. Returns true if anything changed. bool OptimizeInnerLoop(LoopNode* node); + // Performs loop peeling/unrolling once (depends on the 'do_unrolling'); the transformation + // preserves the header and the loop info. + // + // Note: the function records copying information about blocks and instructions. + void PeelOrUnrollOnce(LoopNode* loop_node, + bool do_unrolling, + SuperblockCloner::HBasicBlockMap* bb_map, + SuperblockCloner::HInstructionMap* hir_map); + + // Tries to apply loop unrolling for branch penalty reduction and better instruction scheduling + // opportunities. Returns whether transformation happened. + bool TryUnrollingForBranchPenaltyReduction(LoopNode* loop_node); + // // Vectorization analysis and synthesis. // @@ -203,7 +222,6 @@ class HLoopOptimization : public HOptimization { const ArrayReference* peeling_candidate); uint32_t MaxNumberPeeled(); bool IsVectorizationProfitable(int64_t trip_count); - uint32_t GetUnrollingFactor(HBasicBlock* block, int64_t trip_count); // // Helpers. @@ -297,6 +315,9 @@ class HLoopOptimization : public HOptimization { HBasicBlock* vector_body_; // body of the new loop HInstruction* vector_index_; // normalized index of the new loop + // Helper for target-specific behaviour for loop optimizations. + ArchDefaultLoopHelper* arch_loop_helper_; + friend class LoopOptimizationTest; DISALLOW_COPY_AND_ASSIGN(HLoopOptimization); diff --git a/compiler/optimizing/superblock_cloner.cc b/compiler/optimizing/superblock_cloner.cc index 04942f9a4a..ee74f1001f 100644 --- a/compiler/optimizing/superblock_cloner.cc +++ b/compiler/optimizing/superblock_cloner.cc @@ -853,7 +853,7 @@ void SuperblockCloner::CleanUp() { } } - if (kSuperblockClonerVerify) { + if (kIsDebugBuild) { VerifyGraph(); } } diff --git a/compiler/optimizing/superblock_cloner.h b/compiler/optimizing/superblock_cloner.h index 19c9dd471c..afd5a5d6e7 100644 --- a/compiler/optimizing/superblock_cloner.h +++ b/compiler/optimizing/superblock_cloner.h @@ -25,7 +25,6 @@ namespace art { static const bool kSuperblockClonerLogging = false; -static const bool kSuperblockClonerVerify = false; // Represents an edge between two HBasicBlocks. // -- GitLab From 0127b71a2588efcd1a53c192c5c267157878b010 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 26 Mar 2018 19:31:41 +0000 Subject: [PATCH 138/749] Revert "Allow hidden API access from system libraries" Bug: 64382372 Bug: 76138670 Bug: 76165623 Bug: 76112393 Seems to cause run-test issues. This reverts commit eefabd28c21e6ae02ca05e97bd22f9e23cbf98a5. Change-Id: Ia9b3d946742dbfde4fb56cf14413fde0600c8252 --- libdexfile/dex/dex_file.cc | 3 +- libdexfile/dex/dex_file.h | 11 --- runtime/base/file_utils.cc | 7 -- runtime/base/file_utils.h | 3 - runtime/class_linker.cc | 12 ++-- runtime/dex/art_dex_file_loader.cc | 36 ---------- runtime/dex/art_dex_file_loader.h | 13 ---- runtime/dex/art_dex_file_loader_test.cc | 90 +----------------------- runtime/hidden_api.h | 43 +++-------- runtime/interpreter/unstarted_runtime.cc | 1 - runtime/jni_internal.cc | 9 ++- runtime/native/java_lang_Class.cc | 10 +-- 12 files changed, 28 insertions(+), 210 deletions(-) diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc index 1f471106df..4613b40457 100644 --- a/libdexfile/dex/dex_file.cc +++ b/libdexfile/dex/dex_file.cc @@ -121,8 +121,7 @@ DexFile::DexFile(const uint8_t* base, num_call_site_ids_(0), oat_dex_file_(oat_dex_file), container_(std::move(container)), - is_compact_dex_(is_compact_dex), - is_platform_dex_(false) { + is_compact_dex_(is_compact_dex) { CHECK(begin_ != nullptr) << GetLocation(); CHECK_GT(size_, 0U) << GetLocation(); // Check base (=header) alignment. diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index 683a8243ed..aeb49d2c25 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -992,14 +992,6 @@ class DexFile { ALWAYS_INLINE const StandardDexFile* AsStandardDexFile() const; ALWAYS_INLINE const CompactDexFile* AsCompactDexFile() const; - ALWAYS_INLINE bool IsPlatformDexFile() const { - return is_platform_dex_; - } - - ALWAYS_INLINE void SetIsPlatformDexFile() { - is_platform_dex_ = true; - } - bool IsInMainSection(const void* addr) const { return Begin() <= addr && addr < Begin() + Size(); } @@ -1102,9 +1094,6 @@ class DexFile { // If the dex file is a compact dex file. If false then the dex file is a standard dex file. const bool is_compact_dex_; - // If the dex file is located in /system/framework/. - bool is_platform_dex_; - friend class DexFileLoader; friend class DexFileVerifierTest; friend class OatWriter; diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc index 36caa7fcfb..1cb3b9c380 100644 --- a/runtime/base/file_utils.cc +++ b/runtime/base/file_utils.cc @@ -319,11 +319,4 @@ bool LocationIsOnSystem(const char* location) { return path != nullptr && android::base::StartsWith(path.get(), GetAndroidRoot().c_str()); } -bool LocationIsOnSystemFramework(const char* location) { - UniqueCPtr path(realpath(location, nullptr)); - std::string framework_path = GetAndroidRoot() + "/framework/"; - return path != nullptr && - android::base::StartsWith(path.get(), framework_path.c_str()); -} - } // namespace art diff --git a/runtime/base/file_utils.h b/runtime/base/file_utils.h index 8adb4f7bf8..7f691d546b 100644 --- a/runtime/base/file_utils.h +++ b/runtime/base/file_utils.h @@ -75,9 +75,6 @@ std::string ReplaceFileExtension(const std::string& filename, const std::string& // Return whether the location is on system (i.e. android root). bool LocationIsOnSystem(const char* location); -// Return whether the location is on system/framework (i.e. android_root/framework). -bool LocationIsOnSystemFramework(const char* location); - } // namespace art #endif // ART_RUNTIME_BASE_FILE_UTILS_H_ diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 32cefbdfc5..1d72b46f6c 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -7935,8 +7935,7 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr klass, } DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); if (resolved != nullptr && - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader, dex_cache, hiddenapi::kLinking)) { + hiddenapi::ShouldBlockAccessToMember(resolved, class_loader, hiddenapi::kLinking)) { resolved = nullptr; } if (resolved != nullptr) { @@ -8078,8 +8077,7 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx, resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_); } if (resolved != nullptr && - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) { + hiddenapi::ShouldBlockAccessToMember(resolved, class_loader.Get(), hiddenapi::kLinking)) { resolved = nullptr; } return resolved; @@ -8158,8 +8156,7 @@ ArtField* ClassLinker::ResolveField(uint32_t field_idx, } if (resolved == nullptr || - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) { + 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); @@ -8192,8 +8189,7 @@ ArtField* ClassLinker::ResolveFieldJLS(uint32_t field_idx, StringPiece type(dex_file.GetFieldTypeDescriptor(field_id)); resolved = mirror::Class::FindField(self, klass, name, type); if (resolved != nullptr && - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) { + hiddenapi::ShouldBlockAccessToMember(resolved, class_loader.Get(), hiddenapi::kLinking)) { resolved = nullptr; } if (resolved != nullptr) { diff --git a/runtime/dex/art_dex_file_loader.cc b/runtime/dex/art_dex_file_loader.cc index cf9a814a6b..c456764834 100644 --- a/runtime/dex/art_dex_file_loader.cc +++ b/runtime/dex/art_dex_file_loader.cc @@ -22,7 +22,6 @@ #include "android-base/stringprintf.h" #include "base/file_magic.h" -#include "base/file_utils.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/unix_file/fd_file.h" @@ -489,39 +488,4 @@ bool ArtDexFileLoader::OpenAllDexFilesFromZip( } } -std::unique_ptr ArtDexFileLoader::OpenCommon(const uint8_t* base, - size_t size, - const uint8_t* data_base, - size_t data_size, - const std::string& location, - uint32_t location_checksum, - const OatDexFile* oat_dex_file, - bool verify, - bool verify_checksum, - std::string* error_msg, - std::unique_ptr container, - VerifyResult* verify_result) { - std::unique_ptr dex_file = DexFileLoader::OpenCommon(base, - size, - data_base, - data_size, - location, - location_checksum, - oat_dex_file, - verify, - verify_checksum, - error_msg, - std::move(container), - verify_result); - - // Check if this dex file is located in the framework directory. - // If it is, set a flag on the dex file. This is used by hidden API - // policy decision logic. - if (dex_file != nullptr && LocationIsOnSystemFramework(location.c_str())) { - dex_file->SetIsPlatformDexFile(); - } - - return dex_file; -} - } // namespace art diff --git a/runtime/dex/art_dex_file_loader.h b/runtime/dex/art_dex_file_loader.h index 7577945632..7c7a59b37f 100644 --- a/runtime/dex/art_dex_file_loader.h +++ b/runtime/dex/art_dex_file_loader.h @@ -120,19 +120,6 @@ class ArtDexFileLoader : public DexFileLoader { bool verify_checksum, std::string* error_msg, ZipOpenErrorCode* error_code) const; - - static std::unique_ptr OpenCommon(const uint8_t* base, - size_t size, - const uint8_t* data_base, - size_t data_size, - const std::string& location, - uint32_t location_checksum, - const OatDexFile* oat_dex_file, - bool verify, - bool verify_checksum, - std::string* error_msg, - std::unique_ptr container, - VerifyResult* verify_result); }; } // namespace art diff --git a/runtime/dex/art_dex_file_loader_test.cc b/runtime/dex/art_dex_file_loader_test.cc index 3e0d6662c7..6e2cfec381 100644 --- a/runtime/dex/art_dex_file_loader_test.cc +++ b/runtime/dex/art_dex_file_loader_test.cc @@ -16,11 +16,9 @@ #include -#include #include #include "art_dex_file_loader.h" -#include "base/file_utils.h" #include "base/os.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" @@ -37,40 +35,7 @@ namespace art { -static void Copy(const std::string& src, const std::string& dst) { - std::ifstream src_stream(src, std::ios::binary); - std::ofstream dst_stream(dst, std::ios::binary); - dst_stream << src_stream.rdbuf(); -} - -class ArtDexFileLoaderTest : public CommonRuntimeTest { - public: - virtual void SetUp() { - CommonRuntimeTest::SetUp(); - - std::string dex_location = GetTestDexFileName("Main"); - - data_location_path_ = android_data_ + "/foo.jar"; - system_location_path_ = GetAndroidRoot() + "/foo.jar"; - system_framework_location_path_ = GetAndroidRoot() + "/framework/foo.jar"; - - Copy(dex_location, data_location_path_); - Copy(dex_location, system_location_path_); - Copy(dex_location, system_framework_location_path_); - } - - virtual void TearDown() { - remove(data_location_path_.c_str()); - remove(system_location_path_.c_str()); - remove(system_framework_location_path_.c_str()); - CommonRuntimeTest::TearDown(); - } - - protected: - std::string data_location_path_; - std::string system_location_path_; - std::string system_framework_location_path_; -}; +class ArtDexFileLoaderTest : public CommonRuntimeTest {}; // TODO: Port OpenTestDexFile(s) need to be ported to use non-ART utilities, and // the tests that depend upon them should be moved to dex_file_loader_test.cc @@ -339,57 +304,4 @@ TEST_F(ArtDexFileLoaderTest, GetDexCanonicalLocation) { ASSERT_EQ(0, unlink(dex_location_sym.c_str())); } -TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { - ArtDexFileLoader loader; - bool success; - std::string error_msg; - std::vector> dex_files; - - // Load file from a non-system directory and check that it is not flagged as framework. - ASSERT_FALSE(LocationIsOnSystemFramework(data_location_path_.c_str())); - success = loader.Open(data_location_path_.c_str(), - data_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); - ASSERT_TRUE(success); - ASSERT_GE(dex_files.size(), 1u); - for (std::unique_ptr& dex_file : dex_files) { - ASSERT_FALSE(dex_file->IsPlatformDexFile()); - } - - dex_files.clear(); - - // Load file from a system, non-framework directory and check that it is not flagged as framework. - ASSERT_FALSE(LocationIsOnSystemFramework(system_location_path_.c_str())); - success = loader.Open(system_location_path_.c_str(), - system_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); - ASSERT_TRUE(success); - ASSERT_GE(dex_files.size(), 1u); - for (std::unique_ptr& dex_file : dex_files) { - ASSERT_FALSE(dex_file->IsPlatformDexFile()); - } - - dex_files.clear(); - - // Load file from a system/framework directory and check that it is flagged as a framework dex. - ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_location_path_.c_str())); - success = loader.Open(system_framework_location_path_.c_str(), - system_framework_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); - ASSERT_TRUE(success); - ASSERT_GE(dex_files.size(), 1u); - for (std::unique_ptr& dex_file : dex_files) { - ASSERT_TRUE(dex_file->IsPlatformDexFile()); - } -} - } // namespace art diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index dbe776e050..5c6b4b56bc 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -130,15 +130,15 @@ inline void WarnAboutMemberAccess(ArtMethod* method, AccessMethod access_method) } // Returns true if access to `member` should be denied to the caller of the -// reflective query. The decision is based on whether the caller is in the -// platform or not. Because different users of this function determine this -// in a different way, `fn_caller_in_platform(self)` is called and should -// return true if the caller is located in the platform. +// reflective query. The decision is based on whether the caller is in boot +// 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 hidden. template inline bool ShouldBlockAccessToMember(T* member, Thread* self, - std::function fn_caller_in_platform, + std::function fn_caller_in_boot, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); @@ -149,14 +149,14 @@ inline bool ShouldBlockAccessToMember(T* member, return false; } - // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access. + // Member is hidden. Walk the stack to find the caller. // This can be *very* expensive. Save it for last. - if (fn_caller_in_platform(self)) { - // Caller in the platform. Exit. + if (fn_caller_in_boot(self)) { + // Caller in boot class path. Exit. return false; } - // Member is hidden and caller is not in the platform. + // Member is hidden and we are not in the boot class path. // Print a log message with information about this class member access. // We do this regardless of whether we block the access or not. @@ -187,39 +187,18 @@ inline bool ShouldBlockAccessToMember(T* member, return false; } -// Returns true if the caller is either loaded by the boot strap class loader or comes from -// a dex file located in ${ANDROID_ROOT}/framework/. -inline bool IsCallerInPlatformDex(ObjPtr caller_class_loader, - ObjPtr caller_dex_cache) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (caller_class_loader.IsNull()) { - return true; - } else if (caller_dex_cache.IsNull()) { - return false; - } else { - const DexFile* caller_dex_file = caller_dex_cache->GetDexFile(); - return caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile(); - } -} - -inline bool IsCallerInPlatformDex(ObjPtr caller) - REQUIRES_SHARED(Locks::mutator_lock_) { - return !caller.IsNull() && IsCallerInPlatformDex(caller->GetClassLoader(), caller->GetDexCache()); -} - // 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 inline bool ShouldBlockAccessToMember(T* member, ObjPtr caller_class_loader, - ObjPtr caller_dex_cache, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { - bool caller_in_platform = IsCallerInPlatformDex(caller_class_loader, caller_dex_cache); + bool caller_in_boot = (caller_class_loader.IsNull()); return ShouldBlockAccessToMember(member, /* thread */ nullptr, - [caller_in_platform] (Thread*) { return caller_in_platform; }, + [caller_in_boot] (Thread*) { return caller_in_boot; }, access_method); } diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 4a2dd3bc3f..76df65f730 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -184,7 +184,6 @@ static ALWAYS_INLINE bool ShouldBlockAccessToMember(T* member, ShadowFrame* fram return hiddenapi::ShouldBlockAccessToMember( member, frame->GetMethod()->GetDeclaringClass()->GetClassLoader(), - frame->GetMethod()->GetDeclaringClass()->GetDexCache(), hiddenapi::kReflection); // all uses in this file are from reflection } diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index f309581735..b78fcacc08 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -80,15 +80,18 @@ namespace art { // things not rendering correctly. E.g. b/16858794 static constexpr bool kWarnJniAbort = false; -static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::IsCallerInPlatformDex(GetCallingClass(self, /* num_frames */ 1)); +static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr klass = GetCallingClass(self, /* num_frames */ 1); + // If `klass` is null, it is an unattached native thread. Assume this is + // *not* boot class path. + return klass != nullptr && klass->IsBootStrapClassLoaded(); } template ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { return hiddenapi::ShouldBlockAccessToMember( - member, self, IsCallerInPlatformDex, hiddenapi::kJNI); + member, self, IsCallerInBootClassPath, hiddenapi::kJNI); } // Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index ad05856eaf..fc61c9597e 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -49,8 +49,8 @@ namespace art { -// Returns true if the first non-ClassClass caller up the stack is in a platform dex file. -static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { +// Returns true if the first non-ClassClass caller up the stack is in boot class path. +static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { // Walk the stack and find the first frame not from java.lang.Class. // This is very expensive. Save this till the last. struct FirstNonClassClassCallerVisitor : public StackVisitor { @@ -82,7 +82,7 @@ static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_l FirstNonClassClassCallerVisitor visitor(self); visitor.WalkStack(); return visitor.caller != nullptr && - hiddenapi::IsCallerInPlatformDex(visitor.caller->GetDeclaringClass()); + visitor.caller->GetDeclaringClass()->IsBootStrapClassLoaded(); } // Returns true if the first non-ClassClass caller up the stack is not allowed to @@ -90,7 +90,7 @@ static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_l ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); - return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInPlatformDex(self); + return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInBootClassPath(self); } // Returns true if the first non-ClassClass caller up the stack should not be @@ -99,7 +99,7 @@ template ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { return hiddenapi::ShouldBlockAccessToMember( - member, self, IsCallerInPlatformDex, hiddenapi::kReflection); + member, self, IsCallerInBootClassPath, hiddenapi::kReflection); } // Returns true if a class member should be discoverable with reflection given -- GitLab From b2749310a8b4789b7ddd8fe1055d3bcb19741518 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Fri, 23 Mar 2018 13:03:45 -0700 Subject: [PATCH 139/749] Display all stack frames on a crash. Bug: 74121887 Test: Forced a crash in the unwind code and verified the frames Test: in the unwinder are present. Change-Id: Id158dadea25e986a0d2e9feba0873a921644fd22 --- runtime/native_stack_dump.cc | 4 +++- runtime/native_stack_dump.h | 3 ++- runtime/runtime_common.h | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc index c26c26e544..0db1770eea 100644 --- a/runtime/native_stack_dump.cc +++ b/runtime/native_stack_dump.cc @@ -287,7 +287,8 @@ void DumpNativeStack(std::ostream& os, BacktraceMap* existing_map, const char* prefix, ArtMethod* current_method, - void* ucontext_ptr) { + void* ucontext_ptr, + bool skip_frames) { // b/18119146 if (RUNNING_ON_MEMORY_TOOL != 0) { return; @@ -300,6 +301,7 @@ void DumpNativeStack(std::ostream& os, map = tmp_map.get(); } std::unique_ptr backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid, map)); + backtrace->SetSkipFrames(skip_frames); if (!backtrace->Unwind(0, reinterpret_cast(ucontext_ptr))) { os << prefix << "(backtrace::Unwind failed for thread " << tid << ": " << backtrace->GetErrorString(backtrace->GetError()) << ")" << std::endl; diff --git a/runtime/native_stack_dump.h b/runtime/native_stack_dump.h index d64bc824a5..ad4bfab0e9 100644 --- a/runtime/native_stack_dump.h +++ b/runtime/native_stack_dump.h @@ -35,7 +35,8 @@ void DumpNativeStack(std::ostream& os, BacktraceMap* map = nullptr, const char* prefix = "", ArtMethod* current_method = nullptr, - void* ucontext = nullptr) + void* ucontext = nullptr, + bool skip_frames = true) NO_THREAD_SAFETY_ANALYSIS; // Dumps the kernel stack for thread 'tid' to 'os'. Note that this is only available on linux-x86. diff --git a/runtime/runtime_common.h b/runtime/runtime_common.h index 3fba441b55..698d0603ee 100644 --- a/runtime/runtime_common.h +++ b/runtime/runtime_common.h @@ -40,7 +40,9 @@ struct Backtrace { public: explicit Backtrace(void* raw_context) : raw_context_(raw_context) {} void Dump(std::ostream& os) const { - DumpNativeStack(os, GetTid(), nullptr, "\t", nullptr, raw_context_); + // This is a backtrace from a crash, do not skip any frames in case the + // crash is in the unwinder itself. + DumpNativeStack(os, GetTid(), nullptr, "\t", nullptr, raw_context_, false); } private: // Stores the context of the signal that was unexpected and will terminate the runtime. The -- GitLab From 3fbd3ad99fad077e5c760e7238bcd55b07d4c06e Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 26 Mar 2018 21:14:46 +0000 Subject: [PATCH 140/749] Revert^3 "Compiler changes for bitstring based type checks." This reverts commit 3f41323cc9da335e9aa4f3fbad90a86caa82ee4d. Reason for revert: Fails sporadically. Bug: 26687569 Bug: 64692057 Bug: 76420366 Change-Id: I84d1e9e46c58aeecf17591ff71fbac6a1e583909 --- compiler/driver/compiler_driver.cc | 115 +--------- compiler/optimizing/code_generator.h | 2 - compiler/optimizing/code_generator_arm64.cc | 77 +------ compiler/optimizing/code_generator_arm64.h | 2 - .../optimizing/code_generator_arm_vixl.cc | 121 +--------- compiler/optimizing/code_generator_arm_vixl.h | 3 - compiler/optimizing/code_generator_mips.cc | 98 ++------ compiler/optimizing/code_generator_mips.h | 1 - compiler/optimizing/code_generator_mips64.cc | 98 ++------ compiler/optimizing/code_generator_mips64.h | 1 - compiler/optimizing/code_generator_x86.cc | 62 +---- compiler/optimizing/code_generator_x86.h | 1 - compiler/optimizing/code_generator_x86_64.cc | 71 +----- compiler/optimizing/code_generator_x86_64.h | 1 - compiler/optimizing/graph_checker.cc | 92 ++------ compiler/optimizing/graph_checker.h | 6 - compiler/optimizing/graph_visualizer.cc | 45 ++-- compiler/optimizing/instruction_builder.cc | 107 ++++----- compiler/optimizing/instruction_builder.h | 7 - compiler/optimizing/instruction_simplifier.cc | 43 ++-- compiler/optimizing/nodes.cc | 2 - compiler/optimizing/nodes.h | 216 ++++++------------ .../optimizing/optimizing_compiler_stats.h | 1 - .../prepare_for_register_allocation.cc | 14 -- .../prepare_for_register_allocation.h | 2 - .../optimizing/reference_type_propagation.cc | 35 +-- compiler/optimizing/sharpening.cc | 69 ------ compiler/optimizing/sharpening.h | 27 +-- runtime/arch/arm/quick_entrypoints_arm.S | 4 - runtime/arch/arm64/quick_entrypoints_arm64.S | 4 - runtime/arch/mips/quick_entrypoints_mips.S | 7 - .../arch/mips64/quick_entrypoints_mips64.S | 6 - runtime/arch/x86/quick_entrypoints_x86.S | 5 - .../arch/x86_64/quick_entrypoints_x86_64.S | 5 - runtime/base/arena_allocator.cc | 1 - runtime/base/arena_allocator.h | 1 - .../quick/quick_throw_entrypoints.cc | 23 -- runtime/subtype_check.h | 23 +- 38 files changed, 240 insertions(+), 1158 deletions(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 6836f7594b..bd3a145368 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -781,8 +781,7 @@ void CompilerDriver::Resolve(jobject class_loader, // TODO: Collect the relevant string indices in parallel, then allocate them sequentially in a // stable order. -static void ResolveConstStrings(ClassLinker* class_linker, - Handle dex_cache, +static void ResolveConstStrings(Handle dex_cache, const DexFile& dex_file, const DexFile::CodeItem* code_item) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -791,6 +790,7 @@ static void ResolveConstStrings(ClassLinker* class_linker, return; } + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) { switch (inst->Opcode()) { case Instruction::CONST_STRING: @@ -838,105 +838,22 @@ static void ResolveConstStrings(CompilerDriver* driver, dex_file->StringByTypeIdx(class_def.class_idx_)); if (!compilation_enabled) { // Compilation is skipped, do not resolve const-string in code of this class. - // FIXME: Make sure that inlining honors this. b/26687569 + // TODO: Make sure that inlining honors this. continue; } // Direct and virtual methods. + int64_t previous_method_idx = -1; while (it.HasNextMethod()) { - ResolveConstStrings(class_linker, dex_cache, *dex_file, it.GetMethodCodeItem()); - it.Next(); - } - DCHECK(!it.HasNext()); - } - } -} - -// Initialize type check bit strings for check-cast and instance-of in the code. Done to have -// deterministic allocation behavior. Right now this is single-threaded for simplicity. -// TODO: Collect the relevant type indices in parallel, then process them sequentially in a -// stable order. - -static void InitializeTypeCheckBitstrings(CompilerDriver* driver, - ClassLinker* class_linker, - Handle dex_cache, - const DexFile& dex_file, - const DexFile::CodeItem* code_item) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (code_item == nullptr) { - // Abstract or native method. - return; - } - - for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) { - switch (inst->Opcode()) { - case Instruction::CHECK_CAST: - case Instruction::INSTANCE_OF: { - dex::TypeIndex type_index( - (inst->Opcode() == Instruction::CHECK_CAST) ? inst->VRegB_21c() : inst->VRegC_22c()); - const char* descriptor = dex_file.StringByTypeIdx(type_index); - // We currently do not use the bitstring type check for array or final (including - // primitive) classes. We may reconsider this in future if it's deemed to be beneficial. - // And we cannot use it for classes outside the boot image as we do not know the runtime - // value of their bitstring when compiling (it may not even get assigned at runtime). - if (descriptor[0] == 'L' && driver->IsImageClass(descriptor)) { - ObjPtr klass = - class_linker->LookupResolvedType(type_index, - dex_cache.Get(), - /* class_loader */ nullptr); - CHECK(klass != nullptr) << descriptor << " should have been previously resolved."; - // Now assign the bitstring if the class is not final. Keep this in sync with sharpening. - if (!klass->IsFinal()) { - MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); - SubtypeCheck>::EnsureAssigned(klass); - } + uint32_t method_idx = it.GetMemberIndex(); + if (method_idx == previous_method_idx) { + // smali can create dex files with two encoded_methods sharing the same method_idx + // http://code.google.com/p/smali/issues/detail?id=119 + it.Next(); + continue; } - break; - } - - default: - break; - } - } -} - -static void InitializeTypeCheckBitstrings(CompilerDriver* driver, - const std::vector& dex_files, - TimingLogger* timings) { - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<1> hs(soa.Self()); - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - MutableHandle dex_cache(hs.NewHandle(nullptr)); - - for (const DexFile* dex_file : dex_files) { - dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file)); - TimingLogger::ScopedTiming t("Initialize type check bitstrings", timings); - - size_t class_def_count = dex_file->NumClassDefs(); - for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - // empty class, probably a marker interface - continue; - } - - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - - bool compilation_enabled = driver->IsClassToCompile( - dex_file->StringByTypeIdx(class_def.class_idx_)); - if (!compilation_enabled) { - // Compilation is skipped, do not look for type checks in code of this class. - // FIXME: Make sure that inlining honors this. b/26687569 - continue; - } - - // Direct and virtual methods. - while (it.HasNextMethod()) { - InitializeTypeCheckBitstrings( - driver, class_linker, dex_cache, *dex_file, it.GetMethodCodeItem()); + previous_method_idx = method_idx; + ResolveConstStrings(dex_cache, *dex_file, it.GetMethodCodeItem()); it.Next(); } DCHECK(!it.HasNext()); @@ -1038,14 +955,6 @@ void CompilerDriver::PreCompile(jobject class_loader, UpdateImageClasses(timings); VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false); - - if (kBitstringSubtypeCheckEnabled && - GetCompilerOptions().IsForceDeterminism() && GetCompilerOptions().IsBootImage()) { - // Initialize type check bit string used by check-cast and instanceof. - // Do this now to have a deterministic image. - // Note: This is done after UpdateImageClasses() at it relies on the image classes to be final. - InitializeTypeCheckBitstrings(this, dex_files, timings); - } } bool CompilerDriver::IsImageClass(const char* descriptor) const { diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 3bd5e14539..a4873202b2 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -438,8 +438,6 @@ class CodeGenerator : public DeletableArenaObject { case TypeCheckKind::kArrayCheck: case TypeCheckKind::kUnresolvedCheck: return false; - case TypeCheckKind::kBitstringCheck: - return true; } LOG(FATAL) << "Unreachable"; UNREACHABLE(); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 273346ab4a..a024df8537 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -2129,26 +2129,6 @@ void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCod __ Bind(slow_path->GetExitLabel()); } -void InstructionCodeGeneratorARM64::GenerateBitstringTypeCheckCompare( - HTypeCheckInstruction* check, vixl::aarch64::Register temp) { - uint32_t path_to_root = check->GetBitstringPathToRoot(); - uint32_t mask = check->GetBitstringMask(); - DCHECK(IsPowerOfTwo(mask + 1)); - size_t mask_bits = WhichPowerOf2(mask + 1); - - if (mask_bits == 16u) { - // Load only the bitstring part of the status word. - __ Ldrh(temp, HeapOperand(temp, mirror::Class::StatusOffset())); - } else { - // /* uint32_t */ temp = temp->status_ - __ Ldr(temp, HeapOperand(temp, mirror::Class::StatusOffset())); - // Extract the bitstring bits. - __ Ubfx(temp, temp, 0, mask_bits); - } - // Compare the bitstring bits to `path_to_root`. - __ Cmp(temp, path_to_root); -} - void CodeGeneratorARM64::GenerateMemoryBarrier(MemBarrierKind kind) { BarrierType type = BarrierAll; @@ -3886,8 +3866,6 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; - case TypeCheckKind::kBitstringCheck: - break; } LocationSummary* locations = @@ -3896,13 +3874,7 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::RequiresRegister()); - } + locations->SetInAt(1, Location::RequiresRegister()); // The "out" register is used as a temporary, so it overlaps with the inputs. // Note that TypeCheckSlowPathARM64 uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -3915,9 +3887,7 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = InputRegisterAt(instruction, 0); - Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) - ? Register() - : InputRegisterAt(instruction, 1); + Register cls = InputRegisterAt(instruction, 1); Location out_loc = locations->Out(); Register out = OutputRegister(instruction); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -4103,23 +4073,6 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { } break; } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - out_loc, - obj_loc, - class_offset, - maybe_temp_loc, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, out); - __ Cset(out, eq); - if (zero.IsLinked()) { - __ B(&done); - } - break; - } } if (zero.IsLinked()) { @@ -4142,13 +4095,7 @@ void LocationsBuilderARM64::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::RequiresRegister()); - } + locations->SetInAt(1, Location::RequiresRegister()); // Add temps for read barriers and other uses. One is used by TypeCheckSlowPathARM64. locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -4158,9 +4105,7 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = InputRegisterAt(instruction, 0); - Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) - ? Register() - : InputRegisterAt(instruction, 1); + Register cls = InputRegisterAt(instruction, 1); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); DCHECK_GE(num_temps, 1u); DCHECK_LE(num_temps, 3u); @@ -4341,20 +4286,6 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { __ B(ne, &start_loop); break; } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - obj_loc, - class_offset, - maybe_temp2_loc, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, temp); - __ B(ne, type_check_slow_path->GetEntryLabel()); - break; - } } __ Bind(&done); diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 6a52eecbd3..a8a9802f9a 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -264,8 +264,6 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { private: void GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path, vixl::aarch64::Register class_reg); - void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, - vixl::aarch64::Register temp); void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor); void HandleBinaryOp(HBinaryOperation* instr); diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index b38a006305..6ebcc67b49 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -7523,67 +7523,6 @@ void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck( __ Bind(slow_path->GetExitLabel()); } -void InstructionCodeGeneratorARMVIXL::GenerateBitstringTypeCheckCompare( - HTypeCheckInstruction* check, - vixl32::Register temp, - vixl32::FlagsUpdate flags_update) { - uint32_t path_to_root = check->GetBitstringPathToRoot(); - uint32_t mask = check->GetBitstringMask(); - DCHECK(IsPowerOfTwo(mask + 1)); - size_t mask_bits = WhichPowerOf2(mask + 1); - - // Note that HInstanceOf shall check for zero value in `temp` but HCheckCast needs - // the Z flag for BNE. This is indicated by the `flags_update` parameter. - if (mask_bits == 16u) { - // Load only the bitstring part of the status word. - __ Ldrh(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value())); - // Check if the bitstring bits are equal to `path_to_root`. - if (flags_update == SetFlags) { - __ Cmp(temp, path_to_root); - } else { - __ Sub(temp, temp, path_to_root); - } - } else { - // /* uint32_t */ temp = temp->status_ - __ Ldr(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value())); - if (GetAssembler()->ShifterOperandCanHold(SUB, path_to_root)) { - // Compare the bitstring bits using SUB. - __ Sub(temp, temp, path_to_root); - // Shift out bits that do not contribute to the comparison. - __ Lsl(flags_update, temp, temp, dchecked_integral_cast(32u - mask_bits)); - } else if (IsUint<16>(path_to_root)) { - if (temp.IsLow()) { - // Note: Optimized for size but contains one more dependent instruction than necessary. - // MOVW+SUB(register) would be 8 bytes unless we find a low-reg temporary but the - // macro assembler would use the high reg IP for the constant by default. - // Compare the bitstring bits using SUB. - __ Sub(temp, temp, path_to_root & 0x00ffu); // 16-bit SUB (immediate) T2 - __ Sub(temp, temp, path_to_root & 0xff00u); // 32-bit SUB (immediate) T3 - // Shift out bits that do not contribute to the comparison. - __ Lsl(flags_update, temp, temp, dchecked_integral_cast(32u - mask_bits)); - } else { - // Extract the bitstring bits. - __ Ubfx(temp, temp, 0, mask_bits); - // Check if the bitstring bits are equal to `path_to_root`. - if (flags_update == SetFlags) { - __ Cmp(temp, path_to_root); - } else { - __ Sub(temp, temp, path_to_root); - } - } - } else { - // Shift out bits that do not contribute to the comparison. - __ Lsl(temp, temp, dchecked_integral_cast(32u - mask_bits)); - // Check if the shifted bitstring bits are equal to `path_to_root << (32u - mask_bits)`. - if (flags_update == SetFlags) { - __ Cmp(temp, path_to_root << (32u - mask_bits)); - } else { - __ Sub(temp, temp, path_to_root << (32u - mask_bits)); - } - } - } -} - HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { switch (desired_string_load_kind) { @@ -7775,8 +7714,6 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; - case TypeCheckKind::kBitstringCheck: - break; } LocationSummary* locations = @@ -7785,13 +7722,7 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::RequiresRegister()); - } + locations->SetInAt(1, Location::RequiresRegister()); // The "out" register is used as a temporary, so it overlaps with the inputs. // Note that TypeCheckSlowPathARM uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -7806,9 +7737,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); vixl32::Register obj = InputRegisterAt(instruction, 0); - vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) - ? vixl32::Register() - : InputRegisterAt(instruction, 1); + vixl32::Register cls = InputRegisterAt(instruction, 1); Location out_loc = locations->Out(); vixl32::Register out = OutputRegister(instruction); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -8048,26 +7977,6 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) __ B(slow_path->GetEntryLabel()); break; } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - out_loc, - obj_loc, - class_offset, - maybe_temp_loc, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, out, DontCare); - // If `out` is a low reg and we would have another low reg temp, we could - // optimize this as RSBS+ADC, see GenerateConditionWithZero(). - // - // Also, in some cases when `out` is a low reg and we're loading a constant to IP - // it would make sense to use CMP+MOV+IT+MOV instead of SUB+CLZ+LSR as the code size - // would be the same and we would have fewer direct data dependencies. - codegen_->GenerateConditionWithZero(kCondEQ, out, out); // CLZ+LSR - break; - } } if (done.IsReferenced()) { @@ -8085,13 +7994,7 @@ void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::RequiresRegister()); - } + locations->SetInAt(1, Location::RequiresRegister()); locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -8100,9 +8003,7 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); vixl32::Register obj = InputRegisterAt(instruction, 0); - vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) - ? vixl32::Register() - : InputRegisterAt(instruction, 1); + vixl32::Register cls = InputRegisterAt(instruction, 1); Location temp_loc = locations->GetTemp(0); vixl32::Register temp = RegisterFrom(temp_loc); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); @@ -8287,20 +8188,6 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { __ B(ne, &start_loop, /* far_target */ false); break; } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - obj_loc, - class_offset, - maybe_temp2_loc, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, temp, SetFlags); - __ B(ne, type_check_slow_path->GetEntryLabel()); - break; - } } if (done.IsReferenced()) { __ Bind(&done); diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 2114ea1ba1..6a07e36022 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -322,9 +322,6 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor); void GenerateClassInitializationCheck(LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg); - void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, - vixl::aarch32::Register temp, - vixl::aarch32::FlagsUpdate flags_update); void GenerateAndConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value); void GenerateOrrConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value); void GenerateEorConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value); diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 25e2eddbfa..be9ff48a6b 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -1950,34 +1950,6 @@ void InstructionCodeGeneratorMIPS::GenerateClassInitializationCheck(SlowPathCode __ Bind(slow_path->GetExitLabel()); } -void InstructionCodeGeneratorMIPS::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, - Register temp) { - uint32_t path_to_root = check->GetBitstringPathToRoot(); - uint32_t mask = check->GetBitstringMask(); - DCHECK(IsPowerOfTwo(mask + 1)); - size_t mask_bits = WhichPowerOf2(mask + 1); - - if (mask_bits == 16u) { - // Load only the bitstring part of the status word. - __ LoadFromOffset( - kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value()); - // Compare the bitstring bits using XOR. - __ Xori(temp, temp, dchecked_integral_cast(path_to_root)); - } else { - // /* uint32_t */ temp = temp->status_ - __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value()); - // Compare the bitstring bits using XOR. - if (IsUint<16>(path_to_root)) { - __ Xori(temp, temp, dchecked_integral_cast(path_to_root)); - } else { - __ LoadConst32(TMP, path_to_root); - __ Xor(temp, temp, TMP); - } - // Shift out bits that do not contribute to the comparison. - __ Sll(temp, temp, 32 - mask_bits); - } -} - void InstructionCodeGeneratorMIPS::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) { __ Sync(0); // Only stype 0 is supported. } @@ -3329,13 +3301,7 @@ void LocationsBuilderMIPS::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::RequiresRegister()); - } + locations->SetInAt(1, Location::RequiresRegister()); locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -3344,7 +3310,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = obj_loc.AsRegister(); - Location cls = locations->InAt(1); + Register cls = locations->InAt(1).AsRegister(); Location temp_loc = locations->GetTemp(0); Register temp = temp_loc.AsRegister(); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); @@ -3383,7 +3349,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { kWithoutReadBarrier); // Jump to slow path for throwing the exception or doing a // more involved array check. - __ Bne(temp, cls.AsRegister(), slow_path->GetEntryLabel()); + __ Bne(temp, cls, slow_path->GetEntryLabel()); break; } @@ -3409,7 +3375,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { // exception. __ Beqz(temp, slow_path->GetEntryLabel()); // Otherwise, compare the classes. - __ Bne(temp, cls.AsRegister(), &loop); + __ Bne(temp, cls, &loop); break; } @@ -3424,7 +3390,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { // Walk over the class hierarchy to find a match. MipsLabel loop; __ Bind(&loop); - __ Beq(temp, cls.AsRegister(), &done); + __ Beq(temp, cls, &done); // /* HeapReference */ temp = temp->super_class_ GenerateReferenceLoadOneRegister(instruction, temp_loc, @@ -3447,7 +3413,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { maybe_temp2_loc, kWithoutReadBarrier); // Do an exact check. - __ Beq(temp, cls.AsRegister(), &done); + __ Beq(temp, cls, &done); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference */ temp = temp->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -3506,21 +3472,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { // Go to next interface. __ Addiu(TMP, TMP, -2); // Compare the classes and continue the loop if they do not match. - __ Bne(AT, cls.AsRegister(), &loop); - break; - } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - obj_loc, - class_offset, - maybe_temp2_loc, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, temp); - __ Bnez(temp, slow_path->GetEntryLabel()); + __ Bne(AT, cls, &loop); break; } } @@ -7463,8 +7415,6 @@ void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; - case TypeCheckKind::kBitstringCheck: - break; } LocationSummary* locations = @@ -7473,13 +7423,7 @@ void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::RequiresRegister()); - } + locations->SetInAt(1, Location::RequiresRegister()); // The output does overlap inputs. // Note that TypeCheckSlowPathMIPS uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -7491,7 +7435,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = obj_loc.AsRegister(); - Location cls = locations->InAt(1); + Register cls = locations->InAt(1).AsRegister(); Location out_loc = locations->Out(); Register out = out_loc.AsRegister(); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -7523,7 +7467,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { maybe_temp_loc, read_barrier_option); // Classes must be equal for the instanceof to succeed. - __ Xor(out, out, cls.AsRegister()); + __ Xor(out, out, cls); __ Sltiu(out, out, 1); break; } @@ -7550,7 +7494,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { read_barrier_option); // If `out` is null, we use it for the result, and jump to `done`. __ Beqz(out, &done); - __ Bne(out, cls.AsRegister(), &loop); + __ Bne(out, cls, &loop); __ LoadConst32(out, 1); break; } @@ -7568,7 +7512,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { // Walk over the class hierarchy to find a match. MipsLabel loop, success; __ Bind(&loop); - __ Beq(out, cls.AsRegister(), &success); + __ Beq(out, cls, &success); // /* HeapReference */ out = out->super_class_ GenerateReferenceLoadOneRegister(instruction, out_loc, @@ -7595,7 +7539,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { read_barrier_option); // Do an exact check. MipsLabel success; - __ Beq(out, cls.AsRegister(), &success); + __ Beq(out, cls, &success); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference */ out = out->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -7627,7 +7571,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS( instruction, /* is_fatal */ false); codegen_->AddSlowPath(slow_path); - __ Bne(out, cls.AsRegister(), slow_path->GetEntryLabel()); + __ Bne(out, cls, slow_path->GetEntryLabel()); __ LoadConst32(out, 1); break; } @@ -7659,20 +7603,6 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { __ B(slow_path->GetEntryLabel()); break; } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - out_loc, - obj_loc, - class_offset, - maybe_temp_loc, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, out); - __ Sltiu(out, out, 1); - break; - } } __ Bind(&done); diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 2e7c736dbd..1f1743ff9e 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -237,7 +237,6 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator { private: void GenerateClassInitializationCheck(SlowPathCodeMIPS* slow_path, Register class_reg); void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); - void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, Register temp); void HandleBinaryOp(HBinaryOperation* operation); void HandleCondition(HCondition* instruction); void HandleShift(HBinaryOperation* operation); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 5b07b55cbb..f8851b4eea 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1794,34 +1794,6 @@ void InstructionCodeGeneratorMIPS64::GenerateClassInitializationCheck(SlowPathCo __ Bind(slow_path->GetExitLabel()); } -void InstructionCodeGeneratorMIPS64::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, - GpuRegister temp) { - uint32_t path_to_root = check->GetBitstringPathToRoot(); - uint32_t mask = check->GetBitstringMask(); - DCHECK(IsPowerOfTwo(mask + 1)); - size_t mask_bits = WhichPowerOf2(mask + 1); - - if (mask_bits == 16u) { - // Load only the bitstring part of the status word. - __ LoadFromOffset( - kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value()); - // Compare the bitstring bits using XOR. - __ Xori(temp, temp, dchecked_integral_cast(path_to_root)); - } else { - // /* uint32_t */ temp = temp->status_ - __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value()); - // Compare the bitstring bits using XOR. - if (IsUint<16>(path_to_root)) { - __ Xori(temp, temp, dchecked_integral_cast(path_to_root)); - } else { - __ LoadConst32(TMP, path_to_root); - __ Xor(temp, temp, TMP); - } - // Shift out bits that do not contribute to the comparison. - __ Sll(temp, temp, 32 - mask_bits); - } -} - void InstructionCodeGeneratorMIPS64::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) { __ Sync(0); // only stype 0 is supported } @@ -2882,13 +2854,7 @@ void LocationsBuilderMIPS64::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::RequiresRegister()); - } + locations->SetInAt(1, Location::RequiresRegister()); locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -2897,7 +2863,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); GpuRegister obj = obj_loc.AsRegister(); - Location cls = locations->InAt(1); + GpuRegister cls = locations->InAt(1).AsRegister(); Location temp_loc = locations->GetTemp(0); GpuRegister temp = temp_loc.AsRegister(); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); @@ -2936,7 +2902,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { kWithoutReadBarrier); // Jump to slow path for throwing the exception or doing a // more involved array check. - __ Bnec(temp, cls.AsRegister(), slow_path->GetEntryLabel()); + __ Bnec(temp, cls, slow_path->GetEntryLabel()); break; } @@ -2962,7 +2928,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { // exception. __ Beqzc(temp, slow_path->GetEntryLabel()); // Otherwise, compare the classes. - __ Bnec(temp, cls.AsRegister(), &loop); + __ Bnec(temp, cls, &loop); break; } @@ -2977,7 +2943,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { // Walk over the class hierarchy to find a match. Mips64Label loop; __ Bind(&loop); - __ Beqc(temp, cls.AsRegister(), &done); + __ Beqc(temp, cls, &done); // /* HeapReference */ temp = temp->super_class_ GenerateReferenceLoadOneRegister(instruction, temp_loc, @@ -3000,7 +2966,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { maybe_temp2_loc, kWithoutReadBarrier); // Do an exact check. - __ Beqc(temp, cls.AsRegister(), &done); + __ Beqc(temp, cls, &done); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference */ temp = temp->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -3059,21 +3025,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { __ Daddiu(temp, temp, 2 * kHeapReferenceSize); __ Addiu(TMP, TMP, -2); // Compare the classes and continue the loop if they do not match. - __ Bnec(AT, cls.AsRegister(), &loop); - break; - } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - obj_loc, - class_offset, - maybe_temp2_loc, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, temp); - __ Bnezc(temp, slow_path->GetEntryLabel()); + __ Bnec(AT, cls, &loop); break; } } @@ -5577,8 +5529,6 @@ void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; - case TypeCheckKind::kBitstringCheck: - break; } LocationSummary* locations = @@ -5587,13 +5537,7 @@ void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::RequiresRegister()); - } + locations->SetInAt(1, Location::RequiresRegister()); // The output does overlap inputs. // Note that TypeCheckSlowPathMIPS64 uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -5605,7 +5549,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); GpuRegister obj = obj_loc.AsRegister(); - Location cls = locations->InAt(1); + GpuRegister cls = locations->InAt(1).AsRegister(); Location out_loc = locations->Out(); GpuRegister out = out_loc.AsRegister(); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -5637,7 +5581,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { maybe_temp_loc, read_barrier_option); // Classes must be equal for the instanceof to succeed. - __ Xor(out, out, cls.AsRegister()); + __ Xor(out, out, cls); __ Sltiu(out, out, 1); break; } @@ -5664,7 +5608,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { read_barrier_option); // If `out` is null, we use it for the result, and jump to `done`. __ Beqzc(out, &done); - __ Bnec(out, cls.AsRegister(), &loop); + __ Bnec(out, cls, &loop); __ LoadConst32(out, 1); break; } @@ -5682,7 +5626,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { // Walk over the class hierarchy to find a match. Mips64Label loop, success; __ Bind(&loop); - __ Beqc(out, cls.AsRegister(), &success); + __ Beqc(out, cls, &success); // /* HeapReference */ out = out->super_class_ GenerateReferenceLoadOneRegister(instruction, out_loc, @@ -5709,7 +5653,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { read_barrier_option); // Do an exact check. Mips64Label success; - __ Beqc(out, cls.AsRegister(), &success); + __ Beqc(out, cls, &success); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference */ out = out->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -5741,7 +5685,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS64( instruction, /* is_fatal */ false); codegen_->AddSlowPath(slow_path); - __ Bnec(out, cls.AsRegister(), slow_path->GetEntryLabel()); + __ Bnec(out, cls, slow_path->GetEntryLabel()); __ LoadConst32(out, 1); break; } @@ -5773,20 +5717,6 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { __ Bc(slow_path->GetEntryLabel()); break; } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - out_loc, - obj_loc, - class_offset, - maybe_temp_loc, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, out); - __ Sltiu(out, out, 1); - break; - } } __ Bind(&done); diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 6e69e4611a..74c947e5d5 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -233,7 +233,6 @@ class InstructionCodeGeneratorMIPS64 : public InstructionCodeGenerator { private: void GenerateClassInitializationCheck(SlowPathCodeMIPS64* slow_path, GpuRegister class_reg); - void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, GpuRegister temp); void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); void HandleBinaryOp(HBinaryOperation* operation); void HandleCondition(HCondition* instruction); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 4053f557d9..4818084a72 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -6571,26 +6571,6 @@ void InstructionCodeGeneratorX86::GenerateClassInitializationCheck( // No need for memory fence, thanks to the X86 memory model. } -void InstructionCodeGeneratorX86::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, - Register temp) { - uint32_t path_to_root = check->GetBitstringPathToRoot(); - uint32_t mask = check->GetBitstringMask(); - DCHECK(IsPowerOfTwo(mask + 1)); - size_t mask_bits = WhichPowerOf2(mask + 1); - - if (mask_bits == 16u) { - // Compare the bitstring in memory. - __ cmpw(Address(temp, mirror::Class::StatusOffset()), Immediate(path_to_root)); - } else { - // /* uint32_t */ temp = temp->status_ - __ movl(temp, Address(temp, mirror::Class::StatusOffset())); - // Compare the bitstring bits using SUB. - __ subl(temp, Immediate(path_to_root)); - // Shift out bits that do not contribute to the comparison. - __ shll(temp, Immediate(32u - mask_bits)); - } -} - HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { switch (desired_string_load_kind) { @@ -6784,8 +6764,6 @@ void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; - case TypeCheckKind::kBitstringCheck: - break; } LocationSummary* locations = @@ -6794,13 +6772,7 @@ void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::Any()); - } + locations->SetInAt(1, Location::Any()); // Note that TypeCheckSlowPathX86 uses this "out" register too. locations->SetOut(Location::RequiresRegister()); // When read barriers are enabled, we need a temporary register for some cases. @@ -7021,21 +6993,6 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { } break; } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - out_loc, - obj_loc, - class_offset, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, out); - __ j(kNotEqual, &zero); - __ movl(out, Immediate(1)); - __ jmp(&done); - break; - } } if (zero.IsLinked()) { @@ -7062,10 +7019,6 @@ void LocationsBuilderX86::VisitCheckCast(HCheckCast* instruction) { // Require a register for the interface check since there is a loop that compares the class to // a memory address. locations->SetInAt(1, Location::RequiresRegister()); - } else if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); } else { locations->SetInAt(1, Location::Any()); } @@ -7285,19 +7238,6 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { __ MaybeUnpoisonHeapReference(cls.AsRegister()); break; } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - obj_loc, - class_offset, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, temp); - __ j(kNotEqual, type_check_slow_path->GetEntryLabel()); - break; - } } __ Bind(&done); diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 6c76e27d35..9c537a7371 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -211,7 +211,6 @@ class InstructionCodeGeneratorX86 : public InstructionCodeGenerator { // the suspend call. void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); void GenerateClassInitializationCheck(SlowPathCode* slow_path, Register class_reg); - void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, Register temp); void HandleBitwiseOperation(HBinaryOperation* instruction); void GenerateDivRemIntegral(HBinaryOperation* instruction); void DivRemOneOrMinusOne(HBinaryOperation* instruction); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 496d79d6c8..c378c5b957 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -5716,26 +5716,6 @@ void InstructionCodeGeneratorX86_64::GenerateClassInitializationCheck( // No need for memory fence, thanks to the x86-64 memory model. } -void InstructionCodeGeneratorX86_64::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, - CpuRegister temp) { - uint32_t path_to_root = check->GetBitstringPathToRoot(); - uint32_t mask = check->GetBitstringMask(); - DCHECK(IsPowerOfTwo(mask + 1)); - size_t mask_bits = WhichPowerOf2(mask + 1); - - if (mask_bits == 16u) { - // Compare the bitstring in memory. - __ cmpw(Address(temp, mirror::Class::StatusOffset()), Immediate(path_to_root)); - } else { - // /* uint32_t */ temp = temp->status_ - __ movl(temp, Address(temp, mirror::Class::StatusOffset())); - // Compare the bitstring bits using SUB. - __ subl(temp, Immediate(path_to_root)); - // Shift out bits that do not contribute to the comparison. - __ shll(temp, Immediate(32u - mask_bits)); - } -} - HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind( HLoadClass::LoadKind desired_class_load_kind) { switch (desired_class_load_kind) { @@ -6102,8 +6082,6 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; - case TypeCheckKind::kBitstringCheck: - break; } LocationSummary* locations = @@ -6112,13 +6090,7 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); - } else { - locations->SetInAt(1, Location::Any()); - } + locations->SetInAt(1, Location::Any()); // Note that TypeCheckSlowPathX86_64 uses this "out" register too. locations->SetOut(Location::RequiresRegister()); // When read barriers are enabled, we need a temporary register for @@ -6347,27 +6319,6 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } break; } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - out_loc, - obj_loc, - class_offset, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, out); - if (zero.IsLinked()) { - __ j(kNotEqual, &zero); - __ movl(out, Immediate(1)); - __ jmp(&done); - } else { - __ setcc(kEqual, out); - // setcc only sets the low byte. - __ andl(out, Immediate(1)); - } - break; - } } if (zero.IsLinked()) { @@ -6394,10 +6345,6 @@ void LocationsBuilderX86_64::VisitCheckCast(HCheckCast* instruction) { // Require a register for the interface check since there is a loop that compares the class to // a memory address. locations->SetInAt(1, Location::RequiresRegister()); - } else if (type_check_kind == TypeCheckKind::kBitstringCheck) { - locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); - locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); - locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); } else { locations->SetInAt(1, Location::Any()); } @@ -6584,7 +6531,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { break; } - case TypeCheckKind::kInterfaceCheck: { + case TypeCheckKind::kInterfaceCheck: // Fast path for the interface check. Try to avoid read barriers to improve the fast path. // We can not get false positives by doing this. // /* HeapReference */ temp = obj->klass_ @@ -6620,20 +6567,6 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { // If `cls` was poisoned above, unpoison it. __ MaybeUnpoisonHeapReference(cls.AsRegister()); break; - } - - case TypeCheckKind::kBitstringCheck: { - // /* HeapReference */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - obj_loc, - class_offset, - kWithoutReadBarrier); - - GenerateBitstringTypeCheckCompare(instruction, temp); - __ j(kNotEqual, type_check_slow_path->GetEntryLabel()); - break; - } } if (done.IsLinked()) { diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 9a4c53b524..e8d1efe702 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -208,7 +208,6 @@ class InstructionCodeGeneratorX86_64 : public InstructionCodeGenerator { // the suspend call. void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor); void GenerateClassInitializationCheck(SlowPathCode* slow_path, CpuRegister class_reg); - void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, CpuRegister temp); void HandleBitwiseOperation(HBinaryOperation* operation); void GenerateRemFP(HRem* rem); void DivRemOneOrMinusOne(HBinaryOperation* instruction); diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index fbcbe3608e..c88baa8610 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -25,11 +25,6 @@ #include "base/bit_vector-inl.h" #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" -#include "handle.h" -#include "mirror/class.h" -#include "obj_ptr-inl.h" -#include "scoped_thread_state_change-inl.h" -#include "subtype_check.h" namespace art { @@ -553,83 +548,28 @@ void GraphChecker::VisitReturnVoid(HReturnVoid* ret) { } } -void GraphChecker::CheckTypeCheckBitstringInput(HTypeCheckInstruction* check, - size_t input_pos, - bool check_value, - uint32_t expected_value, - const char* name) { - if (!check->InputAt(input_pos)->IsIntConstant()) { - AddError(StringPrintf("%s:%d (bitstring) expects a HIntConstant input %zu (%s), not %s:%d.", - check->DebugName(), - check->GetId(), - input_pos, - name, - check->InputAt(2)->DebugName(), - check->InputAt(2)->GetId())); - } else if (check_value) { - uint32_t actual_value = - static_cast(check->InputAt(input_pos)->AsIntConstant()->GetValue()); - if (actual_value != expected_value) { - AddError(StringPrintf("%s:%d (bitstring) has %s 0x%x, not 0x%x as expected.", - check->DebugName(), - check->GetId(), - name, - actual_value, - expected_value)); - } - } -} - -void GraphChecker::HandleTypeCheckInstruction(HTypeCheckInstruction* check) { +void GraphChecker::VisitCheckCast(HCheckCast* check) { VisitInstruction(check); HInstruction* input = check->InputAt(1); - if (check->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { - if (!input->IsNullConstant()) { - AddError(StringPrintf("%s:%d (bitstring) expects a HNullConstant as second input, not %s:%d.", - check->DebugName(), - check->GetId(), - input->DebugName(), - input->GetId())); - } - bool check_values = false; - BitString::StorageType expected_path_to_root = 0u; - BitString::StorageType expected_mask = 0u; - { - ScopedObjectAccess soa(Thread::Current()); - ObjPtr klass = check->GetClass().Get(); - MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); - SubtypeCheckInfo::State state = SubtypeCheck>::GetState(klass); - if (state == SubtypeCheckInfo::kAssigned) { - expected_path_to_root = - SubtypeCheck>::GetEncodedPathToRootForTarget(klass); - expected_mask = SubtypeCheck>::GetEncodedPathToRootMask(klass); - check_values = true; - } else { - AddError(StringPrintf("%s:%d (bitstring) references a class with unassigned bitstring.", - check->DebugName(), - check->GetId())); - } - } - CheckTypeCheckBitstringInput( - check, /* input_pos */ 2, check_values, expected_path_to_root, "path_to_root"); - CheckTypeCheckBitstringInput(check, /* input_pos */ 3, check_values, expected_mask, "mask"); - } else { - if (!input->IsLoadClass()) { - AddError(StringPrintf("%s:%d (classic) expects a HLoadClass as second input, not %s:%d.", - check->DebugName(), - check->GetId(), - input->DebugName(), - input->GetId())); - } + if (!input->IsLoadClass()) { + AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.", + check->DebugName(), + check->GetId(), + input->DebugName(), + input->GetId())); } } -void GraphChecker::VisitCheckCast(HCheckCast* check) { - HandleTypeCheckInstruction(check); -} - void GraphChecker::VisitInstanceOf(HInstanceOf* instruction) { - HandleTypeCheckInstruction(instruction); + VisitInstruction(instruction); + HInstruction* input = instruction->InputAt(1); + if (!input->IsLoadClass()) { + AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.", + instruction->DebugName(), + instruction->GetId(), + input->DebugName(), + input->GetId())); + } } void GraphChecker::HandleLoop(HBasicBlock* loop_header) { diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h index dbedc40518..0f0b49d240 100644 --- a/compiler/optimizing/graph_checker.h +++ b/compiler/optimizing/graph_checker.h @@ -71,12 +71,6 @@ class GraphChecker : public HGraphDelegateVisitor { void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE; void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE; - void CheckTypeCheckBitstringInput(HTypeCheckInstruction* check, - size_t input_pos, - bool check_value, - uint32_t expected_value, - const char* name); - void HandleTypeCheckInstruction(HTypeCheckInstruction* instruction); void HandleLoop(HBasicBlock* loop_header); void HandleBooleanInput(HInstruction* instruction, size_t input_index); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 6cb1881d7d..5ff31cead5 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -390,23 +390,16 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { StartAttributeStream("load_kind") << load_string->GetLoadKind(); } - void HandleTypeCheckInstruction(HTypeCheckInstruction* check) { - StartAttributeStream("check_kind") << check->GetTypeCheckKind(); - StartAttributeStream("must_do_null_check") << std::boolalpha - << check->MustDoNullCheck() << std::noboolalpha; - if (check->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { - StartAttributeStream("path_to_root") << std::hex - << "0x" << check->GetBitstringPathToRoot() << std::dec; - StartAttributeStream("mask") << std::hex << "0x" << check->GetBitstringMask() << std::dec; - } - } - void VisitCheckCast(HCheckCast* check_cast) OVERRIDE { - HandleTypeCheckInstruction(check_cast); + StartAttributeStream("check_kind") << check_cast->GetTypeCheckKind(); + StartAttributeStream("must_do_null_check") << std::boolalpha + << check_cast->MustDoNullCheck() << std::noboolalpha; } void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE { - HandleTypeCheckInstruction(instance_of); + StartAttributeStream("check_kind") << instance_of->GetTypeCheckKind(); + StartAttributeStream("must_do_null_check") << std::boolalpha + << instance_of->MustDoNullCheck() << std::noboolalpha; } void VisitArrayLength(HArrayLength* array_length) OVERRIDE { @@ -648,32 +641,20 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { << std::boolalpha << loop_info->IsIrreducible() << std::noboolalpha; } - // For the builder and the inliner, we want to add extra information on HInstructions - // that have reference types, and also HInstanceOf/HCheckcast. if ((IsPass(HGraphBuilder::kBuilderPassName) || IsPass(HInliner::kInlinerPassName)) - && (instruction->GetType() == DataType::Type::kReference || - instruction->IsInstanceOf() || - instruction->IsCheckCast())) { - ReferenceTypeInfo info = (instruction->GetType() == DataType::Type::kReference) - ? instruction->IsLoadClass() - ? instruction->AsLoadClass()->GetLoadedClassRTI() - : instruction->GetReferenceTypeInfo() - : instruction->IsInstanceOf() - ? instruction->AsInstanceOf()->GetTargetClassRTI() - : instruction->AsCheckCast()->GetTargetClassRTI(); + && (instruction->GetType() == DataType::Type::kReference)) { + ReferenceTypeInfo info = instruction->IsLoadClass() + ? instruction->AsLoadClass()->GetLoadedClassRTI() + : instruction->GetReferenceTypeInfo(); ScopedObjectAccess soa(Thread::Current()); if (info.IsValid()) { StartAttributeStream("klass") << mirror::Class::PrettyDescriptor(info.GetTypeHandle().Get()); - if (instruction->GetType() == DataType::Type::kReference) { - StartAttributeStream("can_be_null") - << std::boolalpha << instruction->CanBeNull() << std::noboolalpha; - } + StartAttributeStream("can_be_null") + << std::boolalpha << instruction->CanBeNull() << std::noboolalpha; StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha; - } else if (instruction->IsLoadClass() || - instruction->IsInstanceOf() || - instruction->IsCheckCast()) { + } else if (instruction->IsLoadClass()) { StartAttributeStream("klass") << "unresolved"; } else { // The NullConstant may be added to the graph during other passes that happen between diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 9647dd5d41..c7aef3779d 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1815,6 +1815,29 @@ void HInstructionBuilder::BuildFillWideArrayData(HInstruction* object, } } +static TypeCheckKind ComputeTypeCheckKind(Handle cls) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (cls == nullptr) { + return TypeCheckKind::kUnresolvedCheck; + } else if (cls->IsInterface()) { + return TypeCheckKind::kInterfaceCheck; + } else if (cls->IsArrayClass()) { + if (cls->GetComponentType()->IsObjectClass()) { + return TypeCheckKind::kArrayObjectCheck; + } else if (cls->CannotBeAssignedFromOtherTypes()) { + return TypeCheckKind::kExactCheck; + } else { + return TypeCheckKind::kArrayCheck; + } + } else if (cls->IsFinal()) { + return TypeCheckKind::kExactCheck; + } else if (cls->IsAbstract()) { + return TypeCheckKind::kAbstractClassCheck; + } else { + return TypeCheckKind::kClassHierarchyCheck; + } +} + void HInstructionBuilder::BuildLoadString(dex::StringIndex string_index, uint32_t dex_pc) { HLoadString* load_string = new (allocator_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc); @@ -1829,8 +1852,22 @@ void HInstructionBuilder::BuildLoadString(dex::StringIndex string_index, uint32_ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc) { ScopedObjectAccess soa(Thread::Current()); const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); - Handle klass = ResolveClass(soa, type_index); - bool needs_access_check = LoadClassNeedsAccessCheck(klass); + Handle class_loader = dex_compilation_unit_->GetClassLoader(); + Handle klass = handles_->NewHandle(compiler_driver_->ResolveClass( + soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_)); + + bool needs_access_check = true; + if (klass != nullptr) { + if (klass->IsPublic()) { + needs_access_check = false; + } else { + ObjPtr compiling_class = GetCompilingClass(); + if (compiling_class != nullptr && compiling_class->CanAccess(klass.Get())) { + needs_access_check = false; + } + } + } + return BuildLoadClass(type_index, dex_file, klass, dex_pc, needs_access_check); } @@ -1875,83 +1912,25 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, return load_class; } -Handle HInstructionBuilder::ResolveClass(ScopedObjectAccess& soa, - dex::TypeIndex type_index) { - Handle class_loader = dex_compilation_unit_->GetClassLoader(); - ObjPtr klass = compiler_driver_->ResolveClass( - soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_); - // TODO: Avoid creating excessive handles if the method references the same class repeatedly. - // (Use a map on the local_allocator_.) - return handles_->NewHandle(klass); -} - -bool HInstructionBuilder::LoadClassNeedsAccessCheck(Handle klass) { - if (klass == nullptr) { - return true; - } else if (klass->IsPublic()) { - return false; - } else { - ObjPtr compiling_class = GetCompilingClass(); - return compiling_class == nullptr || !compiling_class->CanAccess(klass.Get()); - } -} - void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, uint8_t destination, uint8_t reference, dex::TypeIndex type_index, uint32_t dex_pc) { HInstruction* object = LoadLocal(reference, DataType::Type::kReference); + HLoadClass* cls = BuildLoadClass(type_index, dex_pc); ScopedObjectAccess soa(Thread::Current()); - const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); - Handle klass = ResolveClass(soa, type_index); - bool needs_access_check = LoadClassNeedsAccessCheck(klass); - TypeCheckKind check_kind = HSharpening::ComputeTypeCheckKind( - klass.Get(), code_generator_, compiler_driver_, needs_access_check); - - HInstruction* class_or_null = nullptr; - HIntConstant* bitstring_path_to_root = nullptr; - HIntConstant* bitstring_mask = nullptr; - if (check_kind == TypeCheckKind::kBitstringCheck) { - // TODO: Allow using the bitstring check also if we need an access check. - DCHECK(!needs_access_check); - class_or_null = graph_->GetNullConstant(dex_pc); - MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); - uint32_t path_to_root = - SubtypeCheck>::GetEncodedPathToRootForTarget(klass.Get()); - uint32_t mask = SubtypeCheck>::GetEncodedPathToRootMask(klass.Get()); - bitstring_path_to_root = graph_->GetIntConstant(static_cast(path_to_root), dex_pc); - bitstring_mask = graph_->GetIntConstant(static_cast(mask), dex_pc); - } else { - class_or_null = BuildLoadClass(type_index, dex_file, klass, dex_pc, needs_access_check); - } - DCHECK(class_or_null != nullptr); - + TypeCheckKind check_kind = ComputeTypeCheckKind(cls->GetClass()); if (instruction.Opcode() == Instruction::INSTANCE_OF) { - AppendInstruction(new (allocator_) HInstanceOf(object, - class_or_null, - check_kind, - klass, - dex_pc, - allocator_, - bitstring_path_to_root, - bitstring_mask)); + AppendInstruction(new (allocator_) HInstanceOf(object, cls, check_kind, dex_pc)); UpdateLocal(destination, current_block_->GetLastInstruction()); } else { DCHECK_EQ(instruction.Opcode(), Instruction::CHECK_CAST); // We emit a CheckCast followed by a BoundType. CheckCast is a statement // which may throw. If it succeeds BoundType sets the new type of `object` // for all subsequent uses. - AppendInstruction( - new (allocator_) HCheckCast(object, - class_or_null, - check_kind, - klass, - dex_pc, - allocator_, - bitstring_path_to_root, - bitstring_mask)); + AppendInstruction(new (allocator_) HCheckCast(object, cls, check_kind, dex_pc)); AppendInstruction(new (allocator_) HBoundType(object, dex_pc)); UpdateLocal(reference, current_block_->GetLastInstruction()); } diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index f78829232d..4428c53277 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -39,7 +39,6 @@ class DexCompilationUnit; class HBasicBlockBuilder; class Instruction; class OptimizingCompilerStats; -class ScopedObjectAccess; class SsaBuilder; class VariableSizedHandleScope; @@ -233,12 +232,6 @@ class HInstructionBuilder : public ValueObject { bool needs_access_check) REQUIRES_SHARED(Locks::mutator_lock_); - Handle ResolveClass(ScopedObjectAccess& soa, dex::TypeIndex type_index) - REQUIRES_SHARED(Locks::mutator_lock_); - - bool LoadClassNeedsAccessCheck(Handle klass) - REQUIRES_SHARED(Locks::mutator_lock_); - // Returns the outer-most compiling method's class. ObjPtr GetOutermostCompilingClass() const; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index fa1d96b89e..2b6f90540f 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -579,9 +579,7 @@ bool InstructionSimplifierVisitor::CanEnsureNotNullAt(HInstruction* input, HInst // Returns whether doing a type test between the class of `object` against `klass` has // a statically known outcome. The result of the test is stored in `outcome`. -static bool TypeCheckHasKnownOutcome(ReferenceTypeInfo class_rti, - HInstruction* object, - /*out*/bool* outcome) { +static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bool* outcome) { DCHECK(!object->IsNullConstant()) << "Null constants should be special cased"; ReferenceTypeInfo obj_rti = object->GetReferenceTypeInfo(); ScopedObjectAccess soa(Thread::Current()); @@ -591,6 +589,7 @@ static bool TypeCheckHasKnownOutcome(ReferenceTypeInfo class_rti, return false; } + ReferenceTypeInfo class_rti = klass->GetLoadedClassRTI(); if (!class_rti.IsValid()) { // Happens when the loaded class is unresolved. return false; @@ -615,8 +614,8 @@ static bool TypeCheckHasKnownOutcome(ReferenceTypeInfo class_rti, void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { HInstruction* object = check_cast->InputAt(0); - if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck && - check_cast->GetTargetClass()->NeedsAccessCheck()) { + HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass(); + if (load_class->NeedsAccessCheck()) { // If we need to perform an access check we cannot remove the instruction. return; } @@ -634,18 +633,15 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { // Note: The `outcome` is initialized to please valgrind - the compiler can reorder // the return value check with the `outcome` check, b/27651442 . bool outcome = false; - if (TypeCheckHasKnownOutcome(check_cast->GetTargetClassRTI(), object, &outcome)) { + if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) { if (outcome) { check_cast->GetBlock()->RemoveInstruction(check_cast); MaybeRecordStat(stats_, MethodCompilationStat::kRemovedCheckedCast); - if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) { - HLoadClass* load_class = check_cast->GetTargetClass(); - if (!load_class->HasUses()) { - // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. - // However, here we know that it cannot because the checkcast was successfull, hence - // the class was already loaded. - load_class->GetBlock()->RemoveInstruction(load_class); - } + if (!load_class->HasUses()) { + // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. + // However, here we know that it cannot because the checkcast was successfull, hence + // the class was already loaded. + load_class->GetBlock()->RemoveInstruction(load_class); } } else { // Don't do anything for exceptional cases for now. Ideally we should remove @@ -656,8 +652,8 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { HInstruction* object = instruction->InputAt(0); - if (instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck && - instruction->GetTargetClass()->NeedsAccessCheck()) { + HLoadClass* load_class = instruction->InputAt(1)->AsLoadClass(); + if (load_class->NeedsAccessCheck()) { // If we need to perform an access check we cannot remove the instruction. return; } @@ -680,7 +676,7 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { // Note: The `outcome` is initialized to please valgrind - the compiler can reorder // the return value check with the `outcome` check, b/27651442 . bool outcome = false; - if (TypeCheckHasKnownOutcome(instruction->GetTargetClassRTI(), object, &outcome)) { + if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) { MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf); if (outcome && can_be_null) { // Type test will succeed, we just need a null test. @@ -693,14 +689,11 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { } RecordSimplification(); instruction->GetBlock()->RemoveInstruction(instruction); - if (outcome && instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) { - HLoadClass* load_class = instruction->GetTargetClass(); - if (!load_class->HasUses()) { - // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. - // However, here we know that it cannot because the instanceof check was successfull, hence - // the class was already loaded. - load_class->GetBlock()->RemoveInstruction(load_class); - } + if (outcome && !load_class->HasUses()) { + // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. + // However, here we know that it cannot because the instanceof check was successfull, hence + // the class was already loaded. + load_class->GetBlock()->RemoveInstruction(load_class); } } } diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index f784f8f7f3..d3212cbbc0 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -3103,8 +3103,6 @@ std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs) { return os << "array_object_check"; case TypeCheckKind::kArrayCheck: return os << "array_check"; - case TypeCheckKind::kBitstringCheck: - return os << "bitstring_check"; default: LOG(FATAL) << "Unknown TypeCheckKind: " << static_cast(rhs); UNREACHABLE(); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 79d733060b..a8fcea2097 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -6178,7 +6178,8 @@ class HLoadClass FINAL : public HInstruction { special_input_(HUserRecord(current_method)), type_index_(type_index), dex_file_(dex_file), - klass_(klass) { + klass_(klass), + loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) { // Referrers class should not need access check. We never inline unverified // methods so we can't possibly end up in this situation. DCHECK(!is_referrers_class || !needs_access_check); @@ -6188,7 +6189,6 @@ class HLoadClass FINAL : public HInstruction { SetPackedFlag(needs_access_check); SetPackedFlag(false); SetPackedFlag(false); - SetPackedFlag(false); } bool IsClonable() const OVERRIDE { return true; } @@ -6243,18 +6243,13 @@ class HLoadClass FINAL : public HInstruction { } ReferenceTypeInfo GetLoadedClassRTI() { - if (GetPackedFlag()) { - // Note: The is_exact flag from the return value should not be used. - return ReferenceTypeInfo::CreateUnchecked(klass_, /* is_exact */ true); - } else { - return ReferenceTypeInfo::CreateInvalid(); - } + return loaded_class_rti_; } - // Loaded class RTI is marked as valid by RTP if the klass_ is admissible. - void SetValidLoadedClassRTI() REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(klass_ != nullptr); - SetPackedFlag(true); + void SetLoadedClassRTI(ReferenceTypeInfo rti) { + // Make sure we only set exact types (the loaded class should never be merged). + DCHECK(rti.IsExact()); + loaded_class_rti_ = rti; } dex::TypeIndex GetTypeIndex() const { return type_index_; } @@ -6307,8 +6302,7 @@ class HLoadClass FINAL : public HInstruction { static constexpr size_t kFieldLoadKind = kFlagGenerateClInitCheck + 1; static constexpr size_t kFieldLoadKindSize = MinimumBitsToStore(static_cast(LoadKind::kLast)); - static constexpr size_t kFlagValidLoadedClassRTI = kFieldLoadKind + kFieldLoadKindSize; - static constexpr size_t kNumberOfLoadClassPackedBits = kFlagValidLoadedClassRTI + 1; + static constexpr size_t kNumberOfLoadClassPackedBits = kFieldLoadKind + kFieldLoadKindSize; static_assert(kNumberOfLoadClassPackedBits < kMaxNumberOfPackedBits, "Too many packed fields."); using LoadKindField = BitField; @@ -6335,6 +6329,8 @@ class HLoadClass FINAL : public HInstruction { const DexFile& dex_file_; Handle klass_; + + ReferenceTypeInfo loaded_class_rti_; }; std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs); @@ -6886,146 +6882,50 @@ enum class TypeCheckKind { kInterfaceCheck, // No optimization yet when checking against an interface. kArrayObjectCheck, // Can just check if the array is not primitive. kArrayCheck, // No optimization yet when checking against a generic array. - kBitstringCheck, // Compare the type check bitstring. kLast = kArrayCheck }; std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs); -// Note: HTypeCheckInstruction is just a helper class, not an abstract instruction with an -// `IsTypeCheckInstruction()`. (New virtual methods in the HInstruction class have a high cost.) -class HTypeCheckInstruction : public HVariableInputSizeInstruction { +class HInstanceOf FINAL : public HExpression<2> { public: - HTypeCheckInstruction(InstructionKind kind, - HInstruction* object, - HInstruction* target_class_or_null, - TypeCheckKind check_kind, - Handle klass, - uint32_t dex_pc, - ArenaAllocator* allocator, - HIntConstant* bitstring_path_to_root, - HIntConstant* bitstring_mask, - SideEffects side_effects) - : HVariableInputSizeInstruction( - kind, - side_effects, - dex_pc, - allocator, - /* number_of_inputs */ check_kind == TypeCheckKind::kBitstringCheck ? 4u : 2u, - kArenaAllocTypeCheckInputs), - klass_(klass) { + HInstanceOf(HInstruction* object, + HLoadClass* target_class, + TypeCheckKind check_kind, + uint32_t dex_pc) + : HExpression(kInstanceOf, + DataType::Type::kBool, + SideEffectsForArchRuntimeCalls(check_kind), + dex_pc) { SetPackedField(check_kind); SetPackedFlag(true); - SetPackedFlag(false); SetRawInputAt(0, object); - SetRawInputAt(1, target_class_or_null); - DCHECK_EQ(check_kind == TypeCheckKind::kBitstringCheck, bitstring_path_to_root != nullptr); - DCHECK_EQ(check_kind == TypeCheckKind::kBitstringCheck, bitstring_mask != nullptr); - if (check_kind == TypeCheckKind::kBitstringCheck) { - DCHECK(target_class_or_null->IsNullConstant()); - SetRawInputAt(2, bitstring_path_to_root); - SetRawInputAt(3, bitstring_mask); - } else { - DCHECK(target_class_or_null->IsLoadClass()); - } + SetRawInputAt(1, target_class); } HLoadClass* GetTargetClass() const { - DCHECK_NE(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck); HInstruction* load_class = InputAt(1); DCHECK(load_class->IsLoadClass()); return load_class->AsLoadClass(); } - uint32_t GetBitstringPathToRoot() const { - DCHECK_EQ(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck); - HInstruction* path_to_root = InputAt(2); - DCHECK(path_to_root->IsIntConstant()); - return static_cast(path_to_root->AsIntConstant()->GetValue()); - } - - uint32_t GetBitstringMask() const { - DCHECK_EQ(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck); - HInstruction* mask = InputAt(3); - DCHECK(mask->IsIntConstant()); - return static_cast(mask->AsIntConstant()->GetValue()); - } - bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { - DCHECK(other->IsInstanceOf() || other->IsCheckCast()) << other->DebugName(); - return GetPackedFields() == down_cast(other)->GetPackedFields(); + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + return true; } + bool NeedsEnvironment() const OVERRIDE { + return CanCallRuntime(GetTypeCheckKind()); + } + + // Used only in code generation. bool MustDoNullCheck() const { return GetPackedFlag(); } void ClearMustDoNullCheck() { SetPackedFlag(false); } TypeCheckKind GetTypeCheckKind() const { return GetPackedField(); } bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } - ReferenceTypeInfo GetTargetClassRTI() { - if (GetPackedFlag()) { - // Note: The is_exact flag from the return value should not be used. - return ReferenceTypeInfo::CreateUnchecked(klass_, /* is_exact */ true); - } else { - return ReferenceTypeInfo::CreateInvalid(); - } - } - - // Target class RTI is marked as valid by RTP if the klass_ is admissible. - void SetValidTargetClassRTI() REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(klass_ != nullptr); - SetPackedFlag(true); - } - - Handle GetClass() const { - return klass_; - } - - protected: - DEFAULT_COPY_CONSTRUCTOR(TypeCheckInstruction); - - private: - static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits; - static constexpr size_t kFieldTypeCheckKindSize = - MinimumBitsToStore(static_cast(TypeCheckKind::kLast)); - static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; - static constexpr size_t kFlagValidTargetClassRTI = kFlagMustDoNullCheck + 1; - static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagValidTargetClassRTI + 1; - static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - using TypeCheckKindField = BitField; - - Handle klass_; -}; - -class HInstanceOf FINAL : public HTypeCheckInstruction { - public: - HInstanceOf(HInstruction* object, - HInstruction* target_class_or_null, - TypeCheckKind check_kind, - Handle klass, - uint32_t dex_pc, - ArenaAllocator* allocator, - HIntConstant* bitstring_path_to_root, - HIntConstant* bitstring_mask) - : HTypeCheckInstruction(kInstanceOf, - object, - target_class_or_null, - check_kind, - klass, - dex_pc, - allocator, - bitstring_path_to_root, - bitstring_mask, - SideEffectsForArchRuntimeCalls(check_kind)) {} - - DataType::Type GetType() const OVERRIDE { return DataType::Type::kBool; } - - bool NeedsEnvironment() const OVERRIDE { - return CanCallRuntime(GetTypeCheckKind()); - } - static bool CanCallRuntime(TypeCheckKind check_kind) { // Mips currently does runtime calls for any other checks. return check_kind != TypeCheckKind::kExactCheck; @@ -7039,6 +6939,15 @@ class HInstanceOf FINAL : public HTypeCheckInstruction { protected: DEFAULT_COPY_CONSTRUCTOR(InstanceOf); + + private: + static constexpr size_t kFieldTypeCheckKind = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldTypeCheckKindSize = + MinimumBitsToStore(static_cast(TypeCheckKind::kLast)); + static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; + static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagMustDoNullCheck + 1; + static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using TypeCheckKindField = BitField; }; class HBoundType FINAL : public HExpression<1> { @@ -7088,26 +6997,31 @@ class HBoundType FINAL : public HExpression<1> { ReferenceTypeInfo upper_bound_; }; -class HCheckCast FINAL : public HTypeCheckInstruction { +class HCheckCast FINAL : public HTemplateInstruction<2> { public: HCheckCast(HInstruction* object, - HInstruction* target_class_or_null, + HLoadClass* target_class, TypeCheckKind check_kind, - Handle klass, - uint32_t dex_pc, - ArenaAllocator* allocator, - HIntConstant* bitstring_path_to_root, - HIntConstant* bitstring_mask) - : HTypeCheckInstruction(kCheckCast, - object, - target_class_or_null, - check_kind, - klass, - dex_pc, - allocator, - bitstring_path_to_root, - bitstring_mask, - SideEffects::CanTriggerGC()) {} + uint32_t dex_pc) + : HTemplateInstruction(kCheckCast, SideEffects::CanTriggerGC(), dex_pc) { + SetPackedField(check_kind); + SetPackedFlag(true); + SetRawInputAt(0, object); + SetRawInputAt(1, target_class); + } + + HLoadClass* GetTargetClass() const { + HInstruction* load_class = InputAt(1); + DCHECK(load_class->IsLoadClass()); + return load_class->AsLoadClass(); + } + + bool IsClonable() const OVERRIDE { return true; } + bool CanBeMoved() const OVERRIDE { return true; } + + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + return true; + } bool NeedsEnvironment() const OVERRIDE { // Instruction may throw a CheckCastError. @@ -7116,10 +7030,24 @@ class HCheckCast FINAL : public HTypeCheckInstruction { bool CanThrow() const OVERRIDE { return true; } + bool MustDoNullCheck() const { return GetPackedFlag(); } + void ClearMustDoNullCheck() { SetPackedFlag(false); } + TypeCheckKind GetTypeCheckKind() const { return GetPackedField(); } + bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } + DECLARE_INSTRUCTION(CheckCast); protected: DEFAULT_COPY_CONSTRUCTOR(CheckCast); + + private: + static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits; + static constexpr size_t kFieldTypeCheckKindSize = + MinimumBitsToStore(static_cast(TypeCheckKind::kLast)); + static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; + static constexpr size_t kNumberOfCheckCastPackedBits = kFlagMustDoNullCheck + 1; + static_assert(kNumberOfCheckCastPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using TypeCheckKindField = BitField; }; /** diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index e0a9cfb934..00194ff1fe 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -99,7 +99,6 @@ enum class MethodCompilationStat { kConstructorFenceRemovedLSE, kConstructorFenceRemovedPFRA, kConstructorFenceRemovedCFRE, - kBitstringTypeCheck, kJitOutOfMemoryForCommit, kLastStat }; diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index 59733397bf..f843c008d8 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -34,20 +34,6 @@ void PrepareForRegisterAllocation::Run() { } } -void PrepareForRegisterAllocation::VisitCheckCast(HCheckCast* check_cast) { - // Record only those bitstring type checks that make it to the codegen stage. - if (check_cast->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { - MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck); - } -} - -void PrepareForRegisterAllocation::VisitInstanceOf(HInstanceOf* instance_of) { - // Record only those bitstring type checks that make it to the codegen stage. - if (instance_of->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { - MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck); - } -} - void PrepareForRegisterAllocation::VisitNullCheck(HNullCheck* check) { check->ReplaceWith(check->InputAt(0)); } diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h index f6e4d3ef99..2c64f016c1 100644 --- a/compiler/optimizing/prepare_for_register_allocation.h +++ b/compiler/optimizing/prepare_for_register_allocation.h @@ -40,8 +40,6 @@ class PrepareForRegisterAllocation : public HGraphDelegateVisitor { "prepare_for_register_allocation"; private: - void VisitCheckCast(HCheckCast* check_cast) OVERRIDE; - void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE; void VisitNullCheck(HNullCheck* check) OVERRIDE; void VisitDivZeroCheck(HDivZeroCheck* check) OVERRIDE; void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE; diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 4030883a57..67a61fc01d 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -87,7 +87,6 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { void VisitDeoptimize(HDeoptimize* deopt) OVERRIDE; void VisitNewInstance(HNewInstance* new_instance) OVERRIDE; void VisitLoadClass(HLoadClass* load_class) OVERRIDE; - void VisitInstanceOf(HInstanceOf* load_class) OVERRIDE; void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE; void VisitLoadString(HLoadString* instr) OVERRIDE; void VisitLoadException(HLoadException* instr) OVERRIDE; @@ -172,12 +171,6 @@ void ReferenceTypePropagation::ValidateTypes() { << "NullCheck " << instr->GetReferenceTypeInfo() << "Input(0) " << instr->InputAt(0)->GetReferenceTypeInfo(); } - } else if (instr->IsInstanceOf()) { - HInstanceOf* iof = instr->AsInstanceOf(); - DCHECK(!iof->GetTargetClassRTI().IsValid() || iof->GetTargetClassRTI().IsExact()); - } else if (instr->IsCheckCast()) { - HCheckCast* check = instr->AsCheckCast(); - DCHECK(!check->GetTargetClassRTI().IsValid() || check->GetTargetClassRTI().IsExact()); } } } @@ -506,7 +499,8 @@ void ReferenceTypePropagation::RTPVisitor::BoundTypeForIfInstanceOf(HBasicBlock* return; } - ReferenceTypeInfo class_rti = instanceOf->GetTargetClassRTI(); + HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass(); + ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); if (!class_rti.IsValid()) { // He have loaded an unresolved class. Don't bother bounding the type. return; @@ -649,20 +643,15 @@ void ReferenceTypePropagation::RTPVisitor::VisitUnresolvedStaticFieldGet( void ReferenceTypePropagation::RTPVisitor::VisitLoadClass(HLoadClass* instr) { ScopedObjectAccess soa(Thread::Current()); - if (IsAdmissible(instr->GetClass().Get())) { - instr->SetValidLoadedClassRTI(); + Handle resolved_class = instr->GetClass(); + if (IsAdmissible(resolved_class.Get())) { + instr->SetLoadedClassRTI(ReferenceTypeInfo::Create( + resolved_class, /* is_exact */ true)); } instr->SetReferenceTypeInfo( ReferenceTypeInfo::Create(handle_cache_->GetClassClassHandle(), /* is_exact */ true)); } -void ReferenceTypePropagation::RTPVisitor::VisitInstanceOf(HInstanceOf* instr) { - ScopedObjectAccess soa(Thread::Current()); - if (IsAdmissible(instr->GetClass().Get())) { - instr->SetValidTargetClassRTI(); - } -} - void ReferenceTypePropagation::RTPVisitor::VisitClinitCheck(HClinitCheck* instr) { instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo()); } @@ -730,6 +719,8 @@ void ReferenceTypePropagation::RTPVisitor::VisitBoundType(HBoundType* instr) { } void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast) { + HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass(); + ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); HBoundType* bound_type = check_cast->GetNext()->AsBoundType(); if (bound_type == nullptr || bound_type->GetUpperBound().IsValid()) { // The next instruction is not an uninitialized BoundType. This must be @@ -738,14 +729,12 @@ void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast } DCHECK_EQ(bound_type->InputAt(0), check_cast->InputAt(0)); - ScopedObjectAccess soa(Thread::Current()); - Handle klass = check_cast->GetClass(); - if (IsAdmissible(klass.Get())) { + if (class_rti.IsValid()) { DCHECK(is_first_run_); - check_cast->SetValidTargetClassRTI(); + ScopedObjectAccess soa(Thread::Current()); // This is the first run of RTP and class is resolved. - bool is_exact = klass->CannotBeAssignedFromOtherTypes(); - bound_type->SetUpperBound(ReferenceTypeInfo::Create(klass, is_exact), + bool is_exact = class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes(); + bound_type->SetUpperBound(ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), is_exact), /* CheckCast succeeds for nulls. */ true); } else { // This is the first run of RTP and class is unresolved. Remove the binding. diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index 70b45763af..7dffb2a378 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -240,75 +240,6 @@ HLoadClass::LoadKind HSharpening::ComputeLoadClassKind( return load_kind; } -static inline bool CanUseTypeCheckBitstring(ObjPtr klass, - CodeGenerator* codegen, - CompilerDriver* compiler_driver) - REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(!klass->IsProxyClass()); - DCHECK(!klass->IsArrayClass()); - - if (Runtime::Current()->UseJitCompilation()) { - // If we're JITting, try to assign a type check bitstring (fall through). - } else if (codegen->GetCompilerOptions().IsBootImage()) { - const char* descriptor = klass->GetDexFile().StringByTypeIdx(klass->GetDexTypeIndex()); - if (!compiler_driver->IsImageClass(descriptor)) { - return false; - } - // If the target is a boot image class, try to assign a type check bitstring (fall through). - // (If --force-determinism, this was already done; repeating is OK and yields the same result.) - } else { - // TODO: Use the bitstring also for AOT app compilation if the target class has a bitstring - // already assigned in the boot image. - return false; - } - - // Try to assign a type check bitstring. - MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); - if ((false) && // FIXME: Inliner does not respect compiler_driver->IsClassToCompile() - // and we're hitting an unassigned bitstring in dex2oat_image_test. b/26687569 - kIsDebugBuild && - codegen->GetCompilerOptions().IsBootImage() && - codegen->GetCompilerOptions().IsForceDeterminism()) { - SubtypeCheckInfo::State old_state = SubtypeCheck>::GetState(klass); - CHECK(old_state == SubtypeCheckInfo::kAssigned || old_state == SubtypeCheckInfo::kOverflowed) - << klass->PrettyDescriptor() << "/" << old_state - << " in " << codegen->GetGraph()->PrettyMethod(); - } - SubtypeCheckInfo::State state = SubtypeCheck>::EnsureAssigned(klass); - return state == SubtypeCheckInfo::kAssigned; -} - -TypeCheckKind HSharpening::ComputeTypeCheckKind(ObjPtr klass, - CodeGenerator* codegen, - CompilerDriver* compiler_driver, - bool needs_access_check) { - if (klass == nullptr) { - return TypeCheckKind::kUnresolvedCheck; - } else if (klass->IsInterface()) { - return TypeCheckKind::kInterfaceCheck; - } else if (klass->IsArrayClass()) { - if (klass->GetComponentType()->IsObjectClass()) { - return TypeCheckKind::kArrayObjectCheck; - } else if (klass->CannotBeAssignedFromOtherTypes()) { - return TypeCheckKind::kExactCheck; - } else { - return TypeCheckKind::kArrayCheck; - } - } else if (klass->IsFinal()) { // TODO: Consider using bitstring for final classes. - return TypeCheckKind::kExactCheck; - } else if (kBitstringSubtypeCheckEnabled && - !needs_access_check && - CanUseTypeCheckBitstring(klass, codegen, compiler_driver)) { - // TODO: We should not need the `!needs_access_check` check but getting rid of that - // requires rewriting some optimizations in instruction simplifier. - return TypeCheckKind::kBitstringCheck; - } else if (klass->IsAbstract()) { - return TypeCheckKind::kAbstractClassCheck; - } else { - return TypeCheckKind::kClassHierarchyCheck; - } -} - void HSharpening::ProcessLoadString( HLoadString* load_string, CodeGenerator* codegen, diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h index fa3e948eeb..6df7d6d91e 100644 --- a/compiler/optimizing/sharpening.h +++ b/compiler/optimizing/sharpening.h @@ -44,10 +44,12 @@ class HSharpening : public HOptimization { static constexpr const char* kSharpeningPassName = "sharpening"; - // Used by Sharpening and InstructionSimplifier. - static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, - CodeGenerator* codegen, - CompilerDriver* compiler_driver); + // Used by the builder. + static void ProcessLoadString(HLoadString* load_string, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + const DexCompilationUnit& dex_compilation_unit, + VariableSizedHandleScope* handles); // Used by the builder and the inliner. static HLoadClass::LoadKind ComputeLoadClassKind(HLoadClass* load_class, @@ -56,19 +58,10 @@ class HSharpening : public HOptimization { const DexCompilationUnit& dex_compilation_unit) REQUIRES_SHARED(Locks::mutator_lock_); - // Used by the builder. - static TypeCheckKind ComputeTypeCheckKind(ObjPtr klass, - CodeGenerator* codegen, - CompilerDriver* compiler_driver, - bool needs_access_check) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Used by the builder. - static void ProcessLoadString(HLoadString* load_string, - CodeGenerator* codegen, - CompilerDriver* compiler_driver, - const DexCompilationUnit& dex_compilation_unit, - VariableSizedHandleScope* handles); + // Used by Sharpening and InstructionSimplifier. + static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, + CodeGenerator* codegen, + CompilerDriver* compiler_driver); private: CodeGenerator* codegen_; diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 0fd239a244..98214fb684 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -809,9 +809,6 @@ END art_quick_unlock_object_no_inline .extern artInstanceOfFromCode .extern artThrowClassCastExceptionForObject ENTRY art_quick_check_instance_of - // Type check using the bit string passes null as the target class. In that case just throw. - cbz r1, .Lthrow_class_cast_exception_for_bitstring_check - push {r0-r2, lr} @ save arguments, padding (r2) and link register .cfi_adjust_cfa_offset 16 .cfi_rel_offset r0, 0 @@ -830,7 +827,6 @@ ENTRY art_quick_check_instance_of .cfi_restore r2 .cfi_restore lr -.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r2 @ save all registers as basis for long jump context mov r2, r9 @ pass Thread::Current bl artThrowClassCastExceptionForObject @ (Object*, Class*, Thread*) diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 9ff5ebede3..fb449ed5c7 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1269,9 +1269,6 @@ END art_quick_unlock_object_no_inline .extern artInstanceOfFromCode .extern artThrowClassCastExceptionForObject ENTRY art_quick_check_instance_of - // Type check using the bit string passes null as the target class. In that case just throw. - cbz x1, .Lthrow_class_cast_exception_for_bitstring_check - // Store arguments and link register // Stack needs to be 16B aligned on calls. SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 32 @@ -1297,7 +1294,6 @@ ENTRY art_quick_check_instance_of // Restore RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32 -.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // save all registers as basis for long jump context mov x2, xSELF // pass Thread::Current bl artThrowClassCastExceptionForObject // (Object*, Class*, Thread*) diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index d8fe480719..b2f7e10f52 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1423,10 +1423,6 @@ END art_quick_unlock_object_no_inline .extern artInstanceOfFromCode .extern artThrowClassCastExceptionForObject ENTRY art_quick_check_instance_of - // Type check using the bit string passes null as the target class. In that case just throw. - beqz $a1, .Lthrow_class_cast_exception_for_bitstring_check - nop - addiu $sp, $sp, -32 .cfi_adjust_cfa_offset 32 sw $gp, 16($sp) @@ -1445,15 +1441,12 @@ ENTRY art_quick_check_instance_of jalr $zero, $ra addiu $sp, $sp, 32 .cfi_adjust_cfa_offset -32 - .Lthrow_class_cast_exception: lw $t9, 8($sp) lw $a1, 4($sp) lw $a0, 0($sp) addiu $sp, $sp, 32 .cfi_adjust_cfa_offset -32 - -.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME la $t9, artThrowClassCastExceptionForObject jalr $zero, $t9 # artThrowClassCastException (Object*, Class*, Thread*) diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 8d2a7bd6c1..58e0e44813 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1364,9 +1364,6 @@ END art_quick_unlock_object_no_inline .extern artInstanceOfFromCode .extern artThrowClassCastExceptionForObject ENTRY art_quick_check_instance_of - // Type check using the bit string passes null as the target class. In that case just throw. - beqzc $a1, .Lthrow_class_cast_exception_for_bitstring_check - daddiu $sp, $sp, -32 .cfi_adjust_cfa_offset 32 sd $ra, 24($sp) @@ -1382,15 +1379,12 @@ ENTRY art_quick_check_instance_of jalr $zero, $ra daddiu $sp, $sp, 32 .cfi_adjust_cfa_offset -32 - .Lthrow_class_cast_exception: ld $t9, 16($sp) ld $a1, 8($sp) ld $a0, 0($sp) daddiu $sp, $sp, 32 .cfi_adjust_cfa_offset -32 - -.Lthrow_class_cast_exception_for_bitstring_check: SETUP_GP SETUP_SAVE_ALL_CALLEE_SAVES_FRAME dla $t9, artThrowClassCastExceptionForObject diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index df43aef94b..5c4ae4ea12 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1432,10 +1432,6 @@ DEFINE_FUNCTION art_quick_instance_of END_FUNCTION art_quick_instance_of DEFINE_FUNCTION art_quick_check_instance_of - // Type check using the bit string passes null as the target class. In that case just throw. - testl %ecx, %ecx - jz .Lthrow_class_cast_exception_for_bitstring_check - PUSH eax // alignment padding PUSH ecx // pass arg2 - checked class PUSH eax // pass arg1 - obj @@ -1453,7 +1449,6 @@ DEFINE_FUNCTION art_quick_check_instance_of addl LITERAL(4), %esp CFI_ADJUST_CFA_OFFSET(-4) -.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME ebx, ebx // save all registers as basis for long jump context // Outgoing argument set up PUSH eax // alignment padding diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 4f941e1c48..a813200606 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -1403,10 +1403,6 @@ DEFINE_FUNCTION art_quick_unlock_object_no_inline END_FUNCTION art_quick_unlock_object_no_inline DEFINE_FUNCTION art_quick_check_instance_of - // Type check using the bit string passes null as the target class. In that case just throw. - testl %esi, %esi - jz .Lthrow_class_cast_exception_for_bitstring_check - // We could check the super classes here but that is usually already checked in the caller. PUSH rdi // Save args for exc PUSH rsi @@ -1430,7 +1426,6 @@ DEFINE_FUNCTION art_quick_check_instance_of POP rsi // Pop arguments POP rdi -.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // save all registers as basis for long jump context mov %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current() call SYMBOL(artThrowClassCastExceptionForObject) // (Object* src, Class* dest, Thread*) diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc index fe0f876d66..292bde0272 100644 --- a/runtime/base/arena_allocator.cc +++ b/runtime/base/arena_allocator.cc @@ -56,7 +56,6 @@ const char* const ArenaAllocatorStatsImpl::kAllocNames[] = { "CtorFenceIns ", "InvokeInputs ", "PhiInputs ", - "TypeCheckIns ", "LoopInfo ", "LIBackEdges ", "TryCatchInf ", diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h index 688f01b71f..c3011091e8 100644 --- a/runtime/base/arena_allocator.h +++ b/runtime/base/arena_allocator.h @@ -62,7 +62,6 @@ enum ArenaAllocKind { kArenaAllocConstructorFenceInputs, kArenaAllocInvokeInputs, kArenaAllocPhiInputs, - kArenaAllocTypeCheckInputs, kArenaAllocLoopInfo, kArenaAllocLoopInfoBackEdges, kArenaAllocTryCatchInfo, diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc index ba7fb6b9db..9b0756b529 100644 --- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc @@ -16,11 +16,8 @@ #include "art_method-inl.h" #include "callee_save_frame.h" -#include "dex/code_item_accessors-inl.h" -#include "dex/dex_instruction-inl.h" #include "common_throws.h" #include "mirror/object-inl.h" -#include "nth_caller_visitor.h" #include "thread.h" #include "well_known_classes.h" @@ -115,26 +112,6 @@ extern "C" NO_RETURN void artThrowClassCastException(mirror::Class* dest_type, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); - if (dest_type == nullptr) { - // Find the target class for check cast using the bitstring check (dest_type == null). - NthCallerVisitor visitor(self, 0u); - visitor.WalkStack(); - DCHECK(visitor.caller != nullptr); - uint32_t dex_pc = visitor.GetDexPc(); - CodeItemDataAccessor accessor(*visitor.caller->GetDexFile(), visitor.caller->GetCodeItem()); - const Instruction& check_cast = accessor.InstructionAt(dex_pc); - DCHECK_EQ(check_cast.Opcode(), Instruction::CHECK_CAST); - dex::TypeIndex type_index(check_cast.VRegB_21c()); - ClassLinker* linker = Runtime::Current()->GetClassLinker(); - dest_type = linker->LookupResolvedType(type_index, visitor.caller).Ptr(); - CHECK(dest_type != nullptr) << "Target class should have been previously resolved: " - << visitor.caller->GetDexFile()->PrettyType(type_index); - CHECK(!dest_type->IsAssignableFrom(src_type)) - << " " << std::hex << dest_type->PrettyDescriptor() << ";" << dest_type->Depth() - << "/" << dest_type->GetField32(mirror::Class::StatusOffset()) - << " <: " << src_type->PrettyDescriptor() << ";" << src_type->Depth() - << "/" << src_type->GetField32(mirror::Class::StatusOffset()); - } DCHECK(!dest_type->IsAssignableFrom(src_type)); ThrowClassCastException(dest_type, src_type); self->QuickDeliverException(); diff --git a/runtime/subtype_check.h b/runtime/subtype_check.h index a8b4dd5348..3b1d5f8c4a 100644 --- a/runtime/subtype_check.h +++ b/runtime/subtype_check.h @@ -25,7 +25,7 @@ #include "runtime.h" // Build flag for the bitstring subtype check runtime hooks. -constexpr bool kBitstringSubtypeCheckEnabled = true; +constexpr bool kBitstringSubtypeCheckEnabled = false; /** * Any node in a tree can have its path (from the root to the node) represented as a string by @@ -286,17 +286,6 @@ struct SubtypeCheck { return SubtypeCheckInfo::kUninitialized; } - // Retrieve the state of this class's SubtypeCheckInfo. - // - // Cost: O(Depth(Class)). - // - // Returns: The precise SubtypeCheckInfo::State. - static SubtypeCheckInfo::State GetState(ClassPtr klass) - REQUIRES(Locks::subtype_check_lock_) - REQUIRES_SHARED(Locks::mutator_lock_) { - return GetSubtypeCheckInfo(klass).GetState(); - } - // Retrieve the path to root bitstring as a plain uintN_t value that is amenable to // be used by a fast check "encoded_src & mask_target == encoded_target". // @@ -319,9 +308,8 @@ struct SubtypeCheck { static BitString::StorageType GetEncodedPathToRootForTarget(ClassPtr klass) REQUIRES(Locks::subtype_check_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { - SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass); - DCHECK_EQ(SubtypeCheckInfo::kAssigned, sci.GetState()); - return sci.GetEncodedPathToRoot(); + DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState()); + return GetSubtypeCheckInfo(klass).GetEncodedPathToRoot(); } // Retrieve the path to root bitstring mask as a plain uintN_t value that is amenable to @@ -333,9 +321,8 @@ struct SubtypeCheck { static BitString::StorageType GetEncodedPathToRootMask(ClassPtr klass) REQUIRES(Locks::subtype_check_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { - SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass); - DCHECK_EQ(SubtypeCheckInfo::kAssigned, sci.GetState()); - return sci.GetEncodedPathToRootMask(); + DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState()); + return GetSubtypeCheckInfo(klass).GetEncodedPathToRootMask(); } // Is the source class a subclass of the target? -- GitLab From bfd3bc801beebc023acb21ae24ab8d8cacf8a4c7 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 26 Mar 2018 14:19:32 -0700 Subject: [PATCH 141/749] ART: Bump image version Bug: 26687569 Bug: 64692057 Bug: 76420366 Test: m Change-Id: I3e67e750d99362accfc8cb35a19510638b217907 --- runtime/image.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/image.cc b/runtime/image.cc index 5af3e5451b..f14707874b 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -26,7 +26,7 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '7', '\0' }; // R^2 Bitstring type check. +const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '8', '\0' }; // R^3 Bitstring type check. ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, -- GitLab From 03c9153f1e0d6db265c86961b3a36db951701c9a Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 27 Mar 2018 09:42:01 +0100 Subject: [PATCH 142/749] Blacklist timing out test on gcstress / debug. Test: test.py Change-Id: Ib69f4cf2406b421ae0029047975fdd126a314e59 --- test/knownfailures.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/knownfailures.json b/test/knownfailures.json index d390c4c049..6d8abe13b2 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -976,5 +976,11 @@ "tests": "677-fsi", "variant": "no-dex2oat | no-image | no-prebuild | relocate-npatchoat | jvm", "description": ["Test requires a successful dex2oat invocation"] + }, + { + "tests": ["990-field-trace", + "991-field-trace-2"], + "variant": "gcstress & debug & target", + "description": ["Test can time out on gcstress with debug"] } ] -- GitLab From 175e7862dbdb44089ef327fc43ba00c791fd3838 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 27 Mar 2018 09:03:13 +0000 Subject: [PATCH 143/749] Revert^4 "Compiler changes for bitstring based type checks." Disabled the build time flag. (No image version bump needed.) Bug: 26687569 Bug: 64692057 Bug: 76420366 This reverts commit 3fbd3ad99fad077e5c760e7238bcd55b07d4c06e. Change-Id: I5d83c4ce8a7331c435d5155ac6e0ce1c77d60004 --- compiler/driver/compiler_driver.cc | 115 +++++++++- compiler/optimizing/code_generator.h | 2 + compiler/optimizing/code_generator_arm64.cc | 77 ++++++- compiler/optimizing/code_generator_arm64.h | 2 + .../optimizing/code_generator_arm_vixl.cc | 121 +++++++++- compiler/optimizing/code_generator_arm_vixl.h | 3 + compiler/optimizing/code_generator_mips.cc | 98 ++++++-- compiler/optimizing/code_generator_mips.h | 1 + compiler/optimizing/code_generator_mips64.cc | 98 ++++++-- compiler/optimizing/code_generator_mips64.h | 1 + compiler/optimizing/code_generator_x86.cc | 62 ++++- compiler/optimizing/code_generator_x86.h | 1 + compiler/optimizing/code_generator_x86_64.cc | 71 +++++- compiler/optimizing/code_generator_x86_64.h | 1 + compiler/optimizing/graph_checker.cc | 92 ++++++-- compiler/optimizing/graph_checker.h | 6 + compiler/optimizing/graph_visualizer.cc | 45 ++-- compiler/optimizing/instruction_builder.cc | 107 +++++---- compiler/optimizing/instruction_builder.h | 7 + compiler/optimizing/instruction_simplifier.cc | 43 ++-- compiler/optimizing/nodes.cc | 2 + compiler/optimizing/nodes.h | 216 ++++++++++++------ .../optimizing/optimizing_compiler_stats.h | 1 + .../prepare_for_register_allocation.cc | 14 ++ .../prepare_for_register_allocation.h | 2 + .../optimizing/reference_type_propagation.cc | 35 ++- compiler/optimizing/sharpening.cc | 69 ++++++ compiler/optimizing/sharpening.h | 27 ++- runtime/arch/arm/quick_entrypoints_arm.S | 4 + runtime/arch/arm64/quick_entrypoints_arm64.S | 4 + runtime/arch/mips/quick_entrypoints_mips.S | 7 + .../arch/mips64/quick_entrypoints_mips64.S | 6 + runtime/arch/x86/quick_entrypoints_x86.S | 5 + .../arch/x86_64/quick_entrypoints_x86_64.S | 5 + runtime/base/arena_allocator.cc | 1 + runtime/base/arena_allocator.h | 1 + .../quick/quick_throw_entrypoints.cc | 23 ++ runtime/subtype_check.h | 21 +- 38 files changed, 1157 insertions(+), 239 deletions(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 127833233a..4093833e0b 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -781,7 +781,8 @@ void CompilerDriver::Resolve(jobject class_loader, // TODO: Collect the relevant string indices in parallel, then allocate them sequentially in a // stable order. -static void ResolveConstStrings(Handle dex_cache, +static void ResolveConstStrings(ClassLinker* class_linker, + Handle dex_cache, const DexFile& dex_file, const DexFile::CodeItem* code_item) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -790,7 +791,6 @@ static void ResolveConstStrings(Handle dex_cache, return; } - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) { switch (inst->Opcode()) { case Instruction::CONST_STRING: @@ -838,22 +838,105 @@ static void ResolveConstStrings(CompilerDriver* driver, dex_file->StringByTypeIdx(class_def.class_idx_)); if (!compilation_enabled) { // Compilation is skipped, do not resolve const-string in code of this class. - // TODO: Make sure that inlining honors this. + // FIXME: Make sure that inlining honors this. b/26687569 continue; } // Direct and virtual methods. - int64_t previous_method_idx = -1; while (it.HasNextMethod()) { - uint32_t method_idx = it.GetMemberIndex(); - if (method_idx == previous_method_idx) { - // smali can create dex files with two encoded_methods sharing the same method_idx - // http://code.google.com/p/smali/issues/detail?id=119 - it.Next(); - continue; + ResolveConstStrings(class_linker, dex_cache, *dex_file, it.GetMethodCodeItem()); + it.Next(); + } + DCHECK(!it.HasNext()); + } + } +} + +// Initialize type check bit strings for check-cast and instance-of in the code. Done to have +// deterministic allocation behavior. Right now this is single-threaded for simplicity. +// TODO: Collect the relevant type indices in parallel, then process them sequentially in a +// stable order. + +static void InitializeTypeCheckBitstrings(CompilerDriver* driver, + ClassLinker* class_linker, + Handle dex_cache, + const DexFile& dex_file, + const DexFile::CodeItem* code_item) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (code_item == nullptr) { + // Abstract or native method. + return; + } + + for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) { + switch (inst->Opcode()) { + case Instruction::CHECK_CAST: + case Instruction::INSTANCE_OF: { + dex::TypeIndex type_index( + (inst->Opcode() == Instruction::CHECK_CAST) ? inst->VRegB_21c() : inst->VRegC_22c()); + const char* descriptor = dex_file.StringByTypeIdx(type_index); + // We currently do not use the bitstring type check for array or final (including + // primitive) classes. We may reconsider this in future if it's deemed to be beneficial. + // And we cannot use it for classes outside the boot image as we do not know the runtime + // value of their bitstring when compiling (it may not even get assigned at runtime). + if (descriptor[0] == 'L' && driver->IsImageClass(descriptor)) { + ObjPtr klass = + class_linker->LookupResolvedType(type_index, + dex_cache.Get(), + /* class_loader */ nullptr); + CHECK(klass != nullptr) << descriptor << " should have been previously resolved."; + // Now assign the bitstring if the class is not final. Keep this in sync with sharpening. + if (!klass->IsFinal()) { + MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); + SubtypeCheck>::EnsureAssigned(klass); + } } - previous_method_idx = method_idx; - ResolveConstStrings(dex_cache, *dex_file, it.GetMethodCodeItem()); + break; + } + + default: + break; + } + } +} + +static void InitializeTypeCheckBitstrings(CompilerDriver* driver, + const std::vector& dex_files, + TimingLogger* timings) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + MutableHandle dex_cache(hs.NewHandle(nullptr)); + + for (const DexFile* dex_file : dex_files) { + dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file)); + TimingLogger::ScopedTiming t("Initialize type check bitstrings", timings); + + size_t class_def_count = dex_file->NumClassDefs(); + for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) { + const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); + + const uint8_t* class_data = dex_file->GetClassData(class_def); + if (class_data == nullptr) { + // empty class, probably a marker interface + continue; + } + + ClassDataItemIterator it(*dex_file, class_data); + it.SkipAllFields(); + + bool compilation_enabled = driver->IsClassToCompile( + dex_file->StringByTypeIdx(class_def.class_idx_)); + if (!compilation_enabled) { + // Compilation is skipped, do not look for type checks in code of this class. + // FIXME: Make sure that inlining honors this. b/26687569 + continue; + } + + // Direct and virtual methods. + while (it.HasNextMethod()) { + InitializeTypeCheckBitstrings( + driver, class_linker, dex_cache, *dex_file, it.GetMethodCodeItem()); it.Next(); } DCHECK(!it.HasNext()); @@ -955,6 +1038,14 @@ void CompilerDriver::PreCompile(jobject class_loader, UpdateImageClasses(timings); VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false); + + if (kBitstringSubtypeCheckEnabled && + GetCompilerOptions().IsForceDeterminism() && GetCompilerOptions().IsBootImage()) { + // Initialize type check bit string used by check-cast and instanceof. + // Do this now to have a deterministic image. + // Note: This is done after UpdateImageClasses() at it relies on the image classes to be final. + InitializeTypeCheckBitstrings(this, dex_files, timings); + } } bool CompilerDriver::IsImageClass(const char* descriptor) const { diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index a4873202b2..3bd5e14539 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -438,6 +438,8 @@ class CodeGenerator : public DeletableArenaObject { case TypeCheckKind::kArrayCheck: case TypeCheckKind::kUnresolvedCheck: return false; + case TypeCheckKind::kBitstringCheck: + return true; } LOG(FATAL) << "Unreachable"; UNREACHABLE(); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index a024df8537..273346ab4a 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -2129,6 +2129,26 @@ void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCod __ Bind(slow_path->GetExitLabel()); } +void InstructionCodeGeneratorARM64::GenerateBitstringTypeCheckCompare( + HTypeCheckInstruction* check, vixl::aarch64::Register temp) { + uint32_t path_to_root = check->GetBitstringPathToRoot(); + uint32_t mask = check->GetBitstringMask(); + DCHECK(IsPowerOfTwo(mask + 1)); + size_t mask_bits = WhichPowerOf2(mask + 1); + + if (mask_bits == 16u) { + // Load only the bitstring part of the status word. + __ Ldrh(temp, HeapOperand(temp, mirror::Class::StatusOffset())); + } else { + // /* uint32_t */ temp = temp->status_ + __ Ldr(temp, HeapOperand(temp, mirror::Class::StatusOffset())); + // Extract the bitstring bits. + __ Ubfx(temp, temp, 0, mask_bits); + } + // Compare the bitstring bits to `path_to_root`. + __ Cmp(temp, path_to_root); +} + void CodeGeneratorARM64::GenerateMemoryBarrier(MemBarrierKind kind) { BarrierType type = BarrierAll; @@ -3866,6 +3886,8 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; + case TypeCheckKind::kBitstringCheck: + break; } LocationSummary* locations = @@ -3874,7 +3896,13 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } // The "out" register is used as a temporary, so it overlaps with the inputs. // Note that TypeCheckSlowPathARM64 uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -3887,7 +3915,9 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = InputRegisterAt(instruction, 0); - Register cls = InputRegisterAt(instruction, 1); + Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) + ? Register() + : InputRegisterAt(instruction, 1); Location out_loc = locations->Out(); Register out = OutputRegister(instruction); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -4073,6 +4103,23 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { } break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, out); + __ Cset(out, eq); + if (zero.IsLinked()) { + __ B(&done); + } + break; + } } if (zero.IsLinked()) { @@ -4095,7 +4142,13 @@ void LocationsBuilderARM64::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } // Add temps for read barriers and other uses. One is used by TypeCheckSlowPathARM64. locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -4105,7 +4158,9 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = InputRegisterAt(instruction, 0); - Register cls = InputRegisterAt(instruction, 1); + Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) + ? Register() + : InputRegisterAt(instruction, 1); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); DCHECK_GE(num_temps, 1u); DCHECK_LE(num_temps, 3u); @@ -4286,6 +4341,20 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { __ B(ne, &start_loop); break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + maybe_temp2_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, temp); + __ B(ne, type_check_slow_path->GetEntryLabel()); + break; + } } __ Bind(&done); diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index a8a9802f9a..6a52eecbd3 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -264,6 +264,8 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { private: void GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path, vixl::aarch64::Register class_reg); + void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, + vixl::aarch64::Register temp); void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor); void HandleBinaryOp(HBinaryOperation* instr); diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 6ebcc67b49..b38a006305 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -7523,6 +7523,67 @@ void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck( __ Bind(slow_path->GetExitLabel()); } +void InstructionCodeGeneratorARMVIXL::GenerateBitstringTypeCheckCompare( + HTypeCheckInstruction* check, + vixl32::Register temp, + vixl32::FlagsUpdate flags_update) { + uint32_t path_to_root = check->GetBitstringPathToRoot(); + uint32_t mask = check->GetBitstringMask(); + DCHECK(IsPowerOfTwo(mask + 1)); + size_t mask_bits = WhichPowerOf2(mask + 1); + + // Note that HInstanceOf shall check for zero value in `temp` but HCheckCast needs + // the Z flag for BNE. This is indicated by the `flags_update` parameter. + if (mask_bits == 16u) { + // Load only the bitstring part of the status word. + __ Ldrh(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value())); + // Check if the bitstring bits are equal to `path_to_root`. + if (flags_update == SetFlags) { + __ Cmp(temp, path_to_root); + } else { + __ Sub(temp, temp, path_to_root); + } + } else { + // /* uint32_t */ temp = temp->status_ + __ Ldr(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value())); + if (GetAssembler()->ShifterOperandCanHold(SUB, path_to_root)) { + // Compare the bitstring bits using SUB. + __ Sub(temp, temp, path_to_root); + // Shift out bits that do not contribute to the comparison. + __ Lsl(flags_update, temp, temp, dchecked_integral_cast(32u - mask_bits)); + } else if (IsUint<16>(path_to_root)) { + if (temp.IsLow()) { + // Note: Optimized for size but contains one more dependent instruction than necessary. + // MOVW+SUB(register) would be 8 bytes unless we find a low-reg temporary but the + // macro assembler would use the high reg IP for the constant by default. + // Compare the bitstring bits using SUB. + __ Sub(temp, temp, path_to_root & 0x00ffu); // 16-bit SUB (immediate) T2 + __ Sub(temp, temp, path_to_root & 0xff00u); // 32-bit SUB (immediate) T3 + // Shift out bits that do not contribute to the comparison. + __ Lsl(flags_update, temp, temp, dchecked_integral_cast(32u - mask_bits)); + } else { + // Extract the bitstring bits. + __ Ubfx(temp, temp, 0, mask_bits); + // Check if the bitstring bits are equal to `path_to_root`. + if (flags_update == SetFlags) { + __ Cmp(temp, path_to_root); + } else { + __ Sub(temp, temp, path_to_root); + } + } + } else { + // Shift out bits that do not contribute to the comparison. + __ Lsl(temp, temp, dchecked_integral_cast(32u - mask_bits)); + // Check if the shifted bitstring bits are equal to `path_to_root << (32u - mask_bits)`. + if (flags_update == SetFlags) { + __ Cmp(temp, path_to_root << (32u - mask_bits)); + } else { + __ Sub(temp, temp, path_to_root << (32u - mask_bits)); + } + } + } +} + HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { switch (desired_string_load_kind) { @@ -7714,6 +7775,8 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; + case TypeCheckKind::kBitstringCheck: + break; } LocationSummary* locations = @@ -7722,7 +7785,13 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } // The "out" register is used as a temporary, so it overlaps with the inputs. // Note that TypeCheckSlowPathARM uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -7737,7 +7806,9 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); vixl32::Register obj = InputRegisterAt(instruction, 0); - vixl32::Register cls = InputRegisterAt(instruction, 1); + vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) + ? vixl32::Register() + : InputRegisterAt(instruction, 1); Location out_loc = locations->Out(); vixl32::Register out = OutputRegister(instruction); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -7977,6 +8048,26 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) __ B(slow_path->GetEntryLabel()); break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, out, DontCare); + // If `out` is a low reg and we would have another low reg temp, we could + // optimize this as RSBS+ADC, see GenerateConditionWithZero(). + // + // Also, in some cases when `out` is a low reg and we're loading a constant to IP + // it would make sense to use CMP+MOV+IT+MOV instead of SUB+CLZ+LSR as the code size + // would be the same and we would have fewer direct data dependencies. + codegen_->GenerateConditionWithZero(kCondEQ, out, out); // CLZ+LSR + break; + } } if (done.IsReferenced()) { @@ -7994,7 +8085,13 @@ void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -8003,7 +8100,9 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); vixl32::Register obj = InputRegisterAt(instruction, 0); - vixl32::Register cls = InputRegisterAt(instruction, 1); + vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck) + ? vixl32::Register() + : InputRegisterAt(instruction, 1); Location temp_loc = locations->GetTemp(0); vixl32::Register temp = RegisterFrom(temp_loc); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); @@ -8188,6 +8287,20 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { __ B(ne, &start_loop, /* far_target */ false); break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + maybe_temp2_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, temp, SetFlags); + __ B(ne, type_check_slow_path->GetEntryLabel()); + break; + } } if (done.IsReferenced()) { __ Bind(&done); diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 6a07e36022..2114ea1ba1 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -322,6 +322,9 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor); void GenerateClassInitializationCheck(LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg); + void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, + vixl::aarch32::Register temp, + vixl::aarch32::FlagsUpdate flags_update); void GenerateAndConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value); void GenerateOrrConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value); void GenerateEorConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value); diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index be9ff48a6b..25e2eddbfa 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -1950,6 +1950,34 @@ void InstructionCodeGeneratorMIPS::GenerateClassInitializationCheck(SlowPathCode __ Bind(slow_path->GetExitLabel()); } +void InstructionCodeGeneratorMIPS::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, + Register temp) { + uint32_t path_to_root = check->GetBitstringPathToRoot(); + uint32_t mask = check->GetBitstringMask(); + DCHECK(IsPowerOfTwo(mask + 1)); + size_t mask_bits = WhichPowerOf2(mask + 1); + + if (mask_bits == 16u) { + // Load only the bitstring part of the status word. + __ LoadFromOffset( + kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value()); + // Compare the bitstring bits using XOR. + __ Xori(temp, temp, dchecked_integral_cast(path_to_root)); + } else { + // /* uint32_t */ temp = temp->status_ + __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value()); + // Compare the bitstring bits using XOR. + if (IsUint<16>(path_to_root)) { + __ Xori(temp, temp, dchecked_integral_cast(path_to_root)); + } else { + __ LoadConst32(TMP, path_to_root); + __ Xor(temp, temp, TMP); + } + // Shift out bits that do not contribute to the comparison. + __ Sll(temp, temp, 32 - mask_bits); + } +} + void InstructionCodeGeneratorMIPS::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) { __ Sync(0); // Only stype 0 is supported. } @@ -3301,7 +3329,13 @@ void LocationsBuilderMIPS::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -3310,7 +3344,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = obj_loc.AsRegister(); - Register cls = locations->InAt(1).AsRegister(); + Location cls = locations->InAt(1); Location temp_loc = locations->GetTemp(0); Register temp = temp_loc.AsRegister(); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); @@ -3349,7 +3383,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { kWithoutReadBarrier); // Jump to slow path for throwing the exception or doing a // more involved array check. - __ Bne(temp, cls, slow_path->GetEntryLabel()); + __ Bne(temp, cls.AsRegister(), slow_path->GetEntryLabel()); break; } @@ -3375,7 +3409,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { // exception. __ Beqz(temp, slow_path->GetEntryLabel()); // Otherwise, compare the classes. - __ Bne(temp, cls, &loop); + __ Bne(temp, cls.AsRegister(), &loop); break; } @@ -3390,7 +3424,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { // Walk over the class hierarchy to find a match. MipsLabel loop; __ Bind(&loop); - __ Beq(temp, cls, &done); + __ Beq(temp, cls.AsRegister(), &done); // /* HeapReference */ temp = temp->super_class_ GenerateReferenceLoadOneRegister(instruction, temp_loc, @@ -3413,7 +3447,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { maybe_temp2_loc, kWithoutReadBarrier); // Do an exact check. - __ Beq(temp, cls, &done); + __ Beq(temp, cls.AsRegister(), &done); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference */ temp = temp->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -3472,7 +3506,21 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) { // Go to next interface. __ Addiu(TMP, TMP, -2); // Compare the classes and continue the loop if they do not match. - __ Bne(AT, cls, &loop); + __ Bne(AT, cls.AsRegister(), &loop); + break; + } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + maybe_temp2_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, temp); + __ Bnez(temp, slow_path->GetEntryLabel()); break; } } @@ -7415,6 +7463,8 @@ void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; + case TypeCheckKind::kBitstringCheck: + break; } LocationSummary* locations = @@ -7423,7 +7473,13 @@ void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } // The output does overlap inputs. // Note that TypeCheckSlowPathMIPS uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -7435,7 +7491,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = obj_loc.AsRegister(); - Register cls = locations->InAt(1).AsRegister(); + Location cls = locations->InAt(1); Location out_loc = locations->Out(); Register out = out_loc.AsRegister(); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -7467,7 +7523,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { maybe_temp_loc, read_barrier_option); // Classes must be equal for the instanceof to succeed. - __ Xor(out, out, cls); + __ Xor(out, out, cls.AsRegister()); __ Sltiu(out, out, 1); break; } @@ -7494,7 +7550,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { read_barrier_option); // If `out` is null, we use it for the result, and jump to `done`. __ Beqz(out, &done); - __ Bne(out, cls, &loop); + __ Bne(out, cls.AsRegister(), &loop); __ LoadConst32(out, 1); break; } @@ -7512,7 +7568,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { // Walk over the class hierarchy to find a match. MipsLabel loop, success; __ Bind(&loop); - __ Beq(out, cls, &success); + __ Beq(out, cls.AsRegister(), &success); // /* HeapReference */ out = out->super_class_ GenerateReferenceLoadOneRegister(instruction, out_loc, @@ -7539,7 +7595,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { read_barrier_option); // Do an exact check. MipsLabel success; - __ Beq(out, cls, &success); + __ Beq(out, cls.AsRegister(), &success); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference */ out = out->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -7571,7 +7627,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS( instruction, /* is_fatal */ false); codegen_->AddSlowPath(slow_path); - __ Bne(out, cls, slow_path->GetEntryLabel()); + __ Bne(out, cls.AsRegister(), slow_path->GetEntryLabel()); __ LoadConst32(out, 1); break; } @@ -7603,6 +7659,20 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) { __ B(slow_path->GetEntryLabel()); break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, out); + __ Sltiu(out, out, 1); + break; + } } __ Bind(&done); diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 1f1743ff9e..2e7c736dbd 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -237,6 +237,7 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator { private: void GenerateClassInitializationCheck(SlowPathCodeMIPS* slow_path, Register class_reg); void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); + void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, Register temp); void HandleBinaryOp(HBinaryOperation* operation); void HandleCondition(HCondition* instruction); void HandleShift(HBinaryOperation* operation); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index f8851b4eea..5b07b55cbb 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1794,6 +1794,34 @@ void InstructionCodeGeneratorMIPS64::GenerateClassInitializationCheck(SlowPathCo __ Bind(slow_path->GetExitLabel()); } +void InstructionCodeGeneratorMIPS64::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, + GpuRegister temp) { + uint32_t path_to_root = check->GetBitstringPathToRoot(); + uint32_t mask = check->GetBitstringMask(); + DCHECK(IsPowerOfTwo(mask + 1)); + size_t mask_bits = WhichPowerOf2(mask + 1); + + if (mask_bits == 16u) { + // Load only the bitstring part of the status word. + __ LoadFromOffset( + kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value()); + // Compare the bitstring bits using XOR. + __ Xori(temp, temp, dchecked_integral_cast(path_to_root)); + } else { + // /* uint32_t */ temp = temp->status_ + __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value()); + // Compare the bitstring bits using XOR. + if (IsUint<16>(path_to_root)) { + __ Xori(temp, temp, dchecked_integral_cast(path_to_root)); + } else { + __ LoadConst32(TMP, path_to_root); + __ Xor(temp, temp, TMP); + } + // Shift out bits that do not contribute to the comparison. + __ Sll(temp, temp, 32 - mask_bits); + } +} + void InstructionCodeGeneratorMIPS64::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) { __ Sync(0); // only stype 0 is supported } @@ -2854,7 +2882,13 @@ void LocationsBuilderMIPS64::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } @@ -2863,7 +2897,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); GpuRegister obj = obj_loc.AsRegister(); - GpuRegister cls = locations->InAt(1).AsRegister(); + Location cls = locations->InAt(1); Location temp_loc = locations->GetTemp(0); GpuRegister temp = temp_loc.AsRegister(); const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); @@ -2902,7 +2936,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { kWithoutReadBarrier); // Jump to slow path for throwing the exception or doing a // more involved array check. - __ Bnec(temp, cls, slow_path->GetEntryLabel()); + __ Bnec(temp, cls.AsRegister(), slow_path->GetEntryLabel()); break; } @@ -2928,7 +2962,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { // exception. __ Beqzc(temp, slow_path->GetEntryLabel()); // Otherwise, compare the classes. - __ Bnec(temp, cls, &loop); + __ Bnec(temp, cls.AsRegister(), &loop); break; } @@ -2943,7 +2977,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { // Walk over the class hierarchy to find a match. Mips64Label loop; __ Bind(&loop); - __ Beqc(temp, cls, &done); + __ Beqc(temp, cls.AsRegister(), &done); // /* HeapReference */ temp = temp->super_class_ GenerateReferenceLoadOneRegister(instruction, temp_loc, @@ -2966,7 +3000,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { maybe_temp2_loc, kWithoutReadBarrier); // Do an exact check. - __ Beqc(temp, cls, &done); + __ Beqc(temp, cls.AsRegister(), &done); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference */ temp = temp->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -3025,7 +3059,21 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { __ Daddiu(temp, temp, 2 * kHeapReferenceSize); __ Addiu(TMP, TMP, -2); // Compare the classes and continue the loop if they do not match. - __ Bnec(AT, cls, &loop); + __ Bnec(AT, cls.AsRegister(), &loop); + break; + } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + maybe_temp2_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, temp); + __ Bnezc(temp, slow_path->GetEntryLabel()); break; } } @@ -5529,6 +5577,8 @@ void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; + case TypeCheckKind::kBitstringCheck: + break; } LocationSummary* locations = @@ -5537,7 +5587,13 @@ void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } // The output does overlap inputs. // Note that TypeCheckSlowPathMIPS64 uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -5549,7 +5605,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); GpuRegister obj = obj_loc.AsRegister(); - GpuRegister cls = locations->InAt(1).AsRegister(); + Location cls = locations->InAt(1); Location out_loc = locations->Out(); GpuRegister out = out_loc.AsRegister(); const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -5581,7 +5637,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { maybe_temp_loc, read_barrier_option); // Classes must be equal for the instanceof to succeed. - __ Xor(out, out, cls); + __ Xor(out, out, cls.AsRegister()); __ Sltiu(out, out, 1); break; } @@ -5608,7 +5664,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { read_barrier_option); // If `out` is null, we use it for the result, and jump to `done`. __ Beqzc(out, &done); - __ Bnec(out, cls, &loop); + __ Bnec(out, cls.AsRegister(), &loop); __ LoadConst32(out, 1); break; } @@ -5626,7 +5682,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { // Walk over the class hierarchy to find a match. Mips64Label loop, success; __ Bind(&loop); - __ Beqc(out, cls, &success); + __ Beqc(out, cls.AsRegister(), &success); // /* HeapReference */ out = out->super_class_ GenerateReferenceLoadOneRegister(instruction, out_loc, @@ -5653,7 +5709,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { read_barrier_option); // Do an exact check. Mips64Label success; - __ Beqc(out, cls, &success); + __ Beqc(out, cls.AsRegister(), &success); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference */ out = out->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -5685,7 +5741,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS64( instruction, /* is_fatal */ false); codegen_->AddSlowPath(slow_path); - __ Bnec(out, cls, slow_path->GetEntryLabel()); + __ Bnec(out, cls.AsRegister(), slow_path->GetEntryLabel()); __ LoadConst32(out, 1); break; } @@ -5717,6 +5773,20 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { __ Bc(slow_path->GetEntryLabel()); break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, out); + __ Sltiu(out, out, 1); + break; + } } __ Bind(&done); diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 74c947e5d5..6e69e4611a 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -233,6 +233,7 @@ class InstructionCodeGeneratorMIPS64 : public InstructionCodeGenerator { private: void GenerateClassInitializationCheck(SlowPathCodeMIPS64* slow_path, GpuRegister class_reg); + void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, GpuRegister temp); void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); void HandleBinaryOp(HBinaryOperation* operation); void HandleCondition(HCondition* instruction); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 4818084a72..4053f557d9 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -6571,6 +6571,26 @@ void InstructionCodeGeneratorX86::GenerateClassInitializationCheck( // No need for memory fence, thanks to the X86 memory model. } +void InstructionCodeGeneratorX86::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, + Register temp) { + uint32_t path_to_root = check->GetBitstringPathToRoot(); + uint32_t mask = check->GetBitstringMask(); + DCHECK(IsPowerOfTwo(mask + 1)); + size_t mask_bits = WhichPowerOf2(mask + 1); + + if (mask_bits == 16u) { + // Compare the bitstring in memory. + __ cmpw(Address(temp, mirror::Class::StatusOffset()), Immediate(path_to_root)); + } else { + // /* uint32_t */ temp = temp->status_ + __ movl(temp, Address(temp, mirror::Class::StatusOffset())); + // Compare the bitstring bits using SUB. + __ subl(temp, Immediate(path_to_root)); + // Shift out bits that do not contribute to the comparison. + __ shll(temp, Immediate(32u - mask_bits)); + } +} + HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { switch (desired_string_load_kind) { @@ -6764,6 +6784,8 @@ void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; + case TypeCheckKind::kBitstringCheck: + break; } LocationSummary* locations = @@ -6772,7 +6794,13 @@ void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::Any()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::Any()); + } // Note that TypeCheckSlowPathX86 uses this "out" register too. locations->SetOut(Location::RequiresRegister()); // When read barriers are enabled, we need a temporary register for some cases. @@ -6993,6 +7021,21 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { } break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, out); + __ j(kNotEqual, &zero); + __ movl(out, Immediate(1)); + __ jmp(&done); + break; + } } if (zero.IsLinked()) { @@ -7019,6 +7062,10 @@ void LocationsBuilderX86::VisitCheckCast(HCheckCast* instruction) { // Require a register for the interface check since there is a loop that compares the class to // a memory address. locations->SetInAt(1, Location::RequiresRegister()); + } else if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); } else { locations->SetInAt(1, Location::Any()); } @@ -7238,6 +7285,19 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { __ MaybeUnpoisonHeapReference(cls.AsRegister()); break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, temp); + __ j(kNotEqual, type_check_slow_path->GetEntryLabel()); + break; + } } __ Bind(&done); diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 9c537a7371..6c76e27d35 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -211,6 +211,7 @@ class InstructionCodeGeneratorX86 : public InstructionCodeGenerator { // the suspend call. void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); void GenerateClassInitializationCheck(SlowPathCode* slow_path, Register class_reg); + void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, Register temp); void HandleBitwiseOperation(HBinaryOperation* instruction); void GenerateDivRemIntegral(HBinaryOperation* instruction); void DivRemOneOrMinusOne(HBinaryOperation* instruction); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index c378c5b957..496d79d6c8 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -5716,6 +5716,26 @@ void InstructionCodeGeneratorX86_64::GenerateClassInitializationCheck( // No need for memory fence, thanks to the x86-64 memory model. } +void InstructionCodeGeneratorX86_64::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, + CpuRegister temp) { + uint32_t path_to_root = check->GetBitstringPathToRoot(); + uint32_t mask = check->GetBitstringMask(); + DCHECK(IsPowerOfTwo(mask + 1)); + size_t mask_bits = WhichPowerOf2(mask + 1); + + if (mask_bits == 16u) { + // Compare the bitstring in memory. + __ cmpw(Address(temp, mirror::Class::StatusOffset()), Immediate(path_to_root)); + } else { + // /* uint32_t */ temp = temp->status_ + __ movl(temp, Address(temp, mirror::Class::StatusOffset())); + // Compare the bitstring bits using SUB. + __ subl(temp, Immediate(path_to_root)); + // Shift out bits that do not contribute to the comparison. + __ shll(temp, Immediate(32u - mask_bits)); + } +} + HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind( HLoadClass::LoadKind desired_class_load_kind) { switch (desired_class_load_kind) { @@ -6082,6 +6102,8 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kInterfaceCheck: call_kind = LocationSummary::kCallOnSlowPath; break; + case TypeCheckKind::kBitstringCheck: + break; } LocationSummary* locations = @@ -6090,7 +6112,13 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::Any()); + if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); + } else { + locations->SetInAt(1, Location::Any()); + } // Note that TypeCheckSlowPathX86_64 uses this "out" register too. locations->SetOut(Location::RequiresRegister()); // When read barriers are enabled, we need a temporary register for @@ -6319,6 +6347,27 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } break; } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, out); + if (zero.IsLinked()) { + __ j(kNotEqual, &zero); + __ movl(out, Immediate(1)); + __ jmp(&done); + } else { + __ setcc(kEqual, out); + // setcc only sets the low byte. + __ andl(out, Immediate(1)); + } + break; + } } if (zero.IsLinked()) { @@ -6345,6 +6394,10 @@ void LocationsBuilderX86_64::VisitCheckCast(HCheckCast* instruction) { // Require a register for the interface check since there is a loop that compares the class to // a memory address. locations->SetInAt(1, Location::RequiresRegister()); + } else if (type_check_kind == TypeCheckKind::kBitstringCheck) { + locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); + locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); + locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); } else { locations->SetInAt(1, Location::Any()); } @@ -6531,7 +6584,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { break; } - case TypeCheckKind::kInterfaceCheck: + case TypeCheckKind::kInterfaceCheck: { // Fast path for the interface check. Try to avoid read barriers to improve the fast path. // We can not get false positives by doing this. // /* HeapReference */ temp = obj->klass_ @@ -6567,6 +6620,20 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { // If `cls` was poisoned above, unpoison it. __ MaybeUnpoisonHeapReference(cls.AsRegister()); break; + } + + case TypeCheckKind::kBitstringCheck: { + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kWithoutReadBarrier); + + GenerateBitstringTypeCheckCompare(instruction, temp); + __ j(kNotEqual, type_check_slow_path->GetEntryLabel()); + break; + } } if (done.IsLinked()) { diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index e8d1efe702..9a4c53b524 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -208,6 +208,7 @@ class InstructionCodeGeneratorX86_64 : public InstructionCodeGenerator { // the suspend call. void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor); void GenerateClassInitializationCheck(SlowPathCode* slow_path, CpuRegister class_reg); + void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, CpuRegister temp); void HandleBitwiseOperation(HBinaryOperation* operation); void GenerateRemFP(HRem* rem); void DivRemOneOrMinusOne(HBinaryOperation* instruction); diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index c88baa8610..fbcbe3608e 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -25,6 +25,11 @@ #include "base/bit_vector-inl.h" #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" +#include "handle.h" +#include "mirror/class.h" +#include "obj_ptr-inl.h" +#include "scoped_thread_state_change-inl.h" +#include "subtype_check.h" namespace art { @@ -548,30 +553,85 @@ void GraphChecker::VisitReturnVoid(HReturnVoid* ret) { } } -void GraphChecker::VisitCheckCast(HCheckCast* check) { - VisitInstruction(check); - HInstruction* input = check->InputAt(1); - if (!input->IsLoadClass()) { - AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.", +void GraphChecker::CheckTypeCheckBitstringInput(HTypeCheckInstruction* check, + size_t input_pos, + bool check_value, + uint32_t expected_value, + const char* name) { + if (!check->InputAt(input_pos)->IsIntConstant()) { + AddError(StringPrintf("%s:%d (bitstring) expects a HIntConstant input %zu (%s), not %s:%d.", check->DebugName(), check->GetId(), - input->DebugName(), - input->GetId())); + input_pos, + name, + check->InputAt(2)->DebugName(), + check->InputAt(2)->GetId())); + } else if (check_value) { + uint32_t actual_value = + static_cast(check->InputAt(input_pos)->AsIntConstant()->GetValue()); + if (actual_value != expected_value) { + AddError(StringPrintf("%s:%d (bitstring) has %s 0x%x, not 0x%x as expected.", + check->DebugName(), + check->GetId(), + name, + actual_value, + expected_value)); + } } } -void GraphChecker::VisitInstanceOf(HInstanceOf* instruction) { - VisitInstruction(instruction); - HInstruction* input = instruction->InputAt(1); - if (!input->IsLoadClass()) { - AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.", - instruction->DebugName(), - instruction->GetId(), - input->DebugName(), - input->GetId())); +void GraphChecker::HandleTypeCheckInstruction(HTypeCheckInstruction* check) { + VisitInstruction(check); + HInstruction* input = check->InputAt(1); + if (check->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { + if (!input->IsNullConstant()) { + AddError(StringPrintf("%s:%d (bitstring) expects a HNullConstant as second input, not %s:%d.", + check->DebugName(), + check->GetId(), + input->DebugName(), + input->GetId())); + } + bool check_values = false; + BitString::StorageType expected_path_to_root = 0u; + BitString::StorageType expected_mask = 0u; + { + ScopedObjectAccess soa(Thread::Current()); + ObjPtr klass = check->GetClass().Get(); + MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); + SubtypeCheckInfo::State state = SubtypeCheck>::GetState(klass); + if (state == SubtypeCheckInfo::kAssigned) { + expected_path_to_root = + SubtypeCheck>::GetEncodedPathToRootForTarget(klass); + expected_mask = SubtypeCheck>::GetEncodedPathToRootMask(klass); + check_values = true; + } else { + AddError(StringPrintf("%s:%d (bitstring) references a class with unassigned bitstring.", + check->DebugName(), + check->GetId())); + } + } + CheckTypeCheckBitstringInput( + check, /* input_pos */ 2, check_values, expected_path_to_root, "path_to_root"); + CheckTypeCheckBitstringInput(check, /* input_pos */ 3, check_values, expected_mask, "mask"); + } else { + if (!input->IsLoadClass()) { + AddError(StringPrintf("%s:%d (classic) expects a HLoadClass as second input, not %s:%d.", + check->DebugName(), + check->GetId(), + input->DebugName(), + input->GetId())); + } } } +void GraphChecker::VisitCheckCast(HCheckCast* check) { + HandleTypeCheckInstruction(check); +} + +void GraphChecker::VisitInstanceOf(HInstanceOf* instruction) { + HandleTypeCheckInstruction(instruction); +} + void GraphChecker::HandleLoop(HBasicBlock* loop_header) { int id = loop_header->GetBlockId(); HLoopInformation* loop_information = loop_header->GetLoopInformation(); diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h index 0f0b49d240..dbedc40518 100644 --- a/compiler/optimizing/graph_checker.h +++ b/compiler/optimizing/graph_checker.h @@ -71,6 +71,12 @@ class GraphChecker : public HGraphDelegateVisitor { void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE; void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE; + void CheckTypeCheckBitstringInput(HTypeCheckInstruction* check, + size_t input_pos, + bool check_value, + uint32_t expected_value, + const char* name); + void HandleTypeCheckInstruction(HTypeCheckInstruction* instruction); void HandleLoop(HBasicBlock* loop_header); void HandleBooleanInput(HInstruction* instruction, size_t input_index); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 5ff31cead5..6cb1881d7d 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -390,16 +390,23 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { StartAttributeStream("load_kind") << load_string->GetLoadKind(); } - void VisitCheckCast(HCheckCast* check_cast) OVERRIDE { - StartAttributeStream("check_kind") << check_cast->GetTypeCheckKind(); + void HandleTypeCheckInstruction(HTypeCheckInstruction* check) { + StartAttributeStream("check_kind") << check->GetTypeCheckKind(); StartAttributeStream("must_do_null_check") << std::boolalpha - << check_cast->MustDoNullCheck() << std::noboolalpha; + << check->MustDoNullCheck() << std::noboolalpha; + if (check->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { + StartAttributeStream("path_to_root") << std::hex + << "0x" << check->GetBitstringPathToRoot() << std::dec; + StartAttributeStream("mask") << std::hex << "0x" << check->GetBitstringMask() << std::dec; + } + } + + void VisitCheckCast(HCheckCast* check_cast) OVERRIDE { + HandleTypeCheckInstruction(check_cast); } void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE { - StartAttributeStream("check_kind") << instance_of->GetTypeCheckKind(); - StartAttributeStream("must_do_null_check") << std::boolalpha - << instance_of->MustDoNullCheck() << std::noboolalpha; + HandleTypeCheckInstruction(instance_of); } void VisitArrayLength(HArrayLength* array_length) OVERRIDE { @@ -641,20 +648,32 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { << std::boolalpha << loop_info->IsIrreducible() << std::noboolalpha; } + // For the builder and the inliner, we want to add extra information on HInstructions + // that have reference types, and also HInstanceOf/HCheckcast. if ((IsPass(HGraphBuilder::kBuilderPassName) || IsPass(HInliner::kInlinerPassName)) - && (instruction->GetType() == DataType::Type::kReference)) { - ReferenceTypeInfo info = instruction->IsLoadClass() - ? instruction->AsLoadClass()->GetLoadedClassRTI() - : instruction->GetReferenceTypeInfo(); + && (instruction->GetType() == DataType::Type::kReference || + instruction->IsInstanceOf() || + instruction->IsCheckCast())) { + ReferenceTypeInfo info = (instruction->GetType() == DataType::Type::kReference) + ? instruction->IsLoadClass() + ? instruction->AsLoadClass()->GetLoadedClassRTI() + : instruction->GetReferenceTypeInfo() + : instruction->IsInstanceOf() + ? instruction->AsInstanceOf()->GetTargetClassRTI() + : instruction->AsCheckCast()->GetTargetClassRTI(); ScopedObjectAccess soa(Thread::Current()); if (info.IsValid()) { StartAttributeStream("klass") << mirror::Class::PrettyDescriptor(info.GetTypeHandle().Get()); - StartAttributeStream("can_be_null") - << std::boolalpha << instruction->CanBeNull() << std::noboolalpha; + if (instruction->GetType() == DataType::Type::kReference) { + StartAttributeStream("can_be_null") + << std::boolalpha << instruction->CanBeNull() << std::noboolalpha; + } StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha; - } else if (instruction->IsLoadClass()) { + } else if (instruction->IsLoadClass() || + instruction->IsInstanceOf() || + instruction->IsCheckCast()) { StartAttributeStream("klass") << "unresolved"; } else { // The NullConstant may be added to the graph during other passes that happen between diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index c7aef3779d..9647dd5d41 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1815,29 +1815,6 @@ void HInstructionBuilder::BuildFillWideArrayData(HInstruction* object, } } -static TypeCheckKind ComputeTypeCheckKind(Handle cls) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (cls == nullptr) { - return TypeCheckKind::kUnresolvedCheck; - } else if (cls->IsInterface()) { - return TypeCheckKind::kInterfaceCheck; - } else if (cls->IsArrayClass()) { - if (cls->GetComponentType()->IsObjectClass()) { - return TypeCheckKind::kArrayObjectCheck; - } else if (cls->CannotBeAssignedFromOtherTypes()) { - return TypeCheckKind::kExactCheck; - } else { - return TypeCheckKind::kArrayCheck; - } - } else if (cls->IsFinal()) { - return TypeCheckKind::kExactCheck; - } else if (cls->IsAbstract()) { - return TypeCheckKind::kAbstractClassCheck; - } else { - return TypeCheckKind::kClassHierarchyCheck; - } -} - void HInstructionBuilder::BuildLoadString(dex::StringIndex string_index, uint32_t dex_pc) { HLoadString* load_string = new (allocator_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc); @@ -1852,22 +1829,8 @@ void HInstructionBuilder::BuildLoadString(dex::StringIndex string_index, uint32_ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc) { ScopedObjectAccess soa(Thread::Current()); const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); - Handle class_loader = dex_compilation_unit_->GetClassLoader(); - Handle klass = handles_->NewHandle(compiler_driver_->ResolveClass( - soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_)); - - bool needs_access_check = true; - if (klass != nullptr) { - if (klass->IsPublic()) { - needs_access_check = false; - } else { - ObjPtr compiling_class = GetCompilingClass(); - if (compiling_class != nullptr && compiling_class->CanAccess(klass.Get())) { - needs_access_check = false; - } - } - } - + Handle klass = ResolveClass(soa, type_index); + bool needs_access_check = LoadClassNeedsAccessCheck(klass); return BuildLoadClass(type_index, dex_file, klass, dex_pc, needs_access_check); } @@ -1912,25 +1875,83 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, return load_class; } +Handle HInstructionBuilder::ResolveClass(ScopedObjectAccess& soa, + dex::TypeIndex type_index) { + Handle class_loader = dex_compilation_unit_->GetClassLoader(); + ObjPtr klass = compiler_driver_->ResolveClass( + soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_); + // TODO: Avoid creating excessive handles if the method references the same class repeatedly. + // (Use a map on the local_allocator_.) + return handles_->NewHandle(klass); +} + +bool HInstructionBuilder::LoadClassNeedsAccessCheck(Handle klass) { + if (klass == nullptr) { + return true; + } else if (klass->IsPublic()) { + return false; + } else { + ObjPtr compiling_class = GetCompilingClass(); + return compiling_class == nullptr || !compiling_class->CanAccess(klass.Get()); + } +} + void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, uint8_t destination, uint8_t reference, dex::TypeIndex type_index, uint32_t dex_pc) { HInstruction* object = LoadLocal(reference, DataType::Type::kReference); - HLoadClass* cls = BuildLoadClass(type_index, dex_pc); ScopedObjectAccess soa(Thread::Current()); - TypeCheckKind check_kind = ComputeTypeCheckKind(cls->GetClass()); + const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); + Handle klass = ResolveClass(soa, type_index); + bool needs_access_check = LoadClassNeedsAccessCheck(klass); + TypeCheckKind check_kind = HSharpening::ComputeTypeCheckKind( + klass.Get(), code_generator_, compiler_driver_, needs_access_check); + + HInstruction* class_or_null = nullptr; + HIntConstant* bitstring_path_to_root = nullptr; + HIntConstant* bitstring_mask = nullptr; + if (check_kind == TypeCheckKind::kBitstringCheck) { + // TODO: Allow using the bitstring check also if we need an access check. + DCHECK(!needs_access_check); + class_or_null = graph_->GetNullConstant(dex_pc); + MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); + uint32_t path_to_root = + SubtypeCheck>::GetEncodedPathToRootForTarget(klass.Get()); + uint32_t mask = SubtypeCheck>::GetEncodedPathToRootMask(klass.Get()); + bitstring_path_to_root = graph_->GetIntConstant(static_cast(path_to_root), dex_pc); + bitstring_mask = graph_->GetIntConstant(static_cast(mask), dex_pc); + } else { + class_or_null = BuildLoadClass(type_index, dex_file, klass, dex_pc, needs_access_check); + } + DCHECK(class_or_null != nullptr); + if (instruction.Opcode() == Instruction::INSTANCE_OF) { - AppendInstruction(new (allocator_) HInstanceOf(object, cls, check_kind, dex_pc)); + AppendInstruction(new (allocator_) HInstanceOf(object, + class_or_null, + check_kind, + klass, + dex_pc, + allocator_, + bitstring_path_to_root, + bitstring_mask)); UpdateLocal(destination, current_block_->GetLastInstruction()); } else { DCHECK_EQ(instruction.Opcode(), Instruction::CHECK_CAST); // We emit a CheckCast followed by a BoundType. CheckCast is a statement // which may throw. If it succeeds BoundType sets the new type of `object` // for all subsequent uses. - AppendInstruction(new (allocator_) HCheckCast(object, cls, check_kind, dex_pc)); + AppendInstruction( + new (allocator_) HCheckCast(object, + class_or_null, + check_kind, + klass, + dex_pc, + allocator_, + bitstring_path_to_root, + bitstring_mask)); AppendInstruction(new (allocator_) HBoundType(object, dex_pc)); UpdateLocal(reference, current_block_->GetLastInstruction()); } diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index 4428c53277..f78829232d 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -39,6 +39,7 @@ class DexCompilationUnit; class HBasicBlockBuilder; class Instruction; class OptimizingCompilerStats; +class ScopedObjectAccess; class SsaBuilder; class VariableSizedHandleScope; @@ -232,6 +233,12 @@ class HInstructionBuilder : public ValueObject { bool needs_access_check) REQUIRES_SHARED(Locks::mutator_lock_); + Handle ResolveClass(ScopedObjectAccess& soa, dex::TypeIndex type_index) + REQUIRES_SHARED(Locks::mutator_lock_); + + bool LoadClassNeedsAccessCheck(Handle klass) + REQUIRES_SHARED(Locks::mutator_lock_); + // Returns the outer-most compiling method's class. ObjPtr GetOutermostCompilingClass() const; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 0b2297d157..676fe6bcb7 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -579,7 +579,9 @@ bool InstructionSimplifierVisitor::CanEnsureNotNullAt(HInstruction* input, HInst // Returns whether doing a type test between the class of `object` against `klass` has // a statically known outcome. The result of the test is stored in `outcome`. -static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bool* outcome) { +static bool TypeCheckHasKnownOutcome(ReferenceTypeInfo class_rti, + HInstruction* object, + /*out*/bool* outcome) { DCHECK(!object->IsNullConstant()) << "Null constants should be special cased"; ReferenceTypeInfo obj_rti = object->GetReferenceTypeInfo(); ScopedObjectAccess soa(Thread::Current()); @@ -589,7 +591,6 @@ static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bo return false; } - ReferenceTypeInfo class_rti = klass->GetLoadedClassRTI(); if (!class_rti.IsValid()) { // Happens when the loaded class is unresolved. return false; @@ -614,8 +615,8 @@ static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bo void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { HInstruction* object = check_cast->InputAt(0); - HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass(); - if (load_class->NeedsAccessCheck()) { + if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck && + check_cast->GetTargetClass()->NeedsAccessCheck()) { // If we need to perform an access check we cannot remove the instruction. return; } @@ -633,15 +634,18 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { // Note: The `outcome` is initialized to please valgrind - the compiler can reorder // the return value check with the `outcome` check, b/27651442 . bool outcome = false; - if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) { + if (TypeCheckHasKnownOutcome(check_cast->GetTargetClassRTI(), object, &outcome)) { if (outcome) { check_cast->GetBlock()->RemoveInstruction(check_cast); MaybeRecordStat(stats_, MethodCompilationStat::kRemovedCheckedCast); - if (!load_class->HasUses()) { - // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. - // However, here we know that it cannot because the checkcast was successfull, hence - // the class was already loaded. - load_class->GetBlock()->RemoveInstruction(load_class); + if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) { + HLoadClass* load_class = check_cast->GetTargetClass(); + if (!load_class->HasUses()) { + // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. + // However, here we know that it cannot because the checkcast was successfull, hence + // the class was already loaded. + load_class->GetBlock()->RemoveInstruction(load_class); + } } } else { // Don't do anything for exceptional cases for now. Ideally we should remove @@ -652,8 +656,8 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { HInstruction* object = instruction->InputAt(0); - HLoadClass* load_class = instruction->InputAt(1)->AsLoadClass(); - if (load_class->NeedsAccessCheck()) { + if (instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck && + instruction->GetTargetClass()->NeedsAccessCheck()) { // If we need to perform an access check we cannot remove the instruction. return; } @@ -676,7 +680,7 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { // Note: The `outcome` is initialized to please valgrind - the compiler can reorder // the return value check with the `outcome` check, b/27651442 . bool outcome = false; - if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) { + if (TypeCheckHasKnownOutcome(instruction->GetTargetClassRTI(), object, &outcome)) { MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf); if (outcome && can_be_null) { // Type test will succeed, we just need a null test. @@ -689,11 +693,14 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { } RecordSimplification(); instruction->GetBlock()->RemoveInstruction(instruction); - if (outcome && !load_class->HasUses()) { - // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. - // However, here we know that it cannot because the instanceof check was successfull, hence - // the class was already loaded. - load_class->GetBlock()->RemoveInstruction(load_class); + if (outcome && instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) { + HLoadClass* load_class = instruction->GetTargetClass(); + if (!load_class->HasUses()) { + // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. + // However, here we know that it cannot because the instanceof check was successfull, hence + // the class was already loaded. + load_class->GetBlock()->RemoveInstruction(load_class); + } } } } diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index d3212cbbc0..f784f8f7f3 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -3103,6 +3103,8 @@ std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs) { return os << "array_object_check"; case TypeCheckKind::kArrayCheck: return os << "array_check"; + case TypeCheckKind::kBitstringCheck: + return os << "bitstring_check"; default: LOG(FATAL) << "Unknown TypeCheckKind: " << static_cast(rhs); UNREACHABLE(); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index a8fcea2097..79d733060b 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -6178,8 +6178,7 @@ class HLoadClass FINAL : public HInstruction { special_input_(HUserRecord(current_method)), type_index_(type_index), dex_file_(dex_file), - klass_(klass), - loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) { + klass_(klass) { // Referrers class should not need access check. We never inline unverified // methods so we can't possibly end up in this situation. DCHECK(!is_referrers_class || !needs_access_check); @@ -6189,6 +6188,7 @@ class HLoadClass FINAL : public HInstruction { SetPackedFlag(needs_access_check); SetPackedFlag(false); SetPackedFlag(false); + SetPackedFlag(false); } bool IsClonable() const OVERRIDE { return true; } @@ -6243,13 +6243,18 @@ class HLoadClass FINAL : public HInstruction { } ReferenceTypeInfo GetLoadedClassRTI() { - return loaded_class_rti_; + if (GetPackedFlag()) { + // Note: The is_exact flag from the return value should not be used. + return ReferenceTypeInfo::CreateUnchecked(klass_, /* is_exact */ true); + } else { + return ReferenceTypeInfo::CreateInvalid(); + } } - void SetLoadedClassRTI(ReferenceTypeInfo rti) { - // Make sure we only set exact types (the loaded class should never be merged). - DCHECK(rti.IsExact()); - loaded_class_rti_ = rti; + // Loaded class RTI is marked as valid by RTP if the klass_ is admissible. + void SetValidLoadedClassRTI() REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(klass_ != nullptr); + SetPackedFlag(true); } dex::TypeIndex GetTypeIndex() const { return type_index_; } @@ -6302,7 +6307,8 @@ class HLoadClass FINAL : public HInstruction { static constexpr size_t kFieldLoadKind = kFlagGenerateClInitCheck + 1; static constexpr size_t kFieldLoadKindSize = MinimumBitsToStore(static_cast(LoadKind::kLast)); - static constexpr size_t kNumberOfLoadClassPackedBits = kFieldLoadKind + kFieldLoadKindSize; + static constexpr size_t kFlagValidLoadedClassRTI = kFieldLoadKind + kFieldLoadKindSize; + static constexpr size_t kNumberOfLoadClassPackedBits = kFlagValidLoadedClassRTI + 1; static_assert(kNumberOfLoadClassPackedBits < kMaxNumberOfPackedBits, "Too many packed fields."); using LoadKindField = BitField; @@ -6329,8 +6335,6 @@ class HLoadClass FINAL : public HInstruction { const DexFile& dex_file_; Handle klass_; - - ReferenceTypeInfo loaded_class_rti_; }; std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs); @@ -6882,72 +6886,159 @@ enum class TypeCheckKind { kInterfaceCheck, // No optimization yet when checking against an interface. kArrayObjectCheck, // Can just check if the array is not primitive. kArrayCheck, // No optimization yet when checking against a generic array. + kBitstringCheck, // Compare the type check bitstring. kLast = kArrayCheck }; std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs); -class HInstanceOf FINAL : public HExpression<2> { +// Note: HTypeCheckInstruction is just a helper class, not an abstract instruction with an +// `IsTypeCheckInstruction()`. (New virtual methods in the HInstruction class have a high cost.) +class HTypeCheckInstruction : public HVariableInputSizeInstruction { public: - HInstanceOf(HInstruction* object, - HLoadClass* target_class, - TypeCheckKind check_kind, - uint32_t dex_pc) - : HExpression(kInstanceOf, - DataType::Type::kBool, - SideEffectsForArchRuntimeCalls(check_kind), - dex_pc) { + HTypeCheckInstruction(InstructionKind kind, + HInstruction* object, + HInstruction* target_class_or_null, + TypeCheckKind check_kind, + Handle klass, + uint32_t dex_pc, + ArenaAllocator* allocator, + HIntConstant* bitstring_path_to_root, + HIntConstant* bitstring_mask, + SideEffects side_effects) + : HVariableInputSizeInstruction( + kind, + side_effects, + dex_pc, + allocator, + /* number_of_inputs */ check_kind == TypeCheckKind::kBitstringCheck ? 4u : 2u, + kArenaAllocTypeCheckInputs), + klass_(klass) { SetPackedField(check_kind); SetPackedFlag(true); + SetPackedFlag(false); SetRawInputAt(0, object); - SetRawInputAt(1, target_class); + SetRawInputAt(1, target_class_or_null); + DCHECK_EQ(check_kind == TypeCheckKind::kBitstringCheck, bitstring_path_to_root != nullptr); + DCHECK_EQ(check_kind == TypeCheckKind::kBitstringCheck, bitstring_mask != nullptr); + if (check_kind == TypeCheckKind::kBitstringCheck) { + DCHECK(target_class_or_null->IsNullConstant()); + SetRawInputAt(2, bitstring_path_to_root); + SetRawInputAt(3, bitstring_mask); + } else { + DCHECK(target_class_or_null->IsLoadClass()); + } } HLoadClass* GetTargetClass() const { + DCHECK_NE(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck); HInstruction* load_class = InputAt(1); DCHECK(load_class->IsLoadClass()); return load_class->AsLoadClass(); } - bool IsClonable() const OVERRIDE { return true; } - bool CanBeMoved() const OVERRIDE { return true; } + uint32_t GetBitstringPathToRoot() const { + DCHECK_EQ(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck); + HInstruction* path_to_root = InputAt(2); + DCHECK(path_to_root->IsIntConstant()); + return static_cast(path_to_root->AsIntConstant()->GetValue()); + } - bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { - return true; + uint32_t GetBitstringMask() const { + DCHECK_EQ(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck); + HInstruction* mask = InputAt(3); + DCHECK(mask->IsIntConstant()); + return static_cast(mask->AsIntConstant()->GetValue()); } - bool NeedsEnvironment() const OVERRIDE { - return CanCallRuntime(GetTypeCheckKind()); + bool IsClonable() const OVERRIDE { return true; } + bool CanBeMoved() const OVERRIDE { return true; } + + bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { + DCHECK(other->IsInstanceOf() || other->IsCheckCast()) << other->DebugName(); + return GetPackedFields() == down_cast(other)->GetPackedFields(); } - // Used only in code generation. bool MustDoNullCheck() const { return GetPackedFlag(); } void ClearMustDoNullCheck() { SetPackedFlag(false); } TypeCheckKind GetTypeCheckKind() const { return GetPackedField(); } bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } - static bool CanCallRuntime(TypeCheckKind check_kind) { - // Mips currently does runtime calls for any other checks. - return check_kind != TypeCheckKind::kExactCheck; + ReferenceTypeInfo GetTargetClassRTI() { + if (GetPackedFlag()) { + // Note: The is_exact flag from the return value should not be used. + return ReferenceTypeInfo::CreateUnchecked(klass_, /* is_exact */ true); + } else { + return ReferenceTypeInfo::CreateInvalid(); + } } - static SideEffects SideEffectsForArchRuntimeCalls(TypeCheckKind check_kind) { - return CanCallRuntime(check_kind) ? SideEffects::CanTriggerGC() : SideEffects::None(); + // Target class RTI is marked as valid by RTP if the klass_ is admissible. + void SetValidTargetClassRTI() REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(klass_ != nullptr); + SetPackedFlag(true); } - DECLARE_INSTRUCTION(InstanceOf); + Handle GetClass() const { + return klass_; + } protected: - DEFAULT_COPY_CONSTRUCTOR(InstanceOf); + DEFAULT_COPY_CONSTRUCTOR(TypeCheckInstruction); private: - static constexpr size_t kFieldTypeCheckKind = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits; static constexpr size_t kFieldTypeCheckKindSize = MinimumBitsToStore(static_cast(TypeCheckKind::kLast)); static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; - static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagMustDoNullCheck + 1; + static constexpr size_t kFlagValidTargetClassRTI = kFlagMustDoNullCheck + 1; + static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagValidTargetClassRTI + 1; static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using TypeCheckKindField = BitField; + + Handle klass_; +}; + +class HInstanceOf FINAL : public HTypeCheckInstruction { + public: + HInstanceOf(HInstruction* object, + HInstruction* target_class_or_null, + TypeCheckKind check_kind, + Handle klass, + uint32_t dex_pc, + ArenaAllocator* allocator, + HIntConstant* bitstring_path_to_root, + HIntConstant* bitstring_mask) + : HTypeCheckInstruction(kInstanceOf, + object, + target_class_or_null, + check_kind, + klass, + dex_pc, + allocator, + bitstring_path_to_root, + bitstring_mask, + SideEffectsForArchRuntimeCalls(check_kind)) {} + + DataType::Type GetType() const OVERRIDE { return DataType::Type::kBool; } + + bool NeedsEnvironment() const OVERRIDE { + return CanCallRuntime(GetTypeCheckKind()); + } + + static bool CanCallRuntime(TypeCheckKind check_kind) { + // Mips currently does runtime calls for any other checks. + return check_kind != TypeCheckKind::kExactCheck; + } + + static SideEffects SideEffectsForArchRuntimeCalls(TypeCheckKind check_kind) { + return CanCallRuntime(check_kind) ? SideEffects::CanTriggerGC() : SideEffects::None(); + } + + DECLARE_INSTRUCTION(InstanceOf); + + protected: + DEFAULT_COPY_CONSTRUCTOR(InstanceOf); }; class HBoundType FINAL : public HExpression<1> { @@ -6997,31 +7088,26 @@ class HBoundType FINAL : public HExpression<1> { ReferenceTypeInfo upper_bound_; }; -class HCheckCast FINAL : public HTemplateInstruction<2> { +class HCheckCast FINAL : public HTypeCheckInstruction { public: HCheckCast(HInstruction* object, - HLoadClass* target_class, + HInstruction* target_class_or_null, TypeCheckKind check_kind, - uint32_t dex_pc) - : HTemplateInstruction(kCheckCast, SideEffects::CanTriggerGC(), dex_pc) { - SetPackedField(check_kind); - SetPackedFlag(true); - SetRawInputAt(0, object); - SetRawInputAt(1, target_class); - } - - HLoadClass* GetTargetClass() const { - HInstruction* load_class = InputAt(1); - DCHECK(load_class->IsLoadClass()); - return load_class->AsLoadClass(); - } - - bool IsClonable() const OVERRIDE { return true; } - bool CanBeMoved() const OVERRIDE { return true; } - - bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { - return true; - } + Handle klass, + uint32_t dex_pc, + ArenaAllocator* allocator, + HIntConstant* bitstring_path_to_root, + HIntConstant* bitstring_mask) + : HTypeCheckInstruction(kCheckCast, + object, + target_class_or_null, + check_kind, + klass, + dex_pc, + allocator, + bitstring_path_to_root, + bitstring_mask, + SideEffects::CanTriggerGC()) {} bool NeedsEnvironment() const OVERRIDE { // Instruction may throw a CheckCastError. @@ -7030,24 +7116,10 @@ class HCheckCast FINAL : public HTemplateInstruction<2> { bool CanThrow() const OVERRIDE { return true; } - bool MustDoNullCheck() const { return GetPackedFlag(); } - void ClearMustDoNullCheck() { SetPackedFlag(false); } - TypeCheckKind GetTypeCheckKind() const { return GetPackedField(); } - bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } - DECLARE_INSTRUCTION(CheckCast); protected: DEFAULT_COPY_CONSTRUCTOR(CheckCast); - - private: - static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits; - static constexpr size_t kFieldTypeCheckKindSize = - MinimumBitsToStore(static_cast(TypeCheckKind::kLast)); - static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; - static constexpr size_t kNumberOfCheckCastPackedBits = kFlagMustDoNullCheck + 1; - static_assert(kNumberOfCheckCastPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - using TypeCheckKindField = BitField; }; /** diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 00194ff1fe..e0a9cfb934 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -99,6 +99,7 @@ enum class MethodCompilationStat { kConstructorFenceRemovedLSE, kConstructorFenceRemovedPFRA, kConstructorFenceRemovedCFRE, + kBitstringTypeCheck, kJitOutOfMemoryForCommit, kLastStat }; diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index f843c008d8..59733397bf 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -34,6 +34,20 @@ void PrepareForRegisterAllocation::Run() { } } +void PrepareForRegisterAllocation::VisitCheckCast(HCheckCast* check_cast) { + // Record only those bitstring type checks that make it to the codegen stage. + if (check_cast->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { + MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck); + } +} + +void PrepareForRegisterAllocation::VisitInstanceOf(HInstanceOf* instance_of) { + // Record only those bitstring type checks that make it to the codegen stage. + if (instance_of->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) { + MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck); + } +} + void PrepareForRegisterAllocation::VisitNullCheck(HNullCheck* check) { check->ReplaceWith(check->InputAt(0)); } diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h index 2c64f016c1..f6e4d3ef99 100644 --- a/compiler/optimizing/prepare_for_register_allocation.h +++ b/compiler/optimizing/prepare_for_register_allocation.h @@ -40,6 +40,8 @@ class PrepareForRegisterAllocation : public HGraphDelegateVisitor { "prepare_for_register_allocation"; private: + void VisitCheckCast(HCheckCast* check_cast) OVERRIDE; + void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE; void VisitNullCheck(HNullCheck* check) OVERRIDE; void VisitDivZeroCheck(HDivZeroCheck* check) OVERRIDE; void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE; diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 67a61fc01d..4030883a57 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -87,6 +87,7 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { void VisitDeoptimize(HDeoptimize* deopt) OVERRIDE; void VisitNewInstance(HNewInstance* new_instance) OVERRIDE; void VisitLoadClass(HLoadClass* load_class) OVERRIDE; + void VisitInstanceOf(HInstanceOf* load_class) OVERRIDE; void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE; void VisitLoadString(HLoadString* instr) OVERRIDE; void VisitLoadException(HLoadException* instr) OVERRIDE; @@ -171,6 +172,12 @@ void ReferenceTypePropagation::ValidateTypes() { << "NullCheck " << instr->GetReferenceTypeInfo() << "Input(0) " << instr->InputAt(0)->GetReferenceTypeInfo(); } + } else if (instr->IsInstanceOf()) { + HInstanceOf* iof = instr->AsInstanceOf(); + DCHECK(!iof->GetTargetClassRTI().IsValid() || iof->GetTargetClassRTI().IsExact()); + } else if (instr->IsCheckCast()) { + HCheckCast* check = instr->AsCheckCast(); + DCHECK(!check->GetTargetClassRTI().IsValid() || check->GetTargetClassRTI().IsExact()); } } } @@ -499,8 +506,7 @@ void ReferenceTypePropagation::RTPVisitor::BoundTypeForIfInstanceOf(HBasicBlock* return; } - HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass(); - ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); + ReferenceTypeInfo class_rti = instanceOf->GetTargetClassRTI(); if (!class_rti.IsValid()) { // He have loaded an unresolved class. Don't bother bounding the type. return; @@ -643,15 +649,20 @@ void ReferenceTypePropagation::RTPVisitor::VisitUnresolvedStaticFieldGet( void ReferenceTypePropagation::RTPVisitor::VisitLoadClass(HLoadClass* instr) { ScopedObjectAccess soa(Thread::Current()); - Handle resolved_class = instr->GetClass(); - if (IsAdmissible(resolved_class.Get())) { - instr->SetLoadedClassRTI(ReferenceTypeInfo::Create( - resolved_class, /* is_exact */ true)); + if (IsAdmissible(instr->GetClass().Get())) { + instr->SetValidLoadedClassRTI(); } instr->SetReferenceTypeInfo( ReferenceTypeInfo::Create(handle_cache_->GetClassClassHandle(), /* is_exact */ true)); } +void ReferenceTypePropagation::RTPVisitor::VisitInstanceOf(HInstanceOf* instr) { + ScopedObjectAccess soa(Thread::Current()); + if (IsAdmissible(instr->GetClass().Get())) { + instr->SetValidTargetClassRTI(); + } +} + void ReferenceTypePropagation::RTPVisitor::VisitClinitCheck(HClinitCheck* instr) { instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo()); } @@ -719,8 +730,6 @@ void ReferenceTypePropagation::RTPVisitor::VisitBoundType(HBoundType* instr) { } void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast) { - HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass(); - ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); HBoundType* bound_type = check_cast->GetNext()->AsBoundType(); if (bound_type == nullptr || bound_type->GetUpperBound().IsValid()) { // The next instruction is not an uninitialized BoundType. This must be @@ -729,12 +738,14 @@ void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast } DCHECK_EQ(bound_type->InputAt(0), check_cast->InputAt(0)); - if (class_rti.IsValid()) { + ScopedObjectAccess soa(Thread::Current()); + Handle klass = check_cast->GetClass(); + if (IsAdmissible(klass.Get())) { DCHECK(is_first_run_); - ScopedObjectAccess soa(Thread::Current()); + check_cast->SetValidTargetClassRTI(); // This is the first run of RTP and class is resolved. - bool is_exact = class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes(); - bound_type->SetUpperBound(ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), is_exact), + bool is_exact = klass->CannotBeAssignedFromOtherTypes(); + bound_type->SetUpperBound(ReferenceTypeInfo::Create(klass, is_exact), /* CheckCast succeeds for nulls. */ true); } else { // This is the first run of RTP and class is unresolved. Remove the binding. diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index 7dffb2a378..70b45763af 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -240,6 +240,75 @@ HLoadClass::LoadKind HSharpening::ComputeLoadClassKind( return load_kind; } +static inline bool CanUseTypeCheckBitstring(ObjPtr klass, + CodeGenerator* codegen, + CompilerDriver* compiler_driver) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!klass->IsProxyClass()); + DCHECK(!klass->IsArrayClass()); + + if (Runtime::Current()->UseJitCompilation()) { + // If we're JITting, try to assign a type check bitstring (fall through). + } else if (codegen->GetCompilerOptions().IsBootImage()) { + const char* descriptor = klass->GetDexFile().StringByTypeIdx(klass->GetDexTypeIndex()); + if (!compiler_driver->IsImageClass(descriptor)) { + return false; + } + // If the target is a boot image class, try to assign a type check bitstring (fall through). + // (If --force-determinism, this was already done; repeating is OK and yields the same result.) + } else { + // TODO: Use the bitstring also for AOT app compilation if the target class has a bitstring + // already assigned in the boot image. + return false; + } + + // Try to assign a type check bitstring. + MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); + if ((false) && // FIXME: Inliner does not respect compiler_driver->IsClassToCompile() + // and we're hitting an unassigned bitstring in dex2oat_image_test. b/26687569 + kIsDebugBuild && + codegen->GetCompilerOptions().IsBootImage() && + codegen->GetCompilerOptions().IsForceDeterminism()) { + SubtypeCheckInfo::State old_state = SubtypeCheck>::GetState(klass); + CHECK(old_state == SubtypeCheckInfo::kAssigned || old_state == SubtypeCheckInfo::kOverflowed) + << klass->PrettyDescriptor() << "/" << old_state + << " in " << codegen->GetGraph()->PrettyMethod(); + } + SubtypeCheckInfo::State state = SubtypeCheck>::EnsureAssigned(klass); + return state == SubtypeCheckInfo::kAssigned; +} + +TypeCheckKind HSharpening::ComputeTypeCheckKind(ObjPtr klass, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + bool needs_access_check) { + if (klass == nullptr) { + return TypeCheckKind::kUnresolvedCheck; + } else if (klass->IsInterface()) { + return TypeCheckKind::kInterfaceCheck; + } else if (klass->IsArrayClass()) { + if (klass->GetComponentType()->IsObjectClass()) { + return TypeCheckKind::kArrayObjectCheck; + } else if (klass->CannotBeAssignedFromOtherTypes()) { + return TypeCheckKind::kExactCheck; + } else { + return TypeCheckKind::kArrayCheck; + } + } else if (klass->IsFinal()) { // TODO: Consider using bitstring for final classes. + return TypeCheckKind::kExactCheck; + } else if (kBitstringSubtypeCheckEnabled && + !needs_access_check && + CanUseTypeCheckBitstring(klass, codegen, compiler_driver)) { + // TODO: We should not need the `!needs_access_check` check but getting rid of that + // requires rewriting some optimizations in instruction simplifier. + return TypeCheckKind::kBitstringCheck; + } else if (klass->IsAbstract()) { + return TypeCheckKind::kAbstractClassCheck; + } else { + return TypeCheckKind::kClassHierarchyCheck; + } +} + void HSharpening::ProcessLoadString( HLoadString* load_string, CodeGenerator* codegen, diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h index 6df7d6d91e..fa3e948eeb 100644 --- a/compiler/optimizing/sharpening.h +++ b/compiler/optimizing/sharpening.h @@ -44,12 +44,10 @@ class HSharpening : public HOptimization { static constexpr const char* kSharpeningPassName = "sharpening"; - // Used by the builder. - static void ProcessLoadString(HLoadString* load_string, - CodeGenerator* codegen, - CompilerDriver* compiler_driver, - const DexCompilationUnit& dex_compilation_unit, - VariableSizedHandleScope* handles); + // Used by Sharpening and InstructionSimplifier. + static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, + CodeGenerator* codegen, + CompilerDriver* compiler_driver); // Used by the builder and the inliner. static HLoadClass::LoadKind ComputeLoadClassKind(HLoadClass* load_class, @@ -58,10 +56,19 @@ class HSharpening : public HOptimization { const DexCompilationUnit& dex_compilation_unit) REQUIRES_SHARED(Locks::mutator_lock_); - // Used by Sharpening and InstructionSimplifier. - static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, - CodeGenerator* codegen, - CompilerDriver* compiler_driver); + // Used by the builder. + static TypeCheckKind ComputeTypeCheckKind(ObjPtr klass, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + bool needs_access_check) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Used by the builder. + static void ProcessLoadString(HLoadString* load_string, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + const DexCompilationUnit& dex_compilation_unit, + VariableSizedHandleScope* handles); private: CodeGenerator* codegen_; diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 98214fb684..0fd239a244 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -809,6 +809,9 @@ END art_quick_unlock_object_no_inline .extern artInstanceOfFromCode .extern artThrowClassCastExceptionForObject ENTRY art_quick_check_instance_of + // Type check using the bit string passes null as the target class. In that case just throw. + cbz r1, .Lthrow_class_cast_exception_for_bitstring_check + push {r0-r2, lr} @ save arguments, padding (r2) and link register .cfi_adjust_cfa_offset 16 .cfi_rel_offset r0, 0 @@ -827,6 +830,7 @@ ENTRY art_quick_check_instance_of .cfi_restore r2 .cfi_restore lr +.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r2 @ save all registers as basis for long jump context mov r2, r9 @ pass Thread::Current bl artThrowClassCastExceptionForObject @ (Object*, Class*, Thread*) diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index fb449ed5c7..9ff5ebede3 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1269,6 +1269,9 @@ END art_quick_unlock_object_no_inline .extern artInstanceOfFromCode .extern artThrowClassCastExceptionForObject ENTRY art_quick_check_instance_of + // Type check using the bit string passes null as the target class. In that case just throw. + cbz x1, .Lthrow_class_cast_exception_for_bitstring_check + // Store arguments and link register // Stack needs to be 16B aligned on calls. SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 32 @@ -1294,6 +1297,7 @@ ENTRY art_quick_check_instance_of // Restore RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32 +.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // save all registers as basis for long jump context mov x2, xSELF // pass Thread::Current bl artThrowClassCastExceptionForObject // (Object*, Class*, Thread*) diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index b2f7e10f52..d8fe480719 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1423,6 +1423,10 @@ END art_quick_unlock_object_no_inline .extern artInstanceOfFromCode .extern artThrowClassCastExceptionForObject ENTRY art_quick_check_instance_of + // Type check using the bit string passes null as the target class. In that case just throw. + beqz $a1, .Lthrow_class_cast_exception_for_bitstring_check + nop + addiu $sp, $sp, -32 .cfi_adjust_cfa_offset 32 sw $gp, 16($sp) @@ -1441,12 +1445,15 @@ ENTRY art_quick_check_instance_of jalr $zero, $ra addiu $sp, $sp, 32 .cfi_adjust_cfa_offset -32 + .Lthrow_class_cast_exception: lw $t9, 8($sp) lw $a1, 4($sp) lw $a0, 0($sp) addiu $sp, $sp, 32 .cfi_adjust_cfa_offset -32 + +.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME la $t9, artThrowClassCastExceptionForObject jalr $zero, $t9 # artThrowClassCastException (Object*, Class*, Thread*) diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 58e0e44813..8d2a7bd6c1 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1364,6 +1364,9 @@ END art_quick_unlock_object_no_inline .extern artInstanceOfFromCode .extern artThrowClassCastExceptionForObject ENTRY art_quick_check_instance_of + // Type check using the bit string passes null as the target class. In that case just throw. + beqzc $a1, .Lthrow_class_cast_exception_for_bitstring_check + daddiu $sp, $sp, -32 .cfi_adjust_cfa_offset 32 sd $ra, 24($sp) @@ -1379,12 +1382,15 @@ ENTRY art_quick_check_instance_of jalr $zero, $ra daddiu $sp, $sp, 32 .cfi_adjust_cfa_offset -32 + .Lthrow_class_cast_exception: ld $t9, 16($sp) ld $a1, 8($sp) ld $a0, 0($sp) daddiu $sp, $sp, 32 .cfi_adjust_cfa_offset -32 + +.Lthrow_class_cast_exception_for_bitstring_check: SETUP_GP SETUP_SAVE_ALL_CALLEE_SAVES_FRAME dla $t9, artThrowClassCastExceptionForObject diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 5c4ae4ea12..df43aef94b 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1432,6 +1432,10 @@ DEFINE_FUNCTION art_quick_instance_of END_FUNCTION art_quick_instance_of DEFINE_FUNCTION art_quick_check_instance_of + // Type check using the bit string passes null as the target class. In that case just throw. + testl %ecx, %ecx + jz .Lthrow_class_cast_exception_for_bitstring_check + PUSH eax // alignment padding PUSH ecx // pass arg2 - checked class PUSH eax // pass arg1 - obj @@ -1449,6 +1453,7 @@ DEFINE_FUNCTION art_quick_check_instance_of addl LITERAL(4), %esp CFI_ADJUST_CFA_OFFSET(-4) +.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME ebx, ebx // save all registers as basis for long jump context // Outgoing argument set up PUSH eax // alignment padding diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index a813200606..4f941e1c48 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -1403,6 +1403,10 @@ DEFINE_FUNCTION art_quick_unlock_object_no_inline END_FUNCTION art_quick_unlock_object_no_inline DEFINE_FUNCTION art_quick_check_instance_of + // Type check using the bit string passes null as the target class. In that case just throw. + testl %esi, %esi + jz .Lthrow_class_cast_exception_for_bitstring_check + // We could check the super classes here but that is usually already checked in the caller. PUSH rdi // Save args for exc PUSH rsi @@ -1426,6 +1430,7 @@ DEFINE_FUNCTION art_quick_check_instance_of POP rsi // Pop arguments POP rdi +.Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // save all registers as basis for long jump context mov %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current() call SYMBOL(artThrowClassCastExceptionForObject) // (Object* src, Class* dest, Thread*) diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc index 292bde0272..fe0f876d66 100644 --- a/runtime/base/arena_allocator.cc +++ b/runtime/base/arena_allocator.cc @@ -56,6 +56,7 @@ const char* const ArenaAllocatorStatsImpl::kAllocNames[] = { "CtorFenceIns ", "InvokeInputs ", "PhiInputs ", + "TypeCheckIns ", "LoopInfo ", "LIBackEdges ", "TryCatchInf ", diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h index c3011091e8..688f01b71f 100644 --- a/runtime/base/arena_allocator.h +++ b/runtime/base/arena_allocator.h @@ -62,6 +62,7 @@ enum ArenaAllocKind { kArenaAllocConstructorFenceInputs, kArenaAllocInvokeInputs, kArenaAllocPhiInputs, + kArenaAllocTypeCheckInputs, kArenaAllocLoopInfo, kArenaAllocLoopInfoBackEdges, kArenaAllocTryCatchInfo, diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc index 9b0756b529..ba7fb6b9db 100644 --- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc @@ -16,8 +16,11 @@ #include "art_method-inl.h" #include "callee_save_frame.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_instruction-inl.h" #include "common_throws.h" #include "mirror/object-inl.h" +#include "nth_caller_visitor.h" #include "thread.h" #include "well_known_classes.h" @@ -112,6 +115,26 @@ extern "C" NO_RETURN void artThrowClassCastException(mirror::Class* dest_type, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); + if (dest_type == nullptr) { + // Find the target class for check cast using the bitstring check (dest_type == null). + NthCallerVisitor visitor(self, 0u); + visitor.WalkStack(); + DCHECK(visitor.caller != nullptr); + uint32_t dex_pc = visitor.GetDexPc(); + CodeItemDataAccessor accessor(*visitor.caller->GetDexFile(), visitor.caller->GetCodeItem()); + const Instruction& check_cast = accessor.InstructionAt(dex_pc); + DCHECK_EQ(check_cast.Opcode(), Instruction::CHECK_CAST); + dex::TypeIndex type_index(check_cast.VRegB_21c()); + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + dest_type = linker->LookupResolvedType(type_index, visitor.caller).Ptr(); + CHECK(dest_type != nullptr) << "Target class should have been previously resolved: " + << visitor.caller->GetDexFile()->PrettyType(type_index); + CHECK(!dest_type->IsAssignableFrom(src_type)) + << " " << std::hex << dest_type->PrettyDescriptor() << ";" << dest_type->Depth() + << "/" << dest_type->GetField32(mirror::Class::StatusOffset()) + << " <: " << src_type->PrettyDescriptor() << ";" << src_type->Depth() + << "/" << src_type->GetField32(mirror::Class::StatusOffset()); + } DCHECK(!dest_type->IsAssignableFrom(src_type)); ThrowClassCastException(dest_type, src_type); self->QuickDeliverException(); diff --git a/runtime/subtype_check.h b/runtime/subtype_check.h index 3b1d5f8c4a..1fe62e8f46 100644 --- a/runtime/subtype_check.h +++ b/runtime/subtype_check.h @@ -286,6 +286,17 @@ struct SubtypeCheck { return SubtypeCheckInfo::kUninitialized; } + // Retrieve the state of this class's SubtypeCheckInfo. + // + // Cost: O(Depth(Class)). + // + // Returns: The precise SubtypeCheckInfo::State. + static SubtypeCheckInfo::State GetState(ClassPtr klass) + REQUIRES(Locks::subtype_check_lock_) + REQUIRES_SHARED(Locks::mutator_lock_) { + return GetSubtypeCheckInfo(klass).GetState(); + } + // Retrieve the path to root bitstring as a plain uintN_t value that is amenable to // be used by a fast check "encoded_src & mask_target == encoded_target". // @@ -308,8 +319,9 @@ struct SubtypeCheck { static BitString::StorageType GetEncodedPathToRootForTarget(ClassPtr klass) REQUIRES(Locks::subtype_check_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState()); - return GetSubtypeCheckInfo(klass).GetEncodedPathToRoot(); + SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass); + DCHECK_EQ(SubtypeCheckInfo::kAssigned, sci.GetState()); + return sci.GetEncodedPathToRoot(); } // Retrieve the path to root bitstring mask as a plain uintN_t value that is amenable to @@ -321,8 +333,9 @@ struct SubtypeCheck { static BitString::StorageType GetEncodedPathToRootMask(ClassPtr klass) REQUIRES(Locks::subtype_check_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState()); - return GetSubtypeCheckInfo(klass).GetEncodedPathToRootMask(); + SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass); + DCHECK_EQ(SubtypeCheckInfo::kAssigned, sci.GetState()); + return sci.GetEncodedPathToRootMask(); } // Is the source class a subclass of the target? -- GitLab From e37dc6ee0489565f911aeb792d627d942a3ae4cd Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 26 Mar 2018 16:04:48 +0100 Subject: [PATCH 144/749] Fix 036-finalizer for JIT-at-first-use gcstress. The array allocation in the OptimizingCompiler::JitCompile() can trigger a GC, so calling a method and holding only onto a WeakReference<> may actually cause the referred object to be collected. Work around this in the 036-finalizer test by avoiding the method call when we expect that object to stay alive. Strangely, the Heap::CheckGcStressMode() causes the GC for x86-64 but not for x86 in this test based on the backtrace comparisons. So the 32-bit test was actually passing. Test: testrunner.py --host -t 036-finalizer \ --jit --runtime-option=-Xjitthreshold=0 Bug: 62611253 Bug: 76454261 Change-Id: I10eb73beec18c66e4f7f1d125aa9e5db340a9d49 --- test/036-finalizer/src/Main.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java index ff6186b240..51d4a81150 100644 --- a/test/036-finalizer/src/Main.java +++ b/test/036-finalizer/src/Main.java @@ -70,15 +70,17 @@ public class Main { return s[0]; } - private static void printWeakReference(WeakReference wimp) { + public static void main(String[] args) { + WeakReference wimp = makeRef(); // Reference ft so we are sure the WeakReference cannot be cleared. + // Note: This is very fragile. It was previously in a helper function but that + // doesn't work for JIT-on-first-use with --gcstress where the object would be + // collected when JIT internally allocates an array. Also adding a scope around + // the keepLive lifetime somehow keeps a non-null `keepLive` around and makes + // the test fail (even when keeping the `null` assignment). b/76454261 FinalizerTest keepLive = wimp.get(); System.out.println("wimp: " + wimpString(wimp)); - } - - public static void main(String[] args) { - WeakReference wimp = makeRef(); - printWeakReference(wimp); + keepLive = null; // Clear the reference. /* this will try to collect and finalize ft */ System.out.println("gc"); -- GitLab From f5f9db5276c6570bd349b3bdac9ef5b19fcf3962 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 14 Mar 2018 16:25:10 +0000 Subject: [PATCH 145/749] Add jit-on-first-use test configurations. Test: run_build_test_target.py -j48 art-jit-on-first-use Test: run_build_test_target.py -j48 art-jit-on-first-use-gcstress Bug: 62611253 Change-Id: I83b84d0dd660198cc712f7d12c4e4f803a5283a0 --- test/testrunner/target_config.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index b323ddc9cb..95e488dc4f 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -46,6 +46,10 @@ target_config = { 'art-jit' : { 'run-test' : ['--jit'] }, + 'art-jit-on-first-use' : { + 'run-test' : ['--jit', + '--runtime-option=-Xjitthreshold:0'] + }, 'art-pictest' : { 'run-test' : ['--pictest', '--optimizing'] @@ -66,6 +70,11 @@ target_config = { 'run-test' : ['--jit', '--gcstress'] }, + 'art-jit-on-first-use-gcstress' : { + 'run-test' : ['--jit', + '--gcstress', + '--runtime-option=-Xjitthreshold:0'] + }, # TODO: Rename or repurpose this configuration as # 'art-read-barrier-heap-poisoning' (b/62611253). 'art-read-barrier' : { -- GitLab From 7d74ef585063ca2adc0ba9c18008b7c1671ff699 Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Fri, 16 Mar 2018 14:18:33 +0000 Subject: [PATCH 146/749] Support for API exemptions from API blacklisting. Add support to VMRuntime for setting a list of exemptions from the hidden API enforcement. The list is used as a prefix match against the signature of a field/method. Any signatures that match are treated as being on the light grey list, rather than the dark grey list or blacklist. Refactor some code in hidden_api.h that deals with field/method signatures, to encapsulate the signature in a new class MemberSignature. This allows us to avoid building the entire signature in member, instead just using its constituent parts. Test: $ make test-art-host-gtest-hidden_api_test Test: $ adb shell settings put global hidden_api_blacklist_exemptions \ Test: Landroid/view/RemoteAnimationDefinition\\\;:Landroid/app/ActivityManager\\\$TaskDescription\\\; Test: Manually verify logcat output from app which uses named APIs Bug: 73337509 Change-Id: Id608743d1b5a7a37059875d8991d0d4d65f5fc36 --- build/Android.gtest.mk | 2 + runtime/Android.bp | 1 + runtime/hidden_api.h | 116 +++++++-- runtime/hidden_api_test.cc | 273 ++++++++++++++++++++++ runtime/native/dalvik_system_VMRuntime.cc | 16 ++ runtime/runtime.h | 11 + test/HiddenApiSignatures/Class1.java | 35 +++ test/HiddenApiSignatures/Class12.java | 24 ++ test/HiddenApiSignatures/Class2.java | 27 +++ test/HiddenApiSignatures/Class3.java | 27 +++ 10 files changed, 509 insertions(+), 23 deletions(-) create mode 100644 runtime/hidden_api_test.cc create mode 100644 test/HiddenApiSignatures/Class1.java create mode 100644 test/HiddenApiSignatures/Class12.java create mode 100644 test/HiddenApiSignatures/Class2.java create mode 100644 test/HiddenApiSignatures/Class3.java diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 8681642c0e..b342abe17c 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -37,6 +37,7 @@ GTEST_DEX_DIRECTORIES := \ ExceptionHandle \ GetMethodSignature \ HiddenApi \ + HiddenApiSignatures \ ImageLayoutA \ ImageLayoutB \ IMTA \ @@ -160,6 +161,7 @@ ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEP ART_GTEST_dex2oat_image_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle ART_GTEST_hiddenapi_test_DEX_DEPS := HiddenApi +ART_GTEST_hidden_api_test_DEX_DEPS := HiddenApiSignatures ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods ART_GTEST_imtable_test_DEX_DEPS := IMTA IMTB ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation diff --git a/runtime/Android.bp b/runtime/Android.bp index 590a399738..aa5d12f535 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -565,6 +565,7 @@ art_cc_test { "gc/task_processor_test.cc", "gtest_test.cc", "handle_scope_test.cc", + "hidden_api_test.cc", "imtable_test.cc", "indenter_test.cc", "indirect_reference_table_test.cc", diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index 5c6b4b56bc..bbedda017e 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -19,6 +19,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" +#include "base/dumpable.h" #include "dex/hidden_api_access_flags.h" #include "mirror/class-inl.h" #include "reflection.h" @@ -107,27 +108,78 @@ inline Action GetMemberAction(uint32_t access_flags) { } } -// Issue a warning about field access. -inline void WarnAboutMemberAccess(ArtField* field, AccessMethod access_method) - REQUIRES_SHARED(Locks::mutator_lock_) { - std::string tmp; - LOG(WARNING) << "Accessing hidden field " - << field->GetDeclaringClass()->GetDescriptor(&tmp) << "->" - << field->GetName() << ":" << field->GetTypeDescriptor() - << " (" << HiddenApiAccessFlags::DecodeFromRuntime(field->GetAccessFlags()) - << ", " << access_method << ")"; -} -// Issue a warning about method access. -inline void WarnAboutMemberAccess(ArtMethod* method, AccessMethod access_method) - REQUIRES_SHARED(Locks::mutator_lock_) { - std::string tmp; - LOG(WARNING) << "Accessing hidden method " - << method->GetDeclaringClass()->GetDescriptor(&tmp) << "->" - << method->GetName() << method->GetSignature().ToString() - << " (" << HiddenApiAccessFlags::DecodeFromRuntime(method->GetAccessFlags()) - << ", " << access_method << ")"; -} +// Class to encapsulate the signature of a member (ArtField or ArtMethod). This +// is used as a helper when matching prefixes, and when logging the signature. +class MemberSignature { + private: + std::string member_type_; + std::vector signature_parts_; + std::string tmp_; + + public: + explicit MemberSignature(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) { + member_type_ = "field"; + signature_parts_ = { + field->GetDeclaringClass()->GetDescriptor(&tmp_), + "->", + field->GetName(), + ":", + field->GetTypeDescriptor() + }; + } + + explicit MemberSignature(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { + member_type_ = "method"; + signature_parts_ = { + method->GetDeclaringClass()->GetDescriptor(&tmp_), + "->", + method->GetName(), + method->GetSignature().ToString() + }; + } + + const std::vector& Parts() const { + return signature_parts_; + } + + void Dump(std::ostream& os) const { + for (std::string part : signature_parts_) { + os << part; + } + } + // Performs prefix match on this member. Since the full member signature is + // composed of several parts, we match each part in turn (rather than + // building the entire thing in memory and performing a simple prefix match) + bool DoesPrefixMatch(const std::string& prefix) const { + size_t pos = 0; + for (const std::string& part : signature_parts_) { + size_t count = std::min(prefix.length() - pos, part.length()); + if (prefix.compare(pos, count, part, 0, count) == 0) { + pos += count; + } else { + return false; + } + } + // We have a complete match if all parts match (we exit the loop without + // returning) AND we've matched the whole prefix. + return pos == prefix.length(); + } + + bool IsExempted(const std::vector& exemptions) { + for (const std::string& exemption : exemptions) { + if (DoesPrefixMatch(exemption)) { + return true; + } + } + return false; + } + + void WarnAboutAccess(AccessMethod access_method, HiddenApiAccessFlags::ApiList list) { + LOG(WARNING) << "Accessing hidden " << member_type_ << " " << Dumpable(*this) + << " (" << list << ", " << access_method << ")"; + } +}; // Returns true if access to `member` should be denied to the caller of the // reflective query. The decision is based on whether the caller is in boot @@ -158,9 +210,29 @@ inline bool ShouldBlockAccessToMember(T* member, // Member is hidden and we are not in the boot class path. + // Get the signature, we need it later. + MemberSignature member_signature(member); + + Runtime* runtime = Runtime::Current(); + + if (action == kDeny) { + // If we were about to deny, check for an exemption first. + // Exempted APIs are treated as light grey list. + if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) { + action = kAllowButWarn; + // Avoid re-examining the exemption list next time. + // Note this results in the warning below showing "light greylist", which + // seems like what one would expect. Exemptions effectively add new members to + // the light greylist. + member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( + member->GetAccessFlags(), HiddenApiAccessFlags::kLightGreylist)); + } + } + // Print a log message with information about this class member access. // We do this regardless of whether we block the access or not. - WarnAboutMemberAccess(member, access_method); + member_signature.WarnAboutAccess(access_method, + HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags())); if (action == kDeny) { // Block access @@ -170,8 +242,6 @@ inline bool ShouldBlockAccessToMember(T* member, // Allow access to this member but print a warning. DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast); - Runtime* runtime = Runtime::Current(); - // Depending on a runtime flag, we might move the member into whitelist and // skip the warning the next time the member is accessed. if (runtime->ShouldDedupeHiddenApiWarnings()) { diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc new file mode 100644 index 0000000000..190d2cb7a7 --- /dev/null +++ b/runtime/hidden_api_test.cc @@ -0,0 +1,273 @@ +/* + * 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. + */ + +#include "hidden_api.h" + +#include "common_runtime_test.h" +#include "jni_internal.h" + +namespace art { + +class HiddenApiTest : public CommonRuntimeTest { + protected: + void SetUp() OVERRIDE { + // Do the normal setup. + CommonRuntimeTest::SetUp(); + self_ = Thread::Current(); + self_->TransitionFromSuspendedToRunnable(); + LoadDex("HiddenApiSignatures"); + bool started = runtime_->Start(); + CHECK(started); + + class1_field1_ = getArtField("mypackage/packagea/Class1", "field1", "I"); + class1_field12_ = getArtField("mypackage/packagea/Class1", "field12", "I"); + class1_init_ = getArtMethod("mypackage/packagea/Class1", "", "()V"); + class1_method1_ = getArtMethod("mypackage/packagea/Class1", "method1", "()V"); + class1_method1_i_ = getArtMethod("mypackage/packagea/Class1", "method1", "(I)V"); + class1_method12_ = getArtMethod("mypackage/packagea/Class1", "method12", "()V"); + class12_field1_ = getArtField("mypackage/packagea/Class12", "field1", "I"); + class12_method1_ = getArtMethod("mypackage/packagea/Class12", "method1", "()V"); + class2_field1_ = getArtField("mypackage/packagea/Class2", "field1", "I"); + class2_method1_ = getArtMethod("mypackage/packagea/Class2", "method1", "()V"); + class2_method1_i_ = getArtMethod("mypackage/packagea/Class2", "method1", "(I)V"); + class3_field1_ = getArtField("mypackage/packageb/Class3", "field1", "I"); + class3_method1_ = getArtMethod("mypackage/packageb/Class3", "method1", "()V"); + class3_method1_i_ = getArtMethod("mypackage/packageb/Class3", "method1", "(I)V"); + } + + ArtMethod* getArtMethod(const char* class_name, const char* name, const char* signature) { + JNIEnv* env = Thread::Current()->GetJniEnv(); + jclass klass = env->FindClass(class_name); + jmethodID method_id = env->GetMethodID(klass, name, signature); + ArtMethod* art_method = jni::DecodeArtMethod(method_id); + return art_method; + } + + ArtField* getArtField(const char* class_name, const char* name, const char* signature) { + JNIEnv* env = Thread::Current()->GetJniEnv(); + jclass klass = env->FindClass(class_name); + jfieldID field_id = env->GetFieldID(klass, name, signature); + ArtField* art_field = jni::DecodeArtField(field_id); + return art_field; + } + + protected: + Thread* self_; + ArtField* class1_field1_; + ArtField* class1_field12_; + ArtMethod* class1_init_; + ArtMethod* class1_method1_; + ArtMethod* class1_method1_i_; + ArtMethod* class1_method12_; + ArtField* class12_field1_; + ArtMethod* class12_method1_; + ArtField* class2_field1_; + ArtMethod* class2_method1_; + ArtMethod* class2_method1_i_; + ArtField* class3_field1_; + ArtMethod* class3_method1_; + ArtMethod* class3_method1_i_; +}; + +TEST_F(HiddenApiTest, CheckMembersRead) { + ASSERT_NE(nullptr, class1_field1_); + ASSERT_NE(nullptr, class1_field12_); + ASSERT_NE(nullptr, class1_init_); + ASSERT_NE(nullptr, class1_method1_); + ASSERT_NE(nullptr, class1_method1_i_); + ASSERT_NE(nullptr, class1_method12_); + ASSERT_NE(nullptr, class12_field1_); + ASSERT_NE(nullptr, class12_method1_); + ASSERT_NE(nullptr, class2_field1_); + ASSERT_NE(nullptr, class2_method1_); + ASSERT_NE(nullptr, class2_method1_i_); + ASSERT_NE(nullptr, class3_field1_); + ASSERT_NE(nullptr, class3_method1_); + ASSERT_NE(nullptr, class3_method1_i_); +} + +TEST_F(HiddenApiTest, CheckEverythingMatchesL) { + ScopedObjectAccess soa(self_); + std::string prefix("L"); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class2_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class2_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class3_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class3_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class3_method1_i_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckPackageMatch) { + ScopedObjectAccess soa(self_); + std::string prefix("Lmypackage/packagea/"); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class2_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class2_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class3_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class3_method1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class3_method1_i_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckClassMatch) { + ScopedObjectAccess soa(self_); + std::string prefix("Lmypackage/packagea/Class1"); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class2_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class2_method1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckClassExactMatch) { + ScopedObjectAccess soa(self_); + std::string prefix("Lmypackage/packagea/Class1;"); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class2_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class2_method1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckMethodMatch) { + ScopedObjectAccess soa(self_); + std::string prefix("Lmypackage/packagea/Class1;->method1"); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckMethodExactMatch) { + ScopedObjectAccess soa(self_); + std::string prefix("Lmypackage/packagea/Class1;->method1("); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckMethodSignatureMatch) { + ScopedObjectAccess soa(self_); + std::string prefix("Lmypackage/packagea/Class1;->method1(I)"); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckMethodSignatureAndReturnMatch) { + ScopedObjectAccess soa(self_); + std::string prefix("Lmypackage/packagea/Class1;->method1()V"); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckFieldMatch) { + ScopedObjectAccess soa(self_); + std::string prefix("Lmypackage/packagea/Class1;->field1"); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckFieldExactMatch) { + ScopedObjectAccess soa(self_); + std::string prefix("Lmypackage/packagea/Class1;->field1:"); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckFieldTypeMatch) { + ScopedObjectAccess soa(self_); + std::string prefix("Lmypackage/packagea/Class1;->field1:I"); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckConstructorMatch) { + ScopedObjectAccess soa(self_); + std::string prefix("Lmypackage/packagea/Class1;->"); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckConstructorExactMatch) { + ScopedObjectAccess soa(self_); + std::string prefix("Lmypackage/packagea/Class1;->()V"); + ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckMethodSignatureTrailingCharsNoMatch) { + ScopedObjectAccess soa(self_); + std::string prefix("Lmypackage/packagea/Class1;->method1()Vfoo"); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckConstructorTrailingCharsNoMatch) { + ScopedObjectAccess soa(self_); + std::string prefix("Lmypackage/packagea/Class1;->()Vfoo"); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix)); +} + +TEST_F(HiddenApiTest, CheckFieldTrailingCharsNoMatch) { + ScopedObjectAccess soa(self_); + std::string prefix("Lmypackage/packagea/Class1;->field1:Ifoo"); + ASSERT_FALSE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); +} + +} // namespace art diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 505b745200..a5ade6f30f 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -78,6 +78,21 @@ static jboolean VMRuntime_hasUsedHiddenApi(JNIEnv*, jobject) { return Runtime::Current()->HasPendingHiddenApiWarning() ? JNI_TRUE : JNI_FALSE; } +static void VMRuntime_setHiddenApiExemptions(JNIEnv* env, + jclass, + jobjectArray exemptions) { + std::vector exemptions_vec; + int exemptions_length = env->GetArrayLength(exemptions); + for (int i = 0; i < exemptions_length; i++) { + jstring exemption = reinterpret_cast(env->GetObjectArrayElement(exemptions, i)); + const char* raw_exemption = env->GetStringUTFChars(exemption, nullptr); + exemptions_vec.push_back(raw_exemption); + env->ReleaseStringUTFChars(exemption, raw_exemption); + } + + Runtime::Current()->SetHiddenApiExemptions(exemptions_vec); +} + static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaElementClass, jint length) { ScopedFastNativeObjectAccess soa(env); @@ -672,6 +687,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, concurrentGC, "()V"), NATIVE_METHOD(VMRuntime, disableJitCompilation, "()V"), NATIVE_METHOD(VMRuntime, hasUsedHiddenApi, "()Z"), + NATIVE_METHOD(VMRuntime, setHiddenApiExemptions, "([Ljava/lang/String;)V"), NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"), FAST_NATIVE_METHOD(VMRuntime, isDebuggerActive, "()Z"), FAST_NATIVE_METHOD(VMRuntime, isNativeDebuggable, "()Z"), diff --git a/runtime/runtime.h b/runtime/runtime.h index dba31b2939..03f17bc04a 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -536,6 +536,14 @@ class Runtime { pending_hidden_api_warning_ = value; } + void SetHiddenApiExemptions(const std::vector& exemptions) { + hidden_api_exemptions_ = exemptions; + } + + const std::vector& GetHiddenApiExemptions() { + return hidden_api_exemptions_; + } + bool HasPendingHiddenApiWarning() const { return pending_hidden_api_warning_; } @@ -996,6 +1004,9 @@ class Runtime { // Whether access checks on hidden API should be performed. hiddenapi::EnforcementPolicy hidden_api_policy_; + // List of signature prefixes of methods that have been removed from the blacklist + std::vector hidden_api_exemptions_; + // Whether the application has used an API which is not restricted but we // should issue a warning about it. bool pending_hidden_api_warning_; diff --git a/test/HiddenApiSignatures/Class1.java b/test/HiddenApiSignatures/Class1.java new file mode 100644 index 0000000000..a9004dcd99 --- /dev/null +++ b/test/HiddenApiSignatures/Class1.java @@ -0,0 +1,35 @@ +/* + * 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. + */ + +package mypackage.packagea; + +public class Class1 { + public int field1; + public int field12; + + public Class1() { + } + + public void method1() { + } + + public void method1(int i) { + } + + public void method12() { + } + +} \ No newline at end of file diff --git a/test/HiddenApiSignatures/Class12.java b/test/HiddenApiSignatures/Class12.java new file mode 100644 index 0000000000..82b22e365f --- /dev/null +++ b/test/HiddenApiSignatures/Class12.java @@ -0,0 +1,24 @@ +/* + * 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. + */ + +package mypackage.packagea; + +public class Class12 { + public int field1; + + public void method1() { + } +} \ No newline at end of file diff --git a/test/HiddenApiSignatures/Class2.java b/test/HiddenApiSignatures/Class2.java new file mode 100644 index 0000000000..dc92b9cfd8 --- /dev/null +++ b/test/HiddenApiSignatures/Class2.java @@ -0,0 +1,27 @@ +/* + * 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. + */ + +package mypackage.packagea; + +public class Class2 { + public int field1; + + public void method1() { + } + + public void method1(int i) { + } +} \ No newline at end of file diff --git a/test/HiddenApiSignatures/Class3.java b/test/HiddenApiSignatures/Class3.java new file mode 100644 index 0000000000..fbf04071a4 --- /dev/null +++ b/test/HiddenApiSignatures/Class3.java @@ -0,0 +1,27 @@ +/* + * 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. + */ + +package mypackage.packageb; + +public class Class3 { + public int field1; + + public void method1() { + } + + public void method1(int i) { + } +} \ No newline at end of file -- GitLab From 81a1f853925d88d19119e850e22b7f66bddef63b Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Mon, 26 Mar 2018 13:55:57 -0700 Subject: [PATCH 147/749] Refined add/sub analysis vis-a-vis SIMD idioms. Rationale: Slightly more general detection of + and - with constants ensures less cases are undetected. Bug: b/74026074 Test: test-art-host,target Change-Id: Ie5bb2dd10294436a27487e5a1ddc77d9e2dd2303 --- compiler/optimizing/loop_optimization.cc | 162 +++++++++--------- test/646-checker-hadd-short/src/Main.java | 38 ++++ test/660-checker-simd-sad-short/src/Main.java | 106 ++++++++++++ .../678-checker-simd-saturation/src/Main.java | 137 ++++++++++++++- 4 files changed, 354 insertions(+), 89 deletions(-) diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 1d83815e1f..71e24de141 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -227,6 +227,7 @@ static bool IsNarrowerOperands(HInstruction* a, /*out*/ HInstruction** r, /*out*/ HInstruction** s, /*out*/ bool* is_unsigned) { + DCHECK(a != nullptr && b != nullptr); // Look for a matching sign extension. DataType::Type stype = HVecOperation::ToSignedType(type); if (IsSignExtensionAndGet(a, stype, r) && IsSignExtensionAndGet(b, stype, s)) { @@ -247,6 +248,7 @@ static bool IsNarrowerOperand(HInstruction* a, DataType::Type type, /*out*/ HInstruction** r, /*out*/ bool* is_unsigned) { + DCHECK(a != nullptr); // Look for a matching sign extension. DataType::Type stype = HVecOperation::ToSignedType(type); if (IsSignExtensionAndGet(a, stype, r)) { @@ -270,20 +272,28 @@ static uint32_t GetOtherVL(DataType::Type other_type, DataType::Type vector_type return vl >> (DataType::SizeShift(other_type) - DataType::SizeShift(vector_type)); } -// Detect up to two instructions a and b, and an acccumulated constant c. -static bool IsAddConstHelper(HInstruction* instruction, - /*out*/ HInstruction** a, - /*out*/ HInstruction** b, - /*out*/ int64_t* c, - int32_t depth) { - static constexpr int32_t kMaxDepth = 8; // don't search too deep +// Detect up to two added operands a and b and an acccumulated constant c. +static bool IsAddConst(HInstruction* instruction, + /*out*/ HInstruction** a, + /*out*/ HInstruction** b, + /*out*/ int64_t* c, + int32_t depth = 8) { // don't search too deep int64_t value = 0; + // Enter add/sub while still within reasonable depth. + if (depth > 0) { + if (instruction->IsAdd()) { + return IsAddConst(instruction->InputAt(0), a, b, c, depth - 1) && + IsAddConst(instruction->InputAt(1), a, b, c, depth - 1); + } else if (instruction->IsSub() && + IsInt64AndGet(instruction->InputAt(1), &value)) { + *c -= value; + return IsAddConst(instruction->InputAt(0), a, b, c, depth - 1); + } + } + // Otherwise, deal with leaf nodes. if (IsInt64AndGet(instruction, &value)) { *c += value; return true; - } else if (instruction->IsAdd() && depth <= kMaxDepth) { - return IsAddConstHelper(instruction->InputAt(0), a, b, c, depth + 1) && - IsAddConstHelper(instruction->InputAt(1), a, b, c, depth + 1); } else if (*a == nullptr) { *a = instruction; return true; @@ -291,42 +301,40 @@ static bool IsAddConstHelper(HInstruction* instruction, *b = instruction; return true; } - return false; // too many non-const operands + return false; // too many operands } -// Detect a + b + c for an optional constant c. -static bool IsAddConst(HInstruction* instruction, - /*out*/ HInstruction** a, - /*out*/ HInstruction** b, - /*out*/ int64_t* c) { - if (instruction->IsAdd()) { - // Try to find a + b and accumulated c. - if (IsAddConstHelper(instruction->InputAt(0), a, b, c, /*depth*/ 0) && - IsAddConstHelper(instruction->InputAt(1), a, b, c, /*depth*/ 0) && - *b != nullptr) { - return true; +// Detect a + b + c with optional constant c. +static bool IsAddConst2(HGraph* graph, + HInstruction* instruction, + /*out*/ HInstruction** a, + /*out*/ HInstruction** b, + /*out*/ int64_t* c) { + if (IsAddConst(instruction, a, b, c) && *a != nullptr) { + if (*b == nullptr) { + // Constant is usually already present, unless accumulated. + *b = graph->GetConstant(instruction->GetType(), (*c)); + *c = 0; } - // Found a + b. - *a = instruction->InputAt(0); - *b = instruction->InputAt(1); - *c = 0; return true; } return false; } -// Detect a + c for constant c. -static bool IsAddConst(HInstruction* instruction, - /*out*/ HInstruction** a, - /*out*/ int64_t* c) { - if (instruction->IsAdd()) { - if (IsInt64AndGet(instruction->InputAt(0), c)) { - *a = instruction->InputAt(1); - return true; - } else if (IsInt64AndGet(instruction->InputAt(1), c)) { - *a = instruction->InputAt(0); - return true; - } +// Detect a direct a - b or a hidden a - (-c). +static bool IsSubConst2(HGraph* graph, + HInstruction* instruction, + /*out*/ HInstruction** a, + /*out*/ HInstruction** b) { + int64_t c = 0; + if (instruction->IsSub()) { + *a = instruction->InputAt(0); + *b = instruction->InputAt(1); + return true; + } else if (IsAddConst(instruction, a, b, &c) && *a != nullptr && *b == nullptr) { + // Constant for the hidden subtraction. + *b = graph->GetConstant(instruction->GetType(), -c); + return true; } return false; } @@ -378,7 +386,8 @@ static bool CanSetRange(DataType::Type type, } // Accept various saturated addition forms. -static bool IsSaturatedAdd(HInstruction* clippee, +static bool IsSaturatedAdd(HInstruction* a, + HInstruction* b, DataType::Type type, int64_t lo, int64_t hi, @@ -390,8 +399,7 @@ static bool IsSaturatedAdd(HInstruction* clippee, // Tighten the range for signed single clipping on constant. if (!is_unsigned) { int64_t c = 0; - HInstruction* notused = nullptr; - if (IsAddConst(clippee, ¬used, &c)) { + if (IsInt64AndGet(a, &c) || IsInt64AndGet(b, &c)) { // For c in proper range and narrower operand r: // MIN(r + c, 127) c > 0 // or MAX(r + c, -128) c < 0 (and possibly redundant bound). @@ -413,7 +421,7 @@ static bool IsSaturatedAdd(HInstruction* clippee, } // Accept various saturated subtraction forms. -static bool IsSaturatedSub(HInstruction* clippee, +static bool IsSaturatedSub(HInstruction* a, DataType::Type type, int64_t lo, int64_t hi, @@ -425,7 +433,7 @@ static bool IsSaturatedSub(HInstruction* clippee, // Tighten the range for signed single clipping on constant. if (!is_unsigned) { int64_t c = 0; - if (IsInt64AndGet(clippee->InputAt(0), /*out*/ &c)) { + if (IsInt64AndGet(a, /*out*/ &c)) { // For c in proper range and narrower operand r: // MIN(c - r, 127) c > 0 // or MAX(c - r, -128) c < 0 (and possibly redundant bound). @@ -1521,8 +1529,7 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node, return false; // reject, unless all operands are same-extension narrower } // Accept MIN/MAX(x, y) for vectorizable operands. - DCHECK(r != nullptr); - DCHECK(s != nullptr); + DCHECK(r != nullptr && s != nullptr); if (generate_code && vector_mode_ != kVector) { // de-idiom r = opa; s = opb; @@ -2026,31 +2033,37 @@ bool HLoopOptimization::VectorizeSaturationIdiom(LoopNode* node, instruction->GetType() != DataType::Type::kInt64) { return false; } - // Clipped addition or subtraction? + // Clipped addition or subtraction on narrower operands? We will try both + // formats since, e.g., x+c can be interpreted as x+c and x-(-c), depending + // on what clipping values are used, to get most benefits. int64_t lo = std::numeric_limits::min(); int64_t hi = std::numeric_limits::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* a = nullptr; + HInstruction* b = nullptr; 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(clippee, type, lo, hi, is_unsigned) - : IsSaturatedSub(clippee, type, lo, hi, is_unsigned))) { - DCHECK(r != nullptr); - DCHECK(s != nullptr); + bool is_add = true; + int64_t c = 0; + // First try for saturated addition. + if (IsAddConst2(graph_, clippee, /*out*/ &a, /*out*/ &b, /*out*/ &c) && c == 0 && + IsNarrowerOperands(a, b, type, &r, &s, &is_unsigned) && + IsSaturatedAdd(r, s, type, lo, hi, is_unsigned)) { + is_add = true; } else { - return false; + // Then try again for saturated subtraction. + a = b = r = s = nullptr; + if (IsSubConst2(graph_, clippee, /*out*/ &a, /*out*/ &b) && + IsNarrowerOperands(a, b, type, &r, &s, &is_unsigned) && + IsSaturatedSub(r, type, lo, hi, is_unsigned)) { + is_add = false; + } else { + return false; + } } // Accept saturation idiom for vectorizable operands. + DCHECK(r != nullptr && s != nullptr); if (generate_code && vector_mode_ != kVector) { // de-idiom r = instruction->InputAt(0); s = instruction->InputAt(1); @@ -2101,8 +2114,7 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node, HInstruction* a = nullptr; HInstruction* b = nullptr; int64_t c = 0; - if (IsAddConst(instruction->InputAt(0), /*out*/ &a, /*out*/ &b, /*out*/ &c)) { - DCHECK(a != nullptr && b != nullptr); + if (IsAddConst2(graph_, instruction->InputAt(0), /*out*/ &a, /*out*/ &b, /*out*/ &c)) { // Accept c == 1 (rounded) or c == 0 (not rounded). bool is_rounded = false; if (c == 1) { @@ -2124,8 +2136,7 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node, } // Accept recognized halving add for vectorizable operands. Vectorized code uses the // shorthand idiomatic operation. Sequential code uses the original scalar expressions. - DCHECK(r != nullptr); - DCHECK(s != nullptr); + DCHECK(r != nullptr && s != nullptr); if (generate_code && vector_mode_ != kVector) { // de-idiom r = instruction->InputAt(0); s = instruction->InputAt(1); @@ -2175,19 +2186,11 @@ bool HLoopOptimization::VectorizeSADIdiom(LoopNode* node, HInstruction* v = instruction->InputAt(1); HInstruction* a = nullptr; HInstruction* b = nullptr; - if (v->GetType() == reduction_type && v->IsAbs()) { - HInstruction* x = v->InputAt(0); - if (x->GetType() == reduction_type) { - int64_t c = 0; - if (x->IsSub()) { - a = x->InputAt(0); - b = x->InputAt(1); - } else if (IsAddConst(x, /*out*/ &a, /*out*/ &c)) { - b = graph_->GetConstant(reduction_type, -c); // hidden SUB! - } - } - } - if (a == nullptr || b == nullptr) { + if (v->IsAbs() && + v->GetType() == reduction_type && + IsSubConst2(graph_, v->InputAt(0), /*out*/ &a, /*out*/ &b)) { + DCHECK(a != nullptr && b != nullptr); + } else { return false; } // Accept same-type or consistent sign extension for narrower-type on operands a and b. @@ -2220,8 +2223,7 @@ bool HLoopOptimization::VectorizeSADIdiom(LoopNode* node, } // Accept SAD idiom for vectorizable operands. Vectorized code uses the shorthand // idiomatic operation. Sequential code uses the original scalar expressions. - DCHECK(r != nullptr); - DCHECK(s != nullptr); + DCHECK(r != nullptr && s != nullptr); if (generate_code && vector_mode_ != kVector) { // de-idiom r = s = v->InputAt(0); } diff --git a/test/646-checker-hadd-short/src/Main.java b/test/646-checker-hadd-short/src/Main.java index 85c2fcaf22..c09da8125b 100644 --- a/test/646-checker-hadd-short/src/Main.java +++ b/test/646-checker-hadd-short/src/Main.java @@ -26,6 +26,10 @@ public class Main { static short[] sB2 = new short[M]; static short[] sBo = new short[M]; + private static int $inline$mone() { + return -1; + } + /// CHECK-START: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (before) /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none @@ -184,6 +188,35 @@ public class Main { } } + /// CHECK-START: void Main.rounding_halving_add_signed_alt3(short[], short[], short[]) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant -1 loop:none + /// CHECK-DAG: <> IntConstant 9 loop:none + /// CHECK-DAG: <> IntConstant -9 loop:none + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Shr [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_signed_alt3(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none + /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:true loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none + private static void rounding_halving_add_signed_alt3(short[] b1, short[] b2, short[] bo) { + int min_length = Math.min(bo.length, Math.min(b1.length, b2.length)); + for (int i = 0; i < min_length; i++) { + // Computations that cancel to adding 1 also do not confuse recognition. + bo[i] = (short) (((b1[i] + 9) + (b2[i] - 9) - $inline$mone()) >> 1); + } + } + /// CHECK-START: void Main.rounding_halving_add_unsigned(short[], short[], short[]) instruction_simplifier (before) /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> IntConstant 65535 loop:none @@ -366,6 +399,11 @@ public class Main { short e = (short) ((sB1[i] + sB2[i] + 1) >> 1); expectEquals(e, sBo[i]); } + rounding_halving_add_signed_alt3(sB1, sB2, sBo); + for (int i = 0; i < M; i++) { + short e = (short) ((sB1[i] + sB2[i] + 1) >> 1); + expectEquals(e, sBo[i]); + } rounding_halving_add_unsigned(sB1, sB2, sBo); for (int i = 0; i < M; i++) { short e = (short) (((sB1[i] & 0xffff) + (sB2[i] & 0xffff) + 1) >> 1); diff --git a/test/660-checker-simd-sad-short/src/Main.java b/test/660-checker-simd-sad-short/src/Main.java index 8a44d9ed12..77c9e53e0c 100644 --- a/test/660-checker-simd-sad-short/src/Main.java +++ b/test/660-checker-simd-sad-short/src/Main.java @@ -19,6 +19,10 @@ */ public class Main { + private static int $inline$seven() { + return 7; + } + // TODO: lower precision still coming, b/64091002 private static short sadShort2Short(short[] s1, short[] s2) { @@ -153,6 +157,102 @@ public class Main { return sad; } + /// CHECK-START: int Main.sadShort2IntConstant1(short[]) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant -7 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstant1(short[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 7 loop:none + /// CHECK-DAG: <> IntConstant 8 loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: <> VecSetScalars [<>] loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + private static int sadShort2IntConstant1(short[] s) { + int sad = 0; + for (int i = 0; i < s.length; i++) { + sad += Math.abs(s[i] - 7); // s[i] + -7 + } + return sad; + } + + /// CHECK-START: int Main.sadShort2IntConstant2(short[]) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 7 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstant2(short[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 7 loop:none + /// CHECK-DAG: <> IntConstant 8 loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: <> VecSetScalars [<>] loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + private static int sadShort2IntConstant2(short[] s) { + int sad = 0; + for (int i = 0; i < s.length; i++) { + sad += Math.abs(s[i] - $inline$seven()); // s[i] - 7 + } + return sad; + } + + /// CHECK-START: int Main.sadShort2IntConstant3(short[]) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 7 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstant3(short[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant -7 loop:none + /// CHECK-DAG: <> IntConstant 8 loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: <> VecSetScalars [<>] loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + private static int sadShort2IntConstant3(short[] s) { + int sad = 0; + for (int i = 0; i < s.length; i++) { + sad += Math.abs(s[i] + $inline$seven()); // hidden s[i] - (-7) + } + return sad; + } + /// CHECK-START: long Main.sadShort2Long(short[], short[]) loop_optimization (before) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 1 loop:none @@ -243,6 +343,9 @@ public class Main { expectEquals(65535, sadShort2IntAlt(s2, s1)); expectEquals(65535, sadShort2IntAlt2(s1, s2)); expectEquals(65535, sadShort2IntAlt2(s2, s1)); + expectEquals(32880, sadShort2IntConstant1(s1)); + expectEquals(32880, sadShort2IntConstant2(s1)); + expectEquals(32866, sadShort2IntConstant3(s1)); expectEquals(65535L, sadShort2Long(s1, s2)); expectEquals(65535L, sadShort2Long(s2, s1)); expectEquals(65536L, sadShort2LongAt1(s1, s2)); @@ -279,6 +382,9 @@ public class Main { expectEquals(1291788, sadShort2Int(s1, s2)); expectEquals(1291788, sadShort2IntAlt(s1, s2)); expectEquals(1291788, sadShort2IntAlt2(s1, s2)); + expectEquals(823907, sadShort2IntConstant1(s1)); + expectEquals(823907, sadShort2IntConstant2(s1)); + expectEquals(823953, sadShort2IntConstant3(s1)); expectEquals(1291788L, sadShort2Long(s1, s2)); expectEquals(1291789L, sadShort2LongAt1(s1, s2)); diff --git a/test/678-checker-simd-saturation/src/Main.java b/test/678-checker-simd-saturation/src/Main.java index d123cc2e25..decc691789 100644 --- a/test/678-checker-simd-saturation/src/Main.java +++ b/test/678-checker-simd-saturation/src/Main.java @@ -19,6 +19,14 @@ */ public class Main { + static final int $inline$p15() { + return 15; + } + + static final int $inline$m15() { + return -15; + } + // // Direct min-max. // @@ -230,8 +238,8 @@ public class Main { /// CHECK-START-{ARM,ARM64}: void Main.satSubPConstSByte(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecReplicateScalar loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none public static void satSubPConstSByte(byte[] a, byte[] b) { int n = Math.min(a.length, b.length); for (int i = 0; i < n; i++) { @@ -242,8 +250,8 @@ public class Main { /// CHECK-START-{ARM,ARM64}: void Main.satSubNConstSByte(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecReplicateScalar loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none public static void satSubNConstSByte(byte[] a, byte[] b) { int n = Math.min(a.length, b.length); for (int i = 0; i < n; i++) { @@ -282,8 +290,8 @@ public class Main { /// CHECK-START-{ARM,ARM64}: void Main.satSubPConstSShort(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecReplicateScalar loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none public static void satSubPConstSShort(short[] a, short[] b) { int n = Math.min(a.length, b.length); for (int i = 0; i < n; i++) { @@ -294,8 +302,8 @@ public class Main { /// CHECK-START-{ARM,ARM64}: void Main.satSubNConstSShort(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecReplicateScalar loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none public static void satSubNConstSShort(short[] a, short[] b) { int n = Math.min(a.length, b.length); for (int i = 0; i < n; i++) { @@ -304,7 +312,59 @@ public class Main { } // - // Alternatives. + // Alternatives 8-bit clipping. + // + + /// CHECK-START-{ARM,ARM64}: void Main.usatAddConst(byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void usatAddConst(byte[] a, byte[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + b[i] = (byte) Math.min((a[i] & 0xff) + $inline$p15(), 255); + } + } + + /// CHECK-START-{ARM,ARM64}: void Main.usatAddConstAlt(byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void usatAddConstAlt(byte[] a, byte[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + b[i] = (byte) Math.min((a[i] & 0xff) - $inline$m15(), 255); + } + } + + /// CHECK-START-{ARM,ARM64}: void Main.usatSubConst(byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void usatSubConst(byte[] a, byte[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + b[i] = (byte) Math.max((a[i] & 0xff) - $inline$p15(), 0); + } + } + + /// CHECK-START-{ARM,ARM64}: void Main.usatSubConstAlt(byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void usatSubConstAlt(byte[] a, byte[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + b[i] = (byte) Math.max((a[i] & 0xff) + $inline$m15(), 0); + } + } + + // + // Alternatives 16-bit clipping. // /// CHECK-START: void Main.satAlt1(short[], short[], short[]) loop_optimization (before) @@ -442,6 +502,34 @@ public class Main { } } + /// CHECK-START-{ARM,ARM64}: void Main.usatSubConst(short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void usatSubConst(short[] a, short[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + int t = a[i] & 0xffff; + int s = t - $inline$p15(); + b[i] = (short)(s > 0 ? s : 0); + } + } + + /// CHECK-START-{ARM,ARM64}: void Main.usatSubConstAlt(short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void usatSubConstAlt(short[] a, short[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + int t = a[i] & 0xffff; + int s = t + $inline$m15(); + b[i] = (short)(s > 0 ? s : 0); + } + } + // // Test drivers. // @@ -503,6 +591,27 @@ public class Main { byte e = (byte) Math.max(-15 - b1[i], -128); expectEquals(e, out[i]); } + // Alternatives. + usatAddConst(b1, out); + for (int i = 0; i < m; i++) { + byte e = (byte) Math.min((b1[i] & 0xff) + 15, 255); + expectEquals(e, out[i]); + } + usatAddConstAlt(b1, out); + for (int i = 0; i < m; i++) { + byte e = (byte) Math.min((b1[i] & 0xff) + 15, 255); + expectEquals(e, out[i]); + } + usatSubConst(b1, out); + for (int i = 0; i < m; i++) { + byte e = (byte) Math.max((b1[i] & 0xff) - 15, 0); + expectEquals(e, out[i]); + } + usatSubConstAlt(b1, out); + for (int i = 0; i < m; i++) { + byte e = (byte) Math.max((b1[i] & 0xff) - 15, 0); + expectEquals(e, out[i]); + } } private static void test16Bit() { @@ -630,6 +739,16 @@ public class Main { short e = (short) Math.max(Math.min(s1[i] + 15, 32767), -32752); expectEquals(e, out[i]); } + usatSubConst(s1, out); + for (int i = 0; i < m; i++) { + short e = (short) Math.max((s1[i] & 0xffff) - 15, 0); + expectEquals(e, out[i]); + } + usatSubConstAlt(s1, out); + for (int i = 0; i < m; i++) { + short e = (short) Math.max((s1[i] & 0xffff) - 15, 0); + expectEquals(e, out[i]); + } } public static void main(String[] args) { -- GitLab From 2aaa44f966186c19232481cafed2a5d49328d8c9 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 27 Mar 2018 15:02:47 +0100 Subject: [PATCH 148/749] Merge SIMD Min/Max tests into one. Reduce the number of tests that we need to run. Test: testrunner.py --host -t 651 Change-Id: I81800b257736215462ecd9be7ae8a38bbbb6fbd1 --- .../651-checker-byte-simd-minmax/expected.txt | 1 - .../651-checker-char-simd-minmax/expected.txt | 1 - test/651-checker-char-simd-minmax/info.txt | 1 - .../expected.txt | 1 - test/651-checker-double-simd-minmax/info.txt | 1 - .../expected.txt | 1 - test/651-checker-float-simd-minmax/info.txt | 1 - test/651-checker-int-simd-minmax/expected.txt | 1 - test/651-checker-int-simd-minmax/info.txt | 1 - .../651-checker-long-simd-minmax/expected.txt | 1 - test/651-checker-long-simd-minmax/info.txt | 1 - .../expected.txt | 1 - test/651-checker-short-simd-minmax/info.txt | 1 - test/651-checker-simd-minmax/expected.txt | 7 +++++ .../info.txt | 0 .../src/ByteSimdMinMax.java} | 30 +++++++++---------- .../src/CharSimdMinMax.java} | 18 +++++------ .../src/DoubleSimdMinMax.java} | 14 ++++----- .../src/FloatSimdMinMax.java} | 14 ++++----- .../src/IntSimdMinMax.java} | 14 ++++----- .../src/LongSimdMinMax.java} | 18 +++++------ test/651-checker-simd-minmax/src/Main.java | 27 +++++++++++++++++ .../src/ShortSimdMinMax.java} | 30 +++++++++---------- 23 files changed, 103 insertions(+), 82 deletions(-) delete mode 100644 test/651-checker-byte-simd-minmax/expected.txt delete mode 100644 test/651-checker-char-simd-minmax/expected.txt delete mode 100644 test/651-checker-char-simd-minmax/info.txt delete mode 100644 test/651-checker-double-simd-minmax/expected.txt delete mode 100644 test/651-checker-double-simd-minmax/info.txt delete mode 100644 test/651-checker-float-simd-minmax/expected.txt delete mode 100644 test/651-checker-float-simd-minmax/info.txt delete mode 100644 test/651-checker-int-simd-minmax/expected.txt delete mode 100644 test/651-checker-int-simd-minmax/info.txt delete mode 100644 test/651-checker-long-simd-minmax/expected.txt delete mode 100644 test/651-checker-long-simd-minmax/info.txt delete mode 100644 test/651-checker-short-simd-minmax/expected.txt delete mode 100644 test/651-checker-short-simd-minmax/info.txt create mode 100644 test/651-checker-simd-minmax/expected.txt rename test/{651-checker-byte-simd-minmax => 651-checker-simd-minmax}/info.txt (100%) rename test/{651-checker-byte-simd-minmax/src/Main.java => 651-checker-simd-minmax/src/ByteSimdMinMax.java} (87%) rename test/{651-checker-char-simd-minmax/src/Main.java => 651-checker-simd-minmax/src/CharSimdMinMax.java} (88%) rename test/{651-checker-double-simd-minmax/src/Main.java => 651-checker-simd-minmax/src/DoubleSimdMinMax.java} (89%) rename test/{651-checker-float-simd-minmax/src/Main.java => 651-checker-simd-minmax/src/FloatSimdMinMax.java} (89%) rename test/{651-checker-int-simd-minmax/src/Main.java => 651-checker-simd-minmax/src/IntSimdMinMax.java} (89%) rename test/{651-checker-long-simd-minmax/src/Main.java => 651-checker-simd-minmax/src/LongSimdMinMax.java} (87%) create mode 100644 test/651-checker-simd-minmax/src/Main.java rename test/{651-checker-short-simd-minmax/src/Main.java => 651-checker-simd-minmax/src/ShortSimdMinMax.java} (88%) diff --git a/test/651-checker-byte-simd-minmax/expected.txt b/test/651-checker-byte-simd-minmax/expected.txt deleted file mode 100644 index b0aad4deb5..0000000000 --- a/test/651-checker-byte-simd-minmax/expected.txt +++ /dev/null @@ -1 +0,0 @@ -passed diff --git a/test/651-checker-char-simd-minmax/expected.txt b/test/651-checker-char-simd-minmax/expected.txt deleted file mode 100644 index b0aad4deb5..0000000000 --- a/test/651-checker-char-simd-minmax/expected.txt +++ /dev/null @@ -1 +0,0 @@ -passed diff --git a/test/651-checker-char-simd-minmax/info.txt b/test/651-checker-char-simd-minmax/info.txt deleted file mode 100644 index 73af1242c0..0000000000 --- a/test/651-checker-char-simd-minmax/info.txt +++ /dev/null @@ -1 +0,0 @@ -Functional tests on min/max SIMD vectorization. diff --git a/test/651-checker-double-simd-minmax/expected.txt b/test/651-checker-double-simd-minmax/expected.txt deleted file mode 100644 index b0aad4deb5..0000000000 --- a/test/651-checker-double-simd-minmax/expected.txt +++ /dev/null @@ -1 +0,0 @@ -passed diff --git a/test/651-checker-double-simd-minmax/info.txt b/test/651-checker-double-simd-minmax/info.txt deleted file mode 100644 index 73af1242c0..0000000000 --- a/test/651-checker-double-simd-minmax/info.txt +++ /dev/null @@ -1 +0,0 @@ -Functional tests on min/max SIMD vectorization. diff --git a/test/651-checker-float-simd-minmax/expected.txt b/test/651-checker-float-simd-minmax/expected.txt deleted file mode 100644 index b0aad4deb5..0000000000 --- a/test/651-checker-float-simd-minmax/expected.txt +++ /dev/null @@ -1 +0,0 @@ -passed diff --git a/test/651-checker-float-simd-minmax/info.txt b/test/651-checker-float-simd-minmax/info.txt deleted file mode 100644 index 73af1242c0..0000000000 --- a/test/651-checker-float-simd-minmax/info.txt +++ /dev/null @@ -1 +0,0 @@ -Functional tests on min/max SIMD vectorization. diff --git a/test/651-checker-int-simd-minmax/expected.txt b/test/651-checker-int-simd-minmax/expected.txt deleted file mode 100644 index b0aad4deb5..0000000000 --- a/test/651-checker-int-simd-minmax/expected.txt +++ /dev/null @@ -1 +0,0 @@ -passed diff --git a/test/651-checker-int-simd-minmax/info.txt b/test/651-checker-int-simd-minmax/info.txt deleted file mode 100644 index 73af1242c0..0000000000 --- a/test/651-checker-int-simd-minmax/info.txt +++ /dev/null @@ -1 +0,0 @@ -Functional tests on min/max SIMD vectorization. diff --git a/test/651-checker-long-simd-minmax/expected.txt b/test/651-checker-long-simd-minmax/expected.txt deleted file mode 100644 index b0aad4deb5..0000000000 --- a/test/651-checker-long-simd-minmax/expected.txt +++ /dev/null @@ -1 +0,0 @@ -passed diff --git a/test/651-checker-long-simd-minmax/info.txt b/test/651-checker-long-simd-minmax/info.txt deleted file mode 100644 index 73af1242c0..0000000000 --- a/test/651-checker-long-simd-minmax/info.txt +++ /dev/null @@ -1 +0,0 @@ -Functional tests on min/max SIMD vectorization. diff --git a/test/651-checker-short-simd-minmax/expected.txt b/test/651-checker-short-simd-minmax/expected.txt deleted file mode 100644 index b0aad4deb5..0000000000 --- a/test/651-checker-short-simd-minmax/expected.txt +++ /dev/null @@ -1 +0,0 @@ -passed diff --git a/test/651-checker-short-simd-minmax/info.txt b/test/651-checker-short-simd-minmax/info.txt deleted file mode 100644 index 73af1242c0..0000000000 --- a/test/651-checker-short-simd-minmax/info.txt +++ /dev/null @@ -1 +0,0 @@ -Functional tests on min/max SIMD vectorization. diff --git a/test/651-checker-simd-minmax/expected.txt b/test/651-checker-simd-minmax/expected.txt new file mode 100644 index 0000000000..f362c45b77 --- /dev/null +++ b/test/651-checker-simd-minmax/expected.txt @@ -0,0 +1,7 @@ +ByteSimdMinMax passed +CharSimdMinMax passed +ShortSimdMinMax passed +IntSimdMinMax passed +LongSimdMinMax passed +DoubleSimdMinMax passed +FloatSimdMinMax passed diff --git a/test/651-checker-byte-simd-minmax/info.txt b/test/651-checker-simd-minmax/info.txt similarity index 100% rename from test/651-checker-byte-simd-minmax/info.txt rename to test/651-checker-simd-minmax/info.txt diff --git a/test/651-checker-byte-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/ByteSimdMinMax.java similarity index 87% rename from test/651-checker-byte-simd-minmax/src/Main.java rename to test/651-checker-simd-minmax/src/ByteSimdMinMax.java index 4e667bbc6f..b9954947f7 100644 --- a/test/651-checker-byte-simd-minmax/src/Main.java +++ b/test/651-checker-simd-minmax/src/ByteSimdMinMax.java @@ -17,9 +17,9 @@ /** * Tests for MIN/MAX vectorization. */ -public class Main { +public class ByteSimdMinMax { - /// CHECK-START: void Main.doitMin(byte[], byte[], byte[]) loop_optimization (before) + /// CHECK-START: void ByteSimdMinMax.doitMin(byte[], byte[], byte[]) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -27,7 +27,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMin(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int8 loop:<> outer_loop:none @@ -39,7 +39,7 @@ public class Main { } } - /// CHECK-START: void Main.doitMinUnsigned(byte[], byte[], byte[]) instruction_simplifier (before) + /// CHECK-START: void ByteSimdMinMax.doitMinUnsigned(byte[], byte[], byte[]) instruction_simplifier (before) /// CHECK-DAG: <> IntConstant 255 loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -50,7 +50,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none // - /// CHECK-START: void Main.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (before) + /// CHECK-START: void ByteSimdMinMax.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -58,7 +58,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint8 loop:<> outer_loop:none @@ -70,7 +70,7 @@ public class Main { } } - /// CHECK-START: void Main.doitMax(byte[], byte[], byte[]) loop_optimization (before) + /// CHECK-START: void ByteSimdMinMax.doitMax(byte[], byte[], byte[]) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -78,7 +78,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMax(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Int8 loop:<> outer_loop:none @@ -90,7 +90,7 @@ public class Main { } } - /// CHECK-START: void Main.doitMaxUnsigned(byte[], byte[], byte[]) instruction_simplifier (before) + /// CHECK-START: void ByteSimdMinMax.doitMaxUnsigned(byte[], byte[], byte[]) instruction_simplifier (before) /// CHECK-DAG: <> IntConstant 255 loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -101,7 +101,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none // - /// CHECK-START: void Main.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (before) + /// CHECK-START: void ByteSimdMinMax.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -109,7 +109,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Uint8 loop:<> outer_loop:none @@ -121,7 +121,7 @@ public class Main { } } - /// CHECK-START: void Main.doitMin100(byte[], byte[]) loop_optimization (before) + /// CHECK-START: void ByteSimdMinMax.doitMin100(byte[], byte[]) loop_optimization (before) /// CHECK-DAG: <> IntConstant 100 loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -129,7 +129,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-{ARM64,MIPS64}: void Main.doitMin100(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void ByteSimdMinMax.doitMin100(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 100 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none @@ -142,7 +142,7 @@ public class Main { } } - public static void main(String[] args) { + public static void main() { // Initialize cross-values for all possible values. int total = 256 * 256; byte[] x = new byte[total]; @@ -185,7 +185,7 @@ public class Main { expectEquals(expected, x[i]); } - System.out.println("passed"); + System.out.println("ByteSimdMinMax passed"); } private static void expectEquals(byte expected, byte result) { diff --git a/test/651-checker-char-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/CharSimdMinMax.java similarity index 88% rename from test/651-checker-char-simd-minmax/src/Main.java rename to test/651-checker-simd-minmax/src/CharSimdMinMax.java index 520e10b6c1..30169c4591 100644 --- a/test/651-checker-char-simd-minmax/src/Main.java +++ b/test/651-checker-simd-minmax/src/CharSimdMinMax.java @@ -17,9 +17,9 @@ /** * Tests for MIN/MAX vectorization. */ -public class Main { +public class CharSimdMinMax { - /// CHECK-START: void Main.doitMin(char[], char[], char[]) loop_optimization (before) + /// CHECK-START: void CharSimdMinMax.doitMin(char[], char[], char[]) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -27,7 +27,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(char[], char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void CharSimdMinMax.doitMin(char[], char[], char[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint16 loop:<> outer_loop:none @@ -39,7 +39,7 @@ public class Main { } } - /// CHECK-START: void Main.doitMax(char[], char[], char[]) loop_optimization (before) + /// CHECK-START: void CharSimdMinMax.doitMax(char[], char[], char[]) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -47,7 +47,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(char[], char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void CharSimdMinMax.doitMax(char[], char[], char[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Uint16 loop:<> outer_loop:none @@ -59,7 +59,7 @@ public class Main { } } - /// CHECK-START: void Main.doitMin100(char[], char[]) loop_optimization (before) + /// CHECK-START: void CharSimdMinMax.doitMin100(char[], char[]) loop_optimization (before) /// CHECK-DAG: <> IntConstant 100 loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -67,7 +67,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-{ARM64,MIPS64}: void Main.doitMin100(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void CharSimdMinMax.doitMin100(char[], char[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 100 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none @@ -80,7 +80,7 @@ public class Main { } } - public static void main(String[] args) { + public static void main() { char[] interesting = { 0x0000, 0x0001, 0x007f, 0x0080, 0x0081, 0x00ff, 0x0100, 0x0101, 0x017f, 0x0180, 0x0181, 0x01ff, @@ -121,7 +121,7 @@ public class Main { expectEquals(expected, x[i]); } - System.out.println("passed"); + System.out.println("CharSimdMinMax passed"); } private static void expectEquals(char expected, char result) { diff --git a/test/651-checker-double-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/DoubleSimdMinMax.java similarity index 89% rename from test/651-checker-double-simd-minmax/src/Main.java rename to test/651-checker-simd-minmax/src/DoubleSimdMinMax.java index 2eaf907167..da20594db8 100644 --- a/test/651-checker-double-simd-minmax/src/Main.java +++ b/test/651-checker-simd-minmax/src/DoubleSimdMinMax.java @@ -17,9 +17,9 @@ /** * Tests for MIN/MAX vectorization. */ -public class Main { +public class DoubleSimdMinMax { - /// CHECK-START: void Main.doitMin(double[], double[], double[]) loop_optimization (before) + /// CHECK-START: void DoubleSimdMinMax.doitMin(double[], double[], double[]) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -29,7 +29,7 @@ public class Main { // TODO x86: 0.0 vs -0.0? // TODO MIPS64: min(x, NaN)? // - /// CHECK-START-ARM64: void Main.doitMin(double[], double[], double[]) loop_optimization (after) + /// CHECK-START-ARM64: void DoubleSimdMinMax.doitMin(double[], double[], double[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMin [<>,<>] loop:<> outer_loop:none @@ -41,7 +41,7 @@ public class Main { } } - /// CHECK-START: void Main.doitMax(double[], double[], double[]) loop_optimization (before) + /// CHECK-START: void DoubleSimdMinMax.doitMax(double[], double[], double[]) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -51,7 +51,7 @@ public class Main { // TODO x86: 0.0 vs -0.0? // TODO MIPS64: max(x, NaN)? // - /// CHECK-START-ARM64: void Main.doitMax(double[], double[], double[]) loop_optimization (after) + /// CHECK-START-ARM64: void DoubleSimdMinMax.doitMax(double[], double[], double[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMax [<>,<>] loop:<> outer_loop:none @@ -63,7 +63,7 @@ public class Main { } } - public static void main(String[] args) { + public static void main() { double[] interesting = { -0.0f, +0.0f, @@ -109,7 +109,7 @@ public class Main { expectEquals(expected, x[i]); } - System.out.println("passed"); + System.out.println("DoubleSimdMinMax passed"); } private static void expectEquals(double expected, double result) { diff --git a/test/651-checker-float-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/FloatSimdMinMax.java similarity index 89% rename from test/651-checker-float-simd-minmax/src/Main.java rename to test/651-checker-simd-minmax/src/FloatSimdMinMax.java index dc09dfc7cc..645081248a 100644 --- a/test/651-checker-float-simd-minmax/src/Main.java +++ b/test/651-checker-simd-minmax/src/FloatSimdMinMax.java @@ -17,9 +17,9 @@ /** * Tests for MIN/MAX vectorization. */ -public class Main { +public class FloatSimdMinMax { - /// CHECK-START: void Main.doitMin(float[], float[], float[]) loop_optimization (before) + /// CHECK-START: void FloatSimdMinMax.doitMin(float[], float[], float[]) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -29,7 +29,7 @@ public class Main { // TODO x86: 0.0 vs -0.0? // TODO MIPS64: min(x, NaN)? // - /// CHECK-START-ARM64: void Main.doitMin(float[], float[], float[]) loop_optimization (after) + /// CHECK-START-ARM64: void FloatSimdMinMax.doitMin(float[], float[], float[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMin [<>,<>] loop:<> outer_loop:none @@ -41,7 +41,7 @@ public class Main { } } - /// CHECK-START: void Main.doitMax(float[], float[], float[]) loop_optimization (before) + /// CHECK-START: void FloatSimdMinMax.doitMax(float[], float[], float[]) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -51,7 +51,7 @@ public class Main { // TODO x86: 0.0 vs -0.0? // TODO MIPS64: max(x, NaN)? // - /// CHECK-START-ARM64: void Main.doitMax(float[], float[], float[]) loop_optimization (after) + /// CHECK-START-ARM64: void FloatSimdMinMax.doitMax(float[], float[], float[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMax [<>,<>] loop:<> outer_loop:none @@ -63,7 +63,7 @@ public class Main { } } - public static void main(String[] args) { + public static void main() { float[] interesting = { -0.0f, +0.0f, @@ -109,7 +109,7 @@ public class Main { expectEquals(expected, x[i]); } - System.out.println("passed"); + System.out.println("FloatSimdMinMax passed"); } private static void expectEquals(float expected, float result) { diff --git a/test/651-checker-int-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/IntSimdMinMax.java similarity index 89% rename from test/651-checker-int-simd-minmax/src/Main.java rename to test/651-checker-simd-minmax/src/IntSimdMinMax.java index 82fad84d08..6373ae10eb 100644 --- a/test/651-checker-int-simd-minmax/src/Main.java +++ b/test/651-checker-simd-minmax/src/IntSimdMinMax.java @@ -17,16 +17,16 @@ /** * Tests for MIN/MAX vectorization. */ -public class Main { +public class IntSimdMinMax { - /// CHECK-START: void Main.doitMin(int[], int[], int[]) loop_optimization (before) + /// CHECK-START: void IntSimdMinMax.doitMin(int[], int[], int[]) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(int[], int[], int[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void IntSimdMinMax.doitMin(int[], int[], int[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int32 loop:<> outer_loop:none @@ -38,14 +38,14 @@ public class Main { } } - /// CHECK-START: void Main.doitMax(int[], int[], int[]) loop_optimization (before) + /// CHECK-START: void IntSimdMinMax.doitMax(int[], int[], int[]) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(int[], int[], int[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void IntSimdMinMax.doitMax(int[], int[], int[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Int32 loop:<> outer_loop:none @@ -57,7 +57,7 @@ public class Main { } } - public static void main(String[] args) { + public static void main() { int[] interesting = { 0x00000000, 0x00000001, 0x00007fff, 0x00008000, 0x00008001, 0x0000ffff, 0x00010000, 0x00010001, 0x00017fff, 0x00018000, 0x00018001, 0x0001ffff, @@ -93,7 +93,7 @@ public class Main { expectEquals(expected, x[i]); } - System.out.println("passed"); + System.out.println("IntSimdMinMax passed"); } private static void expectEquals(int expected, int result) { diff --git a/test/651-checker-long-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/LongSimdMinMax.java similarity index 87% rename from test/651-checker-long-simd-minmax/src/Main.java rename to test/651-checker-simd-minmax/src/LongSimdMinMax.java index f52686e54c..bb0c6047ed 100644 --- a/test/651-checker-long-simd-minmax/src/Main.java +++ b/test/651-checker-simd-minmax/src/LongSimdMinMax.java @@ -17,9 +17,9 @@ /** * Tests for MIN/MAX vectorization. */ -public class Main { +public class LongSimdMinMax { - /// CHECK-START: void Main.doitMin(long[], long[], long[]) loop_optimization (before) + /// CHECK-START: void LongSimdMinMax.doitMin(long[], long[], long[]) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -28,10 +28,10 @@ public class Main { // // Not directly supported for longs. // - /// CHECK-START-ARM64: void Main.doitMin(long[], long[], long[]) loop_optimization (after) + /// CHECK-START-ARM64: void LongSimdMinMax.doitMin(long[], long[], long[]) loop_optimization (after) /// CHECK-NOT: VecMin // - /// CHECK-START-MIPS64: void Main.doitMin(long[], long[], long[]) loop_optimization (after) + /// CHECK-START-MIPS64: void LongSimdMinMax.doitMin(long[], long[], long[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMin [<>,<>] loop:<> outer_loop:none @@ -44,7 +44,7 @@ public class Main { } } - /// CHECK-START: void Main.doitMax(long[], long[], long[]) loop_optimization (before) + /// CHECK-START: void LongSimdMinMax.doitMax(long[], long[], long[]) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -53,10 +53,10 @@ public class Main { // // Not directly supported for longs. // - /// CHECK-START-ARM64: void Main.doitMax(long[], long[], long[]) loop_optimization (after) + /// CHECK-START-ARM64: void LongSimdMinMax.doitMax(long[], long[], long[]) loop_optimization (after) /// CHECK-NOT: VecMax // - /// CHECK-START-MIPS64: void Main.doitMax(long[], long[], long[]) loop_optimization (after) + /// CHECK-START-MIPS64: void LongSimdMinMax.doitMax(long[], long[], long[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMax [<>,<>] loop:<> outer_loop:none @@ -68,7 +68,7 @@ public class Main { } } - public static void main(String[] args) { + public static void main() { long[] interesting = { 0x0000000000000000L, 0x0000000000000001L, 0x000000007fffffffL, 0x0000000080000000L, 0x0000000080000001L, 0x00000000ffffffffL, @@ -110,7 +110,7 @@ public class Main { expectEquals(expected, x[i]); } - System.out.println("passed"); + System.out.println("LongSimdMinMax passed"); } private static void expectEquals(long expected, long result) { diff --git a/test/651-checker-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/Main.java new file mode 100644 index 0000000000..9134dd1edd --- /dev/null +++ b/test/651-checker-simd-minmax/src/Main.java @@ -0,0 +1,27 @@ +/* + * 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 Main { + public static void main(String[] args) { + ByteSimdMinMax.main(); + CharSimdMinMax.main(); + ShortSimdMinMax.main(); + IntSimdMinMax.main(); + LongSimdMinMax.main(); + DoubleSimdMinMax.main(); + FloatSimdMinMax.main(); + } +} diff --git a/test/651-checker-short-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/ShortSimdMinMax.java similarity index 88% rename from test/651-checker-short-simd-minmax/src/Main.java rename to test/651-checker-simd-minmax/src/ShortSimdMinMax.java index 4300ca2951..aae78914d8 100644 --- a/test/651-checker-short-simd-minmax/src/Main.java +++ b/test/651-checker-simd-minmax/src/ShortSimdMinMax.java @@ -17,9 +17,9 @@ /** * Tests for MIN/MAX vectorization. */ -public class Main { +public class ShortSimdMinMax { - /// CHECK-START: void Main.doitMin(short[], short[], short[]) loop_optimization (before) + /// CHECK-START: void ShortSimdMinMax.doitMin(short[], short[], short[]) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -27,7 +27,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMin(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int16 loop:<> outer_loop:none @@ -39,7 +39,7 @@ public class Main { } } - /// CHECK-START: void Main.doitMinUnsigned(short[], short[], short[]) instruction_simplifier (before) + /// CHECK-START: void ShortSimdMinMax.doitMinUnsigned(short[], short[], short[]) instruction_simplifier (before) /// CHECK-DAG: <> IntConstant 65535 loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -50,7 +50,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none // - /// CHECK-START: void Main.doitMinUnsigned(short[], short[], short[]) loop_optimization (before) + /// CHECK-START: void ShortSimdMinMax.doitMinUnsigned(short[], short[], short[]) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -58,7 +58,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMinUnsigned(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMinUnsigned(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint16 loop:<> outer_loop:none @@ -70,7 +70,7 @@ public class Main { } } - /// CHECK-START: void Main.doitMax(short[], short[], short[]) loop_optimization (before) + /// CHECK-START: void ShortSimdMinMax.doitMax(short[], short[], short[]) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -78,7 +78,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMax(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Int16 loop:<> outer_loop:none @@ -90,7 +90,7 @@ public class Main { } } - /// CHECK-START: void Main.doitMaxUnsigned(short[], short[], short[]) instruction_simplifier (before) + /// CHECK-START: void ShortSimdMinMax.doitMaxUnsigned(short[], short[], short[]) instruction_simplifier (before) /// CHECK-DAG: <> IntConstant 65535 loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -101,7 +101,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none // - /// CHECK-START: void Main.doitMaxUnsigned(short[], short[], short[]) loop_optimization (before) + /// CHECK-START: void ShortSimdMinMax.doitMaxUnsigned(short[], short[], short[]) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -109,7 +109,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMaxUnsigned(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMaxUnsigned(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Uint16 loop:<> outer_loop:none @@ -121,7 +121,7 @@ public class Main { } } - /// CHECK-START: void Main.doitMin100(short[], short[]) loop_optimization (before) + /// CHECK-START: void ShortSimdMinMax.doitMin100(short[], short[]) loop_optimization (before) /// CHECK-DAG: <> IntConstant 100 loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none @@ -129,7 +129,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-{ARM64,MIPS64}: void Main.doitMin100(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void ShortSimdMinMax.doitMin100(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 100 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none @@ -142,7 +142,7 @@ public class Main { } } - public static void main(String[] args) { + public static void main() { short[] interesting = { (short) 0x0000, (short) 0x0001, (short) 0x007f, (short) 0x0080, (short) 0x0081, (short) 0x00ff, @@ -199,7 +199,7 @@ public class Main { expectEquals(expected, x[i]); } - System.out.println("passed"); + System.out.println("ShortSimdMinMax passed"); } private static void expectEquals(short expected, short result) { -- GitLab From 8e1a7cb303d7c8f763dfb99ae311b820996b1ab4 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Tue, 27 Mar 2018 08:14:25 +0000 Subject: [PATCH 149/749] Revert "Revert "Allow hidden API access from system libraries"" Libraries like RemoteDisplay provide an APK that an app loads into its process and which accesses internal APIs on the app's behalf, without exposing the internals to the app. These libraries are considered part of the platform, but were not exempt from hidden API checks because they are not loaded with the boot strap class loader. This patch adds a new flag to DexFile class which the constructor sets to true of the canonical location of the newly loaded dex file starts with "${ANDROID_ROOT}/framework/". Hidden API enforcement then checks this flag when determining whether the caller of a hidden class member is allowed to access it or not. This reverts commit 0127b71a2588efcd1a53c192c5c267157878b010. Previous CL saw two issues: - buildbots would set non-existent ANDROID_ROOT for host-side builds - calling realpath on unquickened dex files would overflow the stack Bug: 64382372 Bug: 76138670 Bug: 76165623 Bug: 76112393 Bug: 76452688 Bug: 76429651 Test: art/test.py --target -r -b -t 674-hiddenapi Test: SystemUI APCT test Change-Id: Ie07a088509002593353965d3d24bf7362b643f40 --- libdexfile/dex/dex_file.cc | 3 +- libdexfile/dex/dex_file.h | 11 +++ openjdkjvmti/fixed_up_dex_file.cc | 12 +++- runtime/base/file_utils.cc | 17 +++++ runtime/base/file_utils.h | 3 + runtime/class_linker.cc | 12 ++-- runtime/dex/art_dex_file_loader.cc | 36 ++++++++++ runtime/dex/art_dex_file_loader.h | 13 ++++ runtime/dex/art_dex_file_loader_test.cc | 90 +++++++++++++++++++++++- runtime/hidden_api.h | 43 ++++++++--- runtime/interpreter/unstarted_runtime.cc | 1 + runtime/jni_internal.cc | 9 +-- runtime/native/java_lang_Class.cc | 10 +-- 13 files changed, 230 insertions(+), 30 deletions(-) diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc index 4613b40457..1f471106df 100644 --- a/libdexfile/dex/dex_file.cc +++ b/libdexfile/dex/dex_file.cc @@ -121,7 +121,8 @@ DexFile::DexFile(const uint8_t* base, num_call_site_ids_(0), oat_dex_file_(oat_dex_file), container_(std::move(container)), - is_compact_dex_(is_compact_dex) { + is_compact_dex_(is_compact_dex), + is_platform_dex_(false) { CHECK(begin_ != nullptr) << GetLocation(); CHECK_GT(size_, 0U) << GetLocation(); // Check base (=header) alignment. diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index aeb49d2c25..683a8243ed 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -992,6 +992,14 @@ class DexFile { ALWAYS_INLINE const StandardDexFile* AsStandardDexFile() const; ALWAYS_INLINE const CompactDexFile* AsCompactDexFile() const; + ALWAYS_INLINE bool IsPlatformDexFile() const { + return is_platform_dex_; + } + + ALWAYS_INLINE void SetIsPlatformDexFile() { + is_platform_dex_ = true; + } + bool IsInMainSection(const void* addr) const { return Begin() <= addr && addr < Begin() + Size(); } @@ -1094,6 +1102,9 @@ class DexFile { // If the dex file is a compact dex file. If false then the dex file is a standard dex file. const bool is_compact_dex_; + // If the dex file is located in /system/framework/. + bool is_platform_dex_; + friend class DexFileLoader; friend class DexFileVerifierTest; friend class OatWriter; diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc index ee83c4a4e8..fcbafe7e71 100644 --- a/openjdkjvmti/fixed_up_dex_file.cc +++ b/openjdkjvmti/fixed_up_dex_file.cc @@ -31,7 +31,6 @@ #include "base/leb128.h" #include "fixed_up_dex_file.h" -#include "dex/art_dex_file_loader.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "dex/dex_file_verifier.h" @@ -108,7 +107,12 @@ std::unique_ptr FixedUpDexFile::Create(const art::DexFile& origi std::vector data; std::unique_ptr new_dex_file; std::string error; - const art::ArtDexFileLoader dex_file_loader; + + // Do not use ArtDexFileLoader here. This code runs in a signal handler and + // its stack is too small to invoke the required LocationIsOnSystemFramework + // (b/76429651). Instead, we use DexFileLoader and copy the IsPlatformDexFile + // property from `original` to `new_dex_file`. + const art::DexFileLoader dex_file_loader; if (original.IsCompactDexFile()) { // Since we are supposed to return a standard dex, convert back using dexlayout. It's OK to do @@ -159,6 +163,10 @@ std::unique_ptr FixedUpDexFile::Create(const art::DexFile& origi return nullptr; } + if (original.IsPlatformDexFile()) { + const_cast(new_dex_file.get())->SetIsPlatformDexFile(); + } + DoDexUnquicken(*new_dex_file, original); RecomputeDexChecksum(const_cast(new_dex_file.get())); diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc index 1cb3b9c380..2b3e360650 100644 --- a/runtime/base/file_utils.cc +++ b/runtime/base/file_utils.cc @@ -319,4 +319,21 @@ bool LocationIsOnSystem(const char* location) { return path != nullptr && android::base::StartsWith(path.get(), GetAndroidRoot().c_str()); } +bool LocationIsOnSystemFramework(const char* location) { + std::string error_msg; + std::string root_path = GetAndroidRootSafe(&error_msg); + if (root_path.empty()) { + // Could not find Android root. + // TODO(dbrazdil): change to stricter GetAndroidRoot() once b/76452688 is resolved. + return false; + } + std::string framework_path = root_path + "/framework/"; + + // Warning: Bionic implementation of realpath() allocates > 12KB on the stack. + // Do not run this code on a small stack, e.g. in signal handler. + UniqueCPtr path(realpath(location, nullptr)); + return path != nullptr && + android::base::StartsWith(path.get(), framework_path.c_str()); +} + } // namespace art diff --git a/runtime/base/file_utils.h b/runtime/base/file_utils.h index 7f691d546b..8adb4f7bf8 100644 --- a/runtime/base/file_utils.h +++ b/runtime/base/file_utils.h @@ -75,6 +75,9 @@ std::string ReplaceFileExtension(const std::string& filename, const std::string& // Return whether the location is on system (i.e. android root). bool LocationIsOnSystem(const char* location); +// Return whether the location is on system/framework (i.e. android_root/framework). +bool LocationIsOnSystemFramework(const char* location); + } // namespace art #endif // ART_RUNTIME_BASE_FILE_UTILS_H_ diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 8b64b8def0..2110d0493b 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -7935,7 +7935,8 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr klass, } DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); if (resolved != nullptr && - hiddenapi::ShouldBlockAccessToMember(resolved, class_loader, hiddenapi::kLinking)) { + hiddenapi::ShouldBlockAccessToMember( + resolved, class_loader, dex_cache, hiddenapi::kLinking)) { resolved = nullptr; } if (resolved != nullptr) { @@ -8077,7 +8078,8 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx, resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_); } if (resolved != nullptr && - hiddenapi::ShouldBlockAccessToMember(resolved, class_loader.Get(), hiddenapi::kLinking)) { + hiddenapi::ShouldBlockAccessToMember( + resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) { resolved = nullptr; } return resolved; @@ -8156,7 +8158,8 @@ ArtField* ClassLinker::ResolveField(uint32_t field_idx, } if (resolved == nullptr || - hiddenapi::ShouldBlockAccessToMember(resolved, class_loader.Get(), hiddenapi::kLinking)) { + hiddenapi::ShouldBlockAccessToMember( + resolved, class_loader.Get(), dex_cache.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); @@ -8189,7 +8192,8 @@ ArtField* ClassLinker::ResolveFieldJLS(uint32_t field_idx, 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)) { + hiddenapi::ShouldBlockAccessToMember( + resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) { resolved = nullptr; } if (resolved != nullptr) { diff --git a/runtime/dex/art_dex_file_loader.cc b/runtime/dex/art_dex_file_loader.cc index 9802c6904b..f3e6a69279 100644 --- a/runtime/dex/art_dex_file_loader.cc +++ b/runtime/dex/art_dex_file_loader.cc @@ -22,6 +22,7 @@ #include "android-base/stringprintf.h" #include "base/file_magic.h" +#include "base/file_utils.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/unix_file/fd_file.h" @@ -505,4 +506,39 @@ bool ArtDexFileLoader::OpenAllDexFilesFromZip( } } +std::unique_ptr ArtDexFileLoader::OpenCommon(const uint8_t* base, + size_t size, + const uint8_t* data_base, + size_t data_size, + const std::string& location, + uint32_t location_checksum, + const OatDexFile* oat_dex_file, + bool verify, + bool verify_checksum, + std::string* error_msg, + std::unique_ptr container, + VerifyResult* verify_result) { + std::unique_ptr dex_file = DexFileLoader::OpenCommon(base, + size, + data_base, + data_size, + location, + location_checksum, + oat_dex_file, + verify, + verify_checksum, + error_msg, + std::move(container), + verify_result); + + // Check if this dex file is located in the framework directory. + // If it is, set a flag on the dex file. This is used by hidden API + // policy decision logic. + if (dex_file != nullptr && LocationIsOnSystemFramework(location.c_str())) { + dex_file->SetIsPlatformDexFile(); + } + + return dex_file; +} + } // namespace art diff --git a/runtime/dex/art_dex_file_loader.h b/runtime/dex/art_dex_file_loader.h index 7c7a59b37f..7577945632 100644 --- a/runtime/dex/art_dex_file_loader.h +++ b/runtime/dex/art_dex_file_loader.h @@ -120,6 +120,19 @@ class ArtDexFileLoader : public DexFileLoader { bool verify_checksum, std::string* error_msg, ZipOpenErrorCode* error_code) const; + + static std::unique_ptr OpenCommon(const uint8_t* base, + size_t size, + const uint8_t* data_base, + size_t data_size, + const std::string& location, + uint32_t location_checksum, + const OatDexFile* oat_dex_file, + bool verify, + bool verify_checksum, + std::string* error_msg, + std::unique_ptr container, + VerifyResult* verify_result); }; } // namespace art diff --git a/runtime/dex/art_dex_file_loader_test.cc b/runtime/dex/art_dex_file_loader_test.cc index 6e2cfec381..3e0d6662c7 100644 --- a/runtime/dex/art_dex_file_loader_test.cc +++ b/runtime/dex/art_dex_file_loader_test.cc @@ -16,9 +16,11 @@ #include +#include #include #include "art_dex_file_loader.h" +#include "base/file_utils.h" #include "base/os.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" @@ -35,7 +37,40 @@ namespace art { -class ArtDexFileLoaderTest : public CommonRuntimeTest {}; +static void Copy(const std::string& src, const std::string& dst) { + std::ifstream src_stream(src, std::ios::binary); + std::ofstream dst_stream(dst, std::ios::binary); + dst_stream << src_stream.rdbuf(); +} + +class ArtDexFileLoaderTest : public CommonRuntimeTest { + public: + virtual void SetUp() { + CommonRuntimeTest::SetUp(); + + std::string dex_location = GetTestDexFileName("Main"); + + data_location_path_ = android_data_ + "/foo.jar"; + system_location_path_ = GetAndroidRoot() + "/foo.jar"; + system_framework_location_path_ = GetAndroidRoot() + "/framework/foo.jar"; + + Copy(dex_location, data_location_path_); + Copy(dex_location, system_location_path_); + Copy(dex_location, system_framework_location_path_); + } + + virtual void TearDown() { + remove(data_location_path_.c_str()); + remove(system_location_path_.c_str()); + remove(system_framework_location_path_.c_str()); + CommonRuntimeTest::TearDown(); + } + + protected: + std::string data_location_path_; + std::string system_location_path_; + std::string system_framework_location_path_; +}; // TODO: Port OpenTestDexFile(s) need to be ported to use non-ART utilities, and // the tests that depend upon them should be moved to dex_file_loader_test.cc @@ -304,4 +339,57 @@ TEST_F(ArtDexFileLoaderTest, GetDexCanonicalLocation) { ASSERT_EQ(0, unlink(dex_location_sym.c_str())); } +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { + ArtDexFileLoader loader; + bool success; + std::string error_msg; + std::vector> dex_files; + + // Load file from a non-system directory and check that it is not flagged as framework. + ASSERT_FALSE(LocationIsOnSystemFramework(data_location_path_.c_str())); + success = loader.Open(data_location_path_.c_str(), + data_location_path_, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success); + ASSERT_GE(dex_files.size(), 1u); + for (std::unique_ptr& dex_file : dex_files) { + ASSERT_FALSE(dex_file->IsPlatformDexFile()); + } + + dex_files.clear(); + + // Load file from a system, non-framework directory and check that it is not flagged as framework. + ASSERT_FALSE(LocationIsOnSystemFramework(system_location_path_.c_str())); + success = loader.Open(system_location_path_.c_str(), + system_location_path_, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success); + ASSERT_GE(dex_files.size(), 1u); + for (std::unique_ptr& dex_file : dex_files) { + ASSERT_FALSE(dex_file->IsPlatformDexFile()); + } + + dex_files.clear(); + + // Load file from a system/framework directory and check that it is flagged as a framework dex. + ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_location_path_.c_str())); + success = loader.Open(system_framework_location_path_.c_str(), + system_framework_location_path_, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success); + ASSERT_GE(dex_files.size(), 1u); + for (std::unique_ptr& dex_file : dex_files) { + ASSERT_TRUE(dex_file->IsPlatformDexFile()); + } +} + } // namespace art diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index 5c6b4b56bc..dbe776e050 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -130,15 +130,15 @@ inline void WarnAboutMemberAccess(ArtMethod* method, AccessMethod access_method) } // Returns true if access to `member` should be denied to the caller of the -// reflective query. The decision is based on whether the caller is in boot -// 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. +// reflective query. The decision is based on whether the caller is in the +// platform or not. Because different users of this function determine this +// in a different way, `fn_caller_in_platform(self)` is called and should +// return true if the caller is located in the platform. // This function might print warnings into the log if the member is hidden. template inline bool ShouldBlockAccessToMember(T* member, Thread* self, - std::function fn_caller_in_boot, + std::function fn_caller_in_platform, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); @@ -149,14 +149,14 @@ inline bool ShouldBlockAccessToMember(T* member, return false; } - // Member is hidden. Walk the stack to find the caller. + // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access. // This can be *very* expensive. Save it for last. - if (fn_caller_in_boot(self)) { - // Caller in boot class path. Exit. + if (fn_caller_in_platform(self)) { + // Caller in the platform. Exit. return false; } - // Member is hidden and we are not in the boot class path. + // Member is hidden and caller is not in the platform. // Print a log message with information about this class member access. // We do this regardless of whether we block the access or not. @@ -187,18 +187,39 @@ inline bool ShouldBlockAccessToMember(T* member, return false; } +// Returns true if the caller is either loaded by the boot strap class loader or comes from +// a dex file located in ${ANDROID_ROOT}/framework/. +inline bool IsCallerInPlatformDex(ObjPtr caller_class_loader, + ObjPtr caller_dex_cache) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (caller_class_loader.IsNull()) { + return true; + } else if (caller_dex_cache.IsNull()) { + return false; + } else { + const DexFile* caller_dex_file = caller_dex_cache->GetDexFile(); + return caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile(); + } +} + +inline bool IsCallerInPlatformDex(ObjPtr caller) + REQUIRES_SHARED(Locks::mutator_lock_) { + return !caller.IsNull() && IsCallerInPlatformDex(caller->GetClassLoader(), caller->GetDexCache()); +} + // 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 inline bool ShouldBlockAccessToMember(T* member, ObjPtr caller_class_loader, + ObjPtr caller_dex_cache, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { - bool caller_in_boot = (caller_class_loader.IsNull()); + bool caller_in_platform = IsCallerInPlatformDex(caller_class_loader, caller_dex_cache); return ShouldBlockAccessToMember(member, /* thread */ nullptr, - [caller_in_boot] (Thread*) { return caller_in_boot; }, + [caller_in_platform] (Thread*) { return caller_in_platform; }, access_method); } diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 76df65f730..4a2dd3bc3f 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -184,6 +184,7 @@ static ALWAYS_INLINE bool ShouldBlockAccessToMember(T* member, ShadowFrame* fram return hiddenapi::ShouldBlockAccessToMember( member, frame->GetMethod()->GetDeclaringClass()->GetClassLoader(), + frame->GetMethod()->GetDeclaringClass()->GetDexCache(), hiddenapi::kReflection); // all uses in this file are from reflection } diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index b78fcacc08..f309581735 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -80,18 +80,15 @@ namespace art { // things not rendering correctly. E.g. b/16858794 static constexpr bool kWarnJniAbort = false; -static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr klass = GetCallingClass(self, /* num_frames */ 1); - // If `klass` is null, it is an unattached native thread. Assume this is - // *not* boot class path. - return klass != nullptr && klass->IsBootStrapClassLoaded(); +static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { + return hiddenapi::IsCallerInPlatformDex(GetCallingClass(self, /* num_frames */ 1)); } template ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { return hiddenapi::ShouldBlockAccessToMember( - member, self, IsCallerInBootClassPath, hiddenapi::kJNI); + member, self, IsCallerInPlatformDex, hiddenapi::kJNI); } // Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index fc61c9597e..ad05856eaf 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -49,8 +49,8 @@ namespace art { -// Returns true if the first non-ClassClass caller up the stack is in boot class path. -static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { +// Returns true if the first non-ClassClass caller up the stack is in a platform dex file. +static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { // Walk the stack and find the first frame not from java.lang.Class. // This is very expensive. Save this till the last. struct FirstNonClassClassCallerVisitor : public StackVisitor { @@ -82,7 +82,7 @@ static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator FirstNonClassClassCallerVisitor visitor(self); visitor.WalkStack(); return visitor.caller != nullptr && - visitor.caller->GetDeclaringClass()->IsBootStrapClassLoaded(); + hiddenapi::IsCallerInPlatformDex(visitor.caller->GetDeclaringClass()); } // Returns true if the first non-ClassClass caller up the stack is not allowed to @@ -90,7 +90,7 @@ static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); - return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInBootClassPath(self); + return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInPlatformDex(self); } // Returns true if the first non-ClassClass caller up the stack should not be @@ -99,7 +99,7 @@ template ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { return hiddenapi::ShouldBlockAccessToMember( - member, self, IsCallerInBootClassPath, hiddenapi::kReflection); + member, self, IsCallerInPlatformDex, hiddenapi::kReflection); } // Returns true if a class member should be discoverable with reflection given -- GitLab From 9222417fdcf7c504fe86fa88b8a77363cb941e11 Mon Sep 17 00:00:00 2001 From: Alan Leung Date: Fri, 23 Mar 2018 11:30:36 -0700 Subject: [PATCH 150/749] Move most art test off DX We should move testing to D8 as dexer and prevent further accumulation of DX dependent tests from being checked in. Bug: 65168732 Test: run-test ... 646-checker-hadd-alt-short && run_build_test_target.py art-test && run_build_test_target.py -jvm Change-Id: Ibb76eb82b9517ae4bec2b031bab6eb782909d745 --- test/166-bad-interface-super/build | 20 ++++++++++++++++++++ test/646-checker-hadd-alt-char/build | 20 ++++++++++++++++++++ test/646-checker-hadd-alt-short/build | 20 ++++++++++++++++++++ test/646-checker-hadd-char/build | 20 ++++++++++++++++++++ test/646-checker-hadd-short/build | 20 ++++++++++++++++++++ test/651-checker-short-simd-minmax/build | 20 ++++++++++++++++++++ test/660-checker-simd-sad-byte/build | 20 ++++++++++++++++++++ test/660-checker-simd-sad-char/build | 20 ++++++++++++++++++++ test/660-checker-simd-sad-int/build | 20 ++++++++++++++++++++ test/660-checker-simd-sad-short/build | 20 ++++++++++++++++++++ test/660-checker-simd-sad-short2/build | 20 ++++++++++++++++++++ test/660-checker-simd-sad-short3/build | 20 ++++++++++++++++++++ test/661-checker-simd-reduc/build | 20 ++++++++++++++++++++ test/672-checker-throw-method/build | 20 ++++++++++++++++++++ test/673-checker-throw-vmethod/build | 20 ++++++++++++++++++++ test/678-checker-simd-saturation/build | 20 ++++++++++++++++++++ test/712-varhandle-invocations/build | 3 +++ test/Android.run-test.mk | 7 +------ test/etc/default-build | 2 +- test/run-test | 5 +---- test/testrunner/env.py | 3 --- test/testrunner/testrunner.py | 3 --- tools/build/var_list | 1 - 23 files changed, 326 insertions(+), 18 deletions(-) create mode 100644 test/166-bad-interface-super/build create mode 100644 test/646-checker-hadd-alt-char/build create mode 100644 test/646-checker-hadd-alt-short/build create mode 100644 test/646-checker-hadd-char/build create mode 100644 test/646-checker-hadd-short/build create mode 100644 test/651-checker-short-simd-minmax/build create mode 100644 test/660-checker-simd-sad-byte/build create mode 100644 test/660-checker-simd-sad-char/build create mode 100644 test/660-checker-simd-sad-int/build create mode 100644 test/660-checker-simd-sad-short/build create mode 100644 test/660-checker-simd-sad-short2/build create mode 100644 test/660-checker-simd-sad-short3/build create mode 100644 test/661-checker-simd-reduc/build create mode 100644 test/672-checker-throw-method/build create mode 100644 test/673-checker-throw-vmethod/build create mode 100644 test/678-checker-simd-saturation/build diff --git a/test/166-bad-interface-super/build b/test/166-bad-interface-super/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/166-bad-interface-super/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/646-checker-hadd-alt-char/build b/test/646-checker-hadd-alt-char/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/646-checker-hadd-alt-char/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/646-checker-hadd-alt-short/build b/test/646-checker-hadd-alt-short/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/646-checker-hadd-alt-short/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/646-checker-hadd-char/build b/test/646-checker-hadd-char/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/646-checker-hadd-char/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/646-checker-hadd-short/build b/test/646-checker-hadd-short/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/646-checker-hadd-short/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/651-checker-short-simd-minmax/build b/test/651-checker-short-simd-minmax/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/651-checker-short-simd-minmax/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/660-checker-simd-sad-byte/build b/test/660-checker-simd-sad-byte/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/660-checker-simd-sad-byte/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/660-checker-simd-sad-char/build b/test/660-checker-simd-sad-char/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/660-checker-simd-sad-char/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/660-checker-simd-sad-int/build b/test/660-checker-simd-sad-int/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/660-checker-simd-sad-int/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/660-checker-simd-sad-short/build b/test/660-checker-simd-sad-short/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/660-checker-simd-sad-short/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/660-checker-simd-sad-short2/build b/test/660-checker-simd-sad-short2/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/660-checker-simd-sad-short2/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/660-checker-simd-sad-short3/build b/test/660-checker-simd-sad-short3/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/660-checker-simd-sad-short3/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/661-checker-simd-reduc/build b/test/661-checker-simd-reduc/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/661-checker-simd-reduc/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/672-checker-throw-method/build b/test/672-checker-throw-method/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/672-checker-throw-method/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/673-checker-throw-vmethod/build b/test/673-checker-throw-vmethod/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/673-checker-throw-vmethod/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/678-checker-simd-saturation/build b/test/678-checker-simd-saturation/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/678-checker-simd-saturation/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/712-varhandle-invocations/build b/test/712-varhandle-invocations/build index 253765be91..6d4429f0ef 100755 --- a/test/712-varhandle-invocations/build +++ b/test/712-varhandle-invocations/build @@ -35,5 +35,8 @@ python3 ./util-src/generate_java.py "${GENERATED_SRC}" ${MANUAL_TESTS} # Desugar is not happy with our Java 9 byte code, it shouldn't be necessary here anyway. export USE_DESUGAR=false +# See b/65168732 +export USE_D8=false + # Invoke default build with increased heap size for dx ./default-build "$@" --experimental var-handles --dx-vm-option -JXmx384m diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 6633958140..f8bebdd35f 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -21,17 +21,12 @@ include art/build/Android.common_test.mk TEST_ART_RUN_TEST_DEPENDENCIES := \ $(HOST_OUT_EXECUTABLES)/dx \ $(HOST_OUT_EXECUTABLES)/d8 \ + $(HOST_OUT_EXECUTABLES)/d8-compat-dx \ $(HOST_OUT_EXECUTABLES)/hiddenapi \ $(HOST_OUT_EXECUTABLES)/jasmin \ $(HOST_OUT_EXECUTABLES)/smali \ $(HOST_OUT_JAVA_LIBRARIES)/desugar.jar -# Add d8 dependency, if enabled. -ifeq ($(USE_D8),true) -TEST_ART_RUN_TEST_DEPENDENCIES += \ - $(HOST_OUT_EXECUTABLES)/d8-compat-dx -endif - # We need dex2oat and dalvikvm on the target as well as the core images (all images as we sync # only once). TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUTS) diff --git a/test/etc/default-build b/test/etc/default-build index 9de7294a59..dd5560213a 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -317,7 +317,7 @@ function make_dex() { fi local dexer="${DX}" - if [ ${USE_D8} = "true" ]; then + if [[ "${USE_D8}" != "false" ]]; then dexer="${ANDROID_HOST_OUT}/bin/d8-compat-dx" fi diff --git a/test/run-test b/test/run-test index 5b43b52b41..5f85b0875b 100755 --- a/test/run-test +++ b/test/run-test @@ -45,7 +45,7 @@ export JAVAC="javac -g -Xlint:-options" export RUN="${progdir}/etc/run-test-jar" export DEX_LOCATION=/data/run-test/${test_dir} export NEED_DEX="true" -export USE_D8="false" +export USE_D8="true" export USE_JACK="false" export USE_DESUGAR="true" export SMALI_ARGS="" @@ -365,9 +365,6 @@ while true; do elif [ "x$1" = "x--build-only" ]; then build_only="yes" shift - elif [ "x$1" = "x--build-with-d8" ]; then - USE_D8="true" - shift elif [ "x$1" = "x--build-with-javac-dx" ]; then USE_JACK="false" shift diff --git a/test/testrunner/env.py b/test/testrunner/env.py index 539499173c..7564f5a6b4 100644 --- a/test/testrunner/env.py +++ b/test/testrunner/env.py @@ -71,9 +71,6 @@ ANDROID_BUILD_TOP = _get_android_build_top() # Compiling with jack? Possible values in (True, False, 'default') ANDROID_COMPILE_WITH_JACK = _get_build_var_boolean('ANDROID_COMPILE_WITH_JACK', 'default') -# Follow the build system's D8 usage. -USE_D8_BY_DEFAULT = _get_build_var_boolean('USE_D8_BY_DEFAULT', False) - # Directory used for temporary test files on the host. ART_HOST_TEST_DIR = tempfile.mkdtemp(prefix = 'test-art-') diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 734a600c5e..99bab097ab 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -504,9 +504,6 @@ def run_tests(tests): elif env.ANDROID_COMPILE_WITH_JACK == False: options_test += ' --build-with-javac-dx' - if env.USE_D8_BY_DEFAULT == True: - options_test += ' --build-with-d8' - # TODO(http://36039166): This is a temporary solution to # fix build breakages. options_test = (' --output-path %s') % ( diff --git a/tools/build/var_list b/tools/build/var_list index 3727741dac..adcb066f7c 100644 --- a/tools/build/var_list +++ b/tools/build/var_list @@ -34,5 +34,4 @@ HOST_PREFER_32_BIT HOST_OUT_EXECUTABLES ANDROID_JAVA_TOOLCHAIN ANDROID_COMPILE_WITH_JACK -USE_D8_BY_DEFAULT -- GitLab From 642e9d8249be5aff68022cabdc8ba576a57ff8d6 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Tue, 27 Mar 2018 20:30:55 +0000 Subject: [PATCH 151/749] Revert "Revert "Revert "Add an option to disable native stack dumping on SIGQUIT.""" This reverts commit 74d25c9040dfd1e0985987eb38817e526878a3db. Reason for revert: The original failing condition appears to be gone. Bug: 74121887 Test: Ran 004-ThreadStress many times against the target. Change-Id: Ie5bd050112e654a99bdfea7d6dd673882ca35567 --- runtime/parsed_options.cc | 5 ----- runtime/runtime.cc | 2 -- runtime/runtime.h | 7 ------- runtime/runtime_common.cc | 3 +-- runtime/runtime_options.def | 1 - runtime/thread.cc | 12 ++++-------- runtime/thread.h | 2 -- runtime/thread_list.cc | 21 ++++++++------------- runtime/thread_list.h | 2 +- test/etc/run-test-jar | 4 ---- 10 files changed, 14 insertions(+), 45 deletions(-) diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 5518eb2c49..470287b449 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -161,10 +161,6 @@ std::unique_ptr ParsedOptions::MakeParser(bool ignore_unrecognize .Define({"-XX:EnableHSpaceCompactForOOM", "-XX:DisableHSpaceCompactForOOM"}) .WithValues({true, false}) .IntoKey(M::EnableHSpaceCompactForOOM) - .Define("-XX:DumpNativeStackOnSigQuit:_") - .WithType() - .WithValueMap({{"false", false}, {"true", true}}) - .IntoKey(M::DumpNativeStackOnSigQuit) .Define("-XX:MadviseRandomAccess:_") .WithType() .WithValueMap({{"false", false}, {"true", true}}) @@ -735,7 +731,6 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -XX:BackgroundGC=none\n"); UsageMessage(stream, " -XX:LargeObjectSpace={disabled,map,freelist}\n"); UsageMessage(stream, " -XX:LargeObjectThreshold=N\n"); - UsageMessage(stream, " -XX:DumpNativeStackOnSigQuit=booleanvalue\n"); UsageMessage(stream, " -XX:MadviseRandomAccess:booleanvalue\n"); UsageMessage(stream, " -XX:SlowDebug={false,true}\n"); UsageMessage(stream, " -Xmethod-trace\n"); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 9a626bab00..bb76e61122 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -271,7 +271,6 @@ Runtime::Runtime() pending_hidden_api_warning_(false), dedupe_hidden_api_warnings_(true), always_set_hidden_api_warning_flag_(false), - dump_native_stack_on_sig_quit_(true), pruned_dalvik_cache_(false), // Initially assume we perceive jank in case the process state is never updated. process_state_(kProcessStateJankPerceptible), @@ -1153,7 +1152,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { is_explicit_gc_disabled_ = runtime_options.Exists(Opt::DisableExplicitGC); dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::Dex2Oat); image_dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::ImageDex2Oat); - dump_native_stack_on_sig_quit_ = runtime_options.GetOrDefault(Opt::DumpNativeStackOnSigQuit); vfprintf_ = runtime_options.GetOrDefault(Opt::HookVfprintf); exit_ = runtime_options.GetOrDefault(Opt::HookExit); diff --git a/runtime/runtime.h b/runtime/runtime.h index dba31b2939..7d7cbafe23 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -655,10 +655,6 @@ class Runtime { safe_mode_ = mode; } - bool GetDumpNativeStackOnSigQuit() const { - return dump_native_stack_on_sig_quit_; - } - bool GetPrunedDalvikCache() const { return pruned_dalvik_cache_; } @@ -1009,9 +1005,6 @@ class Runtime { // when there is a warning. This is only used for testing. bool always_set_hidden_api_warning_flag_; - // Whether threads should dump their native stack on SIGQUIT. - bool dump_native_stack_on_sig_quit_; - // Whether the dalvik cache was pruned when initializing the runtime. bool pruned_dalvik_cache_; diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc index 59af9187f9..41bfb58d93 100644 --- a/runtime/runtime_common.cc +++ b/runtime/runtime_common.cc @@ -41,7 +41,6 @@ namespace art { using android::base::StringPrintf; static constexpr bool kUseSigRTTimeout = true; -static constexpr bool kDumpNativeStackOnTimeout = true; const char* GetSignalName(int signal_number) { switch (signal_number) { @@ -441,7 +440,7 @@ void HandleUnexpectedSignalCommon(int signal_number, // Special timeout signal. Try to dump all threads. // Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts // are of value here. - runtime->GetThreadList()->Dump(std::cerr, kDumpNativeStackOnTimeout); + runtime->GetThreadList()->Dump(std::cerr); std::cerr << std::endl; } diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 4121ad69ed..dcb1335023 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -70,7 +70,6 @@ RUNTIME_OPTIONS_KEY (Unit, LowMemoryMode) RUNTIME_OPTIONS_KEY (bool, UseTLAB, (kUseTlab || kUseReadBarrier)) RUNTIME_OPTIONS_KEY (bool, EnableHSpaceCompactForOOM, true) RUNTIME_OPTIONS_KEY (bool, UseJitCompilation, false) -RUNTIME_OPTIONS_KEY (bool, DumpNativeStackOnSigQuit, true) RUNTIME_OPTIONS_KEY (bool, MadviseRandomAccess, false) RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold) RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold) diff --git a/runtime/thread.cc b/runtime/thread.cc index b13d8ec42a..50cf9e0bc4 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1161,10 +1161,9 @@ void Thread::ShortDump(std::ostream& os) const { << "]"; } -void Thread::Dump(std::ostream& os, bool dump_native_stack, BacktraceMap* backtrace_map, - bool force_dump_stack) const { +void Thread::Dump(std::ostream& os, BacktraceMap* backtrace_map, bool force_dump_stack) const { DumpState(os); - DumpStack(os, dump_native_stack, backtrace_map, force_dump_stack); + DumpStack(os, backtrace_map, force_dump_stack); } mirror::String* Thread::GetThreadName() const { @@ -1968,10 +1967,7 @@ void Thread::DumpJavaStack(std::ostream& os, bool check_suspended, bool dump_loc } } -void Thread::DumpStack(std::ostream& os, - bool dump_native_stack, - BacktraceMap* backtrace_map, - bool force_dump_stack) const { +void Thread::DumpStack(std::ostream& os, BacktraceMap* backtrace_map, bool force_dump_stack) const { // TODO: we call this code when dying but may not have suspended the thread ourself. The // IsSuspended check is therefore racy with the use for dumping (normally we inhibit // the race with the thread_suspend_count_lock_). @@ -1984,7 +1980,7 @@ void Thread::DumpStack(std::ostream& os, } if (safe_to_dump || force_dump_stack) { // If we're currently in native code, dump that stack before dumping the managed stack. - if (dump_native_stack && (dump_for_abort || force_dump_stack || ShouldShowNativeStack(this))) { + if (dump_for_abort || force_dump_stack || ShouldShowNativeStack(this)) { DumpKernelStack(os, GetTid(), " kernel: ", false); ArtMethod* method = GetCurrentMethod(nullptr, diff --git a/runtime/thread.h b/runtime/thread.h index 22b77eea64..af1401ee93 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -207,7 +207,6 @@ class Thread { // Dumps the detailed thread state and the thread stack (used for SIGQUIT). void Dump(std::ostream& os, - bool dump_native_stack = true, BacktraceMap* backtrace_map = nullptr, bool force_dump_stack = false) const REQUIRES(!Locks::thread_suspend_count_lock_) @@ -1318,7 +1317,6 @@ class Thread { void DumpState(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_); void DumpStack(std::ostream& os, - bool dump_native_stack = true, BacktraceMap* backtrace_map = nullptr, bool force_dump_stack = false) const REQUIRES(!Locks::thread_suspend_count_lock_) diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 44af867d60..ee683992ba 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -152,9 +152,8 @@ void ThreadList::DumpForSigQuit(std::ostream& os) { suspend_all_historam_.PrintConfidenceIntervals(os, 0.99, data); // Dump time to suspend. } } - bool dump_native_stack = Runtime::Current()->GetDumpNativeStackOnSigQuit(); - Dump(os, dump_native_stack); - DumpUnattachedThreads(os, dump_native_stack && kDumpUnattachedThreadNativeStackForSigQuit); + Dump(os); + DumpUnattachedThreads(os, kDumpUnattachedThreadNativeStackForSigQuit); } static void DumpUnattachedThread(std::ostream& os, pid_t tid, bool dump_native_stack) @@ -201,11 +200,10 @@ static constexpr uint32_t kDumpWaitTimeout = kIsTargetBuild ? 100000 : 20000; // A closure used by Thread::Dump. class DumpCheckpoint FINAL : public Closure { public: - DumpCheckpoint(std::ostream* os, bool dump_native_stack) + explicit DumpCheckpoint(std::ostream* os) : os_(os), barrier_(0), - backtrace_map_(dump_native_stack ? BacktraceMap::Create(getpid()) : nullptr), - dump_native_stack_(dump_native_stack) { + backtrace_map_(BacktraceMap::Create(getpid())) { if (backtrace_map_ != nullptr) { backtrace_map_->SetSuffixesToIgnore(std::vector { "oat", "odex" }); } @@ -219,7 +217,7 @@ class DumpCheckpoint FINAL : public Closure { std::ostringstream local_os; { ScopedObjectAccess soa(self); - thread->Dump(local_os, dump_native_stack_, backtrace_map_.get()); + thread->Dump(local_os, backtrace_map_.get()); } { // Use the logging lock to ensure serialization when writing to the common ostream. @@ -247,18 +245,16 @@ class DumpCheckpoint FINAL : public Closure { Barrier barrier_; // A backtrace map, so that all threads use a shared info and don't reacquire/parse separately. std::unique_ptr backtrace_map_; - // Whether we should dump the native stack. - const bool dump_native_stack_; }; -void ThreadList::Dump(std::ostream& os, bool dump_native_stack) { +void ThreadList::Dump(std::ostream& os) { Thread* self = Thread::Current(); { MutexLock mu(self, *Locks::thread_list_lock_); os << "DALVIK THREADS (" << list_.size() << "):\n"; } if (self != nullptr) { - DumpCheckpoint checkpoint(&os, dump_native_stack); + DumpCheckpoint checkpoint(&os); size_t threads_running_checkpoint; { // Use SOA to prevent deadlocks if multiple threads are calling Dump() at the same time. @@ -269,7 +265,7 @@ void ThreadList::Dump(std::ostream& os, bool dump_native_stack) { checkpoint.WaitForThreadsToRunThroughCheckpoint(threads_running_checkpoint); } } else { - DumpUnattachedThreads(os, dump_native_stack); + DumpUnattachedThreads(os, /* dump_native_stack */ true); } } @@ -491,7 +487,6 @@ void ThreadList::RunEmptyCheckpoint() { // Found a runnable thread that hasn't responded to the empty checkpoint request. // Assume it's stuck and safe to dump its stack. thread->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT), - /*dump_native_stack*/ true, /*backtrace_map*/ nullptr, /*force_dump_stack*/ true); } diff --git a/runtime/thread_list.h b/runtime/thread_list.h index 895c1a41ce..09b10d2ad3 100644 --- a/runtime/thread_list.h +++ b/runtime/thread_list.h @@ -57,7 +57,7 @@ class ThreadList { void DumpForSigQuit(std::ostream& os) REQUIRES(!Locks::thread_list_lock_, !Locks::mutator_lock_); // For thread suspend timeout dumps. - void Dump(std::ostream& os, bool dump_native_stack = true) + void Dump(std::ostream& os) REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_); pid_t GetLockOwner(); // For SignalCatcher. diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index e9127a8101..86adb733a9 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -775,9 +775,6 @@ if [ "$HOST" = "n" ]; then TMP_DIR_OPTION="-Djava.io.tmpdir=/data/local/tmp" fi -# We set DumpNativeStackOnSigQuit to false to avoid stressing libunwind. -# b/27185632 -# b/24664297 dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \ $GDB_ARGS \ $FLAGS \ @@ -792,7 +789,6 @@ dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \ $DEBUGGER_OPTS \ $DALVIKVM_BOOT_OPT \ $TMP_DIR_OPTION \ - -XX:DumpNativeStackOnSigQuit:false \ -cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX $MAIN $ARGS" # Remove whitespace. -- GitLab From a0b252726ea12d3bc1834aad2a4230447d562181 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Tue, 27 Mar 2018 17:04:44 -0700 Subject: [PATCH 152/749] Fix mac build. Add extra parameter to DumpNativeStack for mac definition. Bug: 74121887 Test: NA Change-Id: I7b9ec40fb1f81a4e38e2ce06313df6ca6706312b --- runtime/native_stack_dump.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc index 0db1770eea..14f3f45f9e 100644 --- a/runtime/native_stack_dump.cc +++ b/runtime/native_stack_dump.cc @@ -429,7 +429,8 @@ void DumpNativeStack(std::ostream& os ATTRIBUTE_UNUSED, BacktraceMap* existing_map ATTRIBUTE_UNUSED, const char* prefix ATTRIBUTE_UNUSED, ArtMethod* current_method ATTRIBUTE_UNUSED, - void* ucontext_ptr ATTRIBUTE_UNUSED) { + void* ucontext_ptr ATTRIBUTE_UNUSED, + bool skip_frames ATTRIBUTE_UNUSED) { } void DumpKernelStack(std::ostream& os ATTRIBUTE_UNUSED, -- GitLab From 7f31326f7956d6a1630e7e53473b0581705796ec Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 28 Mar 2018 07:58:53 +0000 Subject: [PATCH 153/749] Revert "Refined add/sub analysis vis-a-vis SIMD idioms." Bug: b/74026074 Fails 551-checker-shifter-operand on target. This reverts commit 81a1f853925d88d19119e850e22b7f66bddef63b. Change-Id: If3094f73744bbb5f9ab1df4509f757df24af0047 --- compiler/optimizing/loop_optimization.cc | 162 +++++++++--------- test/646-checker-hadd-short/src/Main.java | 38 ---- test/660-checker-simd-sad-short/src/Main.java | 106 ------------ .../678-checker-simd-saturation/src/Main.java | 137 +-------------- 4 files changed, 89 insertions(+), 354 deletions(-) diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 71e24de141..1d83815e1f 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -227,7 +227,6 @@ static bool IsNarrowerOperands(HInstruction* a, /*out*/ HInstruction** r, /*out*/ HInstruction** s, /*out*/ bool* is_unsigned) { - DCHECK(a != nullptr && b != nullptr); // Look for a matching sign extension. DataType::Type stype = HVecOperation::ToSignedType(type); if (IsSignExtensionAndGet(a, stype, r) && IsSignExtensionAndGet(b, stype, s)) { @@ -248,7 +247,6 @@ static bool IsNarrowerOperand(HInstruction* a, DataType::Type type, /*out*/ HInstruction** r, /*out*/ bool* is_unsigned) { - DCHECK(a != nullptr); // Look for a matching sign extension. DataType::Type stype = HVecOperation::ToSignedType(type); if (IsSignExtensionAndGet(a, stype, r)) { @@ -272,28 +270,20 @@ static uint32_t GetOtherVL(DataType::Type other_type, DataType::Type vector_type return vl >> (DataType::SizeShift(other_type) - DataType::SizeShift(vector_type)); } -// Detect up to two added operands a and b and an acccumulated constant c. -static bool IsAddConst(HInstruction* instruction, - /*out*/ HInstruction** a, - /*out*/ HInstruction** b, - /*out*/ int64_t* c, - int32_t depth = 8) { // don't search too deep +// Detect up to two instructions a and b, and an acccumulated constant c. +static bool IsAddConstHelper(HInstruction* instruction, + /*out*/ HInstruction** a, + /*out*/ HInstruction** b, + /*out*/ int64_t* c, + int32_t depth) { + static constexpr int32_t kMaxDepth = 8; // don't search too deep int64_t value = 0; - // Enter add/sub while still within reasonable depth. - if (depth > 0) { - if (instruction->IsAdd()) { - return IsAddConst(instruction->InputAt(0), a, b, c, depth - 1) && - IsAddConst(instruction->InputAt(1), a, b, c, depth - 1); - } else if (instruction->IsSub() && - IsInt64AndGet(instruction->InputAt(1), &value)) { - *c -= value; - return IsAddConst(instruction->InputAt(0), a, b, c, depth - 1); - } - } - // Otherwise, deal with leaf nodes. if (IsInt64AndGet(instruction, &value)) { *c += value; return true; + } else if (instruction->IsAdd() && depth <= kMaxDepth) { + return IsAddConstHelper(instruction->InputAt(0), a, b, c, depth + 1) && + IsAddConstHelper(instruction->InputAt(1), a, b, c, depth + 1); } else if (*a == nullptr) { *a = instruction; return true; @@ -301,40 +291,42 @@ static bool IsAddConst(HInstruction* instruction, *b = instruction; return true; } - return false; // too many operands + return false; // too many non-const operands } -// Detect a + b + c with optional constant c. -static bool IsAddConst2(HGraph* graph, - HInstruction* instruction, - /*out*/ HInstruction** a, - /*out*/ HInstruction** b, - /*out*/ int64_t* c) { - if (IsAddConst(instruction, a, b, c) && *a != nullptr) { - if (*b == nullptr) { - // Constant is usually already present, unless accumulated. - *b = graph->GetConstant(instruction->GetType(), (*c)); - *c = 0; +// Detect a + b + c for an optional constant c. +static bool IsAddConst(HInstruction* instruction, + /*out*/ HInstruction** a, + /*out*/ HInstruction** b, + /*out*/ int64_t* c) { + if (instruction->IsAdd()) { + // Try to find a + b and accumulated c. + if (IsAddConstHelper(instruction->InputAt(0), a, b, c, /*depth*/ 0) && + IsAddConstHelper(instruction->InputAt(1), a, b, c, /*depth*/ 0) && + *b != nullptr) { + return true; } + // Found a + b. + *a = instruction->InputAt(0); + *b = instruction->InputAt(1); + *c = 0; return true; } return false; } -// Detect a direct a - b or a hidden a - (-c). -static bool IsSubConst2(HGraph* graph, - HInstruction* instruction, - /*out*/ HInstruction** a, - /*out*/ HInstruction** b) { - int64_t c = 0; - if (instruction->IsSub()) { - *a = instruction->InputAt(0); - *b = instruction->InputAt(1); - return true; - } else if (IsAddConst(instruction, a, b, &c) && *a != nullptr && *b == nullptr) { - // Constant for the hidden subtraction. - *b = graph->GetConstant(instruction->GetType(), -c); - return true; +// Detect a + c for constant c. +static bool IsAddConst(HInstruction* instruction, + /*out*/ HInstruction** a, + /*out*/ int64_t* c) { + if (instruction->IsAdd()) { + if (IsInt64AndGet(instruction->InputAt(0), c)) { + *a = instruction->InputAt(1); + return true; + } else if (IsInt64AndGet(instruction->InputAt(1), c)) { + *a = instruction->InputAt(0); + return true; + } } return false; } @@ -386,8 +378,7 @@ static bool CanSetRange(DataType::Type type, } // Accept various saturated addition forms. -static bool IsSaturatedAdd(HInstruction* a, - HInstruction* b, +static bool IsSaturatedAdd(HInstruction* clippee, DataType::Type type, int64_t lo, int64_t hi, @@ -399,7 +390,8 @@ static bool IsSaturatedAdd(HInstruction* a, // Tighten the range for signed single clipping on constant. if (!is_unsigned) { int64_t c = 0; - if (IsInt64AndGet(a, &c) || IsInt64AndGet(b, &c)) { + HInstruction* notused = nullptr; + if (IsAddConst(clippee, ¬used, &c)) { // For c in proper range and narrower operand r: // MIN(r + c, 127) c > 0 // or MAX(r + c, -128) c < 0 (and possibly redundant bound). @@ -421,7 +413,7 @@ static bool IsSaturatedAdd(HInstruction* a, } // Accept various saturated subtraction forms. -static bool IsSaturatedSub(HInstruction* a, +static bool IsSaturatedSub(HInstruction* clippee, DataType::Type type, int64_t lo, int64_t hi, @@ -433,7 +425,7 @@ static bool IsSaturatedSub(HInstruction* a, // Tighten the range for signed single clipping on constant. if (!is_unsigned) { int64_t c = 0; - if (IsInt64AndGet(a, /*out*/ &c)) { + if (IsInt64AndGet(clippee->InputAt(0), /*out*/ &c)) { // For c in proper range and narrower operand r: // MIN(c - r, 127) c > 0 // or MAX(c - r, -128) c < 0 (and possibly redundant bound). @@ -1529,7 +1521,8 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node, return false; // reject, unless all operands are same-extension narrower } // Accept MIN/MAX(x, y) for vectorizable operands. - DCHECK(r != nullptr && s != nullptr); + DCHECK(r != nullptr); + DCHECK(s != nullptr); if (generate_code && vector_mode_ != kVector) { // de-idiom r = opa; s = opb; @@ -2033,37 +2026,31 @@ bool HLoopOptimization::VectorizeSaturationIdiom(LoopNode* node, instruction->GetType() != DataType::Type::kInt64) { return false; } - // Clipped addition or subtraction on narrower operands? We will try both - // formats since, e.g., x+c can be interpreted as x+c and x-(-c), depending - // on what clipping values are used, to get most benefits. + // Clipped addition or subtraction? int64_t lo = std::numeric_limits::min(); int64_t hi = std::numeric_limits::max(); HInstruction* clippee = FindClippee(instruction, &lo, &hi); - HInstruction* a = nullptr; - HInstruction* b = nullptr; + 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; - bool is_add = true; - int64_t c = 0; - // First try for saturated addition. - if (IsAddConst2(graph_, clippee, /*out*/ &a, /*out*/ &b, /*out*/ &c) && c == 0 && - IsNarrowerOperands(a, b, type, &r, &s, &is_unsigned) && - IsSaturatedAdd(r, s, type, lo, hi, is_unsigned)) { - is_add = true; + if (IsNarrowerOperands(clippee->InputAt(0), clippee->InputAt(1), type, &r, &s, &is_unsigned) && + (is_add ? IsSaturatedAdd(clippee, type, lo, hi, is_unsigned) + : IsSaturatedSub(clippee, type, lo, hi, is_unsigned))) { + DCHECK(r != nullptr); + DCHECK(s != nullptr); } else { - // Then try again for saturated subtraction. - a = b = r = s = nullptr; - if (IsSubConst2(graph_, clippee, /*out*/ &a, /*out*/ &b) && - IsNarrowerOperands(a, b, type, &r, &s, &is_unsigned) && - IsSaturatedSub(r, type, lo, hi, is_unsigned)) { - is_add = false; - } else { - return false; - } + return false; } // Accept saturation idiom for vectorizable operands. - DCHECK(r != nullptr && s != nullptr); if (generate_code && vector_mode_ != kVector) { // de-idiom r = instruction->InputAt(0); s = instruction->InputAt(1); @@ -2114,7 +2101,8 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node, HInstruction* a = nullptr; HInstruction* b = nullptr; int64_t c = 0; - if (IsAddConst2(graph_, instruction->InputAt(0), /*out*/ &a, /*out*/ &b, /*out*/ &c)) { + if (IsAddConst(instruction->InputAt(0), /*out*/ &a, /*out*/ &b, /*out*/ &c)) { + DCHECK(a != nullptr && b != nullptr); // Accept c == 1 (rounded) or c == 0 (not rounded). bool is_rounded = false; if (c == 1) { @@ -2136,7 +2124,8 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node, } // Accept recognized halving add for vectorizable operands. Vectorized code uses the // shorthand idiomatic operation. Sequential code uses the original scalar expressions. - DCHECK(r != nullptr && s != nullptr); + DCHECK(r != nullptr); + DCHECK(s != nullptr); if (generate_code && vector_mode_ != kVector) { // de-idiom r = instruction->InputAt(0); s = instruction->InputAt(1); @@ -2186,11 +2175,19 @@ bool HLoopOptimization::VectorizeSADIdiom(LoopNode* node, HInstruction* v = instruction->InputAt(1); HInstruction* a = nullptr; HInstruction* b = nullptr; - if (v->IsAbs() && - v->GetType() == reduction_type && - IsSubConst2(graph_, v->InputAt(0), /*out*/ &a, /*out*/ &b)) { - DCHECK(a != nullptr && b != nullptr); - } else { + if (v->GetType() == reduction_type && v->IsAbs()) { + HInstruction* x = v->InputAt(0); + if (x->GetType() == reduction_type) { + int64_t c = 0; + if (x->IsSub()) { + a = x->InputAt(0); + b = x->InputAt(1); + } else if (IsAddConst(x, /*out*/ &a, /*out*/ &c)) { + b = graph_->GetConstant(reduction_type, -c); // hidden SUB! + } + } + } + if (a == nullptr || b == nullptr) { return false; } // Accept same-type or consistent sign extension for narrower-type on operands a and b. @@ -2223,7 +2220,8 @@ bool HLoopOptimization::VectorizeSADIdiom(LoopNode* node, } // Accept SAD idiom for vectorizable operands. Vectorized code uses the shorthand // idiomatic operation. Sequential code uses the original scalar expressions. - DCHECK(r != nullptr && s != nullptr); + DCHECK(r != nullptr); + DCHECK(s != nullptr); if (generate_code && vector_mode_ != kVector) { // de-idiom r = s = v->InputAt(0); } diff --git a/test/646-checker-hadd-short/src/Main.java b/test/646-checker-hadd-short/src/Main.java index c09da8125b..85c2fcaf22 100644 --- a/test/646-checker-hadd-short/src/Main.java +++ b/test/646-checker-hadd-short/src/Main.java @@ -26,10 +26,6 @@ public class Main { static short[] sB2 = new short[M]; static short[] sBo = new short[M]; - private static int $inline$mone() { - return -1; - } - /// CHECK-START: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (before) /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none @@ -188,35 +184,6 @@ public class Main { } } - /// CHECK-START: void Main.rounding_halving_add_signed_alt3(short[], short[], short[]) loop_optimization (before) - /// CHECK-DAG: <> IntConstant 1 loop:none - /// CHECK-DAG: <> IntConstant -1 loop:none - /// CHECK-DAG: <> IntConstant 9 loop:none - /// CHECK-DAG: <> IntConstant -9 loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Shr [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_signed_alt3(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void rounding_halving_add_signed_alt3(short[] b1, short[] b2, short[] bo) { - int min_length = Math.min(bo.length, Math.min(b1.length, b2.length)); - for (int i = 0; i < min_length; i++) { - // Computations that cancel to adding 1 also do not confuse recognition. - bo[i] = (short) (((b1[i] + 9) + (b2[i] - 9) - $inline$mone()) >> 1); - } - } - /// CHECK-START: void Main.rounding_halving_add_unsigned(short[], short[], short[]) instruction_simplifier (before) /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> IntConstant 65535 loop:none @@ -399,11 +366,6 @@ public class Main { short e = (short) ((sB1[i] + sB2[i] + 1) >> 1); expectEquals(e, sBo[i]); } - rounding_halving_add_signed_alt3(sB1, sB2, sBo); - for (int i = 0; i < M; i++) { - short e = (short) ((sB1[i] + sB2[i] + 1) >> 1); - expectEquals(e, sBo[i]); - } rounding_halving_add_unsigned(sB1, sB2, sBo); for (int i = 0; i < M; i++) { short e = (short) (((sB1[i] & 0xffff) + (sB2[i] & 0xffff) + 1) >> 1); diff --git a/test/660-checker-simd-sad-short/src/Main.java b/test/660-checker-simd-sad-short/src/Main.java index 77c9e53e0c..8a44d9ed12 100644 --- a/test/660-checker-simd-sad-short/src/Main.java +++ b/test/660-checker-simd-sad-short/src/Main.java @@ -19,10 +19,6 @@ */ public class Main { - private static int $inline$seven() { - return 7; - } - // TODO: lower precision still coming, b/64091002 private static short sadShort2Short(short[] s1, short[] s2) { @@ -157,102 +153,6 @@ public class Main { return sad; } - /// CHECK-START: int Main.sadShort2IntConstant1(short[]) loop_optimization (before) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 1 loop:none - /// CHECK-DAG: <> IntConstant -7 loop:none - /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstant1(short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 1 loop:none - /// CHECK-DAG: <> IntConstant 7 loop:none - /// CHECK-DAG: <> IntConstant 8 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecSetScalars [<>] loop:none - /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - private static int sadShort2IntConstant1(short[] s) { - int sad = 0; - for (int i = 0; i < s.length; i++) { - sad += Math.abs(s[i] - 7); // s[i] + -7 - } - return sad; - } - - /// CHECK-START: int Main.sadShort2IntConstant2(short[]) loop_optimization (before) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 1 loop:none - /// CHECK-DAG: <> IntConstant 7 loop:none - /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstant2(short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 1 loop:none - /// CHECK-DAG: <> IntConstant 7 loop:none - /// CHECK-DAG: <> IntConstant 8 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecSetScalars [<>] loop:none - /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - private static int sadShort2IntConstant2(short[] s) { - int sad = 0; - for (int i = 0; i < s.length; i++) { - sad += Math.abs(s[i] - $inline$seven()); // s[i] - 7 - } - return sad; - } - - /// CHECK-START: int Main.sadShort2IntConstant3(short[]) loop_optimization (before) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 1 loop:none - /// CHECK-DAG: <> IntConstant 7 loop:none - /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstant3(short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 1 loop:none - /// CHECK-DAG: <> IntConstant -7 loop:none - /// CHECK-DAG: <> IntConstant 8 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecSetScalars [<>] loop:none - /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - private static int sadShort2IntConstant3(short[] s) { - int sad = 0; - for (int i = 0; i < s.length; i++) { - sad += Math.abs(s[i] + $inline$seven()); // hidden s[i] - (-7) - } - return sad; - } - /// CHECK-START: long Main.sadShort2Long(short[], short[]) loop_optimization (before) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 1 loop:none @@ -343,9 +243,6 @@ public class Main { expectEquals(65535, sadShort2IntAlt(s2, s1)); expectEquals(65535, sadShort2IntAlt2(s1, s2)); expectEquals(65535, sadShort2IntAlt2(s2, s1)); - expectEquals(32880, sadShort2IntConstant1(s1)); - expectEquals(32880, sadShort2IntConstant2(s1)); - expectEquals(32866, sadShort2IntConstant3(s1)); expectEquals(65535L, sadShort2Long(s1, s2)); expectEquals(65535L, sadShort2Long(s2, s1)); expectEquals(65536L, sadShort2LongAt1(s1, s2)); @@ -382,9 +279,6 @@ public class Main { expectEquals(1291788, sadShort2Int(s1, s2)); expectEquals(1291788, sadShort2IntAlt(s1, s2)); expectEquals(1291788, sadShort2IntAlt2(s1, s2)); - expectEquals(823907, sadShort2IntConstant1(s1)); - expectEquals(823907, sadShort2IntConstant2(s1)); - expectEquals(823953, sadShort2IntConstant3(s1)); expectEquals(1291788L, sadShort2Long(s1, s2)); expectEquals(1291789L, sadShort2LongAt1(s1, s2)); diff --git a/test/678-checker-simd-saturation/src/Main.java b/test/678-checker-simd-saturation/src/Main.java index decc691789..d123cc2e25 100644 --- a/test/678-checker-simd-saturation/src/Main.java +++ b/test/678-checker-simd-saturation/src/Main.java @@ -19,14 +19,6 @@ */ public class Main { - static final int $inline$p15() { - return 15; - } - - static final int $inline$m15() { - return -15; - } - // // Direct min-max. // @@ -238,8 +230,8 @@ public class Main { /// CHECK-START-{ARM,ARM64}: void Main.satSubPConstSByte(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecReplicateScalar loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none public static void satSubPConstSByte(byte[] a, byte[] b) { int n = Math.min(a.length, b.length); for (int i = 0; i < n; i++) { @@ -250,8 +242,8 @@ public class Main { /// CHECK-START-{ARM,ARM64}: void Main.satSubNConstSByte(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecReplicateScalar loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none public static void satSubNConstSByte(byte[] a, byte[] b) { int n = Math.min(a.length, b.length); for (int i = 0; i < n; i++) { @@ -290,8 +282,8 @@ public class Main { /// CHECK-START-{ARM,ARM64}: void Main.satSubPConstSShort(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecReplicateScalar loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none public static void satSubPConstSShort(short[] a, short[] b) { int n = Math.min(a.length, b.length); for (int i = 0; i < n; i++) { @@ -302,8 +294,8 @@ public class Main { /// CHECK-START-{ARM,ARM64}: void Main.satSubNConstSShort(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecReplicateScalar loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none public static void satSubNConstSShort(short[] a, short[] b) { int n = Math.min(a.length, b.length); for (int i = 0; i < n; i++) { @@ -312,59 +304,7 @@ public class Main { } // - // Alternatives 8-bit clipping. - // - - /// CHECK-START-{ARM,ARM64}: void Main.usatAddConst(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void usatAddConst(byte[] a, byte[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - b[i] = (byte) Math.min((a[i] & 0xff) + $inline$p15(), 255); - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.usatAddConstAlt(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void usatAddConstAlt(byte[] a, byte[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - b[i] = (byte) Math.min((a[i] & 0xff) - $inline$m15(), 255); - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.usatSubConst(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void usatSubConst(byte[] a, byte[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - b[i] = (byte) Math.max((a[i] & 0xff) - $inline$p15(), 0); - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.usatSubConstAlt(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void usatSubConstAlt(byte[] a, byte[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - b[i] = (byte) Math.max((a[i] & 0xff) + $inline$m15(), 0); - } - } - - // - // Alternatives 16-bit clipping. + // Alternatives. // /// CHECK-START: void Main.satAlt1(short[], short[], short[]) loop_optimization (before) @@ -502,34 +442,6 @@ public class Main { } } - /// CHECK-START-{ARM,ARM64}: void Main.usatSubConst(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void usatSubConst(short[] a, short[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - int t = a[i] & 0xffff; - int s = t - $inline$p15(); - b[i] = (short)(s > 0 ? s : 0); - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.usatSubConstAlt(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void usatSubConstAlt(short[] a, short[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - int t = a[i] & 0xffff; - int s = t + $inline$m15(); - b[i] = (short)(s > 0 ? s : 0); - } - } - // // Test drivers. // @@ -591,27 +503,6 @@ public class Main { byte e = (byte) Math.max(-15 - b1[i], -128); expectEquals(e, out[i]); } - // Alternatives. - usatAddConst(b1, out); - for (int i = 0; i < m; i++) { - byte e = (byte) Math.min((b1[i] & 0xff) + 15, 255); - expectEquals(e, out[i]); - } - usatAddConstAlt(b1, out); - for (int i = 0; i < m; i++) { - byte e = (byte) Math.min((b1[i] & 0xff) + 15, 255); - expectEquals(e, out[i]); - } - usatSubConst(b1, out); - for (int i = 0; i < m; i++) { - byte e = (byte) Math.max((b1[i] & 0xff) - 15, 0); - expectEquals(e, out[i]); - } - usatSubConstAlt(b1, out); - for (int i = 0; i < m; i++) { - byte e = (byte) Math.max((b1[i] & 0xff) - 15, 0); - expectEquals(e, out[i]); - } } private static void test16Bit() { @@ -739,16 +630,6 @@ public class Main { short e = (short) Math.max(Math.min(s1[i] + 15, 32767), -32752); expectEquals(e, out[i]); } - usatSubConst(s1, out); - for (int i = 0; i < m; i++) { - short e = (short) Math.max((s1[i] & 0xffff) - 15, 0); - expectEquals(e, out[i]); - } - usatSubConstAlt(s1, out); - for (int i = 0; i < m; i++) { - short e = (short) Math.max((s1[i] & 0xffff) - 15, 0); - expectEquals(e, out[i]); - } } public static void main(String[] args) { -- GitLab From 8aeb5136bb4a0f21ea37cb7d5625c01c51df8120 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 28 Mar 2018 08:04:04 +0000 Subject: [PATCH 154/749] Revert "Fix 036-finalizer for JIT-at-first-use gcstress." Fails on gcstress. Bug: 62611253 Bug: 76454261 This reverts commit e37dc6ee0489565f911aeb792d627d942a3ae4cd. Change-Id: I796e3839fc17f15de1bfa03825bd83358a1ee7f6 --- test/036-finalizer/src/Main.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java index 51d4a81150..ff6186b240 100644 --- a/test/036-finalizer/src/Main.java +++ b/test/036-finalizer/src/Main.java @@ -70,17 +70,15 @@ public class Main { return s[0]; } - public static void main(String[] args) { - WeakReference wimp = makeRef(); + private static void printWeakReference(WeakReference wimp) { // Reference ft so we are sure the WeakReference cannot be cleared. - // Note: This is very fragile. It was previously in a helper function but that - // doesn't work for JIT-on-first-use with --gcstress where the object would be - // collected when JIT internally allocates an array. Also adding a scope around - // the keepLive lifetime somehow keeps a non-null `keepLive` around and makes - // the test fail (even when keeping the `null` assignment). b/76454261 FinalizerTest keepLive = wimp.get(); System.out.println("wimp: " + wimpString(wimp)); - keepLive = null; // Clear the reference. + } + + public static void main(String[] args) { + WeakReference wimp = makeRef(); + printWeakReference(wimp); /* this will try to collect and finalize ft */ System.out.println("gc"); -- GitLab From 0e3a6addf60cbf006536d05aebe652e7ccddcd70 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 28 Mar 2018 09:58:22 +0000 Subject: [PATCH 155/749] Revert "Move most art test off DX" Reason for revert: 036-finalizer broken for gcstress configs Note: The run-test script changes do not seem to allow fallback to DX. That also puts into question the need for the new "build" scripts in many tests. Bug: 65168732 This reverts commit 9222417fdcf7c504fe86fa88b8a77363cb941e11. Change-Id: I990167267ac01174cc612cac87dbacd77dec8a45 --- test/166-bad-interface-super/build | 20 -------------------- test/646-checker-hadd-alt-char/build | 20 -------------------- test/646-checker-hadd-alt-short/build | 20 -------------------- test/646-checker-hadd-char/build | 20 -------------------- test/646-checker-hadd-short/build | 20 -------------------- test/651-checker-short-simd-minmax/build | 20 -------------------- test/660-checker-simd-sad-byte/build | 20 -------------------- test/660-checker-simd-sad-char/build | 20 -------------------- test/660-checker-simd-sad-int/build | 20 -------------------- test/660-checker-simd-sad-short/build | 20 -------------------- test/660-checker-simd-sad-short2/build | 20 -------------------- test/660-checker-simd-sad-short3/build | 20 -------------------- test/661-checker-simd-reduc/build | 20 -------------------- test/672-checker-throw-method/build | 20 -------------------- test/673-checker-throw-vmethod/build | 20 -------------------- test/678-checker-simd-saturation/build | 20 -------------------- test/712-varhandle-invocations/build | 3 --- test/Android.run-test.mk | 7 ++++++- test/etc/default-build | 2 +- test/run-test | 5 ++++- test/testrunner/env.py | 3 +++ test/testrunner/testrunner.py | 3 +++ tools/build/var_list | 1 + 23 files changed, 18 insertions(+), 326 deletions(-) delete mode 100644 test/166-bad-interface-super/build delete mode 100644 test/646-checker-hadd-alt-char/build delete mode 100644 test/646-checker-hadd-alt-short/build delete mode 100644 test/646-checker-hadd-char/build delete mode 100644 test/646-checker-hadd-short/build delete mode 100644 test/651-checker-short-simd-minmax/build delete mode 100644 test/660-checker-simd-sad-byte/build delete mode 100644 test/660-checker-simd-sad-char/build delete mode 100644 test/660-checker-simd-sad-int/build delete mode 100644 test/660-checker-simd-sad-short/build delete mode 100644 test/660-checker-simd-sad-short2/build delete mode 100644 test/660-checker-simd-sad-short3/build delete mode 100644 test/661-checker-simd-reduc/build delete mode 100644 test/672-checker-throw-method/build delete mode 100644 test/673-checker-throw-vmethod/build delete mode 100644 test/678-checker-simd-saturation/build diff --git a/test/166-bad-interface-super/build b/test/166-bad-interface-super/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/166-bad-interface-super/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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 -# -# 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/646-checker-hadd-alt-char/build b/test/646-checker-hadd-alt-char/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/646-checker-hadd-alt-char/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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 -# -# 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/646-checker-hadd-alt-short/build b/test/646-checker-hadd-alt-short/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/646-checker-hadd-alt-short/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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 -# -# 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/646-checker-hadd-char/build b/test/646-checker-hadd-char/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/646-checker-hadd-char/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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 -# -# 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/646-checker-hadd-short/build b/test/646-checker-hadd-short/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/646-checker-hadd-short/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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 -# -# 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/651-checker-short-simd-minmax/build b/test/651-checker-short-simd-minmax/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/651-checker-short-simd-minmax/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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 -# -# 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/660-checker-simd-sad-byte/build b/test/660-checker-simd-sad-byte/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/660-checker-simd-sad-byte/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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 -# -# 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/660-checker-simd-sad-char/build b/test/660-checker-simd-sad-char/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/660-checker-simd-sad-char/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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 -# -# 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/660-checker-simd-sad-int/build b/test/660-checker-simd-sad-int/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/660-checker-simd-sad-int/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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 -# -# 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/660-checker-simd-sad-short/build b/test/660-checker-simd-sad-short/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/660-checker-simd-sad-short/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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 -# -# 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/660-checker-simd-sad-short2/build b/test/660-checker-simd-sad-short2/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/660-checker-simd-sad-short2/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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 -# -# 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/660-checker-simd-sad-short3/build b/test/660-checker-simd-sad-short3/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/660-checker-simd-sad-short3/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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 -# -# 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/661-checker-simd-reduc/build b/test/661-checker-simd-reduc/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/661-checker-simd-reduc/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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 -# -# 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/672-checker-throw-method/build b/test/672-checker-throw-method/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/672-checker-throw-method/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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 -# -# 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/673-checker-throw-vmethod/build b/test/673-checker-throw-vmethod/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/673-checker-throw-vmethod/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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 -# -# 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/678-checker-simd-saturation/build b/test/678-checker-simd-saturation/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/678-checker-simd-saturation/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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 -# -# 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/712-varhandle-invocations/build b/test/712-varhandle-invocations/build index 6d4429f0ef..253765be91 100755 --- a/test/712-varhandle-invocations/build +++ b/test/712-varhandle-invocations/build @@ -35,8 +35,5 @@ python3 ./util-src/generate_java.py "${GENERATED_SRC}" ${MANUAL_TESTS} # Desugar is not happy with our Java 9 byte code, it shouldn't be necessary here anyway. export USE_DESUGAR=false -# See b/65168732 -export USE_D8=false - # Invoke default build with increased heap size for dx ./default-build "$@" --experimental var-handles --dx-vm-option -JXmx384m diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index f8bebdd35f..6633958140 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -21,12 +21,17 @@ include art/build/Android.common_test.mk TEST_ART_RUN_TEST_DEPENDENCIES := \ $(HOST_OUT_EXECUTABLES)/dx \ $(HOST_OUT_EXECUTABLES)/d8 \ - $(HOST_OUT_EXECUTABLES)/d8-compat-dx \ $(HOST_OUT_EXECUTABLES)/hiddenapi \ $(HOST_OUT_EXECUTABLES)/jasmin \ $(HOST_OUT_EXECUTABLES)/smali \ $(HOST_OUT_JAVA_LIBRARIES)/desugar.jar +# Add d8 dependency, if enabled. +ifeq ($(USE_D8),true) +TEST_ART_RUN_TEST_DEPENDENCIES += \ + $(HOST_OUT_EXECUTABLES)/d8-compat-dx +endif + # We need dex2oat and dalvikvm on the target as well as the core images (all images as we sync # only once). TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUTS) diff --git a/test/etc/default-build b/test/etc/default-build index dd5560213a..9de7294a59 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -317,7 +317,7 @@ function make_dex() { fi local dexer="${DX}" - if [[ "${USE_D8}" != "false" ]]; then + if [ ${USE_D8} = "true" ]; then dexer="${ANDROID_HOST_OUT}/bin/d8-compat-dx" fi diff --git a/test/run-test b/test/run-test index 5f85b0875b..5b43b52b41 100755 --- a/test/run-test +++ b/test/run-test @@ -45,7 +45,7 @@ export JAVAC="javac -g -Xlint:-options" export RUN="${progdir}/etc/run-test-jar" export DEX_LOCATION=/data/run-test/${test_dir} export NEED_DEX="true" -export USE_D8="true" +export USE_D8="false" export USE_JACK="false" export USE_DESUGAR="true" export SMALI_ARGS="" @@ -365,6 +365,9 @@ while true; do elif [ "x$1" = "x--build-only" ]; then build_only="yes" shift + elif [ "x$1" = "x--build-with-d8" ]; then + USE_D8="true" + shift elif [ "x$1" = "x--build-with-javac-dx" ]; then USE_JACK="false" shift diff --git a/test/testrunner/env.py b/test/testrunner/env.py index 7564f5a6b4..539499173c 100644 --- a/test/testrunner/env.py +++ b/test/testrunner/env.py @@ -71,6 +71,9 @@ ANDROID_BUILD_TOP = _get_android_build_top() # Compiling with jack? Possible values in (True, False, 'default') ANDROID_COMPILE_WITH_JACK = _get_build_var_boolean('ANDROID_COMPILE_WITH_JACK', 'default') +# Follow the build system's D8 usage. +USE_D8_BY_DEFAULT = _get_build_var_boolean('USE_D8_BY_DEFAULT', False) + # Directory used for temporary test files on the host. ART_HOST_TEST_DIR = tempfile.mkdtemp(prefix = 'test-art-') diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 99bab097ab..734a600c5e 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -504,6 +504,9 @@ def run_tests(tests): elif env.ANDROID_COMPILE_WITH_JACK == False: options_test += ' --build-with-javac-dx' + if env.USE_D8_BY_DEFAULT == True: + options_test += ' --build-with-d8' + # TODO(http://36039166): This is a temporary solution to # fix build breakages. options_test = (' --output-path %s') % ( diff --git a/tools/build/var_list b/tools/build/var_list index adcb066f7c..3727741dac 100644 --- a/tools/build/var_list +++ b/tools/build/var_list @@ -34,4 +34,5 @@ HOST_PREFER_32_BIT HOST_OUT_EXECUTABLES ANDROID_JAVA_TOOLCHAIN ANDROID_COMPILE_WITH_JACK +USE_D8_BY_DEFAULT -- GitLab From 0997a88482e947eb214f0a45874ceecce7476494 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 28 Mar 2018 10:21:11 +0000 Subject: [PATCH 156/749] Revert^2 "Fix 036-finalizer for JIT-at-first-use gcstress." The revert did not fix the tests. The actual offending change has been reverted in https://android-review.googlesource.com/650959 . This reverts commit 8aeb5136bb4a0f21ea37cb7d5625c01c51df8120. Change-Id: I67484c90b5f3e235747d64c10c2f2335620a6e49 --- test/036-finalizer/src/Main.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java index ff6186b240..51d4a81150 100644 --- a/test/036-finalizer/src/Main.java +++ b/test/036-finalizer/src/Main.java @@ -70,15 +70,17 @@ public class Main { return s[0]; } - private static void printWeakReference(WeakReference wimp) { + public static void main(String[] args) { + WeakReference wimp = makeRef(); // Reference ft so we are sure the WeakReference cannot be cleared. + // Note: This is very fragile. It was previously in a helper function but that + // doesn't work for JIT-on-first-use with --gcstress where the object would be + // collected when JIT internally allocates an array. Also adding a scope around + // the keepLive lifetime somehow keeps a non-null `keepLive` around and makes + // the test fail (even when keeping the `null` assignment). b/76454261 FinalizerTest keepLive = wimp.get(); System.out.println("wimp: " + wimpString(wimp)); - } - - public static void main(String[] args) { - WeakReference wimp = makeRef(); - printWeakReference(wimp); + keepLive = null; // Clear the reference. /* this will try to collect and finalize ft */ System.out.println("gc"); -- GitLab From a3e232640d21304913778c24cb4e55cc82229101 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 28 Mar 2018 11:15:12 +0000 Subject: [PATCH 157/749] Revert "Revert "Refined add/sub analysis vis-a-vis SIMD idioms."" Bug: b/74026074 Looks like this CL is not the culprit :( Apologies Aart. This reverts commit 7f31326f7956d6a1630e7e53473b0581705796ec. Change-Id: I15830324bb276129bf44caf232af24d7c022ed9a --- compiler/optimizing/loop_optimization.cc | 162 +++++++++--------- test/646-checker-hadd-short/src/Main.java | 38 ++++ test/660-checker-simd-sad-short/src/Main.java | 106 ++++++++++++ .../678-checker-simd-saturation/src/Main.java | 137 ++++++++++++++- 4 files changed, 354 insertions(+), 89 deletions(-) diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 1d83815e1f..71e24de141 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -227,6 +227,7 @@ static bool IsNarrowerOperands(HInstruction* a, /*out*/ HInstruction** r, /*out*/ HInstruction** s, /*out*/ bool* is_unsigned) { + DCHECK(a != nullptr && b != nullptr); // Look for a matching sign extension. DataType::Type stype = HVecOperation::ToSignedType(type); if (IsSignExtensionAndGet(a, stype, r) && IsSignExtensionAndGet(b, stype, s)) { @@ -247,6 +248,7 @@ static bool IsNarrowerOperand(HInstruction* a, DataType::Type type, /*out*/ HInstruction** r, /*out*/ bool* is_unsigned) { + DCHECK(a != nullptr); // Look for a matching sign extension. DataType::Type stype = HVecOperation::ToSignedType(type); if (IsSignExtensionAndGet(a, stype, r)) { @@ -270,20 +272,28 @@ static uint32_t GetOtherVL(DataType::Type other_type, DataType::Type vector_type return vl >> (DataType::SizeShift(other_type) - DataType::SizeShift(vector_type)); } -// Detect up to two instructions a and b, and an acccumulated constant c. -static bool IsAddConstHelper(HInstruction* instruction, - /*out*/ HInstruction** a, - /*out*/ HInstruction** b, - /*out*/ int64_t* c, - int32_t depth) { - static constexpr int32_t kMaxDepth = 8; // don't search too deep +// Detect up to two added operands a and b and an acccumulated constant c. +static bool IsAddConst(HInstruction* instruction, + /*out*/ HInstruction** a, + /*out*/ HInstruction** b, + /*out*/ int64_t* c, + int32_t depth = 8) { // don't search too deep int64_t value = 0; + // Enter add/sub while still within reasonable depth. + if (depth > 0) { + if (instruction->IsAdd()) { + return IsAddConst(instruction->InputAt(0), a, b, c, depth - 1) && + IsAddConst(instruction->InputAt(1), a, b, c, depth - 1); + } else if (instruction->IsSub() && + IsInt64AndGet(instruction->InputAt(1), &value)) { + *c -= value; + return IsAddConst(instruction->InputAt(0), a, b, c, depth - 1); + } + } + // Otherwise, deal with leaf nodes. if (IsInt64AndGet(instruction, &value)) { *c += value; return true; - } else if (instruction->IsAdd() && depth <= kMaxDepth) { - return IsAddConstHelper(instruction->InputAt(0), a, b, c, depth + 1) && - IsAddConstHelper(instruction->InputAt(1), a, b, c, depth + 1); } else if (*a == nullptr) { *a = instruction; return true; @@ -291,42 +301,40 @@ static bool IsAddConstHelper(HInstruction* instruction, *b = instruction; return true; } - return false; // too many non-const operands + return false; // too many operands } -// Detect a + b + c for an optional constant c. -static bool IsAddConst(HInstruction* instruction, - /*out*/ HInstruction** a, - /*out*/ HInstruction** b, - /*out*/ int64_t* c) { - if (instruction->IsAdd()) { - // Try to find a + b and accumulated c. - if (IsAddConstHelper(instruction->InputAt(0), a, b, c, /*depth*/ 0) && - IsAddConstHelper(instruction->InputAt(1), a, b, c, /*depth*/ 0) && - *b != nullptr) { - return true; +// Detect a + b + c with optional constant c. +static bool IsAddConst2(HGraph* graph, + HInstruction* instruction, + /*out*/ HInstruction** a, + /*out*/ HInstruction** b, + /*out*/ int64_t* c) { + if (IsAddConst(instruction, a, b, c) && *a != nullptr) { + if (*b == nullptr) { + // Constant is usually already present, unless accumulated. + *b = graph->GetConstant(instruction->GetType(), (*c)); + *c = 0; } - // Found a + b. - *a = instruction->InputAt(0); - *b = instruction->InputAt(1); - *c = 0; return true; } return false; } -// Detect a + c for constant c. -static bool IsAddConst(HInstruction* instruction, - /*out*/ HInstruction** a, - /*out*/ int64_t* c) { - if (instruction->IsAdd()) { - if (IsInt64AndGet(instruction->InputAt(0), c)) { - *a = instruction->InputAt(1); - return true; - } else if (IsInt64AndGet(instruction->InputAt(1), c)) { - *a = instruction->InputAt(0); - return true; - } +// Detect a direct a - b or a hidden a - (-c). +static bool IsSubConst2(HGraph* graph, + HInstruction* instruction, + /*out*/ HInstruction** a, + /*out*/ HInstruction** b) { + int64_t c = 0; + if (instruction->IsSub()) { + *a = instruction->InputAt(0); + *b = instruction->InputAt(1); + return true; + } else if (IsAddConst(instruction, a, b, &c) && *a != nullptr && *b == nullptr) { + // Constant for the hidden subtraction. + *b = graph->GetConstant(instruction->GetType(), -c); + return true; } return false; } @@ -378,7 +386,8 @@ static bool CanSetRange(DataType::Type type, } // Accept various saturated addition forms. -static bool IsSaturatedAdd(HInstruction* clippee, +static bool IsSaturatedAdd(HInstruction* a, + HInstruction* b, DataType::Type type, int64_t lo, int64_t hi, @@ -390,8 +399,7 @@ static bool IsSaturatedAdd(HInstruction* clippee, // Tighten the range for signed single clipping on constant. if (!is_unsigned) { int64_t c = 0; - HInstruction* notused = nullptr; - if (IsAddConst(clippee, ¬used, &c)) { + if (IsInt64AndGet(a, &c) || IsInt64AndGet(b, &c)) { // For c in proper range and narrower operand r: // MIN(r + c, 127) c > 0 // or MAX(r + c, -128) c < 0 (and possibly redundant bound). @@ -413,7 +421,7 @@ static bool IsSaturatedAdd(HInstruction* clippee, } // Accept various saturated subtraction forms. -static bool IsSaturatedSub(HInstruction* clippee, +static bool IsSaturatedSub(HInstruction* a, DataType::Type type, int64_t lo, int64_t hi, @@ -425,7 +433,7 @@ static bool IsSaturatedSub(HInstruction* clippee, // Tighten the range for signed single clipping on constant. if (!is_unsigned) { int64_t c = 0; - if (IsInt64AndGet(clippee->InputAt(0), /*out*/ &c)) { + if (IsInt64AndGet(a, /*out*/ &c)) { // For c in proper range and narrower operand r: // MIN(c - r, 127) c > 0 // or MAX(c - r, -128) c < 0 (and possibly redundant bound). @@ -1521,8 +1529,7 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node, return false; // reject, unless all operands are same-extension narrower } // Accept MIN/MAX(x, y) for vectorizable operands. - DCHECK(r != nullptr); - DCHECK(s != nullptr); + DCHECK(r != nullptr && s != nullptr); if (generate_code && vector_mode_ != kVector) { // de-idiom r = opa; s = opb; @@ -2026,31 +2033,37 @@ bool HLoopOptimization::VectorizeSaturationIdiom(LoopNode* node, instruction->GetType() != DataType::Type::kInt64) { return false; } - // Clipped addition or subtraction? + // Clipped addition or subtraction on narrower operands? We will try both + // formats since, e.g., x+c can be interpreted as x+c and x-(-c), depending + // on what clipping values are used, to get most benefits. int64_t lo = std::numeric_limits::min(); int64_t hi = std::numeric_limits::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* a = nullptr; + HInstruction* b = nullptr; 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(clippee, type, lo, hi, is_unsigned) - : IsSaturatedSub(clippee, type, lo, hi, is_unsigned))) { - DCHECK(r != nullptr); - DCHECK(s != nullptr); + bool is_add = true; + int64_t c = 0; + // First try for saturated addition. + if (IsAddConst2(graph_, clippee, /*out*/ &a, /*out*/ &b, /*out*/ &c) && c == 0 && + IsNarrowerOperands(a, b, type, &r, &s, &is_unsigned) && + IsSaturatedAdd(r, s, type, lo, hi, is_unsigned)) { + is_add = true; } else { - return false; + // Then try again for saturated subtraction. + a = b = r = s = nullptr; + if (IsSubConst2(graph_, clippee, /*out*/ &a, /*out*/ &b) && + IsNarrowerOperands(a, b, type, &r, &s, &is_unsigned) && + IsSaturatedSub(r, type, lo, hi, is_unsigned)) { + is_add = false; + } else { + return false; + } } // Accept saturation idiom for vectorizable operands. + DCHECK(r != nullptr && s != nullptr); if (generate_code && vector_mode_ != kVector) { // de-idiom r = instruction->InputAt(0); s = instruction->InputAt(1); @@ -2101,8 +2114,7 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node, HInstruction* a = nullptr; HInstruction* b = nullptr; int64_t c = 0; - if (IsAddConst(instruction->InputAt(0), /*out*/ &a, /*out*/ &b, /*out*/ &c)) { - DCHECK(a != nullptr && b != nullptr); + if (IsAddConst2(graph_, instruction->InputAt(0), /*out*/ &a, /*out*/ &b, /*out*/ &c)) { // Accept c == 1 (rounded) or c == 0 (not rounded). bool is_rounded = false; if (c == 1) { @@ -2124,8 +2136,7 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node, } // Accept recognized halving add for vectorizable operands. Vectorized code uses the // shorthand idiomatic operation. Sequential code uses the original scalar expressions. - DCHECK(r != nullptr); - DCHECK(s != nullptr); + DCHECK(r != nullptr && s != nullptr); if (generate_code && vector_mode_ != kVector) { // de-idiom r = instruction->InputAt(0); s = instruction->InputAt(1); @@ -2175,19 +2186,11 @@ bool HLoopOptimization::VectorizeSADIdiom(LoopNode* node, HInstruction* v = instruction->InputAt(1); HInstruction* a = nullptr; HInstruction* b = nullptr; - if (v->GetType() == reduction_type && v->IsAbs()) { - HInstruction* x = v->InputAt(0); - if (x->GetType() == reduction_type) { - int64_t c = 0; - if (x->IsSub()) { - a = x->InputAt(0); - b = x->InputAt(1); - } else if (IsAddConst(x, /*out*/ &a, /*out*/ &c)) { - b = graph_->GetConstant(reduction_type, -c); // hidden SUB! - } - } - } - if (a == nullptr || b == nullptr) { + if (v->IsAbs() && + v->GetType() == reduction_type && + IsSubConst2(graph_, v->InputAt(0), /*out*/ &a, /*out*/ &b)) { + DCHECK(a != nullptr && b != nullptr); + } else { return false; } // Accept same-type or consistent sign extension for narrower-type on operands a and b. @@ -2220,8 +2223,7 @@ bool HLoopOptimization::VectorizeSADIdiom(LoopNode* node, } // Accept SAD idiom for vectorizable operands. Vectorized code uses the shorthand // idiomatic operation. Sequential code uses the original scalar expressions. - DCHECK(r != nullptr); - DCHECK(s != nullptr); + DCHECK(r != nullptr && s != nullptr); if (generate_code && vector_mode_ != kVector) { // de-idiom r = s = v->InputAt(0); } diff --git a/test/646-checker-hadd-short/src/Main.java b/test/646-checker-hadd-short/src/Main.java index 85c2fcaf22..c09da8125b 100644 --- a/test/646-checker-hadd-short/src/Main.java +++ b/test/646-checker-hadd-short/src/Main.java @@ -26,6 +26,10 @@ public class Main { static short[] sB2 = new short[M]; static short[] sBo = new short[M]; + private static int $inline$mone() { + return -1; + } + /// CHECK-START: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (before) /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none @@ -184,6 +188,35 @@ public class Main { } } + /// CHECK-START: void Main.rounding_halving_add_signed_alt3(short[], short[], short[]) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant -1 loop:none + /// CHECK-DAG: <> IntConstant 9 loop:none + /// CHECK-DAG: <> IntConstant -9 loop:none + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Shr [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_signed_alt3(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none + /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:true loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none + private static void rounding_halving_add_signed_alt3(short[] b1, short[] b2, short[] bo) { + int min_length = Math.min(bo.length, Math.min(b1.length, b2.length)); + for (int i = 0; i < min_length; i++) { + // Computations that cancel to adding 1 also do not confuse recognition. + bo[i] = (short) (((b1[i] + 9) + (b2[i] - 9) - $inline$mone()) >> 1); + } + } + /// CHECK-START: void Main.rounding_halving_add_unsigned(short[], short[], short[]) instruction_simplifier (before) /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> IntConstant 65535 loop:none @@ -366,6 +399,11 @@ public class Main { short e = (short) ((sB1[i] + sB2[i] + 1) >> 1); expectEquals(e, sBo[i]); } + rounding_halving_add_signed_alt3(sB1, sB2, sBo); + for (int i = 0; i < M; i++) { + short e = (short) ((sB1[i] + sB2[i] + 1) >> 1); + expectEquals(e, sBo[i]); + } rounding_halving_add_unsigned(sB1, sB2, sBo); for (int i = 0; i < M; i++) { short e = (short) (((sB1[i] & 0xffff) + (sB2[i] & 0xffff) + 1) >> 1); diff --git a/test/660-checker-simd-sad-short/src/Main.java b/test/660-checker-simd-sad-short/src/Main.java index 8a44d9ed12..77c9e53e0c 100644 --- a/test/660-checker-simd-sad-short/src/Main.java +++ b/test/660-checker-simd-sad-short/src/Main.java @@ -19,6 +19,10 @@ */ public class Main { + private static int $inline$seven() { + return 7; + } + // TODO: lower precision still coming, b/64091002 private static short sadShort2Short(short[] s1, short[] s2) { @@ -153,6 +157,102 @@ public class Main { return sad; } + /// CHECK-START: int Main.sadShort2IntConstant1(short[]) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant -7 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstant1(short[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 7 loop:none + /// CHECK-DAG: <> IntConstant 8 loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: <> VecSetScalars [<>] loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + private static int sadShort2IntConstant1(short[] s) { + int sad = 0; + for (int i = 0; i < s.length; i++) { + sad += Math.abs(s[i] - 7); // s[i] + -7 + } + return sad; + } + + /// CHECK-START: int Main.sadShort2IntConstant2(short[]) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 7 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstant2(short[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 7 loop:none + /// CHECK-DAG: <> IntConstant 8 loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: <> VecSetScalars [<>] loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + private static int sadShort2IntConstant2(short[] s) { + int sad = 0; + for (int i = 0; i < s.length; i++) { + sad += Math.abs(s[i] - $inline$seven()); // s[i] - 7 + } + return sad; + } + + /// CHECK-START: int Main.sadShort2IntConstant3(short[]) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 7 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Abs [<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstant3(short[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant -7 loop:none + /// CHECK-DAG: <> IntConstant 8 loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: <> VecSetScalars [<>] loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + private static int sadShort2IntConstant3(short[] s) { + int sad = 0; + for (int i = 0; i < s.length; i++) { + sad += Math.abs(s[i] + $inline$seven()); // hidden s[i] - (-7) + } + return sad; + } + /// CHECK-START: long Main.sadShort2Long(short[], short[]) loop_optimization (before) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 1 loop:none @@ -243,6 +343,9 @@ public class Main { expectEquals(65535, sadShort2IntAlt(s2, s1)); expectEquals(65535, sadShort2IntAlt2(s1, s2)); expectEquals(65535, sadShort2IntAlt2(s2, s1)); + expectEquals(32880, sadShort2IntConstant1(s1)); + expectEquals(32880, sadShort2IntConstant2(s1)); + expectEquals(32866, sadShort2IntConstant3(s1)); expectEquals(65535L, sadShort2Long(s1, s2)); expectEquals(65535L, sadShort2Long(s2, s1)); expectEquals(65536L, sadShort2LongAt1(s1, s2)); @@ -279,6 +382,9 @@ public class Main { expectEquals(1291788, sadShort2Int(s1, s2)); expectEquals(1291788, sadShort2IntAlt(s1, s2)); expectEquals(1291788, sadShort2IntAlt2(s1, s2)); + expectEquals(823907, sadShort2IntConstant1(s1)); + expectEquals(823907, sadShort2IntConstant2(s1)); + expectEquals(823953, sadShort2IntConstant3(s1)); expectEquals(1291788L, sadShort2Long(s1, s2)); expectEquals(1291789L, sadShort2LongAt1(s1, s2)); diff --git a/test/678-checker-simd-saturation/src/Main.java b/test/678-checker-simd-saturation/src/Main.java index d123cc2e25..decc691789 100644 --- a/test/678-checker-simd-saturation/src/Main.java +++ b/test/678-checker-simd-saturation/src/Main.java @@ -19,6 +19,14 @@ */ public class Main { + static final int $inline$p15() { + return 15; + } + + static final int $inline$m15() { + return -15; + } + // // Direct min-max. // @@ -230,8 +238,8 @@ public class Main { /// CHECK-START-{ARM,ARM64}: void Main.satSubPConstSByte(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecReplicateScalar loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none public static void satSubPConstSByte(byte[] a, byte[] b) { int n = Math.min(a.length, b.length); for (int i = 0; i < n; i++) { @@ -242,8 +250,8 @@ public class Main { /// CHECK-START-{ARM,ARM64}: void Main.satSubNConstSByte(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecReplicateScalar loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none public static void satSubNConstSByte(byte[] a, byte[] b) { int n = Math.min(a.length, b.length); for (int i = 0; i < n; i++) { @@ -282,8 +290,8 @@ public class Main { /// CHECK-START-{ARM,ARM64}: void Main.satSubPConstSShort(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecReplicateScalar loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none public static void satSubPConstSShort(short[] a, short[] b) { int n = Math.min(a.length, b.length); for (int i = 0; i < n; i++) { @@ -294,8 +302,8 @@ public class Main { /// CHECK-START-{ARM,ARM64}: void Main.satSubNConstSShort(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecReplicateScalar loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none public static void satSubNConstSShort(short[] a, short[] b) { int n = Math.min(a.length, b.length); for (int i = 0; i < n; i++) { @@ -304,7 +312,59 @@ public class Main { } // - // Alternatives. + // Alternatives 8-bit clipping. + // + + /// CHECK-START-{ARM,ARM64}: void Main.usatAddConst(byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void usatAddConst(byte[] a, byte[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + b[i] = (byte) Math.min((a[i] & 0xff) + $inline$p15(), 255); + } + } + + /// CHECK-START-{ARM,ARM64}: void Main.usatAddConstAlt(byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void usatAddConstAlt(byte[] a, byte[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + b[i] = (byte) Math.min((a[i] & 0xff) - $inline$m15(), 255); + } + } + + /// CHECK-START-{ARM,ARM64}: void Main.usatSubConst(byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void usatSubConst(byte[] a, byte[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + b[i] = (byte) Math.max((a[i] & 0xff) - $inline$p15(), 0); + } + } + + /// CHECK-START-{ARM,ARM64}: void Main.usatSubConstAlt(byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void usatSubConstAlt(byte[] a, byte[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + b[i] = (byte) Math.max((a[i] & 0xff) + $inline$m15(), 0); + } + } + + // + // Alternatives 16-bit clipping. // /// CHECK-START: void Main.satAlt1(short[], short[], short[]) loop_optimization (before) @@ -442,6 +502,34 @@ public class Main { } } + /// CHECK-START-{ARM,ARM64}: void Main.usatSubConst(short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void usatSubConst(short[] a, short[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + int t = a[i] & 0xffff; + int s = t - $inline$p15(); + b[i] = (short)(s > 0 ? s : 0); + } + } + + /// CHECK-START-{ARM,ARM64}: void Main.usatSubConstAlt(short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + public static void usatSubConstAlt(short[] a, short[] b) { + int n = Math.min(a.length, b.length); + for (int i = 0; i < n; i++) { + int t = a[i] & 0xffff; + int s = t + $inline$m15(); + b[i] = (short)(s > 0 ? s : 0); + } + } + // // Test drivers. // @@ -503,6 +591,27 @@ public class Main { byte e = (byte) Math.max(-15 - b1[i], -128); expectEquals(e, out[i]); } + // Alternatives. + usatAddConst(b1, out); + for (int i = 0; i < m; i++) { + byte e = (byte) Math.min((b1[i] & 0xff) + 15, 255); + expectEquals(e, out[i]); + } + usatAddConstAlt(b1, out); + for (int i = 0; i < m; i++) { + byte e = (byte) Math.min((b1[i] & 0xff) + 15, 255); + expectEquals(e, out[i]); + } + usatSubConst(b1, out); + for (int i = 0; i < m; i++) { + byte e = (byte) Math.max((b1[i] & 0xff) - 15, 0); + expectEquals(e, out[i]); + } + usatSubConstAlt(b1, out); + for (int i = 0; i < m; i++) { + byte e = (byte) Math.max((b1[i] & 0xff) - 15, 0); + expectEquals(e, out[i]); + } } private static void test16Bit() { @@ -630,6 +739,16 @@ public class Main { short e = (short) Math.max(Math.min(s1[i] + 15, 32767), -32752); expectEquals(e, out[i]); } + usatSubConst(s1, out); + for (int i = 0; i < m; i++) { + short e = (short) Math.max((s1[i] & 0xffff) - 15, 0); + expectEquals(e, out[i]); + } + usatSubConstAlt(s1, out); + for (int i = 0; i < m; i++) { + short e = (short) Math.max((s1[i] & 0xffff) - 15, 0); + expectEquals(e, out[i]); + } } public static void main(String[] args) { -- GitLab From f66b67f50171df13f6e56938a86286e47ced406e Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 28 Mar 2018 13:32:18 +0100 Subject: [PATCH 158/749] Merge basic arithmetic tests. And remove obsolete "doThrow" statements. Test: testrunner.py --host -t 411-optimizing-arith Change-Id: Ie44374c3ed007c7ffd0462ae4e8eef767bb28863 --- test/411-optimizing-arith-mul/info.txt | 1 - .../expected.txt | 0 test/411-optimizing-arith/info.txt | 7 +++++ .../src/DivTest.java} | 8 ++--- test/411-optimizing-arith/src/Main.java | 26 ++++++++++++++++ .../src/MulTest.java} | 17 +++++------ .../src/NegTest.java} | 30 ++----------------- .../src/RemTest.java} | 4 +-- .../src/ShiftsTest.java} | 4 +-- .../src/SubTest.java} | 4 +-- test/414-optimizing-arith-sub/expected.txt | 0 test/414-optimizing-arith-sub/info.txt | 1 - test/415-optimizing-arith-neg/expected.txt | 0 test/415-optimizing-arith-neg/info.txt | 1 - test/417-optimizing-arith-div/expected.txt | 0 test/417-optimizing-arith-div/info.txt | 1 - test/428-optimizing-arith-rem/expected.txt | 0 test/428-optimizing-arith-rem/info.txt | 1 - test/431-optimizing-arith-shifts/expected.txt | 0 test/431-optimizing-arith-shifts/info.txt | 1 - 20 files changed, 51 insertions(+), 55 deletions(-) delete mode 100644 test/411-optimizing-arith-mul/info.txt rename test/{411-optimizing-arith-mul => 411-optimizing-arith}/expected.txt (100%) create mode 100644 test/411-optimizing-arith/info.txt rename test/{417-optimizing-arith-div/src/Main.java => 411-optimizing-arith/src/DivTest.java} (98%) create mode 100644 test/411-optimizing-arith/src/Main.java rename test/{411-optimizing-arith-mul/src/Main.java => 411-optimizing-arith/src/MulTest.java} (93%) rename test/{415-optimizing-arith-neg/src/Main.java => 411-optimizing-arith/src/NegTest.java} (92%) rename test/{428-optimizing-arith-rem/src/Main.java => 411-optimizing-arith/src/RemTest.java} (98%) rename test/{431-optimizing-arith-shifts/src/Main.java => 411-optimizing-arith/src/ShiftsTest.java} (99%) rename test/{414-optimizing-arith-sub/src/Main.java => 411-optimizing-arith/src/SubTest.java} (99%) delete mode 100644 test/414-optimizing-arith-sub/expected.txt delete mode 100644 test/414-optimizing-arith-sub/info.txt delete mode 100644 test/415-optimizing-arith-neg/expected.txt delete mode 100644 test/415-optimizing-arith-neg/info.txt delete mode 100644 test/417-optimizing-arith-div/expected.txt delete mode 100644 test/417-optimizing-arith-div/info.txt delete mode 100644 test/428-optimizing-arith-rem/expected.txt delete mode 100644 test/428-optimizing-arith-rem/info.txt delete mode 100644 test/431-optimizing-arith-shifts/expected.txt delete mode 100644 test/431-optimizing-arith-shifts/info.txt diff --git a/test/411-optimizing-arith-mul/info.txt b/test/411-optimizing-arith-mul/info.txt deleted file mode 100644 index 10155512f0..0000000000 --- a/test/411-optimizing-arith-mul/info.txt +++ /dev/null @@ -1 +0,0 @@ -Tests for basic arithmethic operations. diff --git a/test/411-optimizing-arith-mul/expected.txt b/test/411-optimizing-arith/expected.txt similarity index 100% rename from test/411-optimizing-arith-mul/expected.txt rename to test/411-optimizing-arith/expected.txt diff --git a/test/411-optimizing-arith/info.txt b/test/411-optimizing-arith/info.txt new file mode 100644 index 0000000000..42be5d564f --- /dev/null +++ b/test/411-optimizing-arith/info.txt @@ -0,0 +1,7 @@ +Tests for basic arithmethic operations: + - multiply, + - subtract, + - negate, + - division, + - modulo (rem), + - shifts. diff --git a/test/417-optimizing-arith-div/src/Main.java b/test/411-optimizing-arith/src/DivTest.java similarity index 98% rename from test/417-optimizing-arith-div/src/Main.java rename to test/411-optimizing-arith/src/DivTest.java index 68e89b3eb2..7696d0a806 100644 --- a/test/417-optimizing-arith-div/src/Main.java +++ b/test/411-optimizing-arith/src/DivTest.java @@ -16,7 +16,7 @@ // Note that $opt$ is a marker for the optimizing compiler to test // it does compile the method. -public class Main { +public class DivTest { public static void expectEquals(int expected, int result) { if (expected != result) { @@ -98,11 +98,7 @@ public class Main { } } - public static void main(String[] args) { - div(); - } - - public static void div() { + public static void main() { divInt(); divLong(); divFloat(); diff --git a/test/411-optimizing-arith/src/Main.java b/test/411-optimizing-arith/src/Main.java new file mode 100644 index 0000000000..e1a43d3b57 --- /dev/null +++ b/test/411-optimizing-arith/src/Main.java @@ -0,0 +1,26 @@ +/* + * 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 Main { + public static void main(String args[]) { + MulTest.main(); + SubTest.main(); + NegTest.main(); + DivTest.main(); + RemTest.main(); + ShiftsTest.main(); + } +} diff --git a/test/411-optimizing-arith-mul/src/Main.java b/test/411-optimizing-arith/src/MulTest.java similarity index 93% rename from test/411-optimizing-arith-mul/src/Main.java rename to test/411-optimizing-arith/src/MulTest.java index 60e418e1e5..b9bffca0d1 100644 --- a/test/411-optimizing-arith-mul/src/Main.java +++ b/test/411-optimizing-arith/src/MulTest.java @@ -16,7 +16,7 @@ // Note that $opt$ is a marker for the optimizing compiler to test // it does compile the method. -public class Main { +public class MulTest { public static void expectEquals(int expected, int result) { if (expected != result) { @@ -72,11 +72,7 @@ public class Main { } } - public static void main(String[] args) { - mul(); - } - - public static void mul() { + public static void main() { mulInt(); mulLong(); mulFloat(); @@ -129,9 +125,12 @@ public class Main { expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(-2F, 3.40282346638528860e+38F)); expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(2F, Float.NEGATIVE_INFINITY)); expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(-2F, Float.NEGATIVE_INFINITY)); - expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY)); - expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)); - expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY)); + expectEquals(Float.NEGATIVE_INFINITY, + $opt$Mul(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY)); + expectEquals(Float.POSITIVE_INFINITY, + $opt$Mul(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)); + expectEquals(Float.POSITIVE_INFINITY, + $opt$Mul(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY)); } private static void mulDouble() { diff --git a/test/415-optimizing-arith-neg/src/Main.java b/test/411-optimizing-arith/src/NegTest.java similarity index 92% rename from test/415-optimizing-arith-neg/src/Main.java rename to test/411-optimizing-arith/src/NegTest.java index c53b639d40..83047269bb 100644 --- a/test/415-optimizing-arith-neg/src/Main.java +++ b/test/411-optimizing-arith/src/NegTest.java @@ -17,7 +17,7 @@ // Note that $opt$ is a marker for the optimizing compiler to test // it does compile the method, and that $noinline$ is a marker to // test that it does not inline it. -public class Main { +public class NegTest { public static void assertEquals(int expected, int result) { if (expected != result) { @@ -67,7 +67,7 @@ public class Main { } } - public static void main(String[] args) { + public static void main() { negInt(); $opt$noinline$InplaceNegOneInt(1); @@ -169,55 +169,29 @@ public class Main { } - static boolean doThrow = false; - private static void $opt$noinline$InplaceNegOneInt(int a) { - if (doThrow) { - // Try defeating inlining. - throw new Error(); - } a = -a; assertEquals(-1, a); } private static void $opt$noinline$InplaceNegOneLong(long a) { - if (doThrow) { - // Try defeating inlining. - throw new Error(); - } a = -a; assertEquals(-1L, a); } private static int $opt$noinline$NegInt(int a){ - if (doThrow) { - // Try defeating inlining. - throw new Error(); - } return -a; } private static long $opt$noinline$NegLong(long a){ - if (doThrow) { - // Try defeating inlining. - throw new Error(); - } return -a; } private static float $opt$noinline$NegFloat(float a){ - if (doThrow) { - // Try defeating inlining. - throw new Error(); - } return -a; } private static double $opt$noinline$NegDouble(double a){ - if (doThrow) { - // Try defeating inlining. - throw new Error(); - } return -a; } } diff --git a/test/428-optimizing-arith-rem/src/Main.java b/test/411-optimizing-arith/src/RemTest.java similarity index 98% rename from test/428-optimizing-arith-rem/src/Main.java rename to test/411-optimizing-arith/src/RemTest.java index 3f77318e6c..1b31f63569 100644 --- a/test/428-optimizing-arith-rem/src/Main.java +++ b/test/411-optimizing-arith/src/RemTest.java @@ -14,9 +14,9 @@ * limitations under the License. */ -public class Main { +public class RemTest { - public static void main(String[] args) { + public static void main() { remInt(); remLong(); } diff --git a/test/431-optimizing-arith-shifts/src/Main.java b/test/411-optimizing-arith/src/ShiftsTest.java similarity index 99% rename from test/431-optimizing-arith-shifts/src/Main.java rename to test/411-optimizing-arith/src/ShiftsTest.java index b7a112f6a3..139ff70bf0 100644 --- a/test/431-optimizing-arith-shifts/src/Main.java +++ b/test/411-optimizing-arith/src/ShiftsTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -public class Main { +public class ShiftsTest { public static void expectEquals(int expected, int result) { if (expected != result) { @@ -28,7 +28,7 @@ public class Main { } } - public static void main(String[] args) { + public static void main() { testShlInt(); testShlLong(); testShrInt(); diff --git a/test/414-optimizing-arith-sub/src/Main.java b/test/411-optimizing-arith/src/SubTest.java similarity index 99% rename from test/414-optimizing-arith-sub/src/Main.java rename to test/411-optimizing-arith/src/SubTest.java index b4531cdfd4..9c9ea92f20 100644 --- a/test/414-optimizing-arith-sub/src/Main.java +++ b/test/411-optimizing-arith/src/SubTest.java @@ -16,7 +16,7 @@ // Note that $opt$ is a marker for the optimizing compiler to test // it does compile the method. -public class Main { +public class SubTest { public static void expectEquals(int expected, int result) { if (expected != result) { @@ -70,7 +70,7 @@ public class Main { } } - public static void main(String[] args) { + public static void main() { subInt(); subLong(); subFloat(); diff --git a/test/414-optimizing-arith-sub/expected.txt b/test/414-optimizing-arith-sub/expected.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/414-optimizing-arith-sub/info.txt b/test/414-optimizing-arith-sub/info.txt deleted file mode 100644 index 1eaa14887b..0000000000 --- a/test/414-optimizing-arith-sub/info.txt +++ /dev/null @@ -1 +0,0 @@ -Subtraction tests. diff --git a/test/415-optimizing-arith-neg/expected.txt b/test/415-optimizing-arith-neg/expected.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/415-optimizing-arith-neg/info.txt b/test/415-optimizing-arith-neg/info.txt deleted file mode 100644 index 8494aad938..0000000000 --- a/test/415-optimizing-arith-neg/info.txt +++ /dev/null @@ -1 +0,0 @@ -Tests for arithmetic negation operations. diff --git a/test/417-optimizing-arith-div/expected.txt b/test/417-optimizing-arith-div/expected.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/417-optimizing-arith-div/info.txt b/test/417-optimizing-arith-div/info.txt deleted file mode 100644 index 1374b0ffb3..0000000000 --- a/test/417-optimizing-arith-div/info.txt +++ /dev/null @@ -1 +0,0 @@ -Tests for division operation. diff --git a/test/428-optimizing-arith-rem/expected.txt b/test/428-optimizing-arith-rem/expected.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/428-optimizing-arith-rem/info.txt b/test/428-optimizing-arith-rem/info.txt deleted file mode 100644 index 3e37ffeee8..0000000000 --- a/test/428-optimizing-arith-rem/info.txt +++ /dev/null @@ -1 +0,0 @@ -Tests for modulo (rem) operation. diff --git a/test/431-optimizing-arith-shifts/expected.txt b/test/431-optimizing-arith-shifts/expected.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/431-optimizing-arith-shifts/info.txt b/test/431-optimizing-arith-shifts/info.txt deleted file mode 100644 index 14ff264662..0000000000 --- a/test/431-optimizing-arith-shifts/info.txt +++ /dev/null @@ -1 +0,0 @@ -Tests for shift operations. -- GitLab From d1ef7178319074529af22cfff31a9a180e35655a Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 28 Mar 2018 09:16:31 +0100 Subject: [PATCH 159/749] [veridex] Add an appcompat rule and appcompat.sh script. Change-Id: I73897ce7d274d6daf4225a40df0a39e9a6980744 --- Android.mk | 1 + tools/veridex/Android.bp | 2 +- tools/veridex/Android.mk | 35 ++++++++++++++++++++++++++ tools/veridex/README.md | 14 +++++++++++ tools/veridex/appcompat.sh | 51 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 tools/veridex/Android.mk create mode 100644 tools/veridex/README.md create mode 100755 tools/veridex/appcompat.sh diff --git a/Android.mk b/Android.mk index 558986e562..e4f4e74cb2 100644 --- a/Android.mk +++ b/Android.mk @@ -67,6 +67,7 @@ include $(art_path)/tools/Android.mk include $(art_path)/tools/ahat/Android.mk include $(art_path)/tools/amm/Android.mk include $(art_path)/tools/dexfuzz/Android.mk +include $(art_path)/tools/veridex/Android.mk include $(art_path)/libart_fake/Android.mk ART_HOST_DEPENDENCIES := \ diff --git a/tools/veridex/Android.bp b/tools/veridex/Android.bp index ff181c89a7..a74bf3d7f9 100644 --- a/tools/veridex/Android.bp +++ b/tools/veridex/Android.bp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -art_cc_binary { +cc_binary { name: "veridex", host_supported: true, srcs: [ diff --git a/tools/veridex/Android.mk b/tools/veridex/Android.mk new file mode 100644 index 0000000000..4183054193 --- /dev/null +++ b/tools/veridex/Android.mk @@ -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. +# + +LOCAL_PATH := $(call my-dir) + +system_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/core_dex_intermediates/classes.dex +$(system_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000 +$(system_stub_dex): $(TOPDIR)prebuilts/sdk/system_current/android.jar | $(ZIP2ZIP) $(DX) + $(transform-classes-d8.jar-to-dex) + + +oahl_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/oahl_dex_intermediates/classes.dex +$(oahl_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000 +$(oahl_stub_dex): $(TOPDIR)prebuilts/sdk/org.apache.http.legacy/org.apache.http.legacy.jar | $(ZIP2ZIP) $(DX) + $(transform-classes-d8.jar-to-dex) + +.PHONY: appcompat + +appcompat: $(system_stub_dex) $(oahl_stub_dex) $(HOST_OUT_EXECUTABLES)/veridex \ + ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-light-greylist.txt \ + ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-dark-greylist.txt \ + ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-blacklist.txt diff --git a/tools/veridex/README.md b/tools/veridex/README.md new file mode 100644 index 0000000000..0f91b08771 --- /dev/null +++ b/tools/veridex/README.md @@ -0,0 +1,14 @@ +appcompat.sh +============ + +Given an APK, finds API uses that fall into the blacklist/greylists APIs. + +NOTE: appcompat.sh is still under development. It can report +API uses that do not execute at runtime, and reflection uses +that do not exist. It can also miss on reflection uses. + +To build it: +> make appcompat + +To run it: +> ./art/tools/veridex/appcompat.sh test.apk diff --git a/tools/veridex/appcompat.sh b/tools/veridex/appcompat.sh new file mode 100755 index 0000000000..f75aa4f0d0 --- /dev/null +++ b/tools/veridex/appcompat.sh @@ -0,0 +1,51 @@ +#!/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. + +# We want to be at the root for simplifying the "out" detection +# logic. +if [ ! -d art ]; then + echo "Script needs to be run at the root of the android tree." + exit 1 +fi + +# Logic for setting out_dir from build/make/core/envsetup.mk: +if [[ -z $OUT_DIR ]]; then + if [[ -z $OUT_DIR_COMMON_BASE ]]; then + OUT=out + else + OUT=${OUT_DIR_COMMON_BASE}/${PWD##*/} + fi +else + OUT=${OUT_DIR} +fi + +PACKAGING=${OUT}/target/common/obj/PACKAGING + +if [ -z "$ANDROID_HOST_OUT" ] ; then + ANDROID_HOST_OUT=${OUT}/host/linux-x86 +fi + +echo "NOTE: appcompat.sh is still under development. It can report" +echo "API uses that do not execute at runtime, and reflection uses" +echo "that do not exist. It can also miss on reflection uses." + + +${ANDROID_HOST_OUT}/bin/veridex \ + --core-stubs=${PACKAGING}/core_dex_intermediates/classes.dex:${PACKAGING}/oahl_dex_intermediates/classes.dex \ + --blacklist=${PACKAGING}/hiddenapi-blacklist.txt \ + --light-greylist=${PACKAGING}/hiddenapi-light-greylist.txt \ + --dark-greylist=${PACKAGING}/hiddenapi-dark-greylist.txt \ + --dex-file=$1 -- GitLab From 82cf9a21a3146f3caf2906f7c48cafe9269dd057 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Tue, 27 Mar 2018 16:36:32 +0100 Subject: [PATCH 160/749] ART: heap counter check Avoid potential CHECK failure updating num_bytes_freed_revoke_. Bug: 31023171 Test: art/test.py --host --64 -j32 Change-Id: Ic3fb621c88f5b858f7b4a3ed1aaa1eef36b1e481 --- runtime/gc/heap.cc | 22 +++++++++++++--------- runtime/gc/heap.h | 2 ++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 52afb3850c..a64a1e7061 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -3727,13 +3727,21 @@ void Heap::RequestTrim(Thread* self) { task_processor_->AddTask(self, added_task); } +void Heap::IncrementNumberOfBytesFreedRevoke(size_t freed_bytes_revoke) { + size_t previous_num_bytes_freed_revoke = + num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst); + // Check the updated value is less than the number of bytes allocated. There is a risk of + // execution being suspended between the increment above and the CHECK below, leading to + // the use of previous_num_bytes_freed_revoke in the comparison. + CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed), + previous_num_bytes_freed_revoke + freed_bytes_revoke); +} + void Heap::RevokeThreadLocalBuffers(Thread* thread) { if (rosalloc_space_ != nullptr) { size_t freed_bytes_revoke = rosalloc_space_->RevokeThreadLocalBuffers(thread); if (freed_bytes_revoke > 0U) { - num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst); - CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed), - num_bytes_freed_revoke_.load(std::memory_order_relaxed)); + IncrementNumberOfBytesFreedRevoke(freed_bytes_revoke); } } if (bump_pointer_space_ != nullptr) { @@ -3748,9 +3756,7 @@ void Heap::RevokeRosAllocThreadLocalBuffers(Thread* thread) { if (rosalloc_space_ != nullptr) { size_t freed_bytes_revoke = rosalloc_space_->RevokeThreadLocalBuffers(thread); if (freed_bytes_revoke > 0U) { - num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst); - CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed), - num_bytes_freed_revoke_.load(std::memory_order_relaxed)); + IncrementNumberOfBytesFreedRevoke(freed_bytes_revoke); } } } @@ -3759,9 +3765,7 @@ void Heap::RevokeAllThreadLocalBuffers() { if (rosalloc_space_ != nullptr) { size_t freed_bytes_revoke = rosalloc_space_->RevokeAllThreadLocalBuffers(); if (freed_bytes_revoke > 0U) { - num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst); - CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed), - num_bytes_freed_revoke_.load(std::memory_order_relaxed)); + IncrementNumberOfBytesFreedRevoke(freed_bytes_revoke); } } if (bump_pointer_space_ != nullptr) { diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 9af57d17e5..ef1c0887bb 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -1091,6 +1091,8 @@ class Heap { return max_free_; } + ALWAYS_INLINE void IncrementNumberOfBytesFreedRevoke(size_t freed_bytes_revoke); + void TraceHeapSize(size_t heap_size); // Remove a vlog code from heap-inl.h which is transitively included in half the world. -- GitLab From 1eeefa63b9ad8289138b83dfccda3ff38ed23fb6 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 19 Mar 2018 13:47:56 -0700 Subject: [PATCH 161/749] Remove unhelpful ExceptionDescribe from DDMS error path. If a DDMS handler threw an error or returned invalid input we would call ExceptionDescribe on the exception. This is usually not very helpful since it goes to System.err instead of logcat and can make testing difficult. This change replaces the ExceptionDescribe with a LOG(INFO). Test: ./test.py --host -j50 Bug: 70988713 Change-Id: I41e743c42e63c0dcb6d32473c31f391983819d32 --- runtime/debugger.cc | 15 ++++++----- test/1940-ddms-ext/check | 21 --------------- test/1940-ddms-ext/expected_error.txt | 4 --- test/1940-ddms-ext/remove_error.py | 38 --------------------------- 4 files changed, 9 insertions(+), 69 deletions(-) delete mode 100755 test/1940-ddms-ext/check delete mode 100644 test/1940-ddms-ext/expected_error.txt delete mode 100755 test/1940-ddms-ext/remove_error.py diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 99a4c77979..4a9449640b 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -4354,9 +4354,11 @@ bool Dbg::DdmHandleChunk(JNIEnv* env, WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch, type, dataArray.get(), 0, data.size())); if (env->ExceptionCheck()) { - LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type); - env->ExceptionDescribe(); - env->ExceptionClear(); + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type) << std::endl + << self->GetException()->Dump(); + self->ClearException(); return false; } @@ -4400,10 +4402,11 @@ bool Dbg::DdmHandleChunk(JNIEnv* env, reinterpret_cast(out_data->data())); if (env->ExceptionCheck()) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); LOG(INFO) << StringPrintf("Exception thrown when reading response data from dispatcher 0x%08x", - type); - env->ExceptionDescribe(); - env->ExceptionClear(); + type) << std::endl << self->GetException()->Dump(); + self->ClearException(); return false; } diff --git a/test/1940-ddms-ext/check b/test/1940-ddms-ext/check deleted file mode 100755 index 91966b41a0..0000000000 --- a/test/1940-ddms-ext/check +++ /dev/null @@ -1,21 +0,0 @@ -#!/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. - -# Need to pull out the describeException ouput since that won't be there on -# device. -./remove_error.py "$2" "./expected_error.txt" > "$2.tmp" - -./default-check "$1" "$2.tmp" diff --git a/test/1940-ddms-ext/expected_error.txt b/test/1940-ddms-ext/expected_error.txt deleted file mode 100644 index 73883b46e2..0000000000 --- a/test/1940-ddms-ext/expected_error.txt +++ /dev/null @@ -1,4 +0,0 @@ -java.lang.ArrayIndexOutOfBoundsException: byte[] offset=12 length=55 src.length=1 - at art.Test1940.processChunk(Native Method) - at art.Test1940.run(Test1940.java:156) - at Main.main(Main.java:19) diff --git a/test/1940-ddms-ext/remove_error.py b/test/1940-ddms-ext/remove_error.py deleted file mode 100755 index 638c479a31..0000000000 --- a/test/1940-ddms-ext/remove_error.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python3 -# -# 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 argparse - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument('input_data', type=open) - parser.add_argument('expected_error', type=str) - args = parser.parse_args() - - for line in map(str.rstrip, args.input_data.readlines()): - print_full = True - with open(args.expected_error) as err_file: - for err_line in map(str.rstrip, err_file): - if line.startswith(err_line): - print_full = False - if line != err_line: - print(line[len(err_line):]) - break - if print_full and line != '': - print(line) - -if __name__ == '__main__': - main() -- GitLab From b9ad26d1ed9146b89555d4333021f44eeb831f05 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Fri, 23 Mar 2018 17:10:04 -0700 Subject: [PATCH 162/749] Ensure that OSR still is possible with jvmti We were previously disabling OSR by always claiming to be interested in every method. This could cause slowdown on some methods. To fix this we correctly only claim to be interested in methods if we might hit breakpoints from an invoke, have modified locals, or have forced the function to the interpreter. There was also a minor bug in the instrumentation removal code that caused prebuilt compiled code to be used when it should have been ignored. In the future we should get the granularity down to single frames but that is currently not possible. Test: ./test.py --host -j50 --all -t 993 Test: ./test.py --host -j50 Test: am start --attach-agent -n com.example.android.displayingbitmaps/.ui.ImageGridActivity Run blur filter. Bug: 76226464 Change-Id: I6a7aa0c6353372aebe01613b66841543614f3054 --- openjdkjvmti/deopt_manager.cc | 74 +++++++++++-------- openjdkjvmti/deopt_manager.h | 20 ++++- openjdkjvmti/ti_method.cc | 3 + runtime/instrumentation.cc | 8 +- .../expected.txt | 2 - .../src/Main.java | 19 +++-- 6 files changed, 85 insertions(+), 41 deletions(-) diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc index 6d84ffa53f..380d95c545 100644 --- a/openjdkjvmti/deopt_manager.cc +++ b/openjdkjvmti/deopt_manager.cc @@ -53,14 +53,18 @@ namespace openjdkjvmti { // TODO We should make this much more selective in the future so we only return true when we -// actually care about the method (i.e. had locals changed, have breakpoints, etc.). For now though -// we can just assume that we care we are loaded at all. -// -// Even if we don't keep track of this at the method level we might want to keep track of it at the -// level of enabled capabilities. -bool JvmtiMethodInspectionCallback::IsMethodBeingInspected( - art::ArtMethod* method ATTRIBUTE_UNUSED) { - return true; +// actually care about the method at this time (ie active frames had locals changed). For now we +// just assume that if anything has changed any frame's locals we care about all methods. If nothing +// has we only care about methods with active breakpoints on them. In the future we should probably +// rewrite all of this to instead do this at the ShadowFrame or thread granularity. +bool JvmtiMethodInspectionCallback::IsMethodBeingInspected(art::ArtMethod* method) { + // Non-java-debuggable runtimes we need to assume that any method might not be debuggable and + // therefore potentially being inspected (due to inlines). If we are debuggable we rely hard on + // inlining not being done since we don't keep track of which methods get inlined where and simply + // look to see if the method is breakpointed. + return !art::Runtime::Current()->IsJavaDebuggable() || + manager_->HaveLocalsChanged() || + manager_->MethodHasBreakpoints(method); } bool JvmtiMethodInspectionCallback::IsMethodSafeToJit(art::ArtMethod* method) { @@ -75,7 +79,10 @@ DeoptManager::DeoptManager() performing_deoptimization_(false), global_deopt_count_(0), deopter_count_(0), - inspection_callback_(this) { } + breakpoint_status_lock_("JVMTI_BreakpointStatusLock", + static_cast(art::LockLevel::kAbortLock + 1)), + inspection_callback_(this), + set_local_variable_called_(false) { } void DeoptManager::Setup() { art::ScopedThreadStateChange stsc(art::Thread::Current(), @@ -121,14 +128,11 @@ void DeoptManager::FinishSetup() { } bool DeoptManager::MethodHasBreakpoints(art::ArtMethod* method) { - art::MutexLock lk(art::Thread::Current(), deoptimization_status_lock_); + art::MutexLock lk(art::Thread::Current(), breakpoint_status_lock_); return MethodHasBreakpointsLocked(method); } bool DeoptManager::MethodHasBreakpointsLocked(art::ArtMethod* method) { - if (deopter_count_ == 0) { - return false; - } auto elem = breakpoint_status_.find(method); return elem != breakpoint_status_.end() && elem->second != 0; } @@ -158,18 +162,23 @@ void DeoptManager::AddMethodBreakpoint(art::ArtMethod* method) { art::ScopedThreadSuspension sts(self, art::kSuspended); deoptimization_status_lock_.ExclusiveLock(self); - - DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; - - if (MethodHasBreakpointsLocked(method)) { - // Don't need to do anything extra. - breakpoint_status_[method]++; - // Another thread might be deoptimizing the very method we just added new breakpoints for. Wait - // for any deopts to finish before moving on. - WaitForDeoptimizationToFinish(self); - return; + { + breakpoint_status_lock_.ExclusiveLock(self); + + DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; + + if (MethodHasBreakpointsLocked(method)) { + // Don't need to do anything extra. + breakpoint_status_[method]++; + // Another thread might be deoptimizing the very method we just added new breakpoints for. + // Wait for any deopts to finish before moving on. + breakpoint_status_lock_.ExclusiveUnlock(self); + WaitForDeoptimizationToFinish(self); + return; + } + breakpoint_status_[method] = 1; + breakpoint_status_lock_.ExclusiveUnlock(self); } - breakpoint_status_[method] = 1; auto instrumentation = art::Runtime::Current()->GetInstrumentation(); if (instrumentation->IsForcedInterpretOnly()) { // We are already interpreting everything so no need to do anything. @@ -196,17 +205,22 @@ void DeoptManager::RemoveMethodBreakpoint(art::ArtMethod* method) { // need but since that is very heavy we will instead just use a condition variable to make sure we // don't race with ourselves. deoptimization_status_lock_.ExclusiveLock(self); - - DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; - DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without " - << "breakpoints present!"; + bool is_last_breakpoint; + { + art::MutexLock mu(self, breakpoint_status_lock_); + + DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; + DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without " + << "breakpoints present!"; + breakpoint_status_[method] -= 1; + is_last_breakpoint = (breakpoint_status_[method] == 0); + } auto instrumentation = art::Runtime::Current()->GetInstrumentation(); - breakpoint_status_[method] -= 1; if (UNLIKELY(instrumentation->IsForcedInterpretOnly())) { // We don't need to do anything since we are interpreting everything anyway. deoptimization_status_lock_.ExclusiveUnlock(self); return; - } else if (breakpoint_status_[method] == 0) { + } else if (is_last_breakpoint) { if (UNLIKELY(is_default)) { RemoveDeoptimizeAllMethodsLocked(self); } else { diff --git a/openjdkjvmti/deopt_manager.h b/openjdkjvmti/deopt_manager.h index a495b6835c..a38690c49e 100644 --- a/openjdkjvmti/deopt_manager.h +++ b/openjdkjvmti/deopt_manager.h @@ -32,6 +32,7 @@ #ifndef ART_OPENJDKJVMTI_DEOPT_MANAGER_H_ #define ART_OPENJDKJVMTI_DEOPT_MANAGER_H_ +#include #include #include "jni.h" @@ -107,9 +108,17 @@ class DeoptManager { static DeoptManager* Get(); + bool HaveLocalsChanged() const { + return set_local_variable_called_.load(); + } + + void SetLocalsUpdated() { + set_local_variable_called_.store(true); + } + private: bool MethodHasBreakpointsLocked(art::ArtMethod* method) - REQUIRES(deoptimization_status_lock_); + REQUIRES(breakpoint_status_lock_); // Wait until nothing is currently in the middle of deoptimizing/undeoptimizing something. This is // needed to ensure that everything is synchronized since threads need to drop the @@ -156,13 +165,20 @@ class DeoptManager { // Number of users of deoptimization there currently are. uint32_t deopter_count_ GUARDED_BY(deoptimization_status_lock_); + // A mutex that just protects the breakpoint-status map. This mutex should always be at the + // bottom of the lock hierarchy. Nothing more should be locked if we hold this. + art::Mutex breakpoint_status_lock_ ACQUIRED_BEFORE(art::Locks::abort_lock_); // A map from methods to the number of breakpoints in them from all envs. std::unordered_map breakpoint_status_ - GUARDED_BY(deoptimization_status_lock_); + GUARDED_BY(breakpoint_status_lock_); // The MethodInspectionCallback we use to tell the runtime if we care about particular methods. JvmtiMethodInspectionCallback inspection_callback_; + // Set to true if anything calls SetLocalVariables on any thread since we need to be careful about + // OSR after this. + std::atomic set_local_variable_called_; + // Helper for setting up/tearing-down for deoptimization. friend class ScopedDeoptimizationContext; }; diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index bf2e6cd104..b83310dc85 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -915,6 +915,9 @@ jvmtiError MethodUtil::SetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED, if (depth < 0) { return ERR(ILLEGAL_ARGUMENT); } + // Make sure that we know not to do any OSR anymore. + // TODO We should really keep track of this at the Frame granularity. + DeoptManager::Get()->SetLocalsUpdated(); art::Thread* self = art::Thread::Current(); // Suspend JIT since it can get confused if we deoptimize methods getting jitted. art::jit::ScopedJitSuspend suspend_jit; diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 84a148f21c..ec3e10e2f8 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -139,10 +139,14 @@ static void UpdateEntrypoints(ArtMethod* method, const void* quick_code) bool Instrumentation::NeedDebugVersionFor(ArtMethod* method) const REQUIRES_SHARED(Locks::mutator_lock_) { - return Runtime::Current()->IsJavaDebuggable() && + art::Runtime* runtime = Runtime::Current(); + return runtime->IsJavaDebuggable() && !method->IsNative() && !method->IsProxyMethod() && - Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method); + // If we don't have a jit this can push us to the pre-compiled version of methods which is + // not something we want since we are debuggable. + (UNLIKELY(runtime->GetJit() == nullptr) || + runtime->GetRuntimeCallbacks()->IsMethodBeingInspected(method)); } void Instrumentation::InstallStubsForMethod(ArtMethod* method) { diff --git a/test/1935-get-set-current-frame-jit/expected.txt b/test/1935-get-set-current-frame-jit/expected.txt index cdb8f6a825..a685891775 100644 --- a/test/1935-get-set-current-frame-jit/expected.txt +++ b/test/1935-get-set-current-frame-jit/expected.txt @@ -1,7 +1,5 @@ JNI_OnLoad called From GetLocalInt(), value is 42 -isInOsrCode? false Value is '42' Setting TARGET to 1337 -isInOsrCode? false Value is '1337' diff --git a/test/1935-get-set-current-frame-jit/src/Main.java b/test/1935-get-set-current-frame-jit/src/Main.java index 714a98aaf3..aad4cd7135 100644 --- a/test/1935-get-set-current-frame-jit/src/Main.java +++ b/test/1935-get-set-current-frame-jit/src/Main.java @@ -49,9 +49,11 @@ public class Main { public static class IntRunner implements Runnable { private volatile boolean continueBusyLoop; private volatile boolean inBusyLoop; - public IntRunner() { + private final boolean expectOsr; + public IntRunner(boolean expectOsr) { this.continueBusyLoop = true; this.inBusyLoop = false; + this.expectOsr = expectOsr; } public void run() { int TARGET = 42; @@ -64,9 +66,16 @@ public class Main { Main.ensureJitCompiled(IntRunner.class, "run"); i++; } - // We shouldn't be doing OSR since we are using JVMTI and the get/set prevents OSR. + // We shouldn't be doing OSR since we are using JVMTI and the set prevents OSR. // Set local will also push us to interpreter but the get local may remain in compiled code. - System.out.println("isInOsrCode? " + (hasJit() && Main.isInOsrCode("run"))); + if (hasJit()) { + boolean inOsr = Main.isInOsrCode("run"); + if (expectOsr && !inOsr) { + throw new Error("Expected to be in OSR but was not."); + } else if (!expectOsr && inOsr) { + throw new Error("Expected not to be in OSR but was."); + } + } reportValue(TARGET); } public void waitForBusyLoopStart() { while (!inBusyLoop) {} } @@ -78,7 +87,7 @@ public class Main { public static void runGet() throws Exception { Method target = IntRunner.class.getDeclaredMethod("run"); // Get Int - IntRunner int_runner = new IntRunner(); + IntRunner int_runner = new IntRunner(true); Thread target_get = new Thread(int_runner, "GetLocalInt - Target"); target_get.start(); int_runner.waitForBusyLoopStart(); @@ -108,7 +117,7 @@ public class Main { public static void runSet() throws Exception { Method target = IntRunner.class.getDeclaredMethod("run"); // Set Int - IntRunner int_runner = new IntRunner(); + IntRunner int_runner = new IntRunner(false); Thread target_set = new Thread(int_runner, "SetLocalInt - Target"); target_set.start(); int_runner.waitForBusyLoopStart(); -- GitLab From 6446437eacf378b5d72a25718b19b777131d90e0 Mon Sep 17 00:00:00 2001 From: Hans Boehm Date: Wed, 28 Mar 2018 15:41:19 -0700 Subject: [PATCH 163/749] Fix Long reverse intrinsic comment Test: TreeHugger only Change-Id: I8823e8d9a9db88ed280f9d968184153543697f39 --- runtime/interpreter/interpreter_intrinsics.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc index 681a582b5d..022b1395bf 100644 --- a/runtime/interpreter/interpreter_intrinsics.cc +++ b/runtime/interpreter/interpreter_intrinsics.cc @@ -91,7 +91,7 @@ BINARY_II_INTRINSIC(MterpIntegerRotateLeft, (Rot), SetI); // java.lang.Integer.signum(I)I UNARY_INTRINSIC(MterpIntegerSignum, Signum, GetVReg, SetI); -// java.lang.Long.reverse(I)I +// java.lang.Long.reverse(J)J UNARY_INTRINSIC(MterpLongReverse, ReverseBits64, GetVRegLong, SetJ); // java.lang.Long.reverseBytes(J)J -- GitLab From a5cd4c05a8218b18a650b411b9978a44d7fc2f78 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Wed, 28 Mar 2018 16:07:39 -0700 Subject: [PATCH 164/749] Ensure that we don't suspend during GetOwnedMonitorInfoCommon We incorrectly failed to tell the synchronous checkpoint in GetOwnedMonitorInfoCommon that the calling thread needs to avoid suspending. Because of this it is possible for the monitors being transfered between threads to miss gc-marking. If the function is called during a GC this can lead to illegal reads. Bug: 76003243 Test: while ./test/run-test --host 1922; do; done Test: ./test.py --host Change-Id: I99be60b0541ee82f241605e6492610a21f5ee4ec --- openjdkjvmti/ti_stack.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc index 41a649b5e3..4526be4cbe 100644 --- a/openjdkjvmti/ti_stack.cc +++ b/openjdkjvmti/ti_stack.cc @@ -925,7 +925,9 @@ static jvmtiError GetOwnedMonitorInfoCommon(const art::ScopedObjectAccessAlready if (target != self) { called_method = true; // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. - if (!target->RequestSynchronousCheckpoint(&closure)) { + // Since this deals with object references we need to avoid going to sleep. + art::ScopedAssertNoThreadSuspension sants("Getting owned monitor usage"); + if (!target->RequestSynchronousCheckpoint(&closure, art::ThreadState::kRunnable)) { return ERR(THREAD_NOT_ALIVE); } } else { -- GitLab From a90c68c3a6acb9e47ec316edfa397cb6eb0a9e45 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 26 Mar 2018 14:50:24 -0700 Subject: [PATCH 165/749] Make testrunner.py --all more useful. Previously if you used testrunner --all it would try to run all ~800000 variants of the test you specified. Often it is much more useful to specify some set of config options to be used by all tests but to run all remaining configurations. This change makes so --all will only add on unspecified variant-types. For example ./test/testrunner/testrunner.py --host --32 -t 001-HelloWorld will runn all ~400000 32-bit variants of test 001-HelloWorld but none of the 64-bit variants. Test: ./test/testrunner/testrunner.py --host Test: ./test/testrunner/testrunner.py --jvm Test: ./test/testrunner/testrunner.py --all --host --32 --dry-run -t 001-HelloWorld Test: ./test/testrunner/testrunner.py --all --host --dry-run -t 001-HelloWorld Test: ./test/testrunner/testrunner.py --all --host --npic-test --jit --dry-run -t 001-HelloWorld Change-Id: I9f4033bbb85ed092ed1251faf15d5ea479602b86 --- test/testrunner/testrunner.py | 77 +++++++++++++---------------------- 1 file changed, 29 insertions(+), 48 deletions(-) diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 734a600c5e..0cfb661019 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -174,56 +174,37 @@ def setup_test_env(): global _user_input_variants global run_all_configs + # These are the default variant-options we will use if nothing in the group is specified. + default_variants = { + 'target': {'host', 'target'}, + 'pictest': {'npictest'}, + 'prebuild': {'prebuild'}, + 'cdex_level': {'cdex-fast'}, + 'jvmti': { 'no-jvmti'}, + 'compiler': {'optimizing', + 'jit', + 'interpreter', + 'interp-ac', + 'speed-profile'}, + 'relocate': {'no-relocate'}, + 'trace': {'ntrace'}, + 'gc': {'cms'}, + 'jni': {'checkjni'}, + 'image': {'picimage'}, + 'pictest': {'pictest'}, + 'debuggable': {'ndebuggable'}, + 'run': {'debug'}, + # address_sizes_target depends on the target so it is dealt with below. + } + # We want to pull these early since the full VARIANT_TYPE_DICT has a few additional ones we don't + # want to pick up if we pass --all. + default_variants_keys = default_variants.keys() if run_all_configs: - target_types = _user_input_variants['target'] - _user_input_variants = VARIANT_TYPE_DICT - _user_input_variants['target'] = target_types - - if not _user_input_variants['target']: - _user_input_variants['target'].add('host') - _user_input_variants['target'].add('target') - - if not _user_input_variants['prebuild']: # Default - _user_input_variants['prebuild'].add('prebuild') - - if not _user_input_variants['cdex_level']: # Default - _user_input_variants['cdex_level'].add('cdex-fast') - - # By default only run without jvmti - if not _user_input_variants['jvmti']: - _user_input_variants['jvmti'].add('no-jvmti') - - # By default we run all 'compiler' variants. - if not _user_input_variants['compiler'] and _user_input_variants['target'] != 'jvm': - _user_input_variants['compiler'].add('optimizing') - _user_input_variants['compiler'].add('jit') - _user_input_variants['compiler'].add('interpreter') - _user_input_variants['compiler'].add('interp-ac') - _user_input_variants['compiler'].add('speed-profile') - - if not _user_input_variants['relocate']: # Default - _user_input_variants['relocate'].add('no-relocate') - - if not _user_input_variants['trace']: # Default - _user_input_variants['trace'].add('ntrace') - - if not _user_input_variants['gc']: # Default - _user_input_variants['gc'].add('cms') - - if not _user_input_variants['jni']: # Default - _user_input_variants['jni'].add('checkjni') - - if not _user_input_variants['image']: # Default - _user_input_variants['image'].add('picimage') - - if not _user_input_variants['pictest']: # Default - _user_input_variants['pictest'].add('npictest') - - if not _user_input_variants['debuggable']: # Default - _user_input_variants['debuggable'].add('ndebuggable') + default_variants = VARIANT_TYPE_DICT - if not _user_input_variants['run']: # Default - _user_input_variants['run'].add('debug') + for key in default_variants_keys: + if not _user_input_variants[key]: + _user_input_variants[key] = default_variants[key] _user_input_variants['address_sizes_target'] = collections.defaultdict(set) if not _user_input_variants['address_sizes']: -- GitLab From aa12001baf69c124ab3901c13385aaa43fc76987 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 28 Mar 2018 16:23:24 -0700 Subject: [PATCH 166/749] ART: Refactor hidden_api Add hidden_api.cc, move handling of hidden fields there. Also remove an unnecessary include that meant hidden_api was imported into too many compilation units, and fix transitive includes. Bug: 73896556 Test: mmma art Test: m test-art-host Change-Id: Ie47e11abcea68e326c410bab215ebbfbf049051b --- runtime/Android.bp | 1 + runtime/class_linker.cc | 1 + runtime/hidden_api.cc | 172 ++++++++++++++++++ runtime/hidden_api.h | 183 ++++--------------- runtime/hidden_api_test.cc | 184 ++++++++++---------- runtime/mirror/class-inl.h | 1 - runtime/native/dalvik_system_ZygoteHooks.cc | 1 + runtime/runtime.cc | 1 + 8 files changed, 306 insertions(+), 238 deletions(-) create mode 100644 runtime/hidden_api.cc diff --git a/runtime/Android.bp b/runtime/Android.bp index 4736fd3ddb..c0f1c366b8 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -87,6 +87,7 @@ cc_defaults { "gc/space/zygote_space.cc", "gc/task_processor.cc", "gc/verification.cc", + "hidden_api.cc", "hprof/hprof.cc", "image.cc", "index_bss_mapping.cc", diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 2110d0493b..412834c366 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -72,6 +72,7 @@ #include "gc/space/space-inl.h" #include "gc_root-inl.h" #include "handle_scope-inl.h" +#include "hidden_api.h" #include "image-inl.h" #include "imt_conflict_table.h" #include "imtable-inl.h" diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc new file mode 100644 index 0000000000..f0b36a090a --- /dev/null +++ b/runtime/hidden_api.cc @@ -0,0 +1,172 @@ +/* + * 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. + */ + +#include "hidden_api.h" + +#include "base/dumpable.h" + +namespace art { +namespace hiddenapi { + +static inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { + switch (value) { + case kReflection: + os << "reflection"; + break; + case kJNI: + os << "JNI"; + break; + case kLinking: + os << "linking"; + break; + } + return os; +} + +static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags::ApiList apiList) { + return static_cast(policy) == static_cast(apiList); +} + +// GetMemberAction-related static_asserts. +static_assert( + EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) && + EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) && + EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist), + "Mismatch between EnforcementPolicy and ApiList enums"); +static_assert( + EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList && + EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly, + "EnforcementPolicy values ordering not correct"); + +namespace detail { + +MemberSignature::MemberSignature(ArtField* field) { + member_type_ = "field"; + signature_parts_ = { + field->GetDeclaringClass()->GetDescriptor(&tmp_), + "->", + field->GetName(), + ":", + field->GetTypeDescriptor() + }; +} + +MemberSignature::MemberSignature(ArtMethod* method) { + member_type_ = "method"; + signature_parts_ = { + method->GetDeclaringClass()->GetDescriptor(&tmp_), + "->", + method->GetName(), + method->GetSignature().ToString() + }; +} + +bool MemberSignature::DoesPrefixMatch(const std::string& prefix) const { + size_t pos = 0; + for (const std::string& part : signature_parts_) { + size_t count = std::min(prefix.length() - pos, part.length()); + if (prefix.compare(pos, count, part, 0, count) == 0) { + pos += count; + } else { + return false; + } + } + // We have a complete match if all parts match (we exit the loop without + // returning) AND we've matched the whole prefix. + return pos == prefix.length(); +} + +bool MemberSignature::IsExempted(const std::vector& exemptions) { + for (const std::string& exemption : exemptions) { + if (DoesPrefixMatch(exemption)) { + return true; + } + } + return false; +} + +void MemberSignature::Dump(std::ostream& os) const { + for (std::string part : signature_parts_) { + os << part; + } +} + +void MemberSignature::WarnAboutAccess(AccessMethod access_method, + HiddenApiAccessFlags::ApiList list) { + LOG(WARNING) << "Accessing hidden " << member_type_ << " " << Dumpable(*this) + << " (" << list << ", " << access_method << ")"; +} + +template +bool ShouldBlockAccessToMemberImpl(T* member, Action action, AccessMethod access_method) { + // Get the signature, we need it later. + MemberSignature member_signature(member); + + Runtime* runtime = Runtime::Current(); + + if (action == kDeny) { + // If we were about to deny, check for an exemption first. + // Exempted APIs are treated as light grey list. + if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) { + action = kAllowButWarn; + // Avoid re-examining the exemption list next time. + // Note this results in the warning below showing "light greylist", which + // seems like what one would expect. Exemptions effectively add new members to + // the light greylist. + member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( + member->GetAccessFlags(), HiddenApiAccessFlags::kLightGreylist)); + } + } + + // Print a log message with information about this class member access. + // We do this regardless of whether we block the access or not. + member_signature.WarnAboutAccess(access_method, + HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags())); + + if (action == kDeny) { + // Block access + return true; + } + + // Allow access to this member but print a warning. + DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast); + + // Depending on a runtime flag, we might move the member into whitelist and + // skip the warning the next time the member is accessed. + if (runtime->ShouldDedupeHiddenApiWarnings()) { + member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( + member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist)); + } + + // If this action requires a UI warning, set the appropriate flag. + if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) { + runtime->SetPendingHiddenApiWarning(true); + } + + return false; +} + +// Need to instantiate this. +template bool ShouldBlockAccessToMemberImpl(ArtField* member, + Action action, + AccessMethod access_method); +template bool ShouldBlockAccessToMemberImpl(ArtMethod* member, + Action action, + AccessMethod access_method); + +} // namespace detail +} // namespace hiddenapi +} // namespace art diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index 0e6f159c53..cc6c146f00 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -19,7 +19,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" -#include "base/dumpable.h" +#include "base/mutex.h" #include "dex/hidden_api_access_flags.h" #include "mirror/class-inl.h" #include "reflection.h" @@ -58,25 +58,6 @@ enum AccessMethod { kLinking, }; -inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { - switch (value) { - case kReflection: - os << "reflection"; - break; - case kJNI: - os << "JNI"; - break; - case kLinking: - os << "linking"; - break; - } - return os; -} - -static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags::ApiList apiList) { - return static_cast(policy) == static_cast(apiList); -} - inline Action GetMemberAction(uint32_t access_flags) { EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); if (policy == EnforcementPolicy::kNoChecks) { @@ -89,16 +70,7 @@ inline Action GetMemberAction(uint32_t access_flags) { return kAllow; } // The logic below relies on equality of values in the enums EnforcementPolicy and - // HiddenApiAccessFlags::ApiList, and their ordering. Assert that this is as expected. - static_assert( - EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) && - EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) && - EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist), - "Mismatch between EnforcementPolicy and ApiList enums"); - static_assert( - EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList && - EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly, - "EnforcementPolicy values ordering not correct"); + // HiddenApiAccessFlags::ApiList, and their ordering. Assertions are in hidden_api.cc. if (static_cast(policy) > static_cast(api_list)) { return api_list == HiddenApiAccessFlags::kDarkGreylist ? kAllowButWarnAndToast @@ -108,6 +80,8 @@ inline Action GetMemberAction(uint32_t access_flags) { } } +// Implementation details. DO NOT ACCESS DIRECTLY. +namespace detail { // Class to encapsulate the signature of a member (ArtField or ArtMethod). This // is used as a helper when matching prefixes, and when logging the signature. @@ -118,68 +92,44 @@ class MemberSignature { std::string tmp_; public: - explicit MemberSignature(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) { - member_type_ = "field"; - signature_parts_ = { - field->GetDeclaringClass()->GetDescriptor(&tmp_), - "->", - field->GetName(), - ":", - field->GetTypeDescriptor() - }; - } + explicit MemberSignature(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_); + explicit MemberSignature(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); - explicit MemberSignature(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { - member_type_ = "method"; - signature_parts_ = { - method->GetDeclaringClass()->GetDescriptor(&tmp_), - "->", - method->GetName(), - method->GetSignature().ToString() - }; - } + void Dump(std::ostream& os) const; - const std::vector& Parts() const { - return signature_parts_; - } - - void Dump(std::ostream& os) const { - for (std::string part : signature_parts_) { - os << part; - } - } // Performs prefix match on this member. Since the full member signature is // composed of several parts, we match each part in turn (rather than // building the entire thing in memory and performing a simple prefix match) - bool DoesPrefixMatch(const std::string& prefix) const { - size_t pos = 0; - for (const std::string& part : signature_parts_) { - size_t count = std::min(prefix.length() - pos, part.length()); - if (prefix.compare(pos, count, part, 0, count) == 0) { - pos += count; - } else { - return false; - } - } - // We have a complete match if all parts match (we exit the loop without - // returning) AND we've matched the whole prefix. - return pos == prefix.length(); - } + bool DoesPrefixMatch(const std::string& prefix) const; + + bool IsExempted(const std::vector& exemptions); + + void WarnAboutAccess(AccessMethod access_method, HiddenApiAccessFlags::ApiList list); +}; + +template +bool ShouldBlockAccessToMemberImpl(T* member, + Action action, + AccessMethod access_method) + REQUIRES_SHARED(Locks::mutator_lock_); - bool IsExempted(const std::vector& exemptions) { - for (const std::string& exemption : exemptions) { - if (DoesPrefixMatch(exemption)) { - return true; - } - } +// Returns true if the caller is either loaded by the boot strap class loader or comes from +// a dex file located in ${ANDROID_ROOT}/framework/. +ALWAYS_INLINE +inline bool IsCallerInPlatformDex(ObjPtr caller_class_loader, + ObjPtr caller_dex_cache) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (caller_class_loader.IsNull()) { + return true; + } else if (caller_dex_cache.IsNull()) { return false; + } else { + const DexFile* caller_dex_file = caller_dex_cache->GetDexFile(); + return caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile(); } +} - void WarnAboutAccess(AccessMethod access_method, HiddenApiAccessFlags::ApiList list) { - LOG(WARNING) << "Accessing hidden " << member_type_ << " " << Dumpable(*this) - << " (" << list << ", " << access_method << ")"; - } -}; +} // namespace detail // Returns true if access to `member` should be denied to the caller of the // reflective query. The decision is based on whether the caller is in the @@ -209,72 +159,13 @@ inline bool ShouldBlockAccessToMember(T* member, } // Member is hidden and caller is not in the platform. - - // Get the signature, we need it later. - MemberSignature member_signature(member); - - Runtime* runtime = Runtime::Current(); - - if (action == kDeny) { - // If we were about to deny, check for an exemption first. - // Exempted APIs are treated as light grey list. - if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) { - action = kAllowButWarn; - // Avoid re-examining the exemption list next time. - // Note this results in the warning below showing "light greylist", which - // seems like what one would expect. Exemptions effectively add new members to - // the light greylist. - member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( - member->GetAccessFlags(), HiddenApiAccessFlags::kLightGreylist)); - } - } - - // Print a log message with information about this class member access. - // We do this regardless of whether we block the access or not. - member_signature.WarnAboutAccess(access_method, - HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags())); - - if (action == kDeny) { - // Block access - return true; - } - - // Allow access to this member but print a warning. - DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast); - - // Depending on a runtime flag, we might move the member into whitelist and - // skip the warning the next time the member is accessed. - if (runtime->ShouldDedupeHiddenApiWarnings()) { - member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( - member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist)); - } - - // If this action requires a UI warning, set the appropriate flag. - if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) { - runtime->SetPendingHiddenApiWarning(true); - } - - return false; -} - -// Returns true if the caller is either loaded by the boot strap class loader or comes from -// a dex file located in ${ANDROID_ROOT}/framework/. -inline bool IsCallerInPlatformDex(ObjPtr caller_class_loader, - ObjPtr caller_dex_cache) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (caller_class_loader.IsNull()) { - return true; - } else if (caller_dex_cache.IsNull()) { - return false; - } else { - const DexFile* caller_dex_file = caller_dex_cache->GetDexFile(); - return caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile(); - } + return detail::ShouldBlockAccessToMemberImpl(member, action, access_method); } inline bool IsCallerInPlatformDex(ObjPtr caller) REQUIRES_SHARED(Locks::mutator_lock_) { - return !caller.IsNull() && IsCallerInPlatformDex(caller->GetClassLoader(), caller->GetDexCache()); + return !caller.IsNull() && + detail::IsCallerInPlatformDex(caller->GetClassLoader(), caller->GetDexCache()); } // Returns true if access to `member` should be denied to a caller loaded with @@ -286,7 +177,7 @@ inline bool ShouldBlockAccessToMember(T* member, ObjPtr caller_dex_cache, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { - bool caller_in_platform = IsCallerInPlatformDex(caller_class_loader, caller_dex_cache); + bool caller_in_platform = detail::IsCallerInPlatformDex(caller_class_loader, caller_dex_cache); return ShouldBlockAccessToMember(member, /* thread */ nullptr, [caller_in_platform] (Thread*) { return caller_in_platform; }, diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc index 190d2cb7a7..5a31dd4972 100644 --- a/runtime/hidden_api_test.cc +++ b/runtime/hidden_api_test.cc @@ -21,6 +21,8 @@ namespace art { +using hiddenapi::detail::MemberSignature; + class HiddenApiTest : public CommonRuntimeTest { protected: void SetUp() OVERRIDE { @@ -102,172 +104,172 @@ TEST_F(HiddenApiTest, CheckMembersRead) { TEST_F(HiddenApiTest, CheckEverythingMatchesL) { ScopedObjectAccess soa(self_); std::string prefix("L"); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class2_field1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class2_method1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class3_field1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class3_method1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class3_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class2_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class2_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class3_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class3_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class3_method1_i_).DoesPrefixMatch(prefix)); } TEST_F(HiddenApiTest, CheckPackageMatch) { ScopedObjectAccess soa(self_); std::string prefix("Lmypackage/packagea/"); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class2_field1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class2_method1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class3_field1_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class3_method1_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class3_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class2_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class2_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class3_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class3_method1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class3_method1_i_).DoesPrefixMatch(prefix)); } TEST_F(HiddenApiTest, CheckClassMatch) { ScopedObjectAccess soa(self_); std::string prefix("Lmypackage/packagea/Class1"); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class2_field1_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class2_method1_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class2_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class2_method1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix)); } TEST_F(HiddenApiTest, CheckClassExactMatch) { ScopedObjectAccess soa(self_); std::string prefix("Lmypackage/packagea/Class1;"); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class2_field1_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class2_method1_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class2_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class2_method1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix)); } TEST_F(HiddenApiTest, CheckMethodMatch) { ScopedObjectAccess soa(self_); std::string prefix("Lmypackage/packagea/Class1;->method1"); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_init_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix)); } TEST_F(HiddenApiTest, CheckMethodExactMatch) { ScopedObjectAccess soa(self_); std::string prefix("Lmypackage/packagea/Class1;->method1("); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_init_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); } TEST_F(HiddenApiTest, CheckMethodSignatureMatch) { ScopedObjectAccess soa(self_); std::string prefix("Lmypackage/packagea/Class1;->method1(I)"); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); } TEST_F(HiddenApiTest, CheckMethodSignatureAndReturnMatch) { ScopedObjectAccess soa(self_); std::string prefix("Lmypackage/packagea/Class1;->method1()V"); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); } TEST_F(HiddenApiTest, CheckFieldMatch) { ScopedObjectAccess soa(self_); std::string prefix("Lmypackage/packagea/Class1;->field1"); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix)); } TEST_F(HiddenApiTest, CheckFieldExactMatch) { ScopedObjectAccess soa(self_); std::string prefix("Lmypackage/packagea/Class1;->field1:"); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); } TEST_F(HiddenApiTest, CheckFieldTypeMatch) { ScopedObjectAccess soa(self_); std::string prefix("Lmypackage/packagea/Class1;->field1:I"); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); } TEST_F(HiddenApiTest, CheckConstructorMatch) { ScopedObjectAccess soa(self_); std::string prefix("Lmypackage/packagea/Class1;->"); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); } TEST_F(HiddenApiTest, CheckConstructorExactMatch) { ScopedObjectAccess soa(self_); std::string prefix("Lmypackage/packagea/Class1;->()V"); - ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix)); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); } TEST_F(HiddenApiTest, CheckMethodSignatureTrailingCharsNoMatch) { ScopedObjectAccess soa(self_); std::string prefix("Lmypackage/packagea/Class1;->method1()Vfoo"); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix)); } TEST_F(HiddenApiTest, CheckConstructorTrailingCharsNoMatch) { ScopedObjectAccess soa(self_); std::string prefix("Lmypackage/packagea/Class1;->()Vfoo"); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_init_).DoesPrefixMatch(prefix)); } TEST_F(HiddenApiTest, CheckFieldTrailingCharsNoMatch) { ScopedObjectAccess soa(self_); std::string prefix("Lmypackage/packagea/Class1;->field1:Ifoo"); - ASSERT_FALSE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); + ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); } } // namespace art diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index f0898f49d3..72b31790f0 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -31,7 +31,6 @@ #include "dex/invoke_type.h" #include "dex_cache.h" #include "gc/heap-inl.h" -#include "hidden_api.h" #include "iftable.h" #include "subtype_check.h" #include "object-inl.h" diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index d9a5096331..cf0a72a477 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -27,6 +27,7 @@ #include "base/mutex.h" #include "base/runtime_debug.h" #include "debugger.h" +#include "hidden_api.h" #include "java_vm_ext.h" #include "jit/jit.h" #include "jni_internal.h" diff --git a/runtime/runtime.cc b/runtime/runtime.cc index bb76e61122..00ccc19b5d 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -86,6 +86,7 @@ #include "gc/space/space-inl.h" #include "gc/system_weak.h" #include "handle_scope-inl.h" +#include "hidden_api.h" #include "image-inl.h" #include "instrumentation.h" #include "intern_table.h" -- GitLab From 4f3d1cfbee45a27d5997c379f4eb7b7108224ca8 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 29 Mar 2018 08:13:20 +0000 Subject: [PATCH 167/749] Revert "Ensure that OSR still is possible with jvmti" Bug: 76226464 Test doesn't work. This reverts commit b9ad26d1ed9146b89555d4333021f44eeb831f05. Change-Id: I3bdb60981e0e37457686cf9aeefb0f1103d485a0 --- openjdkjvmti/deopt_manager.cc | 74 ++++++++----------- openjdkjvmti/deopt_manager.h | 20 +---- openjdkjvmti/ti_method.cc | 3 - runtime/instrumentation.cc | 8 +- .../expected.txt | 2 + .../src/Main.java | 19 ++--- 6 files changed, 41 insertions(+), 85 deletions(-) diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc index 380d95c545..6d84ffa53f 100644 --- a/openjdkjvmti/deopt_manager.cc +++ b/openjdkjvmti/deopt_manager.cc @@ -53,18 +53,14 @@ namespace openjdkjvmti { // TODO We should make this much more selective in the future so we only return true when we -// actually care about the method at this time (ie active frames had locals changed). For now we -// just assume that if anything has changed any frame's locals we care about all methods. If nothing -// has we only care about methods with active breakpoints on them. In the future we should probably -// rewrite all of this to instead do this at the ShadowFrame or thread granularity. -bool JvmtiMethodInspectionCallback::IsMethodBeingInspected(art::ArtMethod* method) { - // Non-java-debuggable runtimes we need to assume that any method might not be debuggable and - // therefore potentially being inspected (due to inlines). If we are debuggable we rely hard on - // inlining not being done since we don't keep track of which methods get inlined where and simply - // look to see if the method is breakpointed. - return !art::Runtime::Current()->IsJavaDebuggable() || - manager_->HaveLocalsChanged() || - manager_->MethodHasBreakpoints(method); +// actually care about the method (i.e. had locals changed, have breakpoints, etc.). For now though +// we can just assume that we care we are loaded at all. +// +// Even if we don't keep track of this at the method level we might want to keep track of it at the +// level of enabled capabilities. +bool JvmtiMethodInspectionCallback::IsMethodBeingInspected( + art::ArtMethod* method ATTRIBUTE_UNUSED) { + return true; } bool JvmtiMethodInspectionCallback::IsMethodSafeToJit(art::ArtMethod* method) { @@ -79,10 +75,7 @@ DeoptManager::DeoptManager() performing_deoptimization_(false), global_deopt_count_(0), deopter_count_(0), - breakpoint_status_lock_("JVMTI_BreakpointStatusLock", - static_cast(art::LockLevel::kAbortLock + 1)), - inspection_callback_(this), - set_local_variable_called_(false) { } + inspection_callback_(this) { } void DeoptManager::Setup() { art::ScopedThreadStateChange stsc(art::Thread::Current(), @@ -128,11 +121,14 @@ void DeoptManager::FinishSetup() { } bool DeoptManager::MethodHasBreakpoints(art::ArtMethod* method) { - art::MutexLock lk(art::Thread::Current(), breakpoint_status_lock_); + art::MutexLock lk(art::Thread::Current(), deoptimization_status_lock_); return MethodHasBreakpointsLocked(method); } bool DeoptManager::MethodHasBreakpointsLocked(art::ArtMethod* method) { + if (deopter_count_ == 0) { + return false; + } auto elem = breakpoint_status_.find(method); return elem != breakpoint_status_.end() && elem->second != 0; } @@ -162,23 +158,18 @@ void DeoptManager::AddMethodBreakpoint(art::ArtMethod* method) { art::ScopedThreadSuspension sts(self, art::kSuspended); deoptimization_status_lock_.ExclusiveLock(self); - { - breakpoint_status_lock_.ExclusiveLock(self); - - DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; - - if (MethodHasBreakpointsLocked(method)) { - // Don't need to do anything extra. - breakpoint_status_[method]++; - // Another thread might be deoptimizing the very method we just added new breakpoints for. - // Wait for any deopts to finish before moving on. - breakpoint_status_lock_.ExclusiveUnlock(self); - WaitForDeoptimizationToFinish(self); - return; - } - breakpoint_status_[method] = 1; - breakpoint_status_lock_.ExclusiveUnlock(self); + + DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; + + if (MethodHasBreakpointsLocked(method)) { + // Don't need to do anything extra. + breakpoint_status_[method]++; + // Another thread might be deoptimizing the very method we just added new breakpoints for. Wait + // for any deopts to finish before moving on. + WaitForDeoptimizationToFinish(self); + return; } + breakpoint_status_[method] = 1; auto instrumentation = art::Runtime::Current()->GetInstrumentation(); if (instrumentation->IsForcedInterpretOnly()) { // We are already interpreting everything so no need to do anything. @@ -205,22 +196,17 @@ void DeoptManager::RemoveMethodBreakpoint(art::ArtMethod* method) { // need but since that is very heavy we will instead just use a condition variable to make sure we // don't race with ourselves. deoptimization_status_lock_.ExclusiveLock(self); - bool is_last_breakpoint; - { - art::MutexLock mu(self, breakpoint_status_lock_); - - DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; - DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without " - << "breakpoints present!"; - breakpoint_status_[method] -= 1; - is_last_breakpoint = (breakpoint_status_[method] == 0); - } + + DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; + DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without " + << "breakpoints present!"; auto instrumentation = art::Runtime::Current()->GetInstrumentation(); + breakpoint_status_[method] -= 1; if (UNLIKELY(instrumentation->IsForcedInterpretOnly())) { // We don't need to do anything since we are interpreting everything anyway. deoptimization_status_lock_.ExclusiveUnlock(self); return; - } else if (is_last_breakpoint) { + } else if (breakpoint_status_[method] == 0) { if (UNLIKELY(is_default)) { RemoveDeoptimizeAllMethodsLocked(self); } else { diff --git a/openjdkjvmti/deopt_manager.h b/openjdkjvmti/deopt_manager.h index a38690c49e..a495b6835c 100644 --- a/openjdkjvmti/deopt_manager.h +++ b/openjdkjvmti/deopt_manager.h @@ -32,7 +32,6 @@ #ifndef ART_OPENJDKJVMTI_DEOPT_MANAGER_H_ #define ART_OPENJDKJVMTI_DEOPT_MANAGER_H_ -#include #include #include "jni.h" @@ -108,17 +107,9 @@ class DeoptManager { static DeoptManager* Get(); - bool HaveLocalsChanged() const { - return set_local_variable_called_.load(); - } - - void SetLocalsUpdated() { - set_local_variable_called_.store(true); - } - private: bool MethodHasBreakpointsLocked(art::ArtMethod* method) - REQUIRES(breakpoint_status_lock_); + REQUIRES(deoptimization_status_lock_); // Wait until nothing is currently in the middle of deoptimizing/undeoptimizing something. This is // needed to ensure that everything is synchronized since threads need to drop the @@ -165,20 +156,13 @@ class DeoptManager { // Number of users of deoptimization there currently are. uint32_t deopter_count_ GUARDED_BY(deoptimization_status_lock_); - // A mutex that just protects the breakpoint-status map. This mutex should always be at the - // bottom of the lock hierarchy. Nothing more should be locked if we hold this. - art::Mutex breakpoint_status_lock_ ACQUIRED_BEFORE(art::Locks::abort_lock_); // A map from methods to the number of breakpoints in them from all envs. std::unordered_map breakpoint_status_ - GUARDED_BY(breakpoint_status_lock_); + GUARDED_BY(deoptimization_status_lock_); // The MethodInspectionCallback we use to tell the runtime if we care about particular methods. JvmtiMethodInspectionCallback inspection_callback_; - // Set to true if anything calls SetLocalVariables on any thread since we need to be careful about - // OSR after this. - std::atomic set_local_variable_called_; - // Helper for setting up/tearing-down for deoptimization. friend class ScopedDeoptimizationContext; }; diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index b83310dc85..bf2e6cd104 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -915,9 +915,6 @@ jvmtiError MethodUtil::SetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED, if (depth < 0) { return ERR(ILLEGAL_ARGUMENT); } - // Make sure that we know not to do any OSR anymore. - // TODO We should really keep track of this at the Frame granularity. - DeoptManager::Get()->SetLocalsUpdated(); art::Thread* self = art::Thread::Current(); // Suspend JIT since it can get confused if we deoptimize methods getting jitted. art::jit::ScopedJitSuspend suspend_jit; diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index ec3e10e2f8..84a148f21c 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -139,14 +139,10 @@ static void UpdateEntrypoints(ArtMethod* method, const void* quick_code) bool Instrumentation::NeedDebugVersionFor(ArtMethod* method) const REQUIRES_SHARED(Locks::mutator_lock_) { - art::Runtime* runtime = Runtime::Current(); - return runtime->IsJavaDebuggable() && + return Runtime::Current()->IsJavaDebuggable() && !method->IsNative() && !method->IsProxyMethod() && - // If we don't have a jit this can push us to the pre-compiled version of methods which is - // not something we want since we are debuggable. - (UNLIKELY(runtime->GetJit() == nullptr) || - runtime->GetRuntimeCallbacks()->IsMethodBeingInspected(method)); + Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method); } void Instrumentation::InstallStubsForMethod(ArtMethod* method) { diff --git a/test/1935-get-set-current-frame-jit/expected.txt b/test/1935-get-set-current-frame-jit/expected.txt index a685891775..cdb8f6a825 100644 --- a/test/1935-get-set-current-frame-jit/expected.txt +++ b/test/1935-get-set-current-frame-jit/expected.txt @@ -1,5 +1,7 @@ JNI_OnLoad called From GetLocalInt(), value is 42 +isInOsrCode? false Value is '42' Setting TARGET to 1337 +isInOsrCode? false Value is '1337' diff --git a/test/1935-get-set-current-frame-jit/src/Main.java b/test/1935-get-set-current-frame-jit/src/Main.java index aad4cd7135..714a98aaf3 100644 --- a/test/1935-get-set-current-frame-jit/src/Main.java +++ b/test/1935-get-set-current-frame-jit/src/Main.java @@ -49,11 +49,9 @@ public class Main { public static class IntRunner implements Runnable { private volatile boolean continueBusyLoop; private volatile boolean inBusyLoop; - private final boolean expectOsr; - public IntRunner(boolean expectOsr) { + public IntRunner() { this.continueBusyLoop = true; this.inBusyLoop = false; - this.expectOsr = expectOsr; } public void run() { int TARGET = 42; @@ -66,16 +64,9 @@ public class Main { Main.ensureJitCompiled(IntRunner.class, "run"); i++; } - // We shouldn't be doing OSR since we are using JVMTI and the set prevents OSR. + // We shouldn't be doing OSR since we are using JVMTI and the get/set prevents OSR. // Set local will also push us to interpreter but the get local may remain in compiled code. - if (hasJit()) { - boolean inOsr = Main.isInOsrCode("run"); - if (expectOsr && !inOsr) { - throw new Error("Expected to be in OSR but was not."); - } else if (!expectOsr && inOsr) { - throw new Error("Expected not to be in OSR but was."); - } - } + System.out.println("isInOsrCode? " + (hasJit() && Main.isInOsrCode("run"))); reportValue(TARGET); } public void waitForBusyLoopStart() { while (!inBusyLoop) {} } @@ -87,7 +78,7 @@ public class Main { public static void runGet() throws Exception { Method target = IntRunner.class.getDeclaredMethod("run"); // Get Int - IntRunner int_runner = new IntRunner(true); + IntRunner int_runner = new IntRunner(); Thread target_get = new Thread(int_runner, "GetLocalInt - Target"); target_get.start(); int_runner.waitForBusyLoopStart(); @@ -117,7 +108,7 @@ public class Main { public static void runSet() throws Exception { Method target = IntRunner.class.getDeclaredMethod("run"); // Set Int - IntRunner int_runner = new IntRunner(false); + IntRunner int_runner = new IntRunner(); Thread target_set = new Thread(int_runner, "SetLocalInt - Target"); target_set.start(); int_runner.waitForBusyLoopStart(); -- GitLab From 8068bc3bc68e9560cc4650c6fb261ec9b8648fbd Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 29 Mar 2018 10:27:56 +0100 Subject: [PATCH 168/749] Force JIT compilation in 680-checker-deopt-dex-pc-0. Do not rely on reaching the JIT threshold as this can take a long time and time out on heavily loaded hosts. Test: testrunner.py --host -t 680-checker-deopt-dex-pc-0 \ --runtime-option:-Xjitthreshold:0 Bug: 62611253 Change-Id: Ib180b61744e832779e577c86a39b476cb3856198 --- test/680-checker-deopt-dex-pc-0/src/Main.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/test/680-checker-deopt-dex-pc-0/src/Main.java b/test/680-checker-deopt-dex-pc-0/src/Main.java index d5a6a9015c..64a3cb3da7 100644 --- a/test/680-checker-deopt-dex-pc-0/src/Main.java +++ b/test/680-checker-deopt-dex-pc-0/src/Main.java @@ -31,15 +31,12 @@ public class Main { System.loadLibrary(args[0]); if (hasJit()) { byte[] array = { 0, 1, 2, 3 }; - while (!hasJitCompiledEntrypoint(Main.class, "$noinline$getInt")) { - for (int i = 0; i < 10000; ++i) { - if ($noinline$getInt(array, 0) != 0x03020100) { - throw new Error(); - } - } - try { - Thread.sleep(200); - } catch (InterruptedException ignored) {} + ensureJitCompiled(Main.class, "$noinline$getInt"); + if (!hasJitCompiledEntrypoint(Main.class, "$noinline$getInt")) { + throw new Error("Unexpected entrypoint!"); + } + if ($noinline$getInt(array, 0) != 0x03020100) { + throw new Error(); } try { // The HDeoptimize at dex pc 0 was previously handled poorly as the dex pc 0 @@ -56,4 +53,5 @@ public class Main { public static native boolean hasJit(); public native static boolean hasJitCompiledEntrypoint(Class cls, String methodName); + public native static void ensureJitCompiled(Class cls, String methodName); } -- GitLab From 27b967611b48120bf0140995ae439700fe6cc139 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Tue, 13 Mar 2018 16:06:57 +0000 Subject: [PATCH 169/749] ART: Simplify quasi_atomic.h Removes fences that duplicate std::atomic_thread_fence(). Bug: 71621075 Test: art/test.py --host -j32 Test: art/test.py --target --64 -j4 Change-Id: I008de4d242d1a3cf4d3f50ce171abbbda647bdaa --- runtime/base/quasi_atomic.h | 12 ------------ runtime/class_linker.cc | 2 +- runtime/gc/collector/concurrent_copying.cc | 4 ++-- runtime/interpreter/unstarted_runtime.cc | 2 +- runtime/jit/jit_code_cache.cc | 2 +- runtime/monitor.cc | 2 +- runtime/native/sun_misc_Unsafe.cc | 6 +++--- 7 files changed, 9 insertions(+), 21 deletions(-) diff --git a/runtime/base/quasi_atomic.h b/runtime/base/quasi_atomic.h index 067d01db01..0012f6482b 100644 --- a/runtime/base/quasi_atomic.h +++ b/runtime/base/quasi_atomic.h @@ -152,14 +152,6 @@ class QuasiAtomic { return NeedSwapMutexes(isa); } - static void ThreadFenceAcquire() { - std::atomic_thread_fence(std::memory_order_acquire); - } - - static void ThreadFenceRelease() { - std::atomic_thread_fence(std::memory_order_release); - } - static void ThreadFenceForConstructor() { #if defined(__aarch64__) __asm__ __volatile__("dmb ishst" : : : "memory"); @@ -168,10 +160,6 @@ class QuasiAtomic { #endif } - static void ThreadFenceSequentiallyConsistent() { - std::atomic_thread_fence(std::memory_order_seq_cst); - } - private: static Mutex* GetSwapMutex(const volatile int64_t* addr); static int64_t SwapMutexRead64(volatile const int64_t* addr); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 412834c366..3025818ab7 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -6179,7 +6179,7 @@ ArtMethod* ClassLinker::AddMethodToConflictTable(ObjPtr klass, // Note that there is a race in the presence of multiple threads and we may leak // memory from the LinearAlloc, but that's a tradeoff compared to using // atomic operations. - QuasiAtomic::ThreadFenceRelease(); + std::atomic_thread_fence(std::memory_order_release); new_conflict_method->SetImtConflictTable(new_table, image_pointer_size_); return new_conflict_method; } diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index bb5167f15d..e1a9c08b0a 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -2471,7 +2471,7 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, // Do a fence to prevent the field CAS in ConcurrentCopying::Process from possibly reordering // before the object copy. - QuasiAtomic::ThreadFenceRelease(); + std::atomic_thread_fence(std::memory_order_release); LockWord new_lock_word = LockWord::FromForwardingAddress(reinterpret_cast(to_ref)); @@ -2566,7 +2566,7 @@ mirror::Object* ConcurrentCopying::IsMarked(mirror::Object* from_ref) { bool ConcurrentCopying::IsOnAllocStack(mirror::Object* ref) { // TODO: Explain why this is here. What release operation does it pair with? - QuasiAtomic::ThreadFenceAcquire(); + std::atomic_thread_fence(std::memory_order_acquire); accounting::ObjectStack* alloc_stack = GetAllocationStack(); return alloc_stack->Contains(ref); } diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 4a2dd3bc3f..4c7a97dfa8 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -1538,7 +1538,7 @@ void UnstartedRuntime::UnstartedUnsafePutOrderedObject( } int64_t offset = shadow_frame->GetVRegLong(arg_offset + 2); mirror::Object* newValue = shadow_frame->GetVRegReference(arg_offset + 4); - QuasiAtomic::ThreadFenceRelease(); + std::atomic_thread_fence(std::memory_order_release); if (Runtime::Current()->IsActiveTransaction()) { obj->SetFieldObject(MemberOffset(offset), newValue); } else { diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 5618b6ebf9..de64fdd2c9 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -1539,7 +1539,7 @@ ProfilingInfo* JitCodeCache::AddProfilingInfoInternal(Thread* self ATTRIBUTE_UNU // Make sure other threads see the data in the profiling info object before the // store in the ArtMethod's ProfilingInfo pointer. - QuasiAtomic::ThreadFenceRelease(); + std::atomic_thread_fence(std::memory_order_release); method->SetProfilingInfo(info); profiling_infos_.push_back(info); diff --git a/runtime/monitor.cc b/runtime/monitor.cc index e110763300..f246d8b1c0 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -1101,7 +1101,7 @@ mirror::Object* Monitor::MonitorEnter(Thread* self, mirror::Object* obj, bool tr case LockWord::kFatLocked: { // We should have done an acquire read of the lockword initially, to ensure // visibility of the monitor data structure. Use an explicit fence instead. - QuasiAtomic::ThreadFenceAcquire(); + std::atomic_thread_fence(std::memory_order_acquire); Monitor* mon = lock_word.FatLockMonitor(); if (trylock) { return mon->TryLock(self) ? h_obj.Get() : nullptr; diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc index 25f984f6be..fb00ae3967 100644 --- a/runtime/native/sun_misc_Unsafe.cc +++ b/runtime/native/sun_misc_Unsafe.cc @@ -116,7 +116,7 @@ static void Unsafe_putOrderedInt(JNIEnv* env, jobject, jobject javaObj, jlong of ScopedFastNativeObjectAccess soa(env); ObjPtr obj = soa.Decode(javaObj); // TODO: A release store is likely to be faster on future processors. - QuasiAtomic::ThreadFenceRelease(); + std::atomic_thread_fence(std::memory_order_release); // JNI must use non transactional mode. obj->SetField32(MemberOffset(offset), newValue); } @@ -152,7 +152,7 @@ static void Unsafe_putOrderedLong(JNIEnv* env, jobject, jobject javaObj, jlong o jlong newValue) { ScopedFastNativeObjectAccess soa(env); ObjPtr obj = soa.Decode(javaObj); - QuasiAtomic::ThreadFenceRelease(); + std::atomic_thread_fence(std::memory_order_release); // JNI must use non transactional mode. obj->SetField64(MemberOffset(offset), newValue); } @@ -194,7 +194,7 @@ static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong ScopedFastNativeObjectAccess soa(env); ObjPtr obj = soa.Decode(javaObj); ObjPtr newValue = soa.Decode(javaNewValue); - QuasiAtomic::ThreadFenceRelease(); + std::atomic_thread_fence(std::memory_order_release); // JNI must use non transactional mode. obj->SetFieldObject(MemberOffset(offset), newValue); } -- GitLab From a83a89c7e7fc6df457a0168d6ac43cc7a7a8eda9 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Wed, 21 Feb 2018 14:20:24 +0000 Subject: [PATCH 170/749] Implement cyclic region allocation in ART's region space. This region allocation strategy tries to allocate new regions instead of reusing previously freed regions. The intent is to minimize region reuse to try to catch some GC bugs earlier. This region allocation behavior is only turned on in debug mode. Test: art/test.py Test: Device boot test with libartd Bug: 74064045 Change-Id: I76442bc65f9fa8cc2f5f67e6584f7fd73869d98e --- runtime/gc/space/region_space-inl.h | 81 ++++++++++++++++++++++++++--- runtime/gc/space/region_space.cc | 21 +++++++- runtime/gc/space/region_space.h | 29 +++++++++++ 3 files changed, 123 insertions(+), 8 deletions(-) diff --git a/runtime/gc/space/region_space-inl.h b/runtime/gc/space/region_space-inl.h index 7072a7e4cc..c6ec174a13 100644 --- a/runtime/gc/space/region_space-inl.h +++ b/runtime/gc/space/region_space-inl.h @@ -258,16 +258,79 @@ inline mirror::Object* RegionSpace::AllocLarge(size_t num_bytes, return nullptr; } } + // Find a large enough set of contiguous free regions. - size_t left = 0; - while (left + num_regs - 1 < num_regions_) { + if (kCyclicRegionAllocation) { + // Try to find a range of free regions within [cyclic_alloc_region_index_, num_regions_). + size_t next_region1 = -1; + mirror::Object* region1 = AllocLargeInRange(cyclic_alloc_region_index_, + num_regions_, + num_regs, + bytes_allocated, + usable_size, + bytes_tl_bulk_allocated, + &next_region1); + if (region1 != nullptr) { + DCHECK_LT(0u, next_region1); + DCHECK_LE(next_region1, num_regions_); + // Move the cyclic allocation region marker to the region + // following the large region that was just allocated. + cyclic_alloc_region_index_ = next_region1 % num_regions_; + return region1; + } + + // If the previous attempt failed, try to find a range of free regions within + // [0, cyclic_alloc_region_index_ + num_regions_ - 1). + size_t next_region2 = -1; + mirror::Object* region2 = + AllocLargeInRange(0, + cyclic_alloc_region_index_ + num_regions_ - 1, + num_regs, + bytes_allocated, + usable_size, + bytes_tl_bulk_allocated, + &next_region2); + if (region2 != nullptr) { + DCHECK_LT(0u, next_region2); + DCHECK_LE(next_region2, num_regions_); + // Move the cyclic allocation region marker to the region + // following the large region that was just allocated. + cyclic_alloc_region_index_ = next_region2 % num_regions_; + return region2; + } + } else { + // Try to find a range of free regions within [0, num_regions_). + mirror::Object* region = AllocLargeInRange(0, + num_regions_, + num_regs, + bytes_allocated, + usable_size, + bytes_tl_bulk_allocated); + if (region != nullptr) { + return region; + } + } + return nullptr; +} + +template +inline mirror::Object* RegionSpace::AllocLargeInRange(size_t begin, + size_t end, + size_t num_regs, + /* out */ size_t* bytes_allocated, + /* out */ size_t* usable_size, + /* out */ size_t* bytes_tl_bulk_allocated, + /* out */ size_t* next_region) { + size_t left = begin; + while (left + num_regs - 1 < end) { bool found = true; size_t right = left; - DCHECK_LT(right, left + num_regs) - << "The inner loop Should iterate at least once"; + DCHECK_LT(right, left + num_regs) << "The inner loop should iterate at least once"; while (right < left + num_regs) { if (regions_[right].IsFree()) { ++right; + // Ensure `right` is not going beyond the past-the-end index of the region space. + DCHECK_LE(right, num_regions_); } else { found = false; break; @@ -303,9 +366,15 @@ inline mirror::Object* RegionSpace::AllocLarge(size_t num_bytes, *usable_size = allocated; } *bytes_tl_bulk_allocated = allocated; - return reinterpret_cast(first_reg->Begin()); + mirror::Object* large_region = reinterpret_cast(first_reg->Begin()); + DCHECK(large_region != nullptr); + if (next_region != nullptr) { + // Return the index to the region next to the allocated large region via `next_region`. + *next_region = right; + } + return large_region; } else { - // right points to the non-free region. Start with the one after it. + // `right` points to the non-free region. Start with the one after it. left = right + 1; } } diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc index 5ea434a318..6a01c88c5d 100644 --- a/runtime/gc/space/region_space.cc +++ b/runtime/gc/space/region_space.cc @@ -92,7 +92,8 @@ RegionSpace::RegionSpace(const std::string& name, MemMap* mem_map) max_peak_num_non_free_regions_(0U), non_free_region_index_limit_(0U), current_region_(&full_region_), - evac_region_(nullptr) { + evac_region_(nullptr), + cyclic_alloc_region_index_(0U) { CHECK_ALIGNED(mem_map->Size(), kRegionSize); CHECK_ALIGNED(mem_map->Begin(), kRegionSize); DCHECK_GT(num_regions_, 0U); @@ -437,6 +438,7 @@ void RegionSpace::Clear() { r->Clear(/*zero_and_release_pages*/true); } SetNonFreeRegionLimit(0); + DCHECK_EQ(num_non_free_regions_, 0u); current_region_ = &full_region_; evac_region_ = &full_region_; } @@ -450,6 +452,9 @@ void RegionSpace::ClampGrowthLimit(size_t new_capacity) { return; } num_regions_ = new_num_regions; + if (kCyclicRegionAllocation && cyclic_alloc_region_index_ >= num_regions_) { + cyclic_alloc_region_index_ = 0u; + } SetLimit(Begin() + new_capacity); if (Size() > new_capacity) { SetEnd(Limit()); @@ -608,7 +613,14 @@ RegionSpace::Region* RegionSpace::AllocateRegion(bool for_evac) { return nullptr; } for (size_t i = 0; i < num_regions_; ++i) { - Region* r = ®ions_[i]; + // When using the cyclic region allocation strategy, try to + // allocate a region starting from the last cyclic allocated + // region marker. Otherwise, try to allocate a region starting + // from the beginning of the region space. + size_t region_index = kCyclicRegionAllocation + ? ((cyclic_alloc_region_index_ + i) % num_regions_) + : i; + Region* r = ®ions_[region_index]; if (r->IsFree()) { r->Unfree(this, time_); if (for_evac) { @@ -618,6 +630,11 @@ RegionSpace::Region* RegionSpace::AllocateRegion(bool for_evac) { r->SetNewlyAllocated(); ++num_non_free_regions_; } + if (kCyclicRegionAllocation) { + // Move the cyclic allocation region marker to the region + // following the one that was just allocated. + cyclic_alloc_region_index_ = (region_index + 1) % num_regions_; + } return r; } } diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h index 6a1371af10..c7e18885db 100644 --- a/runtime/gc/space/region_space.h +++ b/runtime/gc/space/region_space.h @@ -31,6 +31,13 @@ class ReadBarrierTable; namespace space { +// Cyclic region allocation strategy. If `true`, region allocation +// will not try to allocate a new region from the beginning of the +// region space, but from the last allocated region. This allocation +// strategy reduces region reuse and should help catch some GC bugs +// earlier. +static constexpr bool kCyclicRegionAllocation = kIsDebugBuild; + // A space that consists of equal-sized regions. class RegionSpace FINAL : public ContinuousMemMapAllocSpace { public: @@ -571,6 +578,23 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { Region* AllocateRegion(bool for_evac) REQUIRES(region_lock_); + // Scan region range [`begin`, `end`) in increasing order to try to + // allocate a large region having a size of `num_regs` regions. If + // there is no space in the region space to allocate this large + // region, return null. + // + // If argument `next_region` is not null, use `*next_region` to + // return the index to the region next to the allocated large region + // returned by this method. + template + mirror::Object* AllocLargeInRange(size_t num_regs, + size_t begin, + size_t end, + /* out */ size_t* bytes_allocated, + /* out */ size_t* usable_size, + /* out */ size_t* bytes_tl_bulk_allocated, + /* out */ size_t* next_region = nullptr) REQUIRES(region_lock_); + Mutex region_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; uint32_t time_; // The time as the number of collections since the startup. @@ -600,6 +624,11 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { Region* evac_region_; // The region currently used for evacuation. Region full_region_; // The dummy/sentinel region that looks full. + // Index into the region array pointing to the starting region when + // trying to allocate a new region. Only used when + // `kCyclicRegionAllocation` is true. + size_t cyclic_alloc_region_index_ GUARDED_BY(region_lock_); + // Mark bitmap used by the GC. std::unique_ptr mark_bitmap_; -- GitLab From f2a69313826f793a7ebeb5ed09ba002904495fc9 Mon Sep 17 00:00:00 2001 From: Lokesh Gidra Date: Tue, 27 Mar 2018 18:48:59 -0700 Subject: [PATCH 171/749] Fix double accounting of skipped objects during copying When an object is added to skipped_blocks_map_ due to thread losing the race on installing forwarding pointer during copying, we add the size of the object to num_bytes_allocated_. Later, when an object is allocated out of skipped_blocks_map_, num_bytes_allocated_ is not adjusted accordingly. So, when this object gets allocated (or gets added to skipped_blocks_map_ again due to losing the race), num_bytes_allocated_ goes off. Bug: 74763563 Test: make test-art Change-Id: I197b94b317499281e379733d53ad19794dbe772a --- runtime/gc/collector/concurrent_copying.cc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index bb5167f15d..7d35b29393 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -2357,14 +2357,13 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, size_t non_moving_space_bytes_allocated = 0U; size_t bytes_allocated = 0U; size_t dummy; + bool fall_back_to_non_moving = false; mirror::Object* to_ref = region_space_->AllocNonvirtual( region_space_alloc_size, ®ion_space_bytes_allocated, nullptr, &dummy); bytes_allocated = region_space_bytes_allocated; - if (to_ref != nullptr) { + if (LIKELY(to_ref != nullptr)) { DCHECK_EQ(region_space_alloc_size, region_space_bytes_allocated); - } - bool fall_back_to_non_moving = false; - if (UNLIKELY(to_ref == nullptr)) { + } else { // Failed to allocate in the region space. Try the skipped blocks. to_ref = AllocateInSkippedBlock(region_space_alloc_size); if (to_ref != nullptr) { @@ -2374,6 +2373,9 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, region_space_->RecordAlloc(to_ref); } bytes_allocated = region_space_alloc_size; + heap_->num_bytes_allocated_.fetch_sub(bytes_allocated, std::memory_order_seq_cst); + to_space_bytes_skipped_.fetch_sub(bytes_allocated, std::memory_order_seq_cst); + to_space_objects_skipped_.fetch_sub(1, std::memory_order_seq_cst); } else { // Fall back to the non-moving space. fall_back_to_non_moving = true; @@ -2383,7 +2385,6 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, << " skipped_objects=" << to_space_objects_skipped_.load(std::memory_order_seq_cst); } - fall_back_to_non_moving = true; to_ref = heap_->non_moving_space_->Alloc(Thread::Current(), obj_size, &non_moving_space_bytes_allocated, nullptr, &dummy); if (UNLIKELY(to_ref == nullptr)) { -- GitLab From 11ed0275c48ad9a6301d914d3344a27467fcaf0c Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 28 Mar 2018 18:18:48 +0100 Subject: [PATCH 172/749] [veridex] Detect reflection uses. Add HiddenApiFinder to walk over the code item of app dex files, and find static linking uses and potential reflection uses. bug: 64382372 Test: m Change-Id: I35f0b276703504f2e27a80007d410625ba7c9af3 --- libdexfile/dex/hidden_api_access_flags.h | 1 + tools/veridex/Android.bp | 1 + tools/veridex/hidden_api.cc | 11 - tools/veridex/hidden_api.h | 28 ++- tools/veridex/hidden_api_finder.cc | 266 +++++++++++++++++++++++ tools/veridex/hidden_api_finder.h | 59 +++++ tools/veridex/resolver.cc | 12 +- tools/veridex/resolver.h | 10 +- tools/veridex/veridex.cc | 8 +- 9 files changed, 361 insertions(+), 35 deletions(-) create mode 100644 tools/veridex/hidden_api_finder.cc create mode 100644 tools/veridex/hidden_api_finder.h diff --git a/libdexfile/dex/hidden_api_access_flags.h b/libdexfile/dex/hidden_api_access_flags.h index 441b3c14b5..b62d044c6a 100644 --- a/libdexfile/dex/hidden_api_access_flags.h +++ b/libdexfile/dex/hidden_api_access_flags.h @@ -18,6 +18,7 @@ #define ART_LIBDEXFILE_DEX_HIDDEN_API_ACCESS_FLAGS_H_ #include "base/bit_utils.h" +#include "base/macros.h" #include "dex/modifiers.h" namespace art { diff --git a/tools/veridex/Android.bp b/tools/veridex/Android.bp index a74bf3d7f9..cbf62d9e9c 100644 --- a/tools/veridex/Android.bp +++ b/tools/veridex/Android.bp @@ -17,6 +17,7 @@ cc_binary { host_supported: true, srcs: [ "hidden_api.cc", + "hidden_api_finder.cc", "resolver.cc", "veridex.cc", ], diff --git a/tools/veridex/hidden_api.cc b/tools/veridex/hidden_api.cc index 33e499bfc3..93f921a25f 100644 --- a/tools/veridex/hidden_api.cc +++ b/tools/veridex/hidden_api.cc @@ -44,17 +44,6 @@ std::string HiddenApi::GetApiFieldName(const DexFile& dex_file, uint32_t field_i return ss.str(); } -bool HiddenApi::LogIfIn(const std::string& name, - const std::set& list, - const std::string& log, - const std::string& access_kind) { - if (list.find(name) != list.end()) { - LOG(WARNING) << std::string(log) << " usage found " << name << " (" << access_kind << ")"; - return true; - } - return false; -} - void HiddenApi::FillList(const char* filename, std::set& entries) { if (filename == nullptr) { return; diff --git a/tools/veridex/hidden_api.h b/tools/veridex/hidden_api.h index 282e7cf8e8..5893b8ae33 100644 --- a/tools/veridex/hidden_api.h +++ b/tools/veridex/hidden_api.h @@ -17,6 +17,9 @@ #ifndef ART_TOOLS_VERIDEX_HIDDEN_API_H_ #define ART_TOOLS_VERIDEX_HIDDEN_API_H_ +#include "dex/hidden_api_access_flags.h" + +#include #include #include @@ -35,10 +38,20 @@ class HiddenApi { FillList(blacklist, blacklist_); } - bool LogIfInList(const std::string& name, const char* access_kind) const { - return LogIfIn(name, blacklist_, "Blacklist", access_kind) || - LogIfIn(name, dark_greylist_, "Dark greylist", access_kind) || - LogIfIn(name, light_greylist_, "Light greylist", access_kind); + HiddenApiAccessFlags::ApiList GetApiList(const std::string& name) const { + if (IsInList(name, blacklist_)) { + return HiddenApiAccessFlags::kBlacklist; + } else if (IsInList(name, dark_greylist_)) { + return HiddenApiAccessFlags::kDarkGreylist; + } else if (IsInList(name, light_greylist_)) { + return HiddenApiAccessFlags::kLightGreylist; + } else { + return HiddenApiAccessFlags::kWhitelist; + } + } + + bool IsInRestrictionList(const std::string& name) const { + return GetApiList(name) != HiddenApiAccessFlags::kWhitelist; } static std::string GetApiMethodName(const DexFile& dex_file, uint32_t method_index); @@ -46,10 +59,9 @@ class HiddenApi { static std::string GetApiFieldName(const DexFile& dex_file, uint32_t field_index); private: - static bool LogIfIn(const std::string& name, - const std::set& list, - const std::string& log, - const std::string& access_kind); + static bool IsInList(const std::string& name, const std::set& list) { + return list.find(name) != list.end(); + } static void FillList(const char* filename, std::set& entries); diff --git a/tools/veridex/hidden_api_finder.cc b/tools/veridex/hidden_api_finder.cc new file mode 100644 index 0000000000..d611f78eed --- /dev/null +++ b/tools/veridex/hidden_api_finder.cc @@ -0,0 +1,266 @@ +/* + * 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. + */ + +#include "hidden_api_finder.h" + +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_instruction-inl.h" +#include "dex/dex_file.h" +#include "dex/method_reference.h" +#include "hidden_api.h" +#include "resolver.h" +#include "veridex.h" + +#include + +namespace art { + +void HiddenApiFinder::CheckMethod(uint32_t method_id, + VeridexResolver* resolver, + MethodReference ref) { + // Cheap check that the method is resolved. If it is, we know it's not in + // a restricted list. + if (resolver->GetMethod(method_id) != nullptr) { + return; + } + std::string name = HiddenApi::GetApiMethodName(resolver->GetDexFile(), method_id); + if (hidden_api_.IsInRestrictionList(name)) { + method_locations_[name].push_back(ref); + } +} + +void HiddenApiFinder::CheckField(uint32_t field_id, + VeridexResolver* resolver, + MethodReference ref) { + // Cheap check that the field is resolved. If it is, we know it's not in + // a restricted list. + if (resolver->GetField(field_id) != nullptr) { + return; + } + std::string name = HiddenApi::GetApiFieldName(resolver->GetDexFile(), field_id); + if (hidden_api_.IsInRestrictionList(name)) { + field_locations_[name].push_back(ref); + } +} + +void HiddenApiFinder::CollectAccesses(VeridexResolver* resolver) { + const DexFile& dex_file = resolver->GetDexFile(); + size_t class_def_count = dex_file.NumClassDefs(); + for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) { + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); + const uint8_t* class_data = dex_file.GetClassData(class_def); + if (class_data == nullptr) { + // Empty class. + continue; + } + ClassDataItemIterator it(dex_file, class_data); + it.SkipAllFields(); + for (; it.HasNextMethod(); it.Next()) { + const DexFile::CodeItem* code_item = it.GetMethodCodeItem(); + if (code_item == nullptr) { + continue; + } + CodeItemDataAccessor code_item_accessor(dex_file, code_item); + for (const DexInstructionPcPair& inst : code_item_accessor) { + switch (inst->Opcode()) { + case Instruction::CONST_CLASS: { + dex::TypeIndex type_index(inst->VRegB_21c()); + std::string name = dex_file.StringByTypeIdx(type_index); + // Only keep classes that are in a restriction list. + if (hidden_api_.IsInRestrictionList(name)) { + classes_.insert(name); + } + break; + } + case Instruction::CONST_STRING: { + dex::StringIndex string_index(inst->VRegB_21c()); + std::string name = std::string(dex_file.StringDataByIdx(string_index)); + // Cheap filtering on the string literal. We know it cannot be a field/method/class + // if it contains a space. + if (name.find(' ') == std::string::npos) { + // Class names at the Java level are of the form x.y.z, but the list encodes + // them of the form Lx/y/z;. Inner classes have '$' for both Java level class + // names in strings, and hidden API lists. + std::string str = name; + std::replace(str.begin(), str.end(), '.', '/'); + str = "L" + str + ";"; + // Note: we can query the lists directly, as HiddenApi added classes that own + // private methods and fields in them. + // We don't add class names to the `strings_` set as we know method/field names + // don't have '.' or '/'. All hidden API class names have a '/'. + if (hidden_api_.IsInRestrictionList(str)) { + classes_.insert(str); + } else if (hidden_api_.IsInRestrictionList(name)) { + // Could be something passed to JNI. + classes_.insert(name); + } else { + // We only keep track of the location for strings, as these will be the + // field/method names the user is interested in. + strings_.insert(name); + reflection_locations_[name].push_back( + MethodReference(&dex_file, it.GetMemberIndex())); + } + } + break; + } + case Instruction::INVOKE_DIRECT: + case Instruction::INVOKE_INTERFACE: + case Instruction::INVOKE_STATIC: + case Instruction::INVOKE_SUPER: + case Instruction::INVOKE_VIRTUAL: { + CheckMethod( + inst->VRegB_35c(), resolver, MethodReference(&dex_file, it.GetMemberIndex())); + break; + } + + case Instruction::INVOKE_DIRECT_RANGE: + case Instruction::INVOKE_INTERFACE_RANGE: + case Instruction::INVOKE_STATIC_RANGE: + case Instruction::INVOKE_SUPER_RANGE: + case Instruction::INVOKE_VIRTUAL_RANGE: { + CheckMethod( + inst->VRegB_3rc(), resolver, MethodReference(&dex_file, it.GetMemberIndex())); + break; + } + + case Instruction::IGET: + case Instruction::IGET_WIDE: + case Instruction::IGET_OBJECT: + case Instruction::IGET_BOOLEAN: + case Instruction::IGET_BYTE: + case Instruction::IGET_CHAR: + case Instruction::IGET_SHORT: { + CheckField( + inst->VRegC_22c(), resolver, MethodReference(&dex_file, it.GetMemberIndex())); + break; + } + + case Instruction::IPUT: + case Instruction::IPUT_WIDE: + case Instruction::IPUT_OBJECT: + case Instruction::IPUT_BOOLEAN: + case Instruction::IPUT_BYTE: + case Instruction::IPUT_CHAR: + case Instruction::IPUT_SHORT: { + CheckField( + inst->VRegC_22c(), resolver, MethodReference(&dex_file, it.GetMemberIndex())); + break; + } + + case Instruction::SGET: + case Instruction::SGET_WIDE: + case Instruction::SGET_OBJECT: + case Instruction::SGET_BOOLEAN: + case Instruction::SGET_BYTE: + case Instruction::SGET_CHAR: + case Instruction::SGET_SHORT: { + CheckField( + inst->VRegB_21c(), resolver, MethodReference(&dex_file, it.GetMemberIndex())); + break; + } + + case Instruction::SPUT: + case Instruction::SPUT_WIDE: + case Instruction::SPUT_OBJECT: + case Instruction::SPUT_BOOLEAN: + case Instruction::SPUT_BYTE: + case Instruction::SPUT_CHAR: + case Instruction::SPUT_SHORT: { + CheckField( + inst->VRegB_21c(), resolver, MethodReference(&dex_file, it.GetMemberIndex())); + break; + } + + default: + break; + } + } + } + } +} + +static std::string GetApiMethodName(MethodReference ref) { + return HiddenApi::GetApiMethodName(*ref.dex_file, ref.index); +} + +void HiddenApiFinder::Run(const std::vector>& resolvers) { + for (const std::unique_ptr& resolver : resolvers) { + CollectAccesses(resolver.get()); + } + + Dump(std::cout); +} + +void HiddenApiFinder::Dump(std::ostream& os) { + static const char* kPrefix = " "; + uint32_t count = 0; + uint32_t linking_count = method_locations_.size() + field_locations_.size(); + uint32_t api_counts[4] = {0, 0, 0, 0}; + + // Dump methods from hidden APIs linked against. + for (const std::pair>& pair : method_locations_) { + HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(pair.first); + api_counts[api_list]++; + os << "#" << ++count << ": Linking " << api_list << " " << pair.first << " use(s):"; + os << std::endl; + for (const MethodReference& ref : pair.second) { + os << kPrefix << GetApiMethodName(ref) << std::endl; + } + os << std::endl; + } + + // Dump fields from hidden APIs linked against. + for (const std::pair>& pair : field_locations_) { + HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(pair.first); + api_counts[api_list]++; + os << "#" << ++count << ": Linking " << api_list << " " << pair.first << " use(s):"; + os << std::endl; + for (const MethodReference& ref : pair.second) { + os << kPrefix << GetApiMethodName(ref) << std::endl; + } + os << std::endl; + } + + // Dump potential reflection uses. + for (const std::string& cls : classes_) { + for (const std::string& name : strings_) { + std::string full_name = cls + "->" + name; + HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name); + api_counts[api_list]++; + if (api_list != HiddenApiAccessFlags::kWhitelist) { + os << "#" << ++count << ": Reflection " << api_list << " " << full_name + << " potential use(s):"; + os << std::endl; + for (const MethodReference& ref : reflection_locations_[name]) { + os << kPrefix << GetApiMethodName(ref) << std::endl; + } + os << std::endl; + } + } + } + + os << count << " hidden API(s) used: " + << linking_count << " linked against, " + << count - linking_count << " potentially through reflection" << std::endl; + os << kPrefix << api_counts[HiddenApiAccessFlags::kBlacklist] + << " in blacklist" << std::endl; + os << kPrefix << api_counts[HiddenApiAccessFlags::kDarkGreylist] + << " in dark greylist" << std::endl; + os << kPrefix << api_counts[HiddenApiAccessFlags::kLightGreylist] + << " in light greylist" << std::endl; +} + +} // namespace art diff --git a/tools/veridex/hidden_api_finder.h b/tools/veridex/hidden_api_finder.h new file mode 100644 index 0000000000..243079c187 --- /dev/null +++ b/tools/veridex/hidden_api_finder.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#ifndef ART_TOOLS_VERIDEX_HIDDEN_API_FINDER_H_ +#define ART_TOOLS_VERIDEX_HIDDEN_API_FINDER_H_ + +#include "dex/method_reference.h" + +#include +#include +#include +#include + +namespace art { + +class HiddenApi; +class VeridexResolver; + +/** + * Reports potential uses of hidden APIs from static linking and reflection. + */ +class HiddenApiFinder { + public: + explicit HiddenApiFinder(const HiddenApi& hidden_api) : hidden_api_(hidden_api) {} + + // Iterate over the dex files associated with the passed resolvers to report + // hidden API uses. + void Run(const std::vector>& app_resolvers); + + private: + void CollectAccesses(VeridexResolver* resolver); + void CheckMethod(uint32_t method_idx, VeridexResolver* resolver, MethodReference ref); + void CheckField(uint32_t field_idx, VeridexResolver* resolver, MethodReference ref); + void Dump(std::ostream& os); + + const HiddenApi& hidden_api_; + std::set classes_; + std::set strings_; + std::map> reflection_locations_; + std::map> method_locations_; + std::map> field_locations_; +}; + +} // namespace art + +#endif // ART_TOOLS_VERIDEX_HIDDEN_API_FINDER_H_ diff --git a/tools/veridex/resolver.cc b/tools/veridex/resolver.cc index 6ab872ed6f..13dda5c199 100644 --- a/tools/veridex/resolver.cc +++ b/tools/veridex/resolver.cc @@ -277,10 +277,8 @@ VeriField VeridexResolver::GetField(uint32_t field_index) { return field_info; } -void VeridexResolver::ResolveAll(const HiddenApi& hidden_api) { +void VeridexResolver::ResolveAll() { for (uint32_t i = 0; i < dex_file_.NumTypeIds(); ++i) { - // Note: we don't look at HiddenApi for types, as the lists don't contain - // classes. if (GetVeriClass(dex::TypeIndex(i)) == nullptr) { LOG(WARNING) << "Unresolved " << dex_file_.PrettyType(dex::TypeIndex(i)); } @@ -288,17 +286,13 @@ void VeridexResolver::ResolveAll(const HiddenApi& hidden_api) { for (uint32_t i = 0; i < dex_file_.NumMethodIds(); ++i) { if (GetMethod(i) == nullptr) { - if (!hidden_api.LogIfInList(HiddenApi::GetApiMethodName(dex_file_, i), "Linking")) { - LOG(WARNING) << "Unresolved: " << dex_file_.PrettyMethod(i); - } + LOG(WARNING) << "Unresolved: " << dex_file_.PrettyMethod(i); } } for (uint32_t i = 0; i < dex_file_.NumFieldIds(); ++i) { if (GetField(i) == nullptr) { - if (!hidden_api.LogIfInList(HiddenApi::GetApiFieldName(dex_file_, i), "Linking")) { - LOG(WARNING) << "Unresolved: " << dex_file_.PrettyField(i); - } + LOG(WARNING) << "Unresolved: " << dex_file_.PrettyField(i); } } } diff --git a/tools/veridex/resolver.h b/tools/veridex/resolver.h index 82f6aaeddd..06c8aa70c5 100644 --- a/tools/veridex/resolver.h +++ b/tools/veridex/resolver.h @@ -66,9 +66,13 @@ class VeridexResolver { const char* field_name, const char* field_type); - // Resolve all type_id/method_id/field_id. Log for unresolved - // entities, or entities part of a hidden API list. - void ResolveAll(const HiddenApi& hidden_api); + // Resolve all type_id/method_id/field_id. + void ResolveAll(); + + // The dex file this resolver is associated to. + const DexFile& GetDexFile() const { + return dex_file_; + } private: // Return the resolver where `kls` is from. diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc index c5203fea66..16e9f0e55b 100644 --- a/tools/veridex/veridex.cc +++ b/tools/veridex/veridex.cc @@ -21,6 +21,7 @@ #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "hidden_api.h" +#include "hidden_api_finder.h" #include "resolver.h" #include @@ -162,11 +163,10 @@ class Veridex { std::vector> app_resolvers; Resolve(app_dex_files, resolver_map, type_map, &app_resolvers); - // Resolve all type_id/method_id/field_id of app dex files. + // Find and log uses of hidden APIs. HiddenApi hidden_api(options.blacklist, options.dark_greylist, options.light_greylist); - for (const std::unique_ptr& resolver : app_resolvers) { - resolver->ResolveAll(hidden_api); - } + HiddenApiFinder api_finder(hidden_api); + api_finder.Run(app_resolvers); return 0; } -- GitLab From 871bf39030406129af6fbce09f3d4f09af292653 Mon Sep 17 00:00:00 2001 From: Hans Boehm Date: Wed, 28 Mar 2018 17:44:09 -0700 Subject: [PATCH 173/749] Make Remove() atomic, as expected Bug: 31023171 Test: m -j28 test-art-host Change-Id: I0d9a4b19f1b307d98f01ec76a47e4748f713437c --- compiler/utils/atomic_dex_ref_map-inl.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/utils/atomic_dex_ref_map-inl.h b/compiler/utils/atomic_dex_ref_map-inl.h index 4bd323dadb..9915498acc 100644 --- a/compiler/utils/atomic_dex_ref_map-inl.h +++ b/compiler/utils/atomic_dex_ref_map-inl.h @@ -81,8 +81,7 @@ inline bool AtomicDexRefMap::Remove(const DexFileRe if (array == nullptr) { return false; } - *out = (*array)[ref.index].load(std::memory_order_relaxed); - (*array)[ref.index].store(nullptr, std::memory_order_seq_cst); + *out = (*array)[ref.index].exchange(nullptr, std::memory_order_seq_cst); return true; } -- GitLab From c4b1c0c42a707675755a468ba36026d7166a3cc0 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 29 Mar 2018 17:07:17 +0100 Subject: [PATCH 174/749] Fix failure to initialize AnnotatedStackTraceElement. Test: 171-init-aste Test: testrunner.py --host --interpreter Bug: 76208924 Change-Id: I2a0892c5cc8ab5cbc54a94c25a02add1031e68f5 --- runtime/thread.cc | 11 +++++++++ test/171-init-aste/expected.txt | 1 + test/171-init-aste/info.txt | 1 + test/171-init-aste/src-art/Main.java | 37 ++++++++++++++++++++++++++++ test/171-init-aste/src/Main.java | 24 ++++++++++++++++++ 5 files changed, 74 insertions(+) create mode 100644 test/171-init-aste/expected.txt create mode 100644 test/171-init-aste/info.txt create mode 100644 test/171-init-aste/src-art/Main.java create mode 100644 test/171-init-aste/src/Main.java diff --git a/runtime/thread.cc b/runtime/thread.cc index b13d8ec42a..d17f409a7d 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -2881,6 +2881,17 @@ jobjectArray Thread::CreateAnnotatedStackTrace(const ScopedObjectAccessAlreadyRu Handle h_aste_class(hs.NewHandle( h_aste_array_class->GetComponentType())); + + // Make sure the AnnotatedStackTraceElement.class is initialized, b/76208924 . + class_linker->EnsureInitialized(soa.Self(), + h_aste_class, + /* can_init_fields */ true, + /* can_init_parents */ true); + if (soa.Self()->IsExceptionPending()) { + // This should not fail in a healthy runtime. + return nullptr; + } + ArtField* stack_trace_element_field = h_aste_class->FindField( soa.Self(), h_aste_class.Get(), "stackTraceElement", "Ljava/lang/StackTraceElement;"); DCHECK(stack_trace_element_field != nullptr); diff --git a/test/171-init-aste/expected.txt b/test/171-init-aste/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/171-init-aste/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/171-init-aste/info.txt b/test/171-init-aste/info.txt new file mode 100644 index 0000000000..201e8ada57 --- /dev/null +++ b/test/171-init-aste/info.txt @@ -0,0 +1 @@ +Regression test for failure to initialize dalvik.system.AnnotatedStackTraceElement. diff --git a/test/171-init-aste/src-art/Main.java b/test/171-init-aste/src-art/Main.java new file mode 100644 index 0000000000..9d3661022e --- /dev/null +++ b/test/171-init-aste/src-art/Main.java @@ -0,0 +1,37 @@ +/* + * 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.Method; +import dalvik.system.AnnotatedStackTraceElement; + +public class Main { + public static void main(String args[]) throws Exception { + Class vmStack = Class.forName("dalvik.system.VMStack"); + Method getAnnotatedThreadStackTrace = + vmStack.getDeclaredMethod("getAnnotatedThreadStackTrace", Thread.class); + Object[] annotatedStackTrace = + (Object[]) getAnnotatedThreadStackTrace.invoke(null, Thread.currentThread()); + AnnotatedStackTraceElement annotatedElement = + (AnnotatedStackTraceElement) annotatedStackTrace[0]; + // This used to fail an assertion that the AnnotatedStackTraceElement.class + // is at least initializing (i.e. initializing, initialized or resolved-erroneous). + // Note: We cannot use reflection for this test because getDeclaredMethod() would + // initialize the class and hide the failure. + annotatedElement.getStackTraceElement(); + + System.out.println("passed"); + } +} diff --git a/test/171-init-aste/src/Main.java b/test/171-init-aste/src/Main.java new file mode 100644 index 0000000000..4479cb4373 --- /dev/null +++ b/test/171-init-aste/src/Main.java @@ -0,0 +1,24 @@ +/* + * 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 Main { + // Note: This file is used for the RI which does not support + // dalvik.system.AnnotatedStackTraceElement (see src-art/Main.java), + // so that we do not need an exclusion in known failures. + public static void main(String args[]) throws Exception { + System.out.println("passed"); + } +} -- GitLab From 94e3dd79da6c94a6b024da776b34a87d59a6d53d Mon Sep 17 00:00:00 2001 From: Alex Light Date: Thu, 29 Mar 2018 09:15:56 -0700 Subject: [PATCH 175/749] Revert "Revert "Ensure that OSR still is possible with jvmti"" Reason for revert: Fixed issue causing test 1935 to be flaky This reverts commit 4f3d1cfbee45a27d5997c379f4eb7b7108224ca8. This unreverts commit b9ad26d1ed9146b89555d4333021f44eeb831f05 Test: ./test.py --host -j50 --all -t 993 Test: ./test.py --host Test: while ./test/run-test --host --jit 1935; do; done Test: am start --attach-agent -n com.example.android.displayingbitmaps/.ui.ImageGridActivity Run blur filter. Bug: 76226464 Change-Id: Iccdd2d6b788db83786690d697e955c15bcd76c73 --- openjdkjvmti/deopt_manager.cc | 74 +++++++++++-------- openjdkjvmti/deopt_manager.h | 20 ++++- openjdkjvmti/ti_method.cc | 3 + runtime/instrumentation.cc | 8 +- .../expected.txt | 2 - .../src/Main.java | 29 +++++--- 6 files changed, 91 insertions(+), 45 deletions(-) diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc index 6d84ffa53f..380d95c545 100644 --- a/openjdkjvmti/deopt_manager.cc +++ b/openjdkjvmti/deopt_manager.cc @@ -53,14 +53,18 @@ namespace openjdkjvmti { // TODO We should make this much more selective in the future so we only return true when we -// actually care about the method (i.e. had locals changed, have breakpoints, etc.). For now though -// we can just assume that we care we are loaded at all. -// -// Even if we don't keep track of this at the method level we might want to keep track of it at the -// level of enabled capabilities. -bool JvmtiMethodInspectionCallback::IsMethodBeingInspected( - art::ArtMethod* method ATTRIBUTE_UNUSED) { - return true; +// actually care about the method at this time (ie active frames had locals changed). For now we +// just assume that if anything has changed any frame's locals we care about all methods. If nothing +// has we only care about methods with active breakpoints on them. In the future we should probably +// rewrite all of this to instead do this at the ShadowFrame or thread granularity. +bool JvmtiMethodInspectionCallback::IsMethodBeingInspected(art::ArtMethod* method) { + // Non-java-debuggable runtimes we need to assume that any method might not be debuggable and + // therefore potentially being inspected (due to inlines). If we are debuggable we rely hard on + // inlining not being done since we don't keep track of which methods get inlined where and simply + // look to see if the method is breakpointed. + return !art::Runtime::Current()->IsJavaDebuggable() || + manager_->HaveLocalsChanged() || + manager_->MethodHasBreakpoints(method); } bool JvmtiMethodInspectionCallback::IsMethodSafeToJit(art::ArtMethod* method) { @@ -75,7 +79,10 @@ DeoptManager::DeoptManager() performing_deoptimization_(false), global_deopt_count_(0), deopter_count_(0), - inspection_callback_(this) { } + breakpoint_status_lock_("JVMTI_BreakpointStatusLock", + static_cast(art::LockLevel::kAbortLock + 1)), + inspection_callback_(this), + set_local_variable_called_(false) { } void DeoptManager::Setup() { art::ScopedThreadStateChange stsc(art::Thread::Current(), @@ -121,14 +128,11 @@ void DeoptManager::FinishSetup() { } bool DeoptManager::MethodHasBreakpoints(art::ArtMethod* method) { - art::MutexLock lk(art::Thread::Current(), deoptimization_status_lock_); + art::MutexLock lk(art::Thread::Current(), breakpoint_status_lock_); return MethodHasBreakpointsLocked(method); } bool DeoptManager::MethodHasBreakpointsLocked(art::ArtMethod* method) { - if (deopter_count_ == 0) { - return false; - } auto elem = breakpoint_status_.find(method); return elem != breakpoint_status_.end() && elem->second != 0; } @@ -158,18 +162,23 @@ void DeoptManager::AddMethodBreakpoint(art::ArtMethod* method) { art::ScopedThreadSuspension sts(self, art::kSuspended); deoptimization_status_lock_.ExclusiveLock(self); - - DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; - - if (MethodHasBreakpointsLocked(method)) { - // Don't need to do anything extra. - breakpoint_status_[method]++; - // Another thread might be deoptimizing the very method we just added new breakpoints for. Wait - // for any deopts to finish before moving on. - WaitForDeoptimizationToFinish(self); - return; + { + breakpoint_status_lock_.ExclusiveLock(self); + + DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; + + if (MethodHasBreakpointsLocked(method)) { + // Don't need to do anything extra. + breakpoint_status_[method]++; + // Another thread might be deoptimizing the very method we just added new breakpoints for. + // Wait for any deopts to finish before moving on. + breakpoint_status_lock_.ExclusiveUnlock(self); + WaitForDeoptimizationToFinish(self); + return; + } + breakpoint_status_[method] = 1; + breakpoint_status_lock_.ExclusiveUnlock(self); } - breakpoint_status_[method] = 1; auto instrumentation = art::Runtime::Current()->GetInstrumentation(); if (instrumentation->IsForcedInterpretOnly()) { // We are already interpreting everything so no need to do anything. @@ -196,17 +205,22 @@ void DeoptManager::RemoveMethodBreakpoint(art::ArtMethod* method) { // need but since that is very heavy we will instead just use a condition variable to make sure we // don't race with ourselves. deoptimization_status_lock_.ExclusiveLock(self); - - DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; - DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without " - << "breakpoints present!"; + bool is_last_breakpoint; + { + art::MutexLock mu(self, breakpoint_status_lock_); + + DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; + DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without " + << "breakpoints present!"; + breakpoint_status_[method] -= 1; + is_last_breakpoint = (breakpoint_status_[method] == 0); + } auto instrumentation = art::Runtime::Current()->GetInstrumentation(); - breakpoint_status_[method] -= 1; if (UNLIKELY(instrumentation->IsForcedInterpretOnly())) { // We don't need to do anything since we are interpreting everything anyway. deoptimization_status_lock_.ExclusiveUnlock(self); return; - } else if (breakpoint_status_[method] == 0) { + } else if (is_last_breakpoint) { if (UNLIKELY(is_default)) { RemoveDeoptimizeAllMethodsLocked(self); } else { diff --git a/openjdkjvmti/deopt_manager.h b/openjdkjvmti/deopt_manager.h index a495b6835c..a38690c49e 100644 --- a/openjdkjvmti/deopt_manager.h +++ b/openjdkjvmti/deopt_manager.h @@ -32,6 +32,7 @@ #ifndef ART_OPENJDKJVMTI_DEOPT_MANAGER_H_ #define ART_OPENJDKJVMTI_DEOPT_MANAGER_H_ +#include #include #include "jni.h" @@ -107,9 +108,17 @@ class DeoptManager { static DeoptManager* Get(); + bool HaveLocalsChanged() const { + return set_local_variable_called_.load(); + } + + void SetLocalsUpdated() { + set_local_variable_called_.store(true); + } + private: bool MethodHasBreakpointsLocked(art::ArtMethod* method) - REQUIRES(deoptimization_status_lock_); + REQUIRES(breakpoint_status_lock_); // Wait until nothing is currently in the middle of deoptimizing/undeoptimizing something. This is // needed to ensure that everything is synchronized since threads need to drop the @@ -156,13 +165,20 @@ class DeoptManager { // Number of users of deoptimization there currently are. uint32_t deopter_count_ GUARDED_BY(deoptimization_status_lock_); + // A mutex that just protects the breakpoint-status map. This mutex should always be at the + // bottom of the lock hierarchy. Nothing more should be locked if we hold this. + art::Mutex breakpoint_status_lock_ ACQUIRED_BEFORE(art::Locks::abort_lock_); // A map from methods to the number of breakpoints in them from all envs. std::unordered_map breakpoint_status_ - GUARDED_BY(deoptimization_status_lock_); + GUARDED_BY(breakpoint_status_lock_); // The MethodInspectionCallback we use to tell the runtime if we care about particular methods. JvmtiMethodInspectionCallback inspection_callback_; + // Set to true if anything calls SetLocalVariables on any thread since we need to be careful about + // OSR after this. + std::atomic set_local_variable_called_; + // Helper for setting up/tearing-down for deoptimization. friend class ScopedDeoptimizationContext; }; diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index bf2e6cd104..b83310dc85 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -915,6 +915,9 @@ jvmtiError MethodUtil::SetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED, if (depth < 0) { return ERR(ILLEGAL_ARGUMENT); } + // Make sure that we know not to do any OSR anymore. + // TODO We should really keep track of this at the Frame granularity. + DeoptManager::Get()->SetLocalsUpdated(); art::Thread* self = art::Thread::Current(); // Suspend JIT since it can get confused if we deoptimize methods getting jitted. art::jit::ScopedJitSuspend suspend_jit; diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 84a148f21c..ec3e10e2f8 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -139,10 +139,14 @@ static void UpdateEntrypoints(ArtMethod* method, const void* quick_code) bool Instrumentation::NeedDebugVersionFor(ArtMethod* method) const REQUIRES_SHARED(Locks::mutator_lock_) { - return Runtime::Current()->IsJavaDebuggable() && + art::Runtime* runtime = Runtime::Current(); + return runtime->IsJavaDebuggable() && !method->IsNative() && !method->IsProxyMethod() && - Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method); + // If we don't have a jit this can push us to the pre-compiled version of methods which is + // not something we want since we are debuggable. + (UNLIKELY(runtime->GetJit() == nullptr) || + runtime->GetRuntimeCallbacks()->IsMethodBeingInspected(method)); } void Instrumentation::InstallStubsForMethod(ArtMethod* method) { diff --git a/test/1935-get-set-current-frame-jit/expected.txt b/test/1935-get-set-current-frame-jit/expected.txt index cdb8f6a825..a685891775 100644 --- a/test/1935-get-set-current-frame-jit/expected.txt +++ b/test/1935-get-set-current-frame-jit/expected.txt @@ -1,7 +1,5 @@ JNI_OnLoad called From GetLocalInt(), value is 42 -isInOsrCode? false Value is '42' Setting TARGET to 1337 -isInOsrCode? false Value is '1337' diff --git a/test/1935-get-set-current-frame-jit/src/Main.java b/test/1935-get-set-current-frame-jit/src/Main.java index 714a98aaf3..671493bbaa 100644 --- a/test/1935-get-set-current-frame-jit/src/Main.java +++ b/test/1935-get-set-current-frame-jit/src/Main.java @@ -21,6 +21,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.nio.ByteBuffer; +import java.time.Instant; import java.util.concurrent.Semaphore; import java.util.Arrays; import java.util.Collection; @@ -49,9 +50,11 @@ public class Main { public static class IntRunner implements Runnable { private volatile boolean continueBusyLoop; private volatile boolean inBusyLoop; - public IntRunner() { + private final boolean expectOsr; + public IntRunner(boolean expectOsr) { this.continueBusyLoop = true; this.inBusyLoop = false; + this.expectOsr = expectOsr; } public void run() { int TARGET = 42; @@ -59,14 +62,22 @@ public class Main { while (continueBusyLoop) { inBusyLoop = true; } - int i = 0; - while (Main.isInterpreted() && i < 10000) { + // Wait up to 300 seconds for OSR to kick in if we expect it. If we don't give up after only + // 3 seconds. + Instant osrDeadline = Instant.now().plusSeconds(expectOsr ? 600 : 3); + do { Main.ensureJitCompiled(IntRunner.class, "run"); - i++; - } - // We shouldn't be doing OSR since we are using JVMTI and the get/set prevents OSR. + } while (hasJit() && !Main.isInOsrCode("run") && osrDeadline.compareTo(Instant.now()) > 0); + // We shouldn't be doing OSR since we are using JVMTI and the set prevents OSR. // Set local will also push us to interpreter but the get local may remain in compiled code. - System.out.println("isInOsrCode? " + (hasJit() && Main.isInOsrCode("run"))); + if (hasJit()) { + boolean inOsr = Main.isInOsrCode("run"); + if (expectOsr && !inOsr) { + throw new Error("Expected to be in OSR but was not."); + } else if (!expectOsr && inOsr) { + throw new Error("Expected not to be in OSR but was."); + } + } reportValue(TARGET); } public void waitForBusyLoopStart() { while (!inBusyLoop) {} } @@ -78,7 +89,7 @@ public class Main { public static void runGet() throws Exception { Method target = IntRunner.class.getDeclaredMethod("run"); // Get Int - IntRunner int_runner = new IntRunner(); + IntRunner int_runner = new IntRunner(true); Thread target_get = new Thread(int_runner, "GetLocalInt - Target"); target_get.start(); int_runner.waitForBusyLoopStart(); @@ -108,7 +119,7 @@ public class Main { public static void runSet() throws Exception { Method target = IntRunner.class.getDeclaredMethod("run"); // Set Int - IntRunner int_runner = new IntRunner(); + IntRunner int_runner = new IntRunner(false); Thread target_set = new Thread(int_runner, "SetLocalInt - Target"); target_set.start(); int_runner.waitForBusyLoopStart(); -- GitLab From c7b28de9f8bf407d91cff22de782d022492b45f7 Mon Sep 17 00:00:00 2001 From: Hans Boehm Date: Fri, 9 Mar 2018 17:05:28 -0800 Subject: [PATCH 176/749] Add reachabilityFence intrinsics Add intrinsics that generate no code or do nothing for all architectures and for the interpreter. The only impact is to keep the argument live at all suspend points preceding the call. We ensure that the code is not moved across other memory accesses by declaring it to have write side-effects. Add a minimal test. Modify 036-finalizer to use a reachabilityFence, hopefully making it more robust to dead refererence elimination. Bug: 72698200 Test: Build and boot AOSP. art/test.py --host -r -t 072-reachability-fence Look at generated code. Change-Id: I0f298bf5cc375d8ebc19bb791cc05a8490d55430 --- compiler/optimizing/intrinsics_arm64.cc | 8 +++ compiler/optimizing/intrinsics_arm_vixl.cc | 8 +++ compiler/optimizing/intrinsics_mips.cc | 8 +++ compiler/optimizing/intrinsics_mips64.cc | 8 +++ compiler/optimizing/intrinsics_x86.cc | 7 +++ compiler/optimizing/intrinsics_x86_64.cc | 8 +++ runtime/image.cc | 2 +- runtime/interpreter/interpreter_intrinsics.cc | 11 ++++ runtime/intrinsics_list.h | 1 + test/036-finalizer/src/Main.java | 2 + test/072-reachability-fence/expected.txt | 5 ++ test/072-reachability-fence/info.txt | 4 ++ test/072-reachability-fence/src/Main.java | 61 +++++++++++++++++++ 13 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 test/072-reachability-fence/expected.txt create mode 100644 test/072-reachability-fence/info.txt create mode 100644 test/072-reachability-fence/src/Main.java diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 81c0b50932..c3d643a7d1 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -2875,6 +2875,14 @@ void IntrinsicCodeGeneratorARM64::VisitThreadInterrupted(HInvoke* invoke) { __ Bind(&done); } +void IntrinsicLocationsBuilderARM64::VisitReachabilityFence(HInvoke* invoke) { + LocationSummary* locations = + new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); + locations->SetInAt(0, Location::Any()); +} + +void IntrinsicCodeGeneratorARM64::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { } + UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOf); diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index e61a0b0809..29aecbc097 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -3028,6 +3028,14 @@ void IntrinsicCodeGeneratorARMVIXL::VisitThreadInterrupted(HInvoke* invoke) { } } +void IntrinsicLocationsBuilderARMVIXL::VisitReachabilityFence(HInvoke* invoke) { + LocationSummary* locations = + new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); + locations->SetInAt(0, Location::Any()); +} + +void IntrinsicCodeGeneratorARMVIXL::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { } + UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundDouble) // Could be done by changing rounding mode, maybe? UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeCASLong) // High register pressure. UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar) diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index bc1292b2b7..ae248a3e5c 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -2693,6 +2693,14 @@ void IntrinsicCodeGeneratorMIPS::VisitThreadInterrupted(HInvoke* invoke) { __ Bind(&done); } +void IntrinsicLocationsBuilderMIPS::VisitReachabilityFence(HInvoke* invoke) { + LocationSummary* locations = + new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); + locations->SetInAt(0, Location::Any()); +} + +void IntrinsicCodeGeneratorMIPS::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { } + // Unimplemented intrinsics. UNIMPLEMENTED_INTRINSIC(MIPS, MathCeil) diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index f429afde2c..9a9ae714bc 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -2354,6 +2354,14 @@ void IntrinsicCodeGeneratorMIPS64::VisitThreadInterrupted(HInvoke* invoke) { __ Bind(&done); } +void IntrinsicLocationsBuilderMIPS64::VisitReachabilityFence(HInvoke* invoke) { + LocationSummary* locations = + new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); + locations->SetInAt(0, Location::Any()); +} + +void IntrinsicCodeGeneratorMIPS64::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { } + UNIMPLEMENTED_INTRINSIC(MIPS64, ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopy) diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index c4f322bf0c..f84a33bb8e 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -2928,6 +2928,13 @@ void IntrinsicCodeGeneratorX86::VisitThreadInterrupted(HInvoke* invoke) { __ Bind(&done); } +void IntrinsicLocationsBuilderX86::VisitReachabilityFence(HInvoke* invoke) { + LocationSummary* locations = + new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); + locations->SetInAt(0, Location::Any()); +} + +void IntrinsicCodeGeneratorX86::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { } UNIMPLEMENTED_INTRINSIC(X86, MathRoundDouble) UNIMPLEMENTED_INTRINSIC(X86, ReferenceGetReferent) diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 437bc3dd3c..7627dc9490 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -2737,6 +2737,14 @@ void IntrinsicCodeGeneratorX86_64::VisitThreadInterrupted(HInvoke* invoke) { __ Bind(&done); } +void IntrinsicLocationsBuilderX86_64::VisitReachabilityFence(HInvoke* invoke) { + LocationSummary* locations = + new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); + locations->SetInAt(0, Location::Any()); +} + +void IntrinsicCodeGeneratorX86_64::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { } + UNIMPLEMENTED_INTRINSIC(X86_64, ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(X86_64, FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(X86_64, DoubleIsInfinite) diff --git a/runtime/image.cc b/runtime/image.cc index f14707874b..316f7a5c63 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -26,7 +26,7 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '8', '\0' }; // R^3 Bitstring type check. +const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '9', '\0' }; // ReachabilityFence. ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc index 022b1395bf..69dae31b37 100644 --- a/runtime/interpreter/interpreter_intrinsics.cc +++ b/runtime/interpreter/interpreter_intrinsics.cc @@ -399,6 +399,16 @@ VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetAcquire) VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetPlain) VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetRelease) +static ALWAYS_INLINE bool MterpReachabilityFence(ShadowFrame* shadow_frame ATTRIBUTE_UNUSED, + const Instruction* inst ATTRIBUTE_UNUSED, + uint16_t inst_data ATTRIBUTE_UNUSED, + JValue* result_register ATTRIBUTE_UNUSED) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Do nothing; Its only purpose is to keep the argument reference live + // at preceding suspend points. That's automatic in the interpreter. + return true; +} + // Macro to help keep track of what's left to implement. #define UNIMPLEMENTED_CASE(name) \ case Intrinsics::k##name: \ @@ -499,6 +509,7 @@ bool MterpHandleIntrinsic(ShadowFrame* shadow_frame, UNIMPLEMENTED_CASE(MemoryPokeIntNative /* (JI)V */) UNIMPLEMENTED_CASE(MemoryPokeLongNative /* (JJ)V */) UNIMPLEMENTED_CASE(MemoryPokeShortNative /* (JS)V */) + INTRINSIC_CASE(ReachabilityFence /* (Ljava/lang/Object;)V */) INTRINSIC_CASE(StringCharAt) INTRINSIC_CASE(StringCompareTo) INTRINSIC_CASE(StringEquals) diff --git a/runtime/intrinsics_list.h b/runtime/intrinsics_list.h index da08793f59..2f91f5dfe0 100644 --- a/runtime/intrinsics_list.h +++ b/runtime/intrinsics_list.h @@ -218,6 +218,7 @@ V(VarHandleReleaseFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "releaseFence", "()V") \ V(VarHandleLoadLoadFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "loadLoadFence", "()V") \ V(VarHandleStoreStoreFence, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "storeStoreFence", "()V") \ + V(ReachabilityFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/ref/Reference;", "reachabilityFence", "(Ljava/lang/Object;)V") \ SIGNATURE_POLYMORPHIC_INTRINSICS_LIST(V) #endif // ART_RUNTIME_INTRINSICS_LIST_H_ diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java index 51d4a81150..be7ae4a8c2 100644 --- a/test/036-finalizer/src/Main.java +++ b/test/036-finalizer/src/Main.java @@ -14,6 +14,7 @@ * limitations under the License. */ +import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -80,6 +81,7 @@ public class Main { // the test fail (even when keeping the `null` assignment). b/76454261 FinalizerTest keepLive = wimp.get(); System.out.println("wimp: " + wimpString(wimp)); + Reference.reachabilityFence(keepLive); keepLive = null; // Clear the reference. /* this will try to collect and finalize ft */ diff --git a/test/072-reachability-fence/expected.txt b/test/072-reachability-fence/expected.txt new file mode 100644 index 0000000000..fdd0d7bd59 --- /dev/null +++ b/test/072-reachability-fence/expected.txt @@ -0,0 +1,5 @@ +Starting +Reference 0 was live. +Reference 3 was live. +Reference 4 was live. +Finished diff --git a/test/072-reachability-fence/info.txt b/test/072-reachability-fence/info.txt new file mode 100644 index 0000000000..21b6d6a39f --- /dev/null +++ b/test/072-reachability-fence/info.txt @@ -0,0 +1,4 @@ +Check that reachabilityFence() prevents garbage collection of objects only referred to by a dead +reference. + +This is not very convincing, since we currently usually keep such objects around anyway. diff --git a/test/072-reachability-fence/src/Main.java b/test/072-reachability-fence/src/Main.java new file mode 100644 index 0000000000..ac1e131d99 --- /dev/null +++ b/test/072-reachability-fence/src/Main.java @@ -0,0 +1,61 @@ +/* + * 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.Reference; +import java.lang.ref.WeakReference; + +public class Main { + public static void main(String[] args) { + System.out.println("Starting"); + WeakReference wrefs[] = new WeakReference[5]; + String str0 = generateString("String", 0); + String str1 = generateString("String", 1); + String str2 = generateString("String", 2); + String str3 = generateString("String", 3); + String str4 = generateString("String", 4); + wrefs[0] = new WeakReference(str0); + wrefs[1] = new WeakReference(str1); + wrefs[2] = new WeakReference(str2); + wrefs[3] = new WeakReference(str3); + wrefs[4] = new WeakReference(str4); + // Clear a couple as a sanity check. + str1 = null; + str2 = null; + // str dead here; in the future we will possibly reuse the registers. + // Give the compiler something to fill the registers with. + String str5 = generateString("String", 5); + String str6 = generateString("String", 6); + String str7 = generateString("String", 7); + String str8 = generateString("String", 8); + String str9 = generateString("String", 9); + Runtime.getRuntime().gc(); + for (int i = 0; i < 5; ++i) { + if (wrefs[i].get() != null) { + System.out.println("Reference " + i + " was live."); + } + } + Reference.reachabilityFence(str0); + Reference.reachabilityFence(str1); + Reference.reachabilityFence(str2); + Reference.reachabilityFence(str3); + Reference.reachabilityFence(str4); + System.out.println("Finished"); + } + + private static String generateString(String base, int num) { + return base + num; + } +} -- GitLab From cdd53140b77bf724a4d7451d4b495dbd90875372 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 28 Mar 2018 21:17:43 -0700 Subject: [PATCH 177/749] ART: Experiment with timeout dumping In an attempt to diagnose some timeout dumping issues, allow a recursive unexpected signal. Also print the new signal number irrespectively. Test: m test-art-host Test: manual: send timeout signal to run-test Test: manual: send timeout signal to run-test, then send sigbus to run-test Change-Id: Idf198b264a7e5868bdf444f323921c946584c650 --- runtime/runtime_common.cc | 94 +++++++++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 24 deletions(-) diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc index 41bfb58d93..010c6e7571 100644 --- a/runtime/runtime_common.cc +++ b/runtime/runtime_common.cc @@ -370,30 +370,11 @@ static bool IsTimeoutSignal(int signal_number) { #pragma GCC diagnostic ignored "-Wframe-larger-than=" #endif -void HandleUnexpectedSignalCommon(int signal_number, - siginfo_t* info, - void* raw_context, - bool handle_timeout_signal, - bool dump_on_stderr) { - static bool handling_unexpected_signal = false; - if (handling_unexpected_signal) { - LogHelper::LogLineLowStack(__FILE__, - __LINE__, - ::android::base::FATAL_WITHOUT_ABORT, - "HandleUnexpectedSignal reentered\n"); - if (handle_timeout_signal) { - if (IsTimeoutSignal(signal_number)) { - // Ignore a recursive timeout. - return; - } - } - _exit(1); - } - handling_unexpected_signal = true; - - gAborting++; // set before taking any locks - MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_); - +static void HandleUnexpectedSignalCommonDump(int signal_number, + siginfo_t* info, + void* raw_context, + bool handle_timeout_signal, + bool dump_on_stderr) { auto logger = [&](auto& stream) { bool has_address = (signal_number == SIGILL || signal_number == SIGBUS || signal_number == SIGFPE || signal_number == SIGSEGV); @@ -452,6 +433,71 @@ void HandleUnexpectedSignalCommon(int signal_number, } } +void HandleUnexpectedSignalCommon(int signal_number, + siginfo_t* info, + void* raw_context, + bool handle_timeout_signal, + bool dump_on_stderr) { + // Local _static_ storing the currently handled signal (or -1). + static int handling_unexpected_signal = -1; + + // Whether the dump code should be run under the unexpected-signal lock. For diagnostics we + // allow recursive unexpected-signals in certain cases - avoid a deadlock. + bool grab_lock = true; + + if (handling_unexpected_signal != -1) { + LogHelper::LogLineLowStack(__FILE__, + __LINE__, + ::android::base::FATAL_WITHOUT_ABORT, + "HandleUnexpectedSignal reentered\n"); + // Print the signal number. Don't use any standard functions, just some arithmetic. Just best + // effort, with a minimal buffer. + if (0 < signal_number && signal_number < 100) { + char buf[] = { ' ', + 'S', + static_cast('0' + (signal_number / 10)), + static_cast('0' + (signal_number % 10)), + '\n', + 0 }; + LogHelper::LogLineLowStack(__FILE__, + __LINE__, + ::android::base::FATAL_WITHOUT_ABORT, + buf); + } + if (handle_timeout_signal) { + if (IsTimeoutSignal(signal_number)) { + // Ignore a recursive timeout. + return; + } + } + // If we were handling a timeout signal, try to go on. Otherwise hard-exit. + // This relies on the expectation that we'll only ever get one timeout signal. + if (!handle_timeout_signal || handling_unexpected_signal != GetTimeoutSignal()) { + _exit(1); + } + grab_lock = false; // The "outer" handling instance already holds the lock. + } + handling_unexpected_signal = signal_number; + + gAborting++; // set before taking any locks + + if (grab_lock) { + MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_); + + HandleUnexpectedSignalCommonDump(signal_number, + info, + raw_context, + handle_timeout_signal, + dump_on_stderr); + } else { + HandleUnexpectedSignalCommonDump(signal_number, + info, + raw_context, + handle_timeout_signal, + dump_on_stderr); + } +} + #if defined(__APPLE__) #pragma GCC diagnostic pop #endif -- GitLab From 698aa163e1d5a0954e011dd93eb4912173f83031 Mon Sep 17 00:00:00 2001 From: Hans Boehm Date: Thu, 29 Mar 2018 14:29:46 -0700 Subject: [PATCH 178/749] Make atomics use for allocator counters consistent. Use memory_order_relaxed atomics everywhere. Document what that means. We were previously using seq_cst updates in some places. The only benefit of that might have been for the expected invariants between the counters, e.g. bytes_used < total_bytes_used, to actually hold. But they didn't anyway because no care was taken to update them in the correct order. And we were using relaxed (and even volatile) accesses in other places. Update max_bytes_used atomically, so that it can't decrease. Bug: 31023171 Test: Build and boot AOSP Change-Id: Icfca919d48c67899acb1798f5357f17e956099a6 --- libartbase/base/allocator.cc | 4 ++-- libartbase/base/allocator.h | 16 ++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/libartbase/base/allocator.cc b/libartbase/base/allocator.cc index 17da789b36..c7be4e0161 100644 --- a/libartbase/base/allocator.cc +++ b/libartbase/base/allocator.cc @@ -76,7 +76,7 @@ namespace TrackedAllocators { // These globals are safe since they don't have any non-trivial destructors. Atomic g_bytes_used[kAllocatorTagCount]; -volatile size_t g_max_bytes_used[kAllocatorTagCount]; +Atomic g_max_bytes_used[kAllocatorTagCount]; Atomic g_total_bytes_used[kAllocatorTagCount]; void Dump(std::ostream& os) { @@ -84,7 +84,7 @@ void Dump(std::ostream& os) { os << "Dumping native memory usage\n"; for (size_t i = 0; i < kAllocatorTagCount; ++i) { uint64_t bytes_used = g_bytes_used[i].load(std::memory_order_relaxed); - uint64_t max_bytes_used = g_max_bytes_used[i]; + uint64_t max_bytes_used = g_max_bytes_used[i].load(std::memory_order_relaxed); uint64_t total_bytes_used = g_total_bytes_used[i].load(std::memory_order_relaxed); if (total_bytes_used != 0) { os << static_cast(i) << " active=" << bytes_used << " max=" diff --git a/libartbase/base/allocator.h b/libartbase/base/allocator.h index 7ddbacf716..662f78e448 100644 --- a/libartbase/base/allocator.h +++ b/libartbase/base/allocator.h @@ -71,12 +71,14 @@ std::ostream& operator<<(std::ostream& os, const AllocatorTag& tag); namespace TrackedAllocators { +// We use memory_order_relaxed updates of the following counters. Values are treated as approximate +// wherever concurrent updates are possible. // Running count of number of bytes used for this kind of allocation. Increased by allocations, // decreased by deallocations. extern Atomic g_bytes_used[kAllocatorTagCount]; // Largest value of bytes used seen. -extern volatile size_t g_max_bytes_used[kAllocatorTagCount]; +extern Atomic g_max_bytes_used[kAllocatorTagCount]; // Total number of bytes allocated of this kind. extern Atomic g_total_bytes_used[kAllocatorTagCount]; @@ -84,15 +86,17 @@ extern Atomic g_total_bytes_used[kAllocatorTagCount]; void Dump(std::ostream& os); inline void RegisterAllocation(AllocatorTag tag, size_t bytes) { - g_total_bytes_used[tag].fetch_add(bytes, std::memory_order_seq_cst); - size_t new_bytes = g_bytes_used[tag].fetch_add(bytes, std::memory_order_seq_cst) + bytes; - if (g_max_bytes_used[tag] < new_bytes) { - g_max_bytes_used[tag] = new_bytes; + g_total_bytes_used[tag].fetch_add(bytes, std::memory_order_relaxed); + size_t new_bytes = g_bytes_used[tag].fetch_add(bytes, std::memory_order_relaxed) + bytes; + size_t max_bytes = g_max_bytes_used[tag].load(std::memory_order_relaxed); + while (max_bytes < new_bytes + && !g_max_bytes_used[tag].compare_exchange_weak(max_bytes /* updated */, new_bytes, + std::memory_order_relaxed)) { } } inline void RegisterFree(AllocatorTag tag, size_t bytes) { - g_bytes_used[tag].fetch_sub(bytes, std::memory_order_seq_cst); + g_bytes_used[tag].fetch_sub(bytes, std::memory_order_relaxed); } } // namespace TrackedAllocators -- GitLab From e2ac8d7c310acfcb4b8ed5018472910e991bdae3 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 29 Mar 2018 21:09:10 -0700 Subject: [PATCH 179/749] ART: Remove logging restriction The compiler stats have their own dex2oat parameter, the restriction to debug build or verbose logging is antiquated. Test: m Change-Id: Idcbe5753bb2149a9694e39d7fa6ba7902e9c7810 --- compiler/optimizing/optimizing_compiler_stats.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index e0a9cfb934..9a26f2f6c4 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -125,11 +125,6 @@ class OptimizingCompilerStats { } void Log() const { - if (!kIsDebugBuild && !VLOG_IS_ON(compiler)) { - // Log only in debug builds or if the compiler is verbose. - return; - } - uint32_t compiled_intrinsics = GetStat(MethodCompilationStat::kCompiledIntrinsic); uint32_t compiled_native_stubs = GetStat(MethodCompilationStat::kCompiledNativeStub); uint32_t bytecode_attempts = -- GitLab From ce2836aaf9b04a0ad23739b24adc4437b6443bd3 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Fri, 30 Mar 2018 14:32:17 +0000 Subject: [PATCH 180/749] Revert^3 "Ensure that OSR still is possible with jvmti" This reverts commit 94e3dd79da6c94a6b024da776b34a87d59a6d53d. Reason for revert: test 1935 is sporadically failing on bots. Bug: 76226464 Change-Id: I42f6eaa51887701a2c88187abbc100e2ec3ef922 Test: None --- openjdkjvmti/deopt_manager.cc | 74 ++++++++----------- openjdkjvmti/deopt_manager.h | 20 +---- openjdkjvmti/ti_method.cc | 3 - runtime/instrumentation.cc | 8 +- .../expected.txt | 2 + .../src/Main.java | 29 +++----- 6 files changed, 45 insertions(+), 91 deletions(-) diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc index 380d95c545..6d84ffa53f 100644 --- a/openjdkjvmti/deopt_manager.cc +++ b/openjdkjvmti/deopt_manager.cc @@ -53,18 +53,14 @@ namespace openjdkjvmti { // TODO We should make this much more selective in the future so we only return true when we -// actually care about the method at this time (ie active frames had locals changed). For now we -// just assume that if anything has changed any frame's locals we care about all methods. If nothing -// has we only care about methods with active breakpoints on them. In the future we should probably -// rewrite all of this to instead do this at the ShadowFrame or thread granularity. -bool JvmtiMethodInspectionCallback::IsMethodBeingInspected(art::ArtMethod* method) { - // Non-java-debuggable runtimes we need to assume that any method might not be debuggable and - // therefore potentially being inspected (due to inlines). If we are debuggable we rely hard on - // inlining not being done since we don't keep track of which methods get inlined where and simply - // look to see if the method is breakpointed. - return !art::Runtime::Current()->IsJavaDebuggable() || - manager_->HaveLocalsChanged() || - manager_->MethodHasBreakpoints(method); +// actually care about the method (i.e. had locals changed, have breakpoints, etc.). For now though +// we can just assume that we care we are loaded at all. +// +// Even if we don't keep track of this at the method level we might want to keep track of it at the +// level of enabled capabilities. +bool JvmtiMethodInspectionCallback::IsMethodBeingInspected( + art::ArtMethod* method ATTRIBUTE_UNUSED) { + return true; } bool JvmtiMethodInspectionCallback::IsMethodSafeToJit(art::ArtMethod* method) { @@ -79,10 +75,7 @@ DeoptManager::DeoptManager() performing_deoptimization_(false), global_deopt_count_(0), deopter_count_(0), - breakpoint_status_lock_("JVMTI_BreakpointStatusLock", - static_cast(art::LockLevel::kAbortLock + 1)), - inspection_callback_(this), - set_local_variable_called_(false) { } + inspection_callback_(this) { } void DeoptManager::Setup() { art::ScopedThreadStateChange stsc(art::Thread::Current(), @@ -128,11 +121,14 @@ void DeoptManager::FinishSetup() { } bool DeoptManager::MethodHasBreakpoints(art::ArtMethod* method) { - art::MutexLock lk(art::Thread::Current(), breakpoint_status_lock_); + art::MutexLock lk(art::Thread::Current(), deoptimization_status_lock_); return MethodHasBreakpointsLocked(method); } bool DeoptManager::MethodHasBreakpointsLocked(art::ArtMethod* method) { + if (deopter_count_ == 0) { + return false; + } auto elem = breakpoint_status_.find(method); return elem != breakpoint_status_.end() && elem->second != 0; } @@ -162,23 +158,18 @@ void DeoptManager::AddMethodBreakpoint(art::ArtMethod* method) { art::ScopedThreadSuspension sts(self, art::kSuspended); deoptimization_status_lock_.ExclusiveLock(self); - { - breakpoint_status_lock_.ExclusiveLock(self); - - DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; - - if (MethodHasBreakpointsLocked(method)) { - // Don't need to do anything extra. - breakpoint_status_[method]++; - // Another thread might be deoptimizing the very method we just added new breakpoints for. - // Wait for any deopts to finish before moving on. - breakpoint_status_lock_.ExclusiveUnlock(self); - WaitForDeoptimizationToFinish(self); - return; - } - breakpoint_status_[method] = 1; - breakpoint_status_lock_.ExclusiveUnlock(self); + + DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; + + if (MethodHasBreakpointsLocked(method)) { + // Don't need to do anything extra. + breakpoint_status_[method]++; + // Another thread might be deoptimizing the very method we just added new breakpoints for. Wait + // for any deopts to finish before moving on. + WaitForDeoptimizationToFinish(self); + return; } + breakpoint_status_[method] = 1; auto instrumentation = art::Runtime::Current()->GetInstrumentation(); if (instrumentation->IsForcedInterpretOnly()) { // We are already interpreting everything so no need to do anything. @@ -205,22 +196,17 @@ void DeoptManager::RemoveMethodBreakpoint(art::ArtMethod* method) { // need but since that is very heavy we will instead just use a condition variable to make sure we // don't race with ourselves. deoptimization_status_lock_.ExclusiveLock(self); - bool is_last_breakpoint; - { - art::MutexLock mu(self, breakpoint_status_lock_); - - DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; - DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without " - << "breakpoints present!"; - breakpoint_status_[method] -= 1; - is_last_breakpoint = (breakpoint_status_[method] == 0); - } + + DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; + DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without " + << "breakpoints present!"; auto instrumentation = art::Runtime::Current()->GetInstrumentation(); + breakpoint_status_[method] -= 1; if (UNLIKELY(instrumentation->IsForcedInterpretOnly())) { // We don't need to do anything since we are interpreting everything anyway. deoptimization_status_lock_.ExclusiveUnlock(self); return; - } else if (is_last_breakpoint) { + } else if (breakpoint_status_[method] == 0) { if (UNLIKELY(is_default)) { RemoveDeoptimizeAllMethodsLocked(self); } else { diff --git a/openjdkjvmti/deopt_manager.h b/openjdkjvmti/deopt_manager.h index a38690c49e..a495b6835c 100644 --- a/openjdkjvmti/deopt_manager.h +++ b/openjdkjvmti/deopt_manager.h @@ -32,7 +32,6 @@ #ifndef ART_OPENJDKJVMTI_DEOPT_MANAGER_H_ #define ART_OPENJDKJVMTI_DEOPT_MANAGER_H_ -#include #include #include "jni.h" @@ -108,17 +107,9 @@ class DeoptManager { static DeoptManager* Get(); - bool HaveLocalsChanged() const { - return set_local_variable_called_.load(); - } - - void SetLocalsUpdated() { - set_local_variable_called_.store(true); - } - private: bool MethodHasBreakpointsLocked(art::ArtMethod* method) - REQUIRES(breakpoint_status_lock_); + REQUIRES(deoptimization_status_lock_); // Wait until nothing is currently in the middle of deoptimizing/undeoptimizing something. This is // needed to ensure that everything is synchronized since threads need to drop the @@ -165,20 +156,13 @@ class DeoptManager { // Number of users of deoptimization there currently are. uint32_t deopter_count_ GUARDED_BY(deoptimization_status_lock_); - // A mutex that just protects the breakpoint-status map. This mutex should always be at the - // bottom of the lock hierarchy. Nothing more should be locked if we hold this. - art::Mutex breakpoint_status_lock_ ACQUIRED_BEFORE(art::Locks::abort_lock_); // A map from methods to the number of breakpoints in them from all envs. std::unordered_map breakpoint_status_ - GUARDED_BY(breakpoint_status_lock_); + GUARDED_BY(deoptimization_status_lock_); // The MethodInspectionCallback we use to tell the runtime if we care about particular methods. JvmtiMethodInspectionCallback inspection_callback_; - // Set to true if anything calls SetLocalVariables on any thread since we need to be careful about - // OSR after this. - std::atomic set_local_variable_called_; - // Helper for setting up/tearing-down for deoptimization. friend class ScopedDeoptimizationContext; }; diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index b83310dc85..bf2e6cd104 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -915,9 +915,6 @@ jvmtiError MethodUtil::SetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED, if (depth < 0) { return ERR(ILLEGAL_ARGUMENT); } - // Make sure that we know not to do any OSR anymore. - // TODO We should really keep track of this at the Frame granularity. - DeoptManager::Get()->SetLocalsUpdated(); art::Thread* self = art::Thread::Current(); // Suspend JIT since it can get confused if we deoptimize methods getting jitted. art::jit::ScopedJitSuspend suspend_jit; diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index ec3e10e2f8..84a148f21c 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -139,14 +139,10 @@ static void UpdateEntrypoints(ArtMethod* method, const void* quick_code) bool Instrumentation::NeedDebugVersionFor(ArtMethod* method) const REQUIRES_SHARED(Locks::mutator_lock_) { - art::Runtime* runtime = Runtime::Current(); - return runtime->IsJavaDebuggable() && + return Runtime::Current()->IsJavaDebuggable() && !method->IsNative() && !method->IsProxyMethod() && - // If we don't have a jit this can push us to the pre-compiled version of methods which is - // not something we want since we are debuggable. - (UNLIKELY(runtime->GetJit() == nullptr) || - runtime->GetRuntimeCallbacks()->IsMethodBeingInspected(method)); + Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method); } void Instrumentation::InstallStubsForMethod(ArtMethod* method) { diff --git a/test/1935-get-set-current-frame-jit/expected.txt b/test/1935-get-set-current-frame-jit/expected.txt index a685891775..cdb8f6a825 100644 --- a/test/1935-get-set-current-frame-jit/expected.txt +++ b/test/1935-get-set-current-frame-jit/expected.txt @@ -1,5 +1,7 @@ JNI_OnLoad called From GetLocalInt(), value is 42 +isInOsrCode? false Value is '42' Setting TARGET to 1337 +isInOsrCode? false Value is '1337' diff --git a/test/1935-get-set-current-frame-jit/src/Main.java b/test/1935-get-set-current-frame-jit/src/Main.java index 671493bbaa..714a98aaf3 100644 --- a/test/1935-get-set-current-frame-jit/src/Main.java +++ b/test/1935-get-set-current-frame-jit/src/Main.java @@ -21,7 +21,6 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.nio.ByteBuffer; -import java.time.Instant; import java.util.concurrent.Semaphore; import java.util.Arrays; import java.util.Collection; @@ -50,11 +49,9 @@ public class Main { public static class IntRunner implements Runnable { private volatile boolean continueBusyLoop; private volatile boolean inBusyLoop; - private final boolean expectOsr; - public IntRunner(boolean expectOsr) { + public IntRunner() { this.continueBusyLoop = true; this.inBusyLoop = false; - this.expectOsr = expectOsr; } public void run() { int TARGET = 42; @@ -62,22 +59,14 @@ public class Main { while (continueBusyLoop) { inBusyLoop = true; } - // Wait up to 300 seconds for OSR to kick in if we expect it. If we don't give up after only - // 3 seconds. - Instant osrDeadline = Instant.now().plusSeconds(expectOsr ? 600 : 3); - do { + int i = 0; + while (Main.isInterpreted() && i < 10000) { Main.ensureJitCompiled(IntRunner.class, "run"); - } while (hasJit() && !Main.isInOsrCode("run") && osrDeadline.compareTo(Instant.now()) > 0); - // We shouldn't be doing OSR since we are using JVMTI and the set prevents OSR. - // Set local will also push us to interpreter but the get local may remain in compiled code. - if (hasJit()) { - boolean inOsr = Main.isInOsrCode("run"); - if (expectOsr && !inOsr) { - throw new Error("Expected to be in OSR but was not."); - } else if (!expectOsr && inOsr) { - throw new Error("Expected not to be in OSR but was."); - } + i++; } + // We shouldn't be doing OSR since we are using JVMTI and the get/set prevents OSR. + // Set local will also push us to interpreter but the get local may remain in compiled code. + System.out.println("isInOsrCode? " + (hasJit() && Main.isInOsrCode("run"))); reportValue(TARGET); } public void waitForBusyLoopStart() { while (!inBusyLoop) {} } @@ -89,7 +78,7 @@ public class Main { public static void runGet() throws Exception { Method target = IntRunner.class.getDeclaredMethod("run"); // Get Int - IntRunner int_runner = new IntRunner(true); + IntRunner int_runner = new IntRunner(); Thread target_get = new Thread(int_runner, "GetLocalInt - Target"); target_get.start(); int_runner.waitForBusyLoopStart(); @@ -119,7 +108,7 @@ public class Main { public static void runSet() throws Exception { Method target = IntRunner.class.getDeclaredMethod("run"); // Set Int - IntRunner int_runner = new IntRunner(false); + IntRunner int_runner = new IntRunner(); Thread target_set = new Thread(int_runner, "SetLocalInt - Target"); target_set.start(); int_runner.waitForBusyLoopStart(); -- GitLab From 6ee497188b0e8c7bcf5126cefad04090f956616a Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 30 Mar 2018 14:39:05 +0000 Subject: [PATCH 181/749] Revert^4 "Add an option to disable native stack dumping on SIGQUIT." Bug: 74121887 Still failing :( This reverts commit 642e9d8249be5aff68022cabdc8ba576a57ff8d6. Change-Id: I603ca9fdd2d8f2f759527130b3288efe5b23b5c3 --- runtime/parsed_options.cc | 5 +++++ runtime/runtime.cc | 2 ++ runtime/runtime.h | 7 +++++++ runtime/runtime_common.cc | 3 ++- runtime/runtime_options.def | 1 + runtime/thread.cc | 12 ++++++++---- runtime/thread.h | 2 ++ runtime/thread_list.cc | 21 +++++++++++++-------- runtime/thread_list.h | 2 +- test/etc/run-test-jar | 4 ++++ 10 files changed, 45 insertions(+), 14 deletions(-) diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 470287b449..5518eb2c49 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -161,6 +161,10 @@ std::unique_ptr ParsedOptions::MakeParser(bool ignore_unrecognize .Define({"-XX:EnableHSpaceCompactForOOM", "-XX:DisableHSpaceCompactForOOM"}) .WithValues({true, false}) .IntoKey(M::EnableHSpaceCompactForOOM) + .Define("-XX:DumpNativeStackOnSigQuit:_") + .WithType() + .WithValueMap({{"false", false}, {"true", true}}) + .IntoKey(M::DumpNativeStackOnSigQuit) .Define("-XX:MadviseRandomAccess:_") .WithType() .WithValueMap({{"false", false}, {"true", true}}) @@ -731,6 +735,7 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -XX:BackgroundGC=none\n"); UsageMessage(stream, " -XX:LargeObjectSpace={disabled,map,freelist}\n"); UsageMessage(stream, " -XX:LargeObjectThreshold=N\n"); + UsageMessage(stream, " -XX:DumpNativeStackOnSigQuit=booleanvalue\n"); UsageMessage(stream, " -XX:MadviseRandomAccess:booleanvalue\n"); UsageMessage(stream, " -XX:SlowDebug={false,true}\n"); UsageMessage(stream, " -Xmethod-trace\n"); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index bb76e61122..9a626bab00 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -271,6 +271,7 @@ Runtime::Runtime() pending_hidden_api_warning_(false), dedupe_hidden_api_warnings_(true), always_set_hidden_api_warning_flag_(false), + dump_native_stack_on_sig_quit_(true), pruned_dalvik_cache_(false), // Initially assume we perceive jank in case the process state is never updated. process_state_(kProcessStateJankPerceptible), @@ -1152,6 +1153,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { is_explicit_gc_disabled_ = runtime_options.Exists(Opt::DisableExplicitGC); dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::Dex2Oat); image_dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::ImageDex2Oat); + dump_native_stack_on_sig_quit_ = runtime_options.GetOrDefault(Opt::DumpNativeStackOnSigQuit); vfprintf_ = runtime_options.GetOrDefault(Opt::HookVfprintf); exit_ = runtime_options.GetOrDefault(Opt::HookExit); diff --git a/runtime/runtime.h b/runtime/runtime.h index 7d7cbafe23..dba31b2939 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -655,6 +655,10 @@ class Runtime { safe_mode_ = mode; } + bool GetDumpNativeStackOnSigQuit() const { + return dump_native_stack_on_sig_quit_; + } + bool GetPrunedDalvikCache() const { return pruned_dalvik_cache_; } @@ -1005,6 +1009,9 @@ class Runtime { // when there is a warning. This is only used for testing. bool always_set_hidden_api_warning_flag_; + // Whether threads should dump their native stack on SIGQUIT. + bool dump_native_stack_on_sig_quit_; + // Whether the dalvik cache was pruned when initializing the runtime. bool pruned_dalvik_cache_; diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc index 41bfb58d93..59af9187f9 100644 --- a/runtime/runtime_common.cc +++ b/runtime/runtime_common.cc @@ -41,6 +41,7 @@ namespace art { using android::base::StringPrintf; static constexpr bool kUseSigRTTimeout = true; +static constexpr bool kDumpNativeStackOnTimeout = true; const char* GetSignalName(int signal_number) { switch (signal_number) { @@ -440,7 +441,7 @@ void HandleUnexpectedSignalCommon(int signal_number, // Special timeout signal. Try to dump all threads. // Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts // are of value here. - runtime->GetThreadList()->Dump(std::cerr); + runtime->GetThreadList()->Dump(std::cerr, kDumpNativeStackOnTimeout); std::cerr << std::endl; } diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index dcb1335023..4121ad69ed 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -70,6 +70,7 @@ RUNTIME_OPTIONS_KEY (Unit, LowMemoryMode) RUNTIME_OPTIONS_KEY (bool, UseTLAB, (kUseTlab || kUseReadBarrier)) RUNTIME_OPTIONS_KEY (bool, EnableHSpaceCompactForOOM, true) RUNTIME_OPTIONS_KEY (bool, UseJitCompilation, false) +RUNTIME_OPTIONS_KEY (bool, DumpNativeStackOnSigQuit, true) RUNTIME_OPTIONS_KEY (bool, MadviseRandomAccess, false) RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold) RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold) diff --git a/runtime/thread.cc b/runtime/thread.cc index 50cf9e0bc4..b13d8ec42a 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1161,9 +1161,10 @@ void Thread::ShortDump(std::ostream& os) const { << "]"; } -void Thread::Dump(std::ostream& os, BacktraceMap* backtrace_map, bool force_dump_stack) const { +void Thread::Dump(std::ostream& os, bool dump_native_stack, BacktraceMap* backtrace_map, + bool force_dump_stack) const { DumpState(os); - DumpStack(os, backtrace_map, force_dump_stack); + DumpStack(os, dump_native_stack, backtrace_map, force_dump_stack); } mirror::String* Thread::GetThreadName() const { @@ -1967,7 +1968,10 @@ void Thread::DumpJavaStack(std::ostream& os, bool check_suspended, bool dump_loc } } -void Thread::DumpStack(std::ostream& os, BacktraceMap* backtrace_map, bool force_dump_stack) const { +void Thread::DumpStack(std::ostream& os, + bool dump_native_stack, + BacktraceMap* backtrace_map, + bool force_dump_stack) const { // TODO: we call this code when dying but may not have suspended the thread ourself. The // IsSuspended check is therefore racy with the use for dumping (normally we inhibit // the race with the thread_suspend_count_lock_). @@ -1980,7 +1984,7 @@ void Thread::DumpStack(std::ostream& os, BacktraceMap* backtrace_map, bool force } if (safe_to_dump || force_dump_stack) { // If we're currently in native code, dump that stack before dumping the managed stack. - if (dump_for_abort || force_dump_stack || ShouldShowNativeStack(this)) { + if (dump_native_stack && (dump_for_abort || force_dump_stack || ShouldShowNativeStack(this))) { DumpKernelStack(os, GetTid(), " kernel: ", false); ArtMethod* method = GetCurrentMethod(nullptr, diff --git a/runtime/thread.h b/runtime/thread.h index af1401ee93..22b77eea64 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -207,6 +207,7 @@ class Thread { // Dumps the detailed thread state and the thread stack (used for SIGQUIT). void Dump(std::ostream& os, + bool dump_native_stack = true, BacktraceMap* backtrace_map = nullptr, bool force_dump_stack = false) const REQUIRES(!Locks::thread_suspend_count_lock_) @@ -1317,6 +1318,7 @@ class Thread { void DumpState(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_); void DumpStack(std::ostream& os, + bool dump_native_stack = true, BacktraceMap* backtrace_map = nullptr, bool force_dump_stack = false) const REQUIRES(!Locks::thread_suspend_count_lock_) diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index ee683992ba..44af867d60 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -152,8 +152,9 @@ void ThreadList::DumpForSigQuit(std::ostream& os) { suspend_all_historam_.PrintConfidenceIntervals(os, 0.99, data); // Dump time to suspend. } } - Dump(os); - DumpUnattachedThreads(os, kDumpUnattachedThreadNativeStackForSigQuit); + bool dump_native_stack = Runtime::Current()->GetDumpNativeStackOnSigQuit(); + Dump(os, dump_native_stack); + DumpUnattachedThreads(os, dump_native_stack && kDumpUnattachedThreadNativeStackForSigQuit); } static void DumpUnattachedThread(std::ostream& os, pid_t tid, bool dump_native_stack) @@ -200,10 +201,11 @@ static constexpr uint32_t kDumpWaitTimeout = kIsTargetBuild ? 100000 : 20000; // A closure used by Thread::Dump. class DumpCheckpoint FINAL : public Closure { public: - explicit DumpCheckpoint(std::ostream* os) + DumpCheckpoint(std::ostream* os, bool dump_native_stack) : os_(os), barrier_(0), - backtrace_map_(BacktraceMap::Create(getpid())) { + backtrace_map_(dump_native_stack ? BacktraceMap::Create(getpid()) : nullptr), + dump_native_stack_(dump_native_stack) { if (backtrace_map_ != nullptr) { backtrace_map_->SetSuffixesToIgnore(std::vector { "oat", "odex" }); } @@ -217,7 +219,7 @@ class DumpCheckpoint FINAL : public Closure { std::ostringstream local_os; { ScopedObjectAccess soa(self); - thread->Dump(local_os, backtrace_map_.get()); + thread->Dump(local_os, dump_native_stack_, backtrace_map_.get()); } { // Use the logging lock to ensure serialization when writing to the common ostream. @@ -245,16 +247,18 @@ class DumpCheckpoint FINAL : public Closure { Barrier barrier_; // A backtrace map, so that all threads use a shared info and don't reacquire/parse separately. std::unique_ptr backtrace_map_; + // Whether we should dump the native stack. + const bool dump_native_stack_; }; -void ThreadList::Dump(std::ostream& os) { +void ThreadList::Dump(std::ostream& os, bool dump_native_stack) { Thread* self = Thread::Current(); { MutexLock mu(self, *Locks::thread_list_lock_); os << "DALVIK THREADS (" << list_.size() << "):\n"; } if (self != nullptr) { - DumpCheckpoint checkpoint(&os); + DumpCheckpoint checkpoint(&os, dump_native_stack); size_t threads_running_checkpoint; { // Use SOA to prevent deadlocks if multiple threads are calling Dump() at the same time. @@ -265,7 +269,7 @@ void ThreadList::Dump(std::ostream& os) { checkpoint.WaitForThreadsToRunThroughCheckpoint(threads_running_checkpoint); } } else { - DumpUnattachedThreads(os, /* dump_native_stack */ true); + DumpUnattachedThreads(os, dump_native_stack); } } @@ -487,6 +491,7 @@ void ThreadList::RunEmptyCheckpoint() { // Found a runnable thread that hasn't responded to the empty checkpoint request. // Assume it's stuck and safe to dump its stack. thread->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT), + /*dump_native_stack*/ true, /*backtrace_map*/ nullptr, /*force_dump_stack*/ true); } diff --git a/runtime/thread_list.h b/runtime/thread_list.h index 09b10d2ad3..895c1a41ce 100644 --- a/runtime/thread_list.h +++ b/runtime/thread_list.h @@ -57,7 +57,7 @@ class ThreadList { void DumpForSigQuit(std::ostream& os) REQUIRES(!Locks::thread_list_lock_, !Locks::mutator_lock_); // For thread suspend timeout dumps. - void Dump(std::ostream& os) + void Dump(std::ostream& os, bool dump_native_stack = true) REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_); pid_t GetLockOwner(); // For SignalCatcher. diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 86adb733a9..e9127a8101 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -775,6 +775,9 @@ if [ "$HOST" = "n" ]; then TMP_DIR_OPTION="-Djava.io.tmpdir=/data/local/tmp" fi +# We set DumpNativeStackOnSigQuit to false to avoid stressing libunwind. +# b/27185632 +# b/24664297 dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \ $GDB_ARGS \ $FLAGS \ @@ -789,6 +792,7 @@ dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \ $DEBUGGER_OPTS \ $DALVIKVM_BOOT_OPT \ $TMP_DIR_OPTION \ + -XX:DumpNativeStackOnSigQuit:false \ -cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX $MAIN $ARGS" # Remove whitespace. -- GitLab From c6eec4bed1b5fc5f541929294f3072e769b30ac8 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Thu, 29 Mar 2018 17:22:00 -0700 Subject: [PATCH 182/749] Eliminate redundant abs on zero extension Bug: b/74026074 Test: test-art-host,target Change-Id: Ic97c866e3843cd172dfae9652104efe33fced8e5 --- compiler/optimizing/data_type.h | 6 + compiler/optimizing/instruction_simplifier.cc | 13 ++- test/645-checker-abs-simd/src/Main.java | 5 +- test/681-checker-abs/src/Main.java | 105 ++++++++++++++++++ 4 files changed, 124 insertions(+), 5 deletions(-) diff --git a/compiler/optimizing/data_type.h b/compiler/optimizing/data_type.h index 4a6c91459f..be26e67af3 100644 --- a/compiler/optimizing/data_type.h +++ b/compiler/optimizing/data_type.h @@ -210,6 +210,12 @@ class DataType { static bool IsTypeConversionImplicit(Type input_type, Type result_type); static bool IsTypeConversionImplicit(int64_t value, Type result_type); + static bool IsZeroExtension(Type input_type, Type result_type) { + return IsIntOrLongType(result_type) && + IsUnsignedType(input_type) && + Size(result_type) > Size(input_type); + } + static const char* PrettyDescriptor(Type type); private: diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 676fe6bcb7..da3703726d 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -67,7 +67,6 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { bool TryCombineVecMultiplyAccumulate(HVecMul* mul); void VisitShift(HBinaryOperation* shift); - void VisitEqual(HEqual* equal) OVERRIDE; void VisitNotEqual(HNotEqual* equal) OVERRIDE; void VisitBooleanNot(HBooleanNot* bool_not) OVERRIDE; @@ -78,6 +77,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void VisitNullCheck(HNullCheck* instruction) OVERRIDE; void VisitArrayLength(HArrayLength* instruction) OVERRIDE; void VisitCheckCast(HCheckCast* instruction) OVERRIDE; + void VisitAbs(HAbs* instruction) OVERRIDE; void VisitAdd(HAdd* instruction) OVERRIDE; void VisitAnd(HAnd* instruction) OVERRIDE; void VisitCondition(HCondition* instruction) OVERRIDE; @@ -1241,6 +1241,17 @@ void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruct } } +void InstructionSimplifierVisitor::VisitAbs(HAbs* instruction) { + HInstruction* input = instruction->GetInput(); + if (DataType::IsZeroExtension(input->GetType(), instruction->GetResultType())) { + // Zero extension from narrow to wide can never set sign bit in the wider + // operand, making the subsequent Abs redundant (e.g., abs(b & 0xff) for byte b). + instruction->ReplaceWith(input); + instruction->GetBlock()->RemoveInstruction(instruction); + RecordSimplification(); + } +} + void InstructionSimplifierVisitor::VisitAdd(HAdd* instruction) { HConstant* input_cst = instruction->GetConstantRight(); HInstruction* input_other = instruction->GetLeastConstantLeft(); diff --git a/test/645-checker-abs-simd/src/Main.java b/test/645-checker-abs-simd/src/Main.java index 870a403ff5..819304a9e9 100644 --- a/test/645-checker-abs-simd/src/Main.java +++ b/test/645-checker-abs-simd/src/Main.java @@ -48,10 +48,7 @@ public class Main { } /// CHECK-START: void Main.doitChar(char[]) loop_optimization (before) - /// CHECK-DAG: Phi loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: Abs loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-NOT: Abs // /// CHECK-START: void Main.doitChar(char[]) loop_optimization (after) /// CHECK-NOT: VecAbs diff --git a/test/681-checker-abs/src/Main.java b/test/681-checker-abs/src/Main.java index 8064b1dac1..d1ba7c6851 100644 --- a/test/681-checker-abs/src/Main.java +++ b/test/681-checker-abs/src/Main.java @@ -19,6 +19,38 @@ */ public class Main { + /// CHECK-START: int Main.absI(int) instruction_simplifier (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.absI(int) instruction_simplifier (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> Abs [<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.absI(int) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + public static int absI(int a) { + return Math.abs(a); + } + + /// CHECK-START: long Main.absL(long) instruction_simplifier (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsLong + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: long Main.absL(long) instruction_simplifier (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> Abs [<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: long Main.absL(long) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + public static long absL(long a) { + return Math.abs(a); + } + /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_inlining (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 0 @@ -152,7 +184,74 @@ public class Main { return a >= 0 ? a : -a; } + // + // Nop zero extension. + // + + /// CHECK-START: int Main.zabs1(byte) instruction_simplifier (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 255 + /// CHECK-DAG: <> [<>,<>] + /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.zabs1(byte) instruction_simplifier (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> TypeConversion [<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.zabs1(byte) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: Abs + public static int zabs1(byte a) { + return Math.abs(a & 0xff); + } + + /// CHECK-START: int Main.zabs2(short) instruction_simplifier (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 65535 + /// CHECK-DAG: <> [<>,<>] + /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.zabs2(short) instruction_simplifier (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> TypeConversion [<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.zabs2(short) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: Abs + public static int zabs2(short a) { + return Math.abs(a & 0xffff); + } + + /// CHECK-START: int Main.zabs3(char) instruction_simplifier (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.zabs3(char) instruction_simplifier (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> Abs [<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.zabs3(char) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.zabs3(char) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: Abs + public static int zabs3(char a) { + return Math.abs(a); + } + public static void main(String[] args) { + expectEquals(10, absI(-10)); + expectEquals(20, absI(20)); + expectEquals(10L, absL(-10L)); + expectEquals(20L, absL(20L)); expectEquals(10, abs1(-10)); expectEquals(20, abs1(20)); expectEquals(10, abs2(-10)); @@ -167,6 +266,12 @@ public class Main { expectEquals(20, abs6((byte) 20)); expectEquals(10L, abs7(-10L)); expectEquals(20L, abs7(20L)); + expectEquals(1, zabs1((byte) 1)); + expectEquals(0xff, zabs1((byte) -1)); + expectEquals(1, zabs2((short) 1)); + expectEquals(0xffff, zabs2((short) -1)); + expectEquals(1, zabs3((char) 1)); + expectEquals(0xffff, zabs3((char) -1)); System.out.println("passed"); } -- GitLab From 1d746def1cca72979fa18ce900b248502f7cdcef Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Wed, 28 Mar 2018 16:30:02 -0700 Subject: [PATCH 183/749] Recognize nested MIN-MAX operations. Rationale: Prior to this CL, select optimizer and instruction simplifier were unable to deal with more than one select. This CLs improves MIN-MAX recognition by allowing select diamonds to nest deeper and by recognizing constant clipping operations. This yields better optimizable code, as shown with more saturation idioms. Bug: b/74026074 Test: test-art-host,target Change-Id: I8a616a19475f1ae87c2b5210afc76b14265bd571 --- compiler/optimizing/instruction_simplifier.cc | 63 ++++++--- compiler/optimizing/select_generator.cc | 17 ++- .../678-checker-simd-saturation/src/Main.java | 23 +++- test/679-checker-minmax/src/Main.java | 130 ++++++++++++++++++ 4 files changed, 208 insertions(+), 25 deletions(-) diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 676fe6bcb7..7d0add3833 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -903,6 +903,30 @@ static bool AreLowerPrecisionArgs(DataType::Type to_type, HInstruction* a, HInst to_type == DataType::Type::kInt64); } +// Returns an acceptable substitution for "a" on the select +// construct "a b ? c : .." during MIN/MAX recognition. +static HInstruction* AllowInMinMax(IfCondition cmp, + HInstruction* a, + HInstruction* b, + HInstruction* c) { + int64_t value = 0; + if (IsInt64AndGet(b, /*out*/ &value) && + (((cmp == kCondLT || cmp == kCondLE) && c->IsMax()) || + ((cmp == kCondGT || cmp == kCondGE) && c->IsMin()))) { + HConstant* other = c->AsBinaryOperation()->GetConstantRight(); + if (other != nullptr && a == c->AsBinaryOperation()->GetLeastConstantLeft()) { + int64_t other_value = Int64FromConstant(other); + bool is_max = (cmp == kCondLT || cmp == kCondLE); + // Allow the max for a < 100 ? max(a, -100) : .. + // or the min for a > -100 ? min(a, 100) : .. + if (is_max ? (value >= other_value) : (value <= other_value)) { + return c; + } + } + } + return nullptr; +} + void InstructionSimplifierVisitor::VisitSelect(HSelect* select) { HInstruction* replace_with = nullptr; HInstruction* condition = select->GetCondition(); @@ -946,9 +970,17 @@ void InstructionSimplifierVisitor::VisitSelect(HSelect* select) { DataType::Type t_type = true_value->GetType(); DataType::Type f_type = false_value->GetType(); // Here we have a b ? true_value : false_value. - // Test if both values are compatible integral types (resulting - // MIN/MAX/ABS type will be int or long, like the condition). + // Test if both values are compatible integral types (resulting MIN/MAX/ABS + // type will be int or long, like the condition). Replacements are general, + // but assume conditions prefer constants on the right. if (DataType::IsIntegralType(t_type) && DataType::Kind(t_type) == DataType::Kind(f_type)) { + // Allow a < 100 ? max(a, -100) : .. + // or a > -100 ? min(a, 100) : .. + // to use min/max instead of a to detect nested min/max expressions. + HInstruction* new_a = AllowInMinMax(cmp, a, b, true_value); + if (new_a != nullptr) { + a = new_a; + } // Try to replace typical integral MIN/MAX/ABS constructs. if ((cmp == kCondLT || cmp == kCondLE || cmp == kCondGT || cmp == kCondGE) && ((a == true_value && b == false_value) || @@ -957,19 +989,16 @@ void InstructionSimplifierVisitor::VisitSelect(HSelect* select) { // 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))) { - // Found a < 0 ? -a : a which can be replaced by ABS(a). - replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), false_value, select); - } - } else if (false_value->IsNeg()) { - HInstruction* negated = false_value->InputAt(0); - if ((cmp == kCondGT || cmp == kCondGE) && - (a == true_value && a == negated && IsInt64Value(b, 0))) { - // Found a > 0 ? a : -a which can be replaced by ABS(a). - replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), true_value, select); + } else if (((cmp == kCondLT || cmp == kCondLE) && true_value->IsNeg()) || + ((cmp == kCondGT || cmp == kCondGE) && false_value->IsNeg())) { + bool negLeft = (cmp == kCondLT || cmp == kCondLE); + HInstruction* the_negated = negLeft ? true_value->InputAt(0) : false_value->InputAt(0); + HInstruction* not_negated = negLeft ? false_value : true_value; + if (a == the_negated && a == not_negated && IsInt64Value(b, 0)) { + // Found a < 0 ? -a : a + // or a > 0 ? a : -a + // which can be replaced by ABS(a). + replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), a, select); } } else if (true_value->IsSub() && false_value->IsSub()) { HInstruction* true_sub1 = true_value->InputAt(0); @@ -981,8 +1010,8 @@ void InstructionSimplifierVisitor::VisitSelect(HSelect* select) { ((cmp == kCondLT || cmp == kCondLE) && (a == true_sub2 && b == true_sub1 && a == false_sub1 && b == false_sub2))) && AreLowerPrecisionArgs(t_type, a, b)) { - // Found a > b ? a - b : b - a or - // a < b ? b - a : a - b + // Found a > b ? a - b : b - a + // or a < b ? b - a : a - b // which can be replaced by ABS(a - b) for lower precision operands a, b. replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), true_value, select); } diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc index 66e51421ca..3f52bdd13c 100644 --- a/compiler/optimizing/select_generator.cc +++ b/compiler/optimizing/select_generator.cc @@ -43,12 +43,16 @@ static bool IsSimpleBlock(HBasicBlock* block) { for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* instruction = it.Current(); if (instruction->IsControlFlow()) { - if (num_instructions > kMaxInstructionsInBranch) { - return false; - } return instruction->IsGoto() || instruction->IsReturn(); } else if (instruction->CanBeMoved() && !instruction->HasSideEffects()) { - num_instructions++; + if (instruction->IsSelect() && + instruction->AsSelect()->GetCondition()->GetBlock() == block) { + // Count one HCondition and HSelect in the same block as a single instruction. + // This enables finding nested selects. + continue; + } else if (++num_instructions > kMaxInstructionsInBranch) { + return false; // bail as soon as we exceed number of allowed instructions + } } else { return false; } @@ -97,6 +101,7 @@ void HSelectGenerator::Run() { HBasicBlock* true_block = if_instruction->IfTrueSuccessor(); HBasicBlock* false_block = if_instruction->IfFalseSuccessor(); DCHECK_NE(true_block, false_block); + if (!IsSimpleBlock(true_block) || !IsSimpleBlock(false_block) || !BlocksMergeTogether(true_block, false_block)) { @@ -107,10 +112,10 @@ void HSelectGenerator::Run() { // If the branches are not empty, move instructions in front of the If. // TODO(dbrazdil): This puts an instruction between If and its condition. // Implement moving of conditions to first users if possible. - if (!true_block->IsSingleGoto() && !true_block->IsSingleReturn()) { + while (!true_block->IsSingleGoto() && !true_block->IsSingleReturn()) { true_block->GetFirstInstruction()->MoveBefore(if_instruction); } - if (!false_block->IsSingleGoto() && !false_block->IsSingleReturn()) { + while (!false_block->IsSingleGoto() && !false_block->IsSingleReturn()) { false_block->GetFirstInstruction()->MoveBefore(if_instruction); } DCHECK(true_block->IsSingleGoto() || true_block->IsSingleReturn()); diff --git a/test/678-checker-simd-saturation/src/Main.java b/test/678-checker-simd-saturation/src/Main.java index decc691789..7a22ca175d 100644 --- a/test/678-checker-simd-saturation/src/Main.java +++ b/test/678-checker-simd-saturation/src/Main.java @@ -397,7 +397,22 @@ public class Main { } } - // TODO: recognize the more common if-else too. + /// CHECK-START: void Main.satAlt2(short[], short[], short[]) loop_optimization (before) + /// CHECK-DAG: <> IntConstant -32768 loop:none + /// CHECK-DAG: <> IntConstant 32767 loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satAlt2(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none 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++) { @@ -411,7 +426,11 @@ public class Main { } } - // TODO: recognize conditional too. + /// CHECK-START-{ARM,ARM64}: void Main.satAlt3(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none 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++) { diff --git a/test/679-checker-minmax/src/Main.java b/test/679-checker-minmax/src/Main.java index 38085bbd7b..4f0261c0a3 100644 --- a/test/679-checker-minmax/src/Main.java +++ b/test/679-checker-minmax/src/Main.java @@ -19,6 +19,10 @@ */ public class Main { + // + // Different types. + // + /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_inlining (before) /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] @@ -229,7 +233,116 @@ public class Main { return a >= b ? a : b; } + + // + // Complications. + // + + // TODO: coming soon, under discussion + public static int min0(int[] a, int[] b) { + // Repeat of array references needs finding the common subexpressions + // prior to doing the select and min/max recognition. + return a[0] <= b[0] ? a[0] : b[0]; + } + + // TODO: coming soon, under discussion + public static int max0(int[] a, int[] b) { + // Repeat of array references needs finding the common subexpressions + // prior to doing the select and min/max recognition. + return a[0] >= b[0] ? a[0] : b[0]; + } + + /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 100 + /// CHECK-DAG: <> IntConstant -100 + /// CHECK-DAG: <> LessThanOrEqual [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 100 + /// CHECK-DAG: <> IntConstant -100 + /// CHECK-DAG: <> Min [<>,<>] + /// CHECK-DAG: <> Max [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int minmax1(int x) { + // Simple if-if gives clean select sequence. + if (x > 100) { + x = 100; + } + if (x < -100) { + x = -100; + } + return x; + } + + /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 100 + /// CHECK-DAG: <> IntConstant -100 + /// CHECK-DAG: <> LessThanOrEqual [<>,<>] + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 100 + /// CHECK-DAG: <> IntConstant -100 + /// CHECK-DAG: <> Max [<>,<>] + /// CHECK-DAG: <> Min [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int minmax2(int x) { + // Simple if-else requires inspecting bounds of resulting selects. + if (x > 100) { + x = 100; + } else if (x < -100) { + x = -100; + } + return x; + } + + /// CHECK-START: int Main.minmax3(int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 100 + /// CHECK-DAG: <> IntConstant -100 + /// CHECK-DAG: <> Max [<>,<>] + /// CHECK-DAG: <> Min [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.minmax3(int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int minmax3(int x) { + return (x > 100) ? 100 : ((x < -100) ? -100 : x); + } + + /// CHECK-START: int Main.minmax4(int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 100 + /// CHECK-DAG: <> IntConstant -100 + /// CHECK-DAG: <> Min [<>,<>] + /// CHECK-DAG: <> Max [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.minmax4(int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int minmax4(int x) { + return (x < -100) ? -100 : ((x > 100) ? 100 : x); + } + public static void main(String[] args) { + // Types. expectEquals(10, min1(10, 20)); expectEquals(10, min2(10, 20)); expectEquals(10, min3(10, 20)); @@ -244,6 +357,23 @@ public class Main { expectEquals(20, max5((short) 10, (short) 20)); expectEquals(20, max6((byte) 10, (byte) 20)); expectEquals(20L, max7(10L, 20L)); + // Complications. + int[] a = { 10 }; + int[] b = { 20 }; + expectEquals(10, min0(a, b)); + expectEquals(20, max0(a, b)); + expectEquals(-100, minmax1(-200)); + expectEquals(10, minmax1(10)); + expectEquals(100, minmax1(200)); + expectEquals(-100, minmax2(-200)); + expectEquals(10, minmax2(10)); + expectEquals(100, minmax2(200)); + expectEquals(-100, minmax3(-200)); + expectEquals(10, minmax3(10)); + expectEquals(100, minmax3(200)); + expectEquals(-100, minmax4(-200)); + expectEquals(10, minmax4(10)); + expectEquals(100, minmax4(200)); System.out.println("passed"); } -- GitLab From 23be1464aab396f36f0183e635735cedf96d5607 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Fri, 30 Mar 2018 15:44:26 +0000 Subject: [PATCH 184/749] Revert^4 "Ensure that OSR still is possible with jvmti" This reverts commit ce2836aaf9b04a0ad23739b24adc4437b6443bd3. This unreverts commit b9ad26d1ed9146b89555d4333021f44eeb831f05. Reason for revert: Fixed issue with test 1935 causing flakiness. It was possible for the (rather small) jit-code-cache to fill up before we call ensureJitCompiled in test 1935. This call as a side-effect turns off jit garbage collection which means the jit is never able to compile the method and end up failing the test. We fixed this by removing the call to ensureJitComiled and increasing the jit-code-cache size to 32 megabytes. Test: ./test.py --host -j50 --all -t 993 Test: ./test.py --host Test: while ./test/run-test --host --jit 1935; do; done Test: while ./test/run-test --host --jit --jvmti-redefine-stress 1935; do; done Test: am start --attach-agent -n com.example.android.displayingbitmaps/.ui.ImageGridActivity Run blur filter. Bug: 76226464 Bug: 77306669 Change-Id: I9aaf9eaf6d240637567359cd72d4f172e5326511 --- openjdkjvmti/deopt_manager.cc | 74 +++++++++++-------- openjdkjvmti/deopt_manager.h | 20 ++++- openjdkjvmti/ti_method.cc | 3 + runtime/instrumentation.cc | 8 +- .../expected.txt | 2 - test/1935-get-set-current-frame-jit/run | 3 +- .../src/Main.java | 33 ++++++--- 7 files changed, 95 insertions(+), 48 deletions(-) diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc index 6d84ffa53f..380d95c545 100644 --- a/openjdkjvmti/deopt_manager.cc +++ b/openjdkjvmti/deopt_manager.cc @@ -53,14 +53,18 @@ namespace openjdkjvmti { // TODO We should make this much more selective in the future so we only return true when we -// actually care about the method (i.e. had locals changed, have breakpoints, etc.). For now though -// we can just assume that we care we are loaded at all. -// -// Even if we don't keep track of this at the method level we might want to keep track of it at the -// level of enabled capabilities. -bool JvmtiMethodInspectionCallback::IsMethodBeingInspected( - art::ArtMethod* method ATTRIBUTE_UNUSED) { - return true; +// actually care about the method at this time (ie active frames had locals changed). For now we +// just assume that if anything has changed any frame's locals we care about all methods. If nothing +// has we only care about methods with active breakpoints on them. In the future we should probably +// rewrite all of this to instead do this at the ShadowFrame or thread granularity. +bool JvmtiMethodInspectionCallback::IsMethodBeingInspected(art::ArtMethod* method) { + // Non-java-debuggable runtimes we need to assume that any method might not be debuggable and + // therefore potentially being inspected (due to inlines). If we are debuggable we rely hard on + // inlining not being done since we don't keep track of which methods get inlined where and simply + // look to see if the method is breakpointed. + return !art::Runtime::Current()->IsJavaDebuggable() || + manager_->HaveLocalsChanged() || + manager_->MethodHasBreakpoints(method); } bool JvmtiMethodInspectionCallback::IsMethodSafeToJit(art::ArtMethod* method) { @@ -75,7 +79,10 @@ DeoptManager::DeoptManager() performing_deoptimization_(false), global_deopt_count_(0), deopter_count_(0), - inspection_callback_(this) { } + breakpoint_status_lock_("JVMTI_BreakpointStatusLock", + static_cast(art::LockLevel::kAbortLock + 1)), + inspection_callback_(this), + set_local_variable_called_(false) { } void DeoptManager::Setup() { art::ScopedThreadStateChange stsc(art::Thread::Current(), @@ -121,14 +128,11 @@ void DeoptManager::FinishSetup() { } bool DeoptManager::MethodHasBreakpoints(art::ArtMethod* method) { - art::MutexLock lk(art::Thread::Current(), deoptimization_status_lock_); + art::MutexLock lk(art::Thread::Current(), breakpoint_status_lock_); return MethodHasBreakpointsLocked(method); } bool DeoptManager::MethodHasBreakpointsLocked(art::ArtMethod* method) { - if (deopter_count_ == 0) { - return false; - } auto elem = breakpoint_status_.find(method); return elem != breakpoint_status_.end() && elem->second != 0; } @@ -158,18 +162,23 @@ void DeoptManager::AddMethodBreakpoint(art::ArtMethod* method) { art::ScopedThreadSuspension sts(self, art::kSuspended); deoptimization_status_lock_.ExclusiveLock(self); - - DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; - - if (MethodHasBreakpointsLocked(method)) { - // Don't need to do anything extra. - breakpoint_status_[method]++; - // Another thread might be deoptimizing the very method we just added new breakpoints for. Wait - // for any deopts to finish before moving on. - WaitForDeoptimizationToFinish(self); - return; + { + breakpoint_status_lock_.ExclusiveLock(self); + + DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; + + if (MethodHasBreakpointsLocked(method)) { + // Don't need to do anything extra. + breakpoint_status_[method]++; + // Another thread might be deoptimizing the very method we just added new breakpoints for. + // Wait for any deopts to finish before moving on. + breakpoint_status_lock_.ExclusiveUnlock(self); + WaitForDeoptimizationToFinish(self); + return; + } + breakpoint_status_[method] = 1; + breakpoint_status_lock_.ExclusiveUnlock(self); } - breakpoint_status_[method] = 1; auto instrumentation = art::Runtime::Current()->GetInstrumentation(); if (instrumentation->IsForcedInterpretOnly()) { // We are already interpreting everything so no need to do anything. @@ -196,17 +205,22 @@ void DeoptManager::RemoveMethodBreakpoint(art::ArtMethod* method) { // need but since that is very heavy we will instead just use a condition variable to make sure we // don't race with ourselves. deoptimization_status_lock_.ExclusiveLock(self); - - DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; - DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without " - << "breakpoints present!"; + bool is_last_breakpoint; + { + art::MutexLock mu(self, breakpoint_status_lock_); + + DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; + DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without " + << "breakpoints present!"; + breakpoint_status_[method] -= 1; + is_last_breakpoint = (breakpoint_status_[method] == 0); + } auto instrumentation = art::Runtime::Current()->GetInstrumentation(); - breakpoint_status_[method] -= 1; if (UNLIKELY(instrumentation->IsForcedInterpretOnly())) { // We don't need to do anything since we are interpreting everything anyway. deoptimization_status_lock_.ExclusiveUnlock(self); return; - } else if (breakpoint_status_[method] == 0) { + } else if (is_last_breakpoint) { if (UNLIKELY(is_default)) { RemoveDeoptimizeAllMethodsLocked(self); } else { diff --git a/openjdkjvmti/deopt_manager.h b/openjdkjvmti/deopt_manager.h index a495b6835c..a38690c49e 100644 --- a/openjdkjvmti/deopt_manager.h +++ b/openjdkjvmti/deopt_manager.h @@ -32,6 +32,7 @@ #ifndef ART_OPENJDKJVMTI_DEOPT_MANAGER_H_ #define ART_OPENJDKJVMTI_DEOPT_MANAGER_H_ +#include #include #include "jni.h" @@ -107,9 +108,17 @@ class DeoptManager { static DeoptManager* Get(); + bool HaveLocalsChanged() const { + return set_local_variable_called_.load(); + } + + void SetLocalsUpdated() { + set_local_variable_called_.store(true); + } + private: bool MethodHasBreakpointsLocked(art::ArtMethod* method) - REQUIRES(deoptimization_status_lock_); + REQUIRES(breakpoint_status_lock_); // Wait until nothing is currently in the middle of deoptimizing/undeoptimizing something. This is // needed to ensure that everything is synchronized since threads need to drop the @@ -156,13 +165,20 @@ class DeoptManager { // Number of users of deoptimization there currently are. uint32_t deopter_count_ GUARDED_BY(deoptimization_status_lock_); + // A mutex that just protects the breakpoint-status map. This mutex should always be at the + // bottom of the lock hierarchy. Nothing more should be locked if we hold this. + art::Mutex breakpoint_status_lock_ ACQUIRED_BEFORE(art::Locks::abort_lock_); // A map from methods to the number of breakpoints in them from all envs. std::unordered_map breakpoint_status_ - GUARDED_BY(deoptimization_status_lock_); + GUARDED_BY(breakpoint_status_lock_); // The MethodInspectionCallback we use to tell the runtime if we care about particular methods. JvmtiMethodInspectionCallback inspection_callback_; + // Set to true if anything calls SetLocalVariables on any thread since we need to be careful about + // OSR after this. + std::atomic set_local_variable_called_; + // Helper for setting up/tearing-down for deoptimization. friend class ScopedDeoptimizationContext; }; diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index bf2e6cd104..b83310dc85 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -915,6 +915,9 @@ jvmtiError MethodUtil::SetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED, if (depth < 0) { return ERR(ILLEGAL_ARGUMENT); } + // Make sure that we know not to do any OSR anymore. + // TODO We should really keep track of this at the Frame granularity. + DeoptManager::Get()->SetLocalsUpdated(); art::Thread* self = art::Thread::Current(); // Suspend JIT since it can get confused if we deoptimize methods getting jitted. art::jit::ScopedJitSuspend suspend_jit; diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 84a148f21c..ec3e10e2f8 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -139,10 +139,14 @@ static void UpdateEntrypoints(ArtMethod* method, const void* quick_code) bool Instrumentation::NeedDebugVersionFor(ArtMethod* method) const REQUIRES_SHARED(Locks::mutator_lock_) { - return Runtime::Current()->IsJavaDebuggable() && + art::Runtime* runtime = Runtime::Current(); + return runtime->IsJavaDebuggable() && !method->IsNative() && !method->IsProxyMethod() && - Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method); + // If we don't have a jit this can push us to the pre-compiled version of methods which is + // not something we want since we are debuggable. + (UNLIKELY(runtime->GetJit() == nullptr) || + runtime->GetRuntimeCallbacks()->IsMethodBeingInspected(method)); } void Instrumentation::InstallStubsForMethod(ArtMethod* method) { diff --git a/test/1935-get-set-current-frame-jit/expected.txt b/test/1935-get-set-current-frame-jit/expected.txt index cdb8f6a825..a685891775 100644 --- a/test/1935-get-set-current-frame-jit/expected.txt +++ b/test/1935-get-set-current-frame-jit/expected.txt @@ -1,7 +1,5 @@ JNI_OnLoad called From GetLocalInt(), value is 42 -isInOsrCode? false Value is '42' Setting TARGET to 1337 -isInOsrCode? false Value is '1337' diff --git a/test/1935-get-set-current-frame-jit/run b/test/1935-get-set-current-frame-jit/run index 51875a7e86..5c7292d042 100755 --- a/test/1935-get-set-current-frame-jit/run +++ b/test/1935-get-set-current-frame-jit/run @@ -15,4 +15,5 @@ # limitations under the License. # Ask for stack traces to be dumped to a file rather than to stdout. -./default-run "$@" --jvmti +# Ensure the test is not subject to code collection +./default-run "$@" --jvmti --runtime-option -Xjitinitialsize:32M diff --git a/test/1935-get-set-current-frame-jit/src/Main.java b/test/1935-get-set-current-frame-jit/src/Main.java index 714a98aaf3..378aaf7a94 100644 --- a/test/1935-get-set-current-frame-jit/src/Main.java +++ b/test/1935-get-set-current-frame-jit/src/Main.java @@ -21,6 +21,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.nio.ByteBuffer; +import java.time.Instant; import java.util.concurrent.Semaphore; import java.util.Arrays; import java.util.Collection; @@ -49,9 +50,11 @@ public class Main { public static class IntRunner implements Runnable { private volatile boolean continueBusyLoop; private volatile boolean inBusyLoop; - public IntRunner() { + private final boolean expectOsr; + public IntRunner(boolean expectOsr) { this.continueBusyLoop = true; this.inBusyLoop = false; + this.expectOsr = expectOsr; } public void run() { int TARGET = 42; @@ -59,14 +62,23 @@ public class Main { while (continueBusyLoop) { inBusyLoop = true; } - int i = 0; - while (Main.isInterpreted() && i < 10000) { - Main.ensureJitCompiled(IntRunner.class, "run"); - i++; - } - // We shouldn't be doing OSR since we are using JVMTI and the get/set prevents OSR. + // Wait up to 300 seconds for OSR to kick in if we expect it. If we don't give up after only + // 3 seconds. + Instant osrDeadline = Instant.now().plusSeconds(expectOsr ? 600 : 3); + do { + // Don't actually do anything here. + inBusyLoop = true; + } while (hasJit() && !Main.isInOsrCode("run") && osrDeadline.compareTo(Instant.now()) > 0); + // We shouldn't be doing OSR since we are using JVMTI and the set prevents OSR. // Set local will also push us to interpreter but the get local may remain in compiled code. - System.out.println("isInOsrCode? " + (hasJit() && Main.isInOsrCode("run"))); + if (hasJit()) { + boolean inOsr = Main.isInOsrCode("run"); + if (expectOsr && !inOsr) { + throw new Error("Expected to be in OSR but was not."); + } else if (!expectOsr && inOsr) { + throw new Error("Expected not to be in OSR but was."); + } + } reportValue(TARGET); } public void waitForBusyLoopStart() { while (!inBusyLoop) {} } @@ -78,7 +90,7 @@ public class Main { public static void runGet() throws Exception { Method target = IntRunner.class.getDeclaredMethod("run"); // Get Int - IntRunner int_runner = new IntRunner(); + IntRunner int_runner = new IntRunner(true); Thread target_get = new Thread(int_runner, "GetLocalInt - Target"); target_get.start(); int_runner.waitForBusyLoopStart(); @@ -108,7 +120,7 @@ public class Main { public static void runSet() throws Exception { Method target = IntRunner.class.getDeclaredMethod("run"); // Set Int - IntRunner int_runner = new IntRunner(); + IntRunner int_runner = new IntRunner(false); Thread target_set = new Thread(int_runner, "SetLocalInt - Target"); target_set.start(); int_runner.waitForBusyLoopStart(); @@ -157,7 +169,6 @@ public class Main { throw new Error("Unable to find stack frame in method " + target + " on thread " + thr); } - public static native void ensureJitCompiled(Class k, String f); public static native boolean isInterpreted(); public static native boolean isInOsrCode(String methodName); public static native boolean hasJit(); -- GitLab From f807153d18adec59f5eb1ca270dcbbc7a7335cc7 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 2 Apr 2018 18:13:40 +0000 Subject: [PATCH 185/749] Revert^5 "Ensure that OSR still is possible with jvmti" This reverts commit 23be1464aab396f36f0183e635735cedf96d5607. Reason for revert: Seems to break test 993 when running on CTS Bug: 76226464 Bug: 77306669 Test: None Change-Id: Ie62c1c685455bdf67944d3140fa5d20299b42516 --- openjdkjvmti/deopt_manager.cc | 74 ++++++++----------- openjdkjvmti/deopt_manager.h | 20 +---- openjdkjvmti/ti_method.cc | 3 - runtime/instrumentation.cc | 8 +- .../expected.txt | 2 + test/1935-get-set-current-frame-jit/run | 3 +- .../src/Main.java | 33 +++------ 7 files changed, 48 insertions(+), 95 deletions(-) diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc index 380d95c545..6d84ffa53f 100644 --- a/openjdkjvmti/deopt_manager.cc +++ b/openjdkjvmti/deopt_manager.cc @@ -53,18 +53,14 @@ namespace openjdkjvmti { // TODO We should make this much more selective in the future so we only return true when we -// actually care about the method at this time (ie active frames had locals changed). For now we -// just assume that if anything has changed any frame's locals we care about all methods. If nothing -// has we only care about methods with active breakpoints on them. In the future we should probably -// rewrite all of this to instead do this at the ShadowFrame or thread granularity. -bool JvmtiMethodInspectionCallback::IsMethodBeingInspected(art::ArtMethod* method) { - // Non-java-debuggable runtimes we need to assume that any method might not be debuggable and - // therefore potentially being inspected (due to inlines). If we are debuggable we rely hard on - // inlining not being done since we don't keep track of which methods get inlined where and simply - // look to see if the method is breakpointed. - return !art::Runtime::Current()->IsJavaDebuggable() || - manager_->HaveLocalsChanged() || - manager_->MethodHasBreakpoints(method); +// actually care about the method (i.e. had locals changed, have breakpoints, etc.). For now though +// we can just assume that we care we are loaded at all. +// +// Even if we don't keep track of this at the method level we might want to keep track of it at the +// level of enabled capabilities. +bool JvmtiMethodInspectionCallback::IsMethodBeingInspected( + art::ArtMethod* method ATTRIBUTE_UNUSED) { + return true; } bool JvmtiMethodInspectionCallback::IsMethodSafeToJit(art::ArtMethod* method) { @@ -79,10 +75,7 @@ DeoptManager::DeoptManager() performing_deoptimization_(false), global_deopt_count_(0), deopter_count_(0), - breakpoint_status_lock_("JVMTI_BreakpointStatusLock", - static_cast(art::LockLevel::kAbortLock + 1)), - inspection_callback_(this), - set_local_variable_called_(false) { } + inspection_callback_(this) { } void DeoptManager::Setup() { art::ScopedThreadStateChange stsc(art::Thread::Current(), @@ -128,11 +121,14 @@ void DeoptManager::FinishSetup() { } bool DeoptManager::MethodHasBreakpoints(art::ArtMethod* method) { - art::MutexLock lk(art::Thread::Current(), breakpoint_status_lock_); + art::MutexLock lk(art::Thread::Current(), deoptimization_status_lock_); return MethodHasBreakpointsLocked(method); } bool DeoptManager::MethodHasBreakpointsLocked(art::ArtMethod* method) { + if (deopter_count_ == 0) { + return false; + } auto elem = breakpoint_status_.find(method); return elem != breakpoint_status_.end() && elem->second != 0; } @@ -162,23 +158,18 @@ void DeoptManager::AddMethodBreakpoint(art::ArtMethod* method) { art::ScopedThreadSuspension sts(self, art::kSuspended); deoptimization_status_lock_.ExclusiveLock(self); - { - breakpoint_status_lock_.ExclusiveLock(self); - - DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; - - if (MethodHasBreakpointsLocked(method)) { - // Don't need to do anything extra. - breakpoint_status_[method]++; - // Another thread might be deoptimizing the very method we just added new breakpoints for. - // Wait for any deopts to finish before moving on. - breakpoint_status_lock_.ExclusiveUnlock(self); - WaitForDeoptimizationToFinish(self); - return; - } - breakpoint_status_[method] = 1; - breakpoint_status_lock_.ExclusiveUnlock(self); + + DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; + + if (MethodHasBreakpointsLocked(method)) { + // Don't need to do anything extra. + breakpoint_status_[method]++; + // Another thread might be deoptimizing the very method we just added new breakpoints for. Wait + // for any deopts to finish before moving on. + WaitForDeoptimizationToFinish(self); + return; } + breakpoint_status_[method] = 1; auto instrumentation = art::Runtime::Current()->GetInstrumentation(); if (instrumentation->IsForcedInterpretOnly()) { // We are already interpreting everything so no need to do anything. @@ -205,22 +196,17 @@ void DeoptManager::RemoveMethodBreakpoint(art::ArtMethod* method) { // need but since that is very heavy we will instead just use a condition variable to make sure we // don't race with ourselves. deoptimization_status_lock_.ExclusiveLock(self); - bool is_last_breakpoint; - { - art::MutexLock mu(self, breakpoint_status_lock_); - - DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; - DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without " - << "breakpoints present!"; - breakpoint_status_[method] -= 1; - is_last_breakpoint = (breakpoint_status_[method] == 0); - } + + DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; + DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without " + << "breakpoints present!"; auto instrumentation = art::Runtime::Current()->GetInstrumentation(); + breakpoint_status_[method] -= 1; if (UNLIKELY(instrumentation->IsForcedInterpretOnly())) { // We don't need to do anything since we are interpreting everything anyway. deoptimization_status_lock_.ExclusiveUnlock(self); return; - } else if (is_last_breakpoint) { + } else if (breakpoint_status_[method] == 0) { if (UNLIKELY(is_default)) { RemoveDeoptimizeAllMethodsLocked(self); } else { diff --git a/openjdkjvmti/deopt_manager.h b/openjdkjvmti/deopt_manager.h index a38690c49e..a495b6835c 100644 --- a/openjdkjvmti/deopt_manager.h +++ b/openjdkjvmti/deopt_manager.h @@ -32,7 +32,6 @@ #ifndef ART_OPENJDKJVMTI_DEOPT_MANAGER_H_ #define ART_OPENJDKJVMTI_DEOPT_MANAGER_H_ -#include #include #include "jni.h" @@ -108,17 +107,9 @@ class DeoptManager { static DeoptManager* Get(); - bool HaveLocalsChanged() const { - return set_local_variable_called_.load(); - } - - void SetLocalsUpdated() { - set_local_variable_called_.store(true); - } - private: bool MethodHasBreakpointsLocked(art::ArtMethod* method) - REQUIRES(breakpoint_status_lock_); + REQUIRES(deoptimization_status_lock_); // Wait until nothing is currently in the middle of deoptimizing/undeoptimizing something. This is // needed to ensure that everything is synchronized since threads need to drop the @@ -165,20 +156,13 @@ class DeoptManager { // Number of users of deoptimization there currently are. uint32_t deopter_count_ GUARDED_BY(deoptimization_status_lock_); - // A mutex that just protects the breakpoint-status map. This mutex should always be at the - // bottom of the lock hierarchy. Nothing more should be locked if we hold this. - art::Mutex breakpoint_status_lock_ ACQUIRED_BEFORE(art::Locks::abort_lock_); // A map from methods to the number of breakpoints in them from all envs. std::unordered_map breakpoint_status_ - GUARDED_BY(breakpoint_status_lock_); + GUARDED_BY(deoptimization_status_lock_); // The MethodInspectionCallback we use to tell the runtime if we care about particular methods. JvmtiMethodInspectionCallback inspection_callback_; - // Set to true if anything calls SetLocalVariables on any thread since we need to be careful about - // OSR after this. - std::atomic set_local_variable_called_; - // Helper for setting up/tearing-down for deoptimization. friend class ScopedDeoptimizationContext; }; diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index b83310dc85..bf2e6cd104 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -915,9 +915,6 @@ jvmtiError MethodUtil::SetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED, if (depth < 0) { return ERR(ILLEGAL_ARGUMENT); } - // Make sure that we know not to do any OSR anymore. - // TODO We should really keep track of this at the Frame granularity. - DeoptManager::Get()->SetLocalsUpdated(); art::Thread* self = art::Thread::Current(); // Suspend JIT since it can get confused if we deoptimize methods getting jitted. art::jit::ScopedJitSuspend suspend_jit; diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index ec3e10e2f8..84a148f21c 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -139,14 +139,10 @@ static void UpdateEntrypoints(ArtMethod* method, const void* quick_code) bool Instrumentation::NeedDebugVersionFor(ArtMethod* method) const REQUIRES_SHARED(Locks::mutator_lock_) { - art::Runtime* runtime = Runtime::Current(); - return runtime->IsJavaDebuggable() && + return Runtime::Current()->IsJavaDebuggable() && !method->IsNative() && !method->IsProxyMethod() && - // If we don't have a jit this can push us to the pre-compiled version of methods which is - // not something we want since we are debuggable. - (UNLIKELY(runtime->GetJit() == nullptr) || - runtime->GetRuntimeCallbacks()->IsMethodBeingInspected(method)); + Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method); } void Instrumentation::InstallStubsForMethod(ArtMethod* method) { diff --git a/test/1935-get-set-current-frame-jit/expected.txt b/test/1935-get-set-current-frame-jit/expected.txt index a685891775..cdb8f6a825 100644 --- a/test/1935-get-set-current-frame-jit/expected.txt +++ b/test/1935-get-set-current-frame-jit/expected.txt @@ -1,5 +1,7 @@ JNI_OnLoad called From GetLocalInt(), value is 42 +isInOsrCode? false Value is '42' Setting TARGET to 1337 +isInOsrCode? false Value is '1337' diff --git a/test/1935-get-set-current-frame-jit/run b/test/1935-get-set-current-frame-jit/run index 5c7292d042..51875a7e86 100755 --- a/test/1935-get-set-current-frame-jit/run +++ b/test/1935-get-set-current-frame-jit/run @@ -15,5 +15,4 @@ # limitations under the License. # Ask for stack traces to be dumped to a file rather than to stdout. -# Ensure the test is not subject to code collection -./default-run "$@" --jvmti --runtime-option -Xjitinitialsize:32M +./default-run "$@" --jvmti diff --git a/test/1935-get-set-current-frame-jit/src/Main.java b/test/1935-get-set-current-frame-jit/src/Main.java index 378aaf7a94..714a98aaf3 100644 --- a/test/1935-get-set-current-frame-jit/src/Main.java +++ b/test/1935-get-set-current-frame-jit/src/Main.java @@ -21,7 +21,6 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.nio.ByteBuffer; -import java.time.Instant; import java.util.concurrent.Semaphore; import java.util.Arrays; import java.util.Collection; @@ -50,11 +49,9 @@ public class Main { public static class IntRunner implements Runnable { private volatile boolean continueBusyLoop; private volatile boolean inBusyLoop; - private final boolean expectOsr; - public IntRunner(boolean expectOsr) { + public IntRunner() { this.continueBusyLoop = true; this.inBusyLoop = false; - this.expectOsr = expectOsr; } public void run() { int TARGET = 42; @@ -62,23 +59,14 @@ public class Main { while (continueBusyLoop) { inBusyLoop = true; } - // Wait up to 300 seconds for OSR to kick in if we expect it. If we don't give up after only - // 3 seconds. - Instant osrDeadline = Instant.now().plusSeconds(expectOsr ? 600 : 3); - do { - // Don't actually do anything here. - inBusyLoop = true; - } while (hasJit() && !Main.isInOsrCode("run") && osrDeadline.compareTo(Instant.now()) > 0); - // We shouldn't be doing OSR since we are using JVMTI and the set prevents OSR. - // Set local will also push us to interpreter but the get local may remain in compiled code. - if (hasJit()) { - boolean inOsr = Main.isInOsrCode("run"); - if (expectOsr && !inOsr) { - throw new Error("Expected to be in OSR but was not."); - } else if (!expectOsr && inOsr) { - throw new Error("Expected not to be in OSR but was."); - } + int i = 0; + while (Main.isInterpreted() && i < 10000) { + Main.ensureJitCompiled(IntRunner.class, "run"); + i++; } + // We shouldn't be doing OSR since we are using JVMTI and the get/set prevents OSR. + // Set local will also push us to interpreter but the get local may remain in compiled code. + System.out.println("isInOsrCode? " + (hasJit() && Main.isInOsrCode("run"))); reportValue(TARGET); } public void waitForBusyLoopStart() { while (!inBusyLoop) {} } @@ -90,7 +78,7 @@ public class Main { public static void runGet() throws Exception { Method target = IntRunner.class.getDeclaredMethod("run"); // Get Int - IntRunner int_runner = new IntRunner(true); + IntRunner int_runner = new IntRunner(); Thread target_get = new Thread(int_runner, "GetLocalInt - Target"); target_get.start(); int_runner.waitForBusyLoopStart(); @@ -120,7 +108,7 @@ public class Main { public static void runSet() throws Exception { Method target = IntRunner.class.getDeclaredMethod("run"); // Set Int - IntRunner int_runner = new IntRunner(false); + IntRunner int_runner = new IntRunner(); Thread target_set = new Thread(int_runner, "SetLocalInt - Target"); target_set.start(); int_runner.waitForBusyLoopStart(); @@ -169,6 +157,7 @@ public class Main { throw new Error("Unable to find stack frame in method " + target + " on thread " + thr); } + public static native void ensureJitCompiled(Class k, String f); public static native boolean isInterpreted(); public static native boolean isInOsrCode(String methodName); public static native boolean hasJit(); -- GitLab From 1599a664327766f882997d276c240269c4c5674d Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 2 Apr 2018 17:31:34 -0700 Subject: [PATCH 186/749] Add range CHECK for method index in GetQuickenedInfoOf Make sure the method index is in the range of the dex file. Aims to debug possible corruption. Bug: 76162418 Test: test-art-host Change-Id: Iba0a4d32e6a8c486cc67fdc1e2fbcb5531ea897f --- runtime/vdex_file.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index ec4dc417d3..838d7f14bc 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -317,6 +317,7 @@ ArrayRef VdexFile::GetQuickenedInfoOf(const DexFile& dex_file, if (quickening_info.empty()) { return ArrayRef(); } + CHECK_LT(dex_method_idx, dex_file.NumMethodIds()); const uint32_t quickening_offset = GetQuickenInfoOffsetTable(dex_file, quickening_info).GetOffset(dex_method_idx); if (quickening_offset == 0u) { -- GitLab From 252a4e49ddeff7c6977ea88dfb4f5ddf593ba826 Mon Sep 17 00:00:00 2001 From: Alexey Grebenkin Date: Mon, 2 Apr 2018 18:18:01 +0300 Subject: [PATCH 187/749] Fix 616-cha-unloading. Consider cases of implicit arena reuse to prevent false positives. Test: 616-cha-unloading Change-Id: Ia1755fb66167279c08dd9ba59813402e798c0b79 --- runtime/class_linker.cc | 9 ++++++ runtime/class_linker.h | 13 +++++++++ test/616-cha-unloading/cha_unload.cc | 42 +++++++++++++++++++++++++--- 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 3025818ab7..ba90c17b43 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -8917,6 +8917,15 @@ void ClassLinker::VisitClassLoaders(ClassLoaderVisitor* visitor) const { } } +void ClassLinker::VisitAllocators(AllocatorVisitor* visitor) const { + for (const ClassLoaderData& data : class_loaders_) { + LinearAlloc* alloc = data.allocator; + if (alloc != nullptr && !visitor->Visit(alloc)) { + break; + } + } +} + void ClassLinker::InsertDexFileInToClassLoader(ObjPtr dex_file, ObjPtr class_loader) { DCHECK(dex_file != nullptr); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 2f6b754521..fa70f65bca 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -97,6 +97,14 @@ class ClassLoaderVisitor { REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) = 0; }; +class AllocatorVisitor { + public: + virtual ~AllocatorVisitor() {} + // Return true to continue visiting. + virtual bool Visit(LinearAlloc* alloc) + REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) = 0; +}; + class ClassLinker { public: // Well known mirror::Class roots accessed via GetClassRoot. @@ -664,6 +672,11 @@ class ClassLinker { REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + // Visit all of the allocators that belong to classloaders except boot classloader. + // This is used by 616-cha-unloading test to confirm memory reuse. + void VisitAllocators(AllocatorVisitor* visitor) const + REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_); + // Throw the class initialization failure recorded when first trying to initialize the given // class. void ThrowEarlierClassFailure(ObjPtr c, bool wrap_in_no_class_def = false) diff --git a/test/616-cha-unloading/cha_unload.cc b/test/616-cha-unloading/cha_unload.cc index 4be3456e3d..b17be6bd07 100644 --- a/test/616-cha-unloading/cha_unload.cc +++ b/test/616-cha-unloading/cha_unload.cc @@ -19,6 +19,7 @@ #include #include "art_method.h" +#include "class_linker.h" #include "jit/jit.h" #include "linear_alloc.h" #include "nativehelper/ScopedUtfChars.h" @@ -29,6 +30,22 @@ namespace art { namespace { +class FindPointerAllocatorVisitor : public AllocatorVisitor { + public: + explicit FindPointerAllocatorVisitor(void* ptr) : is_found(false), ptr_(ptr) {} + + bool Visit(LinearAlloc* alloc) + REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE { + is_found = alloc->Contains(ptr_); + return !is_found; + } + + bool is_found; + + private: + void* ptr_; +}; + extern "C" JNIEXPORT jlong JNICALL Java_Main_getArtMethod(JNIEnv* env, jclass, jobject java_method) { @@ -40,13 +57,30 @@ extern "C" JNIEXPORT jlong JNICALL Java_Main_getArtMethod(JNIEnv* env, 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. + void* ptr = reinterpret_cast(static_cast(art_method)); + + ReaderMutexLock mu(Thread::Current(), *Locks::mutator_lock_); + ReaderMutexLock mu2(Thread::Current(), *Locks::classlinker_classes_lock_); + // Check if the arena was already implicitly reused by boot classloader. + if (Runtime::Current()->GetLinearAlloc()->Contains(ptr)) { + return; + } + + // Check if the arena was already implicitly reused by some other classloader. + FindPointerAllocatorVisitor visitor(ptr); + Runtime::Current()->GetClassLinker()->VisitAllocators(&visitor); + if (visitor.is_found) { + return; + } + + // The arena was not reused yet. Do it explicitly. + // Create a new allocation and use it to request new arenas until one of them is + // a reused one that covers the art_method pointer. std::unique_ptr alloc(Runtime::Current()->CreateLinearAlloc()); do { - // Ask for a byte - it's sufficient to get an arena and not have issues with size. + // Ask for a byte - it's sufficient to get an arena. alloc->Alloc(Thread::Current(), 1); - } while (!alloc->Contains(reinterpret_cast(static_cast(art_method)))); + } while (!alloc->Contains(ptr)); } } // namespace -- GitLab From 3840b34da05248d91bf3d2a7f1c30f7fa0d63891 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Thu, 29 Mar 2018 19:36:12 +0100 Subject: [PATCH 188/749] Ignore failures on O devices in some network-related libcore tests. These tests fail with the following assertion when run with an unbundled ART (built from AOSP) on devices running Android O (MR1): android.system.ErrnoException: connect failed: EBADMSG (Not a data message) This is a workaround to stop bot spam while we investigate the root of the issue. Test: art/tools/run-libcore-tests.sh --mode=device Bug: 74725685 Change-Id: Ia7129eca795f4b1b01e50820f952bc5f36726d9a --- tools/libcore_network_failures.txt | 92 ++++++++++++++++++++++++++++++ tools/run-libcore-tests.sh | 10 ++++ 2 files changed, 102 insertions(+) create mode 100644 tools/libcore_network_failures.txt diff --git a/tools/libcore_network_failures.txt b/tools/libcore_network_failures.txt new file mode 100644 index 0000000000..e7e31dbe67 --- /dev/null +++ b/tools/libcore_network_failures.txt @@ -0,0 +1,92 @@ +/* + * This file contains extra expectations for ART's buildbot regarding network tests. + * The script that uses this file is art/tools/run-libcore-tests.sh. + */ + +[ +{ + description: "Ignore failure of network-related tests on new devices running Android O", + result: EXEC_FAILED, + bug: 74725685, + modes: [device], + names: ["libcore.libcore.io.OsTest#test_byteBufferPositions_sendto_recvfrom_af_inet", + "libcore.libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithFtpURLConnection", + "libcore.libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithHttpURLConnection", + "libcore.libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithJarFtpURLConnection", + "libcore.libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithJarHttpURLConnection", + "libcore.libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithLoggingSocketHandler", + "org.apache.harmony.tests.java.util.ScannerTest#test_40555", + "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_io_File", + "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_io_FileLjava_lang_String", + "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_io_InputStream", + "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_io_InputStreamLjava_lang_String", + "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_lang_Readable", + "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_lang_String", + "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_nio_channels_ReadableByteChannel", + "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_nio_channels_ReadableByteChannelLjava_lang_String", + "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_nio_file_Path", + "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_nio_file_PathLjava_lang_String", + "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_nio_file_PathLjava_lang_String_Exception", + "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_nio_file_Path_Exception", + "org.apache.harmony.tests.java.util.ScannerTest#test_close", + "org.apache.harmony.tests.java.util.ScannerTest#test_delimiter", + "org.apache.harmony.tests.java.util.ScannerTest#test_findInLine_LPattern", + "org.apache.harmony.tests.java.util.ScannerTest#test_findInLine_LString", + "org.apache.harmony.tests.java.util.ScannerTest#test_findInLine_LString_NPEs", + "org.apache.harmony.tests.java.util.ScannerTest#test_findWithinHorizon_LPatternI", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNext", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextBigDecimal", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextBigInteger", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextBigIntegerI", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextBigIntegerI_cache", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextBoolean", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextByte", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextByteI", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextByteI_cache", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextDouble", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextFloat", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextInt", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextIntI", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextIntI_cache", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLPattern", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLString", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLine", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLine_sequence", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLong", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLongI", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLongI_cache", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextShort", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextShortI", + "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextShortI_cache", + "org.apache.harmony.tests.java.util.ScannerTest#test_ioException", + "org.apache.harmony.tests.java.util.ScannerTest#test_locale", + "org.apache.harmony.tests.java.util.ScannerTest#test_match", + "org.apache.harmony.tests.java.util.ScannerTest#test_next", + "org.apache.harmony.tests.java.util.ScannerTest#test_nextBigDecimal", + "org.apache.harmony.tests.java.util.ScannerTest#test_nextBigInteger", + "org.apache.harmony.tests.java.util.ScannerTest#test_nextBigIntegerI", + "org.apache.harmony.tests.java.util.ScannerTest#test_nextBoolean", + "org.apache.harmony.tests.java.util.ScannerTest#test_nextByte", + "org.apache.harmony.tests.java.util.ScannerTest#test_nextByteI", + "org.apache.harmony.tests.java.util.ScannerTest#test_nextDouble", + "org.apache.harmony.tests.java.util.ScannerTest#test_nextFloat", + "org.apache.harmony.tests.java.util.ScannerTest#test_nextInt", + "org.apache.harmony.tests.java.util.ScannerTest#test_nextIntI", + "org.apache.harmony.tests.java.util.ScannerTest#test_nextLPattern", + "org.apache.harmony.tests.java.util.ScannerTest#test_nextLString", + "org.apache.harmony.tests.java.util.ScannerTest#test_nextLine", + "org.apache.harmony.tests.java.util.ScannerTest#test_nextLong", + "org.apache.harmony.tests.java.util.ScannerTest#test_nextLongI", + "org.apache.harmony.tests.java.util.ScannerTest#test_nextShort", + "org.apache.harmony.tests.java.util.ScannerTest#test_nextShortI", + "org.apache.harmony.tests.java.util.ScannerTest#test_radix", + "org.apache.harmony.tests.java.util.ScannerTest#test_remove", + "org.apache.harmony.tests.java.util.ScannerTest#test_skip_LPattern", + "org.apache.harmony.tests.java.util.ScannerTest#test_skip_LString", + "org.apache.harmony.tests.java.util.ScannerTest#test_toString", + "org.apache.harmony.tests.java.util.ScannerTest#test_useDelimiter_LPattern", + "org.apache.harmony.tests.java.util.ScannerTest#test_useDelimiter_String", + "org.apache.harmony.tests.java.util.ScannerTest#test_useLocale_LLocale", + "org.apache.harmony.tests.java.util.ScannerTest#test_useRadix_I"] +} +] diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh index 2b7c624a3a..7f0383d55d 100755 --- a/tools/run-libcore-tests.sh +++ b/tools/run-libcore-tests.sh @@ -161,6 +161,16 @@ if [[ $gcstress ]]; then fi fi +# Disable network-related libcore tests that are failing on the following +# devices running Android O, as a workaround for b/74725685: +# - FA7BN1A04406 (walleye device testing configuration aosp-poison/volantis-armv7-poison-debug) +# - FA7BN1A04412 (walleye device testing configuration aosp-poison/volantis-armv8-poison-ndebug) +# - FA7BN1A04433 (walleye device testing configuration aosp-poison/volantis-armv8-poison-debug) +case "$ANDROID_SERIAL" in + (FA7BN1A04406|FA7BN1A04412|FA7BN1A04433) + expectations="$expectations --expectations art/tools/libcore_network_failures.txt";; +esac + # Run the tests using vogar. echo "Running tests for the following test packages:" echo ${working_packages[@]} | tr " " "\n" -- GitLab From f28586390b055a5681e50617d729a3fa09792d9c Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 2 Apr 2018 11:28:50 -0700 Subject: [PATCH 189/749] Revert^6 "Ensure that OSR still is possible with jvmti" The instrumentation uninstall could set methods to non-debuggable boot.oat code. This could cause events to be missed due to methods being inlined. We needed to change the path so that we would only have the JIT/interpreter replace methods. We do this by adding a new callback that can be used to determine if a method needs to be debuggable and being more careful about replacing code when this is true. This reverts commit 5f3005c8844d851d7d218b88b5f90d6c9083ce24. This unreverts commit b9ad26d1ed9146b89555d4333021f44eeb831f05. Reason for revert: Fixed issue causing CTS version of test 993 failure. Test: cts-tradefed run cts-dev CtsJvmtiRunTest993HostTestCases Test: ./test.py --host -j50 --all -t 993 Test: ./test.py --host Test: while ./test/run-test --host --jit 1935; do; done Test: while ./test/run-test --host --jit --jvmti-redefine-stress 1935; do; done Test: am start --attach-agent -n com.example.android.displayingbitmaps/.ui.ImageGridActivity Run blur filter. Bug: 76226464 Bug: 77306669 Change-Id: I5068201a03f7613787c66981405499b6499c24e1 --- openjdkjvmti/deopt_manager.cc | 79 ++++++++++++------- openjdkjvmti/deopt_manager.h | 23 +++++- openjdkjvmti/ti_method.cc | 3 + runtime/debugger.cc | 5 ++ runtime/debugger.h | 1 + runtime/instrumentation.cc | 9 ++- runtime/runtime_callbacks.cc | 9 +++ runtime/runtime_callbacks.h | 9 +++ .../expected.txt | 2 - test/1935-get-set-current-frame-jit/run | 4 +- .../src/Main.java | 33 +++++--- 11 files changed, 127 insertions(+), 50 deletions(-) diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc index 6d84ffa53f..a6f1207f34 100644 --- a/openjdkjvmti/deopt_manager.cc +++ b/openjdkjvmti/deopt_manager.cc @@ -53,20 +53,29 @@ namespace openjdkjvmti { // TODO We should make this much more selective in the future so we only return true when we -// actually care about the method (i.e. had locals changed, have breakpoints, etc.). For now though -// we can just assume that we care we are loaded at all. -// -// Even if we don't keep track of this at the method level we might want to keep track of it at the -// level of enabled capabilities. -bool JvmtiMethodInspectionCallback::IsMethodBeingInspected( - art::ArtMethod* method ATTRIBUTE_UNUSED) { - return true; +// actually care about the method at this time (ie active frames had locals changed). For now we +// just assume that if anything has changed any frame's locals we care about all methods. If nothing +// has we only care about methods with active breakpoints on them. In the future we should probably +// rewrite all of this to instead do this at the ShadowFrame or thread granularity. +bool JvmtiMethodInspectionCallback::IsMethodBeingInspected(art::ArtMethod* method) { + // Non-java-debuggable runtimes we need to assume that any method might not be debuggable and + // therefore potentially being inspected (due to inlines). If we are debuggable we rely hard on + // inlining not being done since we don't keep track of which methods get inlined where and simply + // look to see if the method is breakpointed. + return !art::Runtime::Current()->IsJavaDebuggable() || + manager_->HaveLocalsChanged() || + manager_->MethodHasBreakpoints(method); } bool JvmtiMethodInspectionCallback::IsMethodSafeToJit(art::ArtMethod* method) { return !manager_->MethodHasBreakpoints(method); } +bool JvmtiMethodInspectionCallback::MethodNeedsDebugVersion( + art::ArtMethod* method ATTRIBUTE_UNUSED) { + return true; +} + DeoptManager::DeoptManager() : deoptimization_status_lock_("JVMTI_DeoptimizationStatusLock", static_cast( @@ -75,7 +84,10 @@ DeoptManager::DeoptManager() performing_deoptimization_(false), global_deopt_count_(0), deopter_count_(0), - inspection_callback_(this) { } + breakpoint_status_lock_("JVMTI_BreakpointStatusLock", + static_cast(art::LockLevel::kAbortLock + 1)), + inspection_callback_(this), + set_local_variable_called_(false) { } void DeoptManager::Setup() { art::ScopedThreadStateChange stsc(art::Thread::Current(), @@ -121,14 +133,11 @@ void DeoptManager::FinishSetup() { } bool DeoptManager::MethodHasBreakpoints(art::ArtMethod* method) { - art::MutexLock lk(art::Thread::Current(), deoptimization_status_lock_); + art::MutexLock lk(art::Thread::Current(), breakpoint_status_lock_); return MethodHasBreakpointsLocked(method); } bool DeoptManager::MethodHasBreakpointsLocked(art::ArtMethod* method) { - if (deopter_count_ == 0) { - return false; - } auto elem = breakpoint_status_.find(method); return elem != breakpoint_status_.end() && elem->second != 0; } @@ -158,18 +167,23 @@ void DeoptManager::AddMethodBreakpoint(art::ArtMethod* method) { art::ScopedThreadSuspension sts(self, art::kSuspended); deoptimization_status_lock_.ExclusiveLock(self); - - DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; - - if (MethodHasBreakpointsLocked(method)) { - // Don't need to do anything extra. - breakpoint_status_[method]++; - // Another thread might be deoptimizing the very method we just added new breakpoints for. Wait - // for any deopts to finish before moving on. - WaitForDeoptimizationToFinish(self); - return; + { + breakpoint_status_lock_.ExclusiveLock(self); + + DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; + + if (MethodHasBreakpointsLocked(method)) { + // Don't need to do anything extra. + breakpoint_status_[method]++; + // Another thread might be deoptimizing the very method we just added new breakpoints for. + // Wait for any deopts to finish before moving on. + breakpoint_status_lock_.ExclusiveUnlock(self); + WaitForDeoptimizationToFinish(self); + return; + } + breakpoint_status_[method] = 1; + breakpoint_status_lock_.ExclusiveUnlock(self); } - breakpoint_status_[method] = 1; auto instrumentation = art::Runtime::Current()->GetInstrumentation(); if (instrumentation->IsForcedInterpretOnly()) { // We are already interpreting everything so no need to do anything. @@ -196,17 +210,22 @@ void DeoptManager::RemoveMethodBreakpoint(art::ArtMethod* method) { // need but since that is very heavy we will instead just use a condition variable to make sure we // don't race with ourselves. deoptimization_status_lock_.ExclusiveLock(self); - - DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; - DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without " - << "breakpoints present!"; + bool is_last_breakpoint; + { + art::MutexLock mu(self, breakpoint_status_lock_); + + DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request"; + DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without " + << "breakpoints present!"; + breakpoint_status_[method] -= 1; + is_last_breakpoint = (breakpoint_status_[method] == 0); + } auto instrumentation = art::Runtime::Current()->GetInstrumentation(); - breakpoint_status_[method] -= 1; if (UNLIKELY(instrumentation->IsForcedInterpretOnly())) { // We don't need to do anything since we are interpreting everything anyway. deoptimization_status_lock_.ExclusiveUnlock(self); return; - } else if (breakpoint_status_[method] == 0) { + } else if (is_last_breakpoint) { if (UNLIKELY(is_default)) { RemoveDeoptimizeAllMethodsLocked(self); } else { diff --git a/openjdkjvmti/deopt_manager.h b/openjdkjvmti/deopt_manager.h index a495b6835c..6e991dee3d 100644 --- a/openjdkjvmti/deopt_manager.h +++ b/openjdkjvmti/deopt_manager.h @@ -32,6 +32,7 @@ #ifndef ART_OPENJDKJVMTI_DEOPT_MANAGER_H_ #define ART_OPENJDKJVMTI_DEOPT_MANAGER_H_ +#include #include #include "jni.h" @@ -62,6 +63,9 @@ struct JvmtiMethodInspectionCallback : public art::MethodInspectionCallback { bool IsMethodSafeToJit(art::ArtMethod* method) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_); + bool MethodNeedsDebugVersion(art::ArtMethod* method) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_); + private: DeoptManager* manager_; }; @@ -107,9 +111,17 @@ class DeoptManager { static DeoptManager* Get(); + bool HaveLocalsChanged() const { + return set_local_variable_called_.load(); + } + + void SetLocalsUpdated() { + set_local_variable_called_.store(true); + } + private: bool MethodHasBreakpointsLocked(art::ArtMethod* method) - REQUIRES(deoptimization_status_lock_); + REQUIRES(breakpoint_status_lock_); // Wait until nothing is currently in the middle of deoptimizing/undeoptimizing something. This is // needed to ensure that everything is synchronized since threads need to drop the @@ -156,13 +168,20 @@ class DeoptManager { // Number of users of deoptimization there currently are. uint32_t deopter_count_ GUARDED_BY(deoptimization_status_lock_); + // A mutex that just protects the breakpoint-status map. This mutex should always be at the + // bottom of the lock hierarchy. Nothing more should be locked if we hold this. + art::Mutex breakpoint_status_lock_ ACQUIRED_BEFORE(art::Locks::abort_lock_); // A map from methods to the number of breakpoints in them from all envs. std::unordered_map breakpoint_status_ - GUARDED_BY(deoptimization_status_lock_); + GUARDED_BY(breakpoint_status_lock_); // The MethodInspectionCallback we use to tell the runtime if we care about particular methods. JvmtiMethodInspectionCallback inspection_callback_; + // Set to true if anything calls SetLocalVariables on any thread since we need to be careful about + // OSR after this. + std::atomic set_local_variable_called_; + // Helper for setting up/tearing-down for deoptimization. friend class ScopedDeoptimizationContext; }; diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index bf2e6cd104..b83310dc85 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -915,6 +915,9 @@ jvmtiError MethodUtil::SetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED, if (depth < 0) { return ERR(ILLEGAL_ARGUMENT); } + // Make sure that we know not to do any OSR anymore. + // TODO We should really keep track of this at the Frame granularity. + DeoptManager::Get()->SetLocalsUpdated(); art::Thread* self = art::Thread::Current(); // Suspend JIT since it can get confused if we deoptimize methods getting jitted. art::jit::ScopedJitSuspend suspend_jit; diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 4a9449640b..28659cb11d 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -364,6 +364,11 @@ bool DebuggerActiveMethodInspectionCallback::IsMethodSafeToJit(ArtMethod* m) { return !Dbg::MethodHasAnyBreakpoints(m); } +bool DebuggerActiveMethodInspectionCallback::MethodNeedsDebugVersion( + ArtMethod* m ATTRIBUTE_UNUSED) { + return Dbg::IsDebuggerActive(); +} + void InternalDebuggerControlCallback::StartDebugger() { // Release the mutator lock. ScopedThreadStateChange stsc(art::Thread::Current(), kNative); diff --git a/runtime/debugger.h b/runtime/debugger.h index 74018137a0..e1de991812 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -56,6 +56,7 @@ class Thread; struct DebuggerActiveMethodInspectionCallback : public MethodInspectionCallback { bool IsMethodBeingInspected(ArtMethod* method) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); bool IsMethodSafeToJit(ArtMethod* method) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); + bool MethodNeedsDebugVersion(ArtMethod* method) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); }; struct DebuggerDdmCallback : public DdmCallback { diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 84a148f21c..d7f33d5e43 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -139,10 +139,13 @@ static void UpdateEntrypoints(ArtMethod* method, const void* quick_code) bool Instrumentation::NeedDebugVersionFor(ArtMethod* method) const REQUIRES_SHARED(Locks::mutator_lock_) { - return Runtime::Current()->IsJavaDebuggable() && + art::Runtime* runtime = Runtime::Current(); + // If anything says we need the debug version or we are debuggable we will need the debug version + // of the method. + return (runtime->GetRuntimeCallbacks()->MethodNeedsDebugVersion(method) || + runtime->IsJavaDebuggable()) && !method->IsNative() && - !method->IsProxyMethod() && - Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method); + !method->IsProxyMethod(); } void Instrumentation::InstallStubsForMethod(ArtMethod* method) { diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc index cd3c0b7c88..758917cf7e 100644 --- a/runtime/runtime_callbacks.cc +++ b/runtime/runtime_callbacks.cc @@ -106,6 +106,15 @@ bool RuntimeCallbacks::IsMethodBeingInspected(ArtMethod* m) { return false; } +bool RuntimeCallbacks::MethodNeedsDebugVersion(ArtMethod* m) { + for (MethodInspectionCallback* cb : method_inspection_callbacks_) { + if (cb->MethodNeedsDebugVersion(m)) { + return true; + } + } + return false; +} + void RuntimeCallbacks::AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) { thread_callbacks_.push_back(cb); } diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h index 24386ba14a..9f0410d102 100644 --- a/runtime/runtime_callbacks.h +++ b/runtime/runtime_callbacks.h @@ -130,6 +130,10 @@ class MethodInspectionCallback { // Note that '!IsMethodSafeToJit(m) implies IsMethodBeingInspected(m)'. That is that if this // method returns false IsMethodBeingInspected must return true. virtual bool IsMethodSafeToJit(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) = 0; + + // Returns true if we expect the method to be debuggable but are not doing anything unusual with + // it currently. + virtual bool MethodNeedsDebugVersion(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) = 0; }; class RuntimeCallbacks { @@ -198,6 +202,11 @@ class RuntimeCallbacks { // entrypoint should not be changed to JITed code. bool IsMethodSafeToJit(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); + // Returns true if some MethodInspectionCallback indicates the method needs to use a debug + // version. This allows later code to set breakpoints or perform other actions that could be + // broken by some optimizations. + bool MethodNeedsDebugVersion(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); + void AddMethodInspectionCallback(MethodInspectionCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_); void RemoveMethodInspectionCallback(MethodInspectionCallback* cb) diff --git a/test/1935-get-set-current-frame-jit/expected.txt b/test/1935-get-set-current-frame-jit/expected.txt index cdb8f6a825..a685891775 100644 --- a/test/1935-get-set-current-frame-jit/expected.txt +++ b/test/1935-get-set-current-frame-jit/expected.txt @@ -1,7 +1,5 @@ JNI_OnLoad called From GetLocalInt(), value is 42 -isInOsrCode? false Value is '42' Setting TARGET to 1337 -isInOsrCode? false Value is '1337' diff --git a/test/1935-get-set-current-frame-jit/run b/test/1935-get-set-current-frame-jit/run index 51875a7e86..e569d08ffd 100755 --- a/test/1935-get-set-current-frame-jit/run +++ b/test/1935-get-set-current-frame-jit/run @@ -14,5 +14,5 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Ask for stack traces to be dumped to a file rather than to stdout. -./default-run "$@" --jvmti +# Ensure the test is not subject to code collection +./default-run "$@" --jvmti --runtime-option -Xjitinitialsize:32M diff --git a/test/1935-get-set-current-frame-jit/src/Main.java b/test/1935-get-set-current-frame-jit/src/Main.java index 714a98aaf3..378aaf7a94 100644 --- a/test/1935-get-set-current-frame-jit/src/Main.java +++ b/test/1935-get-set-current-frame-jit/src/Main.java @@ -21,6 +21,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.nio.ByteBuffer; +import java.time.Instant; import java.util.concurrent.Semaphore; import java.util.Arrays; import java.util.Collection; @@ -49,9 +50,11 @@ public class Main { public static class IntRunner implements Runnable { private volatile boolean continueBusyLoop; private volatile boolean inBusyLoop; - public IntRunner() { + private final boolean expectOsr; + public IntRunner(boolean expectOsr) { this.continueBusyLoop = true; this.inBusyLoop = false; + this.expectOsr = expectOsr; } public void run() { int TARGET = 42; @@ -59,14 +62,23 @@ public class Main { while (continueBusyLoop) { inBusyLoop = true; } - int i = 0; - while (Main.isInterpreted() && i < 10000) { - Main.ensureJitCompiled(IntRunner.class, "run"); - i++; - } - // We shouldn't be doing OSR since we are using JVMTI and the get/set prevents OSR. + // Wait up to 300 seconds for OSR to kick in if we expect it. If we don't give up after only + // 3 seconds. + Instant osrDeadline = Instant.now().plusSeconds(expectOsr ? 600 : 3); + do { + // Don't actually do anything here. + inBusyLoop = true; + } while (hasJit() && !Main.isInOsrCode("run") && osrDeadline.compareTo(Instant.now()) > 0); + // We shouldn't be doing OSR since we are using JVMTI and the set prevents OSR. // Set local will also push us to interpreter but the get local may remain in compiled code. - System.out.println("isInOsrCode? " + (hasJit() && Main.isInOsrCode("run"))); + if (hasJit()) { + boolean inOsr = Main.isInOsrCode("run"); + if (expectOsr && !inOsr) { + throw new Error("Expected to be in OSR but was not."); + } else if (!expectOsr && inOsr) { + throw new Error("Expected not to be in OSR but was."); + } + } reportValue(TARGET); } public void waitForBusyLoopStart() { while (!inBusyLoop) {} } @@ -78,7 +90,7 @@ public class Main { public static void runGet() throws Exception { Method target = IntRunner.class.getDeclaredMethod("run"); // Get Int - IntRunner int_runner = new IntRunner(); + IntRunner int_runner = new IntRunner(true); Thread target_get = new Thread(int_runner, "GetLocalInt - Target"); target_get.start(); int_runner.waitForBusyLoopStart(); @@ -108,7 +120,7 @@ public class Main { public static void runSet() throws Exception { Method target = IntRunner.class.getDeclaredMethod("run"); // Set Int - IntRunner int_runner = new IntRunner(); + IntRunner int_runner = new IntRunner(false); Thread target_set = new Thread(int_runner, "SetLocalInt - Target"); target_set.start(); int_runner.waitForBusyLoopStart(); @@ -157,7 +169,6 @@ public class Main { throw new Error("Unable to find stack frame in method " + target + " on thread " + thr); } - public static native void ensureJitCompiled(Class k, String f); public static native boolean isInterpreted(); public static native boolean isInOsrCode(String methodName); public static native boolean hasJit(); -- GitLab From f85b3db7f63ef915bb48439c5f8ca34113bd524b Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 2 Apr 2018 18:16:21 -0700 Subject: [PATCH 190/749] Treat no profile the same as empty profile for app image generation Prevent cases where a full app image gets generated since this should be avoided for performance reasons. Bug: 77340429 Test: test-art-host-gtest Change-Id: Ib320fa7c56b4bf78af0df823a06b6881e6af7103 --- dex2oat/dex2oat.cc | 13 ++++++++++--- dex2oat/dex2oat_test.cc | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 6950b93e51..e2c53bbaa8 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -404,6 +404,7 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(" Example: --very-large-app-threshold=100000000"); UsageError(""); UsageError(" --app-image-fd=: specify output file descriptor for app image."); + UsageError(" The image is non-empty only if a profile is passed in."); UsageError(" Example: --app-image-fd=10"); UsageError(""); UsageError(" --app-image-file=: specify a file name for app image."); @@ -1479,9 +1480,15 @@ class Dex2Oat FINAL { } void LoadClassProfileDescriptors() { - if (profile_compilation_info_ != nullptr && IsImage()) { - Runtime* runtime = Runtime::Current(); - CHECK(runtime != nullptr); + if (!IsImage()) { + return; + } + // If we don't have a profile, treat it as an empty set of classes. b/77340429 + if (image_classes_ == nullptr) { + // May be non-null when --image-classes is passed in, in that case avoid clearing the list. + image_classes_.reset(new std::unordered_set()); + } + if (profile_compilation_info_ != nullptr) { // Filter out class path classes since we don't want to include these in the image. image_classes_.reset( new std::unordered_set( diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 0cd39ac11b..c890f8bef0 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -2093,4 +2093,36 @@ TEST_F(Dex2oatTest, CompactDexInZip) { ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) != 0) << status << " " << output_; } +TEST_F(Dex2oatTest, AppImageNoProfile) { + ScratchFile app_image_file; + const std::string out_dir = GetScratchDir(); + const std::string odex_location = out_dir + "/base.odex"; + GenerateOdexForTest(GetTestDexFileName("ManyMethods"), + odex_location, + CompilerFilter::Filter::kSpeedProfile, + { "--app-image-fd=" + std::to_string(app_image_file.GetFd()) }, + true, // expect_success + false, // use_fd + [](const OatFile&) {}); + // Open our generated oat file. + std::string error_msg; + std::unique_ptr odex_file(OatFile::Open(odex_location.c_str(), + odex_location.c_str(), + nullptr, + nullptr, + false, + /*low_4gb*/false, + odex_location.c_str(), + &error_msg)); + ASSERT_TRUE(odex_file != nullptr); + ImageHeader header = {}; + ASSERT_TRUE(app_image_file.GetFile()->PreadFully( + reinterpret_cast(&header), + sizeof(header), + /*offset*/ 0u)) << app_image_file.GetFile()->GetLength(); + EXPECT_GT(header.GetImageSection(ImageHeader::kSectionObjects).Size(), 0u); + EXPECT_EQ(header.GetImageSection(ImageHeader::kSectionArtMethods).Size(), 0u); + EXPECT_EQ(header.GetImageSection(ImageHeader::kSectionArtFields).Size(), 0u); +} + } // namespace art -- GitLab From 3dfaab08c5adbeaa43f363e3b8ae20f71b0a1e05 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Tue, 3 Apr 2018 15:34:40 +0100 Subject: [PATCH 191/749] Ignore failures on O devices in JDWP tests. These tests fail with the following exception when run with an unbundled ART (built from AOSP) on devices running Android O (MR1): java.net.SocketTimeoutException: Poll timed out This is a workaround to stop bot spam while we investigate the root of the issue. Test: art/tools/run-jdwp-tests.sh --mode=device Bug: 74725685 Change-Id: I9edbe1d5ec57fe7b9ecf2d5894fdff6639f30bd6 --- tools/run-jdwp-tests.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index de07a47df7..21ddcbc062 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -19,6 +19,16 @@ if [ ! -d libcore ]; then exit 1 fi +# Prevent JDWP tests from running on the following devices running +# Android O (they are failing because of a network-related issue), as +# a workaround for b/74725685: +# - FA7BN1A04406 (walleye device testing configuration aosp-poison/volantis-armv7-poison-debug) +# - FA7BN1A04412 (walleye device testing configuration aosp-poison/volantis-armv8-poison-ndebug) +# - FA7BN1A04433 (walleye device testing configuration aosp-poison/volantis-armv8-poison-debug) +case "$ANDROID_SERIAL" in + (FA7BN1A04406|FA7BN1A04412|FA7BN1A04433) exit 0;; +esac + source build/envsetup.sh >&/dev/null # for get_build_var, setpaths setpaths # include platform prebuilt java, javac, etc in $PATH. -- GitLab From 30d26960774aaa2c5ea647f0a90a562365efab03 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Thu, 29 Mar 2018 19:36:29 +0100 Subject: [PATCH 192/749] Start netd before running tests on Buildbot devices. Ensure netd is running, as otherwise the logcat would be spammed with the following messages on devices running Android O: E NetdConnector: Communications error: java.io.IOException: No such file or directory E mDnsConnector: Communications error: java.io.IOException: No such file or directory Test: art/tools/run-libcore-tests.sh --mode=device Test: art/tools/run-jdwp-tests.sh --mode=device Bug: 74725685 Change-Id: I8e26cf1170741e17c3fcf3bc10fc2f56696c9883 --- tools/setup-buildbot-device.sh | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh index 9373c69bf8..5ce7f5244e 100755 --- a/tools/setup-buildbot-device.sh +++ b/tools/setup-buildbot-device.sh @@ -57,14 +57,19 @@ 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): +# Ensure netd is running, as otherwise the logcat would be spammed +# with the following messages on devices running Android O: # -# android.system.ErrnoException: connect failed: EBADMSG (Not a data message) +# E NetdConnector: Communications error: java.io.IOException: No such file or directory +# E mDnsConnector: Communications error: java.io.IOException: No such file or directory # -# Turn it off to make these tests pass. -echo -e "${green}Turning off netd${nc}" -adb shell stop netd +# Netd was initially disabled as an attempt to solve issues with +# network-related libcore and JDWP tests failing on devices running +# Android O (MR1) (see b/74725685). These tests are currently +# disabled. When a better solution has been found, we should remove +# the following lines. +echo -e "${green}Turning on netd${nc}" +adb shell start netd adb shell getprop init.svc.netd echo -e "${green}List properties${nc}" -- GitLab From fdca4cb565c25a4a05078b2afc3f7abb374309e3 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 3 Apr 2018 13:29:13 -0700 Subject: [PATCH 193/749] Write shared data section for oatdump export dex Write this out so that the resulting dex can be dumped and inspected. Bug: 77469384 Test: test-art-host-gtest-oatdump_test Change-Id: Iadeaca0eaaf7c75a938dfc776801cf94c89d07f6 --- build/Android.gtest.mk | 6 ++++-- oatdump/oatdump.cc | 25 +++++++++++++++++++++---- oatdump/oatdump_test.cc | 5 +++++ oatdump/oatdump_test.h | 39 ++++++++++++++++++++++++--------------- 4 files changed, 54 insertions(+), 21 deletions(-) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index b342abe17c..b483e5f6f2 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -300,11 +300,13 @@ ART_GTEST_oatdump_test_HOST_DEPS := \ $(HOST_CORE_IMAGE_DEFAULT_64) \ $(HOST_CORE_IMAGE_DEFAULT_32) \ oatdumpd-host \ - oatdumpds-host + oatdumpds-host \ + dexdump2-host ART_GTEST_oatdump_test_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_DEFAULT_64) \ $(TARGET_CORE_IMAGE_DEFAULT_32) \ - oatdumpd-target + oatdumpd-target \ + dexdump2-target ART_GTEST_oatdump_image_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS) ART_GTEST_oatdump_image_test_TARGET_DEPS := $(ART_GTEST_oatdump_test_TARGET_DEPS) ART_GTEST_oatdump_app_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS) \ diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 433ed9aaee..3bff123386 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1180,6 +1180,17 @@ class OatDumper { } } + // Update header for shared section. + uint32_t shared_section_offset = 0u; + uint32_t shared_section_size = 0u; + if (dex_file->IsCompactDexFile()) { + CompactDexFile::Header* const header = + reinterpret_cast(const_cast(dex_file->Begin())); + shared_section_offset = header->data_off_; + shared_section_size = header->data_size_; + // The shared section will be serialized right after the dex file. + header->data_off_ = header->file_size_; + } // Verify output directory exists if (!OS::DirectoryExists(options_.export_dex_location_)) { // TODO: Extend OS::DirectoryExists if symlink support is required @@ -1226,16 +1237,22 @@ class OatDumper { return false; } - bool success = false; - success = file->WriteFully(dex_file->Begin(), fsize); - // } - + bool success = file->WriteFully(dex_file->Begin(), fsize); if (!success) { os << "Failed to write dex file"; file->Erase(); return false; } + if (shared_section_size != 0) { + success = file->WriteFully(dex_file->Begin() + shared_section_offset, shared_section_size); + if (!success) { + os << "Failed to write shared data section"; + file->Erase(); + return false; + } + } + if (file->FlushCloseOrErase() != 0) { os << "Flush and close failed"; return false; diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc index 00344691e0..18cb2fde88 100644 --- a/oatdump/oatdump_test.cc +++ b/oatdump/oatdump_test.cc @@ -75,6 +75,11 @@ TEST_F(OatDumpTest, TestExportDex) { std::string error_msg; ASSERT_TRUE(Exec(kDynamic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly, &error_msg)) << error_msg; + const std::string dex_location = tmp_dir_+ "/core-oj-hostdex.jar_export.dex"; + const std::string dexdump2 = GetExecutableFilePath("dexdump2", + /*is_debug*/false, + /*is_static*/false); + ASSERT_TRUE(ForkAndExecAndWait({dexdump2, "-d", dex_location}, &error_msg)) << error_msg; } TEST_F(OatDumpTest, TestExportDexStatic) { TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h index fac0bb234e..b85730d25e 100644 --- a/oatdump/oatdump_test.h +++ b/oatdump/oatdump_test.h @@ -70,20 +70,24 @@ class OatDumpTest : public CommonRuntimeTest { kStatic, // oatdump(d)s, dex2oat(d)s }; - // Returns path to the oatdump/dex2oat binary. - std::string GetExecutableFilePath(Flavor flavor, const char* name) { + // Returns path to the oatdump/dex2oat/dexdump binary. + std::string GetExecutableFilePath(const char* name, bool is_debug, bool is_static) { std::string root = GetTestAndroidRoot(); root += "/bin/"; root += name; - if (kIsDebugBuild) { + if (is_debug) { root += "d"; } - if (flavor == kStatic) { + if (is_static) { root += "s"; } return root; } + std::string GetExecutableFilePath(Flavor flavor, const char* name) { + return GetExecutableFilePath(name, kIsDebugBuild, flavor == kStatic); + } + enum Mode { kModeOat, kModeOatWithBootImage, @@ -127,17 +131,7 @@ class OatDumpTest : public CommonRuntimeTest { }; exec_argv.insert(exec_argv.end(), args.begin(), args.end()); - pid_t pid; - int pipe_fd; - bool result = ForkAndExec(exec_argv, &pid, &pipe_fd, error_msg); - if (result) { - close(pipe_fd); - int status = 0; - if (waitpid(pid, &status, 0) != -1) { - result = (status == 0); - } - } - return result; + return ForkAndExecAndWait(exec_argv, error_msg); } // Run the test with custom arguments. @@ -300,6 +294,21 @@ class OatDumpTest : public CommonRuntimeTest { } } + bool ForkAndExecAndWait(const std::vector& exec_argv, + /*out*/ std::string* error_msg) { + pid_t pid; + int pipe_fd; + bool result = ForkAndExec(exec_argv, &pid, &pipe_fd, error_msg); + if (result) { + close(pipe_fd); + int status = 0; + if (waitpid(pid, &status, 0) != -1) { + result = (status == 0); + } + } + return result; + } + std::string tmp_dir_; private: -- GitLab From 5aac921bae39686f2edecb5018d87952b0758b25 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Tue, 3 Apr 2018 14:06:43 -0700 Subject: [PATCH 194/749] Enabled nested min-max SIMDization for narrower operands. Bug: b/74026074 Test: test-art-host,target Change-Id: Ic6ee31be6192fb2b3bae3be8986da261a744be07 --- compiler/optimizing/loop_optimization.cc | 24 ++++++ .../src/ByteSimdMinMax.java | 46 +++++++++- .../src/ShortSimdMinMax.java | 46 +++++++++- test/679-checker-minmax/src/Main.java | 84 +++++++++++++++++++ 4 files changed, 198 insertions(+), 2 deletions(-) diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 71e24de141..b41b659083 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -153,6 +153,18 @@ static bool IsSignExtensionAndGet(HInstruction* instruction, return false; } } + // A MIN-MAX on narrower operands qualifies as well + // (returning the operator itself). + if (instruction->IsMin() || instruction->IsMax()) { + HBinaryOperation* min_max = instruction->AsBinaryOperation(); + DCHECK(min_max->GetType() == DataType::Type::kInt32 || + min_max->GetType() == DataType::Type::kInt64); + if (IsSignExtensionAndGet(min_max->InputAt(0), type, operand) && + IsSignExtensionAndGet(min_max->InputAt(1), type, operand)) { + *operand = min_max; + return true; + } + } return false; } @@ -216,6 +228,18 @@ static bool IsZeroExtensionAndGet(HInstruction* instruction, return false; } } + // A MIN-MAX on narrower operands qualifies as well + // (returning the operator itself). + if (instruction->IsMin() || instruction->IsMax()) { + HBinaryOperation* min_max = instruction->AsBinaryOperation(); + DCHECK(min_max->GetType() == DataType::Type::kInt32 || + min_max->GetType() == DataType::Type::kInt64); + if (IsZeroExtensionAndGet(min_max->InputAt(0), type, operand) && + IsZeroExtensionAndGet(min_max->InputAt(1), type, operand)) { + *operand = min_max; + return true; + } + } return false; } diff --git a/test/651-checker-simd-minmax/src/ByteSimdMinMax.java b/test/651-checker-simd-minmax/src/ByteSimdMinMax.java index b9954947f7..8dacd5d51e 100644 --- a/test/651-checker-simd-minmax/src/ByteSimdMinMax.java +++ b/test/651-checker-simd-minmax/src/ByteSimdMinMax.java @@ -129,7 +129,7 @@ public class ByteSimdMinMax { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-{ARM64,MIPS64}: void ByteSimdMinMax.doitMin100(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMin100(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 100 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none @@ -142,6 +142,38 @@ public class ByteSimdMinMax { } } + /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMinMax(byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant -11 loop:none + /// CHECK-DAG: <> IntConstant 23 loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none + /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int8 loop:<> outer_loop:none + /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Int8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none + private static void doitMinMax(byte[] x, byte[] y) { + int n = Math.min(x.length, y.length); + for (int i = 0; i < n; i++) { + x[i] = (byte) Math.max(-11, Math.min(y[i], 23)); + } + } + + /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMinMaxUnsigned(byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 11 loop:none + /// CHECK-DAG: <> IntConstant 23 loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none + /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint8 loop:<> outer_loop:none + /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Uint8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none + private static void doitMinMaxUnsigned(byte[] x, byte[] y) { + int n = Math.min(x.length, y.length); + for (int i = 0; i < n; i++) { + x[i] = (byte) Math.max(11, Math.min(y[i] & 0xff, 23)); + } + } + public static void main() { // Initialize cross-values for all possible values. int total = 256 * 256; @@ -184,6 +216,18 @@ public class ByteSimdMinMax { byte expected = (byte) Math.min(y[i], 100); expectEquals(expected, x[i]); } + doitMinMax(x, y); + for (int i = 0; i < total; i++) { + int s = y[i]; + byte expected = (byte) (s < -11 ? -11 : (s > 23 ? 23 : s)); + expectEquals(expected, x[i]); + } + doitMinMaxUnsigned(x, y); + for (int i = 0; i < total; i++) { + int u = y[i] & 0xff; + byte expected = (byte) (u < 11 ? 11 : (u > 23 ? 23 : u)); + expectEquals(expected, x[i]); + } System.out.println("ByteSimdMinMax passed"); } diff --git a/test/651-checker-simd-minmax/src/ShortSimdMinMax.java b/test/651-checker-simd-minmax/src/ShortSimdMinMax.java index aae78914d8..9075d8007c 100644 --- a/test/651-checker-simd-minmax/src/ShortSimdMinMax.java +++ b/test/651-checker-simd-minmax/src/ShortSimdMinMax.java @@ -129,7 +129,7 @@ public class ShortSimdMinMax { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-{ARM64,MIPS64}: void ShortSimdMinMax.doitMin100(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMin100(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 100 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none @@ -142,6 +142,38 @@ public class ShortSimdMinMax { } } + /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMinMax(short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant -1111 loop:none + /// CHECK-DAG: <> IntConstant 2323 loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none + /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int16 loop:<> outer_loop:none + /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Int16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none + private static void doitMinMax(short[] x, short[] y) { + int n = Math.min(x.length, y.length); + for (int i = 0; i < n; i++) { + x[i] = (short) Math.max(-1111, Math.min(y[i], 2323)); + } + } + + /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMinMaxUnsigned(short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 1111 loop:none + /// CHECK-DAG: <> IntConstant 2323 loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none + /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint16 loop:<> outer_loop:none + /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Uint16 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none + private static void doitMinMaxUnsigned(short[] x, short[] y) { + int n = Math.min(x.length, y.length); + for (int i = 0; i < n; i++) { + x[i] = (short) Math.max(1111, Math.min(y[i] & 0xffff, 2323)); + } + } + public static void main() { short[] interesting = { (short) 0x0000, (short) 0x0001, (short) 0x007f, @@ -198,6 +230,18 @@ public class ShortSimdMinMax { short expected = (short) Math.min(y[i], 100); expectEquals(expected, x[i]); } + doitMinMax(x, y); + for (int i = 0; i < total; i++) { + int s = y[i]; + short expected = (short) (s < -1111 ? -1111 : (s > 2323 ? 2323 : s)); + expectEquals(expected, x[i]); + } + doitMinMaxUnsigned(x, y); + for (int i = 0; i < total; i++) { + int u = y[i] & 0xffff; + short expected = (short) (u < 1111 ? 1111 : (u > 2323 ? 2323 : u)); + expectEquals(expected, x[i]); + } System.out.println("ShortSimdMinMax passed"); } diff --git a/test/679-checker-minmax/src/Main.java b/test/679-checker-minmax/src/Main.java index 4f0261c0a3..e330a5370e 100644 --- a/test/679-checker-minmax/src/Main.java +++ b/test/679-checker-minmax/src/Main.java @@ -19,6 +19,82 @@ */ public class Main { + // + // Direct intrinsics. + // + + /// CHECK-START: int Main.minI(int) instruction_simplifier (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 20 + /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMinIntInt + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.minI(int) instruction_simplifier (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 20 + /// CHECK-DAG: <> Min [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.minI(int) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + public static int minI(int a) { + return Math.min(a, 20); + } + + /// CHECK-START: long Main.minL(long) instruction_simplifier (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> LongConstant 20 + /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMinLongLong + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: long Main.minL(long) instruction_simplifier (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> LongConstant 20 + /// CHECK-DAG: <> Min [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: long Main.minL(long) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + public static long minL(long a) { + return Math.min(a, 20L); + } + + /// CHECK-START: int Main.maxI(int) instruction_simplifier (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 20 + /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMaxIntInt + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.maxI(int) instruction_simplifier (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 20 + /// CHECK-DAG: <> Max [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.maxI(int) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + public static int maxI(int a) { + return Math.max(a, 20); + } + + /// CHECK-START: long Main.maxL(long) instruction_simplifier (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> LongConstant 20 + /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMaxLongLong + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: long Main.maxL(long) instruction_simplifier (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> LongConstant 20 + /// CHECK-DAG: <> Max [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: long Main.maxL(long) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + public static long maxL(long a) { + return Math.max(a, 20L); + } + // // Different types. // @@ -343,6 +419,14 @@ public class Main { public static void main(String[] args) { // Types. + expectEquals(10, minI(10)); + expectEquals(20, minI(25)); + expectEquals(10L, minL(10L)); + expectEquals(20L, minL(25L)); + expectEquals(20, maxI(10)); + expectEquals(25, maxI(25)); + expectEquals(20L, maxL(10L)); + expectEquals(25L, maxL(25L)); expectEquals(10, min1(10, 20)); expectEquals(10, min2(10, 20)); expectEquals(10, min3(10, 20)); -- GitLab From 9cc68edcbd7c19505c33ea9053e9d499ed11ce02 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Tue, 3 Apr 2018 15:55:46 -0700 Subject: [PATCH 195/749] Ensure that art_sigsegv_fault is never inlined We were incorrectly inlining the art_sigsegv_fault function into FaultManager::HandleFault. This is a problem because we want native debuggers to break on this function instead of stopping on SIGSEGV, since we use those for various internal functions. By setting the art_sigsegv_fault function to be NO_INLINE we should not have this problem. Test: Manual inspection of libart.so Bug: 77528455 Change-Id: I77753cf79966011d7bfbea056bb4efc3f55d64df --- runtime/fault_handler.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc index 3015b10103..671079b128 100644 --- a/runtime/fault_handler.cc +++ b/runtime/fault_handler.cc @@ -37,7 +37,9 @@ namespace art { // Static fault manger object accessed by signal handler. FaultManager fault_manager; -extern "C" __attribute__((visibility("default"))) void art_sigsegv_fault() { +// This needs to be NO_INLINE since some debuggers do not read the inline-info to set a breakpoint +// if it isn't. +extern "C" NO_INLINE __attribute__((visibility("default"))) void art_sigsegv_fault() { // Set a breakpoint here to be informed when a SIGSEGV is unhandled by ART. VLOG(signals)<< "Caught unknown SIGSEGV in ART fault handler - chaining to next handler."; } -- GitLab From cf2de16a293583932b331548e87d60603ddca4e0 Mon Sep 17 00:00:00 2001 From: Alan Leung Date: Fri, 30 Mar 2018 20:18:20 +0000 Subject: [PATCH 196/749] Revert "Revert "Move most art test off DX"" This reverts commit 0e3a6addf60cbf006536d05aebe652e7ccddcd70. Reason for revert: The failing test (036-finalizer) is fixed by https://android-review.googlesource.com/637929 . Test: run-test --gcstress 036-finalizer && run-test 651-checker-simd-minmax Change-Id: Ib5efbd4abc57b26f7dfcf73edf201d7145fe8781 --- test/166-bad-interface-super/build | 20 ++++++++++++++++++++ test/646-checker-hadd-alt-char/build | 20 ++++++++++++++++++++ test/646-checker-hadd-alt-short/build | 20 ++++++++++++++++++++ test/646-checker-hadd-char/build | 20 ++++++++++++++++++++ test/646-checker-hadd-short/build | 20 ++++++++++++++++++++ test/651-checker-simd-minmax/build | 20 ++++++++++++++++++++ test/660-checker-simd-sad-byte/build | 20 ++++++++++++++++++++ test/660-checker-simd-sad-char/build | 20 ++++++++++++++++++++ test/660-checker-simd-sad-int/build | 20 ++++++++++++++++++++ test/660-checker-simd-sad-short/build | 20 ++++++++++++++++++++ test/660-checker-simd-sad-short2/build | 20 ++++++++++++++++++++ test/660-checker-simd-sad-short3/build | 20 ++++++++++++++++++++ test/661-checker-simd-reduc/build | 20 ++++++++++++++++++++ test/672-checker-throw-method/build | 20 ++++++++++++++++++++ test/673-checker-throw-vmethod/build | 20 ++++++++++++++++++++ test/678-checker-simd-saturation/build | 20 ++++++++++++++++++++ test/712-varhandle-invocations/build | 3 +++ test/Android.run-test.mk | 7 +------ test/etc/default-build | 2 +- test/run-test | 5 +---- test/testrunner/env.py | 3 --- test/testrunner/testrunner.py | 3 --- tools/build/var_list | 1 - 23 files changed, 326 insertions(+), 18 deletions(-) create mode 100644 test/166-bad-interface-super/build create mode 100644 test/646-checker-hadd-alt-char/build create mode 100644 test/646-checker-hadd-alt-short/build create mode 100644 test/646-checker-hadd-char/build create mode 100644 test/646-checker-hadd-short/build create mode 100644 test/651-checker-simd-minmax/build create mode 100644 test/660-checker-simd-sad-byte/build create mode 100644 test/660-checker-simd-sad-char/build create mode 100644 test/660-checker-simd-sad-int/build create mode 100644 test/660-checker-simd-sad-short/build create mode 100644 test/660-checker-simd-sad-short2/build create mode 100644 test/660-checker-simd-sad-short3/build create mode 100644 test/661-checker-simd-reduc/build create mode 100644 test/672-checker-throw-method/build create mode 100644 test/673-checker-throw-vmethod/build create mode 100644 test/678-checker-simd-saturation/build diff --git a/test/166-bad-interface-super/build b/test/166-bad-interface-super/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/166-bad-interface-super/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/646-checker-hadd-alt-char/build b/test/646-checker-hadd-alt-char/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/646-checker-hadd-alt-char/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/646-checker-hadd-alt-short/build b/test/646-checker-hadd-alt-short/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/646-checker-hadd-alt-short/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/646-checker-hadd-char/build b/test/646-checker-hadd-char/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/646-checker-hadd-char/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/646-checker-hadd-short/build b/test/646-checker-hadd-short/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/646-checker-hadd-short/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/651-checker-simd-minmax/build b/test/651-checker-simd-minmax/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/651-checker-simd-minmax/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/660-checker-simd-sad-byte/build b/test/660-checker-simd-sad-byte/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/660-checker-simd-sad-byte/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/660-checker-simd-sad-char/build b/test/660-checker-simd-sad-char/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/660-checker-simd-sad-char/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/660-checker-simd-sad-int/build b/test/660-checker-simd-sad-int/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/660-checker-simd-sad-int/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/660-checker-simd-sad-short/build b/test/660-checker-simd-sad-short/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/660-checker-simd-sad-short/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/660-checker-simd-sad-short2/build b/test/660-checker-simd-sad-short2/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/660-checker-simd-sad-short2/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/660-checker-simd-sad-short3/build b/test/660-checker-simd-sad-short3/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/660-checker-simd-sad-short3/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/661-checker-simd-reduc/build b/test/661-checker-simd-reduc/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/661-checker-simd-reduc/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/672-checker-throw-method/build b/test/672-checker-throw-method/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/672-checker-throw-method/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/673-checker-throw-vmethod/build b/test/673-checker-throw-vmethod/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/673-checker-throw-vmethod/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/678-checker-simd-saturation/build b/test/678-checker-simd-saturation/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/678-checker-simd-saturation/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/712-varhandle-invocations/build b/test/712-varhandle-invocations/build index 253765be91..6d4429f0ef 100755 --- a/test/712-varhandle-invocations/build +++ b/test/712-varhandle-invocations/build @@ -35,5 +35,8 @@ python3 ./util-src/generate_java.py "${GENERATED_SRC}" ${MANUAL_TESTS} # Desugar is not happy with our Java 9 byte code, it shouldn't be necessary here anyway. export USE_DESUGAR=false +# See b/65168732 +export USE_D8=false + # Invoke default build with increased heap size for dx ./default-build "$@" --experimental var-handles --dx-vm-option -JXmx384m diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 6633958140..f8bebdd35f 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -21,17 +21,12 @@ include art/build/Android.common_test.mk TEST_ART_RUN_TEST_DEPENDENCIES := \ $(HOST_OUT_EXECUTABLES)/dx \ $(HOST_OUT_EXECUTABLES)/d8 \ + $(HOST_OUT_EXECUTABLES)/d8-compat-dx \ $(HOST_OUT_EXECUTABLES)/hiddenapi \ $(HOST_OUT_EXECUTABLES)/jasmin \ $(HOST_OUT_EXECUTABLES)/smali \ $(HOST_OUT_JAVA_LIBRARIES)/desugar.jar -# Add d8 dependency, if enabled. -ifeq ($(USE_D8),true) -TEST_ART_RUN_TEST_DEPENDENCIES += \ - $(HOST_OUT_EXECUTABLES)/d8-compat-dx -endif - # We need dex2oat and dalvikvm on the target as well as the core images (all images as we sync # only once). TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUTS) diff --git a/test/etc/default-build b/test/etc/default-build index 9de7294a59..dd5560213a 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -317,7 +317,7 @@ function make_dex() { fi local dexer="${DX}" - if [ ${USE_D8} = "true" ]; then + if [[ "${USE_D8}" != "false" ]]; then dexer="${ANDROID_HOST_OUT}/bin/d8-compat-dx" fi diff --git a/test/run-test b/test/run-test index 5b43b52b41..5f85b0875b 100755 --- a/test/run-test +++ b/test/run-test @@ -45,7 +45,7 @@ export JAVAC="javac -g -Xlint:-options" export RUN="${progdir}/etc/run-test-jar" export DEX_LOCATION=/data/run-test/${test_dir} export NEED_DEX="true" -export USE_D8="false" +export USE_D8="true" export USE_JACK="false" export USE_DESUGAR="true" export SMALI_ARGS="" @@ -365,9 +365,6 @@ while true; do elif [ "x$1" = "x--build-only" ]; then build_only="yes" shift - elif [ "x$1" = "x--build-with-d8" ]; then - USE_D8="true" - shift elif [ "x$1" = "x--build-with-javac-dx" ]; then USE_JACK="false" shift diff --git a/test/testrunner/env.py b/test/testrunner/env.py index 539499173c..7564f5a6b4 100644 --- a/test/testrunner/env.py +++ b/test/testrunner/env.py @@ -71,9 +71,6 @@ ANDROID_BUILD_TOP = _get_android_build_top() # Compiling with jack? Possible values in (True, False, 'default') ANDROID_COMPILE_WITH_JACK = _get_build_var_boolean('ANDROID_COMPILE_WITH_JACK', 'default') -# Follow the build system's D8 usage. -USE_D8_BY_DEFAULT = _get_build_var_boolean('USE_D8_BY_DEFAULT', False) - # Directory used for temporary test files on the host. ART_HOST_TEST_DIR = tempfile.mkdtemp(prefix = 'test-art-') diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 0cfb661019..88b509d3b7 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -485,9 +485,6 @@ def run_tests(tests): elif env.ANDROID_COMPILE_WITH_JACK == False: options_test += ' --build-with-javac-dx' - if env.USE_D8_BY_DEFAULT == True: - options_test += ' --build-with-d8' - # TODO(http://36039166): This is a temporary solution to # fix build breakages. options_test = (' --output-path %s') % ( diff --git a/tools/build/var_list b/tools/build/var_list index 3727741dac..adcb066f7c 100644 --- a/tools/build/var_list +++ b/tools/build/var_list @@ -34,5 +34,4 @@ HOST_PREFER_32_BIT HOST_OUT_EXECUTABLES ANDROID_JAVA_TOOLCHAIN ANDROID_COMPILE_WITH_JACK -USE_D8_BY_DEFAULT -- GitLab From c9dd2207dfdab42586b1d6a5e7f11cf2fcea3a7a Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 23 Nov 2017 16:05:19 +0000 Subject: [PATCH 197/749] Compile link-time thunks in codegen. Prepare for experimenting with Baker read barrier marking introspection entrypoints for JIT. Test: m test-art-host-gtest Test: Compare compiled boot*.oat before and after (no diff). Test: Pixel 2 XL boots. Bug: 36141117 Change-Id: Idb413a31b158db4bf89a8707ea46dd167a06f110 --- compiler/cfi_test.h | 21 +- compiler/driver/compiled_method_storage.cc | 95 ++++- compiler/driver/compiled_method_storage.h | 26 ++ compiler/jni/jni_cfi_test.cc | 6 +- .../linker/arm/relative_patcher_arm_base.cc | 44 ++- .../linker/arm/relative_patcher_arm_base.h | 9 +- .../linker/arm/relative_patcher_thumb2.cc | 305 +-------------- compiler/linker/arm/relative_patcher_thumb2.h | 78 +--- .../arm/relative_patcher_thumb2_test.cc | 96 +++-- .../linker/arm64/relative_patcher_arm64.cc | 253 +----------- .../linker/arm64/relative_patcher_arm64.h | 54 +-- .../arm64/relative_patcher_arm64_test.cc | 87 ++-- compiler/linker/linker_patch.h | 2 +- compiler/linker/relative_patcher.cc | 13 +- compiler/linker/relative_patcher.h | 27 +- compiler/linker/relative_patcher_test.h | 72 +++- compiler/optimizing/code_generator.cc | 12 + compiler/optimizing/code_generator.h | 6 + compiler/optimizing/code_generator_arm64.cc | 354 ++++++++++++++--- compiler/optimizing/code_generator_arm64.h | 89 ++++- .../optimizing/code_generator_arm_vixl.cc | 370 ++++++++++++++++-- compiler/optimizing/code_generator_arm_vixl.h | 110 +++++- compiler/optimizing/codegen_test_utils.h | 8 +- compiler/optimizing/optimizing_cfi_test.cc | 10 +- compiler/optimizing/optimizing_compiler.cc | 26 +- dex2oat/dex2oat.cc | 4 +- dex2oat/linker/image_test.h | 3 +- dex2oat/linker/multi_oat_relative_patcher.cc | 20 +- dex2oat/linker/multi_oat_relative_patcher.h | 19 +- .../linker/multi_oat_relative_patcher_test.cc | 2 +- dex2oat/linker/oat_writer_test.cc | 3 +- 31 files changed, 1336 insertions(+), 888 deletions(-) diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h index 29ff235cea..581edaa773 100644 --- a/compiler/cfi_test.h +++ b/compiler/cfi_test.h @@ -37,8 +37,8 @@ constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_DEBUG_FRAME_FORMAT; class CFITest : public dwarf::DwarfTest { public: void GenerateExpected(FILE* f, InstructionSet isa, const char* isa_str, - const std::vector& actual_asm, - const std::vector& actual_cfi) { + ArrayRef actual_asm, + ArrayRef actual_cfi) { std::vector lines; // Print the raw bytes. fprintf(f, "static constexpr uint8_t expected_asm_%s[] = {", isa_str); @@ -50,11 +50,18 @@ class CFITest : public dwarf::DwarfTest { // Pretty-print CFI opcodes. constexpr bool is64bit = false; dwarf::DebugFrameOpCodeWriter<> initial_opcodes; - dwarf::WriteCIE(is64bit, dwarf::Reg(8), - initial_opcodes, kCFIFormat, &debug_frame_data_); + dwarf::WriteCIE(is64bit, dwarf::Reg(8), initial_opcodes, kCFIFormat, &debug_frame_data_); std::vector debug_frame_patches; - dwarf::WriteFDE(is64bit, 0, 0, 0, actual_asm.size(), ArrayRef(actual_cfi), - kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches); + dwarf::WriteFDE(is64bit, + /* section_address */ 0, + /* cie_address */ 0, + /* code_address */ 0, + actual_asm.size(), + actual_cfi, + kCFIFormat, + /* buffer_address */ 0, + &debug_frame_data_, + &debug_frame_patches); ReformatCfi(Objdump(false, "-W"), &lines); // Pretty-print assembly. const uint8_t* asm_base = actual_asm.data(); @@ -142,7 +149,7 @@ class CFITest : public dwarf::DwarfTest { } // Pretty-print byte array. 12 bytes per line. - static void HexDump(FILE* f, const std::vector& data) { + static void HexDump(FILE* f, ArrayRef data) { for (size_t i = 0; i < data.size(); i++) { fprintf(f, i % 12 == 0 ? "\n " : " "); // Whitespace. fprintf(f, "0x%02X,", data[i]); diff --git a/compiler/driver/compiled_method_storage.cc b/compiler/driver/compiled_method_storage.cc index a26a985ff9..aa8277edb4 100644 --- a/compiler/driver/compiled_method_storage.cc +++ b/compiler/driver/compiled_method_storage.cc @@ -161,6 +161,46 @@ class CompiledMethodStorage::LengthPrefixedArrayAlloc { SwapSpace* const swap_space_; }; +class CompiledMethodStorage::ThunkMapKey { + public: + ThunkMapKey(linker::LinkerPatch::Type type, uint32_t custom_value1, uint32_t custom_value2) + : type_(type), custom_value1_(custom_value1), custom_value2_(custom_value2) {} + + bool operator<(const ThunkMapKey& other) const { + if (custom_value1_ != other.custom_value1_) { + return custom_value1_ < other.custom_value1_; + } + if (custom_value2_ != other.custom_value2_) { + return custom_value2_ < other.custom_value2_; + } + return type_ < other.type_; + } + + private: + linker::LinkerPatch::Type type_; + uint32_t custom_value1_; + uint32_t custom_value2_; +}; + +class CompiledMethodStorage::ThunkMapValue { + public: + ThunkMapValue(std::vector>&& code, + const std::string& debug_name) + : code_(std::move(code)), debug_name_(debug_name) {} + + ArrayRef GetCode() const { + return ArrayRef(code_); + } + + const std::string& GetDebugName() const { + return debug_name_; + } + + private: + std::vector> code_; + std::string debug_name_; +}; + CompiledMethodStorage::CompiledMethodStorage(int swap_fd) : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)), dedupe_enabled_(true), @@ -171,7 +211,9 @@ CompiledMethodStorage::CompiledMethodStorage(int swap_fd) LengthPrefixedArrayAlloc(swap_space_.get())), dedupe_cfi_info_("dedupe cfi info", LengthPrefixedArrayAlloc(swap_space_.get())), dedupe_linker_patches_("dedupe cfi info", - LengthPrefixedArrayAlloc(swap_space_.get())) { + LengthPrefixedArrayAlloc(swap_space_.get())), + thunk_map_lock_("thunk_map_lock"), + thunk_map_(std::less(), SwapAllocator(swap_space_.get())) { } CompiledMethodStorage::~CompiledMethodStorage() { @@ -237,4 +279,55 @@ void CompiledMethodStorage::ReleaseLinkerPatches( ReleaseArrayIfNotDeduplicated(linker_patches); } +CompiledMethodStorage::ThunkMapKey CompiledMethodStorage::GetThunkMapKey( + const linker::LinkerPatch& linker_patch) { + uint32_t custom_value1 = 0u; + uint32_t custom_value2 = 0u; + switch (linker_patch.GetType()) { + case linker::LinkerPatch::Type::kBakerReadBarrierBranch: + custom_value1 = linker_patch.GetBakerCustomValue1(); + custom_value2 = linker_patch.GetBakerCustomValue2(); + break; + case linker::LinkerPatch::Type::kCallRelative: + // No custom values. + break; + default: + LOG(FATAL) << "Unexpected patch type: " << linker_patch.GetType(); + UNREACHABLE(); + } + return ThunkMapKey(linker_patch.GetType(), custom_value1, custom_value2); +} + +ArrayRef CompiledMethodStorage::GetThunkCode(const linker::LinkerPatch& linker_patch, + /*out*/ std::string* debug_name) { + ThunkMapKey key = GetThunkMapKey(linker_patch); + MutexLock lock(Thread::Current(), thunk_map_lock_); + auto it = thunk_map_.find(key); + if (it != thunk_map_.end()) { + const ThunkMapValue& value = it->second; + if (debug_name != nullptr) { + *debug_name = value.GetDebugName(); + } + return value.GetCode(); + } else { + if (debug_name != nullptr) { + *debug_name = std::string(); + } + return ArrayRef(); + } +} + +void CompiledMethodStorage::SetThunkCode(const linker::LinkerPatch& linker_patch, + ArrayRef code, + const std::string& debug_name) { + DCHECK(!code.empty()); + ThunkMapKey key = GetThunkMapKey(linker_patch); + std::vector> code_copy( + code.begin(), code.end(), SwapAllocator(swap_space_.get())); + ThunkMapValue value(std::move(code_copy), debug_name); + MutexLock lock(Thread::Current(), thunk_map_lock_); + // Note: Multiple threads can try and compile the same thunk, so this may not create a new entry. + thunk_map_.emplace(key, std::move(value)); +} + } // namespace art diff --git a/compiler/driver/compiled_method_storage.h b/compiler/driver/compiled_method_storage.h index 249f06c20f..1634facb7c 100644 --- a/compiler/driver/compiled_method_storage.h +++ b/compiler/driver/compiled_method_storage.h @@ -18,6 +18,7 @@ #define ART_COMPILER_DRIVER_COMPILED_METHOD_STORAGE_H_ #include +#include #include #include "base/array_ref.h" @@ -67,7 +68,29 @@ class CompiledMethodStorage { const ArrayRef& linker_patches); void ReleaseLinkerPatches(const LengthPrefixedArray* linker_patches); + // Returns the code associated with the given patch. + // If the code has not been set, returns empty data. + // If `debug_name` is not null, stores the associated debug name in `*debug_name`. + ArrayRef GetThunkCode(const linker::LinkerPatch& linker_patch, + /*out*/ std::string* debug_name = nullptr); + + // Sets the code and debug name associated with the given patch. + void SetThunkCode(const linker::LinkerPatch& linker_patch, + ArrayRef code, + const std::string& debug_name); + private: + class ThunkMapKey; + class ThunkMapValue; + using ThunkMapValueType = std::pair; + using ThunkMap = std::map, + SwapAllocator>; + static_assert(std::is_same::value, "Value type check."); + + static ThunkMapKey GetThunkMapKey(const linker::LinkerPatch& linker_patch); + template const LengthPrefixedArray* AllocateOrDeduplicateArray(const ArrayRef& data, DedupeSetType* dedupe_set); @@ -102,6 +125,9 @@ class CompiledMethodStorage { ArrayDedupeSet dedupe_cfi_info_; ArrayDedupeSet dedupe_linker_patches_; + Mutex thunk_map_lock_; + ThunkMap thunk_map_ GUARDED_BY(thunk_map_lock_); + DISALLOW_COPY_AND_ASSIGN(CompiledMethodStorage); }; diff --git a/compiler/jni/jni_cfi_test.cc b/compiler/jni/jni_cfi_test.cc index 236b5c0c2e..38f95488a9 100644 --- a/compiler/jni/jni_cfi_test.cc +++ b/compiler/jni/jni_cfi_test.cc @@ -94,7 +94,11 @@ class JNICFITest : public CFITest { const std::vector& actual_cfi = *(jni_asm->cfi().data()); if (kGenerateExpected) { - GenerateExpected(stdout, isa, isa_str, actual_asm, actual_cfi); + GenerateExpected(stdout, + isa, + isa_str, + ArrayRef(actual_asm), + ArrayRef(actual_cfi)); } else { EXPECT_EQ(expected_asm, actual_asm); EXPECT_EQ(expected_cfi, actual_cfi); diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc index 6e0286afac..7cb8ae55c5 100644 --- a/compiler/linker/arm/relative_patcher_arm_base.cc +++ b/compiler/linker/arm/relative_patcher_arm_base.cc @@ -30,8 +30,9 @@ namespace linker { class ArmBaseRelativePatcher::ThunkData { public: - ThunkData(std::vector code, uint32_t max_next_offset) - : code_(std::move(code)), + ThunkData(ArrayRef code, const std::string& debug_name, uint32_t max_next_offset) + : code_(code), + debug_name_(debug_name), offsets_(), max_next_offset_(max_next_offset), pending_offset_(0u) { @@ -45,7 +46,11 @@ class ArmBaseRelativePatcher::ThunkData { } ArrayRef GetCode() const { - return ArrayRef(code_); + return code_; + } + + const std::string& GetDebugName() const { + return debug_name_; } bool NeedsNextThunk() const { @@ -142,10 +147,11 @@ class ArmBaseRelativePatcher::ThunkData { } private: - std::vector code_; // The code of the thunk. - std::vector offsets_; // Offsets at which the thunk needs to be written. - uint32_t max_next_offset_; // The maximum offset at which the next thunk can be placed. - uint32_t pending_offset_; // The index of the next offset to write. + const ArrayRef code_; // The code of the thunk. + const std::string debug_name_; // The debug name of the thunk. + std::vector offsets_; // Offsets at which the thunk needs to be written. + uint32_t max_next_offset_; // The maximum offset at which the next thunk can be placed. + uint32_t pending_offset_; // The index of the next offset to write. }; class ArmBaseRelativePatcher::PendingThunkComparator { @@ -239,14 +245,13 @@ std::vector ArmBaseRelativePatcher::GenerateThunkDebugIn std::vector result; result.reserve(number_of_thunks); for (auto&& entry : thunks_) { - const ThunkKey& key = entry.first; const ThunkData& data = entry.second; size_t start = data.IndexOfFirstThunkAtOrAfter(executable_offset); if (start == data.NumberOfThunks()) { continue; } // Get the base name to use for the first occurrence of the thunk. - std::string base_name = GetThunkDebugName(key); + std::string base_name = data.GetDebugName(); for (size_t i = start, num = data.NumberOfThunks(); i != num; ++i) { debug::MethodDebugInfo info = {}; if (i == 0u) { @@ -267,9 +272,11 @@ std::vector ArmBaseRelativePatcher::GenerateThunkDebugIn return result; } -ArmBaseRelativePatcher::ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider, +ArmBaseRelativePatcher::ArmBaseRelativePatcher(RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider, InstructionSet instruction_set) - : provider_(provider), + : thunk_provider_(thunk_provider), + target_provider_(target_provider), instruction_set_(instruction_set), thunks_(), unprocessed_method_call_patches_(), @@ -398,7 +405,7 @@ void ArmBaseRelativePatcher::ProcessPatches(const CompiledMethod* compiled_metho unprocessed_method_call_patches_.emplace_back(patch_offset, patch.TargetMethod()); if (method_call_thunk_ == nullptr) { uint32_t max_next_offset = CalculateMaxNextOffset(patch_offset, key); - auto it = thunks_.Put(key, ThunkData(CompileThunk(key), max_next_offset)); + auto it = thunks_.Put(key, ThunkDataForPatch(patch, max_next_offset)); method_call_thunk_ = &it->second; AddUnreservedThunk(method_call_thunk_); } else { @@ -409,7 +416,7 @@ void ArmBaseRelativePatcher::ProcessPatches(const CompiledMethod* compiled_metho auto lb = thunks_.lower_bound(key); if (lb == thunks_.end() || thunks_.key_comp()(key, lb->first)) { uint32_t max_next_offset = CalculateMaxNextOffset(patch_offset, key); - auto it = thunks_.PutBefore(lb, key, ThunkData(CompileThunk(key), max_next_offset)); + auto it = thunks_.PutBefore(lb, key, ThunkDataForPatch(patch, max_next_offset)); AddUnreservedThunk(&it->second); } else { old_data = &lb->second; @@ -477,7 +484,7 @@ void ArmBaseRelativePatcher::ResolveMethodCalls(uint32_t quick_code_offset, break; } } else { - auto result = provider_->FindMethodOffset(target_method); + auto result = target_provider_->FindMethodOffset(target_method); if (!result.first) { break; } @@ -518,5 +525,14 @@ inline uint32_t ArmBaseRelativePatcher::CalculateMaxNextOffset(uint32_t patch_of GetInstructionSetAlignment(instruction_set_)); } +inline ArmBaseRelativePatcher::ThunkData ArmBaseRelativePatcher::ThunkDataForPatch( + const LinkerPatch& patch, uint32_t max_next_offset) { + ArrayRef code; + std::string debug_name; + thunk_provider_->GetThunkCode(patch, &code, &debug_name); + DCHECK(!code.empty()); + return ThunkData(code, debug_name, max_next_offset); +} + } // namespace linker } // namespace art diff --git a/compiler/linker/arm/relative_patcher_arm_base.h b/compiler/linker/arm/relative_patcher_arm_base.h index ee09bf96b3..963d6690b0 100644 --- a/compiler/linker/arm/relative_patcher_arm_base.h +++ b/compiler/linker/arm/relative_patcher_arm_base.h @@ -37,7 +37,8 @@ class ArmBaseRelativePatcher : public RelativePatcher { std::vector GenerateThunkDebugInfo(uint32_t executable_offset) OVERRIDE; protected: - ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider, + ArmBaseRelativePatcher(RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider, InstructionSet instruction_set); ~ArmBaseRelativePatcher(); @@ -94,8 +95,6 @@ class ArmBaseRelativePatcher : public RelativePatcher { uint32_t CalculateMethodCallDisplacement(uint32_t patch_offset, uint32_t target_offset); - virtual std::vector CompileThunk(const ThunkKey& key) = 0; - virtual std::string GetThunkDebugName(const ThunkKey& key) = 0; virtual uint32_t MaxPositiveDisplacement(const ThunkKey& key) = 0; virtual uint32_t MaxNegativeDisplacement(const ThunkKey& key) = 0; @@ -108,8 +107,10 @@ class ArmBaseRelativePatcher : public RelativePatcher { void ResolveMethodCalls(uint32_t quick_code_offset, MethodReference method_ref); uint32_t CalculateMaxNextOffset(uint32_t patch_offset, const ThunkKey& key); + ThunkData ThunkDataForPatch(const LinkerPatch& patch, uint32_t max_next_offset); - RelativePatcherTargetProvider* const provider_; + RelativePatcherThunkProvider* const thunk_provider_; + RelativePatcherTargetProvider* const target_provider_; const InstructionSet instruction_set_; // The data for all thunks. diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc index 78755176e4..7400d11c31 100644 --- a/compiler/linker/arm/relative_patcher_thumb2.cc +++ b/compiler/linker/arm/relative_patcher_thumb2.cc @@ -48,8 +48,9 @@ constexpr uint32_t kMaxMethodCallNegativeDisplacement = (1u << 24) - kPcDisplace constexpr uint32_t kMaxBcondPositiveDisplacement = (1u << 20) - 2u + kPcDisplacement; constexpr uint32_t kMaxBcondNegativeDisplacement = (1u << 20) - kPcDisplacement; -Thumb2RelativePatcher::Thumb2RelativePatcher(RelativePatcherTargetProvider* provider) - : ArmBaseRelativePatcher(provider, InstructionSet::kThumb2) { +Thumb2RelativePatcher::Thumb2RelativePatcher(RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider) + : ArmBaseRelativePatcher(thunk_provider, target_provider, InstructionSet::kThumb2) { } void Thumb2RelativePatcher::PatchCall(std::vector* code, @@ -110,62 +111,6 @@ void Thumb2RelativePatcher::PatchBakerReadBarrierBranch(std::vector* co uint32_t insn = GetInsn32(code, literal_offset); DCHECK_EQ(insn, 0xf0408000); // BNE +0 (unpatched) ThunkKey key = GetBakerThunkKey(patch); - if (kIsDebugBuild) { - const uint32_t encoded_data = key.GetCustomValue1(); - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - // Check that the next instruction matches the expected LDR. - switch (kind) { - case BakerReadBarrierKind::kField: { - BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); - if (width == BakerReadBarrierWidth::kWide) { - DCHECK_GE(code->size() - literal_offset, 8u); - uint32_t next_insn = GetInsn32(code, literal_offset + 4u); - // LDR (immediate), encoding T3, with correct base_reg. - CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xffff0000u, 0xf8d00000u | (base_reg << 16)); - } else { - DCHECK_GE(code->size() - literal_offset, 6u); - uint32_t next_insn = GetInsn16(code, literal_offset + 4u); - // LDR (immediate), encoding T1, with correct base_reg. - CheckValidReg(next_insn & 0x7u); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xf838u, 0x6800u | (base_reg << 3)); - } - break; - } - case BakerReadBarrierKind::kArray: { - DCHECK_GE(code->size() - literal_offset, 8u); - uint32_t next_insn = GetInsn32(code, literal_offset + 4u); - // LDR (register) with correct base_reg, S=1 and option=011 (LDR Wt, [Xn, Xm, LSL #2]). - CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xffff0ff0u, 0xf8500020u | (base_reg << 16)); - CheckValidReg(next_insn & 0xf); // Check index register - break; - } - case BakerReadBarrierKind::kGcRoot: { - BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); - if (width == BakerReadBarrierWidth::kWide) { - DCHECK_GE(literal_offset, 4u); - uint32_t prev_insn = GetInsn32(code, literal_offset - 4u); - // LDR (immediate), encoding T3, with correct root_reg. - const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(prev_insn & 0xfff0f000u, 0xf8d00000u | (root_reg << 12)); - } else { - DCHECK_GE(literal_offset, 2u); - uint32_t prev_insn = GetInsn16(code, literal_offset - 2u); - // LDR (immediate), encoding T1, with correct root_reg. - const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(prev_insn & 0xf807u, 0x6800u | root_reg); - } - break; - } - default: - LOG(FATAL) << "Unexpected type: " << static_cast(key.GetType()); - UNREACHABLE(); - } - } uint32_t target_offset = GetThunkTargetOffset(key, patch_offset); DCHECK_ALIGNED(target_offset, 4u); uint32_t disp = target_offset - (patch_offset + kPcDisplacement); @@ -178,250 +123,6 @@ void Thumb2RelativePatcher::PatchBakerReadBarrierBranch(std::vector* co SetInsn32(code, literal_offset, insn); } -#define __ assembler.GetVIXLAssembler()-> - -static void EmitGrayCheckAndFastPath(arm::ArmVIXLAssembler& assembler, - vixl::aarch32::Register base_reg, - vixl::aarch32::MemOperand& lock_word, - vixl::aarch32::Label* slow_path, - int32_t raw_ldr_offset) { - using namespace vixl::aarch32; // NOLINT(build/namespaces) - // Load the lock word containing the rb_state. - __ Ldr(ip, lock_word); - // Given the numeric representation, it's enough to check the low bit of the rb_state. - static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0"); - static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1"); - __ Tst(ip, Operand(LockWord::kReadBarrierStateMaskShifted)); - __ B(ne, slow_path, /* is_far_target */ false); - __ Add(lr, lr, raw_ldr_offset); - // Introduce a dependency on the lock_word including rb_state, - // to prevent load-load reordering, and without using - // a memory barrier (which would be more expensive). - __ Add(base_reg, base_reg, Operand(ip, LSR, 32)); - __ Bx(lr); // And return back to the function. - // Note: The fake dependency is unnecessary for the slow path. -} - -// Load the read barrier introspection entrypoint in register `entrypoint` -static void LoadReadBarrierMarkIntrospectionEntrypoint(arm::ArmVIXLAssembler& assembler, - vixl::aarch32::Register entrypoint) { - using vixl::aarch32::MemOperand; - using vixl::aarch32::ip; - // Thread Register. - const vixl::aarch32::Register tr = vixl::aarch32::r9; - - // The register where the read barrier introspection entrypoint is loaded - // is fixed: `Thumb2RelativePatcher::kBakerCcEntrypointRegister` (R4). - DCHECK_EQ(entrypoint.GetCode(), Thumb2RelativePatcher::kBakerCcEntrypointRegister); - // entrypoint = Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection. - DCHECK_EQ(ip.GetCode(), 12u); - const int32_t entry_point_offset = - Thread::ReadBarrierMarkEntryPointsOffset(ip.GetCode()); - __ Ldr(entrypoint, MemOperand(tr, entry_point_offset)); -} - -void Thumb2RelativePatcher::CompileBakerReadBarrierThunk(arm::ArmVIXLAssembler& assembler, - uint32_t encoded_data) { - using namespace vixl::aarch32; // NOLINT(build/namespaces) - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - switch (kind) { - case BakerReadBarrierKind::kField: { - // Check if the holder is gray and, if not, add fake dependency to the base register - // and return to the LDR instruction to load the reference. Otherwise, use introspection - // to load the reference and call the entrypoint (in kBakerCcEntrypointRegister) - // that performs further checks on the reference and marks it if needed. - Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(base_reg.GetCode()); - Register holder_reg(BakerReadBarrierSecondRegField::Decode(encoded_data)); - CheckValidReg(holder_reg.GetCode()); - BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip); - // If base_reg differs from holder_reg, the offset was too large and we must have - // emitted an explicit null check before the load. Otherwise, we need to null-check - // the holder as we do not necessarily do that check before going to the thunk. - vixl::aarch32::Label throw_npe; - if (holder_reg.Is(base_reg)) { - __ CompareAndBranchIfZero(holder_reg, &throw_npe, /* is_far_target */ false); - } - vixl::aarch32::Label slow_path; - MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value()); - const int32_t raw_ldr_offset = (width == BakerReadBarrierWidth::kWide) - ? BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET - : BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET; - EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset); - __ Bind(&slow_path); - const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 + - raw_ldr_offset; - Register ep_reg(kBakerCcEntrypointRegister); - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); - if (width == BakerReadBarrierWidth::kWide) { - MemOperand ldr_half_address(lr, ldr_offset + 2); - __ Ldrh(ip, ldr_half_address); // Load the LDR immediate half-word with "Rt | imm12". - __ Ubfx(ip, ip, 0, 12); // Extract the offset imm12. - __ Ldr(ip, MemOperand(base_reg, ip)); // Load the reference. - } else { - MemOperand ldr_address(lr, ldr_offset); - __ Ldrh(ip, ldr_address); // Load the LDR immediate, encoding T1. - __ Add(ep_reg, // Adjust the entrypoint address to the entrypoint - ep_reg, // for narrow LDR. - Operand(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET)); - __ Ubfx(ip, ip, 6, 5); // Extract the imm5, i.e. offset / 4. - __ Ldr(ip, MemOperand(base_reg, ip, LSL, 2)); // Load the reference. - } - // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference. - __ Bx(ep_reg); // Jump to the entrypoint. - if (holder_reg.Is(base_reg)) { - // Add null check slow path. The stack map is at the address pointed to by LR. - __ Bind(&throw_npe); - int32_t offset = GetThreadOffset(kQuickThrowNullPointer).Int32Value(); - __ Ldr(ip, MemOperand(/* Thread* */ vixl::aarch32::r9, offset)); - __ Bx(ip); - } - break; - } - case BakerReadBarrierKind::kArray: { - Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(base_reg.GetCode()); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip); - vixl::aarch32::Label slow_path; - int32_t data_offset = - mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value(); - MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset); - DCHECK_LT(lock_word.GetOffsetImmediate(), 0); - const int32_t raw_ldr_offset = BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET; - EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset); - __ Bind(&slow_path); - const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 + - raw_ldr_offset; - MemOperand ldr_address(lr, ldr_offset + 2); - __ Ldrb(ip, ldr_address); // Load the LDR (register) byte with "00 | imm2 | Rm", - // i.e. Rm+32 because the scale in imm2 is 2. - Register ep_reg(kBakerCcEntrypointRegister); - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); - __ Bfi(ep_reg, ip, 3, 6); // Insert ip to the entrypoint address to create - // a switch case target based on the index register. - __ Mov(ip, base_reg); // Move the base register to ip0. - __ Bx(ep_reg); // Jump to the entrypoint's array switch case. - break; - } - case BakerReadBarrierKind::kGcRoot: { - // Check if the reference needs to be marked and if so (i.e. not null, not marked yet - // and it does not have a forwarding address), call the correct introspection entrypoint; - // otherwise return the reference (or the extracted forwarding address). - // There is no gray bit check for GC roots. - Register root_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(root_reg.GetCode()); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip); - vixl::aarch32::Label return_label, not_marked, forwarding_address; - __ CompareAndBranchIfZero(root_reg, &return_label, /* is_far_target */ false); - MemOperand lock_word(root_reg, mirror::Object::MonitorOffset().Int32Value()); - __ Ldr(ip, lock_word); - __ Tst(ip, LockWord::kMarkBitStateMaskShifted); - __ B(eq, ¬_marked); - __ Bind(&return_label); - __ Bx(lr); - __ Bind(¬_marked); - static_assert(LockWord::kStateShift == 30 && LockWord::kStateForwardingAddress == 3, - "To use 'CMP ip, #modified-immediate; BHS', we need the lock word state in " - " the highest bits and the 'forwarding address' state to have all bits set"); - __ Cmp(ip, Operand(0xc0000000)); - __ B(hs, &forwarding_address); - Register ep_reg(kBakerCcEntrypointRegister); - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); - // Adjust the art_quick_read_barrier_mark_introspection address in kBakerCcEntrypointRegister - // to art_quick_read_barrier_mark_introspection_gc_roots. - int32_t entrypoint_offset = (width == BakerReadBarrierWidth::kWide) - ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET - : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET; - __ Add(ep_reg, ep_reg, Operand(entrypoint_offset)); - __ Mov(ip, root_reg); - __ Bx(ep_reg); - __ Bind(&forwarding_address); - __ Lsl(root_reg, ip, LockWord::kForwardingAddressShift); - __ Bx(lr); - break; - } - default: - LOG(FATAL) << "Unexpected kind: " << static_cast(kind); - UNREACHABLE(); - } -} - -std::vector Thumb2RelativePatcher::CompileThunk(const ThunkKey& key) { - ArenaPool pool; - ArenaAllocator allocator(&pool); - arm::ArmVIXLAssembler assembler(&allocator); - - switch (key.GetType()) { - case ThunkType::kMethodCall: - // The thunk just uses the entry point in the ArtMethod. This works even for calls - // to the generic JNI and interpreter trampolines. - assembler.LoadFromOffset( - arm::kLoadWord, - vixl::aarch32::pc, - vixl::aarch32::r0, - ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value()); - __ Bkpt(0); - break; - case ThunkType::kBakerReadBarrier: - CompileBakerReadBarrierThunk(assembler, key.GetCustomValue1()); - break; - } - - assembler.FinalizeCode(); - std::vector thunk_code(assembler.CodeSize()); - MemoryRegion code(thunk_code.data(), thunk_code.size()); - assembler.FinalizeInstructions(code); - return thunk_code; -} - -std::string Thumb2RelativePatcher::GetThunkDebugName(const ThunkKey& key) { - switch (key.GetType()) { - case ThunkType::kMethodCall: - return "MethodCallThunk"; - - case ThunkType::kBakerReadBarrier: { - uint32_t encoded_data = key.GetCustomValue1(); - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - std::ostringstream oss; - oss << "BakerReadBarrierThunk"; - switch (kind) { - case BakerReadBarrierKind::kField: - oss << "Field"; - if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) { - oss << "Wide"; - } - oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data) - << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data); - break; - case BakerReadBarrierKind::kArray: - oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide); - break; - case BakerReadBarrierKind::kGcRoot: - oss << "GcRoot"; - if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) { - oss << "Wide"; - } - oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - break; - } - return oss.str(); - } - } -} - -#undef __ - uint32_t Thumb2RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) { switch (key.GetType()) { case ThunkType::kMethodCall: diff --git a/compiler/linker/arm/relative_patcher_thumb2.h b/compiler/linker/arm/relative_patcher_thumb2.h index 68386c00f4..68610d69e1 100644 --- a/compiler/linker/arm/relative_patcher_thumb2.h +++ b/compiler/linker/arm/relative_patcher_thumb2.h @@ -19,8 +19,6 @@ #include "arch/arm/registers_arm.h" #include "base/array_ref.h" -#include "base/bit_field.h" -#include "base/bit_utils.h" #include "linker/arm/relative_patcher_arm_base.h" namespace art { @@ -33,42 +31,8 @@ namespace linker { class Thumb2RelativePatcher FINAL : public ArmBaseRelativePatcher { public: - static constexpr uint32_t kBakerCcEntrypointRegister = 4u; - - static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, - uint32_t holder_reg, - bool narrow) { - CheckValidReg(base_reg); - CheckValidReg(holder_reg); - DCHECK(!narrow || base_reg < 8u) << base_reg; - BakerReadBarrierWidth width = - narrow ? BakerReadBarrierWidth::kNarrow : BakerReadBarrierWidth::kWide; - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kField) | - BakerReadBarrierFirstRegField::Encode(base_reg) | - BakerReadBarrierSecondRegField::Encode(holder_reg) | - BakerReadBarrierWidthField::Encode(width); - } - - static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { - CheckValidReg(base_reg); - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kArray) | - BakerReadBarrierFirstRegField::Encode(base_reg) | - BakerReadBarrierSecondRegField::Encode(kInvalidEncodedReg) | - BakerReadBarrierWidthField::Encode(BakerReadBarrierWidth::kWide); - } - - static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg, bool narrow) { - CheckValidReg(root_reg); - DCHECK(!narrow || root_reg < 8u) << root_reg; - BakerReadBarrierWidth width = - narrow ? BakerReadBarrierWidth::kNarrow : BakerReadBarrierWidth::kWide; - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kGcRoot) | - BakerReadBarrierFirstRegField::Encode(root_reg) | - BakerReadBarrierSecondRegField::Encode(kInvalidEncodedReg) | - BakerReadBarrierWidthField::Encode(width); - } - - explicit Thumb2RelativePatcher(RelativePatcherTargetProvider* provider); + explicit Thumb2RelativePatcher(RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider); void PatchCall(std::vector* code, uint32_t literal_offset, @@ -83,48 +47,10 @@ class Thumb2RelativePatcher FINAL : public ArmBaseRelativePatcher { uint32_t patch_offset) OVERRIDE; protected: - std::vector CompileThunk(const ThunkKey& key) OVERRIDE; - std::string GetThunkDebugName(const ThunkKey& key) OVERRIDE; uint32_t MaxPositiveDisplacement(const ThunkKey& key) OVERRIDE; uint32_t MaxNegativeDisplacement(const ThunkKey& key) OVERRIDE; private: - static constexpr uint32_t kInvalidEncodedReg = /* pc is invalid */ 15u; - - enum class BakerReadBarrierKind : uint8_t { - kField, // Field get or array get with constant offset (i.e. constant index). - kArray, // Array get with index in register. - kGcRoot, // GC root load. - kLast = kGcRoot - }; - - enum class BakerReadBarrierWidth : uint8_t { - kWide, // 32-bit LDR (and 32-bit NEG if heap poisoning is enabled). - kNarrow, // 16-bit LDR (and 16-bit NEG if heap poisoning is enabled). - kLast = kNarrow - }; - - static constexpr size_t kBitsForBakerReadBarrierKind = - MinimumBitsToStore(static_cast(BakerReadBarrierKind::kLast)); - static constexpr size_t kBitsForRegister = 4u; - using BakerReadBarrierKindField = - BitField; - using BakerReadBarrierFirstRegField = - BitField; - using BakerReadBarrierSecondRegField = - BitField; - static constexpr size_t kBitsForBakerReadBarrierWidth = - MinimumBitsToStore(static_cast(BakerReadBarrierWidth::kLast)); - using BakerReadBarrierWidthField = BitField; - - static void CheckValidReg(uint32_t reg) { - DCHECK(reg < 12u && reg != kBakerCcEntrypointRegister) << reg; - } - - void CompileBakerReadBarrierThunk(arm::ArmVIXLAssembler& assembler, uint32_t encoded_data); - void SetInsn32(std::vector* code, uint32_t offset, uint32_t value); static uint32_t GetInsn32(ArrayRef code, uint32_t offset); diff --git a/compiler/linker/arm/relative_patcher_thumb2_test.cc b/compiler/linker/arm/relative_patcher_thumb2_test.cc index 2c22a352c2..e7b11bd16b 100644 --- a/compiler/linker/arm/relative_patcher_thumb2_test.cc +++ b/compiler/linker/arm/relative_patcher_thumb2_test.cc @@ -16,12 +16,15 @@ #include "linker/arm/relative_patcher_thumb2.h" +#include "arch/arm/instruction_set_features_arm.h" #include "base/casts.h" #include "linker/relative_patcher_test.h" #include "lock_word.h" #include "mirror/array-inl.h" #include "mirror/object.h" #include "oat_quick_method_header.h" +#include "optimizing/code_generator_arm_vixl.h" +#include "optimizing/optimizing_unit_test.h" namespace art { namespace linker { @@ -189,9 +192,42 @@ class Thumb2RelativePatcherTest : public RelativePatcherTest { return result.second - 1 /* thumb mode */; } + std::vector CompileThunk(const LinkerPatch& patch, + /*out*/ std::string* debug_name = nullptr) { + OptimizingUnitTestHelper helper; + HGraph* graph = helper.CreateGraph(); + std::string error_msg; + ArmFeaturesUniquePtr features = + ArmInstructionSetFeatures::FromVariant("default", &error_msg); + CompilerOptions options; + arm::CodeGeneratorARMVIXL codegen(graph, *features, options); + ArenaVector code(helper.GetAllocator()->Adapter()); + codegen.EmitThunkCode(patch, &code, debug_name); + return std::vector(code.begin(), code.end()); + } + + void AddCompiledMethod( + MethodReference method_ref, + const ArrayRef& code, + const ArrayRef& patches = ArrayRef()) { + RelativePatcherTest::AddCompiledMethod(method_ref, code, patches); + + // Make sure the ThunkProvider has all the necessary thunks. + for (const LinkerPatch& patch : patches) { + if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch || + patch.GetType() == LinkerPatch::Type::kCallRelative) { + std::string debug_name; + std::vector thunk_code = CompileThunk(patch, &debug_name); + thunk_provider_.SetThunkCode(patch, ArrayRef(thunk_code), debug_name); + } + } + } + std::vector CompileMethodCallThunk() { - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetMethodCallKey(); - return static_cast(patcher_.get())->CompileThunk(key); + LinkerPatch patch = LinkerPatch::RelativeCodePatch(/* literal_offset */ 0u, + /* target_dex_file*/ nullptr, + /* target_method_idx */ 0u); + return CompileThunk(patch); } uint32_t MethodCallThunkSize() { @@ -228,27 +264,38 @@ class Thumb2RelativePatcherTest : public RelativePatcherTest { void TestStringReference(uint32_t string_offset); void CheckPcRelativePatch(const ArrayRef& patches, uint32_t target_offset); + static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, + uint32_t holder_reg, + bool narrow) { + return arm::CodeGeneratorARMVIXL::EncodeBakerReadBarrierFieldData(base_reg, holder_reg, narrow); + } + + static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { + return arm::CodeGeneratorARMVIXL::EncodeBakerReadBarrierArrayData(base_reg); + } + + static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg, bool narrow) { + return arm::CodeGeneratorARMVIXL::EncodeBakerReadBarrierGcRootData(root_reg, narrow); + } + std::vector CompileBakerOffsetThunk(uint32_t base_reg, uint32_t holder_reg, bool narrow) { const LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - 0u, Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData(base_reg, holder_reg, narrow)); - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); - return down_cast(patcher_.get())->CompileThunk(key); + /* literal_offset */ 0u, EncodeBakerReadBarrierFieldData(base_reg, holder_reg, narrow)); + return CompileThunk(patch); } std::vector CompileBakerArrayThunk(uint32_t base_reg) { LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - 0u, Thumb2RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg)); - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); - return down_cast(patcher_.get())->CompileThunk(key); + /* literal_offset */ 0u, EncodeBakerReadBarrierArrayData(base_reg)); + return CompileThunk(patch); } std::vector CompileBakerGcRootThunk(uint32_t root_reg, bool narrow) { LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - 0u, Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg, narrow)); - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); - return down_cast(patcher_.get())->CompileThunk(key); + /* literal_offset */ 0u, EncodeBakerReadBarrierGcRootData(root_reg, narrow)); + return CompileThunk(patch); } uint32_t GetOutputInsn32(uint32_t offset) { @@ -594,7 +641,7 @@ void Thumb2RelativePatcherTest::TestBakerFieldWide(uint32_t offset, uint32_t ref const std::vector raw_code = RawCode({kBneWPlus0, ldr}); ASSERT_EQ(kMethodCodeSize, raw_code.size()); ArrayRef code(raw_code); - uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = EncodeBakerReadBarrierFieldData( base_reg, holder_reg, /* narrow */ false); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data), @@ -696,7 +743,7 @@ void Thumb2RelativePatcherTest::TestBakerFieldNarrow(uint32_t offset, uint32_t r const std::vector raw_code = RawCode({kBneWPlus0, ldr}); ASSERT_EQ(kMethodCodeSize, raw_code.size()); ArrayRef code(raw_code); - uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = EncodeBakerReadBarrierFieldData( base_reg, holder_reg, /* narrow */ true); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data), @@ -809,7 +856,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddle) { constexpr uint32_t kLiteralOffset1 = 6u; const std::vector raw_code1 = RawCode({kNopWInsn, kNopInsn, kBneWPlus0, kLdrWInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = EncodeBakerReadBarrierFieldData( /* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), @@ -877,7 +924,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkBeforeFiller) { constexpr uint32_t kLiteralOffset1 = 4u; const std::vector raw_code1 = RawCode({kNopWInsn, kBneWPlus0, kLdrWInsn, kNopInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = EncodeBakerReadBarrierFieldData( /* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), @@ -907,7 +954,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddleUnreachableFromLast constexpr uint32_t kLiteralOffset1 = 6u; const std::vector raw_code1 = RawCode({kNopWInsn, kNopInsn, kBneWPlus0, kLdrWInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = EncodeBakerReadBarrierFieldData( /* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), @@ -993,7 +1040,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerArray) { ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, Thumb2RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg)), + kLiteralOffset, EncodeBakerReadBarrierArrayData(base_reg)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1074,8 +1121,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootWide) { ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, - Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ false)), + kLiteralOffset, EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ false)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1134,8 +1180,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootNarrow) { ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, - Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ true)), + kLiteralOffset, EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ true)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1182,8 +1227,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootOffsetBits) { patches.reserve(num_patches); const uint32_t ldr = kLdrWInsn | (/* offset */ 8) | (/* base_reg */ 0 << 16) | (/* root_reg */ 0 << 12); - uint32_t encoded_data = - Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 0, /* narrow */ false); + uint32_t encoded_data = EncodeBakerReadBarrierGcRootData(/* root_reg */ 0, /* narrow */ false); for (size_t i = 0; i != num_patches; ++i) { PushBackInsn(&code, ldr); PushBackInsn(&code, kBneWPlus0); @@ -1264,10 +1308,8 @@ TEST_F(Thumb2RelativePatcherTest, BakerAndMethodCallInteraction) { ldr1, kBneWPlus0, // First GC root LDR with read barrier. ldr2, kBneWPlus0, // Second GC root LDR with read barrier. }); - uint32_t encoded_data1 = - Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 1, /* narrow */ false); - uint32_t encoded_data2 = - Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 2, /* narrow */ false); + uint32_t encoded_data1 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 1, /* narrow */ false); + uint32_t encoded_data2 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 2, /* narrow */ false); const LinkerPatch last_method_patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset1, encoded_data1), LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset2, encoded_data2), diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc index b268204b4a..135e39d100 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.cc +++ b/compiler/linker/arm64/relative_patcher_arm64.cc @@ -82,9 +82,10 @@ inline uint32_t MaxExtraSpace(size_t num_adrp, size_t code_size) { } // anonymous namespace -Arm64RelativePatcher::Arm64RelativePatcher(RelativePatcherTargetProvider* provider, +Arm64RelativePatcher::Arm64RelativePatcher(RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider, const Arm64InstructionSetFeatures* features) - : ArmBaseRelativePatcher(provider, InstructionSet::kArm64), + : ArmBaseRelativePatcher(thunk_provider, target_provider, InstructionSet::kArm64), fix_cortex_a53_843419_(features->NeedFixCortexA53_843419()), reserved_adrp_thunks_(0u), processed_adrp_thunks_(0u) { @@ -313,44 +314,6 @@ void Arm64RelativePatcher::PatchBakerReadBarrierBranch(std::vector* cod uint32_t insn = GetInsn(code, literal_offset); DCHECK_EQ(insn & 0xffffffe0u, 0xb5000000); // CBNZ Xt, +0 (unpatched) ThunkKey key = GetBakerThunkKey(patch); - if (kIsDebugBuild) { - const uint32_t encoded_data = key.GetCustomValue1(); - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - // Check that the next instruction matches the expected LDR. - switch (kind) { - case BakerReadBarrierKind::kField: { - DCHECK_GE(code->size() - literal_offset, 8u); - uint32_t next_insn = GetInsn(code, literal_offset + 4u); - // LDR (immediate) with correct base_reg. - CheckValidReg(next_insn & 0x1fu); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xffc003e0u, 0xb9400000u | (base_reg << 5)); - break; - } - case BakerReadBarrierKind::kArray: { - DCHECK_GE(code->size() - literal_offset, 8u); - uint32_t next_insn = GetInsn(code, literal_offset + 4u); - // LDR (register) with the correct base_reg, size=10 (32-bit), option=011 (extend = LSL), - // and S=1 (shift amount = 2 for 32-bit version), i.e. LDR Wt, [Xn, Xm, LSL #2]. - CheckValidReg(next_insn & 0x1fu); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xffe0ffe0u, 0xb8607800u | (base_reg << 5)); - CheckValidReg((next_insn >> 16) & 0x1f); // Check index register - break; - } - case BakerReadBarrierKind::kGcRoot: { - DCHECK_GE(literal_offset, 4u); - uint32_t prev_insn = GetInsn(code, literal_offset - 4u); - // LDR (immediate) with correct root_reg. - const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(prev_insn & 0xffc0001fu, 0xb9400000u | root_reg); - break; - } - default: - LOG(FATAL) << "Unexpected kind: " << static_cast(kind); - UNREACHABLE(); - } - } uint32_t target_offset = GetThunkTargetOffset(key, patch_offset); DCHECK_ALIGNED(target_offset, 4u); uint32_t disp = target_offset - patch_offset; @@ -359,216 +322,6 @@ void Arm64RelativePatcher::PatchBakerReadBarrierBranch(std::vector* cod SetInsn(code, literal_offset, insn); } -#define __ assembler.GetVIXLAssembler()-> - -static void EmitGrayCheckAndFastPath(arm64::Arm64Assembler& assembler, - vixl::aarch64::Register base_reg, - vixl::aarch64::MemOperand& lock_word, - vixl::aarch64::Label* slow_path) { - using namespace vixl::aarch64; // NOLINT(build/namespaces) - // Load the lock word containing the rb_state. - __ Ldr(ip0.W(), lock_word); - // Given the numeric representation, it's enough to check the low bit of the rb_state. - static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0"); - static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1"); - __ Tbnz(ip0.W(), LockWord::kReadBarrierStateShift, slow_path); - static_assert( - BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET == BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET, - "Field and array LDR offsets must be the same to reuse the same code."); - // Adjust the return address back to the LDR (1 instruction; 2 for heap poisoning). - static_assert(BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4), - "Field LDR must be 1 instruction (4B) before the return address label; " - " 2 instructions (8B) for heap poisoning."); - __ Add(lr, lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET); - // Introduce a dependency on the lock_word including rb_state, - // to prevent load-load reordering, and without using - // a memory barrier (which would be more expensive). - __ Add(base_reg, base_reg, Operand(ip0, LSR, 32)); - __ Br(lr); // And return back to the function. - // Note: The fake dependency is unnecessary for the slow path. -} - -// Load the read barrier introspection entrypoint in register `entrypoint`. -static void LoadReadBarrierMarkIntrospectionEntrypoint(arm64::Arm64Assembler& assembler, - vixl::aarch64::Register entrypoint) { - using vixl::aarch64::MemOperand; - using vixl::aarch64::ip0; - // Thread Register. - const vixl::aarch64::Register tr = vixl::aarch64::x19; - - // entrypoint = Thread::Current()->pReadBarrierMarkReg16, i.e. pReadBarrierMarkIntrospection. - DCHECK_EQ(ip0.GetCode(), 16u); - const int32_t entry_point_offset = - Thread::ReadBarrierMarkEntryPointsOffset(ip0.GetCode()); - __ Ldr(entrypoint, MemOperand(tr, entry_point_offset)); -} - -void Arm64RelativePatcher::CompileBakerReadBarrierThunk(arm64::Arm64Assembler& assembler, - uint32_t encoded_data) { - using namespace vixl::aarch64; // NOLINT(build/namespaces) - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - switch (kind) { - case BakerReadBarrierKind::kField: { - // Check if the holder is gray and, if not, add fake dependency to the base register - // and return to the LDR instruction to load the reference. Otherwise, use introspection - // to load the reference and call the entrypoint (in IP1) that performs further checks - // on the reference and marks it if needed. - auto base_reg = - Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(base_reg.GetCode()); - auto holder_reg = - Register::GetXRegFromCode(BakerReadBarrierSecondRegField::Decode(encoded_data)); - CheckValidReg(holder_reg.GetCode()); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip0, ip1); - // If base_reg differs from holder_reg, the offset was too large and we must have - // emitted an explicit null check before the load. Otherwise, we need to null-check - // the holder as we do not necessarily do that check before going to the thunk. - vixl::aarch64::Label throw_npe; - if (holder_reg.Is(base_reg)) { - __ Cbz(holder_reg.W(), &throw_npe); - } - vixl::aarch64::Label slow_path; - MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value()); - EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path); - __ Bind(&slow_path); - MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET); - __ Ldr(ip0.W(), ldr_address); // Load the LDR (immediate) unsigned offset. - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); - __ Ubfx(ip0.W(), ip0.W(), 10, 12); // Extract the offset. - __ Ldr(ip0.W(), MemOperand(base_reg, ip0, LSL, 2)); // Load the reference. - // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference. - __ Br(ip1); // Jump to the entrypoint. - if (holder_reg.Is(base_reg)) { - // Add null check slow path. The stack map is at the address pointed to by LR. - __ Bind(&throw_npe); - int32_t offset = GetThreadOffset(kQuickThrowNullPointer).Int32Value(); - __ Ldr(ip0, MemOperand(/* Thread* */ vixl::aarch64::x19, offset)); - __ Br(ip0); - } - break; - } - case BakerReadBarrierKind::kArray: { - auto base_reg = - Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(base_reg.GetCode()); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip0, ip1); - vixl::aarch64::Label slow_path; - int32_t data_offset = - mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value(); - MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset); - DCHECK_LT(lock_word.GetOffset(), 0); - EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path); - __ Bind(&slow_path); - MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET); - __ Ldr(ip0.W(), ldr_address); // Load the LDR (register) unsigned offset. - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); - __ Ubfx(ip0, ip0, 16, 6); // Extract the index register, plus 32 (bit 21 is set). - __ Bfi(ip1, ip0, 3, 6); // Insert ip0 to the entrypoint address to create - // a switch case target based on the index register. - __ Mov(ip0, base_reg); // Move the base register to ip0. - __ Br(ip1); // Jump to the entrypoint's array switch case. - break; - } - case BakerReadBarrierKind::kGcRoot: { - // Check if the reference needs to be marked and if so (i.e. not null, not marked yet - // and it does not have a forwarding address), call the correct introspection entrypoint; - // otherwise return the reference (or the extracted forwarding address). - // There is no gray bit check for GC roots. - auto root_reg = - Register::GetWRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(root_reg.GetCode()); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip0, ip1); - vixl::aarch64::Label return_label, not_marked, forwarding_address; - __ Cbz(root_reg, &return_label); - MemOperand lock_word(root_reg.X(), mirror::Object::MonitorOffset().Int32Value()); - __ Ldr(ip0.W(), lock_word); - __ Tbz(ip0.W(), LockWord::kMarkBitStateShift, ¬_marked); - __ Bind(&return_label); - __ Br(lr); - __ Bind(¬_marked); - __ Tst(ip0.W(), Operand(ip0.W(), LSL, 1)); - __ B(&forwarding_address, mi); - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); - // Adjust the art_quick_read_barrier_mark_introspection address in IP1 to - // art_quick_read_barrier_mark_introspection_gc_roots. - __ Add(ip1, ip1, Operand(BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRYPOINT_OFFSET)); - __ Mov(ip0.W(), root_reg); - __ Br(ip1); - __ Bind(&forwarding_address); - __ Lsl(root_reg, ip0.W(), LockWord::kForwardingAddressShift); - __ Br(lr); - break; - } - default: - LOG(FATAL) << "Unexpected kind: " << static_cast(kind); - UNREACHABLE(); - } -} - -std::vector Arm64RelativePatcher::CompileThunk(const ThunkKey& key) { - ArenaPool pool; - ArenaAllocator allocator(&pool); - arm64::Arm64Assembler assembler(&allocator); - - switch (key.GetType()) { - case ThunkType::kMethodCall: { - // The thunk just uses the entry point in the ArtMethod. This works even for calls - // to the generic JNI and interpreter trampolines. - Offset offset(ArtMethod::EntryPointFromQuickCompiledCodeOffset( - kArm64PointerSize).Int32Value()); - assembler.JumpTo(ManagedRegister(arm64::X0), offset, ManagedRegister(arm64::IP0)); - break; - } - case ThunkType::kBakerReadBarrier: { - CompileBakerReadBarrierThunk(assembler, key.GetCustomValue1()); - break; - } - } - - // Ensure we emit the literal pool. - assembler.FinalizeCode(); - std::vector thunk_code(assembler.CodeSize()); - MemoryRegion code(thunk_code.data(), thunk_code.size()); - assembler.FinalizeInstructions(code); - return thunk_code; -} - -std::string Arm64RelativePatcher::GetThunkDebugName(const ThunkKey& key) { - switch (key.GetType()) { - case ThunkType::kMethodCall: - return "MethodCallThunk"; - - case ThunkType::kBakerReadBarrier: { - uint32_t encoded_data = key.GetCustomValue1(); - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - std::ostringstream oss; - oss << "BakerReadBarrierThunk"; - switch (kind) { - case BakerReadBarrierKind::kField: - oss << "Field_r" << BakerReadBarrierFirstRegField::Decode(encoded_data) - << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data); - break; - case BakerReadBarrierKind::kArray: - oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - break; - case BakerReadBarrierKind::kGcRoot: - oss << "GcRoot_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - break; - } - return oss.str(); - } - } -} - -#undef __ - uint32_t Arm64RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) { switch (key.GetType()) { case ThunkType::kMethodCall: diff --git a/compiler/linker/arm64/relative_patcher_arm64.h b/compiler/linker/arm64/relative_patcher_arm64.h index 8ba59976e7..9dc289da44 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.h +++ b/compiler/linker/arm64/relative_patcher_arm64.h @@ -18,8 +18,6 @@ #define ART_COMPILER_LINKER_ARM64_RELATIVE_PATCHER_ARM64_H_ #include "base/array_ref.h" -#include "base/bit_field.h" -#include "base/bit_utils.h" #include "linker/arm/relative_patcher_arm_base.h" namespace art { @@ -32,29 +30,8 @@ namespace linker { class Arm64RelativePatcher FINAL : public ArmBaseRelativePatcher { public: - static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, uint32_t holder_reg) { - CheckValidReg(base_reg); - CheckValidReg(holder_reg); - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kField) | - BakerReadBarrierFirstRegField::Encode(base_reg) | - BakerReadBarrierSecondRegField::Encode(holder_reg); - } - - static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { - CheckValidReg(base_reg); - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kArray) | - BakerReadBarrierFirstRegField::Encode(base_reg) | - BakerReadBarrierSecondRegField::Encode(kInvalidEncodedReg); - } - - static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg) { - CheckValidReg(root_reg); - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kGcRoot) | - BakerReadBarrierFirstRegField::Encode(root_reg) | - BakerReadBarrierSecondRegField::Encode(kInvalidEncodedReg); - } - - Arm64RelativePatcher(RelativePatcherTargetProvider* provider, + Arm64RelativePatcher(RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider, const Arm64InstructionSetFeatures* features); uint32_t ReserveSpace(uint32_t offset, @@ -75,37 +52,10 @@ class Arm64RelativePatcher FINAL : public ArmBaseRelativePatcher { uint32_t patch_offset) OVERRIDE; protected: - std::vector CompileThunk(const ThunkKey& key) OVERRIDE; - std::string GetThunkDebugName(const ThunkKey& key) OVERRIDE; uint32_t MaxPositiveDisplacement(const ThunkKey& key) OVERRIDE; uint32_t MaxNegativeDisplacement(const ThunkKey& key) OVERRIDE; private: - static constexpr uint32_t kInvalidEncodedReg = /* sp/zr is invalid */ 31u; - - enum class BakerReadBarrierKind : uint8_t { - kField, // Field get or array get with constant offset (i.e. constant index). - kArray, // Array get with index in register. - kGcRoot, // GC root load. - kLast = kGcRoot - }; - - static constexpr size_t kBitsForBakerReadBarrierKind = - MinimumBitsToStore(static_cast(BakerReadBarrierKind::kLast)); - static constexpr size_t kBitsForRegister = 5u; - using BakerReadBarrierKindField = - BitField; - using BakerReadBarrierFirstRegField = - BitField; - using BakerReadBarrierSecondRegField = - BitField; - - static void CheckValidReg(uint32_t reg) { - DCHECK(reg < 30u && reg != 16u && reg != 17u) << reg; - } - - void CompileBakerReadBarrierThunk(arm64::Arm64Assembler& assembler, uint32_t encoded_data); - static uint32_t PatchAdrp(uint32_t adrp, uint32_t disp); static bool NeedsErratum843419Thunk(ArrayRef code, uint32_t literal_offset, diff --git a/compiler/linker/arm64/relative_patcher_arm64_test.cc b/compiler/linker/arm64/relative_patcher_arm64_test.cc index 05459a2a82..393733dd0c 100644 --- a/compiler/linker/arm64/relative_patcher_arm64_test.cc +++ b/compiler/linker/arm64/relative_patcher_arm64_test.cc @@ -16,12 +16,15 @@ #include "linker/arm64/relative_patcher_arm64.h" +#include "arch/arm64/instruction_set_features_arm64.h" #include "base/casts.h" #include "linker/relative_patcher_test.h" #include "lock_word.h" #include "mirror/array-inl.h" #include "mirror/object.h" #include "oat_quick_method_header.h" +#include "optimizing/code_generator_arm64.h" +#include "optimizing/optimizing_unit_test.h" namespace art { namespace linker { @@ -168,9 +171,42 @@ class Arm64RelativePatcherTest : public RelativePatcherTest { return result.second; } + std::vector CompileThunk(const LinkerPatch& patch, + /*out*/ std::string* debug_name = nullptr) { + OptimizingUnitTestHelper helper; + HGraph* graph = helper.CreateGraph(); + std::string error_msg; + Arm64FeaturesUniquePtr features = + Arm64InstructionSetFeatures::FromVariant("default", &error_msg); + CompilerOptions options; + arm64::CodeGeneratorARM64 codegen(graph, *features, options); + ArenaVector code(helper.GetAllocator()->Adapter()); + codegen.EmitThunkCode(patch, &code, debug_name); + return std::vector(code.begin(), code.end()); + } + + void AddCompiledMethod( + MethodReference method_ref, + const ArrayRef& code, + const ArrayRef& patches = ArrayRef()) { + RelativePatcherTest::AddCompiledMethod(method_ref, code, patches); + + // Make sure the ThunkProvider has all the necessary thunks. + for (const LinkerPatch& patch : patches) { + if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch || + patch.GetType() == LinkerPatch::Type::kCallRelative) { + std::string debug_name; + std::vector thunk_code = CompileThunk(patch, &debug_name); + thunk_provider_.SetThunkCode(patch, ArrayRef(thunk_code), debug_name); + } + } + } + std::vector CompileMethodCallThunk() { - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetMethodCallKey(); - return down_cast(patcher_.get())->CompileThunk(key); + LinkerPatch patch = LinkerPatch::RelativeCodePatch(/* literal_offset */ 0u, + /* target_dex_file*/ nullptr, + /* target_method_idx */ 0u); + return CompileThunk(patch); } uint32_t MethodCallThunkSize() { @@ -475,25 +511,34 @@ class Arm64RelativePatcherTest : public RelativePatcherTest { TestAdrpInsn2Add(insn2, adrp_offset, has_thunk, string_offset); } + static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, uint32_t holder_reg) { + return arm64::CodeGeneratorARM64::EncodeBakerReadBarrierFieldData(base_reg, holder_reg); + } + + static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { + return arm64::CodeGeneratorARM64::EncodeBakerReadBarrierArrayData(base_reg); + } + + static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg) { + return arm64::CodeGeneratorARM64::EncodeBakerReadBarrierGcRootData(root_reg); + } + std::vector CompileBakerOffsetThunk(uint32_t base_reg, uint32_t holder_reg) { const LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - 0u, Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(base_reg, holder_reg)); - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); - return down_cast(patcher_.get())->CompileThunk(key); + /* literal_offset */ 0u, EncodeBakerReadBarrierFieldData(base_reg, holder_reg)); + return CompileThunk(patch); } std::vector CompileBakerArrayThunk(uint32_t base_reg) { LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - 0u, Arm64RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg)); - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); - return down_cast(patcher_.get())->CompileThunk(key); + /* literal_offset */ 0u, EncodeBakerReadBarrierArrayData(base_reg)); + return CompileThunk(patch); } std::vector CompileBakerGcRootThunk(uint32_t root_reg) { LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - 0u, Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg)); - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); - return down_cast(patcher_.get())->CompileThunk(key); + /* literal_offset */ 0u, EncodeBakerReadBarrierGcRootData(root_reg)); + return CompileThunk(patch); } uint32_t GetOutputInsn(uint32_t offset) { @@ -919,8 +964,7 @@ void Arm64RelativePatcherTest::TestBakerField(uint32_t offset, uint32_t ref_reg) const std::vector raw_code = RawCode({kCbnzIP1Plus0Insn, ldr}); ASSERT_EQ(kMethodCodeSize, raw_code.size()); ArrayRef code(raw_code); - uint32_t encoded_data = - Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(base_reg, holder_reg); + uint32_t encoded_data = EncodeBakerReadBarrierFieldData(base_reg, holder_reg); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data), }; @@ -1005,8 +1049,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddle) { constexpr uint32_t kLiteralOffset1 = 4; const std::vector raw_code1 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = - Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); + uint32_t encoded_data = EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), }; @@ -1066,8 +1109,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkBeforeFiller) { constexpr uint32_t kLiteralOffset1 = 0; const std::vector raw_code1 = RawCode({kCbnzIP1Plus0Insn, kLdrWInsn, kNopInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = - Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); + uint32_t encoded_data = EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), }; @@ -1096,8 +1138,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddleUnreachableFr constexpr uint32_t kLiteralOffset1 = 4; const std::vector raw_code1 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = - Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); + uint32_t encoded_data = EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), }; @@ -1170,7 +1211,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerArray) { ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, Arm64RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg)), + kLiteralOffset, EncodeBakerReadBarrierArrayData(base_reg)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1247,7 +1288,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerGcRoot) { ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg)), + kLiteralOffset, EncodeBakerReadBarrierGcRootData(root_reg)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1343,8 +1384,8 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerAndMethodCallInteraction) { kNopInsn, kNopInsn, // Padding before second GC root read barrier. ldr2, kCbnzIP1Plus0Insn, // Second GC root LDR with read barrier. }); - uint32_t encoded_data1 = Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 1); - uint32_t encoded_data2 = Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 2); + uint32_t encoded_data1 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 1); + uint32_t encoded_data2 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 2); const LinkerPatch last_method_patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset1, encoded_data1), LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset2, encoded_data2), diff --git a/compiler/linker/linker_patch.h b/compiler/linker/linker_patch.h index 710d8a690a..7b35fd9b0c 100644 --- a/compiler/linker/linker_patch.h +++ b/compiler/linker/linker_patch.h @@ -141,7 +141,7 @@ class LinkerPatch { static LinkerPatch BakerReadBarrierBranchPatch(size_t literal_offset, uint32_t custom_value1 = 0u, uint32_t custom_value2 = 0u) { - LinkerPatch patch(literal_offset, Type::kBakerReadBarrierBranch, nullptr); + LinkerPatch patch(literal_offset, Type::kBakerReadBarrierBranch, /* target_dex_file */ nullptr); patch.baker_custom_value1_ = custom_value1; patch.baker_custom_value2_ = custom_value2; return patch; diff --git a/compiler/linker/relative_patcher.cc b/compiler/linker/relative_patcher.cc index 13877f8f12..b82d15230d 100644 --- a/compiler/linker/relative_patcher.cc +++ b/compiler/linker/relative_patcher.cc @@ -43,7 +43,8 @@ namespace linker { std::unique_ptr RelativePatcher::Create( InstructionSet instruction_set, const InstructionSetFeatures* features, - RelativePatcherTargetProvider* provider) { + RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider) { class RelativePatcherNone FINAL : public RelativePatcher { public: RelativePatcherNone() { } @@ -92,7 +93,8 @@ std::unique_ptr RelativePatcher::Create( }; UNUSED(features); - UNUSED(provider); + UNUSED(thunk_provider); + UNUSED(target_provider); switch (instruction_set) { #ifdef ART_ENABLE_CODEGEN_x86 case InstructionSet::kX86: @@ -106,12 +108,15 @@ std::unique_ptr RelativePatcher::Create( case InstructionSet::kArm: // Fall through: we generate Thumb2 code for "arm". case InstructionSet::kThumb2: - return std::unique_ptr(new Thumb2RelativePatcher(provider)); + return std::unique_ptr( + new Thumb2RelativePatcher(thunk_provider, target_provider)); #endif #ifdef ART_ENABLE_CODEGEN_arm64 case InstructionSet::kArm64: return std::unique_ptr( - new Arm64RelativePatcher(provider, features->AsArm64InstructionSetFeatures())); + new Arm64RelativePatcher(thunk_provider, + target_provider, + features->AsArm64InstructionSetFeatures())); #endif #ifdef ART_ENABLE_CODEGEN_mips case InstructionSet::kMips: diff --git a/compiler/linker/relative_patcher.h b/compiler/linker/relative_patcher.h index b58e3dffbd..06c7e70d23 100644 --- a/compiler/linker/relative_patcher.h +++ b/compiler/linker/relative_patcher.h @@ -38,6 +38,27 @@ namespace linker { class LinkerPatch; class OutputStream; +/** + * @class RelativePatcherThunkProvider + * @brief Interface for providing method offsets for relative call targets. + */ +class RelativePatcherThunkProvider { + public: + /** + * Get the code and debug name of a thunk needed by the given linker patch. + * + * @param patch The patch for which we need to retrieve the thunk code. + * @param code A variable to receive the code of the thunk. This code must not be empty. + * @param debug_name A variable to receive the debug name of the thunk. + */ + virtual void GetThunkCode(const LinkerPatch& patch, + /*out*/ ArrayRef* code, + /*out*/ std::string* debug_name) = 0; + + protected: + virtual ~RelativePatcherThunkProvider() { } +}; + /** * @class RelativePatcherTargetProvider * @brief Interface for providing method offsets for relative call targets. @@ -70,8 +91,10 @@ class RelativePatcherTargetProvider { class RelativePatcher { public: static std::unique_ptr Create( - InstructionSet instruction_set, const InstructionSetFeatures* features, - RelativePatcherTargetProvider* provider); + InstructionSet instruction_set, + const InstructionSetFeatures* features, + RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider); virtual ~RelativePatcher() { } diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h index d21f2795b9..af8dc4dbc9 100644 --- a/compiler/linker/relative_patcher_test.h +++ b/compiler/linker/relative_patcher_test.h @@ -58,7 +58,10 @@ class RelativePatcherTest : public testing::Test { instruction_set_(instruction_set), features_(InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg_)), method_offset_map_(), - patcher_(RelativePatcher::Create(instruction_set, features_.get(), &method_offset_map_)), + patcher_(RelativePatcher::Create(instruction_set, + features_.get(), + &thunk_provider_, + &method_offset_map_)), bss_begin_(0u), compiled_method_refs_(), compiled_methods_(), @@ -248,6 +251,72 @@ class RelativePatcherTest : public testing::Test { LOG(ERROR) << " " << diff_indicator_str; } + class ThunkProvider : public RelativePatcherThunkProvider { + public: + ThunkProvider() {} + + void SetThunkCode(const LinkerPatch& patch, + ArrayRef code, + const std::string& debug_name) { + thunk_map_.emplace(ThunkKey(patch), ThunkValue(code, debug_name)); + } + + void GetThunkCode(const LinkerPatch& patch, + /*out*/ ArrayRef* code, + /*out*/ std::string* debug_name) OVERRIDE { + auto it = thunk_map_.find(ThunkKey(patch)); + CHECK(it != thunk_map_.end()); + const ThunkValue& value = it->second; + CHECK(code != nullptr); + *code = value.GetCode(); + CHECK(debug_name != nullptr); + *debug_name = value.GetDebugName(); + } + + private: + class ThunkKey { + public: + explicit ThunkKey(const LinkerPatch& patch) + : type_(patch.GetType()), + custom_value1_(patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch + ? patch.GetBakerCustomValue1() : 0u), + custom_value2_(patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch + ? patch.GetBakerCustomValue2() : 0u) { + CHECK(patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch || + patch.GetType() == LinkerPatch::Type::kCallRelative); + } + + bool operator<(const ThunkKey& other) const { + if (custom_value1_ != other.custom_value1_) { + return custom_value1_ < other.custom_value1_; + } + if (custom_value2_ != other.custom_value2_) { + return custom_value2_ < other.custom_value2_; + } + return type_ < other.type_; + } + + private: + const LinkerPatch::Type type_; + const uint32_t custom_value1_; + const uint32_t custom_value2_; + }; + + class ThunkValue { + public: + ThunkValue(ArrayRef code, const std::string& debug_name) + : code_(code.begin(), code.end()), debug_name_(debug_name) {} + ArrayRef GetCode() const { return ArrayRef(code_); } + const std::string& GetDebugName() const { return debug_name_; } + + private: + const std::vector code_; + const std::string debug_name_; + }; + + std::map thunk_map_; + }; + // Map method reference to assinged offset. // Wrap the map in a class implementing RelativePatcherTargetProvider. class MethodOffsetMap FINAL : public RelativePatcherTargetProvider { @@ -272,6 +341,7 @@ class RelativePatcherTest : public testing::Test { std::string error_msg_; InstructionSet instruction_set_; std::unique_ptr features_; + ThunkProvider thunk_provider_; MethodOffsetMap method_offset_map_; std::unique_ptr patcher_; uint32_t bss_begin_; diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index c2ae7646b5..231017f55e 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -449,6 +449,18 @@ void CodeGenerator::EmitLinkerPatches( // No linker patches by default. } +bool CodeGenerator::NeedsThunkCode(const linker::LinkerPatch& patch ATTRIBUTE_UNUSED) const { + // Code generators that create patches requiring thunk compilation should override this function. + return false; +} + +void CodeGenerator::EmitThunkCode(const linker::LinkerPatch& patch ATTRIBUTE_UNUSED, + /*out*/ ArenaVector* code ATTRIBUTE_UNUSED, + /*out*/ std::string* debug_name ATTRIBUTE_UNUSED) { + // Code generators that create patches requiring thunk compilation should override this function. + LOG(FATAL) << "Unexpected call to EmitThunkCode()."; +} + void CodeGenerator::InitializeCodeGeneration(size_t number_of_spill_slots, size_t maximum_safepoint_spill_size, size_t number_of_out_slots, diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 3bd5e14539..a86b27151d 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -21,6 +21,7 @@ #include "arch/instruction_set_features.h" #include "base/arena_containers.h" #include "base/arena_object.h" +#include "base/array_ref.h" #include "base/bit_field.h" #include "base/bit_utils.h" #include "base/enums.h" @@ -74,6 +75,7 @@ class CodeAllocator { virtual ~CodeAllocator() {} virtual uint8_t* Allocate(size_t size) = 0; + virtual ArrayRef GetMemory() const = 0; private: DISALLOW_COPY_AND_ASSIGN(CodeAllocator); @@ -210,6 +212,10 @@ class CodeGenerator : public DeletableArenaObject { virtual void Initialize() = 0; virtual void Finalize(CodeAllocator* allocator); virtual void EmitLinkerPatches(ArenaVector* linker_patches); + virtual bool NeedsThunkCode(const linker::LinkerPatch& patch) const; + virtual void EmitThunkCode(const linker::LinkerPatch& patch, + /*out*/ ArenaVector* code, + /*out*/ std::string* debug_name); virtual void GenerateFrameEntry() = 0; virtual void GenerateFrameExit() = 0; virtual void Bind(HBasicBlock* block) = 0; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 273346ab4a..31887d92e8 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -30,7 +30,6 @@ #include "heap_poisoning.h" #include "intrinsics.h" #include "intrinsics_arm64.h" -#include "linker/arm64/relative_patcher_arm64.h" #include "linker/linker_patch.h" #include "lock_word.h" #include "mirror/array-inl.h" @@ -1425,6 +1424,62 @@ void CodeGeneratorARM64::Finalize(CodeAllocator* allocator) { __ FinalizeCode(); CodeGenerator::Finalize(allocator); + + // Verify Baker read barrier linker patches. + if (kIsDebugBuild) { + ArrayRef code = allocator->GetMemory(); + for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) { + DCHECK(info.label.IsBound()); + uint32_t literal_offset = info.label.GetLocation(); + DCHECK_ALIGNED(literal_offset, 4u); + + auto GetInsn = [&code](uint32_t offset) { + DCHECK_ALIGNED(offset, 4u); + return + (static_cast(code[offset + 0]) << 0) + + (static_cast(code[offset + 1]) << 8) + + (static_cast(code[offset + 2]) << 16)+ + (static_cast(code[offset + 3]) << 24); + }; + + const uint32_t encoded_data = info.custom_data; + BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); + // Check that the next instruction matches the expected LDR. + switch (kind) { + case BakerReadBarrierKind::kField: { + DCHECK_GE(code.size() - literal_offset, 8u); + uint32_t next_insn = GetInsn(literal_offset + 4u); + // LDR (immediate) with correct base_reg. + CheckValidReg(next_insn & 0x1fu); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xffc003e0u, 0xb9400000u | (base_reg << 5)); + break; + } + case BakerReadBarrierKind::kArray: { + DCHECK_GE(code.size() - literal_offset, 8u); + uint32_t next_insn = GetInsn(literal_offset + 4u); + // LDR (register) with the correct base_reg, size=10 (32-bit), option=011 (extend = LSL), + // and S=1 (shift amount = 2 for 32-bit version), i.e. LDR Wt, [Xn, Xm, LSL #2]. + CheckValidReg(next_insn & 0x1fu); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xffe0ffe0u, 0xb8607800u | (base_reg << 5)); + CheckValidReg((next_insn >> 16) & 0x1f); // Check index register + break; + } + case BakerReadBarrierKind::kGcRoot: { + DCHECK_GE(literal_offset, 4u); + uint32_t prev_insn = GetInsn(literal_offset - 4u); + // LDR (immediate) with correct root_reg. + const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(prev_insn & 0xffc0001fu, 0xb9400000u | root_reg); + break; + } + default: + LOG(FATAL) << "Unexpected kind: " << static_cast(kind); + UNREACHABLE(); + } + } + } } void ParallelMoveResolverARM64::PrepareForEmitNativeCode() { @@ -4814,6 +4869,44 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector* lin DCHECK_EQ(size, linker_patches->size()); } +bool CodeGeneratorARM64::NeedsThunkCode(const linker::LinkerPatch& patch) const { + return patch.GetType() == linker::LinkerPatch::Type::kBakerReadBarrierBranch || + patch.GetType() == linker::LinkerPatch::Type::kCallRelative; +} + +void CodeGeneratorARM64::EmitThunkCode(const linker::LinkerPatch& patch, + /*out*/ ArenaVector* code, + /*out*/ std::string* debug_name) { + Arm64Assembler assembler(GetGraph()->GetAllocator()); + switch (patch.GetType()) { + case linker::LinkerPatch::Type::kCallRelative: { + // The thunk just uses the entry point in the ArtMethod. This works even for calls + // to the generic JNI and interpreter trampolines. + Offset offset(ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kArm64PointerSize).Int32Value()); + assembler.JumpTo(ManagedRegister(arm64::X0), offset, ManagedRegister(arm64::IP0)); + if (GetCompilerOptions().GenerateAnyDebugInfo()) { + *debug_name = "MethodCallThunk"; + } + break; + } + case linker::LinkerPatch::Type::kBakerReadBarrierBranch: { + DCHECK_EQ(patch.GetBakerCustomValue2(), 0u); + CompileBakerReadBarrierThunk(assembler, patch.GetBakerCustomValue1(), debug_name); + break; + } + default: + LOG(FATAL) << "Unexpected patch type " << patch.GetType(); + UNREACHABLE(); + } + + // Ensure we emit the literal pool if any. + assembler.FinalizeCode(); + code->resize(assembler.CodeSize()); + MemoryRegion code_region(code->data(), code->size()); + assembler.FinalizeInstructions(code_region); +} + vixl::aarch64::Literal* CodeGeneratorARM64::DeduplicateUint32Literal(uint32_t value) { return uint32_literals_.GetOrCreate( value, @@ -4954,12 +5047,12 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA DCHECK(!cls->MustGenerateClinitCheck()); // /* GcRoot */ out = current_method->declaring_class_ Register current_method = InputRegisterAt(cls, 0); - GenerateGcRootFieldLoad(cls, - out_loc, - current_method, - ArtMethod::DeclaringClassOffset().Int32Value(), - /* fixup_label */ nullptr, - read_barrier_option); + codegen_->GenerateGcRootFieldLoad(cls, + out_loc, + current_method, + ArtMethod::DeclaringClassOffset().Int32Value(), + /* fixup_label */ nullptr, + read_barrier_option); break; } case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { @@ -5006,12 +5099,12 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA vixl::aarch64::Label* ldr_label = codegen_->NewBssEntryTypePatch(dex_file, type_index, adrp_label); // /* GcRoot */ out = *(base_address + offset) /* PC-relative */ - GenerateGcRootFieldLoad(cls, - out_loc, - temp, - /* offset placeholder */ 0u, - ldr_label, - read_barrier_option); + codegen_->GenerateGcRootFieldLoad(cls, + out_loc, + temp, + /* offset placeholder */ 0u, + ldr_label, + read_barrier_option); generate_null_check = true; break; } @@ -5019,12 +5112,12 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(), cls->GetTypeIndex(), cls->GetClass())); - GenerateGcRootFieldLoad(cls, - out_loc, - out.X(), - /* offset */ 0, - /* fixup_label */ nullptr, - read_barrier_option); + codegen_->GenerateGcRootFieldLoad(cls, + out_loc, + out.X(), + /* offset */ 0, + /* fixup_label */ nullptr, + read_barrier_option); break; } case HLoadClass::LoadKind::kRuntimeCall: @@ -5167,12 +5260,12 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD vixl::aarch64::Label* ldr_label = codegen_->NewStringBssEntryPatch(dex_file, string_index, adrp_label); // /* GcRoot */ out = *(base_address + offset) /* PC-relative */ - GenerateGcRootFieldLoad(load, - out_loc, - temp, - /* offset placeholder */ 0u, - ldr_label, - kCompilerReadBarrierOption); + codegen_->GenerateGcRootFieldLoad(load, + out_loc, + temp, + /* offset placeholder */ 0u, + ldr_label, + kCompilerReadBarrierOption); SlowPathCodeARM64* slow_path = new (codegen_->GetScopedAllocator()) LoadStringSlowPathARM64(load); codegen_->AddSlowPath(slow_path); @@ -5185,12 +5278,12 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(), load->GetStringIndex(), load->GetString())); - GenerateGcRootFieldLoad(load, - out_loc, - out.X(), - /* offset */ 0, - /* fixup_label */ nullptr, - kCompilerReadBarrierOption); + codegen_->GenerateGcRootFieldLoad(load, + out_loc, + out.X(), + /* offset */ 0, + /* fixup_label */ nullptr, + kCompilerReadBarrierOption); return; } default: @@ -6139,7 +6232,7 @@ void InstructionCodeGeneratorARM64::GenerateReferenceLoadTwoRegisters( } } -void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad( +void CodeGeneratorARM64::GenerateGcRootFieldLoad( HInstruction* instruction, Location root, Register obj, @@ -6173,9 +6266,8 @@ void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad( DCHECK(temps.IsAvailable(ip0)); DCHECK(temps.IsAvailable(ip1)); temps.Exclude(ip0, ip1); - uint32_t custom_data = - linker::Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg.GetCode()); - vixl::aarch64::Label* cbnz_label = codegen_->NewBakerReadBarrierPatch(custom_data); + uint32_t custom_data = EncodeBakerReadBarrierGcRootData(root_reg.GetCode()); + vixl::aarch64::Label* cbnz_label = NewBakerReadBarrierPatch(custom_data); EmissionCheckScope guard(GetVIXLAssembler(), 3 * vixl::aarch64::kInstructionSize); vixl::aarch64::Label return_address; @@ -6204,14 +6296,14 @@ void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad( // Slow path marking the GC root `root`. The entrypoint will // be loaded by the slow path code. SlowPathCodeARM64* slow_path = - new (codegen_->GetScopedAllocator()) ReadBarrierMarkSlowPathARM64(instruction, root); - codegen_->AddSlowPath(slow_path); + new (GetScopedAllocator()) ReadBarrierMarkSlowPathARM64(instruction, root); + AddSlowPath(slow_path); // /* GcRoot */ root = *(obj + offset) if (fixup_label == nullptr) { __ Ldr(root_reg, MemOperand(obj, offset)); } else { - codegen_->EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj); + EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj); } static_assert( sizeof(mirror::CompressedReference) == sizeof(GcRoot), @@ -6231,10 +6323,10 @@ void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad( if (fixup_label == nullptr) { __ Add(root_reg.X(), obj.X(), offset); } else { - codegen_->EmitAddPlaceholder(fixup_label, root_reg.X(), obj.X()); + EmitAddPlaceholder(fixup_label, root_reg.X(), obj.X()); } // /* mirror::Object* */ root = root->Read() - codegen_->GenerateReadBarrierForRootSlow(instruction, root, root); + GenerateReadBarrierForRootSlow(instruction, root, root); } } else { // Plain GC root load with no read barrier. @@ -6242,12 +6334,12 @@ void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad( if (fixup_label == nullptr) { __ Ldr(root_reg, MemOperand(obj, offset)); } else { - codegen_->EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj.X()); + EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj.X()); } // Note that GC roots are not affected by heap poisoning, thus we // do not have to unpoison `root_reg` here. } - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__); + MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__); } void CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -6296,9 +6388,7 @@ void CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* ins DCHECK(temps.IsAvailable(ip0)); DCHECK(temps.IsAvailable(ip1)); temps.Exclude(ip0, ip1); - uint32_t custom_data = linker::Arm64RelativePatcher::EncodeBakerReadBarrierFieldData( - base.GetCode(), - obj.GetCode()); + uint32_t custom_data = EncodeBakerReadBarrierFieldData(base.GetCode(), obj.GetCode()); vixl::aarch64::Label* cbnz_label = NewBakerReadBarrierPatch(custom_data); { @@ -6383,8 +6473,7 @@ void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* ins DCHECK(temps.IsAvailable(ip0)); DCHECK(temps.IsAvailable(ip1)); temps.Exclude(ip0, ip1); - uint32_t custom_data = - linker::Arm64RelativePatcher::EncodeBakerReadBarrierArrayData(temp.GetCode()); + uint32_t custom_data = EncodeBakerReadBarrierArrayData(temp.GetCode()); vixl::aarch64::Label* cbnz_label = NewBakerReadBarrierPatch(custom_data); __ Add(temp.X(), obj.X(), Operand(data_offset)); @@ -6744,5 +6833,176 @@ void CodeGeneratorARM64::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_ #undef __ #undef QUICK_ENTRY_POINT +#define __ assembler.GetVIXLAssembler()-> + +static void EmitGrayCheckAndFastPath(arm64::Arm64Assembler& assembler, + vixl::aarch64::Register base_reg, + vixl::aarch64::MemOperand& lock_word, + vixl::aarch64::Label* slow_path) { + // Load the lock word containing the rb_state. + __ Ldr(ip0.W(), lock_word); + // Given the numeric representation, it's enough to check the low bit of the rb_state. + static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0"); + static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1"); + __ Tbnz(ip0.W(), LockWord::kReadBarrierStateShift, slow_path); + static_assert( + BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET == BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET, + "Field and array LDR offsets must be the same to reuse the same code."); + // Adjust the return address back to the LDR (1 instruction; 2 for heap poisoning). + static_assert(BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4), + "Field LDR must be 1 instruction (4B) before the return address label; " + " 2 instructions (8B) for heap poisoning."); + __ Add(lr, lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET); + // Introduce a dependency on the lock_word including rb_state, + // to prevent load-load reordering, and without using + // a memory barrier (which would be more expensive). + __ Add(base_reg, base_reg, Operand(ip0, LSR, 32)); + __ Br(lr); // And return back to the function. + // Note: The fake dependency is unnecessary for the slow path. +} + +// Load the read barrier introspection entrypoint in register `entrypoint`. +static void LoadReadBarrierMarkIntrospectionEntrypoint(arm64::Arm64Assembler& assembler, + vixl::aarch64::Register entrypoint) { + // entrypoint = Thread::Current()->pReadBarrierMarkReg16, i.e. pReadBarrierMarkIntrospection. + DCHECK_EQ(ip0.GetCode(), 16u); + const int32_t entry_point_offset = + Thread::ReadBarrierMarkEntryPointsOffset(ip0.GetCode()); + __ Ldr(entrypoint, MemOperand(tr, entry_point_offset)); +} + +void CodeGeneratorARM64::CompileBakerReadBarrierThunk(Arm64Assembler& assembler, + uint32_t encoded_data, + /*out*/ std::string* debug_name) { + BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); + switch (kind) { + case BakerReadBarrierKind::kField: { + // Check if the holder is gray and, if not, add fake dependency to the base register + // and return to the LDR instruction to load the reference. Otherwise, use introspection + // to load the reference and call the entrypoint (in IP1) that performs further checks + // on the reference and marks it if needed. + auto base_reg = + Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(base_reg.GetCode()); + auto holder_reg = + Register::GetXRegFromCode(BakerReadBarrierSecondRegField::Decode(encoded_data)); + CheckValidReg(holder_reg.GetCode()); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip0, ip1); + // If base_reg differs from holder_reg, the offset was too large and we must have + // emitted an explicit null check before the load. Otherwise, we need to null-check + // the holder as we do not necessarily do that check before going to the thunk. + vixl::aarch64::Label throw_npe; + if (holder_reg.Is(base_reg)) { + __ Cbz(holder_reg.W(), &throw_npe); + } + vixl::aarch64::Label slow_path; + MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value()); + EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path); + __ Bind(&slow_path); + MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET); + __ Ldr(ip0.W(), ldr_address); // Load the LDR (immediate) unsigned offset. + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); + __ Ubfx(ip0.W(), ip0.W(), 10, 12); // Extract the offset. + __ Ldr(ip0.W(), MemOperand(base_reg, ip0, LSL, 2)); // Load the reference. + // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference. + __ Br(ip1); // Jump to the entrypoint. + if (holder_reg.Is(base_reg)) { + // Add null check slow path. The stack map is at the address pointed to by LR. + __ Bind(&throw_npe); + int32_t offset = GetThreadOffset(kQuickThrowNullPointer).Int32Value(); + __ Ldr(ip0, MemOperand(/* Thread* */ vixl::aarch64::x19, offset)); + __ Br(ip0); + } + break; + } + case BakerReadBarrierKind::kArray: { + auto base_reg = + Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(base_reg.GetCode()); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip0, ip1); + vixl::aarch64::Label slow_path; + int32_t data_offset = + mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value(); + MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset); + DCHECK_LT(lock_word.GetOffset(), 0); + EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path); + __ Bind(&slow_path); + MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET); + __ Ldr(ip0.W(), ldr_address); // Load the LDR (register) unsigned offset. + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); + __ Ubfx(ip0, ip0, 16, 6); // Extract the index register, plus 32 (bit 21 is set). + __ Bfi(ip1, ip0, 3, 6); // Insert ip0 to the entrypoint address to create + // a switch case target based on the index register. + __ Mov(ip0, base_reg); // Move the base register to ip0. + __ Br(ip1); // Jump to the entrypoint's array switch case. + break; + } + case BakerReadBarrierKind::kGcRoot: { + // Check if the reference needs to be marked and if so (i.e. not null, not marked yet + // and it does not have a forwarding address), call the correct introspection entrypoint; + // otherwise return the reference (or the extracted forwarding address). + // There is no gray bit check for GC roots. + auto root_reg = + Register::GetWRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(root_reg.GetCode()); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip0, ip1); + vixl::aarch64::Label return_label, not_marked, forwarding_address; + __ Cbz(root_reg, &return_label); + MemOperand lock_word(root_reg.X(), mirror::Object::MonitorOffset().Int32Value()); + __ Ldr(ip0.W(), lock_word); + __ Tbz(ip0.W(), LockWord::kMarkBitStateShift, ¬_marked); + __ Bind(&return_label); + __ Br(lr); + __ Bind(¬_marked); + __ Tst(ip0.W(), Operand(ip0.W(), LSL, 1)); + __ B(&forwarding_address, mi); + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); + // Adjust the art_quick_read_barrier_mark_introspection address in IP1 to + // art_quick_read_barrier_mark_introspection_gc_roots. + __ Add(ip1, ip1, Operand(BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRYPOINT_OFFSET)); + __ Mov(ip0.W(), root_reg); + __ Br(ip1); + __ Bind(&forwarding_address); + __ Lsl(root_reg, ip0.W(), LockWord::kForwardingAddressShift); + __ Br(lr); + break; + } + default: + LOG(FATAL) << "Unexpected kind: " << static_cast(kind); + UNREACHABLE(); + } + + if (GetCompilerOptions().GenerateAnyDebugInfo()) { + std::ostringstream oss; + oss << "BakerReadBarrierThunk"; + switch (kind) { + case BakerReadBarrierKind::kField: + oss << "Field_r" << BakerReadBarrierFirstRegField::Decode(encoded_data) + << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data); + break; + case BakerReadBarrierKind::kArray: + oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + break; + case BakerReadBarrierKind::kGcRoot: + oss << "GcRoot_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + break; + } + *debug_name = oss.str(); + } +} + +#undef __ + } // namespace arm64 } // namespace art diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 6a52eecbd3..aa343b1185 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -18,6 +18,7 @@ #define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_ #include "arch/arm64/quick_method_frame_info_arm64.h" +#include "base/bit_field.h" #include "code_generator.h" #include "common_arm64.h" #include "dex/dex_file_types.h" @@ -36,6 +37,11 @@ #pragma GCC diagnostic pop namespace art { + +namespace linker { +class Arm64RelativePatcherTest; +} // namespace linker + namespace arm64 { class CodeGeneratorARM64; @@ -309,17 +315,6 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { uint32_t offset, Location maybe_temp, ReadBarrierOption read_barrier_option); - // Generate a GC root reference load: - // - // root <- *(obj + offset) - // - // while honoring read barriers based on read_barrier_option. - void GenerateGcRootFieldLoad(HInstruction* instruction, - Location root, - vixl::aarch64::Register obj, - uint32_t offset, - vixl::aarch64::Label* fixup_label, - ReadBarrierOption read_barrier_option); // Generate a floating-point comparison. void GenerateFcmp(HInstruction* instruction); @@ -641,9 +636,24 @@ class CodeGeneratorARM64 : public CodeGenerator { vixl::aarch64::Register base); void EmitLinkerPatches(ArenaVector* linker_patches) OVERRIDE; + bool NeedsThunkCode(const linker::LinkerPatch& patch) const OVERRIDE; + void EmitThunkCode(const linker::LinkerPatch& patch, + /*out*/ ArenaVector* code, + /*out*/ std::string* debug_name) OVERRIDE; void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; + // Generate a GC root reference load: + // + // root <- *(obj + offset) + // + // while honoring read barriers based on read_barrier_option. + void GenerateGcRootFieldLoad(HInstruction* instruction, + Location root, + vixl::aarch64::Register obj, + uint32_t offset, + vixl::aarch64::Label* fixup_label, + ReadBarrierOption read_barrier_option); // Fast path implementation of ReadBarrier::Barrier for a heap // reference field load when Baker's read barriers are used. void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -778,6 +788,62 @@ class CodeGeneratorARM64 : public CodeGenerator { void GenerateExplicitNullCheck(HNullCheck* instruction) OVERRIDE; private: + // Encoding of thunk type and data for link-time generated thunks for Baker read barriers. + + enum class BakerReadBarrierKind : uint8_t { + kField, // Field get or array get with constant offset (i.e. constant index). + kArray, // Array get with index in register. + kGcRoot, // GC root load. + kLast = kGcRoot + }; + + static constexpr uint32_t kBakerReadBarrierInvalidEncodedReg = /* sp/zr is invalid */ 31u; + + static constexpr size_t kBitsForBakerReadBarrierKind = + MinimumBitsToStore(static_cast(BakerReadBarrierKind::kLast)); + static constexpr size_t kBakerReadBarrierBitsForRegister = + MinimumBitsToStore(kBakerReadBarrierInvalidEncodedReg); + using BakerReadBarrierKindField = + BitField; + using BakerReadBarrierFirstRegField = + BitField; + using BakerReadBarrierSecondRegField = + BitField; + + static void CheckValidReg(uint32_t reg) { + DCHECK(reg < vixl::aarch64::lr.GetCode() && + reg != vixl::aarch64::ip0.GetCode() && + reg != vixl::aarch64::ip1.GetCode()) << reg; + } + + static inline uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, uint32_t holder_reg) { + CheckValidReg(base_reg); + CheckValidReg(holder_reg); + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kField) | + BakerReadBarrierFirstRegField::Encode(base_reg) | + BakerReadBarrierSecondRegField::Encode(holder_reg); + } + + static inline uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { + CheckValidReg(base_reg); + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kArray) | + BakerReadBarrierFirstRegField::Encode(base_reg) | + BakerReadBarrierSecondRegField::Encode(kBakerReadBarrierInvalidEncodedReg); + } + + static inline uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg) { + CheckValidReg(root_reg); + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kGcRoot) | + BakerReadBarrierFirstRegField::Encode(root_reg) | + BakerReadBarrierSecondRegField::Encode(kBakerReadBarrierInvalidEncodedReg); + } + + void CompileBakerReadBarrierThunk(Arm64Assembler& assembler, + uint32_t encoded_data, + /*out*/ std::string* debug_name); + using Uint64ToLiteralMap = ArenaSafeMap*>; using Uint32ToLiteralMap = ArenaSafeMap*>; using StringToLiteralMap = ArenaSafeMapIsAvailable(ip)); temps->Exclude(ip); DCHECK(!temps->IsAvailable(kBakerCcEntrypointRegister)); - DCHECK_EQ(kBakerCcEntrypointRegister.GetCode(), - linker::Thumb2RelativePatcher::kBakerCcEntrypointRegister); DCHECK_NE(instruction->GetLocations()->GetTempCount(), 0u); DCHECK(RegisterFrom(instruction->GetLocations()->GetTemp( instruction->GetLocations()->GetTempCount() - 1u)).Is(kBakerCcEntrypointRegister)); @@ -2422,6 +2417,80 @@ void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) { FixJumpTables(); GetAssembler()->FinalizeCode(); CodeGenerator::Finalize(allocator); + + // Verify Baker read barrier linker patches. + if (kIsDebugBuild) { + ArrayRef code = allocator->GetMemory(); + for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) { + DCHECK(info.label.IsBound()); + uint32_t literal_offset = info.label.GetLocation(); + DCHECK_ALIGNED(literal_offset, 2u); + + auto GetInsn16 = [&code](uint32_t offset) { + DCHECK_ALIGNED(offset, 2u); + return (static_cast(code[offset + 0]) << 0) + + (static_cast(code[offset + 1]) << 8); + }; + auto GetInsn32 = [=](uint32_t offset) { + return (GetInsn16(offset) << 16) + (GetInsn16(offset + 2u) << 0); + }; + + uint32_t encoded_data = info.custom_data; + BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); + // Check that the next instruction matches the expected LDR. + switch (kind) { + case BakerReadBarrierKind::kField: { + BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); + if (width == BakerReadBarrierWidth::kWide) { + DCHECK_GE(code.size() - literal_offset, 8u); + uint32_t next_insn = GetInsn32(literal_offset + 4u); + // LDR (immediate), encoding T3, with correct base_reg. + CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xffff0000u, 0xf8d00000u | (base_reg << 16)); + } else { + DCHECK_GE(code.size() - literal_offset, 6u); + uint32_t next_insn = GetInsn16(literal_offset + 4u); + // LDR (immediate), encoding T1, with correct base_reg. + CheckValidReg(next_insn & 0x7u); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xf838u, 0x6800u | (base_reg << 3)); + } + break; + } + case BakerReadBarrierKind::kArray: { + DCHECK_GE(code.size() - literal_offset, 8u); + uint32_t next_insn = GetInsn32(literal_offset + 4u); + // LDR (register) with correct base_reg, S=1 and option=011 (LDR Wt, [Xn, Xm, LSL #2]). + CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xffff0ff0u, 0xf8500020u | (base_reg << 16)); + CheckValidReg(next_insn & 0xf); // Check index register + break; + } + case BakerReadBarrierKind::kGcRoot: { + BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); + if (width == BakerReadBarrierWidth::kWide) { + DCHECK_GE(literal_offset, 4u); + uint32_t prev_insn = GetInsn32(literal_offset - 4u); + // LDR (immediate), encoding T3, with correct root_reg. + const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(prev_insn & 0xfff0f000u, 0xf8d00000u | (root_reg << 12)); + } else { + DCHECK_GE(literal_offset, 2u); + uint32_t prev_insn = GetInsn16(literal_offset - 2u); + // LDR (immediate), encoding T1, with correct root_reg. + const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(prev_insn & 0xf807u, 0x6800u | root_reg); + } + break; + } + default: + LOG(FATAL) << "Unexpected kind: " << static_cast(kind); + UNREACHABLE(); + } + } + } } void CodeGeneratorARMVIXL::SetupBlockedRegisters() const { @@ -7413,11 +7482,11 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ DCHECK(!cls->MustGenerateClinitCheck()); // /* GcRoot */ out = current_method->declaring_class_ vixl32::Register current_method = InputRegisterAt(cls, 0); - GenerateGcRootFieldLoad(cls, - out_loc, - current_method, - ArtMethod::DeclaringClassOffset().Int32Value(), - read_barrier_option); + codegen_->GenerateGcRootFieldLoad(cls, + out_loc, + current_method, + ArtMethod::DeclaringClassOffset().Int32Value(), + read_barrier_option); break; } case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { @@ -7448,7 +7517,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); codegen_->EmitMovwMovtPlaceholder(labels, out); - GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); + codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); generate_null_check = true; break; } @@ -7457,7 +7526,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ cls->GetTypeIndex(), cls->GetClass())); // /* GcRoot */ out = *out - GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); + codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); break; } case HLoadClass::LoadKind::kRuntimeCall: @@ -7665,7 +7734,8 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex()); codegen_->EmitMovwMovtPlaceholder(labels, out); - GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); + codegen_->GenerateGcRootFieldLoad( + load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); LoadStringSlowPathARMVIXL* slow_path = new (codegen_->GetScopedAllocator()) LoadStringSlowPathARMVIXL(load); codegen_->AddSlowPath(slow_path); @@ -7679,7 +7749,8 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE load->GetStringIndex(), load->GetString())); // /* GcRoot */ out = *out - GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); + codegen_->GenerateGcRootFieldLoad( + load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); return; } default: @@ -8730,7 +8801,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadTwoRegisters( } } -void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( +void CodeGeneratorARMVIXL::GenerateGcRootFieldLoad( HInstruction* instruction, Location root, vixl32::Register obj, @@ -8761,9 +8832,8 @@ void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( UseScratchRegisterScope temps(GetVIXLAssembler()); ExcludeIPAndBakerCcEntrypointRegister(&temps, instruction); bool narrow = CanEmitNarrowLdr(root_reg, obj, offset); - uint32_t custom_data = linker::Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData( - root_reg.GetCode(), narrow); - vixl32::Label* bne_label = codegen_->NewBakerReadBarrierPatch(custom_data); + uint32_t custom_data = EncodeBakerReadBarrierGcRootData(root_reg.GetCode(), narrow); + vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data); vixl::EmissionCheckScope guard(GetVIXLAssembler(), 4 * vixl32::kMaxInstructionSizeInBytes); vixl32::Label return_address; @@ -8774,7 +8844,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( DCHECK_LT(offset, kReferenceLoadMinFarOffset); ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset(); __ ldr(EncodingSize(narrow ? Narrow : Wide), root_reg, MemOperand(obj, offset)); - EmitPlaceholderBne(codegen_, bne_label); + EmitPlaceholderBne(this, bne_label); __ Bind(&return_address); DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(), narrow ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET @@ -8794,8 +8864,8 @@ void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( // Slow path marking the GC root `root`. The entrypoint will // be loaded by the slow path code. SlowPathCodeARMVIXL* slow_path = - new (codegen_->GetScopedAllocator()) ReadBarrierMarkSlowPathARMVIXL(instruction, root); - codegen_->AddSlowPath(slow_path); + new (GetScopedAllocator()) ReadBarrierMarkSlowPathARMVIXL(instruction, root); + AddSlowPath(slow_path); // /* GcRoot */ root = *(obj + offset) GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset); @@ -8816,7 +8886,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( // /* GcRoot* */ root = obj + offset __ Add(root_reg, obj, offset); // /* mirror::Object* */ root = root->Read() - codegen_->GenerateReadBarrierForRootSlow(instruction, root, root); + GenerateReadBarrierForRootSlow(instruction, root, root); } } else { // Plain GC root load with no read barrier. @@ -8825,7 +8895,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( // Note that GC roots are not affected by heap poisoning, thus we // do not have to unpoison `root_reg` here. } - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 18); + MaybeGenerateMarkingRegisterCheck(/* code */ 18); } void CodeGeneratorARMVIXL::MaybeAddBakerCcEntrypointTempForFields(LocationSummary* locations) { @@ -8886,8 +8956,7 @@ void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* i } UseScratchRegisterScope temps(GetVIXLAssembler()); ExcludeIPAndBakerCcEntrypointRegister(&temps, instruction); - uint32_t custom_data = linker::Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( - base.GetCode(), obj.GetCode(), narrow); + uint32_t custom_data = EncodeBakerReadBarrierFieldData(base.GetCode(), obj.GetCode(), narrow); vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data); { @@ -8973,8 +9042,7 @@ void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(HInstruction* i UseScratchRegisterScope temps(GetVIXLAssembler()); ExcludeIPAndBakerCcEntrypointRegister(&temps, instruction); - uint32_t custom_data = - linker::Thumb2RelativePatcher::EncodeBakerReadBarrierArrayData(data_reg.GetCode()); + uint32_t custom_data = EncodeBakerReadBarrierArrayData(data_reg.GetCode()); vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data); __ Add(data_reg, obj, Operand(data_offset)); @@ -9111,7 +9179,7 @@ void CodeGeneratorARMVIXL::UpdateReferenceFieldWithBakerReadBarrier(HInstruction void CodeGeneratorARMVIXL::GenerateRawReferenceLoad(HInstruction* instruction, Location ref, - vixl::aarch32::Register obj, + vixl32::Register obj, uint32_t offset, Location index, ScaleFactor scale_factor, @@ -9451,7 +9519,7 @@ CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativePa return &patches->back(); } -vixl::aarch32::Label* CodeGeneratorARMVIXL::NewBakerReadBarrierPatch(uint32_t custom_data) { +vixl32::Label* CodeGeneratorARMVIXL::NewBakerReadBarrierPatch(uint32_t custom_data) { baker_read_barrier_patches_.emplace_back(custom_data); return &baker_read_barrier_patches_.back().label; } @@ -9548,6 +9616,45 @@ void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector* l DCHECK_EQ(size, linker_patches->size()); } +bool CodeGeneratorARMVIXL::NeedsThunkCode(const linker::LinkerPatch& patch) const { + return patch.GetType() == linker::LinkerPatch::Type::kBakerReadBarrierBranch || + patch.GetType() == linker::LinkerPatch::Type::kCallRelative; +} + +void CodeGeneratorARMVIXL::EmitThunkCode(const linker::LinkerPatch& patch, + /*out*/ ArenaVector* code, + /*out*/ std::string* debug_name) { + arm::ArmVIXLAssembler assembler(GetGraph()->GetAllocator()); + switch (patch.GetType()) { + case linker::LinkerPatch::Type::kCallRelative: + // The thunk just uses the entry point in the ArtMethod. This works even for calls + // to the generic JNI and interpreter trampolines. + assembler.LoadFromOffset( + arm::kLoadWord, + vixl32::pc, + vixl32::r0, + ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value()); + assembler.GetVIXLAssembler()->Bkpt(0); + if (GetCompilerOptions().GenerateAnyDebugInfo()) { + *debug_name = "MethodCallThunk"; + } + break; + case linker::LinkerPatch::Type::kBakerReadBarrierBranch: + DCHECK_EQ(patch.GetBakerCustomValue2(), 0u); + CompileBakerReadBarrierThunk(assembler, patch.GetBakerCustomValue1(), debug_name); + break; + default: + LOG(FATAL) << "Unexpected patch type " << patch.GetType(); + UNREACHABLE(); + } + + // Ensure we emit the literal pool if any. + assembler.FinalizeCode(); + code->resize(assembler.CodeSize()); + MemoryRegion code_region(code->data(), code->size()); + assembler.FinalizeInstructions(code_region); +} + VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateUint32Literal( uint32_t value, Uint32ToLiteralMap* map) { @@ -9792,5 +9899,210 @@ void CodeGeneratorARMVIXL::EmitMovwMovtPlaceholder( #undef QUICK_ENTRY_POINT #undef TODO_VIXL32 +#define __ assembler.GetVIXLAssembler()-> + +static void EmitGrayCheckAndFastPath(ArmVIXLAssembler& assembler, + vixl32::Register base_reg, + vixl32::MemOperand& lock_word, + vixl32::Label* slow_path, + int32_t raw_ldr_offset) { + // Load the lock word containing the rb_state. + __ Ldr(ip, lock_word); + // Given the numeric representation, it's enough to check the low bit of the rb_state. + static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0"); + static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1"); + __ Tst(ip, Operand(LockWord::kReadBarrierStateMaskShifted)); + __ B(ne, slow_path, /* is_far_target */ false); + __ Add(lr, lr, raw_ldr_offset); + // Introduce a dependency on the lock_word including rb_state, + // to prevent load-load reordering, and without using + // a memory barrier (which would be more expensive). + __ Add(base_reg, base_reg, Operand(ip, LSR, 32)); + __ Bx(lr); // And return back to the function. + // Note: The fake dependency is unnecessary for the slow path. +} + +// Load the read barrier introspection entrypoint in register `entrypoint` +static void LoadReadBarrierMarkIntrospectionEntrypoint(ArmVIXLAssembler& assembler, + vixl32::Register entrypoint) { + // The register where the read barrier introspection entrypoint is loaded + // is fixed: `Thumb2RelativePatcher::kBakerCcEntrypointRegister` (R4). + DCHECK(entrypoint.Is(kBakerCcEntrypointRegister)); + // entrypoint = Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection. + DCHECK_EQ(ip.GetCode(), 12u); + const int32_t entry_point_offset = + Thread::ReadBarrierMarkEntryPointsOffset(ip.GetCode()); + __ Ldr(entrypoint, MemOperand(tr, entry_point_offset)); +} + +void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assembler, + uint32_t encoded_data, + /*out*/ std::string* debug_name) { + BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); + switch (kind) { + case BakerReadBarrierKind::kField: { + // Check if the holder is gray and, if not, add fake dependency to the base register + // and return to the LDR instruction to load the reference. Otherwise, use introspection + // to load the reference and call the entrypoint (in kBakerCcEntrypointRegister) + // that performs further checks on the reference and marks it if needed. + vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(base_reg.GetCode()); + vixl32::Register holder_reg(BakerReadBarrierSecondRegField::Decode(encoded_data)); + CheckValidReg(holder_reg.GetCode()); + BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip); + // If base_reg differs from holder_reg, the offset was too large and we must have + // emitted an explicit null check before the load. Otherwise, we need to null-check + // the holder as we do not necessarily do that check before going to the thunk. + vixl32::Label throw_npe; + if (holder_reg.Is(base_reg)) { + __ CompareAndBranchIfZero(holder_reg, &throw_npe, /* is_far_target */ false); + } + vixl32::Label slow_path; + MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value()); + const int32_t raw_ldr_offset = (width == BakerReadBarrierWidth::kWide) + ? BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET + : BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET; + EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset); + __ Bind(&slow_path); + const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 + + raw_ldr_offset; + vixl32::Register ep_reg(kBakerCcEntrypointRegister); + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); + if (width == BakerReadBarrierWidth::kWide) { + MemOperand ldr_half_address(lr, ldr_offset + 2); + __ Ldrh(ip, ldr_half_address); // Load the LDR immediate half-word with "Rt | imm12". + __ Ubfx(ip, ip, 0, 12); // Extract the offset imm12. + __ Ldr(ip, MemOperand(base_reg, ip)); // Load the reference. + } else { + MemOperand ldr_address(lr, ldr_offset); + __ Ldrh(ip, ldr_address); // Load the LDR immediate, encoding T1. + __ Add(ep_reg, // Adjust the entrypoint address to the entrypoint + ep_reg, // for narrow LDR. + Operand(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET)); + __ Ubfx(ip, ip, 6, 5); // Extract the imm5, i.e. offset / 4. + __ Ldr(ip, MemOperand(base_reg, ip, LSL, 2)); // Load the reference. + } + // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference. + __ Bx(ep_reg); // Jump to the entrypoint. + if (holder_reg.Is(base_reg)) { + // Add null check slow path. The stack map is at the address pointed to by LR. + __ Bind(&throw_npe); + int32_t offset = GetThreadOffset(kQuickThrowNullPointer).Int32Value(); + __ Ldr(ip, MemOperand(/* Thread* */ vixl32::r9, offset)); + __ Bx(ip); + } + break; + } + case BakerReadBarrierKind::kArray: { + vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(base_reg.GetCode()); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip); + vixl32::Label slow_path; + int32_t data_offset = + mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value(); + MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset); + DCHECK_LT(lock_word.GetOffsetImmediate(), 0); + const int32_t raw_ldr_offset = BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET; + EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset); + __ Bind(&slow_path); + const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 + + raw_ldr_offset; + MemOperand ldr_address(lr, ldr_offset + 2); + __ Ldrb(ip, ldr_address); // Load the LDR (register) byte with "00 | imm2 | Rm", + // i.e. Rm+32 because the scale in imm2 is 2. + vixl32::Register ep_reg(kBakerCcEntrypointRegister); + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); + __ Bfi(ep_reg, ip, 3, 6); // Insert ip to the entrypoint address to create + // a switch case target based on the index register. + __ Mov(ip, base_reg); // Move the base register to ip0. + __ Bx(ep_reg); // Jump to the entrypoint's array switch case. + break; + } + case BakerReadBarrierKind::kGcRoot: { + // Check if the reference needs to be marked and if so (i.e. not null, not marked yet + // and it does not have a forwarding address), call the correct introspection entrypoint; + // otherwise return the reference (or the extracted forwarding address). + // There is no gray bit check for GC roots. + vixl32::Register root_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(root_reg.GetCode()); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip); + vixl32::Label return_label, not_marked, forwarding_address; + __ CompareAndBranchIfZero(root_reg, &return_label, /* is_far_target */ false); + MemOperand lock_word(root_reg, mirror::Object::MonitorOffset().Int32Value()); + __ Ldr(ip, lock_word); + __ Tst(ip, LockWord::kMarkBitStateMaskShifted); + __ B(eq, ¬_marked); + __ Bind(&return_label); + __ Bx(lr); + __ Bind(¬_marked); + static_assert(LockWord::kStateShift == 30 && LockWord::kStateForwardingAddress == 3, + "To use 'CMP ip, #modified-immediate; BHS', we need the lock word state in " + " the highest bits and the 'forwarding address' state to have all bits set"); + __ Cmp(ip, Operand(0xc0000000)); + __ B(hs, &forwarding_address); + vixl32::Register ep_reg(kBakerCcEntrypointRegister); + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); + // Adjust the art_quick_read_barrier_mark_introspection address in kBakerCcEntrypointRegister + // to art_quick_read_barrier_mark_introspection_gc_roots. + int32_t entrypoint_offset = (width == BakerReadBarrierWidth::kWide) + ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET + : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET; + __ Add(ep_reg, ep_reg, Operand(entrypoint_offset)); + __ Mov(ip, root_reg); + __ Bx(ep_reg); + __ Bind(&forwarding_address); + __ Lsl(root_reg, ip, LockWord::kForwardingAddressShift); + __ Bx(lr); + break; + } + default: + LOG(FATAL) << "Unexpected kind: " << static_cast(kind); + UNREACHABLE(); + } + + if (GetCompilerOptions().GenerateAnyDebugInfo()) { + std::ostringstream oss; + oss << "BakerReadBarrierThunk"; + switch (kind) { + case BakerReadBarrierKind::kField: + oss << "Field"; + if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) { + oss << "Wide"; + } + oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data) + << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data); + break; + case BakerReadBarrierKind::kArray: + oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide); + break; + case BakerReadBarrierKind::kGcRoot: + oss << "GcRoot"; + if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) { + oss << "Wide"; + } + oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + break; + } + *debug_name = oss.str(); + } +} + +#undef __ + } // namespace arm } // namespace art diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 2114ea1ba1..6b9919ab15 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -36,6 +36,11 @@ #pragma GCC diagnostic pop namespace art { + +namespace linker { +class Thumb2RelativePatcherTest; +} // namespace linker + namespace arm { // This constant is used as an approximate margin when emission of veneer and literal pools @@ -108,6 +113,9 @@ static const vixl::aarch32::SRegister kRuntimeParameterFpuRegistersVIXL[] = { static const size_t kRuntimeParameterFpuRegistersLengthVIXL = arraysize(kRuntimeParameterFpuRegistersVIXL); +// The reserved entrypoint register for link-time generated thunks. +const vixl::aarch32::Register kBakerCcEntrypointRegister = vixl32::r4; + class LoadClassSlowPathARMVIXL; class CodeGeneratorARMVIXL; @@ -388,16 +396,6 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { uint32_t offset, Location maybe_temp, ReadBarrierOption read_barrier_option); - // Generate a GC root reference load: - // - // root <- *(obj + offset) - // - // while honoring read barriers based on read_barrier_option. - void GenerateGcRootFieldLoad(HInstruction* instruction, - Location root, - vixl::aarch32::Register obj, - uint32_t offset, - ReadBarrierOption read_barrier_option); void GenerateTestAndBranch(HInstruction* instruction, size_t condition_input_index, vixl::aarch32::Label* true_target, @@ -606,6 +604,10 @@ class CodeGeneratorARMVIXL : public CodeGenerator { Handle handle); void EmitLinkerPatches(ArenaVector* linker_patches) OVERRIDE; + bool NeedsThunkCode(const linker::LinkerPatch& patch) const OVERRIDE; + void EmitThunkCode(const linker::LinkerPatch& patch, + /*out*/ ArenaVector* code, + /*out*/ std::string* debug_name) OVERRIDE; void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; @@ -613,6 +615,16 @@ class CodeGeneratorARMVIXL : public CodeGenerator { // is added only for AOT compilation if link-time generated thunks for fields are enabled. void MaybeAddBakerCcEntrypointTempForFields(LocationSummary* locations); + // Generate a GC root reference load: + // + // root <- *(obj + offset) + // + // while honoring read barriers based on read_barrier_option. + void GenerateGcRootFieldLoad(HInstruction* instruction, + Location root, + vixl::aarch32::Register obj, + uint32_t offset, + ReadBarrierOption read_barrier_option); // Fast path implementation of ReadBarrier::Barrier for a heap // reference field load when Baker's read barriers are used. void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -767,6 +779,83 @@ class CodeGeneratorARMVIXL : public CodeGenerator { vixl::aarch32::Register temp = vixl32::Register()); private: + // Encoding of thunk type and data for link-time generated thunks for Baker read barriers. + + enum class BakerReadBarrierKind : uint8_t { + kField, // Field get or array get with constant offset (i.e. constant index). + kArray, // Array get with index in register. + kGcRoot, // GC root load. + kLast = kGcRoot + }; + + enum class BakerReadBarrierWidth : uint8_t { + kWide, // 32-bit LDR (and 32-bit NEG if heap poisoning is enabled). + kNarrow, // 16-bit LDR (and 16-bit NEG if heap poisoning is enabled). + kLast = kNarrow + }; + + static constexpr uint32_t kBakerReadBarrierInvalidEncodedReg = /* pc is invalid */ 15u; + + static constexpr size_t kBitsForBakerReadBarrierKind = + MinimumBitsToStore(static_cast(BakerReadBarrierKind::kLast)); + static constexpr size_t kBakerReadBarrierBitsForRegister = + MinimumBitsToStore(kBakerReadBarrierInvalidEncodedReg); + using BakerReadBarrierKindField = + BitField; + using BakerReadBarrierFirstRegField = + BitField; + using BakerReadBarrierSecondRegField = + BitField; + static constexpr size_t kBitsForBakerReadBarrierWidth = + MinimumBitsToStore(static_cast(BakerReadBarrierWidth::kLast)); + using BakerReadBarrierWidthField = + BitField; + + static void CheckValidReg(uint32_t reg) { + DCHECK(reg < vixl::aarch32::ip.GetCode() && reg != kBakerCcEntrypointRegister.GetCode()) << reg; + } + + static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, + uint32_t holder_reg, + bool narrow) { + CheckValidReg(base_reg); + CheckValidReg(holder_reg); + DCHECK(!narrow || base_reg < 8u) << base_reg; + BakerReadBarrierWidth width = + narrow ? BakerReadBarrierWidth::kNarrow : BakerReadBarrierWidth::kWide; + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kField) | + BakerReadBarrierFirstRegField::Encode(base_reg) | + BakerReadBarrierSecondRegField::Encode(holder_reg) | + BakerReadBarrierWidthField::Encode(width); + } + + static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { + CheckValidReg(base_reg); + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kArray) | + BakerReadBarrierFirstRegField::Encode(base_reg) | + BakerReadBarrierSecondRegField::Encode(kBakerReadBarrierInvalidEncodedReg) | + BakerReadBarrierWidthField::Encode(BakerReadBarrierWidth::kWide); + } + + static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg, bool narrow) { + CheckValidReg(root_reg); + DCHECK(!narrow || root_reg < 8u) << root_reg; + BakerReadBarrierWidth width = + narrow ? BakerReadBarrierWidth::kNarrow : BakerReadBarrierWidth::kWide; + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kGcRoot) | + BakerReadBarrierFirstRegField::Encode(root_reg) | + BakerReadBarrierSecondRegField::Encode(kBakerReadBarrierInvalidEncodedReg) | + BakerReadBarrierWidthField::Encode(width); + } + + void CompileBakerReadBarrierThunk(ArmVIXLAssembler& assembler, + uint32_t encoded_data, + /*out*/ std::string* debug_name); + vixl::aarch32::Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, vixl::aarch32::Register temp); @@ -829,6 +918,7 @@ class CodeGeneratorARMVIXL : public CodeGenerator { // Patches for class literals in JIT compiled code. TypeToLiteralMap jit_class_patches_; + friend class linker::Thumb2RelativePatcherTest; DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARMVIXL); }; diff --git a/compiler/optimizing/codegen_test_utils.h b/compiler/optimizing/codegen_test_utils.h index c41c290c8b..792cfb539a 100644 --- a/compiler/optimizing/codegen_test_utils.h +++ b/compiler/optimizing/codegen_test_utils.h @@ -195,7 +195,9 @@ class InternalCodeAllocator : public CodeAllocator { } size_t GetSize() const { return size_; } - uint8_t* GetMemory() const { return memory_.get(); } + ArrayRef GetMemory() const OVERRIDE { + return ArrayRef(memory_.get(), size_); + } private: size_t size_; @@ -269,8 +271,8 @@ static void Run(const InternalCodeAllocator& allocator, InstructionSet target_isa = codegen.GetInstructionSet(); typedef Expected (*fptr)(); - CommonCompilerTest::MakeExecutable(allocator.GetMemory(), allocator.GetSize()); - fptr f = reinterpret_cast(allocator.GetMemory()); + CommonCompilerTest::MakeExecutable(allocator.GetMemory().data(), allocator.GetMemory().size()); + fptr f = reinterpret_cast(reinterpret_cast(allocator.GetMemory().data())); if (target_isa == InstructionSet::kThumb2) { // For thumb we need the bottom bit set. f = reinterpret_cast(reinterpret_cast(f) + 1); diff --git a/compiler/optimizing/optimizing_cfi_test.cc b/compiler/optimizing/optimizing_cfi_test.cc index d20b681b49..2e189fdd14 100644 --- a/compiler/optimizing/optimizing_cfi_test.cc +++ b/compiler/optimizing/optimizing_cfi_test.cc @@ -105,15 +105,15 @@ class OptimizingCFITest : public CFITest, public OptimizingUnitTestHelper { const std::vector& expected_asm, const std::vector& expected_cfi) { // Get the outputs. - const std::vector& actual_asm = code_allocator_.GetMemory(); + ArrayRef actual_asm = code_allocator_.GetMemory(); Assembler* opt_asm = code_gen_->GetAssembler(); - const std::vector& actual_cfi = *(opt_asm->cfi().data()); + ArrayRef actual_cfi(*(opt_asm->cfi().data())); if (kGenerateExpected) { GenerateExpected(stdout, isa, isa_str, actual_asm, actual_cfi); } else { - EXPECT_EQ(expected_asm, actual_asm); - EXPECT_EQ(expected_cfi, actual_cfi); + EXPECT_EQ(ArrayRef(expected_asm), actual_asm); + EXPECT_EQ(ArrayRef(expected_cfi), actual_cfi); } } @@ -140,7 +140,7 @@ class OptimizingCFITest : public CFITest, public OptimizingUnitTestHelper { return memory_.data(); } - const std::vector& GetMemory() { return memory_; } + ArrayRef GetMemory() const OVERRIDE { return ArrayRef(memory_); } private: std::vector memory_; diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index e42dfc10ba..79165826d1 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -75,22 +75,18 @@ static constexpr const char* kPassNameSeparator = "$"; class CodeVectorAllocator FINAL : public CodeAllocator { public: explicit CodeVectorAllocator(ArenaAllocator* allocator) - : memory_(allocator->Adapter(kArenaAllocCodeBuffer)), - size_(0) {} + : memory_(allocator->Adapter(kArenaAllocCodeBuffer)) {} virtual uint8_t* Allocate(size_t size) { - size_ = size; memory_.resize(size); return &memory_[0]; } - size_t GetSize() const { return size_; } - const ArenaVector& GetMemory() const { return memory_; } + ArrayRef GetMemory() const OVERRIDE { return ArrayRef(memory_); } uint8_t* GetData() { return memory_.data(); } private: ArenaVector memory_; - size_t size_; DISALLOW_COPY_AND_ASSIGN(CodeVectorAllocator); }; @@ -719,7 +715,7 @@ CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod( GetCompilerDriver(), codegen->GetInstructionSet(), - ArrayRef(code_allocator->GetMemory()), + code_allocator->GetMemory(), // Follow Quick's behavior and set the frame size to zero if it is // considered "empty" (see the definition of // art::CodeGenerator::HasEmptyFrame). @@ -731,6 +727,16 @@ CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, ArrayRef(*codegen->GetAssembler()->cfi().data()), ArrayRef(linker_patches)); + CompiledMethodStorage* storage = GetCompilerDriver()->GetCompiledMethodStorage(); + for (const linker::LinkerPatch& patch : linker_patches) { + if (codegen->NeedsThunkCode(patch) && storage->GetThunkCode(patch).empty()) { + ArenaVector code(allocator->Adapter()); + std::string debug_name; + codegen->EmitThunkCode(patch, &code, &debug_name); + storage->SetThunkCode(patch, ArrayRef(code), debug_name); + } + } + return compiled_method; } @@ -1339,7 +1345,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, codegen->GetCoreSpillMask(), codegen->GetFpuSpillMask(), code_allocator.GetMemory().data(), - code_allocator.GetSize(), + code_allocator.GetMemory().size(), data_size, osr, roots, @@ -1369,7 +1375,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, info.is_optimized = true; info.is_code_address_text_relative = false; info.code_address = code_address; - info.code_size = code_allocator.GetSize(); + info.code_size = code_allocator.GetMemory().size(); info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); info.code_info = stack_map_size == 0 ? nullptr : stack_map_data; info.cfi = ArrayRef(*codegen->GetAssembler()->cfi().data()); @@ -1378,7 +1384,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, Runtime::Current()->GetJit()->AddMemoryUsage(method, allocator.BytesUsed()); if (jit_logger != nullptr) { - jit_logger->WriteLog(code, code_allocator.GetSize(), method); + jit_logger->WriteLog(code, code_allocator.GetMemory().size(), method); } if (kArenaAllocatorCountAllocations) { diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 6950b93e51..0c1d778c25 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -2061,7 +2061,9 @@ class Dex2Oat FINAL { { TimingLogger::ScopedTiming t2("dex2oat Write ELF", timings_); - linker::MultiOatRelativePatcher patcher(instruction_set_, instruction_set_features_.get()); + linker::MultiOatRelativePatcher patcher(instruction_set_, + instruction_set_features_.get(), + driver_->GetCompiledMethodStorage()); for (size_t i = 0, size = oat_files_.size(); i != size; ++i) { std::unique_ptr& elf_writer = elf_writers_[i]; std::unique_ptr& oat_writer = oat_writers_[i]; diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index 7449191984..476a843821 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -299,7 +299,8 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, DCHECK_EQ(vdex_files.size(), oat_files.size()); for (size_t i = 0, size = oat_files.size(); i != size; ++i) { MultiOatRelativePatcher patcher(driver->GetInstructionSet(), - driver->GetInstructionSetFeatures()); + driver->GetInstructionSetFeatures(), + driver->GetCompiledMethodStorage()); OatWriter* const oat_writer = oat_writers[i].get(); ElfWriter* const elf_writer = elf_writers[i].get(); std::vector cur_dex_files(1u, class_path[i]); diff --git a/dex2oat/linker/multi_oat_relative_patcher.cc b/dex2oat/linker/multi_oat_relative_patcher.cc index 1abaf7dfd1..1449d478f9 100644 --- a/dex2oat/linker/multi_oat_relative_patcher.cc +++ b/dex2oat/linker/multi_oat_relative_patcher.cc @@ -20,14 +20,28 @@ #include "base/bit_utils.h" #include "globals.h" +#include "driver/compiled_method_storage.h" namespace art { namespace linker { +void MultiOatRelativePatcher::ThunkProvider::GetThunkCode(const LinkerPatch& patch, + /*out*/ ArrayRef* code, + /*out*/ std::string* debug_name) { + *code = storage_->GetThunkCode(patch, debug_name); + DCHECK(!code->empty()); +} + + MultiOatRelativePatcher::MultiOatRelativePatcher(InstructionSet instruction_set, - const InstructionSetFeatures* features) - : method_offset_map_(), - relative_patcher_(RelativePatcher::Create(instruction_set, features, &method_offset_map_)), + const InstructionSetFeatures* features, + CompiledMethodStorage* storage) + : thunk_provider_(storage), + method_offset_map_(), + relative_patcher_(RelativePatcher::Create(instruction_set, + features, + &thunk_provider_, + &method_offset_map_)), adjustment_(0u), instruction_set_(instruction_set), start_size_code_alignment_(0u), diff --git a/dex2oat/linker/multi_oat_relative_patcher.h b/dex2oat/linker/multi_oat_relative_patcher.h index bd33b95318..60fcfe8b58 100644 --- a/dex2oat/linker/multi_oat_relative_patcher.h +++ b/dex2oat/linker/multi_oat_relative_patcher.h @@ -26,6 +26,7 @@ namespace art { class CompiledMethod; +class CompiledMethodStorage; class InstructionSetFeatures; namespace linker { @@ -38,7 +39,9 @@ class MultiOatRelativePatcher FINAL { public: using const_iterator = SafeMap::const_iterator; - MultiOatRelativePatcher(InstructionSet instruction_set, const InstructionSetFeatures* features); + MultiOatRelativePatcher(InstructionSet instruction_set, + const InstructionSetFeatures* features, + CompiledMethodStorage* storage); // Mark the start of a new oat file (for statistics retrieval) and set the // adjustment for a new oat file to apply to all relative offsets that are @@ -129,6 +132,19 @@ class MultiOatRelativePatcher FINAL { uint32_t MiscThunksSize() const; private: + class ThunkProvider : public RelativePatcherThunkProvider { + public: + explicit ThunkProvider(CompiledMethodStorage* storage) + : storage_(storage) {} + + void GetThunkCode(const LinkerPatch& patch, + /*out*/ ArrayRef* code, + /*out*/ std::string* debug_name) OVERRIDE; + + private: + CompiledMethodStorage* storage_; + }; + // Map method reference to assigned offset. // Wrap the map in a class implementing RelativePatcherTargetProvider. class MethodOffsetMap : public RelativePatcherTargetProvider { @@ -137,6 +153,7 @@ class MultiOatRelativePatcher FINAL { SafeMap map; }; + ThunkProvider thunk_provider_; MethodOffsetMap method_offset_map_; std::unique_ptr relative_patcher_; uint32_t adjustment_; diff --git a/dex2oat/linker/multi_oat_relative_patcher_test.cc b/dex2oat/linker/multi_oat_relative_patcher_test.cc index ca9c5f1e84..05fe36a590 100644 --- a/dex2oat/linker/multi_oat_relative_patcher_test.cc +++ b/dex2oat/linker/multi_oat_relative_patcher_test.cc @@ -122,7 +122,7 @@ class MultiOatRelativePatcherTest : public testing::Test { MultiOatRelativePatcherTest() : instruction_set_features_(InstructionSetFeatures::FromCppDefines()), - patcher_(kRuntimeISA, instruction_set_features_.get()) { + patcher_(kRuntimeISA, instruction_set_features_.get(), /* storage */ nullptr) { std::unique_ptr mock(new MockPatcher()); mock_ = mock.get(); patcher_.relative_patcher_ = std::move(mock); diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 6e95393e80..ea4e210b74 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -213,7 +213,8 @@ class OatTest : public CommonCompilerTest { class_linker->RegisterDexFile(*dex_file, nullptr); } MultiOatRelativePatcher patcher(compiler_driver_->GetInstructionSet(), - instruction_set_features_.get()); + instruction_set_features_.get(), + compiler_driver_->GetCompiledMethodStorage()); oat_writer.Initialize(compiler_driver_.get(), nullptr, dex_files); oat_writer.PrepareLayout(&patcher); elf_writer->PrepareDynamicSection(oat_writer.GetOatHeader().GetExecutableOffset(), -- GitLab From bce495e2a48822149c15e1b04e337babce0bd18e Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 3 Apr 2018 12:45:41 +0100 Subject: [PATCH 198/749] [veridex] Detect more reflection uses. 1) Look at all types referenced by a dex file. 2) Add field names (without type) in the hidden lists. bug: 64382372 bug: 77513322 Test: m (cherry picked from commit 76fee048fcd9cfcb76578882ff7cc1779dbf5df2) Change-Id: Icaca46c15dd478e50053f43da914adb6d0bcf0e2 --- tools/veridex/hidden_api.cc | 5 +++++ tools/veridex/hidden_api_finder.cc | 19 ++++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/tools/veridex/hidden_api.cc b/tools/veridex/hidden_api.cc index 93f921a25f..17fa1b8513 100644 --- a/tools/veridex/hidden_api.cc +++ b/tools/veridex/hidden_api.cc @@ -61,6 +61,11 @@ void HiddenApi::FillList(const char* filename, std::set& entries) { // Add the class->method name (so stripping the signature). entries.insert(str.substr(0, pos)); } + pos = str.find(':'); + if (pos != std::string::npos) { + // Add the class->field name (so stripping the type). + entries.insert(str.substr(0, pos)); + } } } } diff --git a/tools/veridex/hidden_api_finder.cc b/tools/veridex/hidden_api_finder.cc index d611f78eed..4885e02769 100644 --- a/tools/veridex/hidden_api_finder.cc +++ b/tools/veridex/hidden_api_finder.cc @@ -58,6 +58,16 @@ void HiddenApiFinder::CheckField(uint32_t field_id, void HiddenApiFinder::CollectAccesses(VeridexResolver* resolver) { const DexFile& dex_file = resolver->GetDexFile(); + // Look at all types referenced in this dex file. Any of these + // types can lead to being used through reflection. + for (uint32_t i = 0; i < dex_file.NumTypeIds(); ++i) { + std::string name(dex_file.StringByTypeIdx(dex::TypeIndex(i))); + if (hidden_api_.IsInRestrictionList(name)) { + classes_.insert(name); + } + } + // Note: we collect strings constants only referenced in code items as the string table + // contains other kind of strings (eg types). size_t class_def_count = dex_file.NumClassDefs(); for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) { const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); @@ -76,15 +86,6 @@ void HiddenApiFinder::CollectAccesses(VeridexResolver* resolver) { CodeItemDataAccessor code_item_accessor(dex_file, code_item); for (const DexInstructionPcPair& inst : code_item_accessor) { switch (inst->Opcode()) { - case Instruction::CONST_CLASS: { - dex::TypeIndex type_index(inst->VRegB_21c()); - std::string name = dex_file.StringByTypeIdx(type_index); - // Only keep classes that are in a restriction list. - if (hidden_api_.IsInRestrictionList(name)) { - classes_.insert(name); - } - break; - } case Instruction::CONST_STRING: { dex::StringIndex string_index(inst->VRegB_21c()); std::string name = std::string(dex_file.StringDataByIdx(string_index)); -- GitLab From 4b670183dcc5173c19922b3f3d87c2fbddbb7a34 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Wed, 4 Apr 2018 12:54:15 +0000 Subject: [PATCH 199/749] Revert "Write shared data section for oatdump export dex" This reverts commit fdca4cb565c25a4a05078b2afc3f7abb374309e3. Reason for revert: CHECK failure in device testing (bad checksum). Change-Id: I43bd3ada4853022728d217ff8b79c32026fc4974 --- build/Android.gtest.mk | 6 ++---- oatdump/oatdump.cc | 25 ++++--------------------- oatdump/oatdump_test.cc | 5 ----- oatdump/oatdump_test.h | 39 +++++++++++++++------------------------ 4 files changed, 21 insertions(+), 54 deletions(-) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index b483e5f6f2..b342abe17c 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -300,13 +300,11 @@ ART_GTEST_oatdump_test_HOST_DEPS := \ $(HOST_CORE_IMAGE_DEFAULT_64) \ $(HOST_CORE_IMAGE_DEFAULT_32) \ oatdumpd-host \ - oatdumpds-host \ - dexdump2-host + oatdumpds-host ART_GTEST_oatdump_test_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_DEFAULT_64) \ $(TARGET_CORE_IMAGE_DEFAULT_32) \ - oatdumpd-target \ - dexdump2-target + oatdumpd-target ART_GTEST_oatdump_image_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS) ART_GTEST_oatdump_image_test_TARGET_DEPS := $(ART_GTEST_oatdump_test_TARGET_DEPS) ART_GTEST_oatdump_app_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS) \ diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 3bff123386..433ed9aaee 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1180,17 +1180,6 @@ class OatDumper { } } - // Update header for shared section. - uint32_t shared_section_offset = 0u; - uint32_t shared_section_size = 0u; - if (dex_file->IsCompactDexFile()) { - CompactDexFile::Header* const header = - reinterpret_cast(const_cast(dex_file->Begin())); - shared_section_offset = header->data_off_; - shared_section_size = header->data_size_; - // The shared section will be serialized right after the dex file. - header->data_off_ = header->file_size_; - } // Verify output directory exists if (!OS::DirectoryExists(options_.export_dex_location_)) { // TODO: Extend OS::DirectoryExists if symlink support is required @@ -1237,22 +1226,16 @@ class OatDumper { return false; } - bool success = file->WriteFully(dex_file->Begin(), fsize); + bool success = false; + success = file->WriteFully(dex_file->Begin(), fsize); + // } + if (!success) { os << "Failed to write dex file"; file->Erase(); return false; } - if (shared_section_size != 0) { - success = file->WriteFully(dex_file->Begin() + shared_section_offset, shared_section_size); - if (!success) { - os << "Failed to write shared data section"; - file->Erase(); - return false; - } - } - if (file->FlushCloseOrErase() != 0) { os << "Flush and close failed"; return false; diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc index 18cb2fde88..00344691e0 100644 --- a/oatdump/oatdump_test.cc +++ b/oatdump/oatdump_test.cc @@ -75,11 +75,6 @@ TEST_F(OatDumpTest, TestExportDex) { std::string error_msg; ASSERT_TRUE(Exec(kDynamic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly, &error_msg)) << error_msg; - const std::string dex_location = tmp_dir_+ "/core-oj-hostdex.jar_export.dex"; - const std::string dexdump2 = GetExecutableFilePath("dexdump2", - /*is_debug*/false, - /*is_static*/false); - ASSERT_TRUE(ForkAndExecAndWait({dexdump2, "-d", dex_location}, &error_msg)) << error_msg; } TEST_F(OatDumpTest, TestExportDexStatic) { TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h index b85730d25e..fac0bb234e 100644 --- a/oatdump/oatdump_test.h +++ b/oatdump/oatdump_test.h @@ -70,24 +70,20 @@ class OatDumpTest : public CommonRuntimeTest { kStatic, // oatdump(d)s, dex2oat(d)s }; - // Returns path to the oatdump/dex2oat/dexdump binary. - std::string GetExecutableFilePath(const char* name, bool is_debug, bool is_static) { + // Returns path to the oatdump/dex2oat binary. + std::string GetExecutableFilePath(Flavor flavor, const char* name) { std::string root = GetTestAndroidRoot(); root += "/bin/"; root += name; - if (is_debug) { + if (kIsDebugBuild) { root += "d"; } - if (is_static) { + if (flavor == kStatic) { root += "s"; } return root; } - std::string GetExecutableFilePath(Flavor flavor, const char* name) { - return GetExecutableFilePath(name, kIsDebugBuild, flavor == kStatic); - } - enum Mode { kModeOat, kModeOatWithBootImage, @@ -131,7 +127,17 @@ class OatDumpTest : public CommonRuntimeTest { }; exec_argv.insert(exec_argv.end(), args.begin(), args.end()); - return ForkAndExecAndWait(exec_argv, error_msg); + pid_t pid; + int pipe_fd; + bool result = ForkAndExec(exec_argv, &pid, &pipe_fd, error_msg); + if (result) { + close(pipe_fd); + int status = 0; + if (waitpid(pid, &status, 0) != -1) { + result = (status == 0); + } + } + return result; } // Run the test with custom arguments. @@ -294,21 +300,6 @@ class OatDumpTest : public CommonRuntimeTest { } } - bool ForkAndExecAndWait(const std::vector& exec_argv, - /*out*/ std::string* error_msg) { - pid_t pid; - int pipe_fd; - bool result = ForkAndExec(exec_argv, &pid, &pipe_fd, error_msg); - if (result) { - close(pipe_fd); - int status = 0; - if (waitpid(pid, &status, 0) != -1) { - result = (status == 0); - } - } - return result; - } - std::string tmp_dir_; private: -- GitLab From 5806a9ec99b5494b511e84c74f494f0b3a8ebec5 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 4 Apr 2018 17:23:28 +0000 Subject: [PATCH 200/749] Revert "Compile link-time thunks in codegen." Reason for revert: This caused clang linker crash in several branches. Bug: 77581732 This reverts commit c9dd2207dfdab42586b1d6a5e7f11cf2fcea3a7a. Change-Id: I1923809083cf41c4f19e3e60df03ae80517aaedb --- compiler/cfi_test.h | 21 +- compiler/driver/compiled_method_storage.cc | 95 +---- compiler/driver/compiled_method_storage.h | 26 -- compiler/jni/jni_cfi_test.cc | 6 +- .../linker/arm/relative_patcher_arm_base.cc | 44 +-- .../linker/arm/relative_patcher_arm_base.h | 9 +- .../linker/arm/relative_patcher_thumb2.cc | 305 ++++++++++++++- compiler/linker/arm/relative_patcher_thumb2.h | 78 +++- .../arm/relative_patcher_thumb2_test.cc | 96 ++--- .../linker/arm64/relative_patcher_arm64.cc | 253 +++++++++++- .../linker/arm64/relative_patcher_arm64.h | 54 ++- .../arm64/relative_patcher_arm64_test.cc | 87 ++-- compiler/linker/linker_patch.h | 2 +- compiler/linker/relative_patcher.cc | 13 +- compiler/linker/relative_patcher.h | 27 +- compiler/linker/relative_patcher_test.h | 72 +--- compiler/optimizing/code_generator.cc | 12 - compiler/optimizing/code_generator.h | 6 - compiler/optimizing/code_generator_arm64.cc | 354 +++-------------- compiler/optimizing/code_generator_arm64.h | 89 +---- .../optimizing/code_generator_arm_vixl.cc | 370 ++---------------- compiler/optimizing/code_generator_arm_vixl.h | 110 +----- compiler/optimizing/codegen_test_utils.h | 8 +- compiler/optimizing/optimizing_cfi_test.cc | 10 +- compiler/optimizing/optimizing_compiler.cc | 26 +- dex2oat/dex2oat.cc | 4 +- dex2oat/linker/image_test.h | 3 +- dex2oat/linker/multi_oat_relative_patcher.cc | 20 +- dex2oat/linker/multi_oat_relative_patcher.h | 19 +- .../linker/multi_oat_relative_patcher_test.cc | 2 +- dex2oat/linker/oat_writer_test.cc | 3 +- 31 files changed, 888 insertions(+), 1336 deletions(-) diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h index 581edaa773..29ff235cea 100644 --- a/compiler/cfi_test.h +++ b/compiler/cfi_test.h @@ -37,8 +37,8 @@ constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_DEBUG_FRAME_FORMAT; class CFITest : public dwarf::DwarfTest { public: void GenerateExpected(FILE* f, InstructionSet isa, const char* isa_str, - ArrayRef actual_asm, - ArrayRef actual_cfi) { + const std::vector& actual_asm, + const std::vector& actual_cfi) { std::vector lines; // Print the raw bytes. fprintf(f, "static constexpr uint8_t expected_asm_%s[] = {", isa_str); @@ -50,18 +50,11 @@ class CFITest : public dwarf::DwarfTest { // Pretty-print CFI opcodes. constexpr bool is64bit = false; dwarf::DebugFrameOpCodeWriter<> initial_opcodes; - dwarf::WriteCIE(is64bit, dwarf::Reg(8), initial_opcodes, kCFIFormat, &debug_frame_data_); + dwarf::WriteCIE(is64bit, dwarf::Reg(8), + initial_opcodes, kCFIFormat, &debug_frame_data_); std::vector debug_frame_patches; - dwarf::WriteFDE(is64bit, - /* section_address */ 0, - /* cie_address */ 0, - /* code_address */ 0, - actual_asm.size(), - actual_cfi, - kCFIFormat, - /* buffer_address */ 0, - &debug_frame_data_, - &debug_frame_patches); + dwarf::WriteFDE(is64bit, 0, 0, 0, actual_asm.size(), ArrayRef(actual_cfi), + kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches); ReformatCfi(Objdump(false, "-W"), &lines); // Pretty-print assembly. const uint8_t* asm_base = actual_asm.data(); @@ -149,7 +142,7 @@ class CFITest : public dwarf::DwarfTest { } // Pretty-print byte array. 12 bytes per line. - static void HexDump(FILE* f, ArrayRef data) { + static void HexDump(FILE* f, const std::vector& data) { for (size_t i = 0; i < data.size(); i++) { fprintf(f, i % 12 == 0 ? "\n " : " "); // Whitespace. fprintf(f, "0x%02X,", data[i]); diff --git a/compiler/driver/compiled_method_storage.cc b/compiler/driver/compiled_method_storage.cc index aa8277edb4..a26a985ff9 100644 --- a/compiler/driver/compiled_method_storage.cc +++ b/compiler/driver/compiled_method_storage.cc @@ -161,46 +161,6 @@ class CompiledMethodStorage::LengthPrefixedArrayAlloc { SwapSpace* const swap_space_; }; -class CompiledMethodStorage::ThunkMapKey { - public: - ThunkMapKey(linker::LinkerPatch::Type type, uint32_t custom_value1, uint32_t custom_value2) - : type_(type), custom_value1_(custom_value1), custom_value2_(custom_value2) {} - - bool operator<(const ThunkMapKey& other) const { - if (custom_value1_ != other.custom_value1_) { - return custom_value1_ < other.custom_value1_; - } - if (custom_value2_ != other.custom_value2_) { - return custom_value2_ < other.custom_value2_; - } - return type_ < other.type_; - } - - private: - linker::LinkerPatch::Type type_; - uint32_t custom_value1_; - uint32_t custom_value2_; -}; - -class CompiledMethodStorage::ThunkMapValue { - public: - ThunkMapValue(std::vector>&& code, - const std::string& debug_name) - : code_(std::move(code)), debug_name_(debug_name) {} - - ArrayRef GetCode() const { - return ArrayRef(code_); - } - - const std::string& GetDebugName() const { - return debug_name_; - } - - private: - std::vector> code_; - std::string debug_name_; -}; - CompiledMethodStorage::CompiledMethodStorage(int swap_fd) : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)), dedupe_enabled_(true), @@ -211,9 +171,7 @@ CompiledMethodStorage::CompiledMethodStorage(int swap_fd) LengthPrefixedArrayAlloc(swap_space_.get())), dedupe_cfi_info_("dedupe cfi info", LengthPrefixedArrayAlloc(swap_space_.get())), dedupe_linker_patches_("dedupe cfi info", - LengthPrefixedArrayAlloc(swap_space_.get())), - thunk_map_lock_("thunk_map_lock"), - thunk_map_(std::less(), SwapAllocator(swap_space_.get())) { + LengthPrefixedArrayAlloc(swap_space_.get())) { } CompiledMethodStorage::~CompiledMethodStorage() { @@ -279,55 +237,4 @@ void CompiledMethodStorage::ReleaseLinkerPatches( ReleaseArrayIfNotDeduplicated(linker_patches); } -CompiledMethodStorage::ThunkMapKey CompiledMethodStorage::GetThunkMapKey( - const linker::LinkerPatch& linker_patch) { - uint32_t custom_value1 = 0u; - uint32_t custom_value2 = 0u; - switch (linker_patch.GetType()) { - case linker::LinkerPatch::Type::kBakerReadBarrierBranch: - custom_value1 = linker_patch.GetBakerCustomValue1(); - custom_value2 = linker_patch.GetBakerCustomValue2(); - break; - case linker::LinkerPatch::Type::kCallRelative: - // No custom values. - break; - default: - LOG(FATAL) << "Unexpected patch type: " << linker_patch.GetType(); - UNREACHABLE(); - } - return ThunkMapKey(linker_patch.GetType(), custom_value1, custom_value2); -} - -ArrayRef CompiledMethodStorage::GetThunkCode(const linker::LinkerPatch& linker_patch, - /*out*/ std::string* debug_name) { - ThunkMapKey key = GetThunkMapKey(linker_patch); - MutexLock lock(Thread::Current(), thunk_map_lock_); - auto it = thunk_map_.find(key); - if (it != thunk_map_.end()) { - const ThunkMapValue& value = it->second; - if (debug_name != nullptr) { - *debug_name = value.GetDebugName(); - } - return value.GetCode(); - } else { - if (debug_name != nullptr) { - *debug_name = std::string(); - } - return ArrayRef(); - } -} - -void CompiledMethodStorage::SetThunkCode(const linker::LinkerPatch& linker_patch, - ArrayRef code, - const std::string& debug_name) { - DCHECK(!code.empty()); - ThunkMapKey key = GetThunkMapKey(linker_patch); - std::vector> code_copy( - code.begin(), code.end(), SwapAllocator(swap_space_.get())); - ThunkMapValue value(std::move(code_copy), debug_name); - MutexLock lock(Thread::Current(), thunk_map_lock_); - // Note: Multiple threads can try and compile the same thunk, so this may not create a new entry. - thunk_map_.emplace(key, std::move(value)); -} - } // namespace art diff --git a/compiler/driver/compiled_method_storage.h b/compiler/driver/compiled_method_storage.h index 1634facb7c..249f06c20f 100644 --- a/compiler/driver/compiled_method_storage.h +++ b/compiler/driver/compiled_method_storage.h @@ -18,7 +18,6 @@ #define ART_COMPILER_DRIVER_COMPILED_METHOD_STORAGE_H_ #include -#include #include #include "base/array_ref.h" @@ -68,29 +67,7 @@ class CompiledMethodStorage { const ArrayRef& linker_patches); void ReleaseLinkerPatches(const LengthPrefixedArray* linker_patches); - // Returns the code associated with the given patch. - // If the code has not been set, returns empty data. - // If `debug_name` is not null, stores the associated debug name in `*debug_name`. - ArrayRef GetThunkCode(const linker::LinkerPatch& linker_patch, - /*out*/ std::string* debug_name = nullptr); - - // Sets the code and debug name associated with the given patch. - void SetThunkCode(const linker::LinkerPatch& linker_patch, - ArrayRef code, - const std::string& debug_name); - private: - class ThunkMapKey; - class ThunkMapValue; - using ThunkMapValueType = std::pair; - using ThunkMap = std::map, - SwapAllocator>; - static_assert(std::is_same::value, "Value type check."); - - static ThunkMapKey GetThunkMapKey(const linker::LinkerPatch& linker_patch); - template const LengthPrefixedArray* AllocateOrDeduplicateArray(const ArrayRef& data, DedupeSetType* dedupe_set); @@ -125,9 +102,6 @@ class CompiledMethodStorage { ArrayDedupeSet dedupe_cfi_info_; ArrayDedupeSet dedupe_linker_patches_; - Mutex thunk_map_lock_; - ThunkMap thunk_map_ GUARDED_BY(thunk_map_lock_); - DISALLOW_COPY_AND_ASSIGN(CompiledMethodStorage); }; diff --git a/compiler/jni/jni_cfi_test.cc b/compiler/jni/jni_cfi_test.cc index 38f95488a9..236b5c0c2e 100644 --- a/compiler/jni/jni_cfi_test.cc +++ b/compiler/jni/jni_cfi_test.cc @@ -94,11 +94,7 @@ class JNICFITest : public CFITest { const std::vector& actual_cfi = *(jni_asm->cfi().data()); if (kGenerateExpected) { - GenerateExpected(stdout, - isa, - isa_str, - ArrayRef(actual_asm), - ArrayRef(actual_cfi)); + GenerateExpected(stdout, isa, isa_str, actual_asm, actual_cfi); } else { EXPECT_EQ(expected_asm, actual_asm); EXPECT_EQ(expected_cfi, actual_cfi); diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc index 7cb8ae55c5..6e0286afac 100644 --- a/compiler/linker/arm/relative_patcher_arm_base.cc +++ b/compiler/linker/arm/relative_patcher_arm_base.cc @@ -30,9 +30,8 @@ namespace linker { class ArmBaseRelativePatcher::ThunkData { public: - ThunkData(ArrayRef code, const std::string& debug_name, uint32_t max_next_offset) - : code_(code), - debug_name_(debug_name), + ThunkData(std::vector code, uint32_t max_next_offset) + : code_(std::move(code)), offsets_(), max_next_offset_(max_next_offset), pending_offset_(0u) { @@ -46,11 +45,7 @@ class ArmBaseRelativePatcher::ThunkData { } ArrayRef GetCode() const { - return code_; - } - - const std::string& GetDebugName() const { - return debug_name_; + return ArrayRef(code_); } bool NeedsNextThunk() const { @@ -147,11 +142,10 @@ class ArmBaseRelativePatcher::ThunkData { } private: - const ArrayRef code_; // The code of the thunk. - const std::string debug_name_; // The debug name of the thunk. - std::vector offsets_; // Offsets at which the thunk needs to be written. - uint32_t max_next_offset_; // The maximum offset at which the next thunk can be placed. - uint32_t pending_offset_; // The index of the next offset to write. + std::vector code_; // The code of the thunk. + std::vector offsets_; // Offsets at which the thunk needs to be written. + uint32_t max_next_offset_; // The maximum offset at which the next thunk can be placed. + uint32_t pending_offset_; // The index of the next offset to write. }; class ArmBaseRelativePatcher::PendingThunkComparator { @@ -245,13 +239,14 @@ std::vector ArmBaseRelativePatcher::GenerateThunkDebugIn std::vector result; result.reserve(number_of_thunks); for (auto&& entry : thunks_) { + const ThunkKey& key = entry.first; const ThunkData& data = entry.second; size_t start = data.IndexOfFirstThunkAtOrAfter(executable_offset); if (start == data.NumberOfThunks()) { continue; } // Get the base name to use for the first occurrence of the thunk. - std::string base_name = data.GetDebugName(); + std::string base_name = GetThunkDebugName(key); for (size_t i = start, num = data.NumberOfThunks(); i != num; ++i) { debug::MethodDebugInfo info = {}; if (i == 0u) { @@ -272,11 +267,9 @@ std::vector ArmBaseRelativePatcher::GenerateThunkDebugIn return result; } -ArmBaseRelativePatcher::ArmBaseRelativePatcher(RelativePatcherThunkProvider* thunk_provider, - RelativePatcherTargetProvider* target_provider, +ArmBaseRelativePatcher::ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider, InstructionSet instruction_set) - : thunk_provider_(thunk_provider), - target_provider_(target_provider), + : provider_(provider), instruction_set_(instruction_set), thunks_(), unprocessed_method_call_patches_(), @@ -405,7 +398,7 @@ void ArmBaseRelativePatcher::ProcessPatches(const CompiledMethod* compiled_metho unprocessed_method_call_patches_.emplace_back(patch_offset, patch.TargetMethod()); if (method_call_thunk_ == nullptr) { uint32_t max_next_offset = CalculateMaxNextOffset(patch_offset, key); - auto it = thunks_.Put(key, ThunkDataForPatch(patch, max_next_offset)); + auto it = thunks_.Put(key, ThunkData(CompileThunk(key), max_next_offset)); method_call_thunk_ = &it->second; AddUnreservedThunk(method_call_thunk_); } else { @@ -416,7 +409,7 @@ void ArmBaseRelativePatcher::ProcessPatches(const CompiledMethod* compiled_metho auto lb = thunks_.lower_bound(key); if (lb == thunks_.end() || thunks_.key_comp()(key, lb->first)) { uint32_t max_next_offset = CalculateMaxNextOffset(patch_offset, key); - auto it = thunks_.PutBefore(lb, key, ThunkDataForPatch(patch, max_next_offset)); + auto it = thunks_.PutBefore(lb, key, ThunkData(CompileThunk(key), max_next_offset)); AddUnreservedThunk(&it->second); } else { old_data = &lb->second; @@ -484,7 +477,7 @@ void ArmBaseRelativePatcher::ResolveMethodCalls(uint32_t quick_code_offset, break; } } else { - auto result = target_provider_->FindMethodOffset(target_method); + auto result = provider_->FindMethodOffset(target_method); if (!result.first) { break; } @@ -525,14 +518,5 @@ inline uint32_t ArmBaseRelativePatcher::CalculateMaxNextOffset(uint32_t patch_of GetInstructionSetAlignment(instruction_set_)); } -inline ArmBaseRelativePatcher::ThunkData ArmBaseRelativePatcher::ThunkDataForPatch( - const LinkerPatch& patch, uint32_t max_next_offset) { - ArrayRef code; - std::string debug_name; - thunk_provider_->GetThunkCode(patch, &code, &debug_name); - DCHECK(!code.empty()); - return ThunkData(code, debug_name, max_next_offset); -} - } // namespace linker } // namespace art diff --git a/compiler/linker/arm/relative_patcher_arm_base.h b/compiler/linker/arm/relative_patcher_arm_base.h index 963d6690b0..ee09bf96b3 100644 --- a/compiler/linker/arm/relative_patcher_arm_base.h +++ b/compiler/linker/arm/relative_patcher_arm_base.h @@ -37,8 +37,7 @@ class ArmBaseRelativePatcher : public RelativePatcher { std::vector GenerateThunkDebugInfo(uint32_t executable_offset) OVERRIDE; protected: - ArmBaseRelativePatcher(RelativePatcherThunkProvider* thunk_provider, - RelativePatcherTargetProvider* target_provider, + ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider, InstructionSet instruction_set); ~ArmBaseRelativePatcher(); @@ -95,6 +94,8 @@ class ArmBaseRelativePatcher : public RelativePatcher { uint32_t CalculateMethodCallDisplacement(uint32_t patch_offset, uint32_t target_offset); + virtual std::vector CompileThunk(const ThunkKey& key) = 0; + virtual std::string GetThunkDebugName(const ThunkKey& key) = 0; virtual uint32_t MaxPositiveDisplacement(const ThunkKey& key) = 0; virtual uint32_t MaxNegativeDisplacement(const ThunkKey& key) = 0; @@ -107,10 +108,8 @@ class ArmBaseRelativePatcher : public RelativePatcher { void ResolveMethodCalls(uint32_t quick_code_offset, MethodReference method_ref); uint32_t CalculateMaxNextOffset(uint32_t patch_offset, const ThunkKey& key); - ThunkData ThunkDataForPatch(const LinkerPatch& patch, uint32_t max_next_offset); - RelativePatcherThunkProvider* const thunk_provider_; - RelativePatcherTargetProvider* const target_provider_; + RelativePatcherTargetProvider* const provider_; const InstructionSet instruction_set_; // The data for all thunks. diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc index 7400d11c31..78755176e4 100644 --- a/compiler/linker/arm/relative_patcher_thumb2.cc +++ b/compiler/linker/arm/relative_patcher_thumb2.cc @@ -48,9 +48,8 @@ constexpr uint32_t kMaxMethodCallNegativeDisplacement = (1u << 24) - kPcDisplace constexpr uint32_t kMaxBcondPositiveDisplacement = (1u << 20) - 2u + kPcDisplacement; constexpr uint32_t kMaxBcondNegativeDisplacement = (1u << 20) - kPcDisplacement; -Thumb2RelativePatcher::Thumb2RelativePatcher(RelativePatcherThunkProvider* thunk_provider, - RelativePatcherTargetProvider* target_provider) - : ArmBaseRelativePatcher(thunk_provider, target_provider, InstructionSet::kThumb2) { +Thumb2RelativePatcher::Thumb2RelativePatcher(RelativePatcherTargetProvider* provider) + : ArmBaseRelativePatcher(provider, InstructionSet::kThumb2) { } void Thumb2RelativePatcher::PatchCall(std::vector* code, @@ -111,6 +110,62 @@ void Thumb2RelativePatcher::PatchBakerReadBarrierBranch(std::vector* co uint32_t insn = GetInsn32(code, literal_offset); DCHECK_EQ(insn, 0xf0408000); // BNE +0 (unpatched) ThunkKey key = GetBakerThunkKey(patch); + if (kIsDebugBuild) { + const uint32_t encoded_data = key.GetCustomValue1(); + BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); + // Check that the next instruction matches the expected LDR. + switch (kind) { + case BakerReadBarrierKind::kField: { + BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); + if (width == BakerReadBarrierWidth::kWide) { + DCHECK_GE(code->size() - literal_offset, 8u); + uint32_t next_insn = GetInsn32(code, literal_offset + 4u); + // LDR (immediate), encoding T3, with correct base_reg. + CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xffff0000u, 0xf8d00000u | (base_reg << 16)); + } else { + DCHECK_GE(code->size() - literal_offset, 6u); + uint32_t next_insn = GetInsn16(code, literal_offset + 4u); + // LDR (immediate), encoding T1, with correct base_reg. + CheckValidReg(next_insn & 0x7u); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xf838u, 0x6800u | (base_reg << 3)); + } + break; + } + case BakerReadBarrierKind::kArray: { + DCHECK_GE(code->size() - literal_offset, 8u); + uint32_t next_insn = GetInsn32(code, literal_offset + 4u); + // LDR (register) with correct base_reg, S=1 and option=011 (LDR Wt, [Xn, Xm, LSL #2]). + CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xffff0ff0u, 0xf8500020u | (base_reg << 16)); + CheckValidReg(next_insn & 0xf); // Check index register + break; + } + case BakerReadBarrierKind::kGcRoot: { + BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); + if (width == BakerReadBarrierWidth::kWide) { + DCHECK_GE(literal_offset, 4u); + uint32_t prev_insn = GetInsn32(code, literal_offset - 4u); + // LDR (immediate), encoding T3, with correct root_reg. + const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(prev_insn & 0xfff0f000u, 0xf8d00000u | (root_reg << 12)); + } else { + DCHECK_GE(literal_offset, 2u); + uint32_t prev_insn = GetInsn16(code, literal_offset - 2u); + // LDR (immediate), encoding T1, with correct root_reg. + const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(prev_insn & 0xf807u, 0x6800u | root_reg); + } + break; + } + default: + LOG(FATAL) << "Unexpected type: " << static_cast(key.GetType()); + UNREACHABLE(); + } + } uint32_t target_offset = GetThunkTargetOffset(key, patch_offset); DCHECK_ALIGNED(target_offset, 4u); uint32_t disp = target_offset - (patch_offset + kPcDisplacement); @@ -123,6 +178,250 @@ void Thumb2RelativePatcher::PatchBakerReadBarrierBranch(std::vector* co SetInsn32(code, literal_offset, insn); } +#define __ assembler.GetVIXLAssembler()-> + +static void EmitGrayCheckAndFastPath(arm::ArmVIXLAssembler& assembler, + vixl::aarch32::Register base_reg, + vixl::aarch32::MemOperand& lock_word, + vixl::aarch32::Label* slow_path, + int32_t raw_ldr_offset) { + using namespace vixl::aarch32; // NOLINT(build/namespaces) + // Load the lock word containing the rb_state. + __ Ldr(ip, lock_word); + // Given the numeric representation, it's enough to check the low bit of the rb_state. + static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0"); + static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1"); + __ Tst(ip, Operand(LockWord::kReadBarrierStateMaskShifted)); + __ B(ne, slow_path, /* is_far_target */ false); + __ Add(lr, lr, raw_ldr_offset); + // Introduce a dependency on the lock_word including rb_state, + // to prevent load-load reordering, and without using + // a memory barrier (which would be more expensive). + __ Add(base_reg, base_reg, Operand(ip, LSR, 32)); + __ Bx(lr); // And return back to the function. + // Note: The fake dependency is unnecessary for the slow path. +} + +// Load the read barrier introspection entrypoint in register `entrypoint` +static void LoadReadBarrierMarkIntrospectionEntrypoint(arm::ArmVIXLAssembler& assembler, + vixl::aarch32::Register entrypoint) { + using vixl::aarch32::MemOperand; + using vixl::aarch32::ip; + // Thread Register. + const vixl::aarch32::Register tr = vixl::aarch32::r9; + + // The register where the read barrier introspection entrypoint is loaded + // is fixed: `Thumb2RelativePatcher::kBakerCcEntrypointRegister` (R4). + DCHECK_EQ(entrypoint.GetCode(), Thumb2RelativePatcher::kBakerCcEntrypointRegister); + // entrypoint = Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection. + DCHECK_EQ(ip.GetCode(), 12u); + const int32_t entry_point_offset = + Thread::ReadBarrierMarkEntryPointsOffset(ip.GetCode()); + __ Ldr(entrypoint, MemOperand(tr, entry_point_offset)); +} + +void Thumb2RelativePatcher::CompileBakerReadBarrierThunk(arm::ArmVIXLAssembler& assembler, + uint32_t encoded_data) { + using namespace vixl::aarch32; // NOLINT(build/namespaces) + BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); + switch (kind) { + case BakerReadBarrierKind::kField: { + // Check if the holder is gray and, if not, add fake dependency to the base register + // and return to the LDR instruction to load the reference. Otherwise, use introspection + // to load the reference and call the entrypoint (in kBakerCcEntrypointRegister) + // that performs further checks on the reference and marks it if needed. + Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(base_reg.GetCode()); + Register holder_reg(BakerReadBarrierSecondRegField::Decode(encoded_data)); + CheckValidReg(holder_reg.GetCode()); + BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip); + // If base_reg differs from holder_reg, the offset was too large and we must have + // emitted an explicit null check before the load. Otherwise, we need to null-check + // the holder as we do not necessarily do that check before going to the thunk. + vixl::aarch32::Label throw_npe; + if (holder_reg.Is(base_reg)) { + __ CompareAndBranchIfZero(holder_reg, &throw_npe, /* is_far_target */ false); + } + vixl::aarch32::Label slow_path; + MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value()); + const int32_t raw_ldr_offset = (width == BakerReadBarrierWidth::kWide) + ? BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET + : BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET; + EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset); + __ Bind(&slow_path); + const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 + + raw_ldr_offset; + Register ep_reg(kBakerCcEntrypointRegister); + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); + if (width == BakerReadBarrierWidth::kWide) { + MemOperand ldr_half_address(lr, ldr_offset + 2); + __ Ldrh(ip, ldr_half_address); // Load the LDR immediate half-word with "Rt | imm12". + __ Ubfx(ip, ip, 0, 12); // Extract the offset imm12. + __ Ldr(ip, MemOperand(base_reg, ip)); // Load the reference. + } else { + MemOperand ldr_address(lr, ldr_offset); + __ Ldrh(ip, ldr_address); // Load the LDR immediate, encoding T1. + __ Add(ep_reg, // Adjust the entrypoint address to the entrypoint + ep_reg, // for narrow LDR. + Operand(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET)); + __ Ubfx(ip, ip, 6, 5); // Extract the imm5, i.e. offset / 4. + __ Ldr(ip, MemOperand(base_reg, ip, LSL, 2)); // Load the reference. + } + // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference. + __ Bx(ep_reg); // Jump to the entrypoint. + if (holder_reg.Is(base_reg)) { + // Add null check slow path. The stack map is at the address pointed to by LR. + __ Bind(&throw_npe); + int32_t offset = GetThreadOffset(kQuickThrowNullPointer).Int32Value(); + __ Ldr(ip, MemOperand(/* Thread* */ vixl::aarch32::r9, offset)); + __ Bx(ip); + } + break; + } + case BakerReadBarrierKind::kArray: { + Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(base_reg.GetCode()); + DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); + DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip); + vixl::aarch32::Label slow_path; + int32_t data_offset = + mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value(); + MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset); + DCHECK_LT(lock_word.GetOffsetImmediate(), 0); + const int32_t raw_ldr_offset = BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET; + EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset); + __ Bind(&slow_path); + const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 + + raw_ldr_offset; + MemOperand ldr_address(lr, ldr_offset + 2); + __ Ldrb(ip, ldr_address); // Load the LDR (register) byte with "00 | imm2 | Rm", + // i.e. Rm+32 because the scale in imm2 is 2. + Register ep_reg(kBakerCcEntrypointRegister); + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); + __ Bfi(ep_reg, ip, 3, 6); // Insert ip to the entrypoint address to create + // a switch case target based on the index register. + __ Mov(ip, base_reg); // Move the base register to ip0. + __ Bx(ep_reg); // Jump to the entrypoint's array switch case. + break; + } + case BakerReadBarrierKind::kGcRoot: { + // Check if the reference needs to be marked and if so (i.e. not null, not marked yet + // and it does not have a forwarding address), call the correct introspection entrypoint; + // otherwise return the reference (or the extracted forwarding address). + // There is no gray bit check for GC roots. + Register root_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(root_reg.GetCode()); + DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); + BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip); + vixl::aarch32::Label return_label, not_marked, forwarding_address; + __ CompareAndBranchIfZero(root_reg, &return_label, /* is_far_target */ false); + MemOperand lock_word(root_reg, mirror::Object::MonitorOffset().Int32Value()); + __ Ldr(ip, lock_word); + __ Tst(ip, LockWord::kMarkBitStateMaskShifted); + __ B(eq, ¬_marked); + __ Bind(&return_label); + __ Bx(lr); + __ Bind(¬_marked); + static_assert(LockWord::kStateShift == 30 && LockWord::kStateForwardingAddress == 3, + "To use 'CMP ip, #modified-immediate; BHS', we need the lock word state in " + " the highest bits and the 'forwarding address' state to have all bits set"); + __ Cmp(ip, Operand(0xc0000000)); + __ B(hs, &forwarding_address); + Register ep_reg(kBakerCcEntrypointRegister); + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); + // Adjust the art_quick_read_barrier_mark_introspection address in kBakerCcEntrypointRegister + // to art_quick_read_barrier_mark_introspection_gc_roots. + int32_t entrypoint_offset = (width == BakerReadBarrierWidth::kWide) + ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET + : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET; + __ Add(ep_reg, ep_reg, Operand(entrypoint_offset)); + __ Mov(ip, root_reg); + __ Bx(ep_reg); + __ Bind(&forwarding_address); + __ Lsl(root_reg, ip, LockWord::kForwardingAddressShift); + __ Bx(lr); + break; + } + default: + LOG(FATAL) << "Unexpected kind: " << static_cast(kind); + UNREACHABLE(); + } +} + +std::vector Thumb2RelativePatcher::CompileThunk(const ThunkKey& key) { + ArenaPool pool; + ArenaAllocator allocator(&pool); + arm::ArmVIXLAssembler assembler(&allocator); + + switch (key.GetType()) { + case ThunkType::kMethodCall: + // The thunk just uses the entry point in the ArtMethod. This works even for calls + // to the generic JNI and interpreter trampolines. + assembler.LoadFromOffset( + arm::kLoadWord, + vixl::aarch32::pc, + vixl::aarch32::r0, + ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value()); + __ Bkpt(0); + break; + case ThunkType::kBakerReadBarrier: + CompileBakerReadBarrierThunk(assembler, key.GetCustomValue1()); + break; + } + + assembler.FinalizeCode(); + std::vector thunk_code(assembler.CodeSize()); + MemoryRegion code(thunk_code.data(), thunk_code.size()); + assembler.FinalizeInstructions(code); + return thunk_code; +} + +std::string Thumb2RelativePatcher::GetThunkDebugName(const ThunkKey& key) { + switch (key.GetType()) { + case ThunkType::kMethodCall: + return "MethodCallThunk"; + + case ThunkType::kBakerReadBarrier: { + uint32_t encoded_data = key.GetCustomValue1(); + BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); + std::ostringstream oss; + oss << "BakerReadBarrierThunk"; + switch (kind) { + case BakerReadBarrierKind::kField: + oss << "Field"; + if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) { + oss << "Wide"; + } + oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data) + << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data); + break; + case BakerReadBarrierKind::kArray: + oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); + DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); + DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide); + break; + case BakerReadBarrierKind::kGcRoot: + oss << "GcRoot"; + if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) { + oss << "Wide"; + } + oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); + DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); + break; + } + return oss.str(); + } + } +} + +#undef __ + uint32_t Thumb2RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) { switch (key.GetType()) { case ThunkType::kMethodCall: diff --git a/compiler/linker/arm/relative_patcher_thumb2.h b/compiler/linker/arm/relative_patcher_thumb2.h index 68610d69e1..68386c00f4 100644 --- a/compiler/linker/arm/relative_patcher_thumb2.h +++ b/compiler/linker/arm/relative_patcher_thumb2.h @@ -19,6 +19,8 @@ #include "arch/arm/registers_arm.h" #include "base/array_ref.h" +#include "base/bit_field.h" +#include "base/bit_utils.h" #include "linker/arm/relative_patcher_arm_base.h" namespace art { @@ -31,8 +33,42 @@ namespace linker { class Thumb2RelativePatcher FINAL : public ArmBaseRelativePatcher { public: - explicit Thumb2RelativePatcher(RelativePatcherThunkProvider* thunk_provider, - RelativePatcherTargetProvider* target_provider); + static constexpr uint32_t kBakerCcEntrypointRegister = 4u; + + static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, + uint32_t holder_reg, + bool narrow) { + CheckValidReg(base_reg); + CheckValidReg(holder_reg); + DCHECK(!narrow || base_reg < 8u) << base_reg; + BakerReadBarrierWidth width = + narrow ? BakerReadBarrierWidth::kNarrow : BakerReadBarrierWidth::kWide; + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kField) | + BakerReadBarrierFirstRegField::Encode(base_reg) | + BakerReadBarrierSecondRegField::Encode(holder_reg) | + BakerReadBarrierWidthField::Encode(width); + } + + static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { + CheckValidReg(base_reg); + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kArray) | + BakerReadBarrierFirstRegField::Encode(base_reg) | + BakerReadBarrierSecondRegField::Encode(kInvalidEncodedReg) | + BakerReadBarrierWidthField::Encode(BakerReadBarrierWidth::kWide); + } + + static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg, bool narrow) { + CheckValidReg(root_reg); + DCHECK(!narrow || root_reg < 8u) << root_reg; + BakerReadBarrierWidth width = + narrow ? BakerReadBarrierWidth::kNarrow : BakerReadBarrierWidth::kWide; + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kGcRoot) | + BakerReadBarrierFirstRegField::Encode(root_reg) | + BakerReadBarrierSecondRegField::Encode(kInvalidEncodedReg) | + BakerReadBarrierWidthField::Encode(width); + } + + explicit Thumb2RelativePatcher(RelativePatcherTargetProvider* provider); void PatchCall(std::vector* code, uint32_t literal_offset, @@ -47,10 +83,48 @@ class Thumb2RelativePatcher FINAL : public ArmBaseRelativePatcher { uint32_t patch_offset) OVERRIDE; protected: + std::vector CompileThunk(const ThunkKey& key) OVERRIDE; + std::string GetThunkDebugName(const ThunkKey& key) OVERRIDE; uint32_t MaxPositiveDisplacement(const ThunkKey& key) OVERRIDE; uint32_t MaxNegativeDisplacement(const ThunkKey& key) OVERRIDE; private: + static constexpr uint32_t kInvalidEncodedReg = /* pc is invalid */ 15u; + + enum class BakerReadBarrierKind : uint8_t { + kField, // Field get or array get with constant offset (i.e. constant index). + kArray, // Array get with index in register. + kGcRoot, // GC root load. + kLast = kGcRoot + }; + + enum class BakerReadBarrierWidth : uint8_t { + kWide, // 32-bit LDR (and 32-bit NEG if heap poisoning is enabled). + kNarrow, // 16-bit LDR (and 16-bit NEG if heap poisoning is enabled). + kLast = kNarrow + }; + + static constexpr size_t kBitsForBakerReadBarrierKind = + MinimumBitsToStore(static_cast(BakerReadBarrierKind::kLast)); + static constexpr size_t kBitsForRegister = 4u; + using BakerReadBarrierKindField = + BitField; + using BakerReadBarrierFirstRegField = + BitField; + using BakerReadBarrierSecondRegField = + BitField; + static constexpr size_t kBitsForBakerReadBarrierWidth = + MinimumBitsToStore(static_cast(BakerReadBarrierWidth::kLast)); + using BakerReadBarrierWidthField = BitField; + + static void CheckValidReg(uint32_t reg) { + DCHECK(reg < 12u && reg != kBakerCcEntrypointRegister) << reg; + } + + void CompileBakerReadBarrierThunk(arm::ArmVIXLAssembler& assembler, uint32_t encoded_data); + void SetInsn32(std::vector* code, uint32_t offset, uint32_t value); static uint32_t GetInsn32(ArrayRef code, uint32_t offset); diff --git a/compiler/linker/arm/relative_patcher_thumb2_test.cc b/compiler/linker/arm/relative_patcher_thumb2_test.cc index e7b11bd16b..2c22a352c2 100644 --- a/compiler/linker/arm/relative_patcher_thumb2_test.cc +++ b/compiler/linker/arm/relative_patcher_thumb2_test.cc @@ -16,15 +16,12 @@ #include "linker/arm/relative_patcher_thumb2.h" -#include "arch/arm/instruction_set_features_arm.h" #include "base/casts.h" #include "linker/relative_patcher_test.h" #include "lock_word.h" #include "mirror/array-inl.h" #include "mirror/object.h" #include "oat_quick_method_header.h" -#include "optimizing/code_generator_arm_vixl.h" -#include "optimizing/optimizing_unit_test.h" namespace art { namespace linker { @@ -192,42 +189,9 @@ class Thumb2RelativePatcherTest : public RelativePatcherTest { return result.second - 1 /* thumb mode */; } - std::vector CompileThunk(const LinkerPatch& patch, - /*out*/ std::string* debug_name = nullptr) { - OptimizingUnitTestHelper helper; - HGraph* graph = helper.CreateGraph(); - std::string error_msg; - ArmFeaturesUniquePtr features = - ArmInstructionSetFeatures::FromVariant("default", &error_msg); - CompilerOptions options; - arm::CodeGeneratorARMVIXL codegen(graph, *features, options); - ArenaVector code(helper.GetAllocator()->Adapter()); - codegen.EmitThunkCode(patch, &code, debug_name); - return std::vector(code.begin(), code.end()); - } - - void AddCompiledMethod( - MethodReference method_ref, - const ArrayRef& code, - const ArrayRef& patches = ArrayRef()) { - RelativePatcherTest::AddCompiledMethod(method_ref, code, patches); - - // Make sure the ThunkProvider has all the necessary thunks. - for (const LinkerPatch& patch : patches) { - if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch || - patch.GetType() == LinkerPatch::Type::kCallRelative) { - std::string debug_name; - std::vector thunk_code = CompileThunk(patch, &debug_name); - thunk_provider_.SetThunkCode(patch, ArrayRef(thunk_code), debug_name); - } - } - } - std::vector CompileMethodCallThunk() { - LinkerPatch patch = LinkerPatch::RelativeCodePatch(/* literal_offset */ 0u, - /* target_dex_file*/ nullptr, - /* target_method_idx */ 0u); - return CompileThunk(patch); + ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetMethodCallKey(); + return static_cast(patcher_.get())->CompileThunk(key); } uint32_t MethodCallThunkSize() { @@ -264,38 +228,27 @@ class Thumb2RelativePatcherTest : public RelativePatcherTest { void TestStringReference(uint32_t string_offset); void CheckPcRelativePatch(const ArrayRef& patches, uint32_t target_offset); - static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, - uint32_t holder_reg, - bool narrow) { - return arm::CodeGeneratorARMVIXL::EncodeBakerReadBarrierFieldData(base_reg, holder_reg, narrow); - } - - static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { - return arm::CodeGeneratorARMVIXL::EncodeBakerReadBarrierArrayData(base_reg); - } - - static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg, bool narrow) { - return arm::CodeGeneratorARMVIXL::EncodeBakerReadBarrierGcRootData(root_reg, narrow); - } - std::vector CompileBakerOffsetThunk(uint32_t base_reg, uint32_t holder_reg, bool narrow) { const LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - /* literal_offset */ 0u, EncodeBakerReadBarrierFieldData(base_reg, holder_reg, narrow)); - return CompileThunk(patch); + 0u, Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData(base_reg, holder_reg, narrow)); + ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); + return down_cast(patcher_.get())->CompileThunk(key); } std::vector CompileBakerArrayThunk(uint32_t base_reg) { LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - /* literal_offset */ 0u, EncodeBakerReadBarrierArrayData(base_reg)); - return CompileThunk(patch); + 0u, Thumb2RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg)); + ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); + return down_cast(patcher_.get())->CompileThunk(key); } std::vector CompileBakerGcRootThunk(uint32_t root_reg, bool narrow) { LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - /* literal_offset */ 0u, EncodeBakerReadBarrierGcRootData(root_reg, narrow)); - return CompileThunk(patch); + 0u, Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg, narrow)); + ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); + return down_cast(patcher_.get())->CompileThunk(key); } uint32_t GetOutputInsn32(uint32_t offset) { @@ -641,7 +594,7 @@ void Thumb2RelativePatcherTest::TestBakerFieldWide(uint32_t offset, uint32_t ref const std::vector raw_code = RawCode({kBneWPlus0, ldr}); ASSERT_EQ(kMethodCodeSize, raw_code.size()); ArrayRef code(raw_code); - uint32_t encoded_data = EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( base_reg, holder_reg, /* narrow */ false); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data), @@ -743,7 +696,7 @@ void Thumb2RelativePatcherTest::TestBakerFieldNarrow(uint32_t offset, uint32_t r const std::vector raw_code = RawCode({kBneWPlus0, ldr}); ASSERT_EQ(kMethodCodeSize, raw_code.size()); ArrayRef code(raw_code); - uint32_t encoded_data = EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( base_reg, holder_reg, /* narrow */ true); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data), @@ -856,7 +809,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddle) { constexpr uint32_t kLiteralOffset1 = 6u; const std::vector raw_code1 = RawCode({kNopWInsn, kNopInsn, kBneWPlus0, kLdrWInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( /* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), @@ -924,7 +877,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkBeforeFiller) { constexpr uint32_t kLiteralOffset1 = 4u; const std::vector raw_code1 = RawCode({kNopWInsn, kBneWPlus0, kLdrWInsn, kNopInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( /* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), @@ -954,7 +907,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddleUnreachableFromLast constexpr uint32_t kLiteralOffset1 = 6u; const std::vector raw_code1 = RawCode({kNopWInsn, kNopInsn, kBneWPlus0, kLdrWInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( /* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), @@ -1040,7 +993,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerArray) { ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, EncodeBakerReadBarrierArrayData(base_reg)), + kLiteralOffset, Thumb2RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1121,7 +1074,8 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootWide) { ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ false)), + kLiteralOffset, + Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ false)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1180,7 +1134,8 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootNarrow) { ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ true)), + kLiteralOffset, + Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ true)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1227,7 +1182,8 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootOffsetBits) { patches.reserve(num_patches); const uint32_t ldr = kLdrWInsn | (/* offset */ 8) | (/* base_reg */ 0 << 16) | (/* root_reg */ 0 << 12); - uint32_t encoded_data = EncodeBakerReadBarrierGcRootData(/* root_reg */ 0, /* narrow */ false); + uint32_t encoded_data = + Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 0, /* narrow */ false); for (size_t i = 0; i != num_patches; ++i) { PushBackInsn(&code, ldr); PushBackInsn(&code, kBneWPlus0); @@ -1308,8 +1264,10 @@ TEST_F(Thumb2RelativePatcherTest, BakerAndMethodCallInteraction) { ldr1, kBneWPlus0, // First GC root LDR with read barrier. ldr2, kBneWPlus0, // Second GC root LDR with read barrier. }); - uint32_t encoded_data1 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 1, /* narrow */ false); - uint32_t encoded_data2 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 2, /* narrow */ false); + uint32_t encoded_data1 = + Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 1, /* narrow */ false); + uint32_t encoded_data2 = + Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 2, /* narrow */ false); const LinkerPatch last_method_patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset1, encoded_data1), LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset2, encoded_data2), diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc index 135e39d100..b268204b4a 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.cc +++ b/compiler/linker/arm64/relative_patcher_arm64.cc @@ -82,10 +82,9 @@ inline uint32_t MaxExtraSpace(size_t num_adrp, size_t code_size) { } // anonymous namespace -Arm64RelativePatcher::Arm64RelativePatcher(RelativePatcherThunkProvider* thunk_provider, - RelativePatcherTargetProvider* target_provider, +Arm64RelativePatcher::Arm64RelativePatcher(RelativePatcherTargetProvider* provider, const Arm64InstructionSetFeatures* features) - : ArmBaseRelativePatcher(thunk_provider, target_provider, InstructionSet::kArm64), + : ArmBaseRelativePatcher(provider, InstructionSet::kArm64), fix_cortex_a53_843419_(features->NeedFixCortexA53_843419()), reserved_adrp_thunks_(0u), processed_adrp_thunks_(0u) { @@ -314,6 +313,44 @@ void Arm64RelativePatcher::PatchBakerReadBarrierBranch(std::vector* cod uint32_t insn = GetInsn(code, literal_offset); DCHECK_EQ(insn & 0xffffffe0u, 0xb5000000); // CBNZ Xt, +0 (unpatched) ThunkKey key = GetBakerThunkKey(patch); + if (kIsDebugBuild) { + const uint32_t encoded_data = key.GetCustomValue1(); + BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); + // Check that the next instruction matches the expected LDR. + switch (kind) { + case BakerReadBarrierKind::kField: { + DCHECK_GE(code->size() - literal_offset, 8u); + uint32_t next_insn = GetInsn(code, literal_offset + 4u); + // LDR (immediate) with correct base_reg. + CheckValidReg(next_insn & 0x1fu); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xffc003e0u, 0xb9400000u | (base_reg << 5)); + break; + } + case BakerReadBarrierKind::kArray: { + DCHECK_GE(code->size() - literal_offset, 8u); + uint32_t next_insn = GetInsn(code, literal_offset + 4u); + // LDR (register) with the correct base_reg, size=10 (32-bit), option=011 (extend = LSL), + // and S=1 (shift amount = 2 for 32-bit version), i.e. LDR Wt, [Xn, Xm, LSL #2]. + CheckValidReg(next_insn & 0x1fu); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xffe0ffe0u, 0xb8607800u | (base_reg << 5)); + CheckValidReg((next_insn >> 16) & 0x1f); // Check index register + break; + } + case BakerReadBarrierKind::kGcRoot: { + DCHECK_GE(literal_offset, 4u); + uint32_t prev_insn = GetInsn(code, literal_offset - 4u); + // LDR (immediate) with correct root_reg. + const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(prev_insn & 0xffc0001fu, 0xb9400000u | root_reg); + break; + } + default: + LOG(FATAL) << "Unexpected kind: " << static_cast(kind); + UNREACHABLE(); + } + } uint32_t target_offset = GetThunkTargetOffset(key, patch_offset); DCHECK_ALIGNED(target_offset, 4u); uint32_t disp = target_offset - patch_offset; @@ -322,6 +359,216 @@ void Arm64RelativePatcher::PatchBakerReadBarrierBranch(std::vector* cod SetInsn(code, literal_offset, insn); } +#define __ assembler.GetVIXLAssembler()-> + +static void EmitGrayCheckAndFastPath(arm64::Arm64Assembler& assembler, + vixl::aarch64::Register base_reg, + vixl::aarch64::MemOperand& lock_word, + vixl::aarch64::Label* slow_path) { + using namespace vixl::aarch64; // NOLINT(build/namespaces) + // Load the lock word containing the rb_state. + __ Ldr(ip0.W(), lock_word); + // Given the numeric representation, it's enough to check the low bit of the rb_state. + static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0"); + static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1"); + __ Tbnz(ip0.W(), LockWord::kReadBarrierStateShift, slow_path); + static_assert( + BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET == BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET, + "Field and array LDR offsets must be the same to reuse the same code."); + // Adjust the return address back to the LDR (1 instruction; 2 for heap poisoning). + static_assert(BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4), + "Field LDR must be 1 instruction (4B) before the return address label; " + " 2 instructions (8B) for heap poisoning."); + __ Add(lr, lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET); + // Introduce a dependency on the lock_word including rb_state, + // to prevent load-load reordering, and without using + // a memory barrier (which would be more expensive). + __ Add(base_reg, base_reg, Operand(ip0, LSR, 32)); + __ Br(lr); // And return back to the function. + // Note: The fake dependency is unnecessary for the slow path. +} + +// Load the read barrier introspection entrypoint in register `entrypoint`. +static void LoadReadBarrierMarkIntrospectionEntrypoint(arm64::Arm64Assembler& assembler, + vixl::aarch64::Register entrypoint) { + using vixl::aarch64::MemOperand; + using vixl::aarch64::ip0; + // Thread Register. + const vixl::aarch64::Register tr = vixl::aarch64::x19; + + // entrypoint = Thread::Current()->pReadBarrierMarkReg16, i.e. pReadBarrierMarkIntrospection. + DCHECK_EQ(ip0.GetCode(), 16u); + const int32_t entry_point_offset = + Thread::ReadBarrierMarkEntryPointsOffset(ip0.GetCode()); + __ Ldr(entrypoint, MemOperand(tr, entry_point_offset)); +} + +void Arm64RelativePatcher::CompileBakerReadBarrierThunk(arm64::Arm64Assembler& assembler, + uint32_t encoded_data) { + using namespace vixl::aarch64; // NOLINT(build/namespaces) + BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); + switch (kind) { + case BakerReadBarrierKind::kField: { + // Check if the holder is gray and, if not, add fake dependency to the base register + // and return to the LDR instruction to load the reference. Otherwise, use introspection + // to load the reference and call the entrypoint (in IP1) that performs further checks + // on the reference and marks it if needed. + auto base_reg = + Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(base_reg.GetCode()); + auto holder_reg = + Register::GetXRegFromCode(BakerReadBarrierSecondRegField::Decode(encoded_data)); + CheckValidReg(holder_reg.GetCode()); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip0, ip1); + // If base_reg differs from holder_reg, the offset was too large and we must have + // emitted an explicit null check before the load. Otherwise, we need to null-check + // the holder as we do not necessarily do that check before going to the thunk. + vixl::aarch64::Label throw_npe; + if (holder_reg.Is(base_reg)) { + __ Cbz(holder_reg.W(), &throw_npe); + } + vixl::aarch64::Label slow_path; + MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value()); + EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path); + __ Bind(&slow_path); + MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET); + __ Ldr(ip0.W(), ldr_address); // Load the LDR (immediate) unsigned offset. + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); + __ Ubfx(ip0.W(), ip0.W(), 10, 12); // Extract the offset. + __ Ldr(ip0.W(), MemOperand(base_reg, ip0, LSL, 2)); // Load the reference. + // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference. + __ Br(ip1); // Jump to the entrypoint. + if (holder_reg.Is(base_reg)) { + // Add null check slow path. The stack map is at the address pointed to by LR. + __ Bind(&throw_npe); + int32_t offset = GetThreadOffset(kQuickThrowNullPointer).Int32Value(); + __ Ldr(ip0, MemOperand(/* Thread* */ vixl::aarch64::x19, offset)); + __ Br(ip0); + } + break; + } + case BakerReadBarrierKind::kArray: { + auto base_reg = + Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(base_reg.GetCode()); + DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip0, ip1); + vixl::aarch64::Label slow_path; + int32_t data_offset = + mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value(); + MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset); + DCHECK_LT(lock_word.GetOffset(), 0); + EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path); + __ Bind(&slow_path); + MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET); + __ Ldr(ip0.W(), ldr_address); // Load the LDR (register) unsigned offset. + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); + __ Ubfx(ip0, ip0, 16, 6); // Extract the index register, plus 32 (bit 21 is set). + __ Bfi(ip1, ip0, 3, 6); // Insert ip0 to the entrypoint address to create + // a switch case target based on the index register. + __ Mov(ip0, base_reg); // Move the base register to ip0. + __ Br(ip1); // Jump to the entrypoint's array switch case. + break; + } + case BakerReadBarrierKind::kGcRoot: { + // Check if the reference needs to be marked and if so (i.e. not null, not marked yet + // and it does not have a forwarding address), call the correct introspection entrypoint; + // otherwise return the reference (or the extracted forwarding address). + // There is no gray bit check for GC roots. + auto root_reg = + Register::GetWRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(root_reg.GetCode()); + DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip0, ip1); + vixl::aarch64::Label return_label, not_marked, forwarding_address; + __ Cbz(root_reg, &return_label); + MemOperand lock_word(root_reg.X(), mirror::Object::MonitorOffset().Int32Value()); + __ Ldr(ip0.W(), lock_word); + __ Tbz(ip0.W(), LockWord::kMarkBitStateShift, ¬_marked); + __ Bind(&return_label); + __ Br(lr); + __ Bind(¬_marked); + __ Tst(ip0.W(), Operand(ip0.W(), LSL, 1)); + __ B(&forwarding_address, mi); + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); + // Adjust the art_quick_read_barrier_mark_introspection address in IP1 to + // art_quick_read_barrier_mark_introspection_gc_roots. + __ Add(ip1, ip1, Operand(BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRYPOINT_OFFSET)); + __ Mov(ip0.W(), root_reg); + __ Br(ip1); + __ Bind(&forwarding_address); + __ Lsl(root_reg, ip0.W(), LockWord::kForwardingAddressShift); + __ Br(lr); + break; + } + default: + LOG(FATAL) << "Unexpected kind: " << static_cast(kind); + UNREACHABLE(); + } +} + +std::vector Arm64RelativePatcher::CompileThunk(const ThunkKey& key) { + ArenaPool pool; + ArenaAllocator allocator(&pool); + arm64::Arm64Assembler assembler(&allocator); + + switch (key.GetType()) { + case ThunkType::kMethodCall: { + // The thunk just uses the entry point in the ArtMethod. This works even for calls + // to the generic JNI and interpreter trampolines. + Offset offset(ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kArm64PointerSize).Int32Value()); + assembler.JumpTo(ManagedRegister(arm64::X0), offset, ManagedRegister(arm64::IP0)); + break; + } + case ThunkType::kBakerReadBarrier: { + CompileBakerReadBarrierThunk(assembler, key.GetCustomValue1()); + break; + } + } + + // Ensure we emit the literal pool. + assembler.FinalizeCode(); + std::vector thunk_code(assembler.CodeSize()); + MemoryRegion code(thunk_code.data(), thunk_code.size()); + assembler.FinalizeInstructions(code); + return thunk_code; +} + +std::string Arm64RelativePatcher::GetThunkDebugName(const ThunkKey& key) { + switch (key.GetType()) { + case ThunkType::kMethodCall: + return "MethodCallThunk"; + + case ThunkType::kBakerReadBarrier: { + uint32_t encoded_data = key.GetCustomValue1(); + BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); + std::ostringstream oss; + oss << "BakerReadBarrierThunk"; + switch (kind) { + case BakerReadBarrierKind::kField: + oss << "Field_r" << BakerReadBarrierFirstRegField::Decode(encoded_data) + << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data); + break; + case BakerReadBarrierKind::kArray: + oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); + DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); + break; + case BakerReadBarrierKind::kGcRoot: + oss << "GcRoot_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); + DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); + break; + } + return oss.str(); + } + } +} + +#undef __ + uint32_t Arm64RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) { switch (key.GetType()) { case ThunkType::kMethodCall: diff --git a/compiler/linker/arm64/relative_patcher_arm64.h b/compiler/linker/arm64/relative_patcher_arm64.h index 9dc289da44..8ba59976e7 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.h +++ b/compiler/linker/arm64/relative_patcher_arm64.h @@ -18,6 +18,8 @@ #define ART_COMPILER_LINKER_ARM64_RELATIVE_PATCHER_ARM64_H_ #include "base/array_ref.h" +#include "base/bit_field.h" +#include "base/bit_utils.h" #include "linker/arm/relative_patcher_arm_base.h" namespace art { @@ -30,8 +32,29 @@ namespace linker { class Arm64RelativePatcher FINAL : public ArmBaseRelativePatcher { public: - Arm64RelativePatcher(RelativePatcherThunkProvider* thunk_provider, - RelativePatcherTargetProvider* target_provider, + static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, uint32_t holder_reg) { + CheckValidReg(base_reg); + CheckValidReg(holder_reg); + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kField) | + BakerReadBarrierFirstRegField::Encode(base_reg) | + BakerReadBarrierSecondRegField::Encode(holder_reg); + } + + static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { + CheckValidReg(base_reg); + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kArray) | + BakerReadBarrierFirstRegField::Encode(base_reg) | + BakerReadBarrierSecondRegField::Encode(kInvalidEncodedReg); + } + + static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg) { + CheckValidReg(root_reg); + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kGcRoot) | + BakerReadBarrierFirstRegField::Encode(root_reg) | + BakerReadBarrierSecondRegField::Encode(kInvalidEncodedReg); + } + + Arm64RelativePatcher(RelativePatcherTargetProvider* provider, const Arm64InstructionSetFeatures* features); uint32_t ReserveSpace(uint32_t offset, @@ -52,10 +75,37 @@ class Arm64RelativePatcher FINAL : public ArmBaseRelativePatcher { uint32_t patch_offset) OVERRIDE; protected: + std::vector CompileThunk(const ThunkKey& key) OVERRIDE; + std::string GetThunkDebugName(const ThunkKey& key) OVERRIDE; uint32_t MaxPositiveDisplacement(const ThunkKey& key) OVERRIDE; uint32_t MaxNegativeDisplacement(const ThunkKey& key) OVERRIDE; private: + static constexpr uint32_t kInvalidEncodedReg = /* sp/zr is invalid */ 31u; + + enum class BakerReadBarrierKind : uint8_t { + kField, // Field get or array get with constant offset (i.e. constant index). + kArray, // Array get with index in register. + kGcRoot, // GC root load. + kLast = kGcRoot + }; + + static constexpr size_t kBitsForBakerReadBarrierKind = + MinimumBitsToStore(static_cast(BakerReadBarrierKind::kLast)); + static constexpr size_t kBitsForRegister = 5u; + using BakerReadBarrierKindField = + BitField; + using BakerReadBarrierFirstRegField = + BitField; + using BakerReadBarrierSecondRegField = + BitField; + + static void CheckValidReg(uint32_t reg) { + DCHECK(reg < 30u && reg != 16u && reg != 17u) << reg; + } + + void CompileBakerReadBarrierThunk(arm64::Arm64Assembler& assembler, uint32_t encoded_data); + static uint32_t PatchAdrp(uint32_t adrp, uint32_t disp); static bool NeedsErratum843419Thunk(ArrayRef code, uint32_t literal_offset, diff --git a/compiler/linker/arm64/relative_patcher_arm64_test.cc b/compiler/linker/arm64/relative_patcher_arm64_test.cc index 393733dd0c..05459a2a82 100644 --- a/compiler/linker/arm64/relative_patcher_arm64_test.cc +++ b/compiler/linker/arm64/relative_patcher_arm64_test.cc @@ -16,15 +16,12 @@ #include "linker/arm64/relative_patcher_arm64.h" -#include "arch/arm64/instruction_set_features_arm64.h" #include "base/casts.h" #include "linker/relative_patcher_test.h" #include "lock_word.h" #include "mirror/array-inl.h" #include "mirror/object.h" #include "oat_quick_method_header.h" -#include "optimizing/code_generator_arm64.h" -#include "optimizing/optimizing_unit_test.h" namespace art { namespace linker { @@ -171,42 +168,9 @@ class Arm64RelativePatcherTest : public RelativePatcherTest { return result.second; } - std::vector CompileThunk(const LinkerPatch& patch, - /*out*/ std::string* debug_name = nullptr) { - OptimizingUnitTestHelper helper; - HGraph* graph = helper.CreateGraph(); - std::string error_msg; - Arm64FeaturesUniquePtr features = - Arm64InstructionSetFeatures::FromVariant("default", &error_msg); - CompilerOptions options; - arm64::CodeGeneratorARM64 codegen(graph, *features, options); - ArenaVector code(helper.GetAllocator()->Adapter()); - codegen.EmitThunkCode(patch, &code, debug_name); - return std::vector(code.begin(), code.end()); - } - - void AddCompiledMethod( - MethodReference method_ref, - const ArrayRef& code, - const ArrayRef& patches = ArrayRef()) { - RelativePatcherTest::AddCompiledMethod(method_ref, code, patches); - - // Make sure the ThunkProvider has all the necessary thunks. - for (const LinkerPatch& patch : patches) { - if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch || - patch.GetType() == LinkerPatch::Type::kCallRelative) { - std::string debug_name; - std::vector thunk_code = CompileThunk(patch, &debug_name); - thunk_provider_.SetThunkCode(patch, ArrayRef(thunk_code), debug_name); - } - } - } - std::vector CompileMethodCallThunk() { - LinkerPatch patch = LinkerPatch::RelativeCodePatch(/* literal_offset */ 0u, - /* target_dex_file*/ nullptr, - /* target_method_idx */ 0u); - return CompileThunk(patch); + ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetMethodCallKey(); + return down_cast(patcher_.get())->CompileThunk(key); } uint32_t MethodCallThunkSize() { @@ -511,34 +475,25 @@ class Arm64RelativePatcherTest : public RelativePatcherTest { TestAdrpInsn2Add(insn2, adrp_offset, has_thunk, string_offset); } - static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, uint32_t holder_reg) { - return arm64::CodeGeneratorARM64::EncodeBakerReadBarrierFieldData(base_reg, holder_reg); - } - - static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { - return arm64::CodeGeneratorARM64::EncodeBakerReadBarrierArrayData(base_reg); - } - - static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg) { - return arm64::CodeGeneratorARM64::EncodeBakerReadBarrierGcRootData(root_reg); - } - std::vector CompileBakerOffsetThunk(uint32_t base_reg, uint32_t holder_reg) { const LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - /* literal_offset */ 0u, EncodeBakerReadBarrierFieldData(base_reg, holder_reg)); - return CompileThunk(patch); + 0u, Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(base_reg, holder_reg)); + ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); + return down_cast(patcher_.get())->CompileThunk(key); } std::vector CompileBakerArrayThunk(uint32_t base_reg) { LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - /* literal_offset */ 0u, EncodeBakerReadBarrierArrayData(base_reg)); - return CompileThunk(patch); + 0u, Arm64RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg)); + ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); + return down_cast(patcher_.get())->CompileThunk(key); } std::vector CompileBakerGcRootThunk(uint32_t root_reg) { LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - /* literal_offset */ 0u, EncodeBakerReadBarrierGcRootData(root_reg)); - return CompileThunk(patch); + 0u, Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg)); + ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); + return down_cast(patcher_.get())->CompileThunk(key); } uint32_t GetOutputInsn(uint32_t offset) { @@ -964,7 +919,8 @@ void Arm64RelativePatcherTest::TestBakerField(uint32_t offset, uint32_t ref_reg) const std::vector raw_code = RawCode({kCbnzIP1Plus0Insn, ldr}); ASSERT_EQ(kMethodCodeSize, raw_code.size()); ArrayRef code(raw_code); - uint32_t encoded_data = EncodeBakerReadBarrierFieldData(base_reg, holder_reg); + uint32_t encoded_data = + Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(base_reg, holder_reg); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data), }; @@ -1049,7 +1005,8 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddle) { constexpr uint32_t kLiteralOffset1 = 4; const std::vector raw_code1 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); + uint32_t encoded_data = + Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), }; @@ -1109,7 +1066,8 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkBeforeFiller) { constexpr uint32_t kLiteralOffset1 = 0; const std::vector raw_code1 = RawCode({kCbnzIP1Plus0Insn, kLdrWInsn, kNopInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); + uint32_t encoded_data = + Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), }; @@ -1138,7 +1096,8 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddleUnreachableFr constexpr uint32_t kLiteralOffset1 = 4; const std::vector raw_code1 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); + uint32_t encoded_data = + Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), }; @@ -1211,7 +1170,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerArray) { ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, EncodeBakerReadBarrierArrayData(base_reg)), + kLiteralOffset, Arm64RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1288,7 +1247,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerGcRoot) { ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, EncodeBakerReadBarrierGcRootData(root_reg)), + kLiteralOffset, Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1384,8 +1343,8 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerAndMethodCallInteraction) { kNopInsn, kNopInsn, // Padding before second GC root read barrier. ldr2, kCbnzIP1Plus0Insn, // Second GC root LDR with read barrier. }); - uint32_t encoded_data1 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 1); - uint32_t encoded_data2 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 2); + uint32_t encoded_data1 = Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 1); + uint32_t encoded_data2 = Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 2); const LinkerPatch last_method_patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset1, encoded_data1), LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset2, encoded_data2), diff --git a/compiler/linker/linker_patch.h b/compiler/linker/linker_patch.h index 7b35fd9b0c..710d8a690a 100644 --- a/compiler/linker/linker_patch.h +++ b/compiler/linker/linker_patch.h @@ -141,7 +141,7 @@ class LinkerPatch { static LinkerPatch BakerReadBarrierBranchPatch(size_t literal_offset, uint32_t custom_value1 = 0u, uint32_t custom_value2 = 0u) { - LinkerPatch patch(literal_offset, Type::kBakerReadBarrierBranch, /* target_dex_file */ nullptr); + LinkerPatch patch(literal_offset, Type::kBakerReadBarrierBranch, nullptr); patch.baker_custom_value1_ = custom_value1; patch.baker_custom_value2_ = custom_value2; return patch; diff --git a/compiler/linker/relative_patcher.cc b/compiler/linker/relative_patcher.cc index b82d15230d..13877f8f12 100644 --- a/compiler/linker/relative_patcher.cc +++ b/compiler/linker/relative_patcher.cc @@ -43,8 +43,7 @@ namespace linker { std::unique_ptr RelativePatcher::Create( InstructionSet instruction_set, const InstructionSetFeatures* features, - RelativePatcherThunkProvider* thunk_provider, - RelativePatcherTargetProvider* target_provider) { + RelativePatcherTargetProvider* provider) { class RelativePatcherNone FINAL : public RelativePatcher { public: RelativePatcherNone() { } @@ -93,8 +92,7 @@ std::unique_ptr RelativePatcher::Create( }; UNUSED(features); - UNUSED(thunk_provider); - UNUSED(target_provider); + UNUSED(provider); switch (instruction_set) { #ifdef ART_ENABLE_CODEGEN_x86 case InstructionSet::kX86: @@ -108,15 +106,12 @@ std::unique_ptr RelativePatcher::Create( case InstructionSet::kArm: // Fall through: we generate Thumb2 code for "arm". case InstructionSet::kThumb2: - return std::unique_ptr( - new Thumb2RelativePatcher(thunk_provider, target_provider)); + return std::unique_ptr(new Thumb2RelativePatcher(provider)); #endif #ifdef ART_ENABLE_CODEGEN_arm64 case InstructionSet::kArm64: return std::unique_ptr( - new Arm64RelativePatcher(thunk_provider, - target_provider, - features->AsArm64InstructionSetFeatures())); + new Arm64RelativePatcher(provider, features->AsArm64InstructionSetFeatures())); #endif #ifdef ART_ENABLE_CODEGEN_mips case InstructionSet::kMips: diff --git a/compiler/linker/relative_patcher.h b/compiler/linker/relative_patcher.h index 06c7e70d23..b58e3dffbd 100644 --- a/compiler/linker/relative_patcher.h +++ b/compiler/linker/relative_patcher.h @@ -38,27 +38,6 @@ namespace linker { class LinkerPatch; class OutputStream; -/** - * @class RelativePatcherThunkProvider - * @brief Interface for providing method offsets for relative call targets. - */ -class RelativePatcherThunkProvider { - public: - /** - * Get the code and debug name of a thunk needed by the given linker patch. - * - * @param patch The patch for which we need to retrieve the thunk code. - * @param code A variable to receive the code of the thunk. This code must not be empty. - * @param debug_name A variable to receive the debug name of the thunk. - */ - virtual void GetThunkCode(const LinkerPatch& patch, - /*out*/ ArrayRef* code, - /*out*/ std::string* debug_name) = 0; - - protected: - virtual ~RelativePatcherThunkProvider() { } -}; - /** * @class RelativePatcherTargetProvider * @brief Interface for providing method offsets for relative call targets. @@ -91,10 +70,8 @@ class RelativePatcherTargetProvider { class RelativePatcher { public: static std::unique_ptr Create( - InstructionSet instruction_set, - const InstructionSetFeatures* features, - RelativePatcherThunkProvider* thunk_provider, - RelativePatcherTargetProvider* target_provider); + InstructionSet instruction_set, const InstructionSetFeatures* features, + RelativePatcherTargetProvider* provider); virtual ~RelativePatcher() { } diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h index af8dc4dbc9..d21f2795b9 100644 --- a/compiler/linker/relative_patcher_test.h +++ b/compiler/linker/relative_patcher_test.h @@ -58,10 +58,7 @@ class RelativePatcherTest : public testing::Test { instruction_set_(instruction_set), features_(InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg_)), method_offset_map_(), - patcher_(RelativePatcher::Create(instruction_set, - features_.get(), - &thunk_provider_, - &method_offset_map_)), + patcher_(RelativePatcher::Create(instruction_set, features_.get(), &method_offset_map_)), bss_begin_(0u), compiled_method_refs_(), compiled_methods_(), @@ -251,72 +248,6 @@ class RelativePatcherTest : public testing::Test { LOG(ERROR) << " " << diff_indicator_str; } - class ThunkProvider : public RelativePatcherThunkProvider { - public: - ThunkProvider() {} - - void SetThunkCode(const LinkerPatch& patch, - ArrayRef code, - const std::string& debug_name) { - thunk_map_.emplace(ThunkKey(patch), ThunkValue(code, debug_name)); - } - - void GetThunkCode(const LinkerPatch& patch, - /*out*/ ArrayRef* code, - /*out*/ std::string* debug_name) OVERRIDE { - auto it = thunk_map_.find(ThunkKey(patch)); - CHECK(it != thunk_map_.end()); - const ThunkValue& value = it->second; - CHECK(code != nullptr); - *code = value.GetCode(); - CHECK(debug_name != nullptr); - *debug_name = value.GetDebugName(); - } - - private: - class ThunkKey { - public: - explicit ThunkKey(const LinkerPatch& patch) - : type_(patch.GetType()), - custom_value1_(patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch - ? patch.GetBakerCustomValue1() : 0u), - custom_value2_(patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch - ? patch.GetBakerCustomValue2() : 0u) { - CHECK(patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch || - patch.GetType() == LinkerPatch::Type::kCallRelative); - } - - bool operator<(const ThunkKey& other) const { - if (custom_value1_ != other.custom_value1_) { - return custom_value1_ < other.custom_value1_; - } - if (custom_value2_ != other.custom_value2_) { - return custom_value2_ < other.custom_value2_; - } - return type_ < other.type_; - } - - private: - const LinkerPatch::Type type_; - const uint32_t custom_value1_; - const uint32_t custom_value2_; - }; - - class ThunkValue { - public: - ThunkValue(ArrayRef code, const std::string& debug_name) - : code_(code.begin(), code.end()), debug_name_(debug_name) {} - ArrayRef GetCode() const { return ArrayRef(code_); } - const std::string& GetDebugName() const { return debug_name_; } - - private: - const std::vector code_; - const std::string debug_name_; - }; - - std::map thunk_map_; - }; - // Map method reference to assinged offset. // Wrap the map in a class implementing RelativePatcherTargetProvider. class MethodOffsetMap FINAL : public RelativePatcherTargetProvider { @@ -341,7 +272,6 @@ class RelativePatcherTest : public testing::Test { std::string error_msg_; InstructionSet instruction_set_; std::unique_ptr features_; - ThunkProvider thunk_provider_; MethodOffsetMap method_offset_map_; std::unique_ptr patcher_; uint32_t bss_begin_; diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 231017f55e..c2ae7646b5 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -449,18 +449,6 @@ void CodeGenerator::EmitLinkerPatches( // No linker patches by default. } -bool CodeGenerator::NeedsThunkCode(const linker::LinkerPatch& patch ATTRIBUTE_UNUSED) const { - // Code generators that create patches requiring thunk compilation should override this function. - return false; -} - -void CodeGenerator::EmitThunkCode(const linker::LinkerPatch& patch ATTRIBUTE_UNUSED, - /*out*/ ArenaVector* code ATTRIBUTE_UNUSED, - /*out*/ std::string* debug_name ATTRIBUTE_UNUSED) { - // Code generators that create patches requiring thunk compilation should override this function. - LOG(FATAL) << "Unexpected call to EmitThunkCode()."; -} - void CodeGenerator::InitializeCodeGeneration(size_t number_of_spill_slots, size_t maximum_safepoint_spill_size, size_t number_of_out_slots, diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index a86b27151d..3bd5e14539 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -21,7 +21,6 @@ #include "arch/instruction_set_features.h" #include "base/arena_containers.h" #include "base/arena_object.h" -#include "base/array_ref.h" #include "base/bit_field.h" #include "base/bit_utils.h" #include "base/enums.h" @@ -75,7 +74,6 @@ class CodeAllocator { virtual ~CodeAllocator() {} virtual uint8_t* Allocate(size_t size) = 0; - virtual ArrayRef GetMemory() const = 0; private: DISALLOW_COPY_AND_ASSIGN(CodeAllocator); @@ -212,10 +210,6 @@ class CodeGenerator : public DeletableArenaObject { virtual void Initialize() = 0; virtual void Finalize(CodeAllocator* allocator); virtual void EmitLinkerPatches(ArenaVector* linker_patches); - virtual bool NeedsThunkCode(const linker::LinkerPatch& patch) const; - virtual void EmitThunkCode(const linker::LinkerPatch& patch, - /*out*/ ArenaVector* code, - /*out*/ std::string* debug_name); virtual void GenerateFrameEntry() = 0; virtual void GenerateFrameExit() = 0; virtual void Bind(HBasicBlock* block) = 0; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 31887d92e8..273346ab4a 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -30,6 +30,7 @@ #include "heap_poisoning.h" #include "intrinsics.h" #include "intrinsics_arm64.h" +#include "linker/arm64/relative_patcher_arm64.h" #include "linker/linker_patch.h" #include "lock_word.h" #include "mirror/array-inl.h" @@ -1424,62 +1425,6 @@ void CodeGeneratorARM64::Finalize(CodeAllocator* allocator) { __ FinalizeCode(); CodeGenerator::Finalize(allocator); - - // Verify Baker read barrier linker patches. - if (kIsDebugBuild) { - ArrayRef code = allocator->GetMemory(); - for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) { - DCHECK(info.label.IsBound()); - uint32_t literal_offset = info.label.GetLocation(); - DCHECK_ALIGNED(literal_offset, 4u); - - auto GetInsn = [&code](uint32_t offset) { - DCHECK_ALIGNED(offset, 4u); - return - (static_cast(code[offset + 0]) << 0) + - (static_cast(code[offset + 1]) << 8) + - (static_cast(code[offset + 2]) << 16)+ - (static_cast(code[offset + 3]) << 24); - }; - - const uint32_t encoded_data = info.custom_data; - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - // Check that the next instruction matches the expected LDR. - switch (kind) { - case BakerReadBarrierKind::kField: { - DCHECK_GE(code.size() - literal_offset, 8u); - uint32_t next_insn = GetInsn(literal_offset + 4u); - // LDR (immediate) with correct base_reg. - CheckValidReg(next_insn & 0x1fu); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xffc003e0u, 0xb9400000u | (base_reg << 5)); - break; - } - case BakerReadBarrierKind::kArray: { - DCHECK_GE(code.size() - literal_offset, 8u); - uint32_t next_insn = GetInsn(literal_offset + 4u); - // LDR (register) with the correct base_reg, size=10 (32-bit), option=011 (extend = LSL), - // and S=1 (shift amount = 2 for 32-bit version), i.e. LDR Wt, [Xn, Xm, LSL #2]. - CheckValidReg(next_insn & 0x1fu); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xffe0ffe0u, 0xb8607800u | (base_reg << 5)); - CheckValidReg((next_insn >> 16) & 0x1f); // Check index register - break; - } - case BakerReadBarrierKind::kGcRoot: { - DCHECK_GE(literal_offset, 4u); - uint32_t prev_insn = GetInsn(literal_offset - 4u); - // LDR (immediate) with correct root_reg. - const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(prev_insn & 0xffc0001fu, 0xb9400000u | root_reg); - break; - } - default: - LOG(FATAL) << "Unexpected kind: " << static_cast(kind); - UNREACHABLE(); - } - } - } } void ParallelMoveResolverARM64::PrepareForEmitNativeCode() { @@ -4869,44 +4814,6 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector* lin DCHECK_EQ(size, linker_patches->size()); } -bool CodeGeneratorARM64::NeedsThunkCode(const linker::LinkerPatch& patch) const { - return patch.GetType() == linker::LinkerPatch::Type::kBakerReadBarrierBranch || - patch.GetType() == linker::LinkerPatch::Type::kCallRelative; -} - -void CodeGeneratorARM64::EmitThunkCode(const linker::LinkerPatch& patch, - /*out*/ ArenaVector* code, - /*out*/ std::string* debug_name) { - Arm64Assembler assembler(GetGraph()->GetAllocator()); - switch (patch.GetType()) { - case linker::LinkerPatch::Type::kCallRelative: { - // The thunk just uses the entry point in the ArtMethod. This works even for calls - // to the generic JNI and interpreter trampolines. - Offset offset(ArtMethod::EntryPointFromQuickCompiledCodeOffset( - kArm64PointerSize).Int32Value()); - assembler.JumpTo(ManagedRegister(arm64::X0), offset, ManagedRegister(arm64::IP0)); - if (GetCompilerOptions().GenerateAnyDebugInfo()) { - *debug_name = "MethodCallThunk"; - } - break; - } - case linker::LinkerPatch::Type::kBakerReadBarrierBranch: { - DCHECK_EQ(patch.GetBakerCustomValue2(), 0u); - CompileBakerReadBarrierThunk(assembler, patch.GetBakerCustomValue1(), debug_name); - break; - } - default: - LOG(FATAL) << "Unexpected patch type " << patch.GetType(); - UNREACHABLE(); - } - - // Ensure we emit the literal pool if any. - assembler.FinalizeCode(); - code->resize(assembler.CodeSize()); - MemoryRegion code_region(code->data(), code->size()); - assembler.FinalizeInstructions(code_region); -} - vixl::aarch64::Literal* CodeGeneratorARM64::DeduplicateUint32Literal(uint32_t value) { return uint32_literals_.GetOrCreate( value, @@ -5047,12 +4954,12 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA DCHECK(!cls->MustGenerateClinitCheck()); // /* GcRoot */ out = current_method->declaring_class_ Register current_method = InputRegisterAt(cls, 0); - codegen_->GenerateGcRootFieldLoad(cls, - out_loc, - current_method, - ArtMethod::DeclaringClassOffset().Int32Value(), - /* fixup_label */ nullptr, - read_barrier_option); + GenerateGcRootFieldLoad(cls, + out_loc, + current_method, + ArtMethod::DeclaringClassOffset().Int32Value(), + /* fixup_label */ nullptr, + read_barrier_option); break; } case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { @@ -5099,12 +5006,12 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA vixl::aarch64::Label* ldr_label = codegen_->NewBssEntryTypePatch(dex_file, type_index, adrp_label); // /* GcRoot */ out = *(base_address + offset) /* PC-relative */ - codegen_->GenerateGcRootFieldLoad(cls, - out_loc, - temp, - /* offset placeholder */ 0u, - ldr_label, - read_barrier_option); + GenerateGcRootFieldLoad(cls, + out_loc, + temp, + /* offset placeholder */ 0u, + ldr_label, + read_barrier_option); generate_null_check = true; break; } @@ -5112,12 +5019,12 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(), cls->GetTypeIndex(), cls->GetClass())); - codegen_->GenerateGcRootFieldLoad(cls, - out_loc, - out.X(), - /* offset */ 0, - /* fixup_label */ nullptr, - read_barrier_option); + GenerateGcRootFieldLoad(cls, + out_loc, + out.X(), + /* offset */ 0, + /* fixup_label */ nullptr, + read_barrier_option); break; } case HLoadClass::LoadKind::kRuntimeCall: @@ -5260,12 +5167,12 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD vixl::aarch64::Label* ldr_label = codegen_->NewStringBssEntryPatch(dex_file, string_index, adrp_label); // /* GcRoot */ out = *(base_address + offset) /* PC-relative */ - codegen_->GenerateGcRootFieldLoad(load, - out_loc, - temp, - /* offset placeholder */ 0u, - ldr_label, - kCompilerReadBarrierOption); + GenerateGcRootFieldLoad(load, + out_loc, + temp, + /* offset placeholder */ 0u, + ldr_label, + kCompilerReadBarrierOption); SlowPathCodeARM64* slow_path = new (codegen_->GetScopedAllocator()) LoadStringSlowPathARM64(load); codegen_->AddSlowPath(slow_path); @@ -5278,12 +5185,12 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(), load->GetStringIndex(), load->GetString())); - codegen_->GenerateGcRootFieldLoad(load, - out_loc, - out.X(), - /* offset */ 0, - /* fixup_label */ nullptr, - kCompilerReadBarrierOption); + GenerateGcRootFieldLoad(load, + out_loc, + out.X(), + /* offset */ 0, + /* fixup_label */ nullptr, + kCompilerReadBarrierOption); return; } default: @@ -6232,7 +6139,7 @@ void InstructionCodeGeneratorARM64::GenerateReferenceLoadTwoRegisters( } } -void CodeGeneratorARM64::GenerateGcRootFieldLoad( +void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad( HInstruction* instruction, Location root, Register obj, @@ -6266,8 +6173,9 @@ void CodeGeneratorARM64::GenerateGcRootFieldLoad( DCHECK(temps.IsAvailable(ip0)); DCHECK(temps.IsAvailable(ip1)); temps.Exclude(ip0, ip1); - uint32_t custom_data = EncodeBakerReadBarrierGcRootData(root_reg.GetCode()); - vixl::aarch64::Label* cbnz_label = NewBakerReadBarrierPatch(custom_data); + uint32_t custom_data = + linker::Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg.GetCode()); + vixl::aarch64::Label* cbnz_label = codegen_->NewBakerReadBarrierPatch(custom_data); EmissionCheckScope guard(GetVIXLAssembler(), 3 * vixl::aarch64::kInstructionSize); vixl::aarch64::Label return_address; @@ -6296,14 +6204,14 @@ void CodeGeneratorARM64::GenerateGcRootFieldLoad( // Slow path marking the GC root `root`. The entrypoint will // be loaded by the slow path code. SlowPathCodeARM64* slow_path = - new (GetScopedAllocator()) ReadBarrierMarkSlowPathARM64(instruction, root); - AddSlowPath(slow_path); + new (codegen_->GetScopedAllocator()) ReadBarrierMarkSlowPathARM64(instruction, root); + codegen_->AddSlowPath(slow_path); // /* GcRoot */ root = *(obj + offset) if (fixup_label == nullptr) { __ Ldr(root_reg, MemOperand(obj, offset)); } else { - EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj); + codegen_->EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj); } static_assert( sizeof(mirror::CompressedReference) == sizeof(GcRoot), @@ -6323,10 +6231,10 @@ void CodeGeneratorARM64::GenerateGcRootFieldLoad( if (fixup_label == nullptr) { __ Add(root_reg.X(), obj.X(), offset); } else { - EmitAddPlaceholder(fixup_label, root_reg.X(), obj.X()); + codegen_->EmitAddPlaceholder(fixup_label, root_reg.X(), obj.X()); } // /* mirror::Object* */ root = root->Read() - GenerateReadBarrierForRootSlow(instruction, root, root); + codegen_->GenerateReadBarrierForRootSlow(instruction, root, root); } } else { // Plain GC root load with no read barrier. @@ -6334,12 +6242,12 @@ void CodeGeneratorARM64::GenerateGcRootFieldLoad( if (fixup_label == nullptr) { __ Ldr(root_reg, MemOperand(obj, offset)); } else { - EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj.X()); + codegen_->EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj.X()); } // Note that GC roots are not affected by heap poisoning, thus we // do not have to unpoison `root_reg` here. } - MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__); } void CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -6388,7 +6296,9 @@ void CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* ins DCHECK(temps.IsAvailable(ip0)); DCHECK(temps.IsAvailable(ip1)); temps.Exclude(ip0, ip1); - uint32_t custom_data = EncodeBakerReadBarrierFieldData(base.GetCode(), obj.GetCode()); + uint32_t custom_data = linker::Arm64RelativePatcher::EncodeBakerReadBarrierFieldData( + base.GetCode(), + obj.GetCode()); vixl::aarch64::Label* cbnz_label = NewBakerReadBarrierPatch(custom_data); { @@ -6473,7 +6383,8 @@ void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* ins DCHECK(temps.IsAvailable(ip0)); DCHECK(temps.IsAvailable(ip1)); temps.Exclude(ip0, ip1); - uint32_t custom_data = EncodeBakerReadBarrierArrayData(temp.GetCode()); + uint32_t custom_data = + linker::Arm64RelativePatcher::EncodeBakerReadBarrierArrayData(temp.GetCode()); vixl::aarch64::Label* cbnz_label = NewBakerReadBarrierPatch(custom_data); __ Add(temp.X(), obj.X(), Operand(data_offset)); @@ -6833,176 +6744,5 @@ void CodeGeneratorARM64::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_ #undef __ #undef QUICK_ENTRY_POINT -#define __ assembler.GetVIXLAssembler()-> - -static void EmitGrayCheckAndFastPath(arm64::Arm64Assembler& assembler, - vixl::aarch64::Register base_reg, - vixl::aarch64::MemOperand& lock_word, - vixl::aarch64::Label* slow_path) { - // Load the lock word containing the rb_state. - __ Ldr(ip0.W(), lock_word); - // Given the numeric representation, it's enough to check the low bit of the rb_state. - static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0"); - static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1"); - __ Tbnz(ip0.W(), LockWord::kReadBarrierStateShift, slow_path); - static_assert( - BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET == BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET, - "Field and array LDR offsets must be the same to reuse the same code."); - // Adjust the return address back to the LDR (1 instruction; 2 for heap poisoning). - static_assert(BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4), - "Field LDR must be 1 instruction (4B) before the return address label; " - " 2 instructions (8B) for heap poisoning."); - __ Add(lr, lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET); - // Introduce a dependency on the lock_word including rb_state, - // to prevent load-load reordering, and without using - // a memory barrier (which would be more expensive). - __ Add(base_reg, base_reg, Operand(ip0, LSR, 32)); - __ Br(lr); // And return back to the function. - // Note: The fake dependency is unnecessary for the slow path. -} - -// Load the read barrier introspection entrypoint in register `entrypoint`. -static void LoadReadBarrierMarkIntrospectionEntrypoint(arm64::Arm64Assembler& assembler, - vixl::aarch64::Register entrypoint) { - // entrypoint = Thread::Current()->pReadBarrierMarkReg16, i.e. pReadBarrierMarkIntrospection. - DCHECK_EQ(ip0.GetCode(), 16u); - const int32_t entry_point_offset = - Thread::ReadBarrierMarkEntryPointsOffset(ip0.GetCode()); - __ Ldr(entrypoint, MemOperand(tr, entry_point_offset)); -} - -void CodeGeneratorARM64::CompileBakerReadBarrierThunk(Arm64Assembler& assembler, - uint32_t encoded_data, - /*out*/ std::string* debug_name) { - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - switch (kind) { - case BakerReadBarrierKind::kField: { - // Check if the holder is gray and, if not, add fake dependency to the base register - // and return to the LDR instruction to load the reference. Otherwise, use introspection - // to load the reference and call the entrypoint (in IP1) that performs further checks - // on the reference and marks it if needed. - auto base_reg = - Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(base_reg.GetCode()); - auto holder_reg = - Register::GetXRegFromCode(BakerReadBarrierSecondRegField::Decode(encoded_data)); - CheckValidReg(holder_reg.GetCode()); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip0, ip1); - // If base_reg differs from holder_reg, the offset was too large and we must have - // emitted an explicit null check before the load. Otherwise, we need to null-check - // the holder as we do not necessarily do that check before going to the thunk. - vixl::aarch64::Label throw_npe; - if (holder_reg.Is(base_reg)) { - __ Cbz(holder_reg.W(), &throw_npe); - } - vixl::aarch64::Label slow_path; - MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value()); - EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path); - __ Bind(&slow_path); - MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET); - __ Ldr(ip0.W(), ldr_address); // Load the LDR (immediate) unsigned offset. - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); - __ Ubfx(ip0.W(), ip0.W(), 10, 12); // Extract the offset. - __ Ldr(ip0.W(), MemOperand(base_reg, ip0, LSL, 2)); // Load the reference. - // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference. - __ Br(ip1); // Jump to the entrypoint. - if (holder_reg.Is(base_reg)) { - // Add null check slow path. The stack map is at the address pointed to by LR. - __ Bind(&throw_npe); - int32_t offset = GetThreadOffset(kQuickThrowNullPointer).Int32Value(); - __ Ldr(ip0, MemOperand(/* Thread* */ vixl::aarch64::x19, offset)); - __ Br(ip0); - } - break; - } - case BakerReadBarrierKind::kArray: { - auto base_reg = - Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(base_reg.GetCode()); - DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, - BakerReadBarrierSecondRegField::Decode(encoded_data)); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip0, ip1); - vixl::aarch64::Label slow_path; - int32_t data_offset = - mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value(); - MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset); - DCHECK_LT(lock_word.GetOffset(), 0); - EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path); - __ Bind(&slow_path); - MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET); - __ Ldr(ip0.W(), ldr_address); // Load the LDR (register) unsigned offset. - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); - __ Ubfx(ip0, ip0, 16, 6); // Extract the index register, plus 32 (bit 21 is set). - __ Bfi(ip1, ip0, 3, 6); // Insert ip0 to the entrypoint address to create - // a switch case target based on the index register. - __ Mov(ip0, base_reg); // Move the base register to ip0. - __ Br(ip1); // Jump to the entrypoint's array switch case. - break; - } - case BakerReadBarrierKind::kGcRoot: { - // Check if the reference needs to be marked and if so (i.e. not null, not marked yet - // and it does not have a forwarding address), call the correct introspection entrypoint; - // otherwise return the reference (or the extracted forwarding address). - // There is no gray bit check for GC roots. - auto root_reg = - Register::GetWRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(root_reg.GetCode()); - DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, - BakerReadBarrierSecondRegField::Decode(encoded_data)); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip0, ip1); - vixl::aarch64::Label return_label, not_marked, forwarding_address; - __ Cbz(root_reg, &return_label); - MemOperand lock_word(root_reg.X(), mirror::Object::MonitorOffset().Int32Value()); - __ Ldr(ip0.W(), lock_word); - __ Tbz(ip0.W(), LockWord::kMarkBitStateShift, ¬_marked); - __ Bind(&return_label); - __ Br(lr); - __ Bind(¬_marked); - __ Tst(ip0.W(), Operand(ip0.W(), LSL, 1)); - __ B(&forwarding_address, mi); - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); - // Adjust the art_quick_read_barrier_mark_introspection address in IP1 to - // art_quick_read_barrier_mark_introspection_gc_roots. - __ Add(ip1, ip1, Operand(BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRYPOINT_OFFSET)); - __ Mov(ip0.W(), root_reg); - __ Br(ip1); - __ Bind(&forwarding_address); - __ Lsl(root_reg, ip0.W(), LockWord::kForwardingAddressShift); - __ Br(lr); - break; - } - default: - LOG(FATAL) << "Unexpected kind: " << static_cast(kind); - UNREACHABLE(); - } - - if (GetCompilerOptions().GenerateAnyDebugInfo()) { - std::ostringstream oss; - oss << "BakerReadBarrierThunk"; - switch (kind) { - case BakerReadBarrierKind::kField: - oss << "Field_r" << BakerReadBarrierFirstRegField::Decode(encoded_data) - << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data); - break; - case BakerReadBarrierKind::kArray: - oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); - DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, - BakerReadBarrierSecondRegField::Decode(encoded_data)); - break; - case BakerReadBarrierKind::kGcRoot: - oss << "GcRoot_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); - DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, - BakerReadBarrierSecondRegField::Decode(encoded_data)); - break; - } - *debug_name = oss.str(); - } -} - -#undef __ - } // namespace arm64 } // namespace art diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index aa343b1185..6a52eecbd3 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -18,7 +18,6 @@ #define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_ #include "arch/arm64/quick_method_frame_info_arm64.h" -#include "base/bit_field.h" #include "code_generator.h" #include "common_arm64.h" #include "dex/dex_file_types.h" @@ -37,11 +36,6 @@ #pragma GCC diagnostic pop namespace art { - -namespace linker { -class Arm64RelativePatcherTest; -} // namespace linker - namespace arm64 { class CodeGeneratorARM64; @@ -315,6 +309,17 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { uint32_t offset, Location maybe_temp, ReadBarrierOption read_barrier_option); + // Generate a GC root reference load: + // + // root <- *(obj + offset) + // + // while honoring read barriers based on read_barrier_option. + void GenerateGcRootFieldLoad(HInstruction* instruction, + Location root, + vixl::aarch64::Register obj, + uint32_t offset, + vixl::aarch64::Label* fixup_label, + ReadBarrierOption read_barrier_option); // Generate a floating-point comparison. void GenerateFcmp(HInstruction* instruction); @@ -636,24 +641,9 @@ class CodeGeneratorARM64 : public CodeGenerator { vixl::aarch64::Register base); void EmitLinkerPatches(ArenaVector* linker_patches) OVERRIDE; - bool NeedsThunkCode(const linker::LinkerPatch& patch) const OVERRIDE; - void EmitThunkCode(const linker::LinkerPatch& patch, - /*out*/ ArenaVector* code, - /*out*/ std::string* debug_name) OVERRIDE; void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; - // Generate a GC root reference load: - // - // root <- *(obj + offset) - // - // while honoring read barriers based on read_barrier_option. - void GenerateGcRootFieldLoad(HInstruction* instruction, - Location root, - vixl::aarch64::Register obj, - uint32_t offset, - vixl::aarch64::Label* fixup_label, - ReadBarrierOption read_barrier_option); // Fast path implementation of ReadBarrier::Barrier for a heap // reference field load when Baker's read barriers are used. void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -788,62 +778,6 @@ class CodeGeneratorARM64 : public CodeGenerator { void GenerateExplicitNullCheck(HNullCheck* instruction) OVERRIDE; private: - // Encoding of thunk type and data for link-time generated thunks for Baker read barriers. - - enum class BakerReadBarrierKind : uint8_t { - kField, // Field get or array get with constant offset (i.e. constant index). - kArray, // Array get with index in register. - kGcRoot, // GC root load. - kLast = kGcRoot - }; - - static constexpr uint32_t kBakerReadBarrierInvalidEncodedReg = /* sp/zr is invalid */ 31u; - - static constexpr size_t kBitsForBakerReadBarrierKind = - MinimumBitsToStore(static_cast(BakerReadBarrierKind::kLast)); - static constexpr size_t kBakerReadBarrierBitsForRegister = - MinimumBitsToStore(kBakerReadBarrierInvalidEncodedReg); - using BakerReadBarrierKindField = - BitField; - using BakerReadBarrierFirstRegField = - BitField; - using BakerReadBarrierSecondRegField = - BitField; - - static void CheckValidReg(uint32_t reg) { - DCHECK(reg < vixl::aarch64::lr.GetCode() && - reg != vixl::aarch64::ip0.GetCode() && - reg != vixl::aarch64::ip1.GetCode()) << reg; - } - - static inline uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, uint32_t holder_reg) { - CheckValidReg(base_reg); - CheckValidReg(holder_reg); - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kField) | - BakerReadBarrierFirstRegField::Encode(base_reg) | - BakerReadBarrierSecondRegField::Encode(holder_reg); - } - - static inline uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { - CheckValidReg(base_reg); - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kArray) | - BakerReadBarrierFirstRegField::Encode(base_reg) | - BakerReadBarrierSecondRegField::Encode(kBakerReadBarrierInvalidEncodedReg); - } - - static inline uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg) { - CheckValidReg(root_reg); - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kGcRoot) | - BakerReadBarrierFirstRegField::Encode(root_reg) | - BakerReadBarrierSecondRegField::Encode(kBakerReadBarrierInvalidEncodedReg); - } - - void CompileBakerReadBarrierThunk(Arm64Assembler& assembler, - uint32_t encoded_data, - /*out*/ std::string* debug_name); - using Uint64ToLiteralMap = ArenaSafeMap*>; using Uint32ToLiteralMap = ArenaSafeMap*>; using StringToLiteralMap = ArenaSafeMapIsAvailable(ip)); temps->Exclude(ip); DCHECK(!temps->IsAvailable(kBakerCcEntrypointRegister)); + DCHECK_EQ(kBakerCcEntrypointRegister.GetCode(), + linker::Thumb2RelativePatcher::kBakerCcEntrypointRegister); DCHECK_NE(instruction->GetLocations()->GetTempCount(), 0u); DCHECK(RegisterFrom(instruction->GetLocations()->GetTemp( instruction->GetLocations()->GetTempCount() - 1u)).Is(kBakerCcEntrypointRegister)); @@ -2417,80 +2422,6 @@ void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) { FixJumpTables(); GetAssembler()->FinalizeCode(); CodeGenerator::Finalize(allocator); - - // Verify Baker read barrier linker patches. - if (kIsDebugBuild) { - ArrayRef code = allocator->GetMemory(); - for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) { - DCHECK(info.label.IsBound()); - uint32_t literal_offset = info.label.GetLocation(); - DCHECK_ALIGNED(literal_offset, 2u); - - auto GetInsn16 = [&code](uint32_t offset) { - DCHECK_ALIGNED(offset, 2u); - return (static_cast(code[offset + 0]) << 0) + - (static_cast(code[offset + 1]) << 8); - }; - auto GetInsn32 = [=](uint32_t offset) { - return (GetInsn16(offset) << 16) + (GetInsn16(offset + 2u) << 0); - }; - - uint32_t encoded_data = info.custom_data; - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - // Check that the next instruction matches the expected LDR. - switch (kind) { - case BakerReadBarrierKind::kField: { - BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); - if (width == BakerReadBarrierWidth::kWide) { - DCHECK_GE(code.size() - literal_offset, 8u); - uint32_t next_insn = GetInsn32(literal_offset + 4u); - // LDR (immediate), encoding T3, with correct base_reg. - CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xffff0000u, 0xf8d00000u | (base_reg << 16)); - } else { - DCHECK_GE(code.size() - literal_offset, 6u); - uint32_t next_insn = GetInsn16(literal_offset + 4u); - // LDR (immediate), encoding T1, with correct base_reg. - CheckValidReg(next_insn & 0x7u); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xf838u, 0x6800u | (base_reg << 3)); - } - break; - } - case BakerReadBarrierKind::kArray: { - DCHECK_GE(code.size() - literal_offset, 8u); - uint32_t next_insn = GetInsn32(literal_offset + 4u); - // LDR (register) with correct base_reg, S=1 and option=011 (LDR Wt, [Xn, Xm, LSL #2]). - CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xffff0ff0u, 0xf8500020u | (base_reg << 16)); - CheckValidReg(next_insn & 0xf); // Check index register - break; - } - case BakerReadBarrierKind::kGcRoot: { - BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); - if (width == BakerReadBarrierWidth::kWide) { - DCHECK_GE(literal_offset, 4u); - uint32_t prev_insn = GetInsn32(literal_offset - 4u); - // LDR (immediate), encoding T3, with correct root_reg. - const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(prev_insn & 0xfff0f000u, 0xf8d00000u | (root_reg << 12)); - } else { - DCHECK_GE(literal_offset, 2u); - uint32_t prev_insn = GetInsn16(literal_offset - 2u); - // LDR (immediate), encoding T1, with correct root_reg. - const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(prev_insn & 0xf807u, 0x6800u | root_reg); - } - break; - } - default: - LOG(FATAL) << "Unexpected kind: " << static_cast(kind); - UNREACHABLE(); - } - } - } } void CodeGeneratorARMVIXL::SetupBlockedRegisters() const { @@ -7482,11 +7413,11 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ DCHECK(!cls->MustGenerateClinitCheck()); // /* GcRoot */ out = current_method->declaring_class_ vixl32::Register current_method = InputRegisterAt(cls, 0); - codegen_->GenerateGcRootFieldLoad(cls, - out_loc, - current_method, - ArtMethod::DeclaringClassOffset().Int32Value(), - read_barrier_option); + GenerateGcRootFieldLoad(cls, + out_loc, + current_method, + ArtMethod::DeclaringClassOffset().Int32Value(), + read_barrier_option); break; } case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { @@ -7517,7 +7448,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); codegen_->EmitMovwMovtPlaceholder(labels, out); - codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); + GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); generate_null_check = true; break; } @@ -7526,7 +7457,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ cls->GetTypeIndex(), cls->GetClass())); // /* GcRoot */ out = *out - codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); + GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); break; } case HLoadClass::LoadKind::kRuntimeCall: @@ -7734,8 +7665,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex()); codegen_->EmitMovwMovtPlaceholder(labels, out); - codegen_->GenerateGcRootFieldLoad( - load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); + GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); LoadStringSlowPathARMVIXL* slow_path = new (codegen_->GetScopedAllocator()) LoadStringSlowPathARMVIXL(load); codegen_->AddSlowPath(slow_path); @@ -7749,8 +7679,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE load->GetStringIndex(), load->GetString())); // /* GcRoot */ out = *out - codegen_->GenerateGcRootFieldLoad( - load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); + GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); return; } default: @@ -8801,7 +8730,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadTwoRegisters( } } -void CodeGeneratorARMVIXL::GenerateGcRootFieldLoad( +void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( HInstruction* instruction, Location root, vixl32::Register obj, @@ -8832,8 +8761,9 @@ void CodeGeneratorARMVIXL::GenerateGcRootFieldLoad( UseScratchRegisterScope temps(GetVIXLAssembler()); ExcludeIPAndBakerCcEntrypointRegister(&temps, instruction); bool narrow = CanEmitNarrowLdr(root_reg, obj, offset); - uint32_t custom_data = EncodeBakerReadBarrierGcRootData(root_reg.GetCode(), narrow); - vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data); + uint32_t custom_data = linker::Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData( + root_reg.GetCode(), narrow); + vixl32::Label* bne_label = codegen_->NewBakerReadBarrierPatch(custom_data); vixl::EmissionCheckScope guard(GetVIXLAssembler(), 4 * vixl32::kMaxInstructionSizeInBytes); vixl32::Label return_address; @@ -8844,7 +8774,7 @@ void CodeGeneratorARMVIXL::GenerateGcRootFieldLoad( DCHECK_LT(offset, kReferenceLoadMinFarOffset); ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset(); __ ldr(EncodingSize(narrow ? Narrow : Wide), root_reg, MemOperand(obj, offset)); - EmitPlaceholderBne(this, bne_label); + EmitPlaceholderBne(codegen_, bne_label); __ Bind(&return_address); DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(), narrow ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET @@ -8864,8 +8794,8 @@ void CodeGeneratorARMVIXL::GenerateGcRootFieldLoad( // Slow path marking the GC root `root`. The entrypoint will // be loaded by the slow path code. SlowPathCodeARMVIXL* slow_path = - new (GetScopedAllocator()) ReadBarrierMarkSlowPathARMVIXL(instruction, root); - AddSlowPath(slow_path); + new (codegen_->GetScopedAllocator()) ReadBarrierMarkSlowPathARMVIXL(instruction, root); + codegen_->AddSlowPath(slow_path); // /* GcRoot */ root = *(obj + offset) GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset); @@ -8886,7 +8816,7 @@ void CodeGeneratorARMVIXL::GenerateGcRootFieldLoad( // /* GcRoot* */ root = obj + offset __ Add(root_reg, obj, offset); // /* mirror::Object* */ root = root->Read() - GenerateReadBarrierForRootSlow(instruction, root, root); + codegen_->GenerateReadBarrierForRootSlow(instruction, root, root); } } else { // Plain GC root load with no read barrier. @@ -8895,7 +8825,7 @@ void CodeGeneratorARMVIXL::GenerateGcRootFieldLoad( // Note that GC roots are not affected by heap poisoning, thus we // do not have to unpoison `root_reg` here. } - MaybeGenerateMarkingRegisterCheck(/* code */ 18); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 18); } void CodeGeneratorARMVIXL::MaybeAddBakerCcEntrypointTempForFields(LocationSummary* locations) { @@ -8956,7 +8886,8 @@ void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* i } UseScratchRegisterScope temps(GetVIXLAssembler()); ExcludeIPAndBakerCcEntrypointRegister(&temps, instruction); - uint32_t custom_data = EncodeBakerReadBarrierFieldData(base.GetCode(), obj.GetCode(), narrow); + uint32_t custom_data = linker::Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( + base.GetCode(), obj.GetCode(), narrow); vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data); { @@ -9042,7 +8973,8 @@ void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(HInstruction* i UseScratchRegisterScope temps(GetVIXLAssembler()); ExcludeIPAndBakerCcEntrypointRegister(&temps, instruction); - uint32_t custom_data = EncodeBakerReadBarrierArrayData(data_reg.GetCode()); + uint32_t custom_data = + linker::Thumb2RelativePatcher::EncodeBakerReadBarrierArrayData(data_reg.GetCode()); vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data); __ Add(data_reg, obj, Operand(data_offset)); @@ -9179,7 +9111,7 @@ void CodeGeneratorARMVIXL::UpdateReferenceFieldWithBakerReadBarrier(HInstruction void CodeGeneratorARMVIXL::GenerateRawReferenceLoad(HInstruction* instruction, Location ref, - vixl32::Register obj, + vixl::aarch32::Register obj, uint32_t offset, Location index, ScaleFactor scale_factor, @@ -9519,7 +9451,7 @@ CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativePa return &patches->back(); } -vixl32::Label* CodeGeneratorARMVIXL::NewBakerReadBarrierPatch(uint32_t custom_data) { +vixl::aarch32::Label* CodeGeneratorARMVIXL::NewBakerReadBarrierPatch(uint32_t custom_data) { baker_read_barrier_patches_.emplace_back(custom_data); return &baker_read_barrier_patches_.back().label; } @@ -9616,45 +9548,6 @@ void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector* l DCHECK_EQ(size, linker_patches->size()); } -bool CodeGeneratorARMVIXL::NeedsThunkCode(const linker::LinkerPatch& patch) const { - return patch.GetType() == linker::LinkerPatch::Type::kBakerReadBarrierBranch || - patch.GetType() == linker::LinkerPatch::Type::kCallRelative; -} - -void CodeGeneratorARMVIXL::EmitThunkCode(const linker::LinkerPatch& patch, - /*out*/ ArenaVector* code, - /*out*/ std::string* debug_name) { - arm::ArmVIXLAssembler assembler(GetGraph()->GetAllocator()); - switch (patch.GetType()) { - case linker::LinkerPatch::Type::kCallRelative: - // The thunk just uses the entry point in the ArtMethod. This works even for calls - // to the generic JNI and interpreter trampolines. - assembler.LoadFromOffset( - arm::kLoadWord, - vixl32::pc, - vixl32::r0, - ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value()); - assembler.GetVIXLAssembler()->Bkpt(0); - if (GetCompilerOptions().GenerateAnyDebugInfo()) { - *debug_name = "MethodCallThunk"; - } - break; - case linker::LinkerPatch::Type::kBakerReadBarrierBranch: - DCHECK_EQ(patch.GetBakerCustomValue2(), 0u); - CompileBakerReadBarrierThunk(assembler, patch.GetBakerCustomValue1(), debug_name); - break; - default: - LOG(FATAL) << "Unexpected patch type " << patch.GetType(); - UNREACHABLE(); - } - - // Ensure we emit the literal pool if any. - assembler.FinalizeCode(); - code->resize(assembler.CodeSize()); - MemoryRegion code_region(code->data(), code->size()); - assembler.FinalizeInstructions(code_region); -} - VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateUint32Literal( uint32_t value, Uint32ToLiteralMap* map) { @@ -9899,210 +9792,5 @@ void CodeGeneratorARMVIXL::EmitMovwMovtPlaceholder( #undef QUICK_ENTRY_POINT #undef TODO_VIXL32 -#define __ assembler.GetVIXLAssembler()-> - -static void EmitGrayCheckAndFastPath(ArmVIXLAssembler& assembler, - vixl32::Register base_reg, - vixl32::MemOperand& lock_word, - vixl32::Label* slow_path, - int32_t raw_ldr_offset) { - // Load the lock word containing the rb_state. - __ Ldr(ip, lock_word); - // Given the numeric representation, it's enough to check the low bit of the rb_state. - static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0"); - static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1"); - __ Tst(ip, Operand(LockWord::kReadBarrierStateMaskShifted)); - __ B(ne, slow_path, /* is_far_target */ false); - __ Add(lr, lr, raw_ldr_offset); - // Introduce a dependency on the lock_word including rb_state, - // to prevent load-load reordering, and without using - // a memory barrier (which would be more expensive). - __ Add(base_reg, base_reg, Operand(ip, LSR, 32)); - __ Bx(lr); // And return back to the function. - // Note: The fake dependency is unnecessary for the slow path. -} - -// Load the read barrier introspection entrypoint in register `entrypoint` -static void LoadReadBarrierMarkIntrospectionEntrypoint(ArmVIXLAssembler& assembler, - vixl32::Register entrypoint) { - // The register where the read barrier introspection entrypoint is loaded - // is fixed: `Thumb2RelativePatcher::kBakerCcEntrypointRegister` (R4). - DCHECK(entrypoint.Is(kBakerCcEntrypointRegister)); - // entrypoint = Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection. - DCHECK_EQ(ip.GetCode(), 12u); - const int32_t entry_point_offset = - Thread::ReadBarrierMarkEntryPointsOffset(ip.GetCode()); - __ Ldr(entrypoint, MemOperand(tr, entry_point_offset)); -} - -void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assembler, - uint32_t encoded_data, - /*out*/ std::string* debug_name) { - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - switch (kind) { - case BakerReadBarrierKind::kField: { - // Check if the holder is gray and, if not, add fake dependency to the base register - // and return to the LDR instruction to load the reference. Otherwise, use introspection - // to load the reference and call the entrypoint (in kBakerCcEntrypointRegister) - // that performs further checks on the reference and marks it if needed. - vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(base_reg.GetCode()); - vixl32::Register holder_reg(BakerReadBarrierSecondRegField::Decode(encoded_data)); - CheckValidReg(holder_reg.GetCode()); - BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip); - // If base_reg differs from holder_reg, the offset was too large and we must have - // emitted an explicit null check before the load. Otherwise, we need to null-check - // the holder as we do not necessarily do that check before going to the thunk. - vixl32::Label throw_npe; - if (holder_reg.Is(base_reg)) { - __ CompareAndBranchIfZero(holder_reg, &throw_npe, /* is_far_target */ false); - } - vixl32::Label slow_path; - MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value()); - const int32_t raw_ldr_offset = (width == BakerReadBarrierWidth::kWide) - ? BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET - : BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET; - EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset); - __ Bind(&slow_path); - const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 + - raw_ldr_offset; - vixl32::Register ep_reg(kBakerCcEntrypointRegister); - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); - if (width == BakerReadBarrierWidth::kWide) { - MemOperand ldr_half_address(lr, ldr_offset + 2); - __ Ldrh(ip, ldr_half_address); // Load the LDR immediate half-word with "Rt | imm12". - __ Ubfx(ip, ip, 0, 12); // Extract the offset imm12. - __ Ldr(ip, MemOperand(base_reg, ip)); // Load the reference. - } else { - MemOperand ldr_address(lr, ldr_offset); - __ Ldrh(ip, ldr_address); // Load the LDR immediate, encoding T1. - __ Add(ep_reg, // Adjust the entrypoint address to the entrypoint - ep_reg, // for narrow LDR. - Operand(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET)); - __ Ubfx(ip, ip, 6, 5); // Extract the imm5, i.e. offset / 4. - __ Ldr(ip, MemOperand(base_reg, ip, LSL, 2)); // Load the reference. - } - // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference. - __ Bx(ep_reg); // Jump to the entrypoint. - if (holder_reg.Is(base_reg)) { - // Add null check slow path. The stack map is at the address pointed to by LR. - __ Bind(&throw_npe); - int32_t offset = GetThreadOffset(kQuickThrowNullPointer).Int32Value(); - __ Ldr(ip, MemOperand(/* Thread* */ vixl32::r9, offset)); - __ Bx(ip); - } - break; - } - case BakerReadBarrierKind::kArray: { - vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(base_reg.GetCode()); - DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, - BakerReadBarrierSecondRegField::Decode(encoded_data)); - DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip); - vixl32::Label slow_path; - int32_t data_offset = - mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value(); - MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset); - DCHECK_LT(lock_word.GetOffsetImmediate(), 0); - const int32_t raw_ldr_offset = BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET; - EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset); - __ Bind(&slow_path); - const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 + - raw_ldr_offset; - MemOperand ldr_address(lr, ldr_offset + 2); - __ Ldrb(ip, ldr_address); // Load the LDR (register) byte with "00 | imm2 | Rm", - // i.e. Rm+32 because the scale in imm2 is 2. - vixl32::Register ep_reg(kBakerCcEntrypointRegister); - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); - __ Bfi(ep_reg, ip, 3, 6); // Insert ip to the entrypoint address to create - // a switch case target based on the index register. - __ Mov(ip, base_reg); // Move the base register to ip0. - __ Bx(ep_reg); // Jump to the entrypoint's array switch case. - break; - } - case BakerReadBarrierKind::kGcRoot: { - // Check if the reference needs to be marked and if so (i.e. not null, not marked yet - // and it does not have a forwarding address), call the correct introspection entrypoint; - // otherwise return the reference (or the extracted forwarding address). - // There is no gray bit check for GC roots. - vixl32::Register root_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(root_reg.GetCode()); - DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, - BakerReadBarrierSecondRegField::Decode(encoded_data)); - BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip); - vixl32::Label return_label, not_marked, forwarding_address; - __ CompareAndBranchIfZero(root_reg, &return_label, /* is_far_target */ false); - MemOperand lock_word(root_reg, mirror::Object::MonitorOffset().Int32Value()); - __ Ldr(ip, lock_word); - __ Tst(ip, LockWord::kMarkBitStateMaskShifted); - __ B(eq, ¬_marked); - __ Bind(&return_label); - __ Bx(lr); - __ Bind(¬_marked); - static_assert(LockWord::kStateShift == 30 && LockWord::kStateForwardingAddress == 3, - "To use 'CMP ip, #modified-immediate; BHS', we need the lock word state in " - " the highest bits and the 'forwarding address' state to have all bits set"); - __ Cmp(ip, Operand(0xc0000000)); - __ B(hs, &forwarding_address); - vixl32::Register ep_reg(kBakerCcEntrypointRegister); - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); - // Adjust the art_quick_read_barrier_mark_introspection address in kBakerCcEntrypointRegister - // to art_quick_read_barrier_mark_introspection_gc_roots. - int32_t entrypoint_offset = (width == BakerReadBarrierWidth::kWide) - ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET - : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET; - __ Add(ep_reg, ep_reg, Operand(entrypoint_offset)); - __ Mov(ip, root_reg); - __ Bx(ep_reg); - __ Bind(&forwarding_address); - __ Lsl(root_reg, ip, LockWord::kForwardingAddressShift); - __ Bx(lr); - break; - } - default: - LOG(FATAL) << "Unexpected kind: " << static_cast(kind); - UNREACHABLE(); - } - - if (GetCompilerOptions().GenerateAnyDebugInfo()) { - std::ostringstream oss; - oss << "BakerReadBarrierThunk"; - switch (kind) { - case BakerReadBarrierKind::kField: - oss << "Field"; - if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) { - oss << "Wide"; - } - oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data) - << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data); - break; - case BakerReadBarrierKind::kArray: - oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); - DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, - BakerReadBarrierSecondRegField::Decode(encoded_data)); - DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide); - break; - case BakerReadBarrierKind::kGcRoot: - oss << "GcRoot"; - if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) { - oss << "Wide"; - } - oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); - DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, - BakerReadBarrierSecondRegField::Decode(encoded_data)); - break; - } - *debug_name = oss.str(); - } -} - -#undef __ - } // namespace arm } // namespace art diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 6b9919ab15..2114ea1ba1 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -36,11 +36,6 @@ #pragma GCC diagnostic pop namespace art { - -namespace linker { -class Thumb2RelativePatcherTest; -} // namespace linker - namespace arm { // This constant is used as an approximate margin when emission of veneer and literal pools @@ -113,9 +108,6 @@ static const vixl::aarch32::SRegister kRuntimeParameterFpuRegistersVIXL[] = { static const size_t kRuntimeParameterFpuRegistersLengthVIXL = arraysize(kRuntimeParameterFpuRegistersVIXL); -// The reserved entrypoint register for link-time generated thunks. -const vixl::aarch32::Register kBakerCcEntrypointRegister = vixl32::r4; - class LoadClassSlowPathARMVIXL; class CodeGeneratorARMVIXL; @@ -396,6 +388,16 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { uint32_t offset, Location maybe_temp, ReadBarrierOption read_barrier_option); + // Generate a GC root reference load: + // + // root <- *(obj + offset) + // + // while honoring read barriers based on read_barrier_option. + void GenerateGcRootFieldLoad(HInstruction* instruction, + Location root, + vixl::aarch32::Register obj, + uint32_t offset, + ReadBarrierOption read_barrier_option); void GenerateTestAndBranch(HInstruction* instruction, size_t condition_input_index, vixl::aarch32::Label* true_target, @@ -604,10 +606,6 @@ class CodeGeneratorARMVIXL : public CodeGenerator { Handle handle); void EmitLinkerPatches(ArenaVector* linker_patches) OVERRIDE; - bool NeedsThunkCode(const linker::LinkerPatch& patch) const OVERRIDE; - void EmitThunkCode(const linker::LinkerPatch& patch, - /*out*/ ArenaVector* code, - /*out*/ std::string* debug_name) OVERRIDE; void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; @@ -615,16 +613,6 @@ class CodeGeneratorARMVIXL : public CodeGenerator { // is added only for AOT compilation if link-time generated thunks for fields are enabled. void MaybeAddBakerCcEntrypointTempForFields(LocationSummary* locations); - // Generate a GC root reference load: - // - // root <- *(obj + offset) - // - // while honoring read barriers based on read_barrier_option. - void GenerateGcRootFieldLoad(HInstruction* instruction, - Location root, - vixl::aarch32::Register obj, - uint32_t offset, - ReadBarrierOption read_barrier_option); // Fast path implementation of ReadBarrier::Barrier for a heap // reference field load when Baker's read barriers are used. void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -779,83 +767,6 @@ class CodeGeneratorARMVIXL : public CodeGenerator { vixl::aarch32::Register temp = vixl32::Register()); private: - // Encoding of thunk type and data for link-time generated thunks for Baker read barriers. - - enum class BakerReadBarrierKind : uint8_t { - kField, // Field get or array get with constant offset (i.e. constant index). - kArray, // Array get with index in register. - kGcRoot, // GC root load. - kLast = kGcRoot - }; - - enum class BakerReadBarrierWidth : uint8_t { - kWide, // 32-bit LDR (and 32-bit NEG if heap poisoning is enabled). - kNarrow, // 16-bit LDR (and 16-bit NEG if heap poisoning is enabled). - kLast = kNarrow - }; - - static constexpr uint32_t kBakerReadBarrierInvalidEncodedReg = /* pc is invalid */ 15u; - - static constexpr size_t kBitsForBakerReadBarrierKind = - MinimumBitsToStore(static_cast(BakerReadBarrierKind::kLast)); - static constexpr size_t kBakerReadBarrierBitsForRegister = - MinimumBitsToStore(kBakerReadBarrierInvalidEncodedReg); - using BakerReadBarrierKindField = - BitField; - using BakerReadBarrierFirstRegField = - BitField; - using BakerReadBarrierSecondRegField = - BitField; - static constexpr size_t kBitsForBakerReadBarrierWidth = - MinimumBitsToStore(static_cast(BakerReadBarrierWidth::kLast)); - using BakerReadBarrierWidthField = - BitField; - - static void CheckValidReg(uint32_t reg) { - DCHECK(reg < vixl::aarch32::ip.GetCode() && reg != kBakerCcEntrypointRegister.GetCode()) << reg; - } - - static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, - uint32_t holder_reg, - bool narrow) { - CheckValidReg(base_reg); - CheckValidReg(holder_reg); - DCHECK(!narrow || base_reg < 8u) << base_reg; - BakerReadBarrierWidth width = - narrow ? BakerReadBarrierWidth::kNarrow : BakerReadBarrierWidth::kWide; - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kField) | - BakerReadBarrierFirstRegField::Encode(base_reg) | - BakerReadBarrierSecondRegField::Encode(holder_reg) | - BakerReadBarrierWidthField::Encode(width); - } - - static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { - CheckValidReg(base_reg); - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kArray) | - BakerReadBarrierFirstRegField::Encode(base_reg) | - BakerReadBarrierSecondRegField::Encode(kBakerReadBarrierInvalidEncodedReg) | - BakerReadBarrierWidthField::Encode(BakerReadBarrierWidth::kWide); - } - - static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg, bool narrow) { - CheckValidReg(root_reg); - DCHECK(!narrow || root_reg < 8u) << root_reg; - BakerReadBarrierWidth width = - narrow ? BakerReadBarrierWidth::kNarrow : BakerReadBarrierWidth::kWide; - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kGcRoot) | - BakerReadBarrierFirstRegField::Encode(root_reg) | - BakerReadBarrierSecondRegField::Encode(kBakerReadBarrierInvalidEncodedReg) | - BakerReadBarrierWidthField::Encode(width); - } - - void CompileBakerReadBarrierThunk(ArmVIXLAssembler& assembler, - uint32_t encoded_data, - /*out*/ std::string* debug_name); - vixl::aarch32::Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, vixl::aarch32::Register temp); @@ -918,7 +829,6 @@ class CodeGeneratorARMVIXL : public CodeGenerator { // Patches for class literals in JIT compiled code. TypeToLiteralMap jit_class_patches_; - friend class linker::Thumb2RelativePatcherTest; DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARMVIXL); }; diff --git a/compiler/optimizing/codegen_test_utils.h b/compiler/optimizing/codegen_test_utils.h index 792cfb539a..c41c290c8b 100644 --- a/compiler/optimizing/codegen_test_utils.h +++ b/compiler/optimizing/codegen_test_utils.h @@ -195,9 +195,7 @@ class InternalCodeAllocator : public CodeAllocator { } size_t GetSize() const { return size_; } - ArrayRef GetMemory() const OVERRIDE { - return ArrayRef(memory_.get(), size_); - } + uint8_t* GetMemory() const { return memory_.get(); } private: size_t size_; @@ -271,8 +269,8 @@ static void Run(const InternalCodeAllocator& allocator, InstructionSet target_isa = codegen.GetInstructionSet(); typedef Expected (*fptr)(); - CommonCompilerTest::MakeExecutable(allocator.GetMemory().data(), allocator.GetMemory().size()); - fptr f = reinterpret_cast(reinterpret_cast(allocator.GetMemory().data())); + CommonCompilerTest::MakeExecutable(allocator.GetMemory(), allocator.GetSize()); + fptr f = reinterpret_cast(allocator.GetMemory()); if (target_isa == InstructionSet::kThumb2) { // For thumb we need the bottom bit set. f = reinterpret_cast(reinterpret_cast(f) + 1); diff --git a/compiler/optimizing/optimizing_cfi_test.cc b/compiler/optimizing/optimizing_cfi_test.cc index 2e189fdd14..d20b681b49 100644 --- a/compiler/optimizing/optimizing_cfi_test.cc +++ b/compiler/optimizing/optimizing_cfi_test.cc @@ -105,15 +105,15 @@ class OptimizingCFITest : public CFITest, public OptimizingUnitTestHelper { const std::vector& expected_asm, const std::vector& expected_cfi) { // Get the outputs. - ArrayRef actual_asm = code_allocator_.GetMemory(); + const std::vector& actual_asm = code_allocator_.GetMemory(); Assembler* opt_asm = code_gen_->GetAssembler(); - ArrayRef actual_cfi(*(opt_asm->cfi().data())); + const std::vector& actual_cfi = *(opt_asm->cfi().data()); if (kGenerateExpected) { GenerateExpected(stdout, isa, isa_str, actual_asm, actual_cfi); } else { - EXPECT_EQ(ArrayRef(expected_asm), actual_asm); - EXPECT_EQ(ArrayRef(expected_cfi), actual_cfi); + EXPECT_EQ(expected_asm, actual_asm); + EXPECT_EQ(expected_cfi, actual_cfi); } } @@ -140,7 +140,7 @@ class OptimizingCFITest : public CFITest, public OptimizingUnitTestHelper { return memory_.data(); } - ArrayRef GetMemory() const OVERRIDE { return ArrayRef(memory_); } + const std::vector& GetMemory() { return memory_; } private: std::vector memory_; diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 79165826d1..e42dfc10ba 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -75,18 +75,22 @@ static constexpr const char* kPassNameSeparator = "$"; class CodeVectorAllocator FINAL : public CodeAllocator { public: explicit CodeVectorAllocator(ArenaAllocator* allocator) - : memory_(allocator->Adapter(kArenaAllocCodeBuffer)) {} + : memory_(allocator->Adapter(kArenaAllocCodeBuffer)), + size_(0) {} virtual uint8_t* Allocate(size_t size) { + size_ = size; memory_.resize(size); return &memory_[0]; } - ArrayRef GetMemory() const OVERRIDE { return ArrayRef(memory_); } + size_t GetSize() const { return size_; } + const ArenaVector& GetMemory() const { return memory_; } uint8_t* GetData() { return memory_.data(); } private: ArenaVector memory_; + size_t size_; DISALLOW_COPY_AND_ASSIGN(CodeVectorAllocator); }; @@ -715,7 +719,7 @@ CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod( GetCompilerDriver(), codegen->GetInstructionSet(), - code_allocator->GetMemory(), + ArrayRef(code_allocator->GetMemory()), // Follow Quick's behavior and set the frame size to zero if it is // considered "empty" (see the definition of // art::CodeGenerator::HasEmptyFrame). @@ -727,16 +731,6 @@ CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, ArrayRef(*codegen->GetAssembler()->cfi().data()), ArrayRef(linker_patches)); - CompiledMethodStorage* storage = GetCompilerDriver()->GetCompiledMethodStorage(); - for (const linker::LinkerPatch& patch : linker_patches) { - if (codegen->NeedsThunkCode(patch) && storage->GetThunkCode(patch).empty()) { - ArenaVector code(allocator->Adapter()); - std::string debug_name; - codegen->EmitThunkCode(patch, &code, &debug_name); - storage->SetThunkCode(patch, ArrayRef(code), debug_name); - } - } - return compiled_method; } @@ -1345,7 +1339,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, codegen->GetCoreSpillMask(), codegen->GetFpuSpillMask(), code_allocator.GetMemory().data(), - code_allocator.GetMemory().size(), + code_allocator.GetSize(), data_size, osr, roots, @@ -1375,7 +1369,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, info.is_optimized = true; info.is_code_address_text_relative = false; info.code_address = code_address; - info.code_size = code_allocator.GetMemory().size(); + info.code_size = code_allocator.GetSize(); info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); info.code_info = stack_map_size == 0 ? nullptr : stack_map_data; info.cfi = ArrayRef(*codegen->GetAssembler()->cfi().data()); @@ -1384,7 +1378,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, Runtime::Current()->GetJit()->AddMemoryUsage(method, allocator.BytesUsed()); if (jit_logger != nullptr) { - jit_logger->WriteLog(code, code_allocator.GetMemory().size(), method); + jit_logger->WriteLog(code, code_allocator.GetSize(), method); } if (kArenaAllocatorCountAllocations) { diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 0c1d778c25..6950b93e51 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -2061,9 +2061,7 @@ class Dex2Oat FINAL { { TimingLogger::ScopedTiming t2("dex2oat Write ELF", timings_); - linker::MultiOatRelativePatcher patcher(instruction_set_, - instruction_set_features_.get(), - driver_->GetCompiledMethodStorage()); + linker::MultiOatRelativePatcher patcher(instruction_set_, instruction_set_features_.get()); for (size_t i = 0, size = oat_files_.size(); i != size; ++i) { std::unique_ptr& elf_writer = elf_writers_[i]; std::unique_ptr& oat_writer = oat_writers_[i]; diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index 476a843821..7449191984 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -299,8 +299,7 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, DCHECK_EQ(vdex_files.size(), oat_files.size()); for (size_t i = 0, size = oat_files.size(); i != size; ++i) { MultiOatRelativePatcher patcher(driver->GetInstructionSet(), - driver->GetInstructionSetFeatures(), - driver->GetCompiledMethodStorage()); + driver->GetInstructionSetFeatures()); OatWriter* const oat_writer = oat_writers[i].get(); ElfWriter* const elf_writer = elf_writers[i].get(); std::vector cur_dex_files(1u, class_path[i]); diff --git a/dex2oat/linker/multi_oat_relative_patcher.cc b/dex2oat/linker/multi_oat_relative_patcher.cc index 1449d478f9..1abaf7dfd1 100644 --- a/dex2oat/linker/multi_oat_relative_patcher.cc +++ b/dex2oat/linker/multi_oat_relative_patcher.cc @@ -20,28 +20,14 @@ #include "base/bit_utils.h" #include "globals.h" -#include "driver/compiled_method_storage.h" namespace art { namespace linker { -void MultiOatRelativePatcher::ThunkProvider::GetThunkCode(const LinkerPatch& patch, - /*out*/ ArrayRef* code, - /*out*/ std::string* debug_name) { - *code = storage_->GetThunkCode(patch, debug_name); - DCHECK(!code->empty()); -} - - MultiOatRelativePatcher::MultiOatRelativePatcher(InstructionSet instruction_set, - const InstructionSetFeatures* features, - CompiledMethodStorage* storage) - : thunk_provider_(storage), - method_offset_map_(), - relative_patcher_(RelativePatcher::Create(instruction_set, - features, - &thunk_provider_, - &method_offset_map_)), + const InstructionSetFeatures* features) + : method_offset_map_(), + relative_patcher_(RelativePatcher::Create(instruction_set, features, &method_offset_map_)), adjustment_(0u), instruction_set_(instruction_set), start_size_code_alignment_(0u), diff --git a/dex2oat/linker/multi_oat_relative_patcher.h b/dex2oat/linker/multi_oat_relative_patcher.h index 60fcfe8b58..bd33b95318 100644 --- a/dex2oat/linker/multi_oat_relative_patcher.h +++ b/dex2oat/linker/multi_oat_relative_patcher.h @@ -26,7 +26,6 @@ namespace art { class CompiledMethod; -class CompiledMethodStorage; class InstructionSetFeatures; namespace linker { @@ -39,9 +38,7 @@ class MultiOatRelativePatcher FINAL { public: using const_iterator = SafeMap::const_iterator; - MultiOatRelativePatcher(InstructionSet instruction_set, - const InstructionSetFeatures* features, - CompiledMethodStorage* storage); + MultiOatRelativePatcher(InstructionSet instruction_set, const InstructionSetFeatures* features); // Mark the start of a new oat file (for statistics retrieval) and set the // adjustment for a new oat file to apply to all relative offsets that are @@ -132,19 +129,6 @@ class MultiOatRelativePatcher FINAL { uint32_t MiscThunksSize() const; private: - class ThunkProvider : public RelativePatcherThunkProvider { - public: - explicit ThunkProvider(CompiledMethodStorage* storage) - : storage_(storage) {} - - void GetThunkCode(const LinkerPatch& patch, - /*out*/ ArrayRef* code, - /*out*/ std::string* debug_name) OVERRIDE; - - private: - CompiledMethodStorage* storage_; - }; - // Map method reference to assigned offset. // Wrap the map in a class implementing RelativePatcherTargetProvider. class MethodOffsetMap : public RelativePatcherTargetProvider { @@ -153,7 +137,6 @@ class MultiOatRelativePatcher FINAL { SafeMap map; }; - ThunkProvider thunk_provider_; MethodOffsetMap method_offset_map_; std::unique_ptr relative_patcher_; uint32_t adjustment_; diff --git a/dex2oat/linker/multi_oat_relative_patcher_test.cc b/dex2oat/linker/multi_oat_relative_patcher_test.cc index 05fe36a590..ca9c5f1e84 100644 --- a/dex2oat/linker/multi_oat_relative_patcher_test.cc +++ b/dex2oat/linker/multi_oat_relative_patcher_test.cc @@ -122,7 +122,7 @@ class MultiOatRelativePatcherTest : public testing::Test { MultiOatRelativePatcherTest() : instruction_set_features_(InstructionSetFeatures::FromCppDefines()), - patcher_(kRuntimeISA, instruction_set_features_.get(), /* storage */ nullptr) { + patcher_(kRuntimeISA, instruction_set_features_.get()) { std::unique_ptr mock(new MockPatcher()); mock_ = mock.get(); patcher_.relative_patcher_ = std::move(mock); diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index ea4e210b74..6e95393e80 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -213,8 +213,7 @@ class OatTest : public CommonCompilerTest { class_linker->RegisterDexFile(*dex_file, nullptr); } MultiOatRelativePatcher patcher(compiler_driver_->GetInstructionSet(), - instruction_set_features_.get(), - compiler_driver_->GetCompiledMethodStorage()); + instruction_set_features_.get()); oat_writer.Initialize(compiler_driver_.get(), nullptr, dex_files); oat_writer.PrepareLayout(&patcher); elf_writer->PrepareDynamicSection(oat_writer.GetOatHeader().GetExecutableOffset(), -- GitLab From 3215fff7ef8fa3c2250b91158560eacc613a4671 Mon Sep 17 00:00:00 2001 From: David Sehr Date: Tue, 3 Apr 2018 17:10:12 -0700 Subject: [PATCH 201/749] Separate Malloc and MemMap ArenaPools Make ArenaPool an abstract base class and leave MallocArenaPool implementation with it. This enables arena_allocator to be free of MemMap, Mutex, etc., in preparation to move the remaining collections out of runtime/base to libartbase/base. Bug: 22322814 Test: make -j 50 test-art-host build and boot Change-Id: Ief84dcbfb749165d9bc82000c6b8f96f93052422 --- compiler/exception_test.cc | 3 +- compiler/jni/jni_cfi_test.cc | 3 +- compiler/jni/quick/jni_compiler.cc | 3 +- .../linker/arm/relative_patcher_thumb2.cc | 3 +- .../linker/arm64/relative_patcher_arm64.cc | 3 +- compiler/optimizing/optimizing_unit_test.h | 3 +- compiler/optimizing/parallel_move_test.cc | 15 +- compiler/optimizing/stack_map_test.cc | 21 +- compiler/trampolines/trampoline_compiler.cc | 5 +- compiler/utils/assembler_test.h | 3 +- compiler/utils/assembler_thumb_test.cc | 3 +- compiler/utils/jni_macro_assembler_test.h | 3 +- compiler/utils/x86/assembler_x86_test.cc | 3 +- .../utils/x86_64/assembler_x86_64_test.cc | 3 +- runtime/Android.bp | 2 + runtime/base/arena_allocator.cc | 193 ------------------ runtime/base/arena_allocator.h | 33 ++- runtime/base/arena_allocator_test.cc | 41 ++-- runtime/base/malloc_arena_pool.cc | 162 +++++++++++++++ runtime/base/malloc_arena_pool.h | 48 +++++ runtime/base/mem_map_arena_pool.cc | 155 ++++++++++++++ runtime/base/mem_map_arena_pool.h | 49 +++++ runtime/jit/profile_compilation_info.cc | 3 +- runtime/jit/profile_compilation_info.h | 3 +- runtime/linear_alloc.h | 1 + runtime/runtime.cc | 14 +- runtime/verifier/register_line.h | 1 + 27 files changed, 511 insertions(+), 268 deletions(-) create mode 100644 runtime/base/malloc_arena_pool.cc create mode 100644 runtime/base/malloc_arena_pool.h create mode 100644 runtime/base/mem_map_arena_pool.cc create mode 100644 runtime/base/mem_map_arena_pool.h diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc index f582341b18..c139fcf1d8 100644 --- a/compiler/exception_test.cc +++ b/compiler/exception_test.cc @@ -20,6 +20,7 @@ #include "base/callee_save_type.h" #include "base/enums.h" #include "base/leb128.h" +#include "base/malloc_arena_pool.h" #include "class_linker.h" #include "common_runtime_test.h" #include "dex/code_item_accessors-inl.h" @@ -67,7 +68,7 @@ class ExceptionTest : public CommonRuntimeTest { fake_code_.push_back(0x70 | i); } - ArenaPool pool; + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stack_maps(&allocator, kRuntimeISA); diff --git a/compiler/jni/jni_cfi_test.cc b/compiler/jni/jni_cfi_test.cc index 236b5c0c2e..11b0e2ba9d 100644 --- a/compiler/jni/jni_cfi_test.cc +++ b/compiler/jni/jni_cfi_test.cc @@ -20,6 +20,7 @@ #include "arch/instruction_set.h" #include "base/arena_allocator.h" #include "base/enums.h" +#include "base/malloc_arena_pool.h" #include "cfi_test.h" #include "gtest/gtest.h" #include "jni/quick/calling_convention.h" @@ -61,7 +62,7 @@ class JNICFITest : public CFITest { const bool is_synchronized = false; const char* shorty = "IIFII"; - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); std::unique_ptr jni_conv( diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index d001cfe4fc..9ccdff3a17 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -27,6 +27,7 @@ #include "base/enums.h" #include "base/logging.h" // For VLOG. #include "base/macros.h" +#include "base/malloc_arena_pool.h" #include "base/utils.h" #include "calling_convention.h" #include "class_linker.h" @@ -174,7 +175,7 @@ static JniCompiledMethod ArtJniCompileMethodInternal(CompilerDriver* driver, } } - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); // Calling conventions used to iterate over parameters to method diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc index 78755176e4..0056c50634 100644 --- a/compiler/linker/arm/relative_patcher_thumb2.cc +++ b/compiler/linker/arm/relative_patcher_thumb2.cc @@ -21,6 +21,7 @@ #include "arch/arm/asm_support_arm.h" #include "art_method.h" #include "base/bit_utils.h" +#include "base/malloc_arena_pool.h" #include "compiled_method.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "linker/linker_patch.h" @@ -355,7 +356,7 @@ void Thumb2RelativePatcher::CompileBakerReadBarrierThunk(arm::ArmVIXLAssembler& } std::vector Thumb2RelativePatcher::CompileThunk(const ThunkKey& key) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); arm::ArmVIXLAssembler assembler(&allocator); diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc index b268204b4a..4bfe99baeb 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.cc +++ b/compiler/linker/arm64/relative_patcher_arm64.cc @@ -20,6 +20,7 @@ #include "arch/arm64/instruction_set_features_arm64.h" #include "art_method.h" #include "base/bit_utils.h" +#include "base/malloc_arena_pool.h" #include "compiled_method-inl.h" #include "driver/compiler_driver.h" #include "entrypoints/quick/quick_entrypoints_enum.h" @@ -511,7 +512,7 @@ void Arm64RelativePatcher::CompileBakerReadBarrierThunk(arm64::Arm64Assembler& a } std::vector Arm64RelativePatcher::CompileThunk(const ThunkKey& key) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); arm64::Arm64Assembler assembler(&allocator); diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h index 6dcbadba6e..a9bc5664c0 100644 --- a/compiler/optimizing/optimizing_unit_test.h +++ b/compiler/optimizing/optimizing_unit_test.h @@ -20,6 +20,7 @@ #include #include +#include "base/malloc_arena_pool.h" #include "base/scoped_arena_allocator.h" #include "builder.h" #include "common_compiler_test.h" @@ -97,7 +98,7 @@ class ArenaPoolAndAllocator { ScopedArenaAllocator* GetScopedAllocator() { return &scoped_allocator_; } private: - ArenaPool pool_; + MallocArenaPool pool_; ArenaAllocator allocator_; ArenaStack arena_stack_; ScopedArenaAllocator scoped_allocator_; diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc index cb87cabe1c..be35201166 100644 --- a/compiler/optimizing/parallel_move_test.cc +++ b/compiler/optimizing/parallel_move_test.cc @@ -15,6 +15,7 @@ */ #include "base/arena_allocator.h" +#include "base/malloc_arena_pool.h" #include "nodes.h" #include "parallel_move_resolver.h" @@ -180,7 +181,7 @@ TYPED_TEST_CASE(ParallelMoveTest, ParallelMoveResolverTestTypes); TYPED_TEST(ParallelMoveTest, Dependency) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); { @@ -207,7 +208,7 @@ TYPED_TEST(ParallelMoveTest, Dependency) { } TYPED_TEST(ParallelMoveTest, Cycle) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); { @@ -257,7 +258,7 @@ TYPED_TEST(ParallelMoveTest, Cycle) { } TYPED_TEST(ParallelMoveTest, ConstantLast) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); TypeParam resolver(&allocator); HParallelMove* moves = new (&allocator) HParallelMove(&allocator); @@ -276,7 +277,7 @@ TYPED_TEST(ParallelMoveTest, ConstantLast) { } TYPED_TEST(ParallelMoveTest, Pairs) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); { @@ -453,7 +454,7 @@ TYPED_TEST(ParallelMoveTest, Pairs) { } TYPED_TEST(ParallelMoveTest, MultiCycles) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); { @@ -551,7 +552,7 @@ TYPED_TEST(ParallelMoveTest, MultiCycles) { // Test that we do 64bits moves before 32bits moves. TYPED_TEST(ParallelMoveTest, CyclesWith64BitsMoves) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); { @@ -610,7 +611,7 @@ TYPED_TEST(ParallelMoveTest, CyclesWith64BitsMoves) { } TYPED_TEST(ParallelMoveTest, CyclesWith64BitsMoves2) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); { diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index 7e517f3485..e36c592662 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -18,6 +18,7 @@ #include "art_method.h" #include "base/arena_bit_vector.h" +#include "base/malloc_arena_pool.h" #include "stack_map_stream.h" #include "gtest/gtest.h" @@ -46,7 +47,7 @@ static bool CheckStackMask( using Kind = DexRegisterLocation::Kind; TEST(StackMapTest, Test1) { - ArenaPool pool; + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stream(&allocator, kRuntimeISA); @@ -128,7 +129,7 @@ TEST(StackMapTest, Test1) { } TEST(StackMapTest, Test2) { - ArenaPool pool; + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stream(&allocator, kRuntimeISA); @@ -413,7 +414,7 @@ TEST(StackMapTest, Test2) { } TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { - ArenaPool pool; + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stream(&allocator, kRuntimeISA); @@ -508,7 +509,7 @@ TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { } TEST(StackMapTest, TestNonLiveDexRegisters) { - ArenaPool pool; + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stream(&allocator, kRuntimeISA); @@ -588,7 +589,7 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { // StackMap::kNoDexRegisterMapSmallEncoding, and ensure we do // not treat it as kNoDexRegisterMap. TEST(StackMapTest, DexRegisterMapOffsetOverflow) { - ArenaPool pool; + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stream(&allocator, kRuntimeISA); @@ -652,7 +653,7 @@ TEST(StackMapTest, DexRegisterMapOffsetOverflow) { } TEST(StackMapTest, TestShareDexRegisterMap) { - ArenaPool pool; + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stream(&allocator, kRuntimeISA); @@ -711,7 +712,7 @@ TEST(StackMapTest, TestShareDexRegisterMap) { } TEST(StackMapTest, TestNoDexRegisterMap) { - ArenaPool pool; + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stream(&allocator, kRuntimeISA); @@ -761,7 +762,7 @@ TEST(StackMapTest, TestNoDexRegisterMap) { } TEST(StackMapTest, InlineTest) { - ArenaPool pool; + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stream(&allocator, kRuntimeISA); @@ -949,7 +950,7 @@ TEST(StackMapTest, CodeOffsetTest) { } TEST(StackMapTest, TestDeduplicateStackMask) { - ArenaPool pool; + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stream(&allocator, kRuntimeISA); @@ -978,7 +979,7 @@ TEST(StackMapTest, TestDeduplicateStackMask) { } TEST(StackMapTest, TestInvokeInfo) { - ArenaPool pool; + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stream(&allocator, kRuntimeISA); diff --git a/compiler/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc index 921d401849..57360e74a3 100644 --- a/compiler/trampolines/trampoline_compiler.cc +++ b/compiler/trampolines/trampoline_compiler.cc @@ -17,6 +17,7 @@ #include "trampoline_compiler.h" #include "base/arena_allocator.h" +#include "base/malloc_arena_pool.h" #include "jni_env_ext.h" #ifdef ART_ENABLE_CODEGEN_arm @@ -243,7 +244,7 @@ static std::unique_ptr> CreateTrampoline(ArenaAllocat std::unique_ptr> CreateTrampoline64(InstructionSet isa, EntryPointCallingConvention abi, ThreadOffset64 offset) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); switch (isa) { #ifdef ART_ENABLE_CODEGEN_arm64 @@ -269,7 +270,7 @@ std::unique_ptr> CreateTrampoline64(InstructionSet is std::unique_ptr> CreateTrampoline32(InstructionSet isa, EntryPointCallingConvention abi, ThreadOffset32 offset) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); switch (isa) { #ifdef ART_ENABLE_CODEGEN_arm diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h index 0cb8bbb2d5..7c800b355f 100644 --- a/compiler/utils/assembler_test.h +++ b/compiler/utils/assembler_test.h @@ -26,6 +26,7 @@ #include #include +#include "base/malloc_arena_pool.h" #include "assembler_test_base.h" #include "common_runtime_test.h" // For ScratchFile @@ -1606,7 +1607,7 @@ class AssemblerTest : public testing::Test { static constexpr size_t kWarnManyCombinationsThreshold = 500; - ArenaPool pool_; + MallocArenaPool pool_; std::unique_ptr allocator_; std::unique_ptr assembler_; std::unique_ptr test_helper_; diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc index 655d17d4fb..053e202523 100644 --- a/compiler/utils/assembler_thumb_test.cc +++ b/compiler/utils/assembler_thumb_test.cc @@ -27,6 +27,7 @@ #include "utils/arm/jni_macro_assembler_arm_vixl.h" #include "base/hex_dump.h" +#include "base/malloc_arena_pool.h" #include "common_runtime_test.h" namespace art { @@ -169,7 +170,7 @@ class ArmVIXLAssemblerTest : public ::testing::Test { public: ArmVIXLAssemblerTest() : pool(), allocator(&pool), assembler(&allocator) { } - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator; ArmVIXLJNIMacroAssembler assembler; }; diff --git a/compiler/utils/jni_macro_assembler_test.h b/compiler/utils/jni_macro_assembler_test.h index 1aefc84c78..b70c18b3e2 100644 --- a/compiler/utils/jni_macro_assembler_test.h +++ b/compiler/utils/jni_macro_assembler_test.h @@ -20,6 +20,7 @@ #include "jni_macro_assembler.h" #include "assembler_test_base.h" +#include "base/malloc_arena_pool.h" #include "common_runtime_test.h" // For ScratchFile #include @@ -139,7 +140,7 @@ class JNIMacroAssemblerTest : public testing::Test { test_helper_->Driver(*data, assembly_text, test_name); } - ArenaPool pool_; + MallocArenaPool pool_; std::unique_ptr allocator_; std::unique_ptr assembler_; std::unique_ptr test_helper_; diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index 8f72db748b..cd007b32d4 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -17,13 +17,14 @@ #include "assembler_x86.h" #include "base/arena_allocator.h" +#include "base/malloc_arena_pool.h" #include "base/stl_util.h" #include "utils/assembler_test.h" namespace art { TEST(AssemblerX86, CreateBuffer) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); AssemblerBuffer buffer(&allocator); AssemblerBuffer::EnsureCapacity ensured(&buffer); diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index 104e215cf5..0589df55d2 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -21,6 +21,7 @@ #include #include "base/bit_utils.h" +#include "base/malloc_arena_pool.h" #include "base/stl_util.h" #include "jni_macro_assembler_x86_64.h" #include "utils/assembler_test.h" @@ -29,7 +30,7 @@ namespace art { TEST(AssemblerX86_64, CreateBuffer) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); AssemblerBuffer buffer(&allocator); AssemblerBuffer::EnsureCapacity ensured(&buffer); diff --git a/runtime/Android.bp b/runtime/Android.bp index c0f1c366b8..b380dab187 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -35,6 +35,8 @@ cc_defaults { "base/arena_allocator.cc", "base/arena_bit_vector.cc", "base/file_utils.cc", + "base/malloc_arena_pool.cc", + "base/mem_map_arena_pool.cc", "base/mutex.cc", "base/quasi_atomic.cc", "base/scoped_arena_allocator.cc", diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc index fe0f876d66..348a812e44 100644 --- a/runtime/base/arena_allocator.cc +++ b/runtime/base/arena_allocator.cc @@ -25,11 +25,6 @@ #include -#include "base/systrace.h" -#include "mem_map.h" -#include "mutex.h" -#include "thread-current-inl.h" - namespace art { constexpr size_t kMemoryToolRedZoneBytes = 8; @@ -190,194 +185,6 @@ void ArenaAllocatorMemoryTool::DoMakeInaccessible(void* ptr, size_t size) { Arena::Arena() : bytes_allocated_(0), memory_(nullptr), size_(0), next_(nullptr) { } -class MallocArena FINAL : public Arena { - public: - explicit MallocArena(size_t size = arena_allocator::kArenaDefaultSize); - virtual ~MallocArena(); - private: - static constexpr size_t RequiredOverallocation() { - return (alignof(std::max_align_t) < ArenaAllocator::kArenaAlignment) - ? ArenaAllocator::kArenaAlignment - alignof(std::max_align_t) - : 0u; - } - - uint8_t* unaligned_memory_; -}; - -MallocArena::MallocArena(size_t size) { - // We need to guarantee kArenaAlignment aligned allocation for the new arena. - // TODO: Use std::aligned_alloc() when it becomes available with C++17. - constexpr size_t overallocation = RequiredOverallocation(); - unaligned_memory_ = reinterpret_cast(calloc(1, size + overallocation)); - CHECK(unaligned_memory_ != nullptr); // Abort on OOM. - DCHECK_ALIGNED(unaligned_memory_, alignof(std::max_align_t)); - if (overallocation == 0u) { - memory_ = unaligned_memory_; - } else { - memory_ = AlignUp(unaligned_memory_, ArenaAllocator::kArenaAlignment); - if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { - size_t head = memory_ - unaligned_memory_; - size_t tail = overallocation - head; - MEMORY_TOOL_MAKE_NOACCESS(unaligned_memory_, head); - MEMORY_TOOL_MAKE_NOACCESS(memory_ + size, tail); - } - } - DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment); - size_ = size; -} - -MallocArena::~MallocArena() { - constexpr size_t overallocation = RequiredOverallocation(); - if (overallocation != 0u && UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { - size_t head = memory_ - unaligned_memory_; - size_t tail = overallocation - head; - MEMORY_TOOL_MAKE_UNDEFINED(unaligned_memory_, head); - MEMORY_TOOL_MAKE_UNDEFINED(memory_ + size_, tail); - } - free(reinterpret_cast(unaligned_memory_)); -} - -class MemMapArena FINAL : public Arena { - public: - MemMapArena(size_t size, bool low_4gb, const char* name); - virtual ~MemMapArena(); - void Release() OVERRIDE; - - private: - std::unique_ptr map_; -}; - -MemMapArena::MemMapArena(size_t size, bool low_4gb, const char* name) { - // Round up to a full page as that's the smallest unit of allocation for mmap() - // and we want to be able to use all memory that we actually allocate. - size = RoundUp(size, kPageSize); - std::string error_msg; - map_.reset(MemMap::MapAnonymous( - name, nullptr, size, PROT_READ | PROT_WRITE, low_4gb, false, &error_msg)); - CHECK(map_.get() != nullptr) << error_msg; - memory_ = map_->Begin(); - static_assert(ArenaAllocator::kArenaAlignment <= kPageSize, - "Arena should not need stronger alignment than kPageSize."); - DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment); - size_ = map_->Size(); -} - -MemMapArena::~MemMapArena() { - // Destroys MemMap via std::unique_ptr<>. -} - -void MemMapArena::Release() { - if (bytes_allocated_ > 0) { - map_->MadviseDontNeedAndZero(); - bytes_allocated_ = 0; - } -} - -void Arena::Reset() { - if (bytes_allocated_ > 0) { - memset(Begin(), 0, bytes_allocated_); - bytes_allocated_ = 0; - } -} - -ArenaPool::ArenaPool(bool use_malloc, bool low_4gb, const char* name) - : use_malloc_(use_malloc), - lock_("Arena pool lock", kArenaPoolLock), - free_arenas_(nullptr), - low_4gb_(low_4gb), - name_(name) { - if (low_4gb) { - CHECK(!use_malloc) << "low4gb must use map implementation"; - } - if (!use_malloc) { - MemMap::Init(); - } -} - -ArenaPool::~ArenaPool() { - ReclaimMemory(); -} - -void ArenaPool::ReclaimMemory() { - while (free_arenas_ != nullptr) { - Arena* arena = free_arenas_; - free_arenas_ = free_arenas_->next_; - delete arena; - } -} - -void ArenaPool::LockReclaimMemory() { - MutexLock lock(Thread::Current(), lock_); - ReclaimMemory(); -} - -Arena* ArenaPool::AllocArena(size_t size) { - Thread* self = Thread::Current(); - Arena* ret = nullptr; - { - MutexLock lock(self, lock_); - if (free_arenas_ != nullptr && LIKELY(free_arenas_->Size() >= size)) { - ret = free_arenas_; - free_arenas_ = free_arenas_->next_; - } - } - if (ret == nullptr) { - ret = use_malloc_ ? static_cast(new MallocArena(size)) : - new MemMapArena(size, low_4gb_, name_); - } - ret->Reset(); - return ret; -} - -void ArenaPool::TrimMaps() { - if (!use_malloc_) { - ScopedTrace trace(__PRETTY_FUNCTION__); - // Doesn't work for malloc. - MutexLock lock(Thread::Current(), lock_); - for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) { - arena->Release(); - } - } -} - -size_t ArenaPool::GetBytesAllocated() const { - size_t total = 0; - MutexLock lock(Thread::Current(), lock_); - for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) { - total += arena->GetBytesAllocated(); - } - return total; -} - -void ArenaPool::FreeArenaChain(Arena* first) { - if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { - for (Arena* arena = first; arena != nullptr; arena = arena->next_) { - MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_); - } - } - - if (arena_allocator::kArenaAllocatorPreciseTracking) { - // Do not reuse arenas when tracking. - while (first != nullptr) { - Arena* next = first->next_; - delete first; - first = next; - } - return; - } - - if (first != nullptr) { - Arena* last = first; - while (last->next_ != nullptr) { - last = last->next_; - } - Thread* self = Thread::Current(); - MutexLock lock(self, lock_); - last->next_ = free_arenas_; - free_arenas_ = first; - } -} - size_t ArenaAllocator::BytesAllocated() const { return ArenaAllocatorStats::BytesAllocated(); } diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h index 688f01b71f..f59cfdd69d 100644 --- a/runtime/base/arena_allocator.h +++ b/runtime/base/arena_allocator.h @@ -25,7 +25,6 @@ #include "base/dchecked_vector.h" #include "base/macros.h" #include "base/memory_tool.h" -#include "mutex.h" namespace art { @@ -236,7 +235,8 @@ class Arena { uint8_t* memory_; size_t size_; Arena* next_; - friend class ArenaPool; + friend class MallocArenaPool; + friend class MemMapArenaPool; friend class ArenaAllocator; friend class ArenaStack; friend class ScopedArenaAllocator; @@ -250,25 +250,20 @@ class Arena { class ArenaPool { public: - explicit ArenaPool(bool use_malloc = true, - bool low_4gb = false, - const char* name = "LinearAlloc"); - ~ArenaPool(); - Arena* AllocArena(size_t size) REQUIRES(!lock_); - void FreeArenaChain(Arena* first) REQUIRES(!lock_); - size_t GetBytesAllocated() const REQUIRES(!lock_); - void ReclaimMemory() NO_THREAD_SAFETY_ANALYSIS; - void LockReclaimMemory() REQUIRES(!lock_); - // Trim the maps in arenas by madvising, used by JIT to reduce memory usage. This only works - // use_malloc is false. - void TrimMaps() REQUIRES(!lock_); + virtual ~ArenaPool() = default; + + virtual Arena* AllocArena(size_t size) = 0; + virtual void FreeArenaChain(Arena* first) = 0; + virtual size_t GetBytesAllocated() const = 0; + virtual void ReclaimMemory() = 0; + virtual void LockReclaimMemory() = 0; + // Trim the maps in arenas by madvising, used by JIT to reduce memory usage. + virtual void TrimMaps() = 0; + + protected: + ArenaPool() = default; private: - const bool use_malloc_; - mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - Arena* free_arenas_ GUARDED_BY(lock_); - const bool low_4gb_; - const char* name_; DISALLOW_COPY_AND_ASSIGN(ArenaPool); }; diff --git a/runtime/base/arena_allocator_test.cc b/runtime/base/arena_allocator_test.cc index 68e26af9f8..68e99f4be0 100644 --- a/runtime/base/arena_allocator_test.cc +++ b/runtime/base/arena_allocator_test.cc @@ -16,6 +16,7 @@ #include "base/arena_allocator-inl.h" #include "base/arena_bit_vector.h" +#include "base/malloc_arena_pool.h" #include "base/memory_tool.h" #include "gtest/gtest.h" @@ -33,7 +34,7 @@ class ArenaAllocatorTest : public testing::Test { }; TEST_F(ArenaAllocatorTest, Test) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); ArenaBitVector bv(&allocator, 10, true); bv.SetBit(5); @@ -44,7 +45,7 @@ TEST_F(ArenaAllocatorTest, Test) { TEST_F(ArenaAllocatorTest, MakeDefined) { // Regression test to make sure we mark the allocated area defined. - ArenaPool pool; + MallocArenaPool pool; static constexpr size_t kSmallArraySize = 10; static constexpr size_t kLargeArraySize = 50; uint32_t* small_array; @@ -71,7 +72,7 @@ TEST_F(ArenaAllocatorTest, LargeAllocations) { } { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); // Note: Leaving some space for memory tool red zones. void* alloc1 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 5 / 8); @@ -80,7 +81,7 @@ TEST_F(ArenaAllocatorTest, LargeAllocations) { ASSERT_EQ(1u, NumberOfArenas(&allocator)); } { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); void* alloc1 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 13 / 16); void* alloc2 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 11 / 16); @@ -92,7 +93,7 @@ TEST_F(ArenaAllocatorTest, LargeAllocations) { ASSERT_EQ(3u, NumberOfArenas(&allocator)); } { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); void* alloc1 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 13 / 16); void* alloc2 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 9 / 16); @@ -105,7 +106,7 @@ TEST_F(ArenaAllocatorTest, LargeAllocations) { ASSERT_EQ(2u, NumberOfArenas(&allocator)); } { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); void* alloc1 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 9 / 16); void* alloc2 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 13 / 16); @@ -118,7 +119,7 @@ TEST_F(ArenaAllocatorTest, LargeAllocations) { ASSERT_EQ(2u, NumberOfArenas(&allocator)); } { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); // Note: Leaving some space for memory tool red zones. for (size_t i = 0; i != 15; ++i) { @@ -133,7 +134,7 @@ TEST_F(ArenaAllocatorTest, LargeAllocations) { } TEST_F(ArenaAllocatorTest, AllocAlignment) { - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); for (size_t iterations = 0; iterations <= 10; ++iterations) { for (size_t size = 1; size <= ArenaAllocator::kAlignment + 1; ++size) { @@ -153,7 +154,7 @@ TEST_F(ArenaAllocatorTest, ReallocReuse) { { // Case 1: small aligned allocation, aligned extend inside arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = ArenaAllocator::kAlignment * 2; @@ -166,7 +167,7 @@ TEST_F(ArenaAllocatorTest, ReallocReuse) { { // Case 2: small aligned allocation, non-aligned extend inside arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = ArenaAllocator::kAlignment * 2; @@ -179,7 +180,7 @@ TEST_F(ArenaAllocatorTest, ReallocReuse) { { // Case 3: small non-aligned allocation, aligned extend inside arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = ArenaAllocator::kAlignment * 2 + (ArenaAllocator::kAlignment / 2); @@ -192,7 +193,7 @@ TEST_F(ArenaAllocatorTest, ReallocReuse) { { // Case 4: small non-aligned allocation, aligned non-extend inside arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = ArenaAllocator::kAlignment * 2 + (ArenaAllocator::kAlignment / 2); @@ -208,7 +209,7 @@ TEST_F(ArenaAllocatorTest, ReallocReuse) { { // Case 5: large allocation, aligned extend into next arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = arena_allocator::kArenaDefaultSize - @@ -222,7 +223,7 @@ TEST_F(ArenaAllocatorTest, ReallocReuse) { { // Case 6: large allocation, non-aligned extend into next arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = arena_allocator::kArenaDefaultSize - @@ -241,7 +242,7 @@ TEST_F(ArenaAllocatorTest, ReallocReuse) { TEST_F(ArenaAllocatorTest, ReallocAlignment) { { // Case 1: small aligned allocation, aligned extend inside arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = ArenaAllocator::kAlignment * 2; @@ -258,7 +259,7 @@ TEST_F(ArenaAllocatorTest, ReallocAlignment) { { // Case 2: small aligned allocation, non-aligned extend inside arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = ArenaAllocator::kAlignment * 2; @@ -275,7 +276,7 @@ TEST_F(ArenaAllocatorTest, ReallocAlignment) { { // Case 3: small non-aligned allocation, aligned extend inside arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = ArenaAllocator::kAlignment * 2 + (ArenaAllocator::kAlignment / 2); @@ -292,7 +293,7 @@ TEST_F(ArenaAllocatorTest, ReallocAlignment) { { // Case 4: small non-aligned allocation, aligned non-extend inside arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = ArenaAllocator::kAlignment * 2 + (ArenaAllocator::kAlignment / 2); @@ -312,7 +313,7 @@ TEST_F(ArenaAllocatorTest, ReallocAlignment) { { // Case 5: large allocation, aligned extend into next arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = arena_allocator::kArenaDefaultSize - @@ -330,7 +331,7 @@ TEST_F(ArenaAllocatorTest, ReallocAlignment) { { // Case 6: large allocation, non-aligned extend into next arena. - ArenaPool pool; + MallocArenaPool pool; ArenaAllocator allocator(&pool); const size_t original_size = arena_allocator::kArenaDefaultSize - diff --git a/runtime/base/malloc_arena_pool.cc b/runtime/base/malloc_arena_pool.cc new file mode 100644 index 0000000000..7df4aef25b --- /dev/null +++ b/runtime/base/malloc_arena_pool.cc @@ -0,0 +1,162 @@ +/* + * 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. + */ + +#include "malloc_arena_pool.h" + +#include + +#include +#include +#include +#include + +#include +#include "base/arena_allocator-inl.h" + +namespace art { + +class MallocArena FINAL : public Arena { + public: + explicit MallocArena(size_t size = arena_allocator::kArenaDefaultSize); + virtual ~MallocArena(); + private: + static constexpr size_t RequiredOverallocation() { + return (alignof(std::max_align_t) < ArenaAllocator::kArenaAlignment) + ? ArenaAllocator::kArenaAlignment - alignof(std::max_align_t) + : 0u; + } + + uint8_t* unaligned_memory_; +}; + +MallocArena::MallocArena(size_t size) { + // We need to guarantee kArenaAlignment aligned allocation for the new arena. + // TODO: Use std::aligned_alloc() when it becomes available with C++17. + constexpr size_t overallocation = RequiredOverallocation(); + unaligned_memory_ = reinterpret_cast(calloc(1, size + overallocation)); + CHECK(unaligned_memory_ != nullptr); // Abort on OOM. + DCHECK_ALIGNED(unaligned_memory_, alignof(std::max_align_t)); + if (overallocation == 0u) { + memory_ = unaligned_memory_; + } else { + memory_ = AlignUp(unaligned_memory_, ArenaAllocator::kArenaAlignment); + if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { + size_t head = memory_ - unaligned_memory_; + size_t tail = overallocation - head; + MEMORY_TOOL_MAKE_NOACCESS(unaligned_memory_, head); + MEMORY_TOOL_MAKE_NOACCESS(memory_ + size, tail); + } + } + DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment); + size_ = size; +} + +MallocArena::~MallocArena() { + constexpr size_t overallocation = RequiredOverallocation(); + if (overallocation != 0u && UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { + size_t head = memory_ - unaligned_memory_; + size_t tail = overallocation - head; + MEMORY_TOOL_MAKE_UNDEFINED(unaligned_memory_, head); + MEMORY_TOOL_MAKE_UNDEFINED(memory_ + size_, tail); + } + free(reinterpret_cast(unaligned_memory_)); +} + +void Arena::Reset() { + if (bytes_allocated_ > 0) { + memset(Begin(), 0, bytes_allocated_); + bytes_allocated_ = 0; + } +} + +MallocArenaPool::MallocArenaPool() : free_arenas_(nullptr) { +} + +MallocArenaPool::~MallocArenaPool() { + ReclaimMemory(); +} + +void MallocArenaPool::ReclaimMemory() { + while (free_arenas_ != nullptr) { + Arena* arena = free_arenas_; + free_arenas_ = free_arenas_->next_; + delete arena; + } +} + +void MallocArenaPool::LockReclaimMemory() { + std::lock_guard lock(lock_); + ReclaimMemory(); +} + +Arena* MallocArenaPool::AllocArena(size_t size) { + Arena* ret = nullptr; + { + std::lock_guard lock(lock_); + if (free_arenas_ != nullptr && LIKELY(free_arenas_->Size() >= size)) { + ret = free_arenas_; + free_arenas_ = free_arenas_->next_; + } + } + if (ret == nullptr) { + ret = new MallocArena(size); + } + ret->Reset(); + return ret; +} + +void MallocArenaPool::TrimMaps() { + // Nop, because there is no way to do madvise here. +} + +size_t MallocArenaPool::GetBytesAllocated() const { + size_t total = 0; + std::lock_guard lock(lock_); + for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) { + total += arena->GetBytesAllocated(); + } + return total; +} + +void MallocArenaPool::FreeArenaChain(Arena* first) { + if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { + for (Arena* arena = first; arena != nullptr; arena = arena->next_) { + MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_); + } + } + + if (arena_allocator::kArenaAllocatorPreciseTracking) { + // Do not reuse arenas when tracking. + while (first != nullptr) { + Arena* next = first->next_; + delete first; + first = next; + } + return; + } + + if (first != nullptr) { + Arena* last = first; + while (last->next_ != nullptr) { + last = last->next_; + } + std::lock_guard lock(lock_); + last->next_ = free_arenas_; + free_arenas_ = first; + } +} + +} // namespace art diff --git a/runtime/base/malloc_arena_pool.h b/runtime/base/malloc_arena_pool.h new file mode 100644 index 0000000000..9cd2572411 --- /dev/null +++ b/runtime/base/malloc_arena_pool.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef ART_RUNTIME_BASE_MALLOC_ARENA_POOL_H_ +#define ART_RUNTIME_BASE_MALLOC_ARENA_POOL_H_ + +#include + +#include "base/arena_allocator.h" + +namespace art { + +class MallocArenaPool FINAL : public ArenaPool { + public: + MallocArenaPool(); + ~MallocArenaPool(); + Arena* AllocArena(size_t size) OVERRIDE; + void FreeArenaChain(Arena* first) OVERRIDE; + size_t GetBytesAllocated() const OVERRIDE; + void ReclaimMemory() OVERRIDE; + void LockReclaimMemory() OVERRIDE; + // Is a nop for malloc pools. + void TrimMaps() OVERRIDE; + + private: + Arena* free_arenas_; + // Use a std::mutex here as Arenas are at the bottom of the lock hierarchy when malloc is used. + mutable std::mutex lock_; + + DISALLOW_COPY_AND_ASSIGN(MallocArenaPool); +}; + +} // namespace art + +#endif // ART_RUNTIME_BASE_MALLOC_ARENA_POOL_H_ diff --git a/runtime/base/mem_map_arena_pool.cc b/runtime/base/mem_map_arena_pool.cc new file mode 100644 index 0000000000..d5ea19b70e --- /dev/null +++ b/runtime/base/mem_map_arena_pool.cc @@ -0,0 +1,155 @@ +/* + * 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. + */ + +#include "mem_map_arena_pool.h" + +#include + +#include +#include +#include +#include + +#include + +#include "base/arena_allocator-inl.h" +#include "base/systrace.h" +#include "mem_map.h" + +namespace art { + +class MemMapArena FINAL : public Arena { + public: + MemMapArena(size_t size, bool low_4gb, const char* name); + virtual ~MemMapArena(); + void Release() OVERRIDE; + + private: + std::unique_ptr map_; +}; + +MemMapArena::MemMapArena(size_t size, bool low_4gb, const char* name) { + // Round up to a full page as that's the smallest unit of allocation for mmap() + // and we want to be able to use all memory that we actually allocate. + size = RoundUp(size, kPageSize); + std::string error_msg; + map_.reset(MemMap::MapAnonymous( + name, nullptr, size, PROT_READ | PROT_WRITE, low_4gb, false, &error_msg)); + CHECK(map_.get() != nullptr) << error_msg; + memory_ = map_->Begin(); + static_assert(ArenaAllocator::kArenaAlignment <= kPageSize, + "Arena should not need stronger alignment than kPageSize."); + DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment); + size_ = map_->Size(); +} + +MemMapArena::~MemMapArena() { + // Destroys MemMap via std::unique_ptr<>. +} + +void MemMapArena::Release() { + if (bytes_allocated_ > 0) { + map_->MadviseDontNeedAndZero(); + bytes_allocated_ = 0; + } +} + +MemMapArenaPool::MemMapArenaPool(bool low_4gb, const char* name) + : low_4gb_(low_4gb), + name_(name), + free_arenas_(nullptr) { + MemMap::Init(); +} + +MemMapArenaPool::~MemMapArenaPool() { + ReclaimMemory(); +} + +void MemMapArenaPool::ReclaimMemory() { + while (free_arenas_ != nullptr) { + Arena* arena = free_arenas_; + free_arenas_ = free_arenas_->next_; + delete arena; + } +} + +void MemMapArenaPool::LockReclaimMemory() { + std::lock_guard lock(lock_); + ReclaimMemory(); +} + +Arena* MemMapArenaPool::AllocArena(size_t size) { + Arena* ret = nullptr; + { + std::lock_guard lock(lock_); + if (free_arenas_ != nullptr && LIKELY(free_arenas_->Size() >= size)) { + ret = free_arenas_; + free_arenas_ = free_arenas_->next_; + } + } + if (ret == nullptr) { + ret = new MemMapArena(size, low_4gb_, name_); + } + ret->Reset(); + return ret; +} + +void MemMapArenaPool::TrimMaps() { + ScopedTrace trace(__PRETTY_FUNCTION__); + std::lock_guard lock(lock_); + for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) { + arena->Release(); + } +} + +size_t MemMapArenaPool::GetBytesAllocated() const { + size_t total = 0; + std::lock_guard lock(lock_); + for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) { + total += arena->GetBytesAllocated(); + } + return total; +} + +void MemMapArenaPool::FreeArenaChain(Arena* first) { + if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { + for (Arena* arena = first; arena != nullptr; arena = arena->next_) { + MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_); + } + } + + if (arena_allocator::kArenaAllocatorPreciseTracking) { + // Do not reuse arenas when tracking. + while (first != nullptr) { + Arena* next = first->next_; + delete first; + first = next; + } + return; + } + + if (first != nullptr) { + Arena* last = first; + while (last->next_ != nullptr) { + last = last->next_; + } + std::lock_guard lock(lock_); + last->next_ = free_arenas_; + free_arenas_ = first; + } +} + +} // namespace art diff --git a/runtime/base/mem_map_arena_pool.h b/runtime/base/mem_map_arena_pool.h new file mode 100644 index 0000000000..24e150e1e7 --- /dev/null +++ b/runtime/base/mem_map_arena_pool.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#ifndef ART_RUNTIME_BASE_MEM_MAP_ARENA_POOL_H_ +#define ART_RUNTIME_BASE_MEM_MAP_ARENA_POOL_H_ + +#include "base/arena_allocator.h" + +namespace art { + +class MemMapArenaPool FINAL : public ArenaPool { + public: + explicit MemMapArenaPool(bool low_4gb = false, const char* name = "LinearAlloc"); + virtual ~MemMapArenaPool(); + Arena* AllocArena(size_t size) OVERRIDE; + void FreeArenaChain(Arena* first) OVERRIDE; + size_t GetBytesAllocated() const OVERRIDE; + void ReclaimMemory() OVERRIDE; + void LockReclaimMemory() OVERRIDE; + // Trim the maps in arenas by madvising, used by JIT to reduce memory usage. + void TrimMaps() OVERRIDE; + + private: + const bool low_4gb_; + const char* name_; + Arena* free_arenas_; + // Use a std::mutex here as Arenas are second-from-the-bottom when using MemMaps, and MemMap + // itself uses std::mutex scoped to within an allocate/free only. + mutable std::mutex lock_; + + DISALLOW_COPY_AND_ASSIGN(MemMapArenaPool); +}; + +} // namespace art + +#endif // ART_RUNTIME_BASE_MEM_MAP_ARENA_POOL_H_ diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index 1bbce4f414..f4132c2343 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -36,6 +36,7 @@ #include "base/dumpable.h" #include "base/file_utils.h" #include "base/logging.h" // For VLOG. +#include "base/malloc_arena_pool.h" #include "base/mutex.h" #include "base/os.h" #include "base/safe_map.h" @@ -90,7 +91,7 @@ ProfileCompilationInfo::ProfileCompilationInfo(ArenaPool* custom_arena_pool) } ProfileCompilationInfo::ProfileCompilationInfo() - : default_arena_pool_(/*use_malloc*/true, /*low_4gb*/false, "ProfileCompilationInfo"), + : default_arena_pool_(), allocator_(&default_arena_pool_), info_(allocator_.Adapter(kArenaAllocProfile)), profile_key_map_(std::less(), allocator_.Adapter(kArenaAllocProfile)) { diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h index 6c56db9f49..4ac8c612e0 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -23,6 +23,7 @@ #include "base/arena_containers.h" #include "base/arena_object.h" #include "base/atomic.h" +#include "base/malloc_arena_pool.h" #include "base/safe_map.h" #include "bit_memory_region.h" #include "dex/dex_cache_resolved_classes.h" @@ -792,7 +793,7 @@ class ProfileCompilationInfo { friend class ProfileAssistantTest; friend class Dex2oatLayoutTest; - ArenaPool default_arena_pool_; + MallocArenaPool default_arena_pool_; ArenaAllocator allocator_; // Vector containing the actual profile info. diff --git a/runtime/linear_alloc.h b/runtime/linear_alloc.h index 384b2e3243..87086f29d4 100644 --- a/runtime/linear_alloc.h +++ b/runtime/linear_alloc.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_LINEAR_ALLOC_H_ #include "base/arena_allocator.h" +#include "base/mutex.h" namespace art { diff --git a/runtime/runtime.cc b/runtime/runtime.cc index b80ce7de46..4068158f3f 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -62,6 +62,8 @@ #include "base/dumpable.h" #include "base/enums.h" #include "base/file_utils.h" +#include "base/malloc_arena_pool.h" +#include "base/mem_map_arena_pool.h" #include "base/memory_tool.h" #include "base/mutex.h" #include "base/os.h" @@ -1332,13 +1334,17 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // Use MemMap arena pool for jit, malloc otherwise. Malloc arenas are faster to allocate but // can't be trimmed as easily. const bool use_malloc = IsAotCompiler(); - arena_pool_.reset(new ArenaPool(use_malloc, /* low_4gb */ false)); - jit_arena_pool_.reset( - new ArenaPool(/* use_malloc */ false, /* low_4gb */ false, "CompilerMetadata")); + if (use_malloc) { + arena_pool_.reset(new MallocArenaPool()); + jit_arena_pool_.reset(new MallocArenaPool()); + } else { + arena_pool_.reset(new MemMapArenaPool(/* low_4gb */ false)); + jit_arena_pool_.reset(new MemMapArenaPool(/* low_4gb */ false, "CompilerMetadata")); + } if (IsAotCompiler() && Is64BitInstructionSet(kRuntimeISA)) { // 4gb, no malloc. Explanation in header. - low_4gb_arena_pool_.reset(new ArenaPool(/* use_malloc */ false, /* low_4gb */ true)); + low_4gb_arena_pool_.reset(new MemMapArenaPool(/* low_4gb */ true)); } linear_alloc_.reset(CreateLinearAlloc()); diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h index 18ad6b5d0c..168eb7bb63 100644 --- a/runtime/verifier/register_line.h +++ b/runtime/verifier/register_line.h @@ -22,6 +22,7 @@ #include +#include "base/mutex.h" #include "base/safe_map.h" #include "base/scoped_arena_containers.h" -- GitLab From 88f1054e8272dfc1a47bf172b14c423b66417554 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Thu, 5 Apr 2018 09:54:52 +0100 Subject: [PATCH 202/749] ART: Temporarily move 3 checker tests away from D8 Postpones switching to D8 for tests failing on the ART buildbots. All of the tests modified were failing on 64-bit ARM. Only 551-checker-shifter-operand was failing on 32-bit ARM. Bug: 65168732 Test: art/test.py --target -j4 -r --64 -t 551-checker-shifter-operand Test: art/test.py --target -j4 -r --64 -t 626-checker-arm64-scratch-register Test: art/test.py --target -j4 -r --64 -t 706-checker-scheduler Change-Id: Ie02d8dc46a36ebe5ddb9a6e24e96bc2bdc4f97c7 --- test/551-checker-shifter-operand/build | 20 +++++++++++++++++++ test/626-checker-arm64-scratch-register/build | 20 +++++++++++++++++++ test/706-checker-scheduler/build | 20 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 test/551-checker-shifter-operand/build create mode 100644 test/626-checker-arm64-scratch-register/build create mode 100644 test/706-checker-scheduler/build diff --git a/test/551-checker-shifter-operand/build b/test/551-checker-shifter-operand/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/551-checker-shifter-operand/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/626-checker-arm64-scratch-register/build b/test/626-checker-arm64-scratch-register/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/626-checker-arm64-scratch-register/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/706-checker-scheduler/build b/test/706-checker-scheduler/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/706-checker-scheduler/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" -- GitLab From 3169abfe80c2165804121f1bcd015eeb504ec307 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Thu, 5 Apr 2018 11:37:53 +0100 Subject: [PATCH 203/749] ART: Temporarily move 704-multiply-accumulate away from D8 Postpones switching to D8 for test failing on the ART buildbots. Failure only observed for gcstress test variants. Bug: 65168732 Test: art/test.py --32 --target -r --gcstress -t 704 Change-Id: Iabccc4b7cb1ecc155ad972450e8995eec4b62959 --- test/704-multiply-accumulate/build | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 test/704-multiply-accumulate/build diff --git a/test/704-multiply-accumulate/build b/test/704-multiply-accumulate/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/704-multiply-accumulate/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" -- GitLab From 606adb3a515b31e6d4b02becb36f732918fe7713 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 5 Apr 2018 14:49:24 +0100 Subject: [PATCH 204/749] Fix and extend debugging output for bug 74410240. Test: Manually break in the resolution trampoline and force printing the message. Bug: 74410240 Change-Id: I5f3fea53a7ec2170b644fb13bca84bae099243c8 --- runtime/Android.bp | 1 + runtime/class_linker.cc | 78 +----------- runtime/debug_print.cc | 113 ++++++++++++++++++ runtime/debug_print.h | 34 ++++++ .../quick/quick_trampoline_entrypoints.cc | 22 +++- 5 files changed, 169 insertions(+), 79 deletions(-) create mode 100644 runtime/debug_print.cc create mode 100644 runtime/debug_print.h diff --git a/runtime/Android.bp b/runtime/Android.bp index c0f1c366b8..08025824c3 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -46,6 +46,7 @@ cc_defaults { "class_table.cc", "common_throws.cc", "compiler_filter.cc", + "debug_print.cc", "debugger.cc", "dex/art_dex_file_loader.cc", "dex/dex_file_annotations.cc", diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 3025818ab7..b5af08f2e8 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -53,6 +53,7 @@ #include "class_loader_utils.h" #include "class_table-inl.h" #include "compiler_callbacks.h" +#include "debug_print.h" #include "debugger.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" @@ -7844,83 +7845,6 @@ ObjPtr ClassLinker::DoResolveType(dex::TypeIndex type_idx, return resolved; } -std::string DescribeSpace(ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_) { - std::ostringstream oss; - gc::Heap* heap = Runtime::Current()->GetHeap(); - gc::space::ContinuousSpace* cs = heap->FindContinuousSpaceFromAddress(klass.Ptr()); - if (cs != nullptr) { - if (cs->IsImageSpace()) { - oss << "image;" << cs->GetName() << ";" << cs->AsImageSpace()->GetImageFilename(); - } else { - oss << "continuous;" << cs->GetName(); - } - } else { - gc::space::DiscontinuousSpace* ds = - heap->FindDiscontinuousSpaceFromObject(klass, /* fail_ok */ true); - if (ds != nullptr) { - oss << "discontinuous;" << ds->GetName(); - } else { - oss << "invalid"; - } - } - return oss.str(); -} - -std::string DescribeLoaders(ObjPtr loader, const char* class_descriptor) - REQUIRES_SHARED(Locks::mutator_lock_) { - std::ostringstream oss; - uint32_t hash = ComputeModifiedUtf8Hash(class_descriptor); - ObjPtr path_class_loader = - WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_PathClassLoader); - ObjPtr dex_class_loader = - WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexClassLoader); - ObjPtr delegate_last_class_loader = - WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DelegateLastClassLoader); - - // Print the class loader chain. - bool found_class = false; - const char* loader_separator = ""; - if (loader == nullptr) { - oss << "BootClassLoader"; // This would be unexpected. - } - for (; loader != nullptr; loader = loader->GetParent()) { - oss << loader_separator << loader->GetClass()->PrettyDescriptor(); - loader_separator = ";"; - // If we didn't find the interface yet, try to find it in the current class loader. - if (!found_class) { - ClassTable* table = Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(loader); - ObjPtr klass = - (table != nullptr) ? table->Lookup(class_descriptor, hash) : nullptr; - if (klass != nullptr) { - found_class = true; - oss << "[hit:" << DescribeSpace(klass) << "]"; - } - } - - // For PathClassLoader, DexClassLoader or DelegateLastClassLoader - // also dump the dex file locations. - if (loader->GetClass() == path_class_loader || - loader->GetClass() == dex_class_loader || - loader->GetClass() == delegate_last_class_loader) { - oss << "("; - ScopedObjectAccessUnchecked soa(Thread::Current()); - StackHandleScope<1> hs(soa.Self()); - Handle handle(hs.NewHandle(loader)); - const char* path_separator = ""; - VisitClassLoaderDexFiles(soa, - handle, - [&](const DexFile* dex_file) { - oss << path_separator << dex_file->GetLocation(); - path_separator = ":"; - return true; // Continue with the next DexFile. - }); - oss << ")"; - } - } - - return oss.str(); -} - ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr klass, ObjPtr dex_cache, ObjPtr class_loader, diff --git a/runtime/debug_print.cc b/runtime/debug_print.cc new file mode 100644 index 0000000000..44a183669d --- /dev/null +++ b/runtime/debug_print.cc @@ -0,0 +1,113 @@ +/* + * 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. + */ + +#include + +#include "debug_print.h" + +#include "class_linker.h" +#include "class_table.h" +#include "class_loader_utils.h" +#include "dex/utf.h" +#include "gc/heap.h" +#include "gc/space/space-inl.h" +#include "mirror/class.h" +#include "mirror/class_loader.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-current-inl.h" +#include "well_known_classes.h" + +namespace art { + +std::string DescribeSpace(ObjPtr klass) { + std::ostringstream oss; + gc::Heap* heap = Runtime::Current()->GetHeap(); + gc::space::ContinuousSpace* cs = + heap->FindContinuousSpaceFromObject(klass.Ptr(), /* fail_ok */ true); + if (cs != nullptr) { + if (cs->IsImageSpace()) { + oss << "image;" << cs->GetName() << ";" << cs->AsImageSpace()->GetImageFilename(); + } else { + oss << "continuous;" << cs->GetName(); + } + } else { + gc::space::DiscontinuousSpace* ds = + heap->FindDiscontinuousSpaceFromObject(klass, /* fail_ok */ true); + if (ds != nullptr) { + oss << "discontinuous;" << ds->GetName(); + } else { + oss << "invalid"; + } + } + return oss.str(); +} + +std::string DescribeLoaders(ObjPtr loader, const char* class_descriptor) { + std::ostringstream oss; + uint32_t hash = ComputeModifiedUtf8Hash(class_descriptor); + ObjPtr path_class_loader = + WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_PathClassLoader); + ObjPtr dex_class_loader = + WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexClassLoader); + ObjPtr delegate_last_class_loader = + WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DelegateLastClassLoader); + + // Print the class loader chain. + bool found_class = false; + const char* loader_separator = ""; + if (loader == nullptr) { + oss << "BootClassLoader"; // This would be unexpected. + } + for (; loader != nullptr; loader = loader->GetParent()) { + oss << loader_separator << loader->GetClass()->PrettyDescriptor(); + loader_separator = ";"; + // If we didn't find the class yet, try to find it in the current class loader. + if (!found_class) { + ClassTable* table = Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(loader); + ObjPtr klass = + (table != nullptr) ? table->Lookup(class_descriptor, hash) : nullptr; + if (klass != nullptr) { + found_class = true; + oss << "[hit:" << DescribeSpace(klass) << "]"; + } + } + + // For PathClassLoader, DexClassLoader or DelegateLastClassLoader + // also dump the dex file locations. + if (loader->GetClass() == path_class_loader || + loader->GetClass() == dex_class_loader || + loader->GetClass() == delegate_last_class_loader) { + oss << "("; + ScopedObjectAccessUnchecked soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Handle handle(hs.NewHandle(loader)); + const char* path_separator = ""; + VisitClassLoaderDexFiles(soa, + handle, + [&](const DexFile* dex_file) { + oss << path_separator << dex_file->GetLocation(); + path_separator = ":"; + return true; // Continue with the next DexFile. + }); + oss << ")"; + } + } + + return oss.str(); +} + +} // namespace art diff --git a/runtime/debug_print.h b/runtime/debug_print.h new file mode 100644 index 0000000000..479c36a02f --- /dev/null +++ b/runtime/debug_print.h @@ -0,0 +1,34 @@ +/* + * 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. + */ + +#ifndef ART_RUNTIME_DEBUG_PRINT_H_ +#define ART_RUNTIME_DEBUG_PRINT_H_ + +#include "base/mutex.h" +#include "mirror/object.h" + +// Helper functions for printing extra information for certain hard to diagnose bugs. + +namespace art { + +std::string DescribeSpace(ObjPtr klass) + REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; +std::string DescribeLoaders(ObjPtr loader, const char* class_descriptor) + REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; + +} // namespace art + +#endif // ART_RUNTIME_DEBUG_PRINT_H_ diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 2284100564..39429c5b41 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -19,6 +19,7 @@ #include "base/enums.h" #include "callee_save_frame.h" #include "common_throws.h" +#include "debug_print.h" #include "debugger.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_types.h" @@ -1187,9 +1188,24 @@ static std::string DumpInstruction(ArtMethod* method, uint32_t dex_pc) } } +static void DumpB74410240ClassData(ObjPtr klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + std::string storage; + const char* descriptor = klass->GetDescriptor(&storage); + LOG(FATAL_WITHOUT_ABORT) << " " << DescribeLoaders(klass->GetClassLoader(), descriptor); + const OatDexFile* oat_dex_file = klass->GetDexFile().GetOatDexFile(); + if (oat_dex_file != nullptr) { + const OatFile* oat_file = oat_dex_file->GetOatFile(); + const char* dex2oat_cmdline = + oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey); + LOG(FATAL_WITHOUT_ABORT) << " OatFile: " << oat_file->GetLocation() + << "; " << (dex2oat_cmdline != nullptr ? dex2oat_cmdline : ""); + } +} + 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."; + LOG(FATAL_WITHOUT_ABORT) << "Dumping debugging data, please attach a bugreport to b/74410240."; constexpr CalleeSaveType type = CalleeSaveType::kSaveRefsAndArgs; CHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(type)); @@ -1227,6 +1243,7 @@ static void DumpB74410240DebugData(ArtMethod** sp) REQUIRES_SHARED(Locks::mutato << " dex pc: " << dex_pc << " dex file: " << outer_method->GetDexFile()->GetLocation() << " class table: " << class_linker->ClassTableForClassLoader(outer_method->GetClassLoader()); + DumpB74410240ClassData(outer_method->GetDeclaringClass()); LOG(FATAL_WITHOUT_ABORT) << " instruction: " << DumpInstruction(outer_method, dex_pc); ArtMethod* caller = outer_method; @@ -1260,7 +1277,8 @@ static void DumpB74410240DebugData(ArtMethod** sp) REQUIRES_SHARED(Locks::mutato << " dex pc: " << dex_pc << " dex file: " << caller->GetDexFile()->GetLocation() << " class table: " - << class_linker->ClassTableForClassLoader(outer_method->GetClassLoader()); + << class_linker->ClassTableForClassLoader(caller->GetClassLoader()); + DumpB74410240ClassData(caller->GetDeclaringClass()); LOG(FATAL_WITHOUT_ABORT) << " instruction: " << DumpInstruction(caller, dex_pc); } } -- GitLab From ddf39558dd3f5f889e9cdfa54807499574098db7 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 3 Apr 2018 10:22:27 -0700 Subject: [PATCH 205/749] Always do cdex conversion for OatWriter dexlayout Don't use DexLayout without CompactDex conversion in the case where a profile is passed in. Bug: 77498934 Test: test-art-host Change-Id: I6c0498db71f1324402b8dfe2ab3ef9bbd121b4cc --- dex2oat/dex2oat.cc | 4 +++- dex2oat/linker/oat_writer.cc | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 88dc6d44b6..220f568d97 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -454,7 +454,9 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(" The image writer will group them together."); UsageError(""); UsageError(" --compact-dex-level=none|fast: None avoids generating compact dex, fast"); - UsageError(" generates compact dex with low compile time."); + UsageError(" generates compact dex with low compile time. If speed-profile is specified as"); + UsageError(" the compiler filter and the profile is not empty, the default compact dex"); + UsageError(" level is always used."); UsageError(""); UsageError(" --deduplicate-code=true|false: enable|disable code deduplication. Deduplicated"); UsageError(" code will have an arbitrary symbol tagged with [DEDUPED]."); diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 65a4c5b244..bcc59098e5 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -445,6 +445,11 @@ OatWriter::OatWriter(bool compiling_boot_image, absolute_patch_locations_(), profile_compilation_info_(info), compact_dex_level_(compact_dex_level) { + // If we have a profile, always use at least the default compact dex level. The reason behind + // this is that CompactDex conversion is not more expensive than normal dexlayout. + if (info != nullptr && compact_dex_level_ == CompactDexLevel::kCompactDexLevelNone) { + compact_dex_level_ = kDefaultCompactDexLevel; + } } static bool ValidateDexFileHeader(const uint8_t* raw_header, const char* location) { -- GitLab From 1ce2b3b76d121a765212d69399241843951973ae Mon Sep 17 00:00:00 2001 From: David Sehr Date: Thu, 5 Apr 2018 11:02:03 -0700 Subject: [PATCH 206/749] Move remaining runtime/base stuff to libartbase Move the remainder of the Arena stuff, plus dumpable and runtime/*memory_region* to libartbase. More preparation to build profiling library. Bug: 22322814 Test: make -j 50 checkbuild Change-Id: Iaf26d310c89bc58846553281576c18102f5e4122 --- compiler/jni/quick/jni_compiler.cc | 2 +- compiler/optimizing/code_generator.h | 2 +- compiler/optimizing/stack_map_stream.h | 2 +- compiler/utils/assembler.cc | 2 +- compiler/utils/assembler.h | 2 +- compiler/utils/jni_macro_assembler.cc | 2 +- compiler/utils/mips/assembler_mips.cc | 2 +- compiler/utils/mips64/assembler_mips64.cc | 2 +- compiler/utils/x86/assembler_x86.cc | 2 +- compiler/utils/x86_64/assembler_x86_64.cc | 2 +- .../x86_64/jni_macro_assembler_x86_64.cc | 2 +- libartbase/Android.bp | 7 +++++ .../base/arena_allocator-inl.h | 6 ++-- .../base/arena_allocator.cc | 0 .../base/arena_allocator.h | 6 ++-- .../base/arena_allocator_test.cc | 0 .../base/arena_bit_vector.cc | 0 .../base/arena_bit_vector.h | 6 ++-- .../base/arena_containers.h | 6 ++-- {runtime => libartbase}/base/arena_object.h | 6 ++-- .../base}/bit_memory_region.h | 6 ++-- {runtime => libartbase}/base/dumpable.h | 28 ++--------------- .../base/malloc_arena_pool.cc | 0 .../base/malloc_arena_pool.h | 6 ++-- {runtime => libartbase/base}/memory_region.cc | 0 {runtime => libartbase/base}/memory_region.h | 13 ++++---- .../base}/memory_region_test.cc | 0 .../base/scoped_arena_allocator.cc | 0 .../base/scoped_arena_allocator.h | 6 ++-- .../base/scoped_arena_containers.h | 6 ++-- runtime/Android.bp | 7 ----- ...mpable-inl.h => mutator_locked_dumpable.h} | 31 ++++++++++++++++--- runtime/indirect_reference_table.cc | 2 +- runtime/jit/profile_compilation_info.cc | 2 -- runtime/jit/profile_compilation_info.h | 2 +- runtime/method_info.h | 2 +- runtime/stack_map.h | 4 +-- 37 files changed, 85 insertions(+), 89 deletions(-) rename {runtime => libartbase}/base/arena_allocator-inl.h (86%) rename {runtime => libartbase}/base/arena_allocator.cc (100%) rename {runtime => libartbase}/base/arena_allocator.h (98%) rename {runtime => libartbase}/base/arena_allocator_test.cc (100%) rename {runtime => libartbase}/base/arena_bit_vector.cc (100%) rename {runtime => libartbase}/base/arena_bit_vector.h (92%) rename {runtime => libartbase}/base/arena_containers.h (98%) rename {runtime => libartbase}/base/arena_object.h (94%) rename {runtime => libartbase/base}/bit_memory_region.h (94%) rename {runtime => libartbase}/base/dumpable.h (63%) rename {runtime => libartbase}/base/malloc_arena_pool.cc (100%) rename {runtime => libartbase}/base/malloc_arena_pool.h (89%) rename {runtime => libartbase/base}/memory_region.cc (100%) rename {runtime => libartbase/base}/memory_region.h (96%) rename {runtime => libartbase/base}/memory_region_test.cc (100%) rename {runtime => libartbase}/base/scoped_arena_allocator.cc (100%) rename {runtime => libartbase}/base/scoped_arena_allocator.h (97%) rename {runtime => libartbase}/base/scoped_arena_containers.h (98%) rename runtime/base/{dumpable-inl.h => mutator_locked_dumpable.h} (52%) diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index 9ccdff3a17..8cb1998f7f 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -28,6 +28,7 @@ #include "base/logging.h" // For VLOG. #include "base/macros.h" #include "base/malloc_arena_pool.h" +#include "base/memory_region.h" #include "base/utils.h" #include "calling_convention.h" #include "class_linker.h" @@ -37,7 +38,6 @@ #include "driver/compiler_options.h" #include "entrypoints/quick/quick_entrypoints.h" #include "jni_env_ext.h" -#include "memory_region.h" #include "thread.h" #include "utils/arm/managed_register_arm.h" #include "utils/arm64/managed_register_arm64.h" diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 3bd5e14539..e1f680fb99 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -24,12 +24,12 @@ #include "base/bit_field.h" #include "base/bit_utils.h" #include "base/enums.h" +#include "base/memory_region.h" #include "dex/string_reference.h" #include "dex/type_reference.h" #include "globals.h" #include "graph_visualizer.h" #include "locations.h" -#include "memory_region.h" #include "nodes.h" #include "optimizing_compiler_stats.h" #include "read_barrier_option.h" diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index 579aabdb5f..268e9bd6e0 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -19,9 +19,9 @@ #include "base/bit_vector-inl.h" #include "base/hash_map.h" +#include "base/memory_region.h" #include "base/scoped_arena_containers.h" #include "base/value_object.h" -#include "memory_region.h" #include "method_info.h" #include "nodes.h" #include "stack_map.h" diff --git a/compiler/utils/assembler.cc b/compiler/utils/assembler.cc index 944c64b591..421c1b6089 100644 --- a/compiler/utils/assembler.cc +++ b/compiler/utils/assembler.cc @@ -20,8 +20,8 @@ #include #include "base/casts.h" +#include "base/memory_region.h" #include "globals.h" -#include "memory_region.h" namespace art { diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h index 5b0cd6baa8..379a6396eb 100644 --- a/compiler/utils/assembler.h +++ b/compiler/utils/assembler.h @@ -29,10 +29,10 @@ #include "base/array_ref.h" #include "base/enums.h" #include "base/macros.h" +#include "base/memory_region.h" #include "debug/dwarf/debug_frame_opcode_writer.h" #include "label.h" #include "managed_register.h" -#include "memory_region.h" #include "mips/constants_mips.h" #include "offsets.h" #include "x86/constants_x86.h" diff --git a/compiler/utils/jni_macro_assembler.cc b/compiler/utils/jni_macro_assembler.cc index 3f7691b6a8..0c34aa4f1d 100644 --- a/compiler/utils/jni_macro_assembler.cc +++ b/compiler/utils/jni_macro_assembler.cc @@ -38,8 +38,8 @@ #include "x86_64/jni_macro_assembler_x86_64.h" #endif #include "base/casts.h" +#include "base/memory_region.h" #include "globals.h" -#include "memory_region.h" namespace art { diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc index b2ad490a57..dce5b95fec 100644 --- a/compiler/utils/mips/assembler_mips.cc +++ b/compiler/utils/mips/assembler_mips.cc @@ -18,9 +18,9 @@ #include "base/bit_utils.h" #include "base/casts.h" +#include "base/memory_region.h" #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" -#include "memory_region.h" #include "thread.h" namespace art { diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc index 5a817fa960..bb1bb82fa5 100644 --- a/compiler/utils/mips64/assembler_mips64.cc +++ b/compiler/utils/mips64/assembler_mips64.cc @@ -18,9 +18,9 @@ #include "base/bit_utils.h" #include "base/casts.h" +#include "base/memory_region.h" #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" -#include "memory_region.h" #include "thread.h" namespace art { diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index 42c2541421..86f9010ea3 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -17,8 +17,8 @@ #include "assembler_x86.h" #include "base/casts.h" +#include "base/memory_region.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "memory_region.h" #include "thread.h" namespace art { diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index c6e16e754c..bd31561937 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -17,8 +17,8 @@ #include "assembler_x86_64.h" #include "base/casts.h" +#include "base/memory_region.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "memory_region.h" #include "thread.h" namespace art { diff --git a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc index 5766f9d44b..9486cb44c5 100644 --- a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc +++ b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc @@ -17,8 +17,8 @@ #include "jni_macro_assembler_x86_64.h" #include "base/casts.h" +#include "base/memory_region.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "memory_region.h" #include "thread.h" namespace art { diff --git a/libartbase/Android.bp b/libartbase/Android.bp index 3c61944477..62157e196d 100644 --- a/libartbase/Android.bp +++ b/libartbase/Android.bp @@ -20,13 +20,18 @@ cc_defaults { host_supported: true, srcs: [ "base/allocator.cc", + "base/arena_allocator.cc", + "base/arena_bit_vector.cc", "base/bit_vector.cc", "base/file_magic.cc", "base/hex_dump.cc", "base/logging.cc", + "base/malloc_arena_pool.cc", + "base/memory_region.cc", "base/os_linux.cc", "base/runtime_debug.cc", "base/safe_copy.cc", + "base/scoped_arena_allocator.cc", "base/scoped_flock.cc", "base/time_utils.cc", "base/unix_file/fd_file.cc", @@ -90,6 +95,7 @@ art_cc_test { "art_gtest_defaults", ], srcs: [ + "base/arena_allocator_test.cc", "base/bit_field_test.cc", "base/bit_string_test.cc", "base/bit_struct_test.cc", @@ -100,6 +106,7 @@ art_cc_test { "base/histogram_test.cc", "base/leb128_test.cc", "base/logging_test.cc", + "base/memory_region_test.cc", "base/safe_copy_test.cc", "base/scoped_flock_test.cc", "base/time_utils_test.cc", diff --git a/runtime/base/arena_allocator-inl.h b/libartbase/base/arena_allocator-inl.h similarity index 86% rename from runtime/base/arena_allocator-inl.h rename to libartbase/base/arena_allocator-inl.h index 0e4383741e..a03e9df3fe 100644 --- a/runtime/base/arena_allocator-inl.h +++ b/libartbase/base/arena_allocator-inl.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_ARENA_ALLOCATOR_INL_H_ -#define ART_RUNTIME_BASE_ARENA_ALLOCATOR_INL_H_ +#ifndef ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_INL_H_ +#define ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_INL_H_ #include "arena_allocator.h" @@ -31,4 +31,4 @@ static constexpr size_t kArenaDefaultSize = kArenaAllocatorPreciseTracking } // namespace arena_allocator } // namespace art -#endif // ART_RUNTIME_BASE_ARENA_ALLOCATOR_INL_H_ +#endif // ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_INL_H_ diff --git a/runtime/base/arena_allocator.cc b/libartbase/base/arena_allocator.cc similarity index 100% rename from runtime/base/arena_allocator.cc rename to libartbase/base/arena_allocator.cc diff --git a/runtime/base/arena_allocator.h b/libartbase/base/arena_allocator.h similarity index 98% rename from runtime/base/arena_allocator.h rename to libartbase/base/arena_allocator.h index f59cfdd69d..3143fbac4c 100644 --- a/runtime/base/arena_allocator.h +++ b/libartbase/base/arena_allocator.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_ARENA_ALLOCATOR_H_ -#define ART_RUNTIME_BASE_ARENA_ALLOCATOR_H_ +#ifndef ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_H_ +#define ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_H_ #include #include @@ -423,4 +423,4 @@ class MemStats { } // namespace art -#endif // ART_RUNTIME_BASE_ARENA_ALLOCATOR_H_ +#endif // ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_H_ diff --git a/runtime/base/arena_allocator_test.cc b/libartbase/base/arena_allocator_test.cc similarity index 100% rename from runtime/base/arena_allocator_test.cc rename to libartbase/base/arena_allocator_test.cc diff --git a/runtime/base/arena_bit_vector.cc b/libartbase/base/arena_bit_vector.cc similarity index 100% rename from runtime/base/arena_bit_vector.cc rename to libartbase/base/arena_bit_vector.cc diff --git a/runtime/base/arena_bit_vector.h b/libartbase/base/arena_bit_vector.h similarity index 92% rename from runtime/base/arena_bit_vector.h rename to libartbase/base/arena_bit_vector.h index ca1d5b1d66..2b2322e74f 100644 --- a/runtime/base/arena_bit_vector.h +++ b/libartbase/base/arena_bit_vector.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_ARENA_BIT_VECTOR_H_ -#define ART_RUNTIME_BASE_ARENA_BIT_VECTOR_H_ +#ifndef ART_LIBARTBASE_BASE_ARENA_BIT_VECTOR_H_ +#define ART_LIBARTBASE_BASE_ARENA_BIT_VECTOR_H_ #include "base/arena_object.h" #include "base/bit_vector.h" @@ -55,4 +55,4 @@ class ArenaBitVector : public BitVector, public ArenaObject #include @@ -241,4 +241,4 @@ inline ArenaAllocatorAdapter ArenaAllocator::Adapter(ArenaAllocKind kind) } // namespace art -#endif // ART_RUNTIME_BASE_ARENA_CONTAINERS_H_ +#endif // ART_LIBARTBASE_BASE_ARENA_CONTAINERS_H_ diff --git a/runtime/base/arena_object.h b/libartbase/base/arena_object.h similarity index 94% rename from runtime/base/arena_object.h rename to libartbase/base/arena_object.h index d01e346f7a..de7cb64a75 100644 --- a/runtime/base/arena_object.h +++ b/libartbase/base/arena_object.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_ARENA_OBJECT_H_ -#define ART_RUNTIME_BASE_ARENA_OBJECT_H_ +#ifndef ART_LIBARTBASE_BASE_ARENA_OBJECT_H_ +#define ART_LIBARTBASE_BASE_ARENA_OBJECT_H_ #include @@ -69,4 +69,4 @@ class DeletableArenaObject { } // namespace art -#endif // ART_RUNTIME_BASE_ARENA_OBJECT_H_ +#endif // ART_LIBARTBASE_BASE_ARENA_OBJECT_H_ diff --git a/runtime/bit_memory_region.h b/libartbase/base/bit_memory_region.h similarity index 94% rename from runtime/bit_memory_region.h rename to libartbase/base/bit_memory_region.h index 3a696f1969..f3926bc9d8 100644 --- a/runtime/bit_memory_region.h +++ b/libartbase/base/bit_memory_region.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BIT_MEMORY_REGION_H_ -#define ART_RUNTIME_BIT_MEMORY_REGION_H_ +#ifndef ART_LIBARTBASE_BASE_BIT_MEMORY_REGION_H_ +#define ART_LIBARTBASE_BASE_BIT_MEMORY_REGION_H_ #include "memory_region.h" @@ -70,4 +70,4 @@ class BitMemoryRegion FINAL : public ValueObject { } // namespace art -#endif // ART_RUNTIME_BIT_MEMORY_REGION_H_ +#endif // ART_LIBARTBASE_BASE_BIT_MEMORY_REGION_H_ diff --git a/runtime/base/dumpable.h b/libartbase/base/dumpable.h similarity index 63% rename from runtime/base/dumpable.h rename to libartbase/base/dumpable.h index 9ef8d69b48..66213972f7 100644 --- a/runtime/base/dumpable.h +++ b/libartbase/base/dumpable.h @@ -14,13 +14,12 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_DUMPABLE_H_ -#define ART_RUNTIME_BASE_DUMPABLE_H_ +#ifndef ART_LIBARTBASE_BASE_DUMPABLE_H_ +#define ART_LIBARTBASE_BASE_DUMPABLE_H_ #include #include "base/macros.h" -#include "base/mutex.h" namespace art { @@ -51,27 +50,6 @@ std::ostream& operator<<(std::ostream& os, const Dumpable& rhs) { return os; } -template -class MutatorLockedDumpable { - public: - explicit MutatorLockedDumpable(T& value) REQUIRES_SHARED(Locks::mutator_lock_) : value_(value) {} - - void Dump(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_) { - value_.Dump(os); - } - - private: - const T& value_; - - DISALLOW_COPY_AND_ASSIGN(MutatorLockedDumpable); -}; - -template -std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable& rhs) - // TODO: should be REQUIRES_SHARED(Locks::mutator_lock_) however annotalysis - // currently fails for this. - NO_THREAD_SAFETY_ANALYSIS; - } // namespace art -#endif // ART_RUNTIME_BASE_DUMPABLE_H_ +#endif // ART_LIBARTBASE_BASE_DUMPABLE_H_ diff --git a/runtime/base/malloc_arena_pool.cc b/libartbase/base/malloc_arena_pool.cc similarity index 100% rename from runtime/base/malloc_arena_pool.cc rename to libartbase/base/malloc_arena_pool.cc diff --git a/runtime/base/malloc_arena_pool.h b/libartbase/base/malloc_arena_pool.h similarity index 89% rename from runtime/base/malloc_arena_pool.h rename to libartbase/base/malloc_arena_pool.h index 9cd2572411..8720189343 100644 --- a/runtime/base/malloc_arena_pool.h +++ b/libartbase/base/malloc_arena_pool.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_MALLOC_ARENA_POOL_H_ -#define ART_RUNTIME_BASE_MALLOC_ARENA_POOL_H_ +#ifndef ART_LIBARTBASE_BASE_MALLOC_ARENA_POOL_H_ +#define ART_LIBARTBASE_BASE_MALLOC_ARENA_POOL_H_ #include @@ -45,4 +45,4 @@ class MallocArenaPool FINAL : public ArenaPool { } // namespace art -#endif // ART_RUNTIME_BASE_MALLOC_ARENA_POOL_H_ +#endif // ART_LIBARTBASE_BASE_MALLOC_ARENA_POOL_H_ diff --git a/runtime/memory_region.cc b/libartbase/base/memory_region.cc similarity index 100% rename from runtime/memory_region.cc rename to libartbase/base/memory_region.cc diff --git a/runtime/memory_region.h b/libartbase/base/memory_region.h similarity index 96% rename from runtime/memory_region.h rename to libartbase/base/memory_region.h index 23e0aecbda..7add466cc7 100644 --- a/runtime/memory_region.h +++ b/libartbase/base/memory_region.h @@ -14,17 +14,17 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_MEMORY_REGION_H_ -#define ART_RUNTIME_MEMORY_REGION_H_ +#ifndef ART_LIBARTBASE_BASE_MEMORY_REGION_H_ +#define ART_LIBARTBASE_BASE_MEMORY_REGION_H_ #include #include #include -#include "arch/instruction_set.h" #include "base/bit_utils.h" #include "base/casts.h" +#include "base/enums.h" #include "base/macros.h" #include "base/value_object.h" #include "globals.h" @@ -211,9 +211,8 @@ class MemoryRegion FINAL : public ValueObject { // Is `address` aligned on a machine word? template static constexpr bool IsWordAligned(const T* address) { - // Word alignment in bytes. - size_t kWordAlignment = static_cast(GetInstructionSetPointerSize(kRuntimeISA)); - return IsAlignedParam(address, kWordAlignment); + // Word alignment in bytes. Determined from pointer size. + return IsAligned(address); } void* pointer_; @@ -222,4 +221,4 @@ class MemoryRegion FINAL : public ValueObject { } // namespace art -#endif // ART_RUNTIME_MEMORY_REGION_H_ +#endif // ART_LIBARTBASE_BASE_MEMORY_REGION_H_ diff --git a/runtime/memory_region_test.cc b/libartbase/base/memory_region_test.cc similarity index 100% rename from runtime/memory_region_test.cc rename to libartbase/base/memory_region_test.cc diff --git a/runtime/base/scoped_arena_allocator.cc b/libartbase/base/scoped_arena_allocator.cc similarity index 100% rename from runtime/base/scoped_arena_allocator.cc rename to libartbase/base/scoped_arena_allocator.cc diff --git a/runtime/base/scoped_arena_allocator.h b/libartbase/base/scoped_arena_allocator.h similarity index 97% rename from runtime/base/scoped_arena_allocator.h rename to libartbase/base/scoped_arena_allocator.h index a253e2f535..d5f6df82cc 100644 --- a/runtime/base/scoped_arena_allocator.h +++ b/libartbase/base/scoped_arena_allocator.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_SCOPED_ARENA_ALLOCATOR_H_ -#define ART_RUNTIME_BASE_SCOPED_ARENA_ALLOCATOR_H_ +#ifndef ART_LIBARTBASE_BASE_SCOPED_ARENA_ALLOCATOR_H_ +#define ART_LIBARTBASE_BASE_SCOPED_ARENA_ALLOCATOR_H_ #include @@ -185,4 +185,4 @@ class ScopedArenaAllocator } // namespace art -#endif // ART_RUNTIME_BASE_SCOPED_ARENA_ALLOCATOR_H_ +#endif // ART_LIBARTBASE_BASE_SCOPED_ARENA_ALLOCATOR_H_ diff --git a/runtime/base/scoped_arena_containers.h b/libartbase/base/scoped_arena_containers.h similarity index 98% rename from runtime/base/scoped_arena_containers.h rename to libartbase/base/scoped_arena_containers.h index f8ee3f33af..4df02b6205 100644 --- a/runtime/base/scoped_arena_containers.h +++ b/libartbase/base/scoped_arena_containers.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_SCOPED_ARENA_CONTAINERS_H_ -#define ART_RUNTIME_BASE_SCOPED_ARENA_CONTAINERS_H_ +#ifndef ART_LIBARTBASE_BASE_SCOPED_ARENA_CONTAINERS_H_ +#define ART_LIBARTBASE_BASE_SCOPED_ARENA_CONTAINERS_H_ #include #include @@ -272,4 +272,4 @@ using ArenaUniquePtr = std::unique_ptr>; } // namespace art -#endif // ART_RUNTIME_BASE_SCOPED_ARENA_CONTAINERS_H_ +#endif // ART_LIBARTBASE_BASE_SCOPED_ARENA_CONTAINERS_H_ diff --git a/runtime/Android.bp b/runtime/Android.bp index b380dab187..7126687a00 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -32,14 +32,10 @@ cc_defaults { "art_field.cc", "art_method.cc", "barrier.cc", - "base/arena_allocator.cc", - "base/arena_bit_vector.cc", "base/file_utils.cc", - "base/malloc_arena_pool.cc", "base/mem_map_arena_pool.cc", "base/mutex.cc", "base/quasi_atomic.cc", - "base/scoped_arena_allocator.cc", "base/timing_logger.cc", "cha.cc", "check_jni.cc", @@ -124,7 +120,6 @@ cc_defaults { "linear_alloc.cc", "managed_stack.cc", "mem_map.cc", - "memory_region.cc", "method_handles.cc", "mirror/array.cc", "mirror/call_site.cc", @@ -543,7 +538,6 @@ art_cc_test { "arch/x86/instruction_set_features_x86_test.cc", "arch/x86_64/instruction_set_features_x86_64_test.cc", "barrier_test.cc", - "base/arena_allocator_test.cc", "base/file_utils_test.cc", "base/mutex_test.cc", "base/timing_logger_test.cc", @@ -587,7 +581,6 @@ art_cc_test { "java_vm_ext_test.cc", "jit/profile_compilation_info_test.cc", "mem_map_test.cc", - "memory_region_test.cc", "method_handles_test.cc", "mirror/dex_cache_test.cc", "mirror/method_type_test.cc", diff --git a/runtime/base/dumpable-inl.h b/runtime/base/mutator_locked_dumpable.h similarity index 52% rename from runtime/base/dumpable-inl.h rename to runtime/base/mutator_locked_dumpable.h index 9d7fc39093..cf2199c100 100644 --- a/runtime/base/dumpable-inl.h +++ b/runtime/base/mutator_locked_dumpable.h @@ -14,17 +14,38 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_DUMPABLE_INL_H_ -#define ART_RUNTIME_BASE_DUMPABLE_INL_H_ +#ifndef ART_RUNTIME_BASE_MUTATOR_LOCKED_DUMPABLE_H_ +#define ART_RUNTIME_BASE_MUTATOR_LOCKED_DUMPABLE_H_ -#include "base/dumpable.h" #include "base/mutex.h" #include "thread-current-inl.h" namespace art { template -inline std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable& rhs) { +class MutatorLockedDumpable { + public: + explicit MutatorLockedDumpable(T& value) REQUIRES_SHARED(Locks::mutator_lock_) : value_(value) {} + + void Dump(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_) { + value_.Dump(os); + } + + private: + const T& value_; + + DISALLOW_COPY_AND_ASSIGN(MutatorLockedDumpable); +}; + +// template +// std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable& rhs) +// // TODO: should be REQUIRES_SHARED(Locks::mutator_lock_) however annotalysis +// // currently fails for this. +// NO_THREAD_SAFETY_ANALYSIS; + +template +inline std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable& rhs) + NO_THREAD_SAFETY_ANALYSIS { Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); rhs.Dump(os); return os; @@ -32,4 +53,4 @@ inline std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable } // namespace art -#endif // ART_RUNTIME_BASE_DUMPABLE_INL_H_ +#endif // ART_RUNTIME_BASE_MUTATOR_LOCKED_DUMPABLE_H_ diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc index 3b9cc0f946..6143ba6fd4 100644 --- a/runtime/indirect_reference_table.cc +++ b/runtime/indirect_reference_table.cc @@ -16,7 +16,7 @@ #include "indirect_reference_table-inl.h" -#include "base/dumpable-inl.h" +#include "base/mutator_locked_dumpable.h" #include "base/systrace.h" #include "base/utils.h" #include "java_vm_ext.h" diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index f4132c2343..6bbe78f3fd 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -34,10 +34,8 @@ #include "base/arena_allocator.h" #include "base/dumpable.h" -#include "base/file_utils.h" #include "base/logging.h" // For VLOG. #include "base/malloc_arena_pool.h" -#include "base/mutex.h" #include "base/os.h" #include "base/safe_map.h" #include "base/scoped_flock.h" diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h index 4ac8c612e0..f8334cea4f 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -23,9 +23,9 @@ #include "base/arena_containers.h" #include "base/arena_object.h" #include "base/atomic.h" +#include "base/bit_memory_region.h" #include "base/malloc_arena_pool.h" #include "base/safe_map.h" -#include "bit_memory_region.h" #include "dex/dex_cache_resolved_classes.h" #include "dex/dex_file.h" #include "dex/dex_file_types.h" diff --git a/runtime/method_info.h b/runtime/method_info.h index fe062564f1..b00ddc660f 100644 --- a/runtime/method_info.h +++ b/runtime/method_info.h @@ -21,7 +21,7 @@ #include "base/leb128.h" #include "base/macros.h" -#include "memory_region.h" +#include "base/memory_region.h" namespace art { diff --git a/runtime/stack_map.h b/runtime/stack_map.h index bde3462437..38397643b6 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -20,12 +20,12 @@ #include #include "arch/code_offset.h" +#include "base/bit_memory_region.h" #include "base/bit_utils.h" #include "base/bit_vector.h" #include "base/leb128.h" -#include "bit_memory_region.h" +#include "base/memory_region.h" #include "dex/dex_file_types.h" -#include "memory_region.h" #include "method_info.h" namespace art { -- GitLab From 276d29b9db2eb6e3667bc3d93f66846c5655e80b Mon Sep 17 00:00:00 2001 From: Alan Leung Date: Thu, 5 Apr 2018 11:37:40 -0700 Subject: [PATCH 207/749] Move 712-varhandle-invocations to API level 28 Bug: 65168732 Test: art/test/run-test --no-relocate --64 --build-with-javac-dx --runtime-option -Xcheck:jni --never-clean --host 712-varhandle-invocations Change-Id: Ie8cab287541dd82324cc8bb8365bd31c552d0518 --- test/712-varhandle-invocations/build | 3 --- test/etc/default-build | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/test/712-varhandle-invocations/build b/test/712-varhandle-invocations/build index 6d4429f0ef..253765be91 100755 --- a/test/712-varhandle-invocations/build +++ b/test/712-varhandle-invocations/build @@ -35,8 +35,5 @@ python3 ./util-src/generate_java.py "${GENERATED_SRC}" ${MANUAL_TESTS} # Desugar is not happy with our Java 9 byte code, it shouldn't be necessary here anyway. export USE_DESUGAR=false -# See b/65168732 -export USE_D8=false - # Invoke default build with increased heap size for dx ./default-build "$@" --experimental var-handles --dx-vm-option -JXmx384m diff --git a/test/etc/default-build b/test/etc/default-build index dd5560213a..8bb898c7c1 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -145,7 +145,7 @@ JAVAC_EXPERIMENTAL_ARGS["agents"]="-source 1.8 -target 1.8" declare -A DX_EXPERIMENTAL_ARGS DX_EXPERIMENTAL_ARGS["method-handles"]="--min-sdk-version=26" DX_EXPERIMENTAL_ARGS["parameter-annotations"]="--min-sdk-version=25" -DX_EXPERIMENTAL_ARGS["var-handles"]="--min-sdk-version=26" +DX_EXPERIMENTAL_ARGS["var-handles"]="--min-sdk-version=28" while true; do if [ "x$1" = "x--dx-option" ]; then -- GitLab From 567dc6f16fd8d399b19e5c9f6199ba5be82374d9 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 5 Apr 2018 16:37:14 -0700 Subject: [PATCH 208/749] Revert "Revert "Write shared data section for oatdump export dex"" Test: test-art-target-gtest-oatdump_test64 -j64 Bug: 77469384 This reverts commit 4b670183dcc5173c19922b3f3d87c2fbddbb7a34. Change-Id: Ic727a005436753ea58ee5d10cd1aca5fbd532c6a --- build/Android.gtest.mk | 6 ++++-- oatdump/oatdump.cc | 25 +++++++++++++++++++++---- oatdump/oatdump_test.cc | 7 +++++++ oatdump/oatdump_test.h | 39 ++++++++++++++++++++++++--------------- 4 files changed, 56 insertions(+), 21 deletions(-) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index b342abe17c..b483e5f6f2 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -300,11 +300,13 @@ ART_GTEST_oatdump_test_HOST_DEPS := \ $(HOST_CORE_IMAGE_DEFAULT_64) \ $(HOST_CORE_IMAGE_DEFAULT_32) \ oatdumpd-host \ - oatdumpds-host + oatdumpds-host \ + dexdump2-host ART_GTEST_oatdump_test_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_DEFAULT_64) \ $(TARGET_CORE_IMAGE_DEFAULT_32) \ - oatdumpd-target + oatdumpd-target \ + dexdump2-target ART_GTEST_oatdump_image_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS) ART_GTEST_oatdump_image_test_TARGET_DEPS := $(ART_GTEST_oatdump_test_TARGET_DEPS) ART_GTEST_oatdump_app_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS) \ diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 433ed9aaee..3bff123386 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1180,6 +1180,17 @@ class OatDumper { } } + // Update header for shared section. + uint32_t shared_section_offset = 0u; + uint32_t shared_section_size = 0u; + if (dex_file->IsCompactDexFile()) { + CompactDexFile::Header* const header = + reinterpret_cast(const_cast(dex_file->Begin())); + shared_section_offset = header->data_off_; + shared_section_size = header->data_size_; + // The shared section will be serialized right after the dex file. + header->data_off_ = header->file_size_; + } // Verify output directory exists if (!OS::DirectoryExists(options_.export_dex_location_)) { // TODO: Extend OS::DirectoryExists if symlink support is required @@ -1226,16 +1237,22 @@ class OatDumper { return false; } - bool success = false; - success = file->WriteFully(dex_file->Begin(), fsize); - // } - + bool success = file->WriteFully(dex_file->Begin(), fsize); if (!success) { os << "Failed to write dex file"; file->Erase(); return false; } + if (shared_section_size != 0) { + success = file->WriteFully(dex_file->Begin() + shared_section_offset, shared_section_size); + if (!success) { + os << "Failed to write shared data section"; + file->Erase(); + return false; + } + } + if (file->FlushCloseOrErase() != 0) { os << "Flush and close failed"; return false; diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc index 00344691e0..b4eddb91f9 100644 --- a/oatdump/oatdump_test.cc +++ b/oatdump/oatdump_test.cc @@ -72,9 +72,16 @@ TEST_F(OatDumpTest, TestSymbolizeStatic) { } TEST_F(OatDumpTest, TestExportDex) { + // Test is failing on target, b/77469384. + TEST_DISABLED_FOR_TARGET(); std::string error_msg; ASSERT_TRUE(Exec(kDynamic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly, &error_msg)) << error_msg; + const std::string dex_location = tmp_dir_+ "/core-oj-hostdex.jar_export.dex"; + const std::string dexdump2 = GetExecutableFilePath("dexdump2", + /*is_debug*/false, + /*is_static*/false); + ASSERT_TRUE(ForkAndExecAndWait({dexdump2, "-d", dex_location}, &error_msg)) << error_msg; } TEST_F(OatDumpTest, TestExportDexStatic) { TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h index fac0bb234e..b85730d25e 100644 --- a/oatdump/oatdump_test.h +++ b/oatdump/oatdump_test.h @@ -70,20 +70,24 @@ class OatDumpTest : public CommonRuntimeTest { kStatic, // oatdump(d)s, dex2oat(d)s }; - // Returns path to the oatdump/dex2oat binary. - std::string GetExecutableFilePath(Flavor flavor, const char* name) { + // Returns path to the oatdump/dex2oat/dexdump binary. + std::string GetExecutableFilePath(const char* name, bool is_debug, bool is_static) { std::string root = GetTestAndroidRoot(); root += "/bin/"; root += name; - if (kIsDebugBuild) { + if (is_debug) { root += "d"; } - if (flavor == kStatic) { + if (is_static) { root += "s"; } return root; } + std::string GetExecutableFilePath(Flavor flavor, const char* name) { + return GetExecutableFilePath(name, kIsDebugBuild, flavor == kStatic); + } + enum Mode { kModeOat, kModeOatWithBootImage, @@ -127,17 +131,7 @@ class OatDumpTest : public CommonRuntimeTest { }; exec_argv.insert(exec_argv.end(), args.begin(), args.end()); - pid_t pid; - int pipe_fd; - bool result = ForkAndExec(exec_argv, &pid, &pipe_fd, error_msg); - if (result) { - close(pipe_fd); - int status = 0; - if (waitpid(pid, &status, 0) != -1) { - result = (status == 0); - } - } - return result; + return ForkAndExecAndWait(exec_argv, error_msg); } // Run the test with custom arguments. @@ -300,6 +294,21 @@ class OatDumpTest : public CommonRuntimeTest { } } + bool ForkAndExecAndWait(const std::vector& exec_argv, + /*out*/ std::string* error_msg) { + pid_t pid; + int pipe_fd; + bool result = ForkAndExec(exec_argv, &pid, &pipe_fd, error_msg); + if (result) { + close(pipe_fd); + int status = 0; + if (waitpid(pid, &status, 0) != -1) { + result = (status == 0); + } + } + return result; + } + std::string tmp_dir_; private: -- GitLab From 9f8d312a91314b0f9985a0db7cfd5aac3a52a34c Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 6 Apr 2018 13:47:59 +0100 Subject: [PATCH 209/749] x86: Remove unnecessary temp from CheckCast locations. This should have been removed in https://android-review.googlesource.com/300816 . Also clean up related comments and refactor type checks in the x86-64 codegen in line with the refactoring for other architectures in that CL. Test: testrunner.py --host --optimizing Change-Id: I721338985e5388ecd9216e11bcd1c772de8416e6 --- compiler/optimizing/code_generator_x86.cc | 8 ++- compiler/optimizing/code_generator_x86_64.cc | 57 +++++++++----------- 2 files changed, 28 insertions(+), 37 deletions(-) diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 4053f557d9..82d1fda878 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -6755,8 +6755,8 @@ static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) { return 0; } -// Interface case has 3 temps, one for holding the number of interfaces, one for the current -// interface pointer, one for loading the current interface. +// Interface case has 2 temps, one for holding the number of interfaces, one for the current +// interface pointer, the current interface is compared in memory. // The other checks have one temp for loading the object's class. static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) { if (type_check_kind == TypeCheckKind::kInterfaceCheck) { @@ -7069,9 +7069,7 @@ void LocationsBuilderX86::VisitCheckCast(HCheckCast* instruction) { } else { locations->SetInAt(1, Location::Any()); } - // Note that TypeCheckSlowPathX86 uses this "temp" register too. - locations->AddTemp(Location::RequiresRegister()); - // When read barriers are enabled, we need an additional temporary register for some cases. + // Add temps for read barriers and other uses. One is used by TypeCheckSlowPathX86. locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 496d79d6c8..322b0cfc4c 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -6063,24 +6063,26 @@ void InstructionCodeGeneratorX86_64::VisitThrow(HThrow* instruction) { CheckEntrypointTypes(); } -static bool CheckCastTypeCheckNeedsATemporary(TypeCheckKind type_check_kind) { - if (type_check_kind == TypeCheckKind::kInterfaceCheck) { - // We need a temporary for holding the iftable length. - return true; - } - return kEmitCompilerReadBarrier && +// Temp is used for read barrier. +static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) { + if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier && (type_check_kind == TypeCheckKind::kAbstractClassCheck || type_check_kind == TypeCheckKind::kClassHierarchyCheck || - type_check_kind == TypeCheckKind::kArrayObjectCheck); + type_check_kind == TypeCheckKind::kArrayObjectCheck)) { + return 1; + } + return 0; } -static bool InstanceOfTypeCheckNeedsATemporary(TypeCheckKind type_check_kind) { - return kEmitCompilerReadBarrier && - !kUseBakerReadBarrier && - (type_check_kind == TypeCheckKind::kAbstractClassCheck || - type_check_kind == TypeCheckKind::kClassHierarchyCheck || - type_check_kind == TypeCheckKind::kArrayObjectCheck); +// Interface case has 2 temps, one for holding the number of interfaces, one for the current +// interface pointer, the current interface is compared in memory. +// The other checks have one temp for loading the object's class. +static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) { + if (type_check_kind == TypeCheckKind::kInterfaceCheck) { + return 2; + } + return 1 + NumberOfInstanceOfTemps(type_check_kind); } void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) { @@ -6121,11 +6123,7 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) { } // Note that TypeCheckSlowPathX86_64 uses this "out" register too. locations->SetOut(Location::RequiresRegister()); - // When read barriers are enabled, we need a temporary register for - // some cases. - if (InstanceOfTypeCheckNeedsATemporary(type_check_kind)) { - locations->AddTemp(Location::RequiresRegister()); - } + locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind)); } void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { @@ -6136,9 +6134,9 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { Location cls = locations->InAt(1); Location out_loc = locations->Out(); CpuRegister out = out_loc.AsRegister(); - Location maybe_temp_loc = InstanceOfTypeCheckNeedsATemporary(type_check_kind) ? - locations->GetTemp(0) : - Location::NoLocation(); + const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); + DCHECK_LE(num_temps, 1u); + Location maybe_temp_loc = (num_temps >= 1u) ? locations->GetTemp(0) : Location::NoLocation(); uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); @@ -6401,14 +6399,8 @@ void LocationsBuilderX86_64::VisitCheckCast(HCheckCast* instruction) { } else { locations->SetInAt(1, Location::Any()); } - - // Note that TypeCheckSlowPathX86_64 uses this "temp" register too. - locations->AddTemp(Location::RequiresRegister()); - // When read barriers are enabled, we need an additional temporary - // register for some cases. - if (CheckCastTypeCheckNeedsATemporary(type_check_kind)) { - locations->AddTemp(Location::RequiresRegister()); - } + // Add temps for read barriers and other uses. One is used by TypeCheckSlowPathX86. + locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { @@ -6419,9 +6411,10 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { Location cls = locations->InAt(1); Location temp_loc = locations->GetTemp(0); CpuRegister temp = temp_loc.AsRegister(); - Location maybe_temp2_loc = CheckCastTypeCheckNeedsATemporary(type_check_kind) ? - locations->GetTemp(1) : - Location::NoLocation(); + const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); + DCHECK_GE(num_temps, 1u); + DCHECK_LE(num_temps, 2u); + Location maybe_temp2_loc = (num_temps >= 2u) ? locations->GetTemp(1) : Location::NoLocation(); const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); -- GitLab From 2ebff0539394b1936d3c364bec4b1ffd85cd630e Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 4 Apr 2018 22:32:03 +0100 Subject: [PATCH 210/749] [veridex] Add a flow analysis pass to detect precise reflection usages. bug: 77513322 Test: m (cherry picked from commit 242758af3cf6eae389f43d3804acaabaa4ba93da) Change-Id: Ifd9bed0819541ea88fc5363b5c7c2726972456ed --- tools/veridex/Android.bp | 2 + tools/veridex/README.md | 2 +- tools/veridex/appcompat.sh | 2 +- tools/veridex/flow_analysis.cc | 602 +++++++++++++++++++++ tools/veridex/flow_analysis.h | 147 +++++ tools/veridex/hidden_api.h | 12 + tools/veridex/hidden_api_finder.cc | 65 +-- tools/veridex/hidden_api_finder.h | 4 +- tools/veridex/precise_hidden_api_finder.cc | 92 ++++ tools/veridex/precise_hidden_api_finder.h | 55 ++ tools/veridex/resolver.cc | 36 ++ tools/veridex/resolver.h | 5 + tools/veridex/veridex.cc | 66 ++- tools/veridex/veridex.h | 38 +- 14 files changed, 1075 insertions(+), 53 deletions(-) create mode 100644 tools/veridex/flow_analysis.cc create mode 100644 tools/veridex/flow_analysis.h create mode 100644 tools/veridex/precise_hidden_api_finder.cc create mode 100644 tools/veridex/precise_hidden_api_finder.h diff --git a/tools/veridex/Android.bp b/tools/veridex/Android.bp index cbf62d9e9c..570960c4da 100644 --- a/tools/veridex/Android.bp +++ b/tools/veridex/Android.bp @@ -16,8 +16,10 @@ cc_binary { name: "veridex", host_supported: true, srcs: [ + "flow_analysis.cc", "hidden_api.cc", "hidden_api_finder.cc", + "precise_hidden_api_finder.cc", "resolver.cc", "veridex.cc", ], diff --git a/tools/veridex/README.md b/tools/veridex/README.md index 0f91b08771..f85a51bb48 100644 --- a/tools/veridex/README.md +++ b/tools/veridex/README.md @@ -11,4 +11,4 @@ To build it: > make appcompat To run it: -> ./art/tools/veridex/appcompat.sh test.apk +> ./art/tools/veridex/appcompat.sh --dex-file=test.apk diff --git a/tools/veridex/appcompat.sh b/tools/veridex/appcompat.sh index f75aa4f0d0..31a8654b58 100755 --- a/tools/veridex/appcompat.sh +++ b/tools/veridex/appcompat.sh @@ -48,4 +48,4 @@ ${ANDROID_HOST_OUT}/bin/veridex \ --blacklist=${PACKAGING}/hiddenapi-blacklist.txt \ --light-greylist=${PACKAGING}/hiddenapi-light-greylist.txt \ --dark-greylist=${PACKAGING}/hiddenapi-dark-greylist.txt \ - --dex-file=$1 + $@ diff --git a/tools/veridex/flow_analysis.cc b/tools/veridex/flow_analysis.cc new file mode 100644 index 0000000000..abd0b9b28c --- /dev/null +++ b/tools/veridex/flow_analysis.cc @@ -0,0 +1,602 @@ +/* + * 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. + */ + +#include "flow_analysis.h" + +#include "dex/bytecode_utils.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_instruction-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_exception_helpers.h" +#include "resolver.h" +#include "veridex.h" + +namespace art { + + +void VeriFlowAnalysis::SetAsBranchTarget(uint32_t dex_pc) { + if (dex_registers_[dex_pc] == nullptr) { + dex_registers_[dex_pc].reset( + new std::vector(code_item_accessor_.RegistersSize())); + } +} + +bool VeriFlowAnalysis::IsBranchTarget(uint32_t dex_pc) { + return dex_registers_[dex_pc] != nullptr; +} + +bool VeriFlowAnalysis::MergeRegisterValues(uint32_t dex_pc) { + // TODO: Do the merging. Right now, just return that we should continue + // the iteration if the instruction has not been visited. + return !instruction_infos_[dex_pc].has_been_visited; +} + +void VeriFlowAnalysis::SetVisited(uint32_t dex_pc) { + instruction_infos_[dex_pc].has_been_visited = true; +} + +void VeriFlowAnalysis::FindBranches() { + SetAsBranchTarget(0); + + if (code_item_accessor_.TriesSize() != 0) { + // TODO: We need to mark the range of dex pcs as flowing in the handlers. + /* + for (const DexFile::TryItem& try_item : code_item_accessor_.TryItems()) { + uint32_t dex_pc_start = try_item.start_addr_; + uint32_t dex_pc_end = dex_pc_start + try_item.insn_count_; + } + */ + + // Create branch targets for exception handlers. + const uint8_t* handlers_ptr = code_item_accessor_.GetCatchHandlerData(); + uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); + for (uint32_t idx = 0; idx < handlers_size; ++idx) { + CatchHandlerIterator iterator(handlers_ptr); + for (; iterator.HasNext(); iterator.Next()) { + SetAsBranchTarget(iterator.GetHandlerAddress()); + } + handlers_ptr = iterator.EndDataPointer(); + } + } + + // Iterate over all instructions and find branching instructions. + for (const DexInstructionPcPair& pair : code_item_accessor_) { + const uint32_t dex_pc = pair.DexPc(); + const Instruction& instruction = pair.Inst(); + + if (instruction.IsBranch()) { + SetAsBranchTarget(dex_pc + instruction.GetTargetOffset()); + } else if (instruction.IsSwitch()) { + DexSwitchTable table(instruction, dex_pc); + for (DexSwitchTableIterator s_it(table); !s_it.Done(); s_it.Advance()) { + SetAsBranchTarget(dex_pc + s_it.CurrentTargetOffset()); + if (table.ShouldBuildDecisionTree() && !s_it.IsLast()) { + SetAsBranchTarget(s_it.GetDexPcForCurrentIndex()); + } + } + } + } +} + +void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, + RegisterSource kind, + VeriClass* cls, + uint32_t source_id) { + current_registers_[dex_register] = RegisterValue( + kind, DexFileReference(&resolver_->GetDexFile(), source_id), cls); +} + +void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, const RegisterValue& value) { + current_registers_[dex_register] = value; +} + +void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, const VeriClass* cls) { + current_registers_[dex_register] = + RegisterValue(RegisterSource::kNone, DexFileReference(nullptr, 0), cls); +} + +const RegisterValue& VeriFlowAnalysis::GetRegister(uint32_t dex_register) { + return current_registers_[dex_register]; +} + +RegisterValue VeriFlowAnalysis::GetReturnType(uint32_t method_index) { + const DexFile& dex_file = resolver_->GetDexFile(); + const DexFile::MethodId& method_id = dex_file.GetMethodId(method_index); + const DexFile::ProtoId& proto_id = dex_file.GetMethodPrototype(method_id); + VeriClass* cls = resolver_->GetVeriClass(proto_id.return_type_idx_); + return RegisterValue(RegisterSource::kMethod, DexFileReference(&dex_file, method_index), cls); +} + +RegisterValue VeriFlowAnalysis::GetFieldType(uint32_t field_index) { + const DexFile& dex_file = resolver_->GetDexFile(); + const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); + VeriClass* cls = resolver_->GetVeriClass(field_id.type_idx_); + return RegisterValue(RegisterSource::kField, DexFileReference(&dex_file, field_index), cls); +} + +void VeriFlowAnalysis::AnalyzeCode() { + std::vector work_list; + work_list.push_back(0); + // Iterate over the code. + // When visiting unconditional branches (goto), move to that instruction. + // When visiting conditional branches, move to one destination, and put the other + // in the worklist. + while (!work_list.empty()) { + uint32_t dex_pc = work_list.back(); + work_list.pop_back(); + CHECK(IsBranchTarget(dex_pc)); + current_registers_ = *dex_registers_[dex_pc].get(); + while (true) { + const uint16_t* insns = code_item_accessor_.Insns() + dex_pc; + const Instruction& inst = *Instruction::At(insns); + ProcessDexInstruction(inst); + SetVisited(dex_pc); + + int opcode_flags = Instruction::FlagsOf(inst.Opcode()); + if ((opcode_flags & Instruction::kContinue) != 0) { + if ((opcode_flags & Instruction::kBranch) != 0) { + uint32_t branch_dex_pc = dex_pc + inst.GetTargetOffset(); + if (MergeRegisterValues(branch_dex_pc)) { + work_list.push_back(branch_dex_pc); + } + } + dex_pc += inst.SizeInCodeUnits(); + } else if ((opcode_flags & Instruction::kBranch) != 0) { + dex_pc += inst.GetTargetOffset(); + DCHECK(IsBranchTarget(dex_pc)); + } else { + break; + } + + if (IsBranchTarget(dex_pc)) { + if (MergeRegisterValues(dex_pc)) { + current_registers_ = *dex_registers_[dex_pc].get(); + } else { + break; + } + } + } + } +} + +void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { + switch (instruction.Opcode()) { + case Instruction::CONST_4: + case Instruction::CONST_16: + case Instruction::CONST: + case Instruction::CONST_HIGH16: { + int32_t register_index = instruction.VRegA(); + UpdateRegister(register_index, VeriClass::integer_); + break; + } + + case Instruction::CONST_WIDE_16: + case Instruction::CONST_WIDE_32: + case Instruction::CONST_WIDE: + case Instruction::CONST_WIDE_HIGH16: { + int32_t register_index = instruction.VRegA(); + UpdateRegister(register_index, VeriClass::long_); + break; + } + + case Instruction::MOVE: + case Instruction::MOVE_FROM16: + case Instruction::MOVE_16: { + UpdateRegister(instruction.VRegA(), GetRegister(instruction.VRegB())); + break; + } + + case Instruction::MOVE_WIDE: + case Instruction::MOVE_WIDE_FROM16: + case Instruction::MOVE_WIDE_16: { + UpdateRegister(instruction.VRegA(), GetRegister(instruction.VRegB())); + break; + } + + case Instruction::MOVE_OBJECT: + case Instruction::MOVE_OBJECT_16: + case Instruction::MOVE_OBJECT_FROM16: { + UpdateRegister(instruction.VRegA(), GetRegister(instruction.VRegB())); + break; + } + case Instruction::CONST_CLASS: { + UpdateRegister(instruction.VRegA_21c(), + RegisterSource::kClass, + VeriClass::class_, + instruction.VRegB_21c()); + break; + } + case Instruction::CONST_STRING: { + UpdateRegister(instruction.VRegA_21c(), + RegisterSource::kString, + VeriClass::string_, + instruction.VRegB_21c()); + break; + } + + case Instruction::CONST_STRING_JUMBO: { + UpdateRegister(instruction.VRegA_31c(), + RegisterSource::kString, + VeriClass::string_, + instruction.VRegB_31c()); + break; + } + case Instruction::INVOKE_DIRECT: + case Instruction::INVOKE_INTERFACE: + case Instruction::INVOKE_STATIC: + case Instruction::INVOKE_SUPER: + case Instruction::INVOKE_VIRTUAL: { + VeriMethod method = resolver_->GetMethod(instruction.VRegB_35c()); + uint32_t args[5]; + instruction.GetVarArgs(args); + if (method == VeriClass::forName_) { + RegisterValue value = GetRegister(args[0]); + last_result_ = RegisterValue( + value.GetSource(), value.GetDexFileReference(), VeriClass::class_); + } else if (IsGetField(method)) { + RegisterValue cls = GetRegister(args[0]); + RegisterValue name = GetRegister(args[1]); + field_uses_.push_back(std::make_pair(cls, name)); + last_result_ = GetReturnType(instruction.VRegB_35c()); + } else if (IsGetMethod(method)) { + RegisterValue cls = GetRegister(args[0]); + RegisterValue name = GetRegister(args[1]); + method_uses_.push_back(std::make_pair(cls, name)); + last_result_ = GetReturnType(instruction.VRegB_35c()); + } else if (method == VeriClass::getClass_) { + RegisterValue obj = GetRegister(args[0]); + last_result_ = RegisterValue( + obj.GetSource(), obj.GetDexFileReference(), VeriClass::class_); + } else { + last_result_ = GetReturnType(instruction.VRegB_35c()); + } + break; + } + + case Instruction::INVOKE_DIRECT_RANGE: + case Instruction::INVOKE_INTERFACE_RANGE: + case Instruction::INVOKE_STATIC_RANGE: + case Instruction::INVOKE_SUPER_RANGE: + case Instruction::INVOKE_VIRTUAL_RANGE: { + last_result_ = GetReturnType(instruction.VRegB_3rc()); + break; + } + + case Instruction::MOVE_RESULT: + case Instruction::MOVE_RESULT_WIDE: + case Instruction::MOVE_RESULT_OBJECT: { + UpdateRegister(instruction.VRegA(), last_result_); + break; + } + case Instruction::RETURN_VOID: + case Instruction::RETURN_OBJECT: + case Instruction::RETURN_WIDE: + case Instruction::RETURN: { + break; + } + #define IF_XX(cond) \ + case Instruction::IF_##cond: break; \ + case Instruction::IF_##cond##Z: break + + IF_XX(EQ); + IF_XX(NE); + IF_XX(LT); + IF_XX(LE); + IF_XX(GT); + IF_XX(GE); + + case Instruction::GOTO: + case Instruction::GOTO_16: + case Instruction::GOTO_32: { + break; + } + case Instruction::INVOKE_POLYMORPHIC: { + // TODO + break; + } + + case Instruction::INVOKE_POLYMORPHIC_RANGE: { + // TODO + break; + } + + case Instruction::NEG_INT: + case Instruction::NEG_LONG: + case Instruction::NEG_FLOAT: + case Instruction::NEG_DOUBLE: + case Instruction::NOT_INT: + case Instruction::NOT_LONG: { + UpdateRegister(instruction.VRegA(), VeriClass::integer_); + break; + } + + case Instruction::INT_TO_LONG: + case Instruction::INT_TO_FLOAT: + case Instruction::INT_TO_DOUBLE: + case Instruction::LONG_TO_INT: + case Instruction::LONG_TO_FLOAT: + case Instruction::LONG_TO_DOUBLE: + case Instruction::FLOAT_TO_INT: + case Instruction::FLOAT_TO_LONG: + case Instruction::FLOAT_TO_DOUBLE: + case Instruction::DOUBLE_TO_INT: + case Instruction::DOUBLE_TO_LONG: + case Instruction::DOUBLE_TO_FLOAT: + case Instruction::INT_TO_BYTE: + case Instruction::INT_TO_SHORT: + case Instruction::INT_TO_CHAR: { + UpdateRegister(instruction.VRegA(), VeriClass::integer_); + break; + } + + case Instruction::ADD_INT: + case Instruction::ADD_LONG: + case Instruction::ADD_DOUBLE: + case Instruction::ADD_FLOAT: + case Instruction::SUB_INT: + case Instruction::SUB_LONG: + case Instruction::SUB_FLOAT: + case Instruction::SUB_DOUBLE: + case Instruction::MUL_INT: + case Instruction::MUL_LONG: + case Instruction::MUL_FLOAT: + case Instruction::MUL_DOUBLE: + case Instruction::DIV_INT: + case Instruction::DIV_LONG: + case Instruction::DIV_FLOAT: + case Instruction::DIV_DOUBLE: + case Instruction::REM_INT: + case Instruction::REM_LONG: + case Instruction::REM_FLOAT: + case Instruction::REM_DOUBLE: + case Instruction::AND_INT: + case Instruction::AND_LONG: + case Instruction::SHL_INT: + case Instruction::SHL_LONG: + case Instruction::SHR_INT: + case Instruction::SHR_LONG: + case Instruction::USHR_INT: + case Instruction::USHR_LONG: + case Instruction::OR_INT: + case Instruction::OR_LONG: + case Instruction::XOR_INT: + case Instruction::XOR_LONG: { + UpdateRegister(instruction.VRegA(), VeriClass::integer_); + break; + } + + case Instruction::ADD_INT_2ADDR: + case Instruction::ADD_LONG_2ADDR: + case Instruction::ADD_DOUBLE_2ADDR: + case Instruction::ADD_FLOAT_2ADDR: + case Instruction::SUB_INT_2ADDR: + case Instruction::SUB_LONG_2ADDR: + case Instruction::SUB_FLOAT_2ADDR: + case Instruction::SUB_DOUBLE_2ADDR: + case Instruction::MUL_INT_2ADDR: + case Instruction::MUL_LONG_2ADDR: + case Instruction::MUL_FLOAT_2ADDR: + case Instruction::MUL_DOUBLE_2ADDR: + case Instruction::DIV_INT_2ADDR: + case Instruction::DIV_LONG_2ADDR: + case Instruction::REM_INT_2ADDR: + case Instruction::REM_LONG_2ADDR: + case Instruction::REM_FLOAT_2ADDR: + case Instruction::REM_DOUBLE_2ADDR: + case Instruction::SHL_INT_2ADDR: + case Instruction::SHL_LONG_2ADDR: + case Instruction::SHR_INT_2ADDR: + case Instruction::SHR_LONG_2ADDR: + case Instruction::USHR_INT_2ADDR: + case Instruction::USHR_LONG_2ADDR: + case Instruction::DIV_FLOAT_2ADDR: + case Instruction::DIV_DOUBLE_2ADDR: + case Instruction::AND_INT_2ADDR: + case Instruction::AND_LONG_2ADDR: + case Instruction::OR_INT_2ADDR: + case Instruction::OR_LONG_2ADDR: + case Instruction::XOR_INT_2ADDR: + case Instruction::XOR_LONG_2ADDR: { + UpdateRegister(instruction.VRegA(), VeriClass::integer_); + break; + } + + case Instruction::ADD_INT_LIT16: + case Instruction::AND_INT_LIT16: + case Instruction::OR_INT_LIT16: + case Instruction::XOR_INT_LIT16: + case Instruction::RSUB_INT: + case Instruction::MUL_INT_LIT16: + case Instruction::DIV_INT_LIT16: + case Instruction::REM_INT_LIT16: { + UpdateRegister(instruction.VRegA(), VeriClass::integer_); + break; + } + + case Instruction::ADD_INT_LIT8: + case Instruction::AND_INT_LIT8: + case Instruction::OR_INT_LIT8: + case Instruction::XOR_INT_LIT8: + case Instruction::RSUB_INT_LIT8: + case Instruction::MUL_INT_LIT8: + case Instruction::DIV_INT_LIT8: + case Instruction::REM_INT_LIT8: + case Instruction::SHL_INT_LIT8: + case Instruction::SHR_INT_LIT8: { + case Instruction::USHR_INT_LIT8: { + UpdateRegister(instruction.VRegA(), VeriClass::integer_); + break; + } + + case Instruction::NEW_INSTANCE: { + VeriClass* cls = resolver_->GetVeriClass(dex::TypeIndex(instruction.VRegB_21c())); + UpdateRegister(instruction.VRegA(), cls); + break; + } + + case Instruction::NEW_ARRAY: { + dex::TypeIndex type_index(instruction.VRegC_22c()); + VeriClass* cls = resolver_->GetVeriClass(type_index); + UpdateRegister(instruction.VRegA_22c(), cls); + break; + } + + case Instruction::FILLED_NEW_ARRAY: { + dex::TypeIndex type_index(instruction.VRegB_35c()); + VeriClass* cls = resolver_->GetVeriClass(type_index); + UpdateRegister(instruction.VRegA_22c(), cls); + break; + } + + case Instruction::FILLED_NEW_ARRAY_RANGE: { + dex::TypeIndex type_index(instruction.VRegB_3rc()); + uint32_t register_index = instruction.VRegC_3rc(); + VeriClass* cls = resolver_->GetVeriClass(type_index); + UpdateRegister(register_index, cls); + break; + } + + case Instruction::FILL_ARRAY_DATA: { + break; + } + + case Instruction::CMP_LONG: + case Instruction::CMPG_FLOAT: + case Instruction::CMPG_DOUBLE: + case Instruction::CMPL_FLOAT: + case Instruction::CMPL_DOUBLE: + UpdateRegister(instruction.VRegA(), VeriClass::integer_); + break; + } + + case Instruction::NOP: + break; + + case Instruction::IGET: + case Instruction::IGET_WIDE: + case Instruction::IGET_OBJECT: + case Instruction::IGET_BOOLEAN: + case Instruction::IGET_BYTE: + case Instruction::IGET_CHAR: + case Instruction::IGET_SHORT: { + UpdateRegister(instruction.VRegA_22c(), GetFieldType(instruction.VRegC_22c())); + break; + } + + case Instruction::IPUT: + case Instruction::IPUT_WIDE: + case Instruction::IPUT_OBJECT: + case Instruction::IPUT_BOOLEAN: + case Instruction::IPUT_BYTE: + case Instruction::IPUT_CHAR: + case Instruction::IPUT_SHORT: { + break; + } + + case Instruction::SGET: + case Instruction::SGET_WIDE: + case Instruction::SGET_OBJECT: + case Instruction::SGET_BOOLEAN: + case Instruction::SGET_BYTE: + case Instruction::SGET_CHAR: + case Instruction::SGET_SHORT: { + UpdateRegister(instruction.VRegA_22c(), GetFieldType(instruction.VRegC_22c())); + break; + } + + case Instruction::SPUT: + case Instruction::SPUT_WIDE: + case Instruction::SPUT_OBJECT: + case Instruction::SPUT_BOOLEAN: + case Instruction::SPUT_BYTE: + case Instruction::SPUT_CHAR: + case Instruction::SPUT_SHORT: { + break; + } + +#define ARRAY_XX(kind, anticipated_type) \ + case Instruction::AGET##kind: { \ + UpdateRegister(instruction.VRegA_23x(), anticipated_type); \ + break; \ + } \ + case Instruction::APUT##kind: { \ + break; \ + } + + ARRAY_XX(, VeriClass::integer_); + ARRAY_XX(_WIDE, VeriClass::long_); + ARRAY_XX(_BOOLEAN, VeriClass::boolean_); + ARRAY_XX(_BYTE, VeriClass::byte_); + ARRAY_XX(_CHAR, VeriClass::char_); + ARRAY_XX(_SHORT, VeriClass::short_); + + case Instruction::AGET_OBJECT: { + // TODO: take the component type. + UpdateRegister(instruction.VRegA_23x(), VeriClass::object_); + break; + } + + case Instruction::APUT_OBJECT: { + break; + } + + case Instruction::ARRAY_LENGTH: { + UpdateRegister(instruction.VRegA_12x(), VeriClass::integer_); + break; + } + + case Instruction::MOVE_EXCEPTION: { + UpdateRegister(instruction.VRegA_11x(), VeriClass::throwable_); + break; + } + + case Instruction::THROW: { + break; + } + + case Instruction::INSTANCE_OF: { + uint8_t destination = instruction.VRegA_22c(); + UpdateRegister(destination, VeriClass::boolean_); + break; + } + + case Instruction::CHECK_CAST: { + uint8_t reference = instruction.VRegA_21c(); + dex::TypeIndex type_index(instruction.VRegB_21c()); + UpdateRegister(reference, resolver_->GetVeriClass(type_index)); + break; + } + + case Instruction::MONITOR_ENTER: + case Instruction::MONITOR_EXIT: { + break; + } + + case Instruction::SPARSE_SWITCH: + case Instruction::PACKED_SWITCH: + break; + + default: + break; + } +} + +void VeriFlowAnalysis::Run() { + FindBranches(); + AnalyzeCode(); +} + +} // namespace art diff --git a/tools/veridex/flow_analysis.h b/tools/veridex/flow_analysis.h new file mode 100644 index 0000000000..c065fb8c24 --- /dev/null +++ b/tools/veridex/flow_analysis.h @@ -0,0 +1,147 @@ +/* + * 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. + */ + +#ifndef ART_TOOLS_VERIDEX_FLOW_ANALYSIS_H_ +#define ART_TOOLS_VERIDEX_FLOW_ANALYSIS_H_ + +#include "dex/code_item_accessors.h" +#include "dex/dex_file_reference.h" +#include "dex/method_reference.h" +#include "veridex.h" + +namespace art { + +class VeridexClass; +class VeridexResolver; + +/** + * The source where a dex register comes from. + */ +enum class RegisterSource { + kParameter, + kField, + kMethod, + kClass, + kString, + kNone +}; + +/** + * Abstract representation of a dex register value. + */ +class RegisterValue { + public: + RegisterValue() : source_(RegisterSource::kNone), reference_(nullptr, 0), type_(nullptr) {} + RegisterValue(RegisterSource source, DexFileReference reference, const VeriClass* type) + : source_(source), reference_(reference), type_(type) {} + + RegisterSource GetSource() const { return source_; } + DexFileReference GetDexFileReference() const { return reference_; } + const VeriClass* GetType() const { return type_; } + + const char* ToString() const { + switch (source_) { + case RegisterSource::kString: + return reference_.dex_file->StringDataByIdx(dex::StringIndex(reference_.index)); + case RegisterSource::kClass: + return reference_.dex_file->StringByTypeIdx(dex::TypeIndex(reference_.index)); + default: + return ""; + } + } + + private: + RegisterSource source_; + DexFileReference reference_; + const VeriClass* type_; +}; + +struct InstructionInfo { + bool has_been_visited; +}; + +class VeriFlowAnalysis { + public: + VeriFlowAnalysis(VeridexResolver* resolver, + const CodeItemDataAccessor& code_item_accessor) + : resolver_(resolver), + code_item_accessor_(code_item_accessor), + dex_registers_(code_item_accessor.InsnsSizeInCodeUnits()), + instruction_infos_(code_item_accessor.InsnsSizeInCodeUnits()) {} + + void Run(); + + const std::vector>& GetFieldUses() const { + return field_uses_; + } + + const std::vector>& GetMethodUses() const { + return method_uses_; + } + + private: + // Find all branches in the code. + void FindBranches(); + + // Analyze all non-deead instructions in the code. + void AnalyzeCode(); + + // Set the instruction at the given pc as a branch target. + void SetAsBranchTarget(uint32_t dex_pc); + + // Whether the instruction at the given pc is a branch target. + bool IsBranchTarget(uint32_t dex_pc); + + // Merge the register values at the given pc with `current_registers`. + // Return whether the register values have changed, and the instruction needs + // to be visited again. + bool MergeRegisterValues(uint32_t dex_pc); + + void UpdateRegister( + uint32_t dex_register, RegisterSource kind, VeriClass* cls, uint32_t source_id); + void UpdateRegister(uint32_t dex_register, const RegisterValue& value); + void UpdateRegister(uint32_t dex_register, const VeriClass* cls); + const RegisterValue& GetRegister(uint32_t dex_register); + void ProcessDexInstruction(const Instruction& inst); + void SetVisited(uint32_t dex_pc); + RegisterValue GetReturnType(uint32_t method_index); + RegisterValue GetFieldType(uint32_t field_index); + + VeridexResolver* resolver_; + const CodeItemDataAccessor& code_item_accessor_; + + // Vector of register values for all branch targets. + std::vector>> dex_registers_; + + // The current values of dex registers. + std::vector current_registers_; + + // Information on each instruction useful for the analysis. + std::vector instruction_infos_; + + // The value of invoke instructions, to be fetched when visiting move-result. + RegisterValue last_result_; + + // List of reflection field uses found. + std::vector> field_uses_; + + // List of reflection method uses found. + std::vector> method_uses_; +}; + +} // namespace art + +#endif // ART_TOOLS_VERIDEX_FLOW_ANALYSIS_H_ diff --git a/tools/veridex/hidden_api.h b/tools/veridex/hidden_api.h index 5893b8ae33..4c67768a00 100644 --- a/tools/veridex/hidden_api.h +++ b/tools/veridex/hidden_api.h @@ -18,6 +18,7 @@ #define ART_TOOLS_VERIDEX_HIDDEN_API_H_ #include "dex/hidden_api_access_flags.h" +#include "dex/method_reference.h" #include #include @@ -58,6 +59,10 @@ class HiddenApi { static std::string GetApiFieldName(const DexFile& dex_file, uint32_t field_index); + static std::string GetApiMethodName(MethodReference ref) { + return HiddenApi::GetApiMethodName(*ref.dex_file, ref.index); + } + private: static bool IsInList(const std::string& name, const std::set& list) { return list.find(name) != list.end(); @@ -70,6 +75,13 @@ class HiddenApi { std::set dark_greylist_; }; +struct HiddenApiStats { + uint32_t count = 0; + uint32_t reflection_count = 0; + uint32_t linking_count = 0; + uint32_t api_counts[4] = { 0, 0, 0, 0 }; +}; + } // namespace art #endif // ART_TOOLS_VERIDEX_HIDDEN_API_H_ diff --git a/tools/veridex/hidden_api_finder.cc b/tools/veridex/hidden_api_finder.cc index 4885e02769..b9be618ed7 100644 --- a/tools/veridex/hidden_api_finder.cc +++ b/tools/veridex/hidden_api_finder.cc @@ -193,32 +193,26 @@ void HiddenApiFinder::CollectAccesses(VeridexResolver* resolver) { } } -static std::string GetApiMethodName(MethodReference ref) { - return HiddenApi::GetApiMethodName(*ref.dex_file, ref.index); -} - void HiddenApiFinder::Run(const std::vector>& resolvers) { for (const std::unique_ptr& resolver : resolvers) { CollectAccesses(resolver.get()); } - - Dump(std::cout); } -void HiddenApiFinder::Dump(std::ostream& os) { +void HiddenApiFinder::Dump(std::ostream& os, + HiddenApiStats* stats, + bool dump_reflection) { static const char* kPrefix = " "; - uint32_t count = 0; - uint32_t linking_count = method_locations_.size() + field_locations_.size(); - uint32_t api_counts[4] = {0, 0, 0, 0}; + stats->linking_count = method_locations_.size() + field_locations_.size(); // Dump methods from hidden APIs linked against. for (const std::pair>& pair : method_locations_) { HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(pair.first); - api_counts[api_list]++; - os << "#" << ++count << ": Linking " << api_list << " " << pair.first << " use(s):"; + stats->api_counts[api_list]++; + os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):"; os << std::endl; for (const MethodReference& ref : pair.second) { - os << kPrefix << GetApiMethodName(ref) << std::endl; + os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl; } os << std::endl; } @@ -226,42 +220,35 @@ void HiddenApiFinder::Dump(std::ostream& os) { // Dump fields from hidden APIs linked against. for (const std::pair>& pair : field_locations_) { HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(pair.first); - api_counts[api_list]++; - os << "#" << ++count << ": Linking " << api_list << " " << pair.first << " use(s):"; + stats->api_counts[api_list]++; + os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):"; os << std::endl; for (const MethodReference& ref : pair.second) { - os << kPrefix << GetApiMethodName(ref) << std::endl; + os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl; } os << std::endl; } - // Dump potential reflection uses. - for (const std::string& cls : classes_) { - for (const std::string& name : strings_) { - std::string full_name = cls + "->" + name; - HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name); - api_counts[api_list]++; - if (api_list != HiddenApiAccessFlags::kWhitelist) { - os << "#" << ++count << ": Reflection " << api_list << " " << full_name - << " potential use(s):"; - os << std::endl; - for (const MethodReference& ref : reflection_locations_[name]) { - os << kPrefix << GetApiMethodName(ref) << std::endl; + if (dump_reflection) { + // Dump potential reflection uses. + for (const std::string& cls : classes_) { + for (const std::string& name : strings_) { + std::string full_name = cls + "->" + name; + HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name); + stats->api_counts[api_list]++; + if (api_list != HiddenApiAccessFlags::kWhitelist) { + stats->reflection_count++; + os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name + << " potential use(s):"; + os << std::endl; + for (const MethodReference& ref : reflection_locations_[name]) { + os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl; + } + os << std::endl; } - os << std::endl; } } } - - os << count << " hidden API(s) used: " - << linking_count << " linked against, " - << count - linking_count << " potentially through reflection" << std::endl; - os << kPrefix << api_counts[HiddenApiAccessFlags::kBlacklist] - << " in blacklist" << std::endl; - os << kPrefix << api_counts[HiddenApiAccessFlags::kDarkGreylist] - << " in dark greylist" << std::endl; - os << kPrefix << api_counts[HiddenApiAccessFlags::kLightGreylist] - << " in light greylist" << std::endl; } } // namespace art diff --git a/tools/veridex/hidden_api_finder.h b/tools/veridex/hidden_api_finder.h index 243079c187..f7d3dc832d 100644 --- a/tools/veridex/hidden_api_finder.h +++ b/tools/veridex/hidden_api_finder.h @@ -27,6 +27,7 @@ namespace art { class HiddenApi; +struct HiddenApiStats; class VeridexResolver; /** @@ -40,11 +41,12 @@ class HiddenApiFinder { // hidden API uses. void Run(const std::vector>& app_resolvers); + void Dump(std::ostream& os, HiddenApiStats* stats, bool dump_reflection); + private: void CollectAccesses(VeridexResolver* resolver); void CheckMethod(uint32_t method_idx, VeridexResolver* resolver, MethodReference ref); void CheckField(uint32_t field_idx, VeridexResolver* resolver, MethodReference ref); - void Dump(std::ostream& os); const HiddenApi& hidden_api_; std::set classes_; diff --git a/tools/veridex/precise_hidden_api_finder.cc b/tools/veridex/precise_hidden_api_finder.cc new file mode 100644 index 0000000000..2092af3d75 --- /dev/null +++ b/tools/veridex/precise_hidden_api_finder.cc @@ -0,0 +1,92 @@ +/* + * 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. + */ + +#include "precise_hidden_api_finder.h" + +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_instruction-inl.h" +#include "dex/dex_file.h" +#include "dex/method_reference.h" +#include "flow_analysis.h" +#include "hidden_api.h" +#include "resolver.h" +#include "veridex.h" + +#include + +namespace art { + +void PreciseHiddenApiFinder::Run(const std::vector>& resolvers) { + for (const std::unique_ptr& resolver : resolvers) { + const DexFile& dex_file = resolver->GetDexFile(); + size_t class_def_count = dex_file.NumClassDefs(); + for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) { + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); + const uint8_t* class_data = dex_file.GetClassData(class_def); + if (class_data == nullptr) { + // Empty class. + continue; + } + ClassDataItemIterator it(dex_file, class_data); + it.SkipAllFields(); + for (; it.HasNextMethod(); it.Next()) { + const DexFile::CodeItem* code_item = it.GetMethodCodeItem(); + if (code_item == nullptr) { + continue; + } + CodeItemDataAccessor code_item_accessor(dex_file, code_item); + VeriFlowAnalysis ana(resolver.get(), code_item_accessor); + ana.Run(); + if (!ana.GetFieldUses().empty()) { + field_uses_[MethodReference(&dex_file, it.GetMemberIndex())] = ana.GetFieldUses(); + } + if (!ana.GetMethodUses().empty()) { + method_uses_[MethodReference(&dex_file, it.GetMemberIndex())] = ana.GetMethodUses(); + } + } + } + } +} + +void PreciseHiddenApiFinder::Dump(std::ostream& os, HiddenApiStats* stats) { + static const char* kPrefix = " "; + for (auto kinds : { field_uses_, method_uses_ }) { + for (auto it : kinds) { + MethodReference ref = it.first; + for (const std::pair& info : it.second) { + if ((info.first.GetSource() == RegisterSource::kClass || + info.first.GetSource() == RegisterSource::kString) && + info.second.GetSource() == RegisterSource::kString) { + std::string cls(info.first.ToString()); + std::string name(info.second.ToString()); + std::string full_name = cls + "->" + name; + HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name); + stats->api_counts[api_list]++; + if (api_list != HiddenApiAccessFlags::kWhitelist) { + ++stats->reflection_count; + os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name + << " use:"; + os << std::endl; + os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl; + os << std::endl; + } + } + } + } + } +} + +} // namespace art diff --git a/tools/veridex/precise_hidden_api_finder.h b/tools/veridex/precise_hidden_api_finder.h new file mode 100644 index 0000000000..22744a6f1f --- /dev/null +++ b/tools/veridex/precise_hidden_api_finder.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#ifndef ART_TOOLS_VERIDEX_PRECISE_HIDDEN_API_FINDER_H_ +#define ART_TOOLS_VERIDEX_PRECISE_HIDDEN_API_FINDER_H_ + +#include "dex/method_reference.h" +#include "flow_analysis.h" + +#include +#include +#include +#include + +namespace art { + +class HiddenApi; +struct HiddenApiStats; +class VeridexResolver; + +/** + * Reports known uses of hidden APIs from reflection. + */ +class PreciseHiddenApiFinder { + public: + explicit PreciseHiddenApiFinder(const HiddenApi& hidden_api) : hidden_api_(hidden_api) {} + + // Iterate over the dex files associated with the passed resolvers to report + // hidden API uses. + void Run(const std::vector>& app_resolvers); + + void Dump(std::ostream& os, HiddenApiStats* stats); + + private: + const HiddenApi& hidden_api_; + std::map>> field_uses_; + std::map>> method_uses_; +}; + +} // namespace art + +#endif // ART_TOOLS_VERIDEX_PRECISE_HIDDEN_API_FINDER_H_ diff --git a/tools/veridex/resolver.cc b/tools/veridex/resolver.cc index 13dda5c199..9113039b04 100644 --- a/tools/veridex/resolver.cc +++ b/tools/veridex/resolver.cc @@ -56,6 +56,14 @@ void VeridexResolver::Run() { } } +static bool HasSameNameAndSignature(const DexFile& dex_file, + const DexFile::MethodId& method_id, + const char* method_name, + const char* type) { + return strcmp(method_name, dex_file.GetMethodName(method_id)) == 0 && + strcmp(type, dex_file.GetMethodSignature(method_id).ToString().c_str()) == 0; +} + static bool HasSameNameAndSignature(const DexFile& dex_file, const DexFile::MethodId& method_id, const char* method_name, @@ -241,6 +249,34 @@ VeriField VeridexResolver::LookupFieldIn(const VeriClass& kls, return nullptr; } +VeriMethod VeridexResolver::LookupDeclaredMethodIn(const VeriClass& kls, + const char* method_name, + const char* type) const { + if (kls.IsPrimitive()) { + return nullptr; + } + if (kls.IsArray()) { + return nullptr; + } + VeridexResolver* resolver = GetResolverOf(kls); + const DexFile& other_dex_file = resolver->dex_file_; + const uint8_t* class_data = other_dex_file.GetClassData(*kls.GetClassDef()); + if (class_data != nullptr) { + ClassDataItemIterator it(other_dex_file, class_data); + it.SkipAllFields(); + for (; it.HasNextMethod(); it.Next()) { + const DexFile::MethodId& other_method_id = other_dex_file.GetMethodId(it.GetMemberIndex()); + if (HasSameNameAndSignature(other_dex_file, + other_method_id, + method_name, + type)) { + return it.DataPointer(); + } + } + } + return nullptr; +} + VeriMethod VeridexResolver::GetMethod(uint32_t method_index) { VeriMethod method_info = method_infos_[method_index]; if (method_info == nullptr) { diff --git a/tools/veridex/resolver.h b/tools/veridex/resolver.h index 06c8aa70c5..52b15fe0ed 100644 --- a/tools/veridex/resolver.h +++ b/tools/veridex/resolver.h @@ -66,6 +66,11 @@ class VeridexResolver { const char* field_name, const char* field_type); + // Lookup a method declared in `kls`. + VeriMethod LookupDeclaredMethodIn(const VeriClass& kls, + const char* method_name, + const char* signature) const; + // Resolve all type_id/method_id/field_id. void ResolveAll(); diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc index 16e9f0e55b..6e72faaf57 100644 --- a/tools/veridex/veridex.cc +++ b/tools/veridex/veridex.cc @@ -22,6 +22,7 @@ #include "dex/dex_file_loader.h" #include "hidden_api.h" #include "hidden_api_finder.h" +#include "precise_hidden_api_finder.h" #include "resolver.h" #include @@ -47,8 +48,18 @@ VeriClass* VeriClass::float_ = &f_; VeriClass* VeriClass::double_ = &d_; VeriClass* VeriClass::long_ = &j_; VeriClass* VeriClass::void_ = &v_; + // Will be set after boot classpath has been resolved. VeriClass* VeriClass::object_ = nullptr; +VeriClass* VeriClass::class_ = nullptr; +VeriClass* VeriClass::string_ = nullptr; +VeriClass* VeriClass::throwable_ = nullptr; +VeriMethod VeriClass::forName_ = nullptr; +VeriMethod VeriClass::getField_ = nullptr; +VeriMethod VeriClass::getDeclaredField_ = nullptr; +VeriMethod VeriClass::getMethod_ = nullptr; +VeriMethod VeriClass::getDeclaredMethod_ = nullptr; +VeriMethod VeriClass::getClass_ = nullptr; struct VeridexOptions { const char* dex_file = nullptr; @@ -56,6 +67,7 @@ struct VeridexOptions { const char* blacklist = nullptr; const char* light_greylist = nullptr; const char* dark_greylist = nullptr; + bool precise = true; }; static const char* Substr(const char* str, int index) { @@ -76,6 +88,7 @@ static void ParseArgs(VeridexOptions* options, int argc, char** argv) { static const char* kBlacklistOption = "--blacklist="; static const char* kDarkGreylistOption = "--dark-greylist="; static const char* kLightGreylistOption = "--light-greylist="; + static const char* kImprecise = "--imprecise"; for (int i = 0; i < argc; ++i) { if (StartsWith(argv[i], kDexFileOption)) { @@ -88,6 +101,8 @@ static void ParseArgs(VeridexOptions* options, int argc, char** argv) { options->dark_greylist = Substr(argv[i], strlen(kDarkGreylistOption)); } else if (StartsWith(argv[i], kLightGreylistOption)) { options->light_greylist = Substr(argv[i], strlen(kLightGreylistOption)); + } else if (strcmp(argv[i], kImprecise) == 0) { + options->precise = false; } } } @@ -157,21 +172,70 @@ class Veridex { std::vector> boot_resolvers; Resolve(boot_dex_files, resolver_map, type_map, &boot_resolvers); - // Now that boot classpath has been resolved, fill j.l.Object. + // Now that boot classpath has been resolved, fill classes and reflection + // methods. VeriClass::object_ = type_map["Ljava/lang/Object;"]; + VeriClass::class_ = type_map["Ljava/lang/Class;"]; + VeriClass::string_ = type_map["Ljava/lang/String;"]; + VeriClass::throwable_ = type_map["Ljava/lang/Throwable;"]; + VeriClass::forName_ = boot_resolvers[0]->LookupDeclaredMethodIn( + *VeriClass::class_, "forName", "(Ljava/lang/String;)Ljava/lang/Class;"); + VeriClass::getField_ = boot_resolvers[0]->LookupDeclaredMethodIn( + *VeriClass::class_, "getField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;"); + VeriClass::getDeclaredField_ = boot_resolvers[0]->LookupDeclaredMethodIn( + *VeriClass::class_, "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;"); + VeriClass::getMethod_ = boot_resolvers[0]->LookupDeclaredMethodIn( + *VeriClass::class_, + "getMethod", + "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"); + VeriClass::getDeclaredMethod_ = boot_resolvers[0]->LookupDeclaredMethodIn( + *VeriClass::class_, + "getDeclaredMethod", + "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"); + VeriClass::getClass_ = boot_resolvers[0]->LookupDeclaredMethodIn( + *VeriClass::object_, "getClass", "()Ljava/lang/Class;"); std::vector> app_resolvers; Resolve(app_dex_files, resolver_map, type_map, &app_resolvers); // Find and log uses of hidden APIs. HiddenApi hidden_api(options.blacklist, options.dark_greylist, options.light_greylist); + HiddenApiStats stats; + HiddenApiFinder api_finder(hidden_api); api_finder.Run(app_resolvers); + api_finder.Dump(std::cout, &stats, !options.precise); + + if (options.precise) { + PreciseHiddenApiFinder precise_api_finder(hidden_api); + precise_api_finder.Run(app_resolvers); + precise_api_finder.Dump(std::cout, &stats); + } + + DumpSummaryStats(std::cout, stats); + + if (options.precise) { + std::cout << "To run an analysis that can give more reflection accesses, " << std::endl + << "but could include false positives, pass the --imprecise flag. " << std::endl; + } return 0; } private: + static void DumpSummaryStats(std::ostream& os, const HiddenApiStats& stats) { + static const char* kPrefix = " "; + os << stats.count << " hidden API(s) used: " + << stats.linking_count << " linked against, " + << stats.reflection_count << " through reflection" << std::endl; + os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kBlacklist] + << " in blacklist" << std::endl; + os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kDarkGreylist] + << " in dark greylist" << std::endl; + os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kLightGreylist] + << " in light greylist" << std::endl; + } + static bool Load(const std::string& filename, std::string& content, std::vector>* dex_files, diff --git a/tools/veridex/veridex.h b/tools/veridex/veridex.h index 0c928ab166..75e4845293 100644 --- a/tools/veridex/veridex.h +++ b/tools/veridex/veridex.h @@ -24,6 +24,18 @@ namespace art { +/** + * Abstraction for fields defined in dex files. Currently, that's a pointer into their + * `encoded_field` description. + */ +using VeriField = const uint8_t*; + +/** + * Abstraction for methods defined in dex files. Currently, that's a pointer into their + * `encoded_method` description. + */ +using VeriMethod = const uint8_t*; + /** * Abstraction for classes defined, or implicitly defined (for arrays and primitives) * in dex files. @@ -52,6 +64,9 @@ class VeriClass { const DexFile::ClassDef* GetClassDef() const { return class_def_; } static VeriClass* object_; + static VeriClass* class_; + static VeriClass* string_; + static VeriClass* throwable_; static VeriClass* boolean_; static VeriClass* byte_; static VeriClass* char_; @@ -62,23 +77,26 @@ class VeriClass { static VeriClass* long_; static VeriClass* void_; + static VeriMethod forName_; + static VeriMethod getField_; + static VeriMethod getDeclaredField_; + static VeriMethod getMethod_; + static VeriMethod getDeclaredMethod_; + static VeriMethod getClass_; + private: Primitive::Type kind_; uint8_t dimensions_; const DexFile::ClassDef* class_def_; }; -/** - * Abstraction for fields defined in dex files. Currently, that's a pointer into their - * `encoded_field` description. - */ -using VeriField = const uint8_t*; +inline bool IsGetMethod(VeriMethod method) { + return method == VeriClass::getMethod_ || method == VeriClass::getDeclaredMethod_; +} -/** - * Abstraction for methods defined in dex files. Currently, that's a pointer into their - * `encoded_method` description. - */ -using VeriMethod = const uint8_t*; +inline bool IsGetField(VeriMethod method) { + return method == VeriClass::getField_ || method == VeriClass::getDeclaredField_; +} /** * Map from name to VeriClass to quickly lookup classes. -- GitLab From a9660f1dc13b4d595b3f89b06dd5b70eeee18c43 Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Thu, 29 Mar 2018 10:21:47 +0100 Subject: [PATCH 211/749] hidden_api: Call back into libcore on hidden api detection This change also removes some unnecessary RI specific logic for building src-ex since it isn't required. Bug: 73896556 Test: run-test --host 674-hiddenapi Test: StrictModeTest Co-Authored-By: Andreas Gampe (cherry picked from commit 757a9d0a2e97d43bafeb8a95cc3c51102be99586) Merged-In: Ib2b4dfad55c5d829630bfe2adb4a468124bea61c Change-Id: Ida0943990aa1b3bad0c674bc31ff46766ae493a6 --- runtime/hidden_api.cc | 33 ++++ runtime/native/java_lang_Class.cc | 7 +- runtime/well_known_classes.cc | 6 + runtime/well_known_classes.h | 3 + test/674-hiddenapi/build | 11 -- test/674-hiddenapi/src-ex/ChildClass.java | 180 ++++++++++++++-------- 6 files changed, 159 insertions(+), 81 deletions(-) diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index f0b36a090a..aad3917f87 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -16,7 +16,11 @@ #include "hidden_api.h" +#include + #include "base/dumpable.h" +#include "thread-current-inl.h" +#include "well_known_classes.h" namespace art { namespace hiddenapi { @@ -136,6 +140,35 @@ bool ShouldBlockAccessToMemberImpl(T* member, Action action, AccessMethod access member_signature.WarnAboutAccess(access_method, HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags())); + // We're now not in the boot classpath and have decided to warn, show + // a toast or deny access. Let strict mode know if a callback is set. + // + // For consistency of reasoning, we assume that a callback is never set + // when running unstarted with dex2oat. + if (access_method == kReflection && !runtime->IsAotCompiler()) { + ScopedObjectAccessUnchecked soa(Thread::Current()); + + ScopedLocalRef consumer_object(soa.Env(), + soa.Env()->GetStaticObjectField( + WellKnownClasses::dalvik_system_VMRuntime, + WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer)); + // If the consumer is non-null, we call back to it to let it know that we + // have encountered an API that's in one of our lists. + if (consumer_object != nullptr) { + std::ostringstream member_signature_str; + member_signature.Dump(member_signature_str); + + ScopedLocalRef signature_str( + soa.Env(), + soa.Env()->NewStringUTF(member_signature_str.str().c_str())); + + // Call through to Consumer.accept(String memberSignature); + soa.Env()->CallVoidMethod(consumer_object.get(), + WellKnownClasses::java_util_function_Consumer_accept, + signature_str.get()); + } + } + if (action == kDeny) { // Block access return true; diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index ad05856eaf..1c17806b5e 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -535,18 +535,19 @@ static jobjectArray Class_getDeclaredConstructorsInternal( static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis, jstring name, jobjectArray args) { ScopedFastNativeObjectAccess soa(env); + StackHandleScope<1> hs(soa.Self()); DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); DCHECK(!Runtime::Current()->IsActiveTransaction()); - ObjPtr result = + Handle result = hs.NewHandle( mirror::Class::GetDeclaredMethodInternal( soa.Self(), DecodeClass(soa, javaThis), soa.Decode(name), - soa.Decode>(args)); + soa.Decode>(args))); if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) { return nullptr; } - return soa.AddLocalReference(result); + return soa.AddLocalReference(result.Get()); } static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaThis, diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index bf36ccf0fa..742e713774 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -77,6 +77,7 @@ jclass WellKnownClasses::java_nio_ByteBuffer; jclass WellKnownClasses::java_nio_DirectByteBuffer; jclass WellKnownClasses::java_util_ArrayList; jclass WellKnownClasses::java_util_Collections; +jclass WellKnownClasses::java_util_function_Consumer; jclass WellKnownClasses::libcore_reflect_AnnotationFactory; jclass WellKnownClasses::libcore_reflect_AnnotationMember; jclass WellKnownClasses::libcore_util_EmptyArray; @@ -115,6 +116,7 @@ jmethodID WellKnownClasses::java_lang_Thread_run; jmethodID WellKnownClasses::java_lang_ThreadGroup_add; jmethodID WellKnownClasses::java_lang_ThreadGroup_removeThread; jmethodID WellKnownClasses::java_nio_DirectByteBuffer_init; +jmethodID WellKnownClasses::java_util_function_Consumer_accept; jmethodID WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation; jmethodID WellKnownClasses::libcore_reflect_AnnotationMember_init; jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast; @@ -125,6 +127,7 @@ jfieldID WellKnownClasses::dalvik_system_DexFile_fileName; jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList; jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements; jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile; +jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer; jfieldID WellKnownClasses::java_lang_Thread_daemon; jfieldID WellKnownClasses::java_lang_Thread_group; jfieldID WellKnownClasses::java_lang_Thread_lock; @@ -349,6 +352,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer"); java_util_ArrayList = CacheClass(env, "java/util/ArrayList"); java_util_Collections = CacheClass(env, "java/util/Collections"); + java_util_function_Consumer = CacheClass(env, "java/util/function/Consumer"); libcore_reflect_AnnotationFactory = CacheClass(env, "libcore/reflect/AnnotationFactory"); libcore_reflect_AnnotationMember = CacheClass(env, "libcore/reflect/AnnotationMember"); libcore_util_EmptyArray = CacheClass(env, "libcore/util/EmptyArray"); @@ -379,6 +383,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_ThreadGroup_add = CacheMethod(env, java_lang_ThreadGroup, false, "add", "(Ljava/lang/Thread;)V"); java_lang_ThreadGroup_removeThread = CacheMethod(env, java_lang_ThreadGroup, false, "threadTerminated", "(Ljava/lang/Thread;)V"); java_nio_DirectByteBuffer_init = CacheMethod(env, java_nio_DirectByteBuffer, false, "", "(JI)V"); + java_util_function_Consumer_accept = CacheMethod(env, java_util_function_Consumer, false, "accept", "(Ljava/lang/Object;)V"); libcore_reflect_AnnotationFactory_createAnnotation = CacheMethod(env, libcore_reflect_AnnotationFactory, true, "createAnnotation", "(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)Ljava/lang/annotation/Annotation;"); libcore_reflect_AnnotationMember_init = CacheMethod(env, libcore_reflect_AnnotationMember, false, "", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V"); org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V"); @@ -389,6 +394,7 @@ void WellKnownClasses::Init(JNIEnv* env) { dalvik_system_DexFile_fileName = CacheField(env, dalvik_system_DexFile, false, "mFileName", "Ljava/lang/String;"); dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;"); dalvik_system_DexPathList__Element_dexFile = CacheField(env, dalvik_system_DexPathList__Element, false, "dexFile", "Ldalvik/system/DexFile;"); + dalvik_system_VMRuntime_nonSdkApiUsageConsumer = CacheField(env, dalvik_system_VMRuntime, true, "nonSdkApiUsageConsumer", "Ljava/util/function/Consumer;"); java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z"); java_lang_Thread_group = CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;"); java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index d5d7033132..7b1a2943d2 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -85,6 +85,7 @@ struct WellKnownClasses { static jclass java_lang_Throwable; static jclass java_util_ArrayList; static jclass java_util_Collections; + static jclass java_util_function_Consumer; static jclass java_nio_ByteBuffer; static jclass java_nio_DirectByteBuffer; static jclass libcore_reflect_AnnotationFactory; @@ -125,6 +126,7 @@ struct WellKnownClasses { static jmethodID java_lang_ThreadGroup_add; static jmethodID java_lang_ThreadGroup_removeThread; static jmethodID java_nio_DirectByteBuffer_init; + static jmethodID java_util_function_Consumer_accept; static jmethodID libcore_reflect_AnnotationFactory_createAnnotation; static jmethodID libcore_reflect_AnnotationMember_init; static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_broadcast; @@ -135,6 +137,7 @@ struct WellKnownClasses { static jfieldID dalvik_system_DexFile_fileName; static jfieldID dalvik_system_DexPathList_dexElements; static jfieldID dalvik_system_DexPathList__Element_dexFile; + static jfieldID dalvik_system_VMRuntime_nonSdkApiUsageConsumer; static jfieldID java_lang_reflect_Executable_artMethod; static jfieldID java_lang_reflect_Proxy_h; static jfieldID java_lang_Thread_daemon; diff --git a/test/674-hiddenapi/build b/test/674-hiddenapi/build index 9012e8fd13..330a6def29 100644 --- a/test/674-hiddenapi/build +++ b/test/674-hiddenapi/build @@ -16,15 +16,6 @@ set -e -# Special build logic to handle src-ex .java files which have code that only builds on RI. -custom_build_logic() { - [[ -d ignore.src-ex ]] && mv ignore.src-ex src-ex - # src-ex uses code that can only build on RI. - ${JAVAC} -source 1.8 -target 1.8 -sourcepath src-ex -sourcepath src -d classes-ex $(find src-ex -name '*.java') - # remove src-ex so that default-build doesn't try to build it. - [[ -d src-ex ]] && mv src-ex ignore.src-ex -} - # Build the jars twice. First with applying hiddenapi, creating a boot jar, then # a second time without to create a normal jar. We need to do this because we # want to load the jar once as an app module and once as a member of the boot @@ -33,7 +24,6 @@ custom_build_logic() { # class path dex files, so the boot jar loads fine in the latter case. export USE_HIDDENAPI=true -custom_build_logic ./default-build "$@" # Move the jar file into the resource folder to be bundled with the test. @@ -45,5 +35,4 @@ mv ${TEST_NAME}.jar res/boot.jar rm -rf classes* export USE_HIDDENAPI=false -custom_build_logic ./default-build "$@" diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java index 582e907ca3..822224c539 100644 --- a/test/674-hiddenapi/src-ex/ChildClass.java +++ b/test/674-hiddenapi/src-ex/ChildClass.java @@ -14,6 +14,7 @@ * limitations under the License. */ +import dalvik.system.VMRuntime; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -21,11 +22,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; - -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.type.PrimitiveType; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeVisitor; +import java.util.function.Consumer; public class ChildClass { enum PrimitiveType { @@ -136,6 +133,47 @@ public class ChildClass { } } + static final class RecordingConsumer implements Consumer { + public String recordedValue = null; + + @Override + public void accept(String value) { + recordedValue = value; + } + } + + private static void checkMemberCallback(Class klass, String name, + boolean isPublic, boolean isField) { + try { + RecordingConsumer consumer = new RecordingConsumer(); + VMRuntime.setNonSdkApiUsageConsumer(consumer); + try { + if (isPublic) { + if (isField) { + klass.getField(name); + } else { + klass.getMethod(name); + } + } else { + if (isField) { + klass.getDeclaredField(name); + } else { + klass.getDeclaredMethod(name); + } + } + } catch (NoSuchFieldException|NoSuchMethodException ignored) { + // We're not concerned whether an exception is thrown or not - we're + // only interested in whether the callback is invoked. + } + + if (consumer.recordedValue == null || !consumer.recordedValue.contains(name)) { + throw new RuntimeException("No callback for member: " + name); + } + } finally { + VMRuntime.setNonSdkApiUsageConsumer(null); + } + } + private static void checkField(Class klass, String name, boolean isStatic, Visibility visibility, Behaviour behaviour) throws Exception { @@ -174,48 +212,52 @@ public class ChildClass { // Finish here if we could not discover the field. - if (!canDiscover) { - return; - } + if (canDiscover) { + // Test that modifiers are unaffected. - // Test that modifiers are unaffected. + if (Reflection.canObserveFieldHiddenAccessFlags(klass, name)) { + throwModifiersException(klass, name, true); + } - if (Reflection.canObserveFieldHiddenAccessFlags(klass, name)) { - throwModifiersException(klass, name, true); - } + // Test getters and setters when meaningful. - // Test getters and setters when meaningful. + clearWarning(); + if (!Reflection.canGetField(klass, name)) { + throwAccessException(klass, name, true, "Field.getInt()"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "Field.getInt()", setsWarning); + } - clearWarning(); - if (!Reflection.canGetField(klass, name)) { - throwAccessException(klass, name, true, "Field.getInt()"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "Field.getInt()", setsWarning); - } + clearWarning(); + if (!Reflection.canSetField(klass, name)) { + throwAccessException(klass, name, true, "Field.setInt()"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "Field.setInt()", setsWarning); + } - clearWarning(); - if (!Reflection.canSetField(klass, name)) { - throwAccessException(klass, name, true, "Field.setInt()"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "Field.setInt()", setsWarning); - } + clearWarning(); + if (!JNI.canGetField(klass, name, isStatic)) { + throwAccessException(klass, name, true, "getIntField"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "getIntField", setsWarning); + } - clearWarning(); - if (!JNI.canGetField(klass, name, isStatic)) { - throwAccessException(klass, name, true, "getIntField"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "getIntField", setsWarning); + clearWarning(); + if (!JNI.canSetField(klass, name, isStatic)) { + throwAccessException(klass, name, true, "setIntField"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "setIntField", setsWarning); + } } + // Test that callbacks are invoked correctly. clearWarning(); - if (!JNI.canSetField(klass, name, isStatic)) { - throwAccessException(klass, name, true, "setIntField"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "setIntField", setsWarning); + if (setsWarning || !canDiscover) { + checkMemberCallback(klass, name, isPublic, true /* isField */); } } @@ -257,42 +299,46 @@ public class ChildClass { // Finish here if we could not discover the field. - if (!canDiscover) { - return; - } + if (canDiscover) { + // Test that modifiers are unaffected. - // Test that modifiers are unaffected. + if (Reflection.canObserveMethodHiddenAccessFlags(klass, name)) { + throwModifiersException(klass, name, false); + } - if (Reflection.canObserveMethodHiddenAccessFlags(klass, name)) { - throwModifiersException(klass, name, false); - } + // Test whether we can invoke the method. This skips non-static interface methods. - // Test whether we can invoke the method. This skips non-static interface methods. + if (!klass.isInterface() || isStatic) { + clearWarning(); + if (!Reflection.canInvokeMethod(klass, name)) { + throwAccessException(klass, name, false, "invoke()"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, false, "invoke()", setsWarning); + } - if (!klass.isInterface() || isStatic) { - clearWarning(); - if (!Reflection.canInvokeMethod(klass, name)) { - throwAccessException(klass, name, false, "invoke()"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "invoke()", setsWarning); - } + clearWarning(); + if (!JNI.canInvokeMethodA(klass, name, isStatic)) { + throwAccessException(klass, name, false, "CallMethodA"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, false, "CallMethodA()", setsWarning); + } - clearWarning(); - if (!JNI.canInvokeMethodA(klass, name, isStatic)) { - throwAccessException(klass, name, false, "CallMethodA"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "CallMethodA()", setsWarning); + clearWarning(); + if (!JNI.canInvokeMethodV(klass, name, isStatic)) { + throwAccessException(klass, name, false, "CallMethodV"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, false, "CallMethodV()", setsWarning); + } } + } - clearWarning(); - if (!JNI.canInvokeMethodV(klass, name, isStatic)) { - throwAccessException(klass, name, false, "CallMethodV"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "CallMethodV()", setsWarning); - } + // Test that callbacks are invoked correctly. + clearWarning(); + if (setsWarning || !canDiscover) { + checkMemberCallback(klass, name, isPublic, false /* isField */); } } -- GitLab From 8c5de0f16444441c23a5ae807e4dd5cc0dd586a3 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Tue, 3 Apr 2018 14:13:13 +0000 Subject: [PATCH 212/749] Revert "hidden_api: Call back into libcore on hidden api detection" This reverts commit 757a9d0a2e97d43bafeb8a95cc3c51102be99586. Reason for revert: Test failures with "art/test.py --host -t test-art-host-run-test-debug-prebuild-interpreter-no-relocate-ntrace-gcstress-checkjni-picimage-pictest-ndebuggable-no-jvmti-cdex-fast-674-hiddenapi64" Bug: 73896556 Test: art/test.py --host -t test-art-host-run-test-debug-prebuild-interpreter-no-relocate-ntrace-gcstress-checkjni-picimage-pictest-ndebuggable-no-jvmti-cdex-fast-674-hiddenapi64 (cherry picked from commit 9e68ade384abdb15714054feaed06cb38eb5432f) Merged-In: Ib2ad89c16ad797c37f6212bc7e5c0b6b92ce56b5 Change-Id: I11fa9b76da07162fde8773eb05cfc6a6514e0ca1 --- runtime/hidden_api.cc | 33 ---- runtime/native/java_lang_Class.cc | 7 +- runtime/well_known_classes.cc | 6 - runtime/well_known_classes.h | 3 - test/674-hiddenapi/build | 11 ++ test/674-hiddenapi/src-ex/ChildClass.java | 180 ++++++++-------------- 6 files changed, 81 insertions(+), 159 deletions(-) diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index aad3917f87..f0b36a090a 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -16,11 +16,7 @@ #include "hidden_api.h" -#include - #include "base/dumpable.h" -#include "thread-current-inl.h" -#include "well_known_classes.h" namespace art { namespace hiddenapi { @@ -140,35 +136,6 @@ bool ShouldBlockAccessToMemberImpl(T* member, Action action, AccessMethod access member_signature.WarnAboutAccess(access_method, HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags())); - // We're now not in the boot classpath and have decided to warn, show - // a toast or deny access. Let strict mode know if a callback is set. - // - // For consistency of reasoning, we assume that a callback is never set - // when running unstarted with dex2oat. - if (access_method == kReflection && !runtime->IsAotCompiler()) { - ScopedObjectAccessUnchecked soa(Thread::Current()); - - ScopedLocalRef consumer_object(soa.Env(), - soa.Env()->GetStaticObjectField( - WellKnownClasses::dalvik_system_VMRuntime, - WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer)); - // If the consumer is non-null, we call back to it to let it know that we - // have encountered an API that's in one of our lists. - if (consumer_object != nullptr) { - std::ostringstream member_signature_str; - member_signature.Dump(member_signature_str); - - ScopedLocalRef signature_str( - soa.Env(), - soa.Env()->NewStringUTF(member_signature_str.str().c_str())); - - // Call through to Consumer.accept(String memberSignature); - soa.Env()->CallVoidMethod(consumer_object.get(), - WellKnownClasses::java_util_function_Consumer_accept, - signature_str.get()); - } - } - if (action == kDeny) { // Block access return true; diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 1c17806b5e..ad05856eaf 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -535,19 +535,18 @@ static jobjectArray Class_getDeclaredConstructorsInternal( static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis, jstring name, jobjectArray args) { ScopedFastNativeObjectAccess soa(env); - StackHandleScope<1> hs(soa.Self()); DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); DCHECK(!Runtime::Current()->IsActiveTransaction()); - Handle result = hs.NewHandle( + ObjPtr result = mirror::Class::GetDeclaredMethodInternal( soa.Self(), DecodeClass(soa, javaThis), soa.Decode(name), - soa.Decode>(args))); + soa.Decode>(args)); if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) { return nullptr; } - return soa.AddLocalReference(result.Get()); + return soa.AddLocalReference(result); } static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaThis, diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 742e713774..bf36ccf0fa 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -77,7 +77,6 @@ jclass WellKnownClasses::java_nio_ByteBuffer; jclass WellKnownClasses::java_nio_DirectByteBuffer; jclass WellKnownClasses::java_util_ArrayList; jclass WellKnownClasses::java_util_Collections; -jclass WellKnownClasses::java_util_function_Consumer; jclass WellKnownClasses::libcore_reflect_AnnotationFactory; jclass WellKnownClasses::libcore_reflect_AnnotationMember; jclass WellKnownClasses::libcore_util_EmptyArray; @@ -116,7 +115,6 @@ jmethodID WellKnownClasses::java_lang_Thread_run; jmethodID WellKnownClasses::java_lang_ThreadGroup_add; jmethodID WellKnownClasses::java_lang_ThreadGroup_removeThread; jmethodID WellKnownClasses::java_nio_DirectByteBuffer_init; -jmethodID WellKnownClasses::java_util_function_Consumer_accept; jmethodID WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation; jmethodID WellKnownClasses::libcore_reflect_AnnotationMember_init; jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast; @@ -127,7 +125,6 @@ jfieldID WellKnownClasses::dalvik_system_DexFile_fileName; jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList; jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements; jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile; -jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer; jfieldID WellKnownClasses::java_lang_Thread_daemon; jfieldID WellKnownClasses::java_lang_Thread_group; jfieldID WellKnownClasses::java_lang_Thread_lock; @@ -352,7 +349,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer"); java_util_ArrayList = CacheClass(env, "java/util/ArrayList"); java_util_Collections = CacheClass(env, "java/util/Collections"); - java_util_function_Consumer = CacheClass(env, "java/util/function/Consumer"); libcore_reflect_AnnotationFactory = CacheClass(env, "libcore/reflect/AnnotationFactory"); libcore_reflect_AnnotationMember = CacheClass(env, "libcore/reflect/AnnotationMember"); libcore_util_EmptyArray = CacheClass(env, "libcore/util/EmptyArray"); @@ -383,7 +379,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_ThreadGroup_add = CacheMethod(env, java_lang_ThreadGroup, false, "add", "(Ljava/lang/Thread;)V"); java_lang_ThreadGroup_removeThread = CacheMethod(env, java_lang_ThreadGroup, false, "threadTerminated", "(Ljava/lang/Thread;)V"); java_nio_DirectByteBuffer_init = CacheMethod(env, java_nio_DirectByteBuffer, false, "", "(JI)V"); - java_util_function_Consumer_accept = CacheMethod(env, java_util_function_Consumer, false, "accept", "(Ljava/lang/Object;)V"); libcore_reflect_AnnotationFactory_createAnnotation = CacheMethod(env, libcore_reflect_AnnotationFactory, true, "createAnnotation", "(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)Ljava/lang/annotation/Annotation;"); libcore_reflect_AnnotationMember_init = CacheMethod(env, libcore_reflect_AnnotationMember, false, "", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V"); org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V"); @@ -394,7 +389,6 @@ void WellKnownClasses::Init(JNIEnv* env) { dalvik_system_DexFile_fileName = CacheField(env, dalvik_system_DexFile, false, "mFileName", "Ljava/lang/String;"); dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;"); dalvik_system_DexPathList__Element_dexFile = CacheField(env, dalvik_system_DexPathList__Element, false, "dexFile", "Ldalvik/system/DexFile;"); - dalvik_system_VMRuntime_nonSdkApiUsageConsumer = CacheField(env, dalvik_system_VMRuntime, true, "nonSdkApiUsageConsumer", "Ljava/util/function/Consumer;"); java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z"); java_lang_Thread_group = CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;"); java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index 7b1a2943d2..d5d7033132 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -85,7 +85,6 @@ struct WellKnownClasses { static jclass java_lang_Throwable; static jclass java_util_ArrayList; static jclass java_util_Collections; - static jclass java_util_function_Consumer; static jclass java_nio_ByteBuffer; static jclass java_nio_DirectByteBuffer; static jclass libcore_reflect_AnnotationFactory; @@ -126,7 +125,6 @@ struct WellKnownClasses { static jmethodID java_lang_ThreadGroup_add; static jmethodID java_lang_ThreadGroup_removeThread; static jmethodID java_nio_DirectByteBuffer_init; - static jmethodID java_util_function_Consumer_accept; static jmethodID libcore_reflect_AnnotationFactory_createAnnotation; static jmethodID libcore_reflect_AnnotationMember_init; static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_broadcast; @@ -137,7 +135,6 @@ struct WellKnownClasses { static jfieldID dalvik_system_DexFile_fileName; static jfieldID dalvik_system_DexPathList_dexElements; static jfieldID dalvik_system_DexPathList__Element_dexFile; - static jfieldID dalvik_system_VMRuntime_nonSdkApiUsageConsumer; static jfieldID java_lang_reflect_Executable_artMethod; static jfieldID java_lang_reflect_Proxy_h; static jfieldID java_lang_Thread_daemon; diff --git a/test/674-hiddenapi/build b/test/674-hiddenapi/build index 330a6def29..9012e8fd13 100644 --- a/test/674-hiddenapi/build +++ b/test/674-hiddenapi/build @@ -16,6 +16,15 @@ set -e +# Special build logic to handle src-ex .java files which have code that only builds on RI. +custom_build_logic() { + [[ -d ignore.src-ex ]] && mv ignore.src-ex src-ex + # src-ex uses code that can only build on RI. + ${JAVAC} -source 1.8 -target 1.8 -sourcepath src-ex -sourcepath src -d classes-ex $(find src-ex -name '*.java') + # remove src-ex so that default-build doesn't try to build it. + [[ -d src-ex ]] && mv src-ex ignore.src-ex +} + # Build the jars twice. First with applying hiddenapi, creating a boot jar, then # a second time without to create a normal jar. We need to do this because we # want to load the jar once as an app module and once as a member of the boot @@ -24,6 +33,7 @@ set -e # class path dex files, so the boot jar loads fine in the latter case. export USE_HIDDENAPI=true +custom_build_logic ./default-build "$@" # Move the jar file into the resource folder to be bundled with the test. @@ -35,4 +45,5 @@ mv ${TEST_NAME}.jar res/boot.jar rm -rf classes* export USE_HIDDENAPI=false +custom_build_logic ./default-build "$@" diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java index 822224c539..582e907ca3 100644 --- a/test/674-hiddenapi/src-ex/ChildClass.java +++ b/test/674-hiddenapi/src-ex/ChildClass.java @@ -14,7 +14,6 @@ * limitations under the License. */ -import dalvik.system.VMRuntime; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -22,7 +21,11 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; -import java.util.function.Consumer; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.type.PrimitiveType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeVisitor; public class ChildClass { enum PrimitiveType { @@ -133,47 +136,6 @@ public class ChildClass { } } - static final class RecordingConsumer implements Consumer { - public String recordedValue = null; - - @Override - public void accept(String value) { - recordedValue = value; - } - } - - private static void checkMemberCallback(Class klass, String name, - boolean isPublic, boolean isField) { - try { - RecordingConsumer consumer = new RecordingConsumer(); - VMRuntime.setNonSdkApiUsageConsumer(consumer); - try { - if (isPublic) { - if (isField) { - klass.getField(name); - } else { - klass.getMethod(name); - } - } else { - if (isField) { - klass.getDeclaredField(name); - } else { - klass.getDeclaredMethod(name); - } - } - } catch (NoSuchFieldException|NoSuchMethodException ignored) { - // We're not concerned whether an exception is thrown or not - we're - // only interested in whether the callback is invoked. - } - - if (consumer.recordedValue == null || !consumer.recordedValue.contains(name)) { - throw new RuntimeException("No callback for member: " + name); - } - } finally { - VMRuntime.setNonSdkApiUsageConsumer(null); - } - } - private static void checkField(Class klass, String name, boolean isStatic, Visibility visibility, Behaviour behaviour) throws Exception { @@ -212,52 +174,48 @@ public class ChildClass { // Finish here if we could not discover the field. - if (canDiscover) { - // Test that modifiers are unaffected. + if (!canDiscover) { + return; + } - if (Reflection.canObserveFieldHiddenAccessFlags(klass, name)) { - throwModifiersException(klass, name, true); - } + // Test that modifiers are unaffected. - // Test getters and setters when meaningful. + if (Reflection.canObserveFieldHiddenAccessFlags(klass, name)) { + throwModifiersException(klass, name, true); + } - clearWarning(); - if (!Reflection.canGetField(klass, name)) { - throwAccessException(klass, name, true, "Field.getInt()"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "Field.getInt()", setsWarning); - } + // Test getters and setters when meaningful. - clearWarning(); - if (!Reflection.canSetField(klass, name)) { - throwAccessException(klass, name, true, "Field.setInt()"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "Field.setInt()", setsWarning); - } + clearWarning(); + if (!Reflection.canGetField(klass, name)) { + throwAccessException(klass, name, true, "Field.getInt()"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "Field.getInt()", setsWarning); + } - clearWarning(); - if (!JNI.canGetField(klass, name, isStatic)) { - throwAccessException(klass, name, true, "getIntField"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "getIntField", setsWarning); - } + clearWarning(); + if (!Reflection.canSetField(klass, name)) { + throwAccessException(klass, name, true, "Field.setInt()"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "Field.setInt()", setsWarning); + } - clearWarning(); - if (!JNI.canSetField(klass, name, isStatic)) { - throwAccessException(klass, name, true, "setIntField"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "setIntField", setsWarning); - } + clearWarning(); + if (!JNI.canGetField(klass, name, isStatic)) { + throwAccessException(klass, name, true, "getIntField"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "getIntField", setsWarning); } - // Test that callbacks are invoked correctly. clearWarning(); - if (setsWarning || !canDiscover) { - checkMemberCallback(klass, name, isPublic, true /* isField */); + if (!JNI.canSetField(klass, name, isStatic)) { + throwAccessException(klass, name, true, "setIntField"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "setIntField", setsWarning); } } @@ -299,46 +257,42 @@ public class ChildClass { // Finish here if we could not discover the field. - if (canDiscover) { - // Test that modifiers are unaffected. + if (!canDiscover) { + return; + } - if (Reflection.canObserveMethodHiddenAccessFlags(klass, name)) { - throwModifiersException(klass, name, false); - } + // Test that modifiers are unaffected. - // Test whether we can invoke the method. This skips non-static interface methods. + if (Reflection.canObserveMethodHiddenAccessFlags(klass, name)) { + throwModifiersException(klass, name, false); + } - if (!klass.isInterface() || isStatic) { - clearWarning(); - if (!Reflection.canInvokeMethod(klass, name)) { - throwAccessException(klass, name, false, "invoke()"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "invoke()", setsWarning); - } + // Test whether we can invoke the method. This skips non-static interface methods. - clearWarning(); - if (!JNI.canInvokeMethodA(klass, name, isStatic)) { - throwAccessException(klass, name, false, "CallMethodA"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "CallMethodA()", setsWarning); - } + if (!klass.isInterface() || isStatic) { + clearWarning(); + if (!Reflection.canInvokeMethod(klass, name)) { + throwAccessException(klass, name, false, "invoke()"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, false, "invoke()", setsWarning); + } - clearWarning(); - if (!JNI.canInvokeMethodV(klass, name, isStatic)) { - throwAccessException(klass, name, false, "CallMethodV"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "CallMethodV()", setsWarning); - } + clearWarning(); + if (!JNI.canInvokeMethodA(klass, name, isStatic)) { + throwAccessException(klass, name, false, "CallMethodA"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, false, "CallMethodA()", setsWarning); } - } - // Test that callbacks are invoked correctly. - clearWarning(); - if (setsWarning || !canDiscover) { - checkMemberCallback(klass, name, isPublic, false /* isField */); + clearWarning(); + if (!JNI.canInvokeMethodV(klass, name, isStatic)) { + throwAccessException(klass, name, false, "CallMethodV"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, false, "CallMethodV()", setsWarning); + } } } -- GitLab From f5f1f80aa6c1c10c61b6723bbc52d5aec2eba2b9 Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Tue, 3 Apr 2018 15:23:46 +0100 Subject: [PATCH 213/749] Revert^2 "hidden_api: Call back into libcore on hidden api detection"" This reverts commit bbe60d58496991c16e2943e174e26ab8a096b3d0. This CL deviates from the approach of the original change. Instead of calling back every time ShouldBlock.. was called, we explicitly call back in cases where it's safe to do so. Note that we only call back on reflective accesses for now, and not link time accesses. Coverage for the latter will be added in a follow up change. Bug: 73896556 Test: test-art-host Test: art/test.py --host -t test-art-host-run-test-debug-prebuild-\ interpreter-no-relocate-ntrace-gcstress-checkjni-picimage-pictest-\ ndebuggable-no-jvmti-cdex-fast-674-hiddenapi64 (cherry picked from commit e453a8dd87731f4b37b86a1284f7655d86c2a809) Merged-In: Ie99ac268a083af167accbdf955639da068bea950 Change-Id: I76860519d40b87032dbb8db38b04fcf79ef09723 --- runtime/class_linker.cc | 16 +- runtime/hidden_api.cc | 56 +++++-- runtime/hidden_api.h | 46 +++--- runtime/interpreter/unstarted_runtime.cc | 6 +- runtime/jni_internal.cc | 7 +- runtime/native/java_lang_Class.cc | 35 +++-- runtime/well_known_classes.cc | 6 + runtime/well_known_classes.h | 3 + test/674-hiddenapi/build | 11 -- test/674-hiddenapi/src-ex/ChildClass.java | 180 ++++++++++++++-------- 10 files changed, 235 insertions(+), 131 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 5e69efea77..3f33f7976e 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -7860,8 +7860,8 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr klass, } DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); if (resolved != nullptr && - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader, dex_cache, hiddenapi::kLinking)) { + hiddenapi::GetMemberAction( + resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) { resolved = nullptr; } if (resolved != nullptr) { @@ -8003,8 +8003,8 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx, resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_); } if (resolved != nullptr && - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) { + hiddenapi::GetMemberAction( + resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) { resolved = nullptr; } return resolved; @@ -8083,8 +8083,8 @@ ArtField* ClassLinker::ResolveField(uint32_t field_idx, } if (resolved == nullptr || - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) { + hiddenapi::GetMemberAction( + resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) { const char* name = dex_file.GetFieldName(field_id); const char* type = dex_file.GetFieldTypeDescriptor(field_id); ThrowNoSuchFieldError(is_static ? "static " : "instance ", klass, type, name); @@ -8117,8 +8117,8 @@ ArtField* ClassLinker::ResolveFieldJLS(uint32_t field_idx, StringPiece type(dex_file.GetFieldTypeDescriptor(field_id)); resolved = mirror::Class::FindField(self, klass, name, type); if (resolved != nullptr && - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) { + hiddenapi::GetMemberAction( + resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) { resolved = nullptr; } if (resolved != nullptr) { diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index f0b36a090a..0e72f275d2 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -16,7 +16,11 @@ #include "hidden_api.h" +#include + #include "base/dumpable.h" +#include "thread-current-inl.h" +#include "well_known_classes.h" namespace art { namespace hiddenapi { @@ -111,7 +115,7 @@ void MemberSignature::WarnAboutAccess(AccessMethod access_method, } template -bool ShouldBlockAccessToMemberImpl(T* member, Action action, AccessMethod access_method) { +Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) { // Get the signature, we need it later. MemberSignature member_signature(member); @@ -138,7 +142,7 @@ bool ShouldBlockAccessToMemberImpl(T* member, Action action, AccessMethod access if (action == kDeny) { // Block access - return true; + return action; } // Allow access to this member but print a warning. @@ -156,17 +160,49 @@ bool ShouldBlockAccessToMemberImpl(T* member, Action action, AccessMethod access runtime->SetPendingHiddenApiWarning(true); } - return false; + return action; } // Need to instantiate this. -template bool ShouldBlockAccessToMemberImpl(ArtField* member, - Action action, - AccessMethod access_method); -template bool ShouldBlockAccessToMemberImpl(ArtMethod* member, - Action action, - AccessMethod access_method); - +template Action GetMemberActionImpl(ArtField* member, + Action action, + AccessMethod access_method); +template Action GetMemberActionImpl(ArtMethod* member, + Action action, + AccessMethod access_method); } // namespace detail + +template +void NotifyHiddenApiListener(T* member) { + Runtime* runtime = Runtime::Current(); + if (!runtime->IsAotCompiler()) { + ScopedObjectAccessUnchecked soa(Thread::Current()); + + ScopedLocalRef consumer_object(soa.Env(), + soa.Env()->GetStaticObjectField( + WellKnownClasses::dalvik_system_VMRuntime, + WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer)); + // If the consumer is non-null, we call back to it to let it know that we + // have encountered an API that's in one of our lists. + if (consumer_object != nullptr) { + detail::MemberSignature member_signature(member); + std::ostringstream member_signature_str; + member_signature.Dump(member_signature_str); + + ScopedLocalRef signature_str( + soa.Env(), + soa.Env()->NewStringUTF(member_signature_str.str().c_str())); + + // Call through to Consumer.accept(String memberSignature); + soa.Env()->CallVoidMethod(consumer_object.get(), + WellKnownClasses::java_util_function_Consumer_accept, + signature_str.get()); + } + } +} + +template void NotifyHiddenApiListener(ArtMethod* member); +template void NotifyHiddenApiListener(ArtField* member); + } // namespace hiddenapi } // namespace art diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index cc6c146f00..ffdeacbfff 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -58,7 +58,7 @@ enum AccessMethod { kLinking, }; -inline Action GetMemberAction(uint32_t access_flags) { +inline Action GetActionFromAccessFlags(uint32_t access_flags) { EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); if (policy == EnforcementPolicy::kNoChecks) { // Exit early. Nothing to enforce. @@ -108,9 +108,7 @@ class MemberSignature { }; template -bool ShouldBlockAccessToMemberImpl(T* member, - Action action, - AccessMethod access_method) +Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_); // Returns true if the caller is either loaded by the boot strap class loader or comes from @@ -138,28 +136,28 @@ inline bool IsCallerInPlatformDex(ObjPtr caller_class_loade // return true if the caller is located in the platform. // This function might print warnings into the log if the member is hidden. template -inline bool ShouldBlockAccessToMember(T* member, - Thread* self, - std::function fn_caller_in_platform, - AccessMethod access_method) +inline Action GetMemberAction(T* member, + Thread* self, + std::function fn_caller_in_platform, + AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); - Action action = GetMemberAction(member->GetAccessFlags()); + Action action = GetActionFromAccessFlags(member->GetAccessFlags()); if (action == kAllow) { // Nothing to do. - return false; + return action; } // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access. // This can be *very* expensive. Save it for last. if (fn_caller_in_platform(self)) { // Caller in the platform. Exit. - return false; + return kAllow; } // Member is hidden and caller is not in the platform. - return detail::ShouldBlockAccessToMemberImpl(member, action, access_method); + return detail::GetMemberActionImpl(member, action, access_method); } inline bool IsCallerInPlatformDex(ObjPtr caller) @@ -172,18 +170,26 @@ inline bool IsCallerInPlatformDex(ObjPtr caller) // `caller_class_loader`. // This function might print warnings into the log if the member is hidden. template -inline bool ShouldBlockAccessToMember(T* member, - ObjPtr caller_class_loader, - ObjPtr caller_dex_cache, - AccessMethod access_method) +inline Action GetMemberAction(T* member, + ObjPtr caller_class_loader, + ObjPtr caller_dex_cache, + AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { bool caller_in_platform = detail::IsCallerInPlatformDex(caller_class_loader, caller_dex_cache); - return ShouldBlockAccessToMember(member, - /* thread */ nullptr, - [caller_in_platform] (Thread*) { return caller_in_platform; }, - access_method); + return GetMemberAction(member, + /* thread */ nullptr, + [caller_in_platform] (Thread*) { return caller_in_platform; }, + access_method); } +// Calls back into managed code to notify VMRuntime.nonSdkApiUsageConsumer that +// |member| was accessed. This is usually called when an API is on the black, +// dark grey or light grey lists. Given that the callback can execute arbitrary +// code, a call to this method can result in thread suspension. +template void NotifyHiddenApiListener(T* member) + REQUIRES_SHARED(Locks::mutator_lock_); + + } // namespace hiddenapi } // namespace art diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 4c7a97dfa8..dd8d7dd3e2 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -181,11 +181,13 @@ static mirror::String* GetClassName(Thread* self, ShadowFrame* shadow_frame, siz template static ALWAYS_INLINE bool ShouldBlockAccessToMember(T* member, ShadowFrame* frame) REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::ShouldBlockAccessToMember( + // All uses in this file are from reflection + constexpr hiddenapi::AccessMethod access_method = hiddenapi::kReflection; + return hiddenapi::GetMemberAction( member, frame->GetMethod()->GetDeclaringClass()->GetClassLoader(), frame->GetMethod()->GetDeclaringClass()->GetDexCache(), - hiddenapi::kReflection); // all uses in this file are from reflection + access_method) == hiddenapi::kDeny; } void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self, diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index f309581735..9dbcded867 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -87,8 +87,13 @@ static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_l template ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::ShouldBlockAccessToMember( + hiddenapi::Action action = hiddenapi::GetMemberAction( member, self, IsCallerInPlatformDex, hiddenapi::kJNI); + if (action != hiddenapi::kAllow) { + hiddenapi::NotifyHiddenApiListener(member); + } + + return action == hiddenapi::kDeny; } // Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index ad05856eaf..a8b203bff2 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -98,8 +98,13 @@ ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self) template ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::ShouldBlockAccessToMember( + hiddenapi::Action action = hiddenapi::GetMemberAction( member, self, IsCallerInPlatformDex, hiddenapi::kReflection); + if (action != hiddenapi::kAllow) { + hiddenapi::NotifyHiddenApiListener(member); + } + + return action == hiddenapi::kDeny; } // Returns true if a class member should be discoverable with reflection given @@ -113,7 +118,8 @@ ALWAYS_INLINE static bool IsDiscoverable(bool public_only, return false; } - if (enforce_hidden_api && hiddenapi::GetMemberAction(access_flags) == hiddenapi::kDeny) { + if (enforce_hidden_api && + hiddenapi::GetActionFromAccessFlags(access_flags) == hiddenapi::kDeny) { return false; } @@ -433,12 +439,14 @@ static jobject Class_getPublicFieldRecursive(JNIEnv* env, jobject javaThis, jstr return nullptr; } - mirror::Field* field = GetPublicFieldRecursive( - soa.Self(), DecodeClass(soa, javaThis), name_string); - if (field == nullptr || ShouldBlockAccessToMember(field->GetArtField(), soa.Self())) { + StackHandleScope<1> hs(soa.Self()); + Handle field = hs.NewHandle(GetPublicFieldRecursive( + soa.Self(), DecodeClass(soa, javaThis), name_string)); + if (field.Get() == nullptr || + ShouldBlockAccessToMember(field->GetArtField(), soa.Self())) { return nullptr; } - return soa.AddLocalReference(field); + return soa.AddLocalReference(field.Get()); } static jobject Class_getDeclaredField(JNIEnv* env, jobject javaThis, jstring name) { @@ -477,15 +485,17 @@ static jobject Class_getDeclaredConstructorInternal( ScopedFastNativeObjectAccess soa(env); DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); DCHECK(!Runtime::Current()->IsActiveTransaction()); - ObjPtr result = + + StackHandleScope<1> hs(soa.Self()); + Handle result = hs.NewHandle( mirror::Class::GetDeclaredConstructorInternal( soa.Self(), DecodeClass(soa, javaThis), - soa.Decode>(args)); + soa.Decode>(args))); if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) { return nullptr; } - return soa.AddLocalReference(result); + return soa.AddLocalReference(result.Get()); } static ALWAYS_INLINE inline bool MethodMatchesConstructor( @@ -535,18 +545,19 @@ static jobjectArray Class_getDeclaredConstructorsInternal( static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis, jstring name, jobjectArray args) { ScopedFastNativeObjectAccess soa(env); + StackHandleScope<1> hs(soa.Self()); DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); DCHECK(!Runtime::Current()->IsActiveTransaction()); - ObjPtr result = + Handle result = hs.NewHandle( mirror::Class::GetDeclaredMethodInternal( soa.Self(), DecodeClass(soa, javaThis), soa.Decode(name), - soa.Decode>(args)); + soa.Decode>(args))); if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) { return nullptr; } - return soa.AddLocalReference(result); + return soa.AddLocalReference(result.Get()); } static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaThis, diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index bf36ccf0fa..742e713774 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -77,6 +77,7 @@ jclass WellKnownClasses::java_nio_ByteBuffer; jclass WellKnownClasses::java_nio_DirectByteBuffer; jclass WellKnownClasses::java_util_ArrayList; jclass WellKnownClasses::java_util_Collections; +jclass WellKnownClasses::java_util_function_Consumer; jclass WellKnownClasses::libcore_reflect_AnnotationFactory; jclass WellKnownClasses::libcore_reflect_AnnotationMember; jclass WellKnownClasses::libcore_util_EmptyArray; @@ -115,6 +116,7 @@ jmethodID WellKnownClasses::java_lang_Thread_run; jmethodID WellKnownClasses::java_lang_ThreadGroup_add; jmethodID WellKnownClasses::java_lang_ThreadGroup_removeThread; jmethodID WellKnownClasses::java_nio_DirectByteBuffer_init; +jmethodID WellKnownClasses::java_util_function_Consumer_accept; jmethodID WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation; jmethodID WellKnownClasses::libcore_reflect_AnnotationMember_init; jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast; @@ -125,6 +127,7 @@ jfieldID WellKnownClasses::dalvik_system_DexFile_fileName; jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList; jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements; jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile; +jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer; jfieldID WellKnownClasses::java_lang_Thread_daemon; jfieldID WellKnownClasses::java_lang_Thread_group; jfieldID WellKnownClasses::java_lang_Thread_lock; @@ -349,6 +352,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer"); java_util_ArrayList = CacheClass(env, "java/util/ArrayList"); java_util_Collections = CacheClass(env, "java/util/Collections"); + java_util_function_Consumer = CacheClass(env, "java/util/function/Consumer"); libcore_reflect_AnnotationFactory = CacheClass(env, "libcore/reflect/AnnotationFactory"); libcore_reflect_AnnotationMember = CacheClass(env, "libcore/reflect/AnnotationMember"); libcore_util_EmptyArray = CacheClass(env, "libcore/util/EmptyArray"); @@ -379,6 +383,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_ThreadGroup_add = CacheMethod(env, java_lang_ThreadGroup, false, "add", "(Ljava/lang/Thread;)V"); java_lang_ThreadGroup_removeThread = CacheMethod(env, java_lang_ThreadGroup, false, "threadTerminated", "(Ljava/lang/Thread;)V"); java_nio_DirectByteBuffer_init = CacheMethod(env, java_nio_DirectByteBuffer, false, "", "(JI)V"); + java_util_function_Consumer_accept = CacheMethod(env, java_util_function_Consumer, false, "accept", "(Ljava/lang/Object;)V"); libcore_reflect_AnnotationFactory_createAnnotation = CacheMethod(env, libcore_reflect_AnnotationFactory, true, "createAnnotation", "(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)Ljava/lang/annotation/Annotation;"); libcore_reflect_AnnotationMember_init = CacheMethod(env, libcore_reflect_AnnotationMember, false, "", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V"); org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V"); @@ -389,6 +394,7 @@ void WellKnownClasses::Init(JNIEnv* env) { dalvik_system_DexFile_fileName = CacheField(env, dalvik_system_DexFile, false, "mFileName", "Ljava/lang/String;"); dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;"); dalvik_system_DexPathList__Element_dexFile = CacheField(env, dalvik_system_DexPathList__Element, false, "dexFile", "Ldalvik/system/DexFile;"); + dalvik_system_VMRuntime_nonSdkApiUsageConsumer = CacheField(env, dalvik_system_VMRuntime, true, "nonSdkApiUsageConsumer", "Ljava/util/function/Consumer;"); java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z"); java_lang_Thread_group = CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;"); java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index d5d7033132..7b1a2943d2 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -85,6 +85,7 @@ struct WellKnownClasses { static jclass java_lang_Throwable; static jclass java_util_ArrayList; static jclass java_util_Collections; + static jclass java_util_function_Consumer; static jclass java_nio_ByteBuffer; static jclass java_nio_DirectByteBuffer; static jclass libcore_reflect_AnnotationFactory; @@ -125,6 +126,7 @@ struct WellKnownClasses { static jmethodID java_lang_ThreadGroup_add; static jmethodID java_lang_ThreadGroup_removeThread; static jmethodID java_nio_DirectByteBuffer_init; + static jmethodID java_util_function_Consumer_accept; static jmethodID libcore_reflect_AnnotationFactory_createAnnotation; static jmethodID libcore_reflect_AnnotationMember_init; static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_broadcast; @@ -135,6 +137,7 @@ struct WellKnownClasses { static jfieldID dalvik_system_DexFile_fileName; static jfieldID dalvik_system_DexPathList_dexElements; static jfieldID dalvik_system_DexPathList__Element_dexFile; + static jfieldID dalvik_system_VMRuntime_nonSdkApiUsageConsumer; static jfieldID java_lang_reflect_Executable_artMethod; static jfieldID java_lang_reflect_Proxy_h; static jfieldID java_lang_Thread_daemon; diff --git a/test/674-hiddenapi/build b/test/674-hiddenapi/build index 9012e8fd13..330a6def29 100644 --- a/test/674-hiddenapi/build +++ b/test/674-hiddenapi/build @@ -16,15 +16,6 @@ set -e -# Special build logic to handle src-ex .java files which have code that only builds on RI. -custom_build_logic() { - [[ -d ignore.src-ex ]] && mv ignore.src-ex src-ex - # src-ex uses code that can only build on RI. - ${JAVAC} -source 1.8 -target 1.8 -sourcepath src-ex -sourcepath src -d classes-ex $(find src-ex -name '*.java') - # remove src-ex so that default-build doesn't try to build it. - [[ -d src-ex ]] && mv src-ex ignore.src-ex -} - # Build the jars twice. First with applying hiddenapi, creating a boot jar, then # a second time without to create a normal jar. We need to do this because we # want to load the jar once as an app module and once as a member of the boot @@ -33,7 +24,6 @@ custom_build_logic() { # class path dex files, so the boot jar loads fine in the latter case. export USE_HIDDENAPI=true -custom_build_logic ./default-build "$@" # Move the jar file into the resource folder to be bundled with the test. @@ -45,5 +35,4 @@ mv ${TEST_NAME}.jar res/boot.jar rm -rf classes* export USE_HIDDENAPI=false -custom_build_logic ./default-build "$@" diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java index 582e907ca3..822224c539 100644 --- a/test/674-hiddenapi/src-ex/ChildClass.java +++ b/test/674-hiddenapi/src-ex/ChildClass.java @@ -14,6 +14,7 @@ * limitations under the License. */ +import dalvik.system.VMRuntime; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -21,11 +22,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; - -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.type.PrimitiveType; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeVisitor; +import java.util.function.Consumer; public class ChildClass { enum PrimitiveType { @@ -136,6 +133,47 @@ public class ChildClass { } } + static final class RecordingConsumer implements Consumer { + public String recordedValue = null; + + @Override + public void accept(String value) { + recordedValue = value; + } + } + + private static void checkMemberCallback(Class klass, String name, + boolean isPublic, boolean isField) { + try { + RecordingConsumer consumer = new RecordingConsumer(); + VMRuntime.setNonSdkApiUsageConsumer(consumer); + try { + if (isPublic) { + if (isField) { + klass.getField(name); + } else { + klass.getMethod(name); + } + } else { + if (isField) { + klass.getDeclaredField(name); + } else { + klass.getDeclaredMethod(name); + } + } + } catch (NoSuchFieldException|NoSuchMethodException ignored) { + // We're not concerned whether an exception is thrown or not - we're + // only interested in whether the callback is invoked. + } + + if (consumer.recordedValue == null || !consumer.recordedValue.contains(name)) { + throw new RuntimeException("No callback for member: " + name); + } + } finally { + VMRuntime.setNonSdkApiUsageConsumer(null); + } + } + private static void checkField(Class klass, String name, boolean isStatic, Visibility visibility, Behaviour behaviour) throws Exception { @@ -174,48 +212,52 @@ public class ChildClass { // Finish here if we could not discover the field. - if (!canDiscover) { - return; - } + if (canDiscover) { + // Test that modifiers are unaffected. - // Test that modifiers are unaffected. + if (Reflection.canObserveFieldHiddenAccessFlags(klass, name)) { + throwModifiersException(klass, name, true); + } - if (Reflection.canObserveFieldHiddenAccessFlags(klass, name)) { - throwModifiersException(klass, name, true); - } + // Test getters and setters when meaningful. - // Test getters and setters when meaningful. + clearWarning(); + if (!Reflection.canGetField(klass, name)) { + throwAccessException(klass, name, true, "Field.getInt()"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "Field.getInt()", setsWarning); + } - clearWarning(); - if (!Reflection.canGetField(klass, name)) { - throwAccessException(klass, name, true, "Field.getInt()"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "Field.getInt()", setsWarning); - } + clearWarning(); + if (!Reflection.canSetField(klass, name)) { + throwAccessException(klass, name, true, "Field.setInt()"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "Field.setInt()", setsWarning); + } - clearWarning(); - if (!Reflection.canSetField(klass, name)) { - throwAccessException(klass, name, true, "Field.setInt()"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "Field.setInt()", setsWarning); - } + clearWarning(); + if (!JNI.canGetField(klass, name, isStatic)) { + throwAccessException(klass, name, true, "getIntField"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "getIntField", setsWarning); + } - clearWarning(); - if (!JNI.canGetField(klass, name, isStatic)) { - throwAccessException(klass, name, true, "getIntField"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "getIntField", setsWarning); + clearWarning(); + if (!JNI.canSetField(klass, name, isStatic)) { + throwAccessException(klass, name, true, "setIntField"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "setIntField", setsWarning); + } } + // Test that callbacks are invoked correctly. clearWarning(); - if (!JNI.canSetField(klass, name, isStatic)) { - throwAccessException(klass, name, true, "setIntField"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "setIntField", setsWarning); + if (setsWarning || !canDiscover) { + checkMemberCallback(klass, name, isPublic, true /* isField */); } } @@ -257,42 +299,46 @@ public class ChildClass { // Finish here if we could not discover the field. - if (!canDiscover) { - return; - } + if (canDiscover) { + // Test that modifiers are unaffected. - // Test that modifiers are unaffected. + if (Reflection.canObserveMethodHiddenAccessFlags(klass, name)) { + throwModifiersException(klass, name, false); + } - if (Reflection.canObserveMethodHiddenAccessFlags(klass, name)) { - throwModifiersException(klass, name, false); - } + // Test whether we can invoke the method. This skips non-static interface methods. - // Test whether we can invoke the method. This skips non-static interface methods. + if (!klass.isInterface() || isStatic) { + clearWarning(); + if (!Reflection.canInvokeMethod(klass, name)) { + throwAccessException(klass, name, false, "invoke()"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, false, "invoke()", setsWarning); + } - if (!klass.isInterface() || isStatic) { - clearWarning(); - if (!Reflection.canInvokeMethod(klass, name)) { - throwAccessException(klass, name, false, "invoke()"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "invoke()", setsWarning); - } + clearWarning(); + if (!JNI.canInvokeMethodA(klass, name, isStatic)) { + throwAccessException(klass, name, false, "CallMethodA"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, false, "CallMethodA()", setsWarning); + } - clearWarning(); - if (!JNI.canInvokeMethodA(klass, name, isStatic)) { - throwAccessException(klass, name, false, "CallMethodA"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "CallMethodA()", setsWarning); + clearWarning(); + if (!JNI.canInvokeMethodV(klass, name, isStatic)) { + throwAccessException(klass, name, false, "CallMethodV"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, false, "CallMethodV()", setsWarning); + } } + } - clearWarning(); - if (!JNI.canInvokeMethodV(klass, name, isStatic)) { - throwAccessException(klass, name, false, "CallMethodV"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "CallMethodV()", setsWarning); - } + // Test that callbacks are invoked correctly. + clearWarning(); + if (setsWarning || !canDiscover) { + checkMemberCallback(klass, name, isPublic, false /* isField */); } } -- GitLab From 177696278224434829b6a7a7bc0a3cdebe7ae19d Mon Sep 17 00:00:00 2001 From: Dan Willemsen Date: Fri, 6 Apr 2018 13:26:36 -0700 Subject: [PATCH 214/749] Fix overriding commands warnings/error On mac, where the x86_64 architecture is disabled, and on linux when the ART_BUILD_HOST_[N]DEBUG=false flags are set (or even the TARGET versions), the modules referenced here aren't defined. Due to the text processing, we end up with multiple rules defining the 'Uncompressed', 'Stripped', etc targets: art/build/Android.gtest.mk:97: error: overriding commands for target `Stripped', previously defined at art/build/Android.gtest.mk:93` These are becoming errors in this CL: https://android-review.googlesource.com/c/platform/build/+/657896 Bug: 77611511 Test: on mac: m nothing Test: on linux: ART_BUILD_HOST_DEBUG=false ART_BUILD_HOST_NDEBUG=false \ ART_BUILD_TARGET_DEBUG=false ART_BUILD_TARGET_NDEBUG=false m nothing Test: out/build-aosp_arm.ninja is the same (on linux with no args) Change-Id: I0fec90398839fb6e3cc6443e2e37f934401dc177 --- build/Android.gtest.mk | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index b483e5f6f2..be27869fe0 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -89,43 +89,59 @@ ART_TEST_TARGET_GTEST_EmptyUncompressed_DEX := $(basename $(ART_TEST_TARGET_GTES ART_TEST_HOST_GTEST_MultiDexUncompressed_DEX := $(basename $(ART_TEST_HOST_GTEST_MultiDex_DEX))Uncompressed$(suffix $(ART_TEST_HOST_GTEST_MultiDex_DEX)) ART_TEST_TARGET_GTEST_MultiDexUncompressed_DEX := $(basename $(ART_TEST_TARGET_GTEST_MultiDex_DEX))Uncompressed$(suffix $(ART_TEST_TARGET_GTEST_MultiDex_DEX)) +ifdef ART_TEST_HOST_GTEST_Main_DEX $(ART_TEST_HOST_GTEST_MainStripped_DEX): $(ART_TEST_HOST_GTEST_Main_DEX) cp $< $@ $(call dexpreopt-remove-classes.dex,$@) +endif +ifdef ART_TEST_TARGET_GTEST_Main_DEX $(ART_TEST_TARGET_GTEST_MainStripped_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX) cp $< $@ $(call dexpreopt-remove-classes.dex,$@) +endif +ifdef ART_TEST_HOST_GTEST_Main_DEX $(ART_TEST_HOST_GTEST_MainUncompressed_DEX): $(ART_TEST_HOST_GTEST_Main_DEX) $(ZIPALIGN) cp $< $@ $(call uncompress-dexs, $@) $(call align-package, $@) +endif +ifdef ART_TEST_TARGET_GTEST_Main_DEX $(ART_TEST_TARGET_GTEST_MainUncompressed_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX) $(ZIPALIGN) cp $< $@ $(call uncompress-dexs, $@) $(call align-package, $@) +endif +ifdef ART_TEST_HOST_GTEST_Main_DEX $(ART_TEST_HOST_GTEST_EmptyUncompressed_DEX): $(ZIPALIGN) touch $(dir $@)classes.dex zip -j -qD -X -0 $@ $(dir $@)classes.dex rm $(dir $@)classes.dex +endif +ifdef ART_TEST_TARGET_GTEST_Main_DEX $(ART_TEST_TARGET_GTEST_EmptyUncompressed_DEX): $(ZIPALIGN) touch $(dir $@)classes.dex zip -j -qD -X -0 $@ $(dir $@)classes.dex rm $(dir $@)classes.dex +endif +ifdef ART_TEST_HOST_GTEST_MultiDex_DEX $(ART_TEST_HOST_GTEST_MultiDexUncompressed_DEX): $(ART_TEST_HOST_GTEST_MultiDex_DEX) $(ZIPALIGN) cp $< $@ $(call uncompress-dexs, $@) $(call align-package, $@) +endif +ifdef ART_TEST_TARGET_GTEST_MultiDex_DEX $(ART_TEST_TARGET_GTEST_MultiDexUncompressed_DEX): $(ART_TEST_TARGET_GTEST_MultiDex_DEX) $(ZIPALIGN) cp $< $@ $(call uncompress-dexs, $@) $(call align-package, $@) +endif ART_TEST_GTEST_VerifierDeps_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDeps/*.smali)) ART_TEST_GTEST_VerifierDepsMulti_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDepsMulti/*.smali)) -- GitLab From f8e5d8ce6cdd9048dc8e67f7343d3842189f7601 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 6 Apr 2018 13:35:37 -0700 Subject: [PATCH 215/749] Delete MarkCompact GC Test: test-art-host Bug: 77721758 Change-Id: I99889353eb97f46c59de7ed44f2944f83867031d --- cmdline/cmdline_types.h | 2 - runtime/Android.bp | 1 - runtime/gc/collector/mark_compact.cc | 642 --------------------------- runtime/gc/collector/mark_compact.h | 238 ---------- runtime/gc/collector_type.h | 2 - runtime/gc/heap.cc | 25 +- runtime/gc/heap.h | 4 - runtime/parsed_options_test.cc | 4 +- 8 files changed, 5 insertions(+), 913 deletions(-) delete mode 100644 runtime/gc/collector/mark_compact.cc delete mode 100644 runtime/gc/collector/mark_compact.h diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index c8be69d922..5a25a6ceb6 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -416,8 +416,6 @@ static gc::CollectorType ParseCollectorType(const std::string& option) { return gc::kCollectorTypeGSS; } else if (option == "CC") { return gc::kCollectorTypeCC; - } else if (option == "MC") { - return gc::kCollectorTypeMC; } else { return gc::kCollectorTypeNone; } diff --git a/runtime/Android.bp b/runtime/Android.bp index 9271a0558d..00d4a6080a 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -65,7 +65,6 @@ cc_defaults { "gc/collector/garbage_collector.cc", "gc/collector/immune_region.cc", "gc/collector/immune_spaces.cc", - "gc/collector/mark_compact.cc", "gc/collector/mark_sweep.cc", "gc/collector/partial_mark_sweep.cc", "gc/collector/semi_space.cc", diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc deleted file mode 100644 index 34cc129ce8..0000000000 --- a/runtime/gc/collector/mark_compact.cc +++ /dev/null @@ -1,642 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mark_compact.h" - -#include - -#include "base/macros.h" -#include "base/mutex-inl.h" -#include "base/timing_logger.h" -#include "gc/accounting/heap_bitmap-inl.h" -#include "gc/accounting/mod_union_table.h" -#include "gc/accounting/space_bitmap-inl.h" -#include "gc/heap.h" -#include "gc/reference_processor.h" -#include "gc/space/bump_pointer_space-inl.h" -#include "gc/space/large_object_space.h" -#include "gc/space/space-inl.h" -#include "mirror/class-inl.h" -#include "mirror/object-inl.h" -#include "mirror/object-refvisitor-inl.h" -#include "runtime.h" -#include "stack.h" -#include "thread-current-inl.h" -#include "thread_list.h" - -namespace art { -namespace gc { -namespace collector { - -void MarkCompact::BindBitmaps() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - // Mark all of the spaces we never collect as immune. - for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyNeverCollect || - space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect) { - immune_spaces_.AddSpace(space); - } - } -} - -MarkCompact::MarkCompact(Heap* heap, const std::string& name_prefix) - : GarbageCollector(heap, name_prefix + (name_prefix.empty() ? "" : " ") + "mark compact"), - mark_stack_(nullptr), - space_(nullptr), - mark_bitmap_(nullptr), - collector_name_(name_), - bump_pointer_(nullptr), - live_objects_in_space_(0), - updating_references_(false) {} - -void MarkCompact::RunPhases() { - Thread* self = Thread::Current(); - InitializePhase(); - CHECK(!Locks::mutator_lock_->IsExclusiveHeld(self)); - { - ScopedPause pause(this); - GetHeap()->PreGcVerificationPaused(this); - GetHeap()->PrePauseRosAllocVerification(this); - MarkingPhase(); - ReclaimPhase(); - } - GetHeap()->PostGcVerification(this); - FinishPhase(); -} - -void MarkCompact::ForwardObject(mirror::Object* obj) { - const size_t alloc_size = RoundUp(obj->SizeOf(), space::BumpPointerSpace::kAlignment); - LockWord lock_word = obj->GetLockWord(false); - // If we have a non empty lock word, store it and restore it later. - if (!LockWord::IsDefault(lock_word)) { - // Set the bit in the bitmap so that we know to restore it later. - objects_with_lockword_->Set(obj); - lock_words_to_restore_.push_back(lock_word); - } - obj->SetLockWord(LockWord::FromForwardingAddress(reinterpret_cast(bump_pointer_)), - false); - bump_pointer_ += alloc_size; - ++live_objects_in_space_; -} - - -void MarkCompact::CalculateObjectForwardingAddresses() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - // The bump pointer in the space where the next forwarding address will be. - bump_pointer_ = reinterpret_cast(space_->Begin()); - // Visit all the marked objects in the bitmap. - objects_before_forwarding_->VisitMarkedRange(reinterpret_cast(space_->Begin()), - reinterpret_cast(space_->End()), - [this](mirror::Object* obj) - REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { - DCHECK_ALIGNED(obj, space::BumpPointerSpace::kAlignment); - DCHECK(IsMarked(obj) != nullptr); - ForwardObject(obj); - }); -} - -void MarkCompact::InitializePhase() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - mark_stack_ = heap_->GetMarkStack(); - DCHECK(mark_stack_ != nullptr); - immune_spaces_.Reset(); - CHECK(space_->CanMoveObjects()) << "Attempting compact non-movable space from " << *space_; - // TODO: I don't think we should need heap bitmap lock to Get the mark bitmap. - ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - mark_bitmap_ = heap_->GetMarkBitmap(); - live_objects_in_space_ = 0; -} - -void MarkCompact::ProcessReferences(Thread* self) { - WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap_->GetReferenceProcessor()->ProcessReferences( - false, GetTimings(), GetCurrentIteration()->GetClearSoftReferences(), this); -} - -inline mirror::Object* MarkCompact::MarkObject(mirror::Object* obj) { - if (obj == nullptr) { - return nullptr; - } - if (kUseBakerReadBarrier) { - // Verify all the objects have the correct forward state installed. - obj->AssertReadBarrierState(); - } - if (!immune_spaces_.IsInImmuneRegion(obj)) { - if (objects_before_forwarding_->HasAddress(obj)) { - if (!objects_before_forwarding_->Set(obj)) { - MarkStackPush(obj); // This object was not previously marked. - } - } else { - DCHECK(!space_->HasAddress(obj)); - auto slow_path = [](const mirror::Object* ref) - REQUIRES_SHARED(Locks::mutator_lock_) { - // Marking a large object, make sure its aligned as a sanity check. - if (!IsAligned(ref)) { - Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR)); - LOG(FATAL) << ref; - } - }; - if (!mark_bitmap_->Set(obj, slow_path)) { - // This object was not previously marked. - MarkStackPush(obj); - } - } - } - return obj; -} - -void MarkCompact::MarkingPhase() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - Thread* self = Thread::Current(); - // Bitmap which describes which objects we have to move. - objects_before_forwarding_.reset(accounting::ContinuousSpaceBitmap::Create( - "objects before forwarding", space_->Begin(), space_->Size())); - // Bitmap which describes which lock words we need to restore. - objects_with_lockword_.reset(accounting::ContinuousSpaceBitmap::Create( - "objects with lock words", space_->Begin(), space_->Size())); - CHECK(Locks::mutator_lock_->IsExclusiveHeld(self)); - // Assume the cleared space is already empty. - BindBitmaps(); - t.NewTiming("ProcessCards"); - // Process dirty cards and add dirty cards to mod-union tables. - heap_->ProcessCards(GetTimings(), false, false, true); - // Clear the whole card table since we cannot get any additional dirty cards during the - // paused GC. This saves memory but only works for pause the world collectors. - t.NewTiming("ClearCardTable"); - heap_->GetCardTable()->ClearCardTable(); - // Need to do this before the checkpoint since we don't want any threads to add references to - // the live stack during the recursive mark. - if (kUseThreadLocalAllocationStack) { - t.NewTiming("RevokeAllThreadLocalAllocationStacks"); - heap_->RevokeAllThreadLocalAllocationStacks(self); - } - t.NewTiming("SwapStacks"); - heap_->SwapStacks(); - { - WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - MarkRoots(); - // Mark roots of immune spaces. - UpdateAndMarkModUnion(); - // Recursively mark remaining objects. - MarkReachableObjects(); - } - ProcessReferences(self); - { - ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); - SweepSystemWeaks(); - } - Runtime::Current()->GetClassLinker()->CleanupClassLoaders(); - // Revoke buffers before measuring how many objects were moved since the TLABs need to be revoked - // before they are properly counted. - RevokeAllThreadLocalBuffers(); - // Disabled due to an issue where we have objects in the bump pointer space which reference dead - // objects. - // heap_->PreSweepingGcVerification(this); -} - -void MarkCompact::UpdateAndMarkModUnion() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - for (auto& space : heap_->GetContinuousSpaces()) { - // If the space is immune then we need to mark the references to other spaces. - if (immune_spaces_.ContainsSpace(space)) { - accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space); - if (table != nullptr) { - // TODO: Improve naming. - TimingLogger::ScopedTiming t2( - space->IsZygoteSpace() ? "UpdateAndMarkZygoteModUnionTable" : - "UpdateAndMarkImageModUnionTable", GetTimings()); - table->UpdateAndMarkReferences(this); - } - } - } -} - -void MarkCompact::MarkReachableObjects() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - accounting::ObjectStack* live_stack = heap_->GetLiveStack(); - { - TimingLogger::ScopedTiming t2("MarkAllocStackAsLive", GetTimings()); - heap_->MarkAllocStackAsLive(live_stack); - } - live_stack->Reset(); - // Recursively process the mark stack. - ProcessMarkStack(); -} - -void MarkCompact::ReclaimPhase() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - // Reclaim unmarked objects. - Sweep(false); - // Swap the live and mark bitmaps for each space which we modified space. This is an - // optimization that enables us to not clear live bits inside of the sweep. Only swaps unbound - // bitmaps. - SwapBitmaps(); - GetHeap()->UnBindBitmaps(); // Unbind the live and mark bitmaps. - Compact(); -} - -void MarkCompact::ResizeMarkStack(size_t new_size) { - std::vector> temp(mark_stack_->Begin(), mark_stack_->End()); - CHECK_LE(mark_stack_->Size(), new_size); - mark_stack_->Resize(new_size); - for (auto& obj : temp) { - mark_stack_->PushBack(obj.AsMirrorPtr()); - } -} - -inline void MarkCompact::MarkStackPush(mirror::Object* obj) { - if (UNLIKELY(mark_stack_->Size() >= mark_stack_->Capacity())) { - ResizeMarkStack(mark_stack_->Capacity() * 2); - } - // The object must be pushed on to the mark stack. - mark_stack_->PushBack(obj); -} - -void MarkCompact::MarkHeapReference(mirror::HeapReference* obj_ptr, - bool do_atomic_update ATTRIBUTE_UNUSED) { - if (updating_references_) { - UpdateHeapReference(obj_ptr); - } else { - MarkObject(obj_ptr->AsMirrorPtr()); - } -} - -void MarkCompact::VisitRoots( - mirror::Object*** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED) { - for (size_t i = 0; i < count; ++i) { - MarkObject(*roots[i]); - } -} - -void MarkCompact::VisitRoots( - mirror::CompressedReference** roots, size_t count, - const RootInfo& info ATTRIBUTE_UNUSED) { - for (size_t i = 0; i < count; ++i) { - MarkObject(roots[i]->AsMirrorPtr()); - } -} - -class MarkCompact::UpdateRootVisitor : public RootVisitor { - public: - explicit UpdateRootVisitor(MarkCompact* collector) : collector_(collector) {} - - void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED) - OVERRIDE REQUIRES(Locks::mutator_lock_) - REQUIRES_SHARED(Locks::heap_bitmap_lock_) { - for (size_t i = 0; i < count; ++i) { - mirror::Object* obj = *roots[i]; - mirror::Object* new_obj = collector_->GetMarkedForwardAddress(obj); - if (obj != new_obj) { - *roots[i] = new_obj; - DCHECK(new_obj != nullptr); - } - } - } - - void VisitRoots(mirror::CompressedReference** roots, size_t count, - const RootInfo& info ATTRIBUTE_UNUSED) - OVERRIDE REQUIRES(Locks::mutator_lock_) - REQUIRES_SHARED(Locks::heap_bitmap_lock_) { - for (size_t i = 0; i < count; ++i) { - mirror::Object* obj = roots[i]->AsMirrorPtr(); - mirror::Object* new_obj = collector_->GetMarkedForwardAddress(obj); - if (obj != new_obj) { - roots[i]->Assign(new_obj); - DCHECK(new_obj != nullptr); - } - } - } - - private: - MarkCompact* const collector_; -}; - -class MarkCompact::UpdateObjectReferencesVisitor { - public: - explicit UpdateObjectReferencesVisitor(MarkCompact* collector) : collector_(collector) {} - - void operator()(mirror::Object* obj) const REQUIRES_SHARED(Locks::heap_bitmap_lock_) - REQUIRES(Locks::mutator_lock_) ALWAYS_INLINE { - collector_->UpdateObjectReferences(obj); - } - - private: - MarkCompact* const collector_; -}; - -void MarkCompact::UpdateReferences() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - updating_references_ = true; - Runtime* runtime = Runtime::Current(); - // Update roots. - UpdateRootVisitor update_root_visitor(this); - runtime->VisitRoots(&update_root_visitor); - // Update object references in mod union tables and spaces. - for (const auto& space : heap_->GetContinuousSpaces()) { - // If the space is immune then we need to mark the references to other spaces. - accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space); - if (table != nullptr) { - // TODO: Improve naming. - TimingLogger::ScopedTiming t2( - space->IsZygoteSpace() ? "UpdateZygoteModUnionTableReferences" : - "UpdateImageModUnionTableReferences", - GetTimings()); - table->UpdateAndMarkReferences(this); - } else { - // No mod union table, so we need to scan the space using bitmap visit. - // Scan the space using bitmap visit. - accounting::ContinuousSpaceBitmap* bitmap = space->GetLiveBitmap(); - if (bitmap != nullptr) { - UpdateObjectReferencesVisitor visitor(this); - bitmap->VisitMarkedRange(reinterpret_cast(space->Begin()), - reinterpret_cast(space->End()), - visitor); - } - } - } - CHECK(!kMovingClasses) - << "Didn't update large object classes since they are assumed to not move."; - // Update the system weaks, these should already have been swept. - runtime->SweepSystemWeaks(this); - // Update the objects in the bump pointer space last, these objects don't have a bitmap. - UpdateObjectReferencesVisitor visitor(this); - objects_before_forwarding_->VisitMarkedRange(reinterpret_cast(space_->Begin()), - reinterpret_cast(space_->End()), - visitor); - // Update the reference processor cleared list. - heap_->GetReferenceProcessor()->UpdateRoots(this); - updating_references_ = false; -} - -void MarkCompact::Compact() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - CalculateObjectForwardingAddresses(); - UpdateReferences(); - MoveObjects(); - // Space - int64_t objects_freed = space_->GetObjectsAllocated() - live_objects_in_space_; - int64_t bytes_freed = reinterpret_cast(space_->End()) - - reinterpret_cast(bump_pointer_); - t.NewTiming("RecordFree"); - space_->RecordFree(objects_freed, bytes_freed); - RecordFree(ObjectBytePair(objects_freed, bytes_freed)); - space_->SetEnd(bump_pointer_); - // Need to zero out the memory we freed. TODO: Use madvise for pages. - memset(bump_pointer_, 0, bytes_freed); -} - -// Marks all objects in the root set. -void MarkCompact::MarkRoots() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - Runtime::Current()->VisitRoots(this); -} - -inline void MarkCompact::UpdateHeapReference(mirror::HeapReference* reference) { - mirror::Object* obj = reference->AsMirrorPtr(); - if (obj != nullptr) { - mirror::Object* new_obj = GetMarkedForwardAddress(obj); - if (obj != new_obj) { - DCHECK(new_obj != nullptr); - reference->Assign(new_obj); - } - } -} - -class MarkCompact::UpdateReferenceVisitor { - public: - explicit UpdateReferenceVisitor(MarkCompact* collector) : collector_(collector) {} - - void operator()(mirror::Object* obj, MemberOffset offset, bool /*is_static*/) const - ALWAYS_INLINE REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { - collector_->UpdateHeapReference(obj->GetFieldObjectReferenceAddr(offset)); - } - - void operator()(ObjPtr /*klass*/, mirror::Reference* ref) const - REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { - collector_->UpdateHeapReference( - ref->GetFieldObjectReferenceAddr(mirror::Reference::ReferentOffset())); - } - - // TODO: Remove NO_THREAD_SAFETY_ANALYSIS when clang better understands visitors. - void VisitRootIfNonNull(mirror::CompressedReference* root) const - NO_THREAD_SAFETY_ANALYSIS { - if (!root->IsNull()) { - VisitRoot(root); - } - } - - void VisitRoot(mirror::CompressedReference* root) const - NO_THREAD_SAFETY_ANALYSIS { - root->Assign(collector_->GetMarkedForwardAddress(root->AsMirrorPtr())); - } - - private: - MarkCompact* const collector_; -}; - -void MarkCompact::UpdateObjectReferences(mirror::Object* obj) { - UpdateReferenceVisitor visitor(this); - obj->VisitReferences(visitor, visitor); -} - -inline mirror::Object* MarkCompact::GetMarkedForwardAddress(mirror::Object* obj) { - DCHECK(obj != nullptr); - if (objects_before_forwarding_->HasAddress(obj)) { - DCHECK(objects_before_forwarding_->Test(obj)); - mirror::Object* ret = - reinterpret_cast(obj->GetLockWord(false).ForwardingAddress()); - DCHECK(ret != nullptr); - return ret; - } - DCHECK(!space_->HasAddress(obj)); - return obj; -} - -mirror::Object* MarkCompact::IsMarked(mirror::Object* object) { - if (immune_spaces_.IsInImmuneRegion(object)) { - return object; - } - if (updating_references_) { - return GetMarkedForwardAddress(object); - } - if (objects_before_forwarding_->HasAddress(object)) { - return objects_before_forwarding_->Test(object) ? object : nullptr; - } - return mark_bitmap_->Test(object) ? object : nullptr; -} - -bool MarkCompact::IsNullOrMarkedHeapReference(mirror::HeapReference* ref_ptr, - // MarkCompact does the GC in a pause. No CAS needed. - bool do_atomic_update ATTRIBUTE_UNUSED) { - // Side effect free since we call this before ever moving objects. - mirror::Object* obj = ref_ptr->AsMirrorPtr(); - if (obj == nullptr) { - return true; - } - return IsMarked(obj) != nullptr; -} - -void MarkCompact::SweepSystemWeaks() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - Runtime::Current()->SweepSystemWeaks(this); -} - -bool MarkCompact::ShouldSweepSpace(space::ContinuousSpace* space) const { - return space != space_ && !immune_spaces_.ContainsSpace(space); -} - -void MarkCompact::MoveObject(mirror::Object* obj, size_t len) { - // Look at the forwarding address stored in the lock word to know where to copy. - DCHECK(space_->HasAddress(obj)) << obj; - uintptr_t dest_addr = obj->GetLockWord(false).ForwardingAddress(); - mirror::Object* dest_obj = reinterpret_cast(dest_addr); - DCHECK(space_->HasAddress(dest_obj)) << dest_obj; - // Use memmove since there may be overlap. - memmove(reinterpret_cast(dest_addr), reinterpret_cast(obj), len); - // Restore the saved lock word if needed. - LockWord lock_word = LockWord::Default(); - if (UNLIKELY(objects_with_lockword_->Test(obj))) { - lock_word = lock_words_to_restore_.front(); - lock_words_to_restore_.pop_front(); - } - dest_obj->SetLockWord(lock_word, false); -} - -void MarkCompact::MoveObjects() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - // Move the objects in the before forwarding bitmap. - objects_before_forwarding_->VisitMarkedRange(reinterpret_cast(space_->Begin()), - reinterpret_cast(space_->End()), - [this](mirror::Object* obj) - REQUIRES_SHARED(Locks::heap_bitmap_lock_) - REQUIRES(Locks::mutator_lock_) ALWAYS_INLINE { - MoveObject(obj, obj->SizeOf()); - }); - CHECK(lock_words_to_restore_.empty()); -} - -void MarkCompact::Sweep(bool swap_bitmaps) { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - DCHECK(mark_stack_->IsEmpty()); - for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (space->IsContinuousMemMapAllocSpace()) { - space::ContinuousMemMapAllocSpace* alloc_space = space->AsContinuousMemMapAllocSpace(); - if (!ShouldSweepSpace(alloc_space)) { - continue; - } - TimingLogger::ScopedTiming t2( - alloc_space->IsZygoteSpace() ? "SweepZygoteSpace" : "SweepAllocSpace", GetTimings()); - RecordFree(alloc_space->Sweep(swap_bitmaps)); - } - } - SweepLargeObjects(swap_bitmaps); -} - -void MarkCompact::SweepLargeObjects(bool swap_bitmaps) { - space::LargeObjectSpace* los = heap_->GetLargeObjectsSpace(); - if (los != nullptr) { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());\ - RecordFreeLOS(los->Sweep(swap_bitmaps)); - } -} - -// Process the "referent" field in a java.lang.ref.Reference. If the referent has not yet been -// marked, put it on the appropriate list in the heap for later processing. -void MarkCompact::DelayReferenceReferent(ObjPtr klass, - ObjPtr reference) { - heap_->GetReferenceProcessor()->DelayReferenceReferent(klass, reference, this); -} - -class MarkCompact::MarkObjectVisitor { - public: - explicit MarkObjectVisitor(MarkCompact* collector) : collector_(collector) {} - - void operator()(ObjPtr obj, - MemberOffset offset, - bool /*is_static*/) const ALWAYS_INLINE - REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { - // Object was already verified when we scanned it. - collector_->MarkObject(obj->GetFieldObject(offset)); - } - - void operator()(ObjPtr klass, - ObjPtr ref) const - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(Locks::heap_bitmap_lock_) { - collector_->DelayReferenceReferent(klass, ref); - } - - // TODO: Remove NO_THREAD_SAFETY_ANALYSIS when clang better understands visitors. - void VisitRootIfNonNull(mirror::CompressedReference* root) const - NO_THREAD_SAFETY_ANALYSIS { - if (!root->IsNull()) { - VisitRoot(root); - } - } - - void VisitRoot(mirror::CompressedReference* root) const - NO_THREAD_SAFETY_ANALYSIS { - collector_->MarkObject(root->AsMirrorPtr()); - } - - private: - MarkCompact* const collector_; -}; - -// Visit all of the references of an object and update. -void MarkCompact::ScanObject(mirror::Object* obj) { - MarkObjectVisitor visitor(this); - obj->VisitReferences(visitor, visitor); -} - -// Scan anything that's on the mark stack. -void MarkCompact::ProcessMarkStack() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - while (!mark_stack_->IsEmpty()) { - mirror::Object* obj = mark_stack_->PopBack(); - DCHECK(obj != nullptr); - ScanObject(obj); - } -} - -void MarkCompact::SetSpace(space::BumpPointerSpace* space) { - DCHECK(space != nullptr); - space_ = space; -} - -void MarkCompact::FinishPhase() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - space_ = nullptr; - CHECK(mark_stack_->IsEmpty()); - mark_stack_->Reset(); - // Clear all of the spaces' mark bitmaps. - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - heap_->ClearMarkedObjects(); - // Release our bitmaps. - objects_before_forwarding_.reset(nullptr); - objects_with_lockword_.reset(nullptr); -} - -void MarkCompact::RevokeAllThreadLocalBuffers() { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - GetHeap()->RevokeAllThreadLocalBuffers(); -} - -} // namespace collector -} // namespace gc -} // namespace art diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h deleted file mode 100644 index e7749597cd..0000000000 --- a/runtime/gc/collector/mark_compact.h +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_GC_COLLECTOR_MARK_COMPACT_H_ -#define ART_RUNTIME_GC_COLLECTOR_MARK_COMPACT_H_ - -#include -#include // For unique_ptr. - -#include "base/atomic.h" -#include "base/macros.h" -#include "base/mutex.h" -#include "garbage_collector.h" -#include "gc/accounting/heap_bitmap.h" -#include "gc_root.h" -#include "immune_spaces.h" -#include "lock_word.h" -#include "offsets.h" - -namespace art { - -class Thread; - -namespace mirror { -class Class; -class Object; -} // namespace mirror - -namespace gc { - -class Heap; - -namespace accounting { -template class AtomicStack; -typedef AtomicStack ObjectStack; -} // namespace accounting - -namespace space { -class BumpPointerSpace; -class ContinuousMemMapAllocSpace; -class ContinuousSpace; -} // namespace space - -namespace collector { - -class MarkCompact : public GarbageCollector { - public: - explicit MarkCompact(Heap* heap, const std::string& name_prefix = ""); - ~MarkCompact() {} - - virtual void RunPhases() OVERRIDE NO_THREAD_SAFETY_ANALYSIS; - void InitializePhase(); - void MarkingPhase() REQUIRES(Locks::mutator_lock_) - REQUIRES(!Locks::heap_bitmap_lock_); - void ReclaimPhase() REQUIRES(Locks::mutator_lock_) - REQUIRES(!Locks::heap_bitmap_lock_); - void FinishPhase() REQUIRES(Locks::mutator_lock_); - void MarkReachableObjects() - REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_); - virtual GcType GetGcType() const OVERRIDE { - return kGcTypePartial; - } - virtual CollectorType GetCollectorType() const OVERRIDE { - return kCollectorTypeMC; - } - - // Sets which space we will be copying objects in. - void SetSpace(space::BumpPointerSpace* space); - - // Initializes internal structures. - void Init(); - - // Find the default mark bitmap. - void FindDefaultMarkBitmap(); - - void ScanObject(mirror::Object* obj) - REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - - // Marks the root set at the start of a garbage collection. - void MarkRoots() - REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - - // Bind the live bits to the mark bits of bitmaps for spaces that are never collected, ie - // the image. Mark that portion of the heap as immune. - void BindBitmaps() REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Locks::heap_bitmap_lock_); - - void UnBindBitmaps() - REQUIRES(Locks::heap_bitmap_lock_); - - void ProcessReferences(Thread* self) REQUIRES(Locks::mutator_lock_) - REQUIRES(Locks::mutator_lock_); - - // Sweeps unmarked objects to complete the garbage collection. - void Sweep(bool swap_bitmaps) REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - - // Sweeps unmarked objects to complete the garbage collection. - void SweepLargeObjects(bool swap_bitmaps) REQUIRES(Locks::heap_bitmap_lock_); - - void SweepSystemWeaks() - REQUIRES_SHARED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - - virtual void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info) - OVERRIDE REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_); - - virtual void VisitRoots(mirror::CompressedReference** roots, size_t count, - const RootInfo& info) - OVERRIDE REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_); - - // Schedules an unmarked object for reference processing. - void DelayReferenceReferent(ObjPtr klass, ObjPtr reference) - REQUIRES_SHARED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - - protected: - // Returns null if the object is not marked, otherwise returns the forwarding address (same as - // object for non movable things). - mirror::Object* GetMarkedForwardAddress(mirror::Object* object) - REQUIRES(Locks::mutator_lock_) - REQUIRES_SHARED(Locks::heap_bitmap_lock_); - - // Marks or unmarks a large object based on whether or not set is true. If set is true, then we - // mark, otherwise we unmark. - bool MarkLargeObject(const mirror::Object* obj) - REQUIRES(Locks::heap_bitmap_lock_) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Expand mark stack to 2x its current size. - void ResizeMarkStack(size_t new_size) REQUIRES_SHARED(Locks::mutator_lock_); - - // Returns true if we should sweep the space. - bool ShouldSweepSpace(space::ContinuousSpace* space) const; - - // Push an object onto the mark stack. - void MarkStackPush(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_); - - void UpdateAndMarkModUnion() - REQUIRES(Locks::heap_bitmap_lock_) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Recursively blackens objects on the mark stack. - void ProcessMarkStack() - REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_); - - // 3 pass mark compact approach. - void Compact() REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_); - // Calculate the forwarding address of objects marked as "live" in the objects_before_forwarding - // bitmap. - void CalculateObjectForwardingAddresses() - REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_); - // Update the references of objects by using the forwarding addresses. - void UpdateReferences() REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_); - // Move objects and restore lock words. - void MoveObjects() REQUIRES(Locks::mutator_lock_); - // Move a single object to its forward address. - void MoveObject(mirror::Object* obj, size_t len) REQUIRES(Locks::mutator_lock_); - // Mark a single object. - virtual mirror::Object* MarkObject(mirror::Object* obj) OVERRIDE - REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - virtual void MarkHeapReference(mirror::HeapReference* obj_ptr, - bool do_atomic_update) OVERRIDE - REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - virtual mirror::Object* IsMarked(mirror::Object* obj) OVERRIDE - REQUIRES_SHARED(Locks::heap_bitmap_lock_) - REQUIRES(Locks::mutator_lock_); - virtual bool IsNullOrMarkedHeapReference(mirror::HeapReference* obj, - bool do_atomic_update) OVERRIDE - REQUIRES_SHARED(Locks::heap_bitmap_lock_) - REQUIRES(Locks::mutator_lock_); - void ForwardObject(mirror::Object* obj) REQUIRES(Locks::heap_bitmap_lock_, - Locks::mutator_lock_); - // Update a single heap reference. - void UpdateHeapReference(mirror::HeapReference* reference) - REQUIRES_SHARED(Locks::heap_bitmap_lock_) - REQUIRES(Locks::mutator_lock_); - // Update all of the references of a single object. - void UpdateObjectReferences(mirror::Object* obj) - REQUIRES_SHARED(Locks::heap_bitmap_lock_) - REQUIRES(Locks::mutator_lock_); - - // Revoke all the thread-local buffers. - void RevokeAllThreadLocalBuffers(); - - accounting::ObjectStack* mark_stack_; - - // Every object inside the immune spaces is assumed to be marked. - ImmuneSpaces immune_spaces_; - - // Bump pointer space which we are collecting. - space::BumpPointerSpace* space_; - // Cached mark bitmap as an optimization. - accounting::HeapBitmap* mark_bitmap_; - - // The name of the collector. - std::string collector_name_; - - // The bump pointer in the space where the next forwarding address will be. - uint8_t* bump_pointer_; - // How many live objects we have in the space. - size_t live_objects_in_space_; - - // Bitmap which describes which objects we have to move, need to do / 2 so that we can handle - // objects which are only 8 bytes. - std::unique_ptr objects_before_forwarding_; - // Bitmap which describes which lock words we need to restore. - std::unique_ptr objects_with_lockword_; - // Which lock words we need to restore as we are moving objects. - std::deque lock_words_to_restore_; - - // State whether or not we are updating references. - bool updating_references_; - - private: - class MarkObjectVisitor; - class UpdateObjectReferencesVisitor; - class UpdateReferenceVisitor; - class UpdateRootVisitor; - - DISALLOW_IMPLICIT_CONSTRUCTORS(MarkCompact); -}; - -} // namespace collector -} // namespace gc -} // namespace art - -#endif // ART_RUNTIME_GC_COLLECTOR_MARK_COMPACT_H_ diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h index 8979e742c8..4759fca46c 100644 --- a/runtime/gc/collector_type.h +++ b/runtime/gc/collector_type.h @@ -34,8 +34,6 @@ enum CollectorType { kCollectorTypeSS, // A generational variant of kCollectorTypeSS. kCollectorTypeGSS, - // Mark compact collector. - kCollectorTypeMC, // Heap trimming collector, doesn't do any actual collecting. kCollectorTypeHeapTrim, // A (mostly) concurrent copying collector. diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index f16138c058..684922099b 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -48,7 +48,6 @@ #include "gc/accounting/remembered_set.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/collector/concurrent_copying.h" -#include "gc/collector/mark_compact.h" #include "gc/collector/mark_sweep.h" #include "gc/collector/partial_mark_sweep.h" #include "gc/collector/semi_space.h" @@ -262,7 +261,6 @@ Heap::Heap(size_t initial_size, verify_object_mode_(kVerifyObjectModeDisabled), disable_moving_gc_count_(0), semi_space_collector_(nullptr), - mark_compact_collector_(nullptr), concurrent_copying_collector_(nullptr), is_running_on_memory_tool_(Runtime::Current()->IsRunningOnMemoryTool()), use_tlab_(use_tlab), @@ -603,10 +601,6 @@ Heap::Heap(size_t initial_size, concurrent_copying_collector_->SetRegionSpace(region_space_); garbage_collectors_.push_back(concurrent_copying_collector_); } - if (MayUseCollector(kCollectorTypeMC)) { - mark_compact_collector_ = new collector::MarkCompact(this); - garbage_collectors_.push_back(mark_compact_collector_); - } } if (!GetBootImageSpaces().empty() && non_moving_space_ != nullptr && (is_zygote || separate_non_moving_space || foreground_collector_type_ == kCollectorTypeGSS)) { @@ -2122,10 +2116,6 @@ void Heap::TransitionCollector(CollectorType collector_type) { void Heap::ChangeCollector(CollectorType collector_type) { // TODO: Only do this with all mutators suspended to avoid races. if (collector_type != collector_type_) { - if (collector_type == kCollectorTypeMC) { - // Don't allow mark compact unless support is compiled in. - CHECK(kMarkCompactSupport); - } collector_type_ = collector_type; gc_plan_.clear(); switch (collector_type_) { @@ -2138,7 +2128,6 @@ void Heap::ChangeCollector(CollectorType collector_type) { } break; } - case kCollectorTypeMC: // Fall-through. case kCollectorTypeSS: // Fall-through. case kCollectorTypeGSS: { gc_plan_.push_back(collector::kGcTypeFull); @@ -2487,13 +2476,9 @@ collector::GarbageCollector* Heap::Compact(space::ContinuousMemMapAllocSpace* ta semi_space_collector_->SetToSpace(target_space); semi_space_collector_->Run(gc_cause, false); return semi_space_collector_; - } else { - CHECK(target_space->IsBumpPointerSpace()) - << "In-place compaction is only supported for bump pointer spaces"; - mark_compact_collector_->SetSpace(target_space->AsBumpPointerSpace()); - mark_compact_collector_->Run(kGcCauseCollectorTransition, false); - return mark_compact_collector_; } + LOG(FATAL) << "Unsupported"; + UNREACHABLE(); } void Heap::TraceHeapSize(size_t heap_size) { @@ -2583,14 +2568,10 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, case kCollectorTypeCC: collector = concurrent_copying_collector_; break; - case kCollectorTypeMC: - mark_compact_collector_->SetSpace(bump_pointer_space_); - collector = mark_compact_collector_; - break; default: LOG(FATAL) << "Invalid collector type " << static_cast(collector_type_); } - if (collector != mark_compact_collector_ && collector != concurrent_copying_collector_) { + if (collector != concurrent_copying_collector_) { temp_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); if (kIsDebugBuild) { // Try to read each page of the memory map in case mprotect didn't work properly b/19894268. diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index ef1c0887bb..99ebab9357 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -84,7 +84,6 @@ class RememberedSet; namespace collector { class ConcurrentCopying; class GarbageCollector; -class MarkCompact; class MarkSweep; class SemiSpace; } // namespace collector @@ -883,7 +882,6 @@ class Heap { collector_type == kCollectorTypeGSS || collector_type == kCollectorTypeCC || collector_type == kCollectorTypeCCBackground || - collector_type == kCollectorTypeMC || collector_type == kCollectorTypeHomogeneousSpaceCompact; } bool ShouldAllocLargeObject(ObjPtr c, size_t byte_count) const @@ -1354,7 +1352,6 @@ class Heap { std::vector garbage_collectors_; collector::SemiSpace* semi_space_collector_; - collector::MarkCompact* mark_compact_collector_; collector::ConcurrentCopying* concurrent_copying_collector_; const bool is_running_on_memory_tool_; @@ -1442,7 +1439,6 @@ class Heap { friend class CollectorTransitionTask; friend class collector::GarbageCollector; - friend class collector::MarkCompact; friend class collector::ConcurrentCopying; friend class collector::MarkSweep; friend class collector::SemiSpace; diff --git a/runtime/parsed_options_test.cc b/runtime/parsed_options_test.cc index 8948c710db..9e5d9abe3b 100644 --- a/runtime/parsed_options_test.cc +++ b/runtime/parsed_options_test.cc @@ -112,7 +112,7 @@ TEST_F(ParsedOptionsTest, ParsedOptions) { TEST_F(ParsedOptionsTest, ParsedOptionsGc) { RuntimeOptions options; - options.push_back(std::make_pair("-Xgc:MC", nullptr)); + options.push_back(std::make_pair("-Xgc:SS", nullptr)); RuntimeArgumentMap map; bool parsed = ParsedOptions::Parse(options, false, &map); @@ -124,7 +124,7 @@ TEST_F(ParsedOptionsTest, ParsedOptionsGc) { EXPECT_TRUE(map.Exists(Opt::GcOption)); XGcOption xgc = map.GetOrDefault(Opt::GcOption); - EXPECT_EQ(gc::kCollectorTypeMC, xgc.collector_type_); + EXPECT_EQ(gc::kCollectorTypeSS, xgc.collector_type_); } TEST_F(ParsedOptionsTest, ParsedOptionsInstructionSet) { -- GitLab From 4eb1779101686c864aac0ad504b61ee1d5c4a4a8 Mon Sep 17 00:00:00 2001 From: Greg Kaiser Date: Fri, 6 Apr 2018 14:26:23 -0700 Subject: [PATCH 216/749] patchoat: Move nullptr CHECK We now check 'out' for nullptr prior to dereferencing it. Test: Treehugger Change-Id: I4b0f871a24894107b895789a1b73b95847438dcc --- patchoat/patchoat.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index b9a9a69ab5..dc9d990e29 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -700,6 +700,7 @@ bool PatchOat::Verify(const std::string& image_location, } bool PatchOat::WriteImage(File* out) { + CHECK(out != nullptr); TimingLogger::ScopedTiming t("Writing image File", timings_); std::string error_msg; @@ -709,7 +710,6 @@ bool PatchOat::WriteImage(File* out) { true /* read_only_mode */, &error_msg); CHECK(image_ != nullptr); - CHECK(out != nullptr); size_t expect = image_->Size(); if (out->WriteFully(reinterpret_cast(image_->Begin()), expect) && out->SetLength(expect) == 0) { -- GitLab From d99f203eab7eb310f8326f2739fdf44cd0025679 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Fri, 6 Apr 2018 15:24:35 -0700 Subject: [PATCH 217/749] Count switch as branch. Rationale: Builder heuristics bail for method without branches. A method with a switch should not be discarded that easily. Boot size delta (oat files, sailfish arm64): + 16488 bytes Test: test-art-host,target Bug: b/77652521 Change-Id: I6f90fcd65263e85024d8a268bcd8bfa739af2c6f --- compiler/optimizing/block_builder.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/optimizing/block_builder.cc b/compiler/optimizing/block_builder.cc index 95f2e98ac6..d9df23fd47 100644 --- a/compiler/optimizing/block_builder.cc +++ b/compiler/optimizing/block_builder.cc @@ -107,6 +107,7 @@ bool HBasicBlockBuilder::CreateBranchTargets() { number_of_branches_++; MaybeCreateBlockAt(dex_pc + instruction.GetTargetOffset()); } else if (instruction.IsSwitch()) { + number_of_branches_++; // count as at least one branch (b/77652521) DexSwitchTable table(instruction, dex_pc); for (DexSwitchTableIterator s_it(table); !s_it.Done(); s_it.Advance()) { MaybeCreateBlockAt(dex_pc + s_it.CurrentTargetOffset()); -- GitLab From dffcbe9d8686bc2010695c95c3a6573596af34fe Mon Sep 17 00:00:00 2001 From: John Stuart Date: Fri, 6 Apr 2018 16:31:43 -0700 Subject: [PATCH 218/749] Remove unused DexFile UTF-16-based string lookup DexFile has 2 methods for finding a string ID given an input string: - MUTF-8 (C null-terminated) - UTF-16 Only the first one is used in ART. Bug: 77725588 Test: test-art-host Change-Id: I2bdaecf4914aa346b77c9a0f6ad17f96abbead9d --- libdexfile/dex/dex_file.cc | 19 ------------------- libdexfile/dex/dex_file.h | 3 --- 2 files changed, 22 deletions(-) diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc index 1f471106df..8cfee6655e 100644 --- a/libdexfile/dex/dex_file.cc +++ b/libdexfile/dex/dex_file.cc @@ -349,25 +349,6 @@ const DexFile::TypeId* DexFile::FindTypeId(const char* string) const { return nullptr; } -const DexFile::StringId* DexFile::FindStringId(const uint16_t* string, size_t length) const { - int32_t lo = 0; - int32_t hi = NumStringIds() - 1; - while (hi >= lo) { - int32_t mid = (hi + lo) / 2; - const DexFile::StringId& str_id = GetStringId(dex::StringIndex(mid)); - const char* str = GetStringData(str_id); - int compare = CompareModifiedUtf8ToUtf16AsCodePointValues(str, string, length); - if (compare > 0) { - lo = mid + 1; - } else if (compare < 0) { - hi = mid - 1; - } else { - return &str_id; - } - } - return nullptr; -} - const DexFile::TypeId* DexFile::FindTypeId(dex::StringIndex string_idx) const { int32_t lo = 0; int32_t hi = NumTypeIds() - 1; diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index 683a8243ed..4098b4250a 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -504,9 +504,6 @@ class DexFile { const TypeId* FindTypeId(const char* string) const; - // Looks up a string id for a given utf16 string. - const StringId* FindStringId(const uint16_t* string, size_t length) const; - // Returns the number of type identifiers in the .dex file. uint32_t NumTypeIds() const { DCHECK(header_ != nullptr) << GetLocation(); -- GitLab From 6f99ad983f7a0d2712a6aae0554a33ac2c1708a4 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Fri, 6 Apr 2018 17:46:59 -0700 Subject: [PATCH 219/749] Remove unused SIZEOF_MEMBER. Bug: N/A Test: builds Change-Id: I941a5ad71020e4156c1f6fc94faadf9864137f05 --- libartbase/base/macros.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/libartbase/base/macros.h b/libartbase/base/macros.h index 6dd981dc9e..f26cf0708b 100644 --- a/libartbase/base/macros.h +++ b/libartbase/base/macros.h @@ -45,8 +45,6 @@ template ART_FRIEND_TEST(test_set_name, individual_test) private: \ void* operator new(size_t) = delete // NOLINT -#define SIZEOF_MEMBER(t, f) sizeof((reinterpret_cast(4096))->f) // NOLINT - #define OFFSETOF_MEMBER(t, f) \ (reinterpret_cast(&reinterpret_cast(16)->f) - static_cast(16u)) // NOLINT -- GitLab From 91690828cdcf0b2c1572748f826771bb78fe884e Mon Sep 17 00:00:00 2001 From: Richard Uhler Date: Mon, 9 Apr 2018 10:27:24 +0100 Subject: [PATCH 220/749] Improve error message in 151-OpenFileLimit. Bug: 77641050 Test: ./art/test/testrunner/testrunner.py -b --host --jit --32 -t 151-OpenFileLimit Change-Id: Icb75fa08d3a61ce93dcadba43e4f1c558e131df4 --- test/151-OpenFileLimit/src/Main.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/151-OpenFileLimit/src/Main.java b/test/151-OpenFileLimit/src/Main.java index de5890cbfa..9b16090fbb 100644 --- a/test/151-OpenFileLimit/src/Main.java +++ b/test/151-OpenFileLimit/src/Main.java @@ -38,7 +38,8 @@ public class Main { if (e.getMessage().contains("Too many open files")) { System.out.println("Message includes \"Too many open files\""); } else { - System.out.println(e.getMessage()); + System.out.println("Unexpected exception:"); + e.printStackTrace(); } } -- GitLab From 6097672947fe8b2632cfd10e3d8e44792199a346 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 6 Apr 2018 15:29:38 +0100 Subject: [PATCH 221/749] Remove some unnecessary items from WellKnownClasses. Replace a few uses with alternatives, namely mirror::{Constructor,Method,Field}::StaticClass(). Alternatively, we could use class roots in the ClassLinker. And clear dalvik_system_VMRuntime_nonSdkApiUsageConsumer in WellKnownClasses::Clear(). Test: testrunner.py --host Bug: 74943277 Change-Id: I7561db76cd62d376c22efd4386dffd83ec74aa66 --- runtime/check_jni.cc | 11 ++++++----- runtime/native/java_lang_Class.cc | 6 ++---- runtime/well_known_classes.cc | 16 +--------------- runtime/well_known_classes.h | 5 ----- 4 files changed, 9 insertions(+), 29 deletions(-) diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index 900ce0eac3..c625a9700c 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -37,6 +37,8 @@ #include "java_vm_ext.h" #include "jni_internal.h" #include "mirror/class-inl.h" +#include "mirror/field.h" +#include "mirror/method.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" @@ -599,9 +601,8 @@ class ScopedCheck { AbortF("expected non-null method"); return false; } - mirror::Class* c = method->GetClass(); - if (soa.Decode(WellKnownClasses::java_lang_reflect_Method) != c && - soa.Decode(WellKnownClasses::java_lang_reflect_Constructor) != c) { + ObjPtr c = method->GetClass(); + if (mirror::Method::StaticClass() != c && mirror::Constructor::StaticClass() != c) { AbortF("expected java.lang.reflect.Method or " "java.lang.reflect.Constructor but got object of type %s: %p", method->PrettyTypeOf().c_str(), jmethod); @@ -630,8 +631,8 @@ class ScopedCheck { AbortF("expected non-null java.lang.reflect.Field"); return false; } - mirror::Class* c = field->GetClass(); - if (soa.Decode(WellKnownClasses::java_lang_reflect_Field) != c) { + ObjPtr c = field->GetClass(); + if (mirror::Field::StaticClass() != c) { AbortF("expected java.lang.reflect.Field but got object of type %s: %p", field->PrettyTypeOf().c_str(), jfield); return false; diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index a8b203bff2..82300fe9ba 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -683,8 +683,7 @@ static jobject Class_getEnclosingConstructorNative(JNIEnv* env, jobject javaThis } ObjPtr method = annotations::GetEnclosingMethod(klass); if (method != nullptr) { - if (soa.Decode(WellKnownClasses::java_lang_reflect_Constructor) == - method->GetClass()) { + if (mirror::Constructor::StaticClass() == method->GetClass()) { return soa.AddLocalReference(method); } } @@ -700,8 +699,7 @@ static jobject Class_getEnclosingMethodNative(JNIEnv* env, jobject javaThis) { } ObjPtr method = annotations::GetEnclosingMethod(klass); if (method != nullptr) { - if (soa.Decode(WellKnownClasses::java_lang_reflect_Method) == - method->GetClass()) { + if (mirror::Method::StaticClass() == method->GetClass()) { return soa.AddLocalReference(method); } } diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 742e713774..f5d112c30b 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -58,10 +58,6 @@ jclass WellKnownClasses::java_lang_IllegalAccessError; jclass WellKnownClasses::java_lang_NoClassDefFoundError; jclass WellKnownClasses::java_lang_Object; jclass WellKnownClasses::java_lang_OutOfMemoryError; -jclass WellKnownClasses::java_lang_reflect_Constructor; -jclass WellKnownClasses::java_lang_reflect_Executable; -jclass WellKnownClasses::java_lang_reflect_Field; -jclass WellKnownClasses::java_lang_reflect_Method; jclass WellKnownClasses::java_lang_reflect_Parameter; jclass WellKnownClasses::java_lang_reflect_Parameter__array; jclass WellKnownClasses::java_lang_reflect_Proxy; @@ -145,7 +141,6 @@ jfieldID WellKnownClasses::java_lang_Throwable_detailMessage; jfieldID WellKnownClasses::java_lang_Throwable_stackTrace; jfieldID WellKnownClasses::java_lang_Throwable_stackState; jfieldID WellKnownClasses::java_lang_Throwable_suppressedExceptions; -jfieldID WellKnownClasses::java_lang_reflect_Executable_artMethod; jfieldID WellKnownClasses::java_lang_reflect_Proxy_h; jfieldID WellKnownClasses::java_nio_ByteBuffer_address; jfieldID WellKnownClasses::java_nio_ByteBuffer_hb; @@ -333,10 +328,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_invoke_MethodHandle = CacheClass(env, "java/lang/invoke/MethodHandle"); java_lang_invoke_VarHandle = CacheClass(env, "java/lang/invoke/VarHandle"); java_lang_NoClassDefFoundError = CacheClass(env, "java/lang/NoClassDefFoundError"); - java_lang_reflect_Constructor = CacheClass(env, "java/lang/reflect/Constructor"); - java_lang_reflect_Executable = CacheClass(env, "java/lang/reflect/Executable"); - java_lang_reflect_Field = CacheClass(env, "java/lang/reflect/Field"); - java_lang_reflect_Method = CacheClass(env, "java/lang/reflect/Method"); java_lang_reflect_Parameter = CacheClass(env, "java/lang/reflect/Parameter"); java_lang_reflect_Parameter__array = CacheClass(env, "[Ljava/lang/reflect/Parameter;"); java_lang_reflect_Proxy = CacheClass(env, "java/lang/reflect/Proxy"); @@ -412,7 +403,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Throwable_stackTrace = CacheField(env, java_lang_Throwable, false, "stackTrace", "[Ljava/lang/StackTraceElement;"); java_lang_Throwable_stackState = CacheField(env, java_lang_Throwable, false, "backtrace", "Ljava/lang/Object;"); java_lang_Throwable_suppressedExceptions = CacheField(env, java_lang_Throwable, false, "suppressedExceptions", "Ljava/util/List;"); - java_lang_reflect_Executable_artMethod = CacheField(env, java_lang_reflect_Executable, false, "artMethod", "J"); java_nio_ByteBuffer_address = CacheField(env, java_nio_ByteBuffer, false, "address", "J"); java_nio_ByteBuffer_hb = CacheField(env, java_nio_ByteBuffer, false, "hb", "[B"); java_nio_ByteBuffer_isReadOnly = CacheField(env, java_nio_ByteBuffer, false, "isReadOnly", "Z"); @@ -484,10 +474,6 @@ void WellKnownClasses::Clear() { java_lang_NoClassDefFoundError = nullptr; java_lang_Object = nullptr; java_lang_OutOfMemoryError = nullptr; - java_lang_reflect_Constructor = nullptr; - java_lang_reflect_Executable = nullptr; - java_lang_reflect_Field = nullptr; - java_lang_reflect_Method = nullptr; java_lang_reflect_Parameter = nullptr; java_lang_reflect_Parameter__array = nullptr; java_lang_reflect_Proxy = nullptr; @@ -551,7 +537,7 @@ void WellKnownClasses::Clear() { dalvik_system_DexFile_fileName = nullptr; dalvik_system_DexPathList_dexElements = nullptr; dalvik_system_DexPathList__Element_dexFile = nullptr; - java_lang_reflect_Executable_artMethod = nullptr; + dalvik_system_VMRuntime_nonSdkApiUsageConsumer = nullptr; java_lang_reflect_Proxy_h = nullptr; java_lang_Thread_daemon = nullptr; java_lang_Thread_group = nullptr; diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index 7b1a2943d2..25c07b27de 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -68,10 +68,6 @@ struct WellKnownClasses { static jclass java_lang_NoClassDefFoundError; static jclass java_lang_Object; static jclass java_lang_OutOfMemoryError; - static jclass java_lang_reflect_Constructor; - static jclass java_lang_reflect_Executable; - static jclass java_lang_reflect_Field; - static jclass java_lang_reflect_Method; static jclass java_lang_reflect_Parameter; static jclass java_lang_reflect_Parameter__array; static jclass java_lang_reflect_Proxy; @@ -138,7 +134,6 @@ struct WellKnownClasses { static jfieldID dalvik_system_DexPathList_dexElements; static jfieldID dalvik_system_DexPathList__Element_dexFile; static jfieldID dalvik_system_VMRuntime_nonSdkApiUsageConsumer; - static jfieldID java_lang_reflect_Executable_artMethod; static jfieldID java_lang_reflect_Proxy_h; static jfieldID java_lang_Thread_daemon; static jfieldID java_lang_Thread_group; -- GitLab From c83e7fe38da1e94fa98f05454d96c38f5de639a4 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 9 Apr 2018 13:30:58 +0100 Subject: [PATCH 222/749] Fix oat_file_assistant_test. Change a few EXPECT_EQ()s to ASSERT_EQ()s to avoid SIGSEGV in a following EXPECT_*() hiding the first error message. Test: Rely on TreeHugger. Bug: 77787955 Change-Id: Ibd9ddb3b869b5cd2d6a915eb69557c726a7794d6 --- runtime/oat_file_assistant_test.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 676071b247..99bc0b2c6e 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -1487,7 +1487,7 @@ TEST_F(OatFileAssistantTest, SystemOdex) { true, false); int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg); - EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable()); } @@ -1497,7 +1497,7 @@ TEST_F(OatFileAssistantTest, SystemOdex) { true, true); int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg); - EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; EXPECT_FALSE(oat_file_assistant.GetBestOatFile()->IsExecutable()); } @@ -1510,7 +1510,7 @@ TEST_F(OatFileAssistantTest, SystemOdex) { true, false); int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg); - EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable()); } @@ -1520,7 +1520,7 @@ TEST_F(OatFileAssistantTest, SystemOdex) { true, true); int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg); - EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable()); } } -- GitLab From d6c5b13461fd08c5ddcdb0ce186d6ce2bf7fd408 Mon Sep 17 00:00:00 2001 From: Tobias Thierer Date: Fri, 6 Apr 2018 18:40:43 +0100 Subject: [PATCH 223/749] Drop removed OsTest.test_setgroups() from libcore_failures.txt This test is dropped by another CL in this topic. Bug: 77599196 Test: Treehugger (cherry picked from commit bff3055520380d6ca446f6ea9a59caae759d12a6) Change-Id: Id032384a20e6d3cdf29009cd4e500b21730a2b27 --- tools/libcore_failures.txt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index 68aeedde85..60aac3cdb5 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -214,12 +214,5 @@ description: "java.io.IOException: Error writing ASN.1 encoding", result: EXEC_FAILED, names: ["libcore.javax.crypto.spec.AlgorithmParametersTestGCM#testEncoding"] -}, -{ - description: "Failure only on device. Blacklist it temporarily", - result: EXEC_FAILED, - modes: [device], - bug: 69023954, - names: ["libcore.libcore.io.OsTest#test_setgroups"] } ] -- GitLab From f5c537efc5c35a91283ce1df188a0c70947d3cb4 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 9 Apr 2018 18:33:55 +0100 Subject: [PATCH 224/749] Add extra logging for bug 77342775. Test: testrunner.py --host --64 --optimizing Test: Repeat with disabled filtering to Lorg/apache/http/, manually expect failure messages. Bug: 77342775 Change-Id: I98b1e4fb77dc5c6b57a91c7dda19e1a4b15c29be --- runtime/common_throws.cc | 4 +++ runtime/debug_print.cc | 46 ++++++++++++++++++++++++++++ runtime/debug_print.h | 3 ++ runtime/verifier/register_line-inl.h | 9 ++++++ 4 files changed, 62 insertions(+) diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index 945442d539..8fd95ed890 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -24,6 +24,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" #include "class_linker-inl.h" +#include "debug_print.h" #include "dex/dex_file-inl.h" #include "dex/dex_instruction-inl.h" #include "dex/invoke_type.h" @@ -152,6 +153,7 @@ void ThrowWrappedBootstrapMethodError(const char* fmt, ...) { // ClassCastException void ThrowClassCastException(ObjPtr dest_type, ObjPtr src_type) { + DumpB77342775DebugData(dest_type, src_type); ThrowException("Ljava/lang/ClassCastException;", nullptr, StringPrintf("%s cannot be cast to %s", mirror::Class::PrettyDescriptor(src_type).c_str(), @@ -279,6 +281,7 @@ void ThrowIncompatibleClassChangeErrorClassForInterfaceSuper(ArtMethod* method, << "' does not implement interface '" << mirror::Class::PrettyDescriptor(target_class) << "' in call to '" << ArtMethod::PrettyMethod(method) << "'"; + DumpB77342775DebugData(target_class, this_object->GetClass()); ThrowException("Ljava/lang/IncompatibleClassChangeError;", referrer != nullptr ? referrer->GetDeclaringClass() : nullptr, msg.str().c_str()); @@ -295,6 +298,7 @@ void ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(ArtMethod* inter << "' does not implement interface '" << mirror::Class::PrettyDescriptor(interface_method->GetDeclaringClass()) << "' in call to '" << ArtMethod::PrettyMethod(interface_method) << "'"; + DumpB77342775DebugData(interface_method->GetDeclaringClass(), this_object->GetClass()); ThrowException("Ljava/lang/IncompatibleClassChangeError;", referrer != nullptr ? referrer->GetDeclaringClass() : nullptr, msg.str().c_str()); diff --git a/runtime/debug_print.cc b/runtime/debug_print.cc index 44a183669d..c7530bec6e 100644 --- a/runtime/debug_print.cc +++ b/runtime/debug_print.cc @@ -110,4 +110,50 @@ std::string DescribeLoaders(ObjPtr loader, const char* clas return oss.str(); } +void DumpB77342775DebugData(ObjPtr target_class, ObjPtr src_class) { + std::string target_descriptor_storage; + const char* target_descriptor = target_class->GetDescriptor(&target_descriptor_storage); + const char kCheckedPrefix[] = "Lorg/apache/http/"; + // Avoid spam for other packages. (That spam would break some ART run-tests for example.) + if (strncmp(target_descriptor, kCheckedPrefix, sizeof(kCheckedPrefix) - 1) != 0) { + return; + } + auto matcher = [target_descriptor, target_class](ObjPtr klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (klass->DescriptorEquals(target_descriptor)) { + LOG(ERROR) << " descriptor match in " + << DescribeLoaders(klass->GetClassLoader(), target_descriptor) + << " match? " << std::boolalpha << (klass == target_class); + } + }; + + std::string source_descriptor_storage; + const char* source_descriptor = src_class->GetDescriptor(&source_descriptor_storage); + + if (target_class->IsInterface()) { + ObjPtr iftable = src_class->GetIfTable(); + CHECK(iftable != nullptr); + size_t ifcount = iftable->Count(); + LOG(ERROR) << "Maybe bug 77342775, looking for " << target_descriptor + << " with loader " << DescribeLoaders(src_class->GetClassLoader(), target_descriptor) + << " in interface table for " << source_descriptor << " ifcount=" << ifcount; + for (size_t i = 0; i != ifcount; ++i) { + ObjPtr iface = iftable->GetInterface(i); + CHECK(iface != nullptr); + LOG(ERROR) << " iface #" << i << ": " << iface->PrettyDescriptor(); + matcher(iface); + } + } else { + LOG(ERROR) << "Maybe bug 77342775, looking for " << target_descriptor + << " with loader " << DescribeLoaders(src_class->GetClassLoader(), target_descriptor) + << " in superclass chain for " << source_descriptor; + for (ObjPtr klass = src_class; + klass != nullptr; + klass = klass->GetSuperClass()) { + LOG(ERROR) << " - " << klass->PrettyDescriptor(); + matcher(klass); + } + } +} + } // namespace art diff --git a/runtime/debug_print.h b/runtime/debug_print.h index 479c36a02f..df00f064bd 100644 --- a/runtime/debug_print.h +++ b/runtime/debug_print.h @@ -29,6 +29,9 @@ std::string DescribeSpace(ObjPtr klass) std::string DescribeLoaders(ObjPtr loader, const char* class_descriptor) REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; +void DumpB77342775DebugData(ObjPtr target_class, ObjPtr src_class) + REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; + } // namespace art #endif // ART_RUNTIME_DEBUG_PRINT_H_ diff --git a/runtime/verifier/register_line-inl.h b/runtime/verifier/register_line-inl.h index 39d73f54d8..5160daf42d 100644 --- a/runtime/verifier/register_line-inl.h +++ b/runtime/verifier/register_line-inl.h @@ -20,6 +20,7 @@ #include "register_line.h" #include "base/logging.h" // For VLOG. +#include "debug_print.h" #include "method_verifier.h" #include "reg_type_cache-inl.h" @@ -147,6 +148,14 @@ inline bool RegisterLine::VerifyRegisterType(MethodVerifier* verifier, uint32_t } verifier->Fail(fail_type) << "register v" << vsrc << " has type " << src_type << " but expected " << check_type; + if (check_type.IsNonZeroReferenceTypes() && + !check_type.IsUnresolvedTypes() && + check_type.HasClass() && + src_type.IsNonZeroReferenceTypes() && + !src_type.IsUnresolvedTypes() && + src_type.HasClass()) { + DumpB77342775DebugData(check_type.GetClass(), src_type.GetClass()); + } return false; } if (check_type.IsLowHalf()) { -- GitLab From cdb4cb19f1135bf2003a6649c2c72ea13a406f88 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Mon, 9 Apr 2018 11:29:16 -0700 Subject: [PATCH 225/749] Remove historical workarounds. We removed libcorkscrew in L, and we removed its replacement in P. We removed the LLVM uses several years ago too. Bug: N/A Test: builds Change-Id: I414c9c3dc3c2c2e0016b9da0e9f8aa16068daa8c --- runtime/runtime.cc | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 4068158f3f..f550a151bb 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -575,19 +575,7 @@ void Runtime::Abort(const char* msg) { LOG(FATAL_WITHOUT_ABORT) << "Unexpectedly returned from abort hook!"; } -#if defined(__GLIBC__) - // TODO: we ought to be able to use pthread_kill(3) here (or abort(3), - // which POSIX defines in terms of raise(3), which POSIX defines in terms - // of pthread_kill(3)). On Linux, though, libcorkscrew can't unwind through - // libpthread, which means the stacks we dump would be useless. Calling - // tgkill(2) directly avoids that. - syscall(__NR_tgkill, getpid(), GetTid(), SIGABRT); - // TODO: LLVM installs it's own SIGABRT handler so exit to be safe... Can we disable that in LLVM? - // If not, we could use sigaction(3) before calling tgkill(2) and lose this call to exit(3). - exit(1); -#else abort(); -#endif // notreached } -- GitLab From 5f71c65f3ed4f728b1083cf85661d0a724504701 Mon Sep 17 00:00:00 2001 From: Sascha Roth Date: Tue, 10 Apr 2018 00:27:15 +0200 Subject: [PATCH 226/749] Fix --oat-filename in dex2oat usage message --oat-filename flag was moved to --oat-file, but an error message wasn't updated. This commit fixes the error message to the new --oat-file flag name. Change-Id: I0cd0dcc00883975942ae92ef1f246bad5e5e8305 --- dex2oat/dex2oat.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 6950b93e51..acc206e704 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -749,7 +749,7 @@ class Dex2Oat FINAL { } if ((output_vdex_fd_ == -1) != (oat_fd_ == -1)) { - Usage("VDEX and OAT output must be specified either with one --oat-filename " + Usage("VDEX and OAT output must be specified either with one --oat-file " "or with --oat-fd and --output-vdex-fd file descriptors"); } -- GitLab From 9c4a015cf01989597fd899011dba310b704dacaa Mon Sep 17 00:00:00 2001 From: David Sehr Date: Thu, 5 Apr 2018 12:23:54 -0700 Subject: [PATCH 227/749] Move more runtime code to libartbase, libdexfile Move some code that logically belongs to libartbase or libdexfile to those places respectively. Bug: 22322814 Test: make -j 50 checkbuild Change-Id: Idfded5d24b40d8587011681e6235b6672846da19 --- compiler/verifier_deps_test.cc | 2 +- dex2oat/linker/oat_writer.cc | 2 +- dexlayout/dexlayout.cc | 1 - libartbase/Android.bp | 1 + {runtime => libartbase/base}/indenter.h | 6 +++--- {runtime => libartbase/base}/indenter_test.cc | 0 libartbase/base/logging_test.cc | 6 +++--- libartbase/base/safe_copy_test.cc | 7 ++++--- libdexfile/Android.bp | 2 ++ {runtime => libdexfile/dex}/type_lookup_table.cc | 1 - {runtime => libdexfile/dex}/type_lookup_table.h | 6 +++--- {runtime => libdexfile/dex}/type_lookup_table_test.cc | 0 oatdump/oatdump.cc | 4 ++-- runtime/Android.bp | 3 --- runtime/oat_file.cc | 2 +- runtime/oat_file.h | 2 +- runtime/stack_map.cc | 2 +- runtime/verifier/method_verifier.cc | 2 +- runtime/verifier/verifier_deps.cc | 2 +- 19 files changed, 25 insertions(+), 26 deletions(-) rename {runtime => libartbase/base}/indenter.h (96%) rename {runtime => libartbase/base}/indenter_test.cc (100%) rename {runtime => libdexfile/dex}/type_lookup_table.cc (99%) rename {runtime => libdexfile/dex}/type_lookup_table.h (97%) rename {runtime => libdexfile/dex}/type_lookup_table_test.cc (100%) diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 76448d819c..553d131e2f 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -18,6 +18,7 @@ #include "verifier/verifier_deps.h" #include "art_method-inl.h" +#include "base/indenter.h" #include "class_linker.h" #include "common_compiler_test.h" #include "compiler_callbacks.h" @@ -28,7 +29,6 @@ #include "driver/compiler_driver-inl.h" #include "driver/compiler_options.h" #include "handle_scope-inl.h" -#include "indenter.h" #include "mirror/class_loader.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index bcc59098e5..690b565090 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -40,6 +40,7 @@ #include "dex/dex_file_loader.h" #include "dex/dex_file_types.h" #include "dex/standard_dex_file.h" +#include "dex/type_lookup_table.h" #include "dex/verification_results.h" #include "dex_container.h" #include "dexlayout.h" @@ -63,7 +64,6 @@ #include "oat_quick_method_header.h" #include "quicken_info.h" #include "scoped_thread_state_change-inl.h" -#include "type_lookup_table.h" #include "utils/dex_cache_arrays_layout-inl.h" #include "vdex_file.h" #include "verifier/verifier_deps.h" diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index ec0cbe6a60..da1573cef1 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -49,7 +49,6 @@ #include "dex_visualize.h" #include "dex_writer.h" #include "jit/profile_compilation_info.h" -#include "mem_map.h" namespace art { diff --git a/libartbase/Android.bp b/libartbase/Android.bp index 62157e196d..e68c2fba5b 100644 --- a/libartbase/Android.bp +++ b/libartbase/Android.bp @@ -104,6 +104,7 @@ art_cc_test { "base/hash_set_test.cc", "base/hex_dump_test.cc", "base/histogram_test.cc", + "base/indenter_test.cc", "base/leb128_test.cc", "base/logging_test.cc", "base/memory_region_test.cc", diff --git a/runtime/indenter.h b/libartbase/base/indenter.h similarity index 96% rename from runtime/indenter.h rename to libartbase/base/indenter.h index 6361dd2092..850b7c4f73 100644 --- a/runtime/indenter.h +++ b/libartbase/base/indenter.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_INDENTER_H_ -#define ART_RUNTIME_INDENTER_H_ +#ifndef ART_LIBARTBASE_BASE_INDENTER_H_ +#define ART_LIBARTBASE_BASE_INDENTER_H_ #include #include @@ -160,4 +160,4 @@ class ScopedIndentation { } // namespace art -#endif // ART_RUNTIME_INDENTER_H_ +#endif // ART_LIBARTBASE_BASE_INDENTER_H_ diff --git a/runtime/indenter_test.cc b/libartbase/base/indenter_test.cc similarity index 100% rename from runtime/indenter_test.cc rename to libartbase/base/indenter_test.cc diff --git a/libartbase/base/logging_test.cc b/libartbase/base/logging_test.cc index 404e080b03..1456eb30fa 100644 --- a/libartbase/base/logging_test.cc +++ b/libartbase/base/logging_test.cc @@ -21,7 +21,7 @@ #include "android-base/logging.h" #include "base/bit_utils.h" #include "base/macros.h" -#include "common_runtime_test.h" +#include "gtest/gtest.h" #include "runtime_debug.h" namespace art { @@ -31,9 +31,9 @@ static void SimpleAborter(const char* msg) { _exit(1); } -class LoggingTest : public CommonRuntimeTest { +class LoggingTest : public testing::Test { protected: - void PostRuntimeCreate() OVERRIDE { + LoggingTest() { // In our abort tests we really don't want the runtime to create a real dump. android::base::SetAborter(SimpleAborter); } diff --git a/libartbase/base/safe_copy_test.cc b/libartbase/base/safe_copy_test.cc index a9ec9528a1..f1d7c55e77 100644 --- a/libartbase/base/safe_copy_test.cc +++ b/libartbase/base/safe_copy_test.cc @@ -16,14 +16,15 @@ #include "safe_copy.h" -#include "common_runtime_test.h" - #include #include #include #include -#include "globals.h" +#include "android-base/logging.h" +#include "base/globals.h" +#include "gtest/gtest.h" + namespace art { diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp index 3fd61ee251..b2c041c81f 100644 --- a/libdexfile/Android.bp +++ b/libdexfile/Android.bp @@ -32,6 +32,7 @@ cc_defaults { "dex/modifiers.cc", "dex/primitive.cc", "dex/standard_dex_file.cc", + "dex/type_lookup_table.cc", "dex/utf.cc", ], @@ -123,6 +124,7 @@ art_cc_test { "dex/dex_instruction_test.cc", "dex/primitive_test.cc", "dex/string_reference_test.cc", + "dex/type_lookup_table_test.cc", "dex/utf_test.cc", ], shared_libs: [ diff --git a/runtime/type_lookup_table.cc b/libdexfile/dex/type_lookup_table.cc similarity index 99% rename from runtime/type_lookup_table.cc rename to libdexfile/dex/type_lookup_table.cc index 7e204fc03a..ca5ec2f798 100644 --- a/runtime/type_lookup_table.cc +++ b/libdexfile/dex/type_lookup_table.cc @@ -20,7 +20,6 @@ #include #include "base/bit_utils.h" -#include "base/utils.h" #include "dex/dex_file-inl.h" #include "dex/utf-inl.h" diff --git a/runtime/type_lookup_table.h b/libdexfile/dex/type_lookup_table.h similarity index 97% rename from runtime/type_lookup_table.h rename to libdexfile/dex/type_lookup_table.h index 3352d60ed1..0ba2b75dc6 100644 --- a/runtime/type_lookup_table.h +++ b/libdexfile/dex/type_lookup_table.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_TYPE_LOOKUP_TABLE_H_ -#define ART_RUNTIME_TYPE_LOOKUP_TABLE_H_ +#ifndef ART_LIBDEXFILE_DEX_TYPE_LOOKUP_TABLE_H_ +#define ART_LIBDEXFILE_DEX_TYPE_LOOKUP_TABLE_H_ #include "base/leb128.h" #include "dex/dex_file_types.h" @@ -172,4 +172,4 @@ class TypeLookupTable { } // namespace art -#endif // ART_RUNTIME_TYPE_LOOKUP_TABLE_H_ +#endif // ART_LIBDEXFILE_DEX_TYPE_LOOKUP_TABLE_H_ diff --git a/runtime/type_lookup_table_test.cc b/libdexfile/dex/type_lookup_table_test.cc similarity index 100% rename from runtime/type_lookup_table_test.cc rename to libdexfile/dex/type_lookup_table_test.cc diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 3bff123386..7530a9c5a8 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -34,6 +34,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" #include "base/bit_utils_iterator.h" +#include "base/indenter.h" #include "base/os.h" #include "base/safe_map.h" #include "base/stl_util.h" @@ -49,6 +50,7 @@ #include "dex/dex_file-inl.h" #include "dex/dex_instruction-inl.h" #include "dex/string_reference.h" +#include "dex/type_lookup_table.h" #include "disassembler.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/space/image_space.h" @@ -56,7 +58,6 @@ #include "gc/space/space-inl.h" #include "image-inl.h" #include "imtable-inl.h" -#include "indenter.h" #include "subtype_check.h" #include "index_bss_mapping.h" #include "interpreter/unstarted_runtime.h" @@ -75,7 +76,6 @@ #include "stack.h" #include "stack_map.h" #include "thread_list.h" -#include "type_lookup_table.h" #include "vdex_file.h" #include "verifier/method_verifier.h" #include "verifier/verifier_deps.h" diff --git a/runtime/Android.bp b/runtime/Android.bp index 00d4a6080a..d643509af7 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -199,7 +199,6 @@ cc_defaults { "ti/agent.cc", "trace.cc", "transaction.cc", - "type_lookup_table.cc", "vdex_file.cc", "verifier/instruction_flags.cc", "verifier/method_verifier.cc", @@ -571,7 +570,6 @@ art_cc_test { "handle_scope_test.cc", "hidden_api_test.cc", "imtable_test.cc", - "indenter_test.cc", "indirect_reference_table_test.cc", "instrumentation_test.cc", "intern_table_test.cc", @@ -598,7 +596,6 @@ art_cc_test { "subtype_check_test.cc", "thread_pool_test.cc", "transaction_test.cc", - "type_lookup_table_test.cc", "vdex_file_test.cc", "verifier/method_verifier_test.cc", "verifier/reg_type_test.cc", diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index cfbcda36b9..bda6604724 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -49,6 +49,7 @@ #include "dex/dex_file_loader.h" #include "dex/dex_file_types.h" #include "dex/standard_dex_file.h" +#include "dex/type_lookup_table.h" #include "dex/utf-inl.h" #include "elf_file.h" #include "elf_utils.h" @@ -61,7 +62,6 @@ #include "oat_file-inl.h" #include "oat_file_manager.h" #include "runtime.h" -#include "type_lookup_table.h" #include "vdex_file.h" namespace art { diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 24868dd55d..6494b4c58e 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -32,11 +32,11 @@ #include "compiler_filter.h" #include "dex/dex_file.h" #include "dex/dex_file_layout.h" +#include "dex/type_lookup_table.h" #include "dex/utf.h" #include "index_bss_mapping.h" #include "mirror/object.h" #include "oat.h" -#include "type_lookup_table.h" namespace art { diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index 250ff2af1a..9c7b6875cf 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -19,7 +19,7 @@ #include #include "art_method.h" -#include "indenter.h" +#include "base/indenter.h" #include "scoped_thread_state_change-inl.h" namespace art { diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index cee717610d..72292c33f8 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -25,6 +25,7 @@ #include "base/aborting.h" #include "base/enums.h" #include "base/leb128.h" +#include "base/indenter.h" #include "base/logging.h" // For VLOG. #include "base/mutex-inl.h" #include "base/stl_util.h" @@ -41,7 +42,6 @@ #include "experimental_flags.h" #include "gc/accounting/card_table-inl.h" #include "handle_scope-inl.h" -#include "indenter.h" #include "intern_table.h" #include "mirror/class-inl.h" #include "mirror/class.h" diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc index 4772e538aa..fe839f7312 100644 --- a/runtime/verifier/verifier_deps.cc +++ b/runtime/verifier/verifier_deps.cc @@ -20,11 +20,11 @@ #include "art_field-inl.h" #include "art_method-inl.h" +#include "base/indenter.h" #include "base/leb128.h" #include "base/stl_util.h" #include "compiler_callbacks.h" #include "dex/dex_file-inl.h" -#include "indenter.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "obj_ptr-inl.h" -- GitLab From 4a265c21860177839d503d0629dae3d343f352d9 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 9 Apr 2018 14:25:14 -0700 Subject: [PATCH 228/749] Don't pass empty profile to OatWriter Doing so causes dexlayout to run and unnecessarily increases dex2oat time. This change passes null if the profile is empty to fix this. Bug: 77719042 Test: test-art-host (cherry picked from commit cc2cd98c21f77c8ae368988d380827811be2dc9d) Merged-In: Id663d2cbf32dae57f0b562a1f5263293869727d7 Change-Id: I4599f4784c7d2ac3f5e759249226ca9d155efc31 --- dex2oat/dex2oat.cc | 5 ++++- runtime/jit/profile_compilation_info.h | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index e2177697bf..d2a8154f22 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -2524,7 +2524,10 @@ class Dex2Oat FINAL { compiler_options_.get(), oat_file.get())); elf_writers_.back()->Start(); - const bool do_oat_writer_layout = DoDexLayoutOptimizations() || DoOatLayoutOptimizations(); + bool do_oat_writer_layout = DoDexLayoutOptimizations() || DoOatLayoutOptimizations(); + if (profile_compilation_info_ != nullptr && profile_compilation_info_->IsEmpty()) { + do_oat_writer_layout = false; + } oat_writers_.emplace_back(new linker::OatWriter( IsBootImage(), timings_, diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h index f8334cea4f..79cb46170c 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -440,6 +440,9 @@ class ProfileCompilationInfo { // the method returns false. Otherwise it returns true. bool UpdateProfileKeys(const std::vector>& dex_files); + // Checks if the profile is empty. + bool IsEmpty() const; + private: enum ProfileLoadStatus { kProfileLoadWouldOverwiteData, @@ -587,9 +590,6 @@ class ProfileCompilationInfo { // the key or the checksum mismatches. const DexFileData* FindDexData(const DexFile* dex_file) const; - // Checks if the profile is empty. - bool IsEmpty() const; - // Inflate the input buffer (in_buffer) of size in_size. It returns a buffer of // compressed data for the input buffer of "compressed_data_size" size. std::unique_ptr DeflateBuffer(const uint8_t* in_buffer, -- GitLab From 28be106773d2191019941db795560bdd05ac0081 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Tue, 10 Apr 2018 11:24:30 +0100 Subject: [PATCH 229/749] Load-balance gcstress and gcverify ART test configurations. Configuration art-gcstress-gcverify is sometimes hitting the build timeout. Move 'interpreter', 'optimizing', and 'jit' coverage to other gcstress configurations to balance the load among builders. Test: art/test/testrunner/run_build_test_target.py -j30 art-gcstress-gcverify Test: art/test/testrunner/run_build_test_target.py -j30 art-interpreter-gcstress Test: art/test/testrunner/run_build_test_target.py -j30 art-optimizing-gcstress Test: art/test/testrunner/run_build_test_target.py -j30 art-jit-gcstress Bug: 74196452 Bug: 62611253 Change-Id: I7793256b284a56ce442d2d340405ca810a207dd8 --- test/testrunner/target_config.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index 95e488dc4f..e0757abbe0 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -55,20 +55,31 @@ target_config = { '--optimizing'] }, 'art-gcstress-gcverify': { - 'run-test': ['--gcstress', + # Do not exercise '--interpreter', '--optimizing', nor '--jit' in this + # configuration, as they are covered by the 'art-interpreter-gcstress', + # 'art-optimizing-gcstress' and 'art-jit-gcstress' configurations below. + 'run-test': ['--interp-ac', + '--speed-profile', + '--gcstress', '--gcverify'] }, + # Rename this configuration as 'art-interpreter-gcstress-gcverify' (b/62611253). 'art-interpreter-gcstress' : { 'run-test' : ['--interpreter', - '--gcstress'] + '--gcstress', + '--gcverify'] }, + # Rename this configuration as 'art-optimizing-gcstress-gcverify' (b/62611253). 'art-optimizing-gcstress' : { - 'run-test' : ['--gcstress', - '--optimizing'] + 'run-test' : ['--optimizing', + '--gcstress', + '--gcverify'] }, + # Rename this configuration as 'art-jit-gcstress-gcverify' (b/62611253). 'art-jit-gcstress' : { 'run-test' : ['--jit', - '--gcstress'] + '--gcstress', + '--gcverify'] }, 'art-jit-on-first-use-gcstress' : { 'run-test' : ['--jit', -- GitLab From 0f59574625155a70f560f7825661a355dee45a54 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Tue, 10 Apr 2018 14:49:28 +0100 Subject: [PATCH 230/749] ART: Fix MethodHandle invoke-super Adds test for MethodHandles.unreflectSpecial() and fixes the associated method refinement in the method handle dispatch. Bug: 77729852 Test: art/test/run-test --host --64 956 (cherry picked from commit d7959c2abe72629581f7465500b95a3d4e57e7ca) Change-Id: Idf022997a3d34b75428cac0bc539f6ea4bb1bc94 Merged-In: I6c364a5a6edaa9e9d04ae96683d5bb3e6adbcb6e --- runtime/method_handles.cc | 8 +-- test/956-methodhandles/expected.txt | 12 ++++- test/956-methodhandles/src/Main.java | 56 +++++++++++++++++++- test/956-methodhandles/src/other/Chatty.java | 23 ++++++++ 4 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 test/956-methodhandles/src/other/Chatty.java diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 82370c4631..64ab78997f 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -674,13 +674,15 @@ ArtMethod* RefineTargetMethod(Thread* self, return WellKnownClasses::StringInitToStringFactory(target_method); } } else if (handle_kind == mirror::MethodHandle::Kind::kInvokeSuper) { - ObjPtr declaring_class = target_method->GetDeclaringClass(); - // Note that we're not dynamically dispatching on the type of the receiver // here. We use the static type of the "receiver" object that we've // recorded in the method handle's type, which will be the same as the // special caller that was specified at the point of lookup. ObjPtr referrer_class = handle_type->GetPTypes()->Get(0); + ObjPtr declaring_class = target_method->GetDeclaringClass(); + if (referrer_class == declaring_class) { + return target_method; + } if (!declaring_class->IsInterface()) { ObjPtr super_class = referrer_class->GetSuperClass(); uint16_t vtable_index = target_method->GetMethodIndex(); @@ -690,8 +692,6 @@ ArtMethod* RefineTargetMethod(Thread* self, // will always be declared by super_class (or one of its super classes). DCHECK_LT(vtable_index, super_class->GetVTableLength()); return super_class->GetVTableEntry(vtable_index, kRuntimePointerSize); - } else { - return referrer_class->FindVirtualMethodForInterfaceSuper(target_method, kRuntimePointerSize); } } return target_method; diff --git a/test/956-methodhandles/expected.txt b/test/956-methodhandles/expected.txt index 9b0932708e..6954c22ccb 100644 --- a/test/956-methodhandles/expected.txt +++ b/test/956-methodhandles/expected.txt @@ -3,7 +3,17 @@ foo_A foo_A foo_B privateRyan_D -Received exception: Expected (java.lang.String, java.lang.String)java.lang.String but was (java.lang.String, java.lang.Object)void +Received WrongMethodTypeException exception +G.sayHi() +G.sayHi() +G.sayHi() +F.sayHi() +F.sayHi() +H.chatter() +H.chatter() +H.chatter() +Chatty.chatter() +Chatty.chatter() String constructors done. testReferenceReturnValueConversions done. testPrimitiveReturnValueConversions done. diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java index 1ddef03d62..dee818a4ee 100644 --- a/test/956-methodhandles/src/Main.java +++ b/test/956-methodhandles/src/Main.java @@ -30,6 +30,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import other.Chatty; + public class Main { public static class A { @@ -66,6 +68,30 @@ public class Main { public static final Lookup lookup = MethodHandles.lookup(); } + private interface F { + public default void sayHi() { + System.out.println("F.sayHi()"); + } + } + + public static class G implements F { + public void sayHi() { + System.out.println("G.sayHi()"); + } + public MethodHandles.Lookup getLookup() { + return MethodHandles.lookup(); + } + } + + public static class H implements Chatty { + public void chatter() { + System.out.println("H.chatter()"); + } + public MethodHandles.Lookup getLookup() { + return MethodHandles.lookup(); + } + } + public static void main(String[] args) throws Throwable { testfindSpecial_invokeSuperBehaviour(); testfindSpecial_invokeDirectBehaviour(); @@ -173,7 +199,7 @@ public class Main { handle.invokeExact("a", new Object()); System.out.println("invokeExact(\"a\", new Object()) unexpectedly succeeded."); } catch (WrongMethodTypeException ex) { - System.out.println("Received exception: " + ex.getMessage()); + System.out.println("Received WrongMethodTypeException exception"); } } @@ -528,6 +554,34 @@ public class Main { privateStaticField.set(null, "updatedStaticValue"); mh.invokeExact("updatedStaticValue2"); assertEquals("updatedStaticValue2", (String) privateStaticField.get(null)); + + // unreflectSpecial testing - F is an interface that G implements + + G g = new G(); + g.sayHi(); // prints "G.sayHi()" + + MethodHandles.Lookup lookupInG = g.getLookup(); + Method methodInG = G.class.getDeclaredMethod("sayHi"); + lookupInG.unreflectSpecial(methodInG, G.class).invoke(g); // prints "G.sayHi()" + + Method methodInF = F.class.getDeclaredMethod("sayHi"); + lookupInG.unreflect(methodInF).invoke(g); // prints "G.sayHi()" + lookupInG.in(G.class).unreflectSpecial(methodInF, G.class).invoke(g); // prints "F.sayHi()" + lookupInG.unreflectSpecial(methodInF, G.class).bindTo(g).invokeWithArguments(); + + // unreflectSpecial testing - other.Chatty is an interface that H implements + + H h = new H(); + h.chatter(); + + MethodHandles.Lookup lookupInH = h.getLookup(); + Method methodInH = H.class.getDeclaredMethod("chatter"); + lookupInH.unreflectSpecial(methodInH, H.class).invoke(h); + + Method methodInChatty = Chatty.class.getDeclaredMethod("chatter"); + lookupInH.unreflect(methodInChatty).invoke(h); + lookupInH.in(H.class).unreflectSpecial(methodInChatty, H.class).invoke(h); + lookupInH.unreflectSpecial(methodInChatty, H.class).bindTo(h).invokeWithArguments(); } // This method only exists to fool Jack's handling of types. See b/32536744. diff --git a/test/956-methodhandles/src/other/Chatty.java b/test/956-methodhandles/src/other/Chatty.java new file mode 100644 index 0000000000..98aef14b90 --- /dev/null +++ b/test/956-methodhandles/src/other/Chatty.java @@ -0,0 +1,23 @@ +/* + * 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 other; + +public interface Chatty { + public default void chatter() { + System.out.println("Chatty.chatter()"); + } +} -- GitLab From 9e7859cb2449d6150b97c7d8ff9646a5405737f8 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Thu, 5 Apr 2018 13:49:43 -0700 Subject: [PATCH 231/749] Prevent deadlock calling transformation functions in ClassLoad callback. If an agent called RetransformClasses or RedefineClasses from the ClassLoad event callback and the class that caused the event is included the thread would deadlock. This happened because the verification code would try to wait for the class to be linked but since the current thread is responsible for doing that this will never happen. To prevent this from happening we make those functions simply return JVMTI_ERROR_INTERNAL if they are called on the same thread the class is being loaded on. In order to test this we needed to modify the test helper code to keep track of the current jvmtiEventCallbacks state. Bug: 77652488 Test: ./test.py --host -j50 Change-Id: I0a196b999a08ec3bf9cdf98357e223f89fdcd666 --- openjdkjvmti/ti_redefine.cc | 34 +++- openjdkjvmti/ti_redefine.h | 4 + openjdkjvmti/transform.cc | 5 +- test/1950-unprepared-transform/check | 22 +++ test/1950-unprepared-transform/expected.txt | 7 + test/1950-unprepared-transform/info.txt | 1 + .../jvm-expected.patch | 6 + test/1950-unprepared-transform/run | 17 ++ .../src-ex/Transform.java | 22 +++ test/1950-unprepared-transform/src/Main.java | 153 ++++++++++++++++++ .../src/art/Redefinition.java | 91 +++++++++++ .../unprepared_transform.cc | 77 +++++++++ test/Android.bp | 1 + test/ti-agent/breakpoint_helper.cc | 9 +- test/ti-agent/exceptions_helper.cc | 11 +- test/ti-agent/frame_pop_helper.cc | 9 +- test/ti-agent/monitors_helper.cc | 15 +- test/ti-agent/redefinition_helper.cc | 12 +- test/ti-agent/test_env.cc | 1 + test/ti-agent/test_env.h | 5 + test/ti-agent/trace_helper.cc | 23 +-- 21 files changed, 477 insertions(+), 48 deletions(-) create mode 100755 test/1950-unprepared-transform/check create mode 100644 test/1950-unprepared-transform/expected.txt create mode 100644 test/1950-unprepared-transform/info.txt create mode 100644 test/1950-unprepared-transform/jvm-expected.patch create mode 100755 test/1950-unprepared-transform/run create mode 100644 test/1950-unprepared-transform/src-ex/Transform.java create mode 100644 test/1950-unprepared-transform/src/Main.java create mode 100644 test/1950-unprepared-transform/src/art/Redefinition.java create mode 100644 test/1950-unprepared-transform/unprepared_transform.cc diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index 5d430d2073..a23baa5095 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -234,12 +234,39 @@ jvmtiError Redefiner::IsModifiableClass(jvmtiEnv* env ATTRIBUTE_UNUSED, art::Handle h_klass(hs.NewHandle(obj->AsClass())); std::string err_unused; *is_redefinable = - Redefiner::GetClassRedefinitionError(h_klass, &err_unused) == OK ? JNI_TRUE : JNI_FALSE; + Redefiner::GetClassRedefinitionError(h_klass, &err_unused) != ERR(UNMODIFIABLE_CLASS) + ? JNI_TRUE : JNI_FALSE; return OK; } +jvmtiError Redefiner::GetClassRedefinitionError(jclass klass, /*out*/std::string* error_msg) { + art::Thread* self = art::Thread::Current(); + art::ScopedObjectAccess soa(self); + art::StackHandleScope<1> hs(self); + art::ObjPtr obj(self->DecodeJObject(klass)); + if (obj.IsNull()) { + return ERR(INVALID_CLASS); + } + art::Handle h_klass(hs.NewHandle(obj->AsClass())); + return Redefiner::GetClassRedefinitionError(h_klass, error_msg); +} + jvmtiError Redefiner::GetClassRedefinitionError(art::Handle klass, /*out*/std::string* error_msg) { + if (!klass->IsResolved()) { + // It's only a problem to try to retransform/redefine a unprepared class if it's happening on + // the same thread as the class-linking process. If it's on another thread we will be able to + // wait for the preparation to finish and continue from there. + if (klass->GetLockOwnerThreadId() == art::Thread::Current()->GetThreadId()) { + *error_msg = "Modification of class " + klass->PrettyClass() + + " from within the classes ClassLoad callback is not supported to prevent deadlocks." + + " Please use ClassFileLoadHook directly instead."; + return ERR(INTERNAL); + } else { + LOG(WARNING) << klass->PrettyClass() << " is not yet resolved. Attempting to transform " + << "it could cause arbitrary length waits as the class is being resolved."; + } + } if (klass->IsPrimitive()) { *error_msg = "Modification of primitive classes is not supported"; return ERR(UNMODIFIABLE_CLASS); @@ -332,12 +359,9 @@ jvmtiError Redefiner::RedefineClasses(ArtJvmTiEnv* env, std::vector def_vector; def_vector.reserve(class_count); for (jint i = 0; i < class_count; i++) { - jboolean is_modifiable = JNI_FALSE; - jvmtiError res = env->IsModifiableClass(definitions[i].klass, &is_modifiable); + jvmtiError res = Redefiner::GetClassRedefinitionError(definitions[i].klass, error_msg); if (res != OK) { return res; - } else if (!is_modifiable) { - return ERR(UNMODIFIABLE_CLASS); } // We make a copy of the class_bytes to pass into the retransformation. // This makes cleanup easier (since we unambiguously own the bytes) and also is useful since we diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h index 778f87e68e..fa7d28648d 100644 --- a/openjdkjvmti/ti_redefine.h +++ b/openjdkjvmti/ti_redefine.h @@ -98,6 +98,10 @@ class Redefiner { art::ArrayRef data, std::string* error_msg); + // Helper for checking if redefinition/retransformation is allowed. + static jvmtiError GetClassRedefinitionError(jclass klass, /*out*/std::string* error_msg) + REQUIRES(!art::Locks::mutator_lock_); + private: class ClassRedefinition { public: diff --git a/openjdkjvmti/transform.cc b/openjdkjvmti/transform.cc index 43b8fe94f4..62094a327d 100644 --- a/openjdkjvmti/transform.cc +++ b/openjdkjvmti/transform.cc @@ -313,12 +313,9 @@ jvmtiError Transformer::RetransformClasses(ArtJvmTiEnv* env, std::vector definitions; jvmtiError res = OK; for (jint i = 0; i < class_count; i++) { - jboolean is_modifiable = JNI_FALSE; - res = env->IsModifiableClass(classes[i], &is_modifiable); + res = Redefiner::GetClassRedefinitionError(classes[i], error_msg); if (res != OK) { return res; - } else if (!is_modifiable) { - return ERR(UNMODIFIABLE_CLASS); } ArtClassDefinition def; res = def.Init(self, classes[i]); diff --git a/test/1950-unprepared-transform/check b/test/1950-unprepared-transform/check new file mode 100755 index 0000000000..8a84388a8f --- /dev/null +++ b/test/1950-unprepared-transform/check @@ -0,0 +1,22 @@ +#!/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. + +# The RI sends an extra event that art doesn't. Add it to the expected output. +if [[ "$TEST_RUNTIME" == "jvm" ]]; then + patch -p0 expected.txt < jvm-expected.patch >/dev/null +fi + +./default-check "$@" diff --git a/test/1950-unprepared-transform/expected.txt b/test/1950-unprepared-transform/expected.txt new file mode 100644 index 0000000000..3be28a5696 --- /dev/null +++ b/test/1950-unprepared-transform/expected.txt @@ -0,0 +1,7 @@ +Redefine in ClassLoad on current thread. +Trying to redefine: class Transform. Caught error class java.lang.Exception: Failed to retransform class due to JVMTI_ERROR_INTERNAL +Object out is: NON Transformed Object +Redefine during ClassLoad on another thread. +retransformClasses on an unprepared class succeeded +Object out is: Transformed object! +Redefinition thread finished. diff --git a/test/1950-unprepared-transform/info.txt b/test/1950-unprepared-transform/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/1950-unprepared-transform/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/1950-unprepared-transform/jvm-expected.patch b/test/1950-unprepared-transform/jvm-expected.patch new file mode 100644 index 0000000000..fd0131e5e4 --- /dev/null +++ b/test/1950-unprepared-transform/jvm-expected.patch @@ -0,0 +1,6 @@ +2,3c2,3 +< Trying to redefine: class Transform. Caught error class java.lang.Exception: Failed to retransform class due to JVMTI_ERROR_INTERNAL +< Object out is: NON Transformed Object +--- +> retransformClasses on an unprepared class succeeded +> Object out is: Transformed object! diff --git a/test/1950-unprepared-transform/run b/test/1950-unprepared-transform/run new file mode 100755 index 0000000000..adb1a1c507 --- /dev/null +++ b/test/1950-unprepared-transform/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +./default-run "$@" --jvmti --no-app-image diff --git a/test/1950-unprepared-transform/src-ex/Transform.java b/test/1950-unprepared-transform/src-ex/Transform.java new file mode 100644 index 0000000000..29aa8c68dd --- /dev/null +++ b/test/1950-unprepared-transform/src-ex/Transform.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This class is caught during loading. +public class Transform { + public String toString() { + return "NON Transformed Object"; + } +} diff --git a/test/1950-unprepared-transform/src/Main.java b/test/1950-unprepared-transform/src/Main.java new file mode 100644 index 0000000000..adbcf38ce3 --- /dev/null +++ b/test/1950-unprepared-transform/src/Main.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import art.Redefinition; + +import java.lang.reflect.*; +import java.util.Base64; +import java.util.concurrent.CountDownLatch; +import java.util.function.Consumer; + +class Main { + public static String TEST_NAME = "1950-unprepared-transform"; + + // Base 64 encoding of the following class: + // + // public class Transform { + // public String toString() { + // return "Transformed object!"; + // } + // } + public static final byte[] CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADQAEQoABAANCAAOBwAPBwAQAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1i" + + "ZXJUYWJsZQEACHRvU3RyaW5nAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAApTb3VyY2VGaWxlAQAO" + + "VHJhbnNmb3JtLmphdmEMAAUABgEAE1RyYW5zZm9ybWVkIG9iamVjdCEBAAlUcmFuc2Zvcm0BABBq" + + "YXZhL2xhbmcvT2JqZWN0ACEAAwAEAAAAAAACAAEABQAGAAEABwAAAB0AAQABAAAABSq3AAGxAAAA" + + "AQAIAAAABgABAAAAEgABAAkACgABAAcAAAAbAAEAAQAAAAMSArAAAAABAAgAAAAGAAEAAAAUAAEA" + + "CwAAAAIADA=="); + + public static final byte[] DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzOACaXU/P8oJOECPrdN1Cu9/ob2cUb2vOKxqYAgAAcAAAAHhWNBIAAAAAAAAAABACAAAK" + + "AAAAcAAAAAQAAACYAAAAAgAAAKgAAAAAAAAAAAAAAAMAAADAAAAAAQAAANgAAACgAQAA+AAAADAB" + + "AAA4AQAAOwEAAEgBAABcAQAAcAEAAIABAACVAQAAmAEAAKIBAAACAAAAAwAAAAQAAAAHAAAAAQAA" + + "AAIAAAAAAAAABwAAAAMAAAAAAAAAAAABAAAAAAAAAAAACAAAAAEAAQAAAAAAAAAAAAEAAAABAAAA" + + "AAAAAAUAAAAAAAAAAAIAAAAAAAACAAEAAAAAACwBAAADAAAAGgAGABEAAAABAAEAAQAAACgBAAAE" + + "AAAAcBACAAAADgASAA4AFAAOAAY8aW5pdD4AAUwAC0xUcmFuc2Zvcm07ABJMamF2YS9sYW5nL09i" + + "amVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAOVHJhbnNmb3JtLmphdmEAE1RyYW5zZm9ybWVkIG9i" + + "amVjdCEAAVYACHRvU3RyaW5nAFx+fkQ4eyJtaW4tYXBpIjoyNywic2hhLTEiOiI3YTdjNDlhY2Nj" + + "NTkzNTIyNzY4MTY3MThhNGM3YWU1MmY5NjgzZjk5IiwidmVyc2lvbiI6InYxLjIuNC1kZXYifQAA" + + "AAEBAIGABJACAQH4AQAACwAAAAAAAAABAAAAAAAAAAEAAAAKAAAAcAAAAAIAAAAEAAAAmAAAAAMA" + + "AAACAAAAqAAAAAUAAAADAAAAwAAAAAYAAAABAAAA2AAAAAEgAAACAAAA+AAAAAMgAAACAAAAKAEA" + + "AAIgAAAKAAAAMAEAAAAgAAABAAAAAAIAAAAQAAABAAAAEAIAAA=="); + + public static native void setupClassLoadHook(Thread target); + public static native void clearClassLoadHook(Thread target); + private static Consumer> doRedefine = null; + + public static void doClassLoad(Class c) { + try { + if (c.getName().equals("Transform")) { + Redefinition.addCommonTransformationResult("Transform", CLASS_BYTES, DEX_BYTES); + doRedefine.accept(c); + System.out.println("retransformClasses on an unprepared class succeeded"); + } + } catch (Throwable e) { + System.out.println("Trying to redefine: " + c + ". " + + "Caught error " + e.getClass() + ": " + e.getMessage()); + } + } + + public static ClassLoader getClassLoaderFor(String location) throws Exception { + try { + Class class_loader_class = Class.forName("dalvik.system.PathClassLoader"); + Constructor ctor = class_loader_class.getConstructor(String.class, ClassLoader.class); + /* on Dalvik, this is a DexFile; otherwise, it's null */ + return (ClassLoader)ctor.newInstance(location + "/" + TEST_NAME + "-ex.jar", + Main.class.getClassLoader()); + } catch (ClassNotFoundException e) { + // Running on RI. Use URLClassLoader. + return new java.net.URLClassLoader( + new java.net.URL[] { new java.net.URL("file://" + location + "/classes-ex/") }); + } + } + + public static void testCurrentThread() throws Throwable { + System.out.println("Redefine in ClassLoad on current thread."); + doRedefine = (c) -> { Redefinition.doCommonClassRetransformation(c); }; + ClassLoader new_loader = getClassLoaderFor(System.getenv("DEX_LOCATION")); + Class klass = (Class)new_loader.loadClass("Transform"); + if (klass == null) { + throw new AssertionError("loadClass failed"); + } + Object o = klass.newInstance(); + System.out.println("Object out is: " + o); + } + + public static void testRemoteThread() throws Throwable { + System.out.println("Redefine during ClassLoad on another thread."); + final Class[] loaded = new Class[] { null, }; + final CountDownLatch gotClass = new CountDownLatch(1); + final CountDownLatch wokeUp = new CountDownLatch(1); + Thread redef_thread = new Thread(() -> { + try { + gotClass.await(); + wokeUp.countDown(); + // This will wait until the otehr thread returns so we need to wake up the other thread + // first. + Redefinition.doCommonClassRetransformation(loaded[0]); + } catch (Exception e) { + throw new Error("Failed to do redef!", e); + } + }); + redef_thread.start(); + doRedefine = (c) -> { + try { + loaded[0] = c; + gotClass.countDown(); + wokeUp.await(); + // Let the other thread do some stuff. + Thread.sleep(5000); + } catch (Exception e) { + throw new Error("Failed to do redef!", e); + } + }; + ClassLoader new_loader = getClassLoaderFor(System.getenv("DEX_LOCATION")); + Class klass = (Class)new_loader.loadClass("Transform"); + if (klass == null) { + throw new AssertionError("loadClass failed"); + } + Object o = klass.newInstance(); + System.out.println("Object out is: " + o); + redef_thread.join(); + System.out.println("Redefinition thread finished."); + } + + public static void main(String[] args) { + // make sure we can do the transform. + Redefinition.setTestConfiguration(Redefinition.Config.COMMON_RETRANSFORM); + Redefinition.setPopRetransformations(false); + Redefinition.enableCommonRetransformation(true); + setupClassLoadHook(Thread.currentThread()); + try { + testCurrentThread(); + testRemoteThread(); + } catch (Throwable e) { + System.out.println(e.toString()); + e.printStackTrace(System.out); + } + clearClassLoadHook(Thread.currentThread()); + } +} diff --git a/test/1950-unprepared-transform/src/art/Redefinition.java b/test/1950-unprepared-transform/src/art/Redefinition.java new file mode 100644 index 0000000000..56d2938a01 --- /dev/null +++ b/test/1950-unprepared-transform/src/art/Redefinition.java @@ -0,0 +1,91 @@ +/* + * 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. + */ + +package art; + +import java.util.ArrayList; +// Common Redefinition functions. Placed here for use by CTS +public class Redefinition { + public static final class CommonClassDefinition { + public final Class target; + public final byte[] class_file_bytes; + public final byte[] dex_file_bytes; + + public CommonClassDefinition(Class target, byte[] class_file_bytes, byte[] dex_file_bytes) { + this.target = target; + this.class_file_bytes = class_file_bytes; + this.dex_file_bytes = dex_file_bytes; + } + } + + // A set of possible test configurations. Test should set this if they need to. + // This must be kept in sync with the defines in ti-agent/common_helper.cc + public static enum Config { + COMMON_REDEFINE(0), + COMMON_RETRANSFORM(1), + COMMON_TRANSFORM(2); + + private final int val; + private Config(int val) { + this.val = val; + } + } + + public static void setTestConfiguration(Config type) { + nativeSetTestConfiguration(type.val); + } + + private static native void nativeSetTestConfiguration(int type); + + // Transforms the class + public static native void doCommonClassRedefinition(Class target, + byte[] classfile, + byte[] dexfile); + + public static void doMultiClassRedefinition(CommonClassDefinition... defs) { + ArrayList> classes = new ArrayList<>(); + ArrayList class_files = new ArrayList<>(); + ArrayList dex_files = new ArrayList<>(); + + for (CommonClassDefinition d : defs) { + classes.add(d.target); + class_files.add(d.class_file_bytes); + dex_files.add(d.dex_file_bytes); + } + doCommonMultiClassRedefinition(classes.toArray(new Class[0]), + class_files.toArray(new byte[0][]), + dex_files.toArray(new byte[0][])); + } + + public static void addMultiTransformationResults(CommonClassDefinition... defs) { + for (CommonClassDefinition d : defs) { + addCommonTransformationResult(d.target.getCanonicalName(), + d.class_file_bytes, + d.dex_file_bytes); + } + } + + public static native void doCommonMultiClassRedefinition(Class[] targets, + byte[][] classfiles, + byte[][] dexfiles); + public static native void doCommonClassRetransformation(Class... target); + public static native void setPopRetransformations(boolean pop); + public static native void popTransformationFor(String name); + public static native void enableCommonRetransformation(boolean enable); + public static native void addCommonTransformationResult(String target_name, + byte[] class_bytes, + byte[] dex_bytes); +} diff --git a/test/1950-unprepared-transform/unprepared_transform.cc b/test/1950-unprepared-transform/unprepared_transform.cc new file mode 100644 index 0000000000..620ede887f --- /dev/null +++ b/test/1950-unprepared-transform/unprepared_transform.cc @@ -0,0 +1,77 @@ +/* + * 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. + */ + + +#include +#include +#include +#include + +#include "android-base/logging.h" +#include "android-base/stringprintf.h" +#include "jni.h" +#include "jvmti.h" +#include "scoped_local_ref.h" +#include "scoped_utf_chars.h" + +// Test infrastructure +#include "jvmti_helper.h" +#include "test_env.h" + +namespace art { +namespace Test1950UnpreparedTransform { + +jclass kMainClass = nullptr; +jmethodID kPrepareFunc = nullptr; + +extern "C" JNIEXPORT void ClassLoadCallback(jvmtiEnv* jvmti ATTRIBUTE_UNUSED, + JNIEnv* env, + jthread thr ATTRIBUTE_UNUSED, + jclass klass) { + env->CallStaticVoidMethod(kMainClass, kPrepareFunc, klass); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_clearClassLoadHook( + JNIEnv* env, jclass main ATTRIBUTE_UNUSED, jthread thr) { + JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, + JVMTI_EVENT_CLASS_LOAD, + thr)); +} +extern "C" JNIEXPORT void JNICALL Java_Main_setupClassLoadHook( + JNIEnv* env, jclass main, jthread thr) { + kMainClass = reinterpret_cast(env->NewGlobalRef(main)); + kPrepareFunc = env->GetStaticMethodID(main, "doClassLoad", "(Ljava/lang/Class;)V"); + if (env->ExceptionCheck()) { + return; + } + current_callbacks.ClassLoad = ClassLoadCallback; + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetEventCallbacks( + ¤t_callbacks, sizeof(current_callbacks)))) { + return; + } + JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, + JVMTI_EVENT_CLASS_LOAD, + thr)); +} + +} // namespace Test1950UnpreparedTransform +} // namespace art diff --git a/test/Android.bp b/test/Android.bp index 0c1edcaab8..bd13de2fa9 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -277,6 +277,7 @@ art_cc_defaults { "1942-suspend-raw-monitor-exit/native_suspend_monitor.cc", "1943-suspend-raw-monitor-wait/native_suspend_monitor.cc", "1946-list-descriptors/descriptors.cc", + "1950-unprepared-transform/unprepared_transform.cc", ], // Use NDK-compatible headers for ctstiagent. header_libs: [ diff --git a/test/ti-agent/breakpoint_helper.cc b/test/ti-agent/breakpoint_helper.cc index 78aab4376f..db4ea61f1c 100644 --- a/test/ti-agent/breakpoint_helper.cc +++ b/test/ti-agent/breakpoint_helper.cc @@ -172,10 +172,11 @@ extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_startBreakpointWatch( if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) { return; } - jvmtiEventCallbacks cb; - memset(&cb, 0, sizeof(cb)); - cb.Breakpoint = breakpointCB; - if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) { + current_callbacks.Breakpoint = breakpointCB; + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetEventCallbacks(¤t_callbacks, + sizeof(current_callbacks)))) { return; } if (JvmtiErrorToException(env, diff --git a/test/ti-agent/exceptions_helper.cc b/test/ti-agent/exceptions_helper.cc index 74f7ecc881..e56c39b9eb 100644 --- a/test/ti-agent/exceptions_helper.cc +++ b/test/ti-agent/exceptions_helper.cc @@ -147,11 +147,12 @@ extern "C" JNIEXPORT void JNICALL Java_art_Exceptions_setupExceptionTracing( return; } - jvmtiEventCallbacks cb; - memset(&cb, 0, sizeof(cb)); - cb.Exception = exceptionCB; - cb.ExceptionCatch = exceptionCatchCB; - if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) { + current_callbacks.Exception = exceptionCB; + current_callbacks.ExceptionCatch = exceptionCatchCB; + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetEventCallbacks(¤t_callbacks, + sizeof(current_callbacks)))) { return; } } diff --git a/test/ti-agent/frame_pop_helper.cc b/test/ti-agent/frame_pop_helper.cc index 4571032ce6..f39e1854bc 100644 --- a/test/ti-agent/frame_pop_helper.cc +++ b/test/ti-agent/frame_pop_helper.cc @@ -90,10 +90,11 @@ extern "C" JNIEXPORT void JNICALL Java_art_FramePop_enableFramePopEvent( if (JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps))) { return; } - jvmtiEventCallbacks cb; - memset(&cb, 0, sizeof(cb)); - cb.FramePop = framePopCB; - if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) { + current_callbacks.FramePop = framePopCB; + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetEventCallbacks(¤t_callbacks, + sizeof(current_callbacks)))) { return; } JvmtiErrorToException(env, diff --git a/test/ti-agent/monitors_helper.cc b/test/ti-agent/monitors_helper.cc index 81d4cdc3ae..4434baf01c 100644 --- a/test/ti-agent/monitors_helper.cc +++ b/test/ti-agent/monitors_helper.cc @@ -182,13 +182,14 @@ extern "C" JNIEXPORT void JNICALL Java_art_Monitors_setupMonitorEvents( return; } - jvmtiEventCallbacks cb; - memset(&cb, 0, sizeof(cb)); - cb.MonitorContendedEnter = monitorEnterCB; - cb.MonitorContendedEntered = monitorEnteredCB; - cb.MonitorWait = monitorWaitCB; - cb.MonitorWaited = monitorWaitedCB; - if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) { + current_callbacks.MonitorContendedEnter = monitorEnterCB; + current_callbacks.MonitorContendedEntered = monitorEnteredCB; + current_callbacks.MonitorWait = monitorWaitCB; + current_callbacks.MonitorWaited = monitorWaitedCB; + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetEventCallbacks(¤t_callbacks, + sizeof(current_callbacks)))) { return; } if (JvmtiErrorToException(env, diff --git a/test/ti-agent/redefinition_helper.cc b/test/ti-agent/redefinition_helper.cc index 76371de20a..0e4b1bdd8c 100644 --- a/test/ti-agent/redefinition_helper.cc +++ b/test/ti-agent/redefinition_helper.cc @@ -381,10 +381,8 @@ static void SetupCommonRedefine() { static void SetupCommonRetransform() { SetStandardCapabilities(jvmti_env); - jvmtiEventCallbacks cb; - memset(&cb, 0, sizeof(cb)); - cb.ClassFileLoadHook = common_retransform::CommonClassFileLoadHookRetransformable; - jvmtiError res = jvmti_env->SetEventCallbacks(&cb, sizeof(cb)); + current_callbacks.ClassFileLoadHook = common_retransform::CommonClassFileLoadHookRetransformable; + jvmtiError res = jvmti_env->SetEventCallbacks(¤t_callbacks, sizeof(current_callbacks)); CHECK_EQ(res, JVMTI_ERROR_NONE); common_retransform::gTransformations.clear(); } @@ -397,10 +395,8 @@ static void SetupCommonTransform() { jvmti_env->AddCapabilities(&caps); // Use the same callback as the retransform test. - jvmtiEventCallbacks cb; - memset(&cb, 0, sizeof(cb)); - cb.ClassFileLoadHook = common_retransform::CommonClassFileLoadHookRetransformable; - jvmtiError res = jvmti_env->SetEventCallbacks(&cb, sizeof(cb)); + current_callbacks.ClassFileLoadHook = common_retransform::CommonClassFileLoadHookRetransformable; + jvmtiError res = jvmti_env->SetEventCallbacks(¤t_callbacks, sizeof(current_callbacks)); CHECK_EQ(res, JVMTI_ERROR_NONE); common_retransform::gTransformations.clear(); } diff --git a/test/ti-agent/test_env.cc b/test/ti-agent/test_env.cc index cf47f22b03..49313876c9 100644 --- a/test/ti-agent/test_env.cc +++ b/test/ti-agent/test_env.cc @@ -19,6 +19,7 @@ namespace art { jvmtiEnv* jvmti_env = nullptr; +jvmtiEventCallbacks current_callbacks = {}; static bool gRuntimeIsJVM = false; diff --git a/test/ti-agent/test_env.h b/test/ti-agent/test_env.h index 2eb631c36c..a8a9f57097 100644 --- a/test/ti-agent/test_env.h +++ b/test/ti-agent/test_env.h @@ -23,6 +23,11 @@ namespace art { extern jvmtiEnv* jvmti_env; +// This is a jvmtiEventCallbacks struct that is used by all common ti-agent code whenever it calls +// SetEventCallbacks. This can be used by single tests to add additional event callbacks without +// being unable to use the rest of the ti-agent support code. +extern jvmtiEventCallbacks current_callbacks; + bool IsJVM(); void SetJVM(bool b); diff --git a/test/ti-agent/trace_helper.cc b/test/ti-agent/trace_helper.cc index b590175d77..11e1c15757 100644 --- a/test/ti-agent/trace_helper.cc +++ b/test/ti-agent/trace_helper.cc @@ -513,17 +513,18 @@ extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing2( return; } - jvmtiEventCallbacks cb; - memset(&cb, 0, sizeof(cb)); - cb.MethodEntry = methodEntryCB; - cb.MethodExit = methodExitCB; - cb.FieldAccess = fieldAccessCB; - cb.FieldModification = fieldModificationCB; - cb.ClassPrepare = classPrepareCB; - cb.SingleStep = singleStepCB; - cb.ThreadStart = threadStartCB; - cb.ThreadEnd = threadEndCB; - if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) { + current_callbacks.MethodEntry = methodEntryCB; + current_callbacks.MethodExit = methodExitCB; + current_callbacks.FieldAccess = fieldAccessCB; + current_callbacks.FieldModification = fieldModificationCB; + current_callbacks.ClassPrepare = classPrepareCB; + current_callbacks.SingleStep = singleStepCB; + current_callbacks.ThreadStart = threadStartCB; + current_callbacks.ThreadEnd = threadEndCB; + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetEventCallbacks(¤t_callbacks, + sizeof(current_callbacks)))) { return; } if (enter != nullptr && -- GitLab From cf767de732a63afb03622c694cd40c01538d67ed Mon Sep 17 00:00:00 2001 From: Dan Willemsen Date: Tue, 10 Apr 2018 16:40:10 -0700 Subject: [PATCH 232/749] Fix RUNTIME_TARGET_GTEST_MAKE_TARGETS This was including only the , while Soong test_per_src tests are exposed to make as _. So deconstruct the path, which uses ...//, instead of just taking the basename. This has been causing warnings on some internal branch builds, about how we were asking for these modules, but that they didn't exist. This change removes those warnings. Test: Check warnings on internal branches Change-Id: I4b8559961f51cc32f2fac0bf6fb15ee34ba72bdd --- build/Android.gtest.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index be27869fe0..7ca7409f88 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -705,7 +705,7 @@ endif # Used outside the art project to get a list of the current tests RUNTIME_TARGET_GTEST_MAKE_TARGETS := -$(foreach file, $(ART_TARGET_GTEST_FILES), $(eval RUNTIME_TARGET_GTEST_MAKE_TARGETS += $$(notdir $$(basename $$(file))))) +$(foreach file, $(ART_TARGET_GTEST_FILES), $(eval RUNTIME_TARGET_GTEST_MAKE_TARGETS += $$(notdir $$(patsubst %/,%,$$(dir $$(file))))_$$(notdir $$(basename $$(file))))) COMPILER_TARGET_GTEST_MAKE_TARGETS := # Define all the combinations of host/target, valgrind and suffix such as: -- GitLab From 703b82a047a5e7a3b2c325c3e68819228e997ec4 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 10 Apr 2018 15:52:32 -0700 Subject: [PATCH 233/749] Add extra timing loggers for image writing Used to diagnose image writer performance. Bug: 77719042 Test: test-art-host Change-Id: Iba73eec94a3de33813261b04e497c8857e339d89 --- dex2oat/dex2oat.cc | 2 +- dex2oat/linker/image_test.h | 2 +- dex2oat/linker/image_writer.cc | 16 +++++++++++++--- dex2oat/linker/image_writer.h | 3 ++- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index ac5dd61062..efadc3dfc4 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -2026,7 +2026,7 @@ class Dex2Oat FINAL { // We need to prepare method offsets in the image address space for direct method patching. TimingLogger::ScopedTiming t2("dex2oat Prepare image address space", timings_); - if (!image_writer_->PrepareImageAddressSpace()) { + if (!image_writer_->PrepareImageAddressSpace(timings_)) { LOG(ERROR) << "Failed to prepare image address space."; return false; } diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index 7449191984..7490485eb3 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -293,7 +293,7 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, ASSERT_TRUE(cur_opened_dex_files.empty()); } } - bool image_space_ok = writer->PrepareImageAddressSpace(); + bool image_space_ok = writer->PrepareImageAddressSpace(&timings); ASSERT_TRUE(image_space_ok); DCHECK_EQ(vdex_files.size(), oat_files.size()); diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 6530ead2d1..c7a30a06ed 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -133,23 +133,31 @@ static void ClearDexFileCookies() REQUIRES_SHARED(Locks::mutator_lock_) { Runtime::Current()->GetHeap()->VisitObjects(visitor); } -bool ImageWriter::PrepareImageAddressSpace() { +bool ImageWriter::PrepareImageAddressSpace(TimingLogger* timings) { target_ptr_size_ = InstructionSetPointerSize(compiler_driver_.GetInstructionSet()); gc::Heap* const heap = Runtime::Current()->GetHeap(); { ScopedObjectAccess soa(Thread::Current()); - PruneNonImageClasses(); // Remove junk + { + TimingLogger::ScopedTiming t("PruneNonImageClasses", timings); + PruneNonImageClasses(); // Remove junk + } if (compile_app_image_) { + TimingLogger::ScopedTiming t("ClearDexFileCookies", timings); // Clear dex file cookies for app images to enable app image determinism. This is required // since the cookie field contains long pointers to DexFiles which are not deterministic. // b/34090128 ClearDexFileCookies(); } else { + TimingLogger::ScopedTiming t("ComputeLazyFieldsForImageClasses", timings); // Avoid for app image since this may increase RAM and image size. ComputeLazyFieldsForImageClasses(); // Add useful information } } - heap->CollectGarbage(/* clear_soft_references */ false); // Remove garbage. + { + TimingLogger::ScopedTiming t("CollectGarbage", timings); + heap->CollectGarbage(/* clear_soft_references */ false); // Remove garbage. + } if (kIsDebugBuild) { ScopedObjectAccess soa(Thread::Current()); @@ -157,12 +165,14 @@ bool ImageWriter::PrepareImageAddressSpace() { } { + TimingLogger::ScopedTiming t("CalculateNewObjectOffsets", timings); ScopedObjectAccess soa(Thread::Current()); CalculateNewObjectOffsets(); } // This needs to happen after CalculateNewObjectOffsets since it relies on intern_table_bytes_ and // bin size sums being calculated. + TimingLogger::ScopedTiming t("AllocMemory", timings); if (!AllocMemory()) { return false; } diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h index c67835b455..197253e102 100644 --- a/dex2oat/linker/image_writer.h +++ b/dex2oat/linker/image_writer.h @@ -64,6 +64,7 @@ class ClassLoader; class ClassLoaderVisitor; class ImTable; class ImtConflictTable; +class TimingLogger; static constexpr int kInvalidFd = -1; @@ -81,7 +82,7 @@ class ImageWriter FINAL { const std::unordered_map& dex_file_oat_index_map, const std::unordered_set* dirty_image_objects); - bool PrepareImageAddressSpace(); + bool PrepareImageAddressSpace(TimingLogger* timings); bool IsImageAddressSpaceReady() const { DCHECK(!image_infos_.empty()); -- GitLab From ca1e038eb94694f0f1f94ed3781572411c85d365 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 11 Apr 2018 09:58:41 +0000 Subject: [PATCH 234/749] Revert^2 "Compile link-time thunks in codegen." The linker crash (the reason for revert) is flaky and maybe we shall not see it with this CL now that unrelated source code has changed. Test: Rely on TreeHugger Bug: 36141117 Bug: 77581732 This reverts commit 5806a9ec99b5494b511e84c74f494f0b3a8ebec5. Change-Id: I3a4a058847dff601681ba391abf45833424fa06d --- compiler/cfi_test.h | 21 +- compiler/driver/compiled_method_storage.cc | 95 ++++- compiler/driver/compiled_method_storage.h | 26 ++ compiler/jni/jni_cfi_test.cc | 6 +- .../linker/arm/relative_patcher_arm_base.cc | 44 ++- .../linker/arm/relative_patcher_arm_base.h | 9 +- .../linker/arm/relative_patcher_thumb2.cc | 305 +-------------- compiler/linker/arm/relative_patcher_thumb2.h | 78 +--- .../arm/relative_patcher_thumb2_test.cc | 96 +++-- .../linker/arm64/relative_patcher_arm64.cc | 253 +----------- .../linker/arm64/relative_patcher_arm64.h | 54 +-- .../arm64/relative_patcher_arm64_test.cc | 87 ++-- compiler/linker/linker_patch.h | 2 +- compiler/linker/relative_patcher.cc | 13 +- compiler/linker/relative_patcher.h | 27 +- compiler/linker/relative_patcher_test.h | 72 +++- compiler/optimizing/code_generator.cc | 12 + compiler/optimizing/code_generator.h | 6 + compiler/optimizing/code_generator_arm64.cc | 354 ++++++++++++++--- compiler/optimizing/code_generator_arm64.h | 89 ++++- .../optimizing/code_generator_arm_vixl.cc | 370 ++++++++++++++++-- compiler/optimizing/code_generator_arm_vixl.h | 110 +++++- compiler/optimizing/codegen_test_utils.h | 8 +- compiler/optimizing/optimizing_cfi_test.cc | 10 +- compiler/optimizing/optimizing_compiler.cc | 26 +- dex2oat/dex2oat.cc | 4 +- dex2oat/linker/image_test.h | 3 +- dex2oat/linker/multi_oat_relative_patcher.cc | 20 +- dex2oat/linker/multi_oat_relative_patcher.h | 19 +- .../linker/multi_oat_relative_patcher_test.cc | 2 +- dex2oat/linker/oat_writer_test.cc | 3 +- 31 files changed, 1336 insertions(+), 888 deletions(-) diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h index 29ff235cea..581edaa773 100644 --- a/compiler/cfi_test.h +++ b/compiler/cfi_test.h @@ -37,8 +37,8 @@ constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_DEBUG_FRAME_FORMAT; class CFITest : public dwarf::DwarfTest { public: void GenerateExpected(FILE* f, InstructionSet isa, const char* isa_str, - const std::vector& actual_asm, - const std::vector& actual_cfi) { + ArrayRef actual_asm, + ArrayRef actual_cfi) { std::vector lines; // Print the raw bytes. fprintf(f, "static constexpr uint8_t expected_asm_%s[] = {", isa_str); @@ -50,11 +50,18 @@ class CFITest : public dwarf::DwarfTest { // Pretty-print CFI opcodes. constexpr bool is64bit = false; dwarf::DebugFrameOpCodeWriter<> initial_opcodes; - dwarf::WriteCIE(is64bit, dwarf::Reg(8), - initial_opcodes, kCFIFormat, &debug_frame_data_); + dwarf::WriteCIE(is64bit, dwarf::Reg(8), initial_opcodes, kCFIFormat, &debug_frame_data_); std::vector debug_frame_patches; - dwarf::WriteFDE(is64bit, 0, 0, 0, actual_asm.size(), ArrayRef(actual_cfi), - kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches); + dwarf::WriteFDE(is64bit, + /* section_address */ 0, + /* cie_address */ 0, + /* code_address */ 0, + actual_asm.size(), + actual_cfi, + kCFIFormat, + /* buffer_address */ 0, + &debug_frame_data_, + &debug_frame_patches); ReformatCfi(Objdump(false, "-W"), &lines); // Pretty-print assembly. const uint8_t* asm_base = actual_asm.data(); @@ -142,7 +149,7 @@ class CFITest : public dwarf::DwarfTest { } // Pretty-print byte array. 12 bytes per line. - static void HexDump(FILE* f, const std::vector& data) { + static void HexDump(FILE* f, ArrayRef data) { for (size_t i = 0; i < data.size(); i++) { fprintf(f, i % 12 == 0 ? "\n " : " "); // Whitespace. fprintf(f, "0x%02X,", data[i]); diff --git a/compiler/driver/compiled_method_storage.cc b/compiler/driver/compiled_method_storage.cc index a26a985ff9..aa8277edb4 100644 --- a/compiler/driver/compiled_method_storage.cc +++ b/compiler/driver/compiled_method_storage.cc @@ -161,6 +161,46 @@ class CompiledMethodStorage::LengthPrefixedArrayAlloc { SwapSpace* const swap_space_; }; +class CompiledMethodStorage::ThunkMapKey { + public: + ThunkMapKey(linker::LinkerPatch::Type type, uint32_t custom_value1, uint32_t custom_value2) + : type_(type), custom_value1_(custom_value1), custom_value2_(custom_value2) {} + + bool operator<(const ThunkMapKey& other) const { + if (custom_value1_ != other.custom_value1_) { + return custom_value1_ < other.custom_value1_; + } + if (custom_value2_ != other.custom_value2_) { + return custom_value2_ < other.custom_value2_; + } + return type_ < other.type_; + } + + private: + linker::LinkerPatch::Type type_; + uint32_t custom_value1_; + uint32_t custom_value2_; +}; + +class CompiledMethodStorage::ThunkMapValue { + public: + ThunkMapValue(std::vector>&& code, + const std::string& debug_name) + : code_(std::move(code)), debug_name_(debug_name) {} + + ArrayRef GetCode() const { + return ArrayRef(code_); + } + + const std::string& GetDebugName() const { + return debug_name_; + } + + private: + std::vector> code_; + std::string debug_name_; +}; + CompiledMethodStorage::CompiledMethodStorage(int swap_fd) : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)), dedupe_enabled_(true), @@ -171,7 +211,9 @@ CompiledMethodStorage::CompiledMethodStorage(int swap_fd) LengthPrefixedArrayAlloc(swap_space_.get())), dedupe_cfi_info_("dedupe cfi info", LengthPrefixedArrayAlloc(swap_space_.get())), dedupe_linker_patches_("dedupe cfi info", - LengthPrefixedArrayAlloc(swap_space_.get())) { + LengthPrefixedArrayAlloc(swap_space_.get())), + thunk_map_lock_("thunk_map_lock"), + thunk_map_(std::less(), SwapAllocator(swap_space_.get())) { } CompiledMethodStorage::~CompiledMethodStorage() { @@ -237,4 +279,55 @@ void CompiledMethodStorage::ReleaseLinkerPatches( ReleaseArrayIfNotDeduplicated(linker_patches); } +CompiledMethodStorage::ThunkMapKey CompiledMethodStorage::GetThunkMapKey( + const linker::LinkerPatch& linker_patch) { + uint32_t custom_value1 = 0u; + uint32_t custom_value2 = 0u; + switch (linker_patch.GetType()) { + case linker::LinkerPatch::Type::kBakerReadBarrierBranch: + custom_value1 = linker_patch.GetBakerCustomValue1(); + custom_value2 = linker_patch.GetBakerCustomValue2(); + break; + case linker::LinkerPatch::Type::kCallRelative: + // No custom values. + break; + default: + LOG(FATAL) << "Unexpected patch type: " << linker_patch.GetType(); + UNREACHABLE(); + } + return ThunkMapKey(linker_patch.GetType(), custom_value1, custom_value2); +} + +ArrayRef CompiledMethodStorage::GetThunkCode(const linker::LinkerPatch& linker_patch, + /*out*/ std::string* debug_name) { + ThunkMapKey key = GetThunkMapKey(linker_patch); + MutexLock lock(Thread::Current(), thunk_map_lock_); + auto it = thunk_map_.find(key); + if (it != thunk_map_.end()) { + const ThunkMapValue& value = it->second; + if (debug_name != nullptr) { + *debug_name = value.GetDebugName(); + } + return value.GetCode(); + } else { + if (debug_name != nullptr) { + *debug_name = std::string(); + } + return ArrayRef(); + } +} + +void CompiledMethodStorage::SetThunkCode(const linker::LinkerPatch& linker_patch, + ArrayRef code, + const std::string& debug_name) { + DCHECK(!code.empty()); + ThunkMapKey key = GetThunkMapKey(linker_patch); + std::vector> code_copy( + code.begin(), code.end(), SwapAllocator(swap_space_.get())); + ThunkMapValue value(std::move(code_copy), debug_name); + MutexLock lock(Thread::Current(), thunk_map_lock_); + // Note: Multiple threads can try and compile the same thunk, so this may not create a new entry. + thunk_map_.emplace(key, std::move(value)); +} + } // namespace art diff --git a/compiler/driver/compiled_method_storage.h b/compiler/driver/compiled_method_storage.h index 249f06c20f..1634facb7c 100644 --- a/compiler/driver/compiled_method_storage.h +++ b/compiler/driver/compiled_method_storage.h @@ -18,6 +18,7 @@ #define ART_COMPILER_DRIVER_COMPILED_METHOD_STORAGE_H_ #include +#include #include #include "base/array_ref.h" @@ -67,7 +68,29 @@ class CompiledMethodStorage { const ArrayRef& linker_patches); void ReleaseLinkerPatches(const LengthPrefixedArray* linker_patches); + // Returns the code associated with the given patch. + // If the code has not been set, returns empty data. + // If `debug_name` is not null, stores the associated debug name in `*debug_name`. + ArrayRef GetThunkCode(const linker::LinkerPatch& linker_patch, + /*out*/ std::string* debug_name = nullptr); + + // Sets the code and debug name associated with the given patch. + void SetThunkCode(const linker::LinkerPatch& linker_patch, + ArrayRef code, + const std::string& debug_name); + private: + class ThunkMapKey; + class ThunkMapValue; + using ThunkMapValueType = std::pair; + using ThunkMap = std::map, + SwapAllocator>; + static_assert(std::is_same::value, "Value type check."); + + static ThunkMapKey GetThunkMapKey(const linker::LinkerPatch& linker_patch); + template const LengthPrefixedArray* AllocateOrDeduplicateArray(const ArrayRef& data, DedupeSetType* dedupe_set); @@ -102,6 +125,9 @@ class CompiledMethodStorage { ArrayDedupeSet dedupe_cfi_info_; ArrayDedupeSet dedupe_linker_patches_; + Mutex thunk_map_lock_; + ThunkMap thunk_map_ GUARDED_BY(thunk_map_lock_); + DISALLOW_COPY_AND_ASSIGN(CompiledMethodStorage); }; diff --git a/compiler/jni/jni_cfi_test.cc b/compiler/jni/jni_cfi_test.cc index 11b0e2ba9d..920a3a8da6 100644 --- a/compiler/jni/jni_cfi_test.cc +++ b/compiler/jni/jni_cfi_test.cc @@ -95,7 +95,11 @@ class JNICFITest : public CFITest { const std::vector& actual_cfi = *(jni_asm->cfi().data()); if (kGenerateExpected) { - GenerateExpected(stdout, isa, isa_str, actual_asm, actual_cfi); + GenerateExpected(stdout, + isa, + isa_str, + ArrayRef(actual_asm), + ArrayRef(actual_cfi)); } else { EXPECT_EQ(expected_asm, actual_asm); EXPECT_EQ(expected_cfi, actual_cfi); diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc index 6e0286afac..7cb8ae55c5 100644 --- a/compiler/linker/arm/relative_patcher_arm_base.cc +++ b/compiler/linker/arm/relative_patcher_arm_base.cc @@ -30,8 +30,9 @@ namespace linker { class ArmBaseRelativePatcher::ThunkData { public: - ThunkData(std::vector code, uint32_t max_next_offset) - : code_(std::move(code)), + ThunkData(ArrayRef code, const std::string& debug_name, uint32_t max_next_offset) + : code_(code), + debug_name_(debug_name), offsets_(), max_next_offset_(max_next_offset), pending_offset_(0u) { @@ -45,7 +46,11 @@ class ArmBaseRelativePatcher::ThunkData { } ArrayRef GetCode() const { - return ArrayRef(code_); + return code_; + } + + const std::string& GetDebugName() const { + return debug_name_; } bool NeedsNextThunk() const { @@ -142,10 +147,11 @@ class ArmBaseRelativePatcher::ThunkData { } private: - std::vector code_; // The code of the thunk. - std::vector offsets_; // Offsets at which the thunk needs to be written. - uint32_t max_next_offset_; // The maximum offset at which the next thunk can be placed. - uint32_t pending_offset_; // The index of the next offset to write. + const ArrayRef code_; // The code of the thunk. + const std::string debug_name_; // The debug name of the thunk. + std::vector offsets_; // Offsets at which the thunk needs to be written. + uint32_t max_next_offset_; // The maximum offset at which the next thunk can be placed. + uint32_t pending_offset_; // The index of the next offset to write. }; class ArmBaseRelativePatcher::PendingThunkComparator { @@ -239,14 +245,13 @@ std::vector ArmBaseRelativePatcher::GenerateThunkDebugIn std::vector result; result.reserve(number_of_thunks); for (auto&& entry : thunks_) { - const ThunkKey& key = entry.first; const ThunkData& data = entry.second; size_t start = data.IndexOfFirstThunkAtOrAfter(executable_offset); if (start == data.NumberOfThunks()) { continue; } // Get the base name to use for the first occurrence of the thunk. - std::string base_name = GetThunkDebugName(key); + std::string base_name = data.GetDebugName(); for (size_t i = start, num = data.NumberOfThunks(); i != num; ++i) { debug::MethodDebugInfo info = {}; if (i == 0u) { @@ -267,9 +272,11 @@ std::vector ArmBaseRelativePatcher::GenerateThunkDebugIn return result; } -ArmBaseRelativePatcher::ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider, +ArmBaseRelativePatcher::ArmBaseRelativePatcher(RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider, InstructionSet instruction_set) - : provider_(provider), + : thunk_provider_(thunk_provider), + target_provider_(target_provider), instruction_set_(instruction_set), thunks_(), unprocessed_method_call_patches_(), @@ -398,7 +405,7 @@ void ArmBaseRelativePatcher::ProcessPatches(const CompiledMethod* compiled_metho unprocessed_method_call_patches_.emplace_back(patch_offset, patch.TargetMethod()); if (method_call_thunk_ == nullptr) { uint32_t max_next_offset = CalculateMaxNextOffset(patch_offset, key); - auto it = thunks_.Put(key, ThunkData(CompileThunk(key), max_next_offset)); + auto it = thunks_.Put(key, ThunkDataForPatch(patch, max_next_offset)); method_call_thunk_ = &it->second; AddUnreservedThunk(method_call_thunk_); } else { @@ -409,7 +416,7 @@ void ArmBaseRelativePatcher::ProcessPatches(const CompiledMethod* compiled_metho auto lb = thunks_.lower_bound(key); if (lb == thunks_.end() || thunks_.key_comp()(key, lb->first)) { uint32_t max_next_offset = CalculateMaxNextOffset(patch_offset, key); - auto it = thunks_.PutBefore(lb, key, ThunkData(CompileThunk(key), max_next_offset)); + auto it = thunks_.PutBefore(lb, key, ThunkDataForPatch(patch, max_next_offset)); AddUnreservedThunk(&it->second); } else { old_data = &lb->second; @@ -477,7 +484,7 @@ void ArmBaseRelativePatcher::ResolveMethodCalls(uint32_t quick_code_offset, break; } } else { - auto result = provider_->FindMethodOffset(target_method); + auto result = target_provider_->FindMethodOffset(target_method); if (!result.first) { break; } @@ -518,5 +525,14 @@ inline uint32_t ArmBaseRelativePatcher::CalculateMaxNextOffset(uint32_t patch_of GetInstructionSetAlignment(instruction_set_)); } +inline ArmBaseRelativePatcher::ThunkData ArmBaseRelativePatcher::ThunkDataForPatch( + const LinkerPatch& patch, uint32_t max_next_offset) { + ArrayRef code; + std::string debug_name; + thunk_provider_->GetThunkCode(patch, &code, &debug_name); + DCHECK(!code.empty()); + return ThunkData(code, debug_name, max_next_offset); +} + } // namespace linker } // namespace art diff --git a/compiler/linker/arm/relative_patcher_arm_base.h b/compiler/linker/arm/relative_patcher_arm_base.h index ee09bf96b3..963d6690b0 100644 --- a/compiler/linker/arm/relative_patcher_arm_base.h +++ b/compiler/linker/arm/relative_patcher_arm_base.h @@ -37,7 +37,8 @@ class ArmBaseRelativePatcher : public RelativePatcher { std::vector GenerateThunkDebugInfo(uint32_t executable_offset) OVERRIDE; protected: - ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider, + ArmBaseRelativePatcher(RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider, InstructionSet instruction_set); ~ArmBaseRelativePatcher(); @@ -94,8 +95,6 @@ class ArmBaseRelativePatcher : public RelativePatcher { uint32_t CalculateMethodCallDisplacement(uint32_t patch_offset, uint32_t target_offset); - virtual std::vector CompileThunk(const ThunkKey& key) = 0; - virtual std::string GetThunkDebugName(const ThunkKey& key) = 0; virtual uint32_t MaxPositiveDisplacement(const ThunkKey& key) = 0; virtual uint32_t MaxNegativeDisplacement(const ThunkKey& key) = 0; @@ -108,8 +107,10 @@ class ArmBaseRelativePatcher : public RelativePatcher { void ResolveMethodCalls(uint32_t quick_code_offset, MethodReference method_ref); uint32_t CalculateMaxNextOffset(uint32_t patch_offset, const ThunkKey& key); + ThunkData ThunkDataForPatch(const LinkerPatch& patch, uint32_t max_next_offset); - RelativePatcherTargetProvider* const provider_; + RelativePatcherThunkProvider* const thunk_provider_; + RelativePatcherTargetProvider* const target_provider_; const InstructionSet instruction_set_; // The data for all thunks. diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc index 0056c50634..697fb09f73 100644 --- a/compiler/linker/arm/relative_patcher_thumb2.cc +++ b/compiler/linker/arm/relative_patcher_thumb2.cc @@ -49,8 +49,9 @@ constexpr uint32_t kMaxMethodCallNegativeDisplacement = (1u << 24) - kPcDisplace constexpr uint32_t kMaxBcondPositiveDisplacement = (1u << 20) - 2u + kPcDisplacement; constexpr uint32_t kMaxBcondNegativeDisplacement = (1u << 20) - kPcDisplacement; -Thumb2RelativePatcher::Thumb2RelativePatcher(RelativePatcherTargetProvider* provider) - : ArmBaseRelativePatcher(provider, InstructionSet::kThumb2) { +Thumb2RelativePatcher::Thumb2RelativePatcher(RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider) + : ArmBaseRelativePatcher(thunk_provider, target_provider, InstructionSet::kThumb2) { } void Thumb2RelativePatcher::PatchCall(std::vector* code, @@ -111,62 +112,6 @@ void Thumb2RelativePatcher::PatchBakerReadBarrierBranch(std::vector* co uint32_t insn = GetInsn32(code, literal_offset); DCHECK_EQ(insn, 0xf0408000); // BNE +0 (unpatched) ThunkKey key = GetBakerThunkKey(patch); - if (kIsDebugBuild) { - const uint32_t encoded_data = key.GetCustomValue1(); - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - // Check that the next instruction matches the expected LDR. - switch (kind) { - case BakerReadBarrierKind::kField: { - BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); - if (width == BakerReadBarrierWidth::kWide) { - DCHECK_GE(code->size() - literal_offset, 8u); - uint32_t next_insn = GetInsn32(code, literal_offset + 4u); - // LDR (immediate), encoding T3, with correct base_reg. - CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xffff0000u, 0xf8d00000u | (base_reg << 16)); - } else { - DCHECK_GE(code->size() - literal_offset, 6u); - uint32_t next_insn = GetInsn16(code, literal_offset + 4u); - // LDR (immediate), encoding T1, with correct base_reg. - CheckValidReg(next_insn & 0x7u); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xf838u, 0x6800u | (base_reg << 3)); - } - break; - } - case BakerReadBarrierKind::kArray: { - DCHECK_GE(code->size() - literal_offset, 8u); - uint32_t next_insn = GetInsn32(code, literal_offset + 4u); - // LDR (register) with correct base_reg, S=1 and option=011 (LDR Wt, [Xn, Xm, LSL #2]). - CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xffff0ff0u, 0xf8500020u | (base_reg << 16)); - CheckValidReg(next_insn & 0xf); // Check index register - break; - } - case BakerReadBarrierKind::kGcRoot: { - BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); - if (width == BakerReadBarrierWidth::kWide) { - DCHECK_GE(literal_offset, 4u); - uint32_t prev_insn = GetInsn32(code, literal_offset - 4u); - // LDR (immediate), encoding T3, with correct root_reg. - const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(prev_insn & 0xfff0f000u, 0xf8d00000u | (root_reg << 12)); - } else { - DCHECK_GE(literal_offset, 2u); - uint32_t prev_insn = GetInsn16(code, literal_offset - 2u); - // LDR (immediate), encoding T1, with correct root_reg. - const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(prev_insn & 0xf807u, 0x6800u | root_reg); - } - break; - } - default: - LOG(FATAL) << "Unexpected type: " << static_cast(key.GetType()); - UNREACHABLE(); - } - } uint32_t target_offset = GetThunkTargetOffset(key, patch_offset); DCHECK_ALIGNED(target_offset, 4u); uint32_t disp = target_offset - (patch_offset + kPcDisplacement); @@ -179,250 +124,6 @@ void Thumb2RelativePatcher::PatchBakerReadBarrierBranch(std::vector* co SetInsn32(code, literal_offset, insn); } -#define __ assembler.GetVIXLAssembler()-> - -static void EmitGrayCheckAndFastPath(arm::ArmVIXLAssembler& assembler, - vixl::aarch32::Register base_reg, - vixl::aarch32::MemOperand& lock_word, - vixl::aarch32::Label* slow_path, - int32_t raw_ldr_offset) { - using namespace vixl::aarch32; // NOLINT(build/namespaces) - // Load the lock word containing the rb_state. - __ Ldr(ip, lock_word); - // Given the numeric representation, it's enough to check the low bit of the rb_state. - static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0"); - static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1"); - __ Tst(ip, Operand(LockWord::kReadBarrierStateMaskShifted)); - __ B(ne, slow_path, /* is_far_target */ false); - __ Add(lr, lr, raw_ldr_offset); - // Introduce a dependency on the lock_word including rb_state, - // to prevent load-load reordering, and without using - // a memory barrier (which would be more expensive). - __ Add(base_reg, base_reg, Operand(ip, LSR, 32)); - __ Bx(lr); // And return back to the function. - // Note: The fake dependency is unnecessary for the slow path. -} - -// Load the read barrier introspection entrypoint in register `entrypoint` -static void LoadReadBarrierMarkIntrospectionEntrypoint(arm::ArmVIXLAssembler& assembler, - vixl::aarch32::Register entrypoint) { - using vixl::aarch32::MemOperand; - using vixl::aarch32::ip; - // Thread Register. - const vixl::aarch32::Register tr = vixl::aarch32::r9; - - // The register where the read barrier introspection entrypoint is loaded - // is fixed: `Thumb2RelativePatcher::kBakerCcEntrypointRegister` (R4). - DCHECK_EQ(entrypoint.GetCode(), Thumb2RelativePatcher::kBakerCcEntrypointRegister); - // entrypoint = Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection. - DCHECK_EQ(ip.GetCode(), 12u); - const int32_t entry_point_offset = - Thread::ReadBarrierMarkEntryPointsOffset(ip.GetCode()); - __ Ldr(entrypoint, MemOperand(tr, entry_point_offset)); -} - -void Thumb2RelativePatcher::CompileBakerReadBarrierThunk(arm::ArmVIXLAssembler& assembler, - uint32_t encoded_data) { - using namespace vixl::aarch32; // NOLINT(build/namespaces) - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - switch (kind) { - case BakerReadBarrierKind::kField: { - // Check if the holder is gray and, if not, add fake dependency to the base register - // and return to the LDR instruction to load the reference. Otherwise, use introspection - // to load the reference and call the entrypoint (in kBakerCcEntrypointRegister) - // that performs further checks on the reference and marks it if needed. - Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(base_reg.GetCode()); - Register holder_reg(BakerReadBarrierSecondRegField::Decode(encoded_data)); - CheckValidReg(holder_reg.GetCode()); - BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip); - // If base_reg differs from holder_reg, the offset was too large and we must have - // emitted an explicit null check before the load. Otherwise, we need to null-check - // the holder as we do not necessarily do that check before going to the thunk. - vixl::aarch32::Label throw_npe; - if (holder_reg.Is(base_reg)) { - __ CompareAndBranchIfZero(holder_reg, &throw_npe, /* is_far_target */ false); - } - vixl::aarch32::Label slow_path; - MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value()); - const int32_t raw_ldr_offset = (width == BakerReadBarrierWidth::kWide) - ? BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET - : BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET; - EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset); - __ Bind(&slow_path); - const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 + - raw_ldr_offset; - Register ep_reg(kBakerCcEntrypointRegister); - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); - if (width == BakerReadBarrierWidth::kWide) { - MemOperand ldr_half_address(lr, ldr_offset + 2); - __ Ldrh(ip, ldr_half_address); // Load the LDR immediate half-word with "Rt | imm12". - __ Ubfx(ip, ip, 0, 12); // Extract the offset imm12. - __ Ldr(ip, MemOperand(base_reg, ip)); // Load the reference. - } else { - MemOperand ldr_address(lr, ldr_offset); - __ Ldrh(ip, ldr_address); // Load the LDR immediate, encoding T1. - __ Add(ep_reg, // Adjust the entrypoint address to the entrypoint - ep_reg, // for narrow LDR. - Operand(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET)); - __ Ubfx(ip, ip, 6, 5); // Extract the imm5, i.e. offset / 4. - __ Ldr(ip, MemOperand(base_reg, ip, LSL, 2)); // Load the reference. - } - // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference. - __ Bx(ep_reg); // Jump to the entrypoint. - if (holder_reg.Is(base_reg)) { - // Add null check slow path. The stack map is at the address pointed to by LR. - __ Bind(&throw_npe); - int32_t offset = GetThreadOffset(kQuickThrowNullPointer).Int32Value(); - __ Ldr(ip, MemOperand(/* Thread* */ vixl::aarch32::r9, offset)); - __ Bx(ip); - } - break; - } - case BakerReadBarrierKind::kArray: { - Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(base_reg.GetCode()); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip); - vixl::aarch32::Label slow_path; - int32_t data_offset = - mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value(); - MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset); - DCHECK_LT(lock_word.GetOffsetImmediate(), 0); - const int32_t raw_ldr_offset = BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET; - EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset); - __ Bind(&slow_path); - const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 + - raw_ldr_offset; - MemOperand ldr_address(lr, ldr_offset + 2); - __ Ldrb(ip, ldr_address); // Load the LDR (register) byte with "00 | imm2 | Rm", - // i.e. Rm+32 because the scale in imm2 is 2. - Register ep_reg(kBakerCcEntrypointRegister); - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); - __ Bfi(ep_reg, ip, 3, 6); // Insert ip to the entrypoint address to create - // a switch case target based on the index register. - __ Mov(ip, base_reg); // Move the base register to ip0. - __ Bx(ep_reg); // Jump to the entrypoint's array switch case. - break; - } - case BakerReadBarrierKind::kGcRoot: { - // Check if the reference needs to be marked and if so (i.e. not null, not marked yet - // and it does not have a forwarding address), call the correct introspection entrypoint; - // otherwise return the reference (or the extracted forwarding address). - // There is no gray bit check for GC roots. - Register root_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(root_reg.GetCode()); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip); - vixl::aarch32::Label return_label, not_marked, forwarding_address; - __ CompareAndBranchIfZero(root_reg, &return_label, /* is_far_target */ false); - MemOperand lock_word(root_reg, mirror::Object::MonitorOffset().Int32Value()); - __ Ldr(ip, lock_word); - __ Tst(ip, LockWord::kMarkBitStateMaskShifted); - __ B(eq, ¬_marked); - __ Bind(&return_label); - __ Bx(lr); - __ Bind(¬_marked); - static_assert(LockWord::kStateShift == 30 && LockWord::kStateForwardingAddress == 3, - "To use 'CMP ip, #modified-immediate; BHS', we need the lock word state in " - " the highest bits and the 'forwarding address' state to have all bits set"); - __ Cmp(ip, Operand(0xc0000000)); - __ B(hs, &forwarding_address); - Register ep_reg(kBakerCcEntrypointRegister); - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); - // Adjust the art_quick_read_barrier_mark_introspection address in kBakerCcEntrypointRegister - // to art_quick_read_barrier_mark_introspection_gc_roots. - int32_t entrypoint_offset = (width == BakerReadBarrierWidth::kWide) - ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET - : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET; - __ Add(ep_reg, ep_reg, Operand(entrypoint_offset)); - __ Mov(ip, root_reg); - __ Bx(ep_reg); - __ Bind(&forwarding_address); - __ Lsl(root_reg, ip, LockWord::kForwardingAddressShift); - __ Bx(lr); - break; - } - default: - LOG(FATAL) << "Unexpected kind: " << static_cast(kind); - UNREACHABLE(); - } -} - -std::vector Thumb2RelativePatcher::CompileThunk(const ThunkKey& key) { - MallocArenaPool pool; - ArenaAllocator allocator(&pool); - arm::ArmVIXLAssembler assembler(&allocator); - - switch (key.GetType()) { - case ThunkType::kMethodCall: - // The thunk just uses the entry point in the ArtMethod. This works even for calls - // to the generic JNI and interpreter trampolines. - assembler.LoadFromOffset( - arm::kLoadWord, - vixl::aarch32::pc, - vixl::aarch32::r0, - ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value()); - __ Bkpt(0); - break; - case ThunkType::kBakerReadBarrier: - CompileBakerReadBarrierThunk(assembler, key.GetCustomValue1()); - break; - } - - assembler.FinalizeCode(); - std::vector thunk_code(assembler.CodeSize()); - MemoryRegion code(thunk_code.data(), thunk_code.size()); - assembler.FinalizeInstructions(code); - return thunk_code; -} - -std::string Thumb2RelativePatcher::GetThunkDebugName(const ThunkKey& key) { - switch (key.GetType()) { - case ThunkType::kMethodCall: - return "MethodCallThunk"; - - case ThunkType::kBakerReadBarrier: { - uint32_t encoded_data = key.GetCustomValue1(); - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - std::ostringstream oss; - oss << "BakerReadBarrierThunk"; - switch (kind) { - case BakerReadBarrierKind::kField: - oss << "Field"; - if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) { - oss << "Wide"; - } - oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data) - << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data); - break; - case BakerReadBarrierKind::kArray: - oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide); - break; - case BakerReadBarrierKind::kGcRoot: - oss << "GcRoot"; - if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) { - oss << "Wide"; - } - oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - break; - } - return oss.str(); - } - } -} - -#undef __ - uint32_t Thumb2RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) { switch (key.GetType()) { case ThunkType::kMethodCall: diff --git a/compiler/linker/arm/relative_patcher_thumb2.h b/compiler/linker/arm/relative_patcher_thumb2.h index 68386c00f4..68610d69e1 100644 --- a/compiler/linker/arm/relative_patcher_thumb2.h +++ b/compiler/linker/arm/relative_patcher_thumb2.h @@ -19,8 +19,6 @@ #include "arch/arm/registers_arm.h" #include "base/array_ref.h" -#include "base/bit_field.h" -#include "base/bit_utils.h" #include "linker/arm/relative_patcher_arm_base.h" namespace art { @@ -33,42 +31,8 @@ namespace linker { class Thumb2RelativePatcher FINAL : public ArmBaseRelativePatcher { public: - static constexpr uint32_t kBakerCcEntrypointRegister = 4u; - - static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, - uint32_t holder_reg, - bool narrow) { - CheckValidReg(base_reg); - CheckValidReg(holder_reg); - DCHECK(!narrow || base_reg < 8u) << base_reg; - BakerReadBarrierWidth width = - narrow ? BakerReadBarrierWidth::kNarrow : BakerReadBarrierWidth::kWide; - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kField) | - BakerReadBarrierFirstRegField::Encode(base_reg) | - BakerReadBarrierSecondRegField::Encode(holder_reg) | - BakerReadBarrierWidthField::Encode(width); - } - - static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { - CheckValidReg(base_reg); - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kArray) | - BakerReadBarrierFirstRegField::Encode(base_reg) | - BakerReadBarrierSecondRegField::Encode(kInvalidEncodedReg) | - BakerReadBarrierWidthField::Encode(BakerReadBarrierWidth::kWide); - } - - static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg, bool narrow) { - CheckValidReg(root_reg); - DCHECK(!narrow || root_reg < 8u) << root_reg; - BakerReadBarrierWidth width = - narrow ? BakerReadBarrierWidth::kNarrow : BakerReadBarrierWidth::kWide; - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kGcRoot) | - BakerReadBarrierFirstRegField::Encode(root_reg) | - BakerReadBarrierSecondRegField::Encode(kInvalidEncodedReg) | - BakerReadBarrierWidthField::Encode(width); - } - - explicit Thumb2RelativePatcher(RelativePatcherTargetProvider* provider); + explicit Thumb2RelativePatcher(RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider); void PatchCall(std::vector* code, uint32_t literal_offset, @@ -83,48 +47,10 @@ class Thumb2RelativePatcher FINAL : public ArmBaseRelativePatcher { uint32_t patch_offset) OVERRIDE; protected: - std::vector CompileThunk(const ThunkKey& key) OVERRIDE; - std::string GetThunkDebugName(const ThunkKey& key) OVERRIDE; uint32_t MaxPositiveDisplacement(const ThunkKey& key) OVERRIDE; uint32_t MaxNegativeDisplacement(const ThunkKey& key) OVERRIDE; private: - static constexpr uint32_t kInvalidEncodedReg = /* pc is invalid */ 15u; - - enum class BakerReadBarrierKind : uint8_t { - kField, // Field get or array get with constant offset (i.e. constant index). - kArray, // Array get with index in register. - kGcRoot, // GC root load. - kLast = kGcRoot - }; - - enum class BakerReadBarrierWidth : uint8_t { - kWide, // 32-bit LDR (and 32-bit NEG if heap poisoning is enabled). - kNarrow, // 16-bit LDR (and 16-bit NEG if heap poisoning is enabled). - kLast = kNarrow - }; - - static constexpr size_t kBitsForBakerReadBarrierKind = - MinimumBitsToStore(static_cast(BakerReadBarrierKind::kLast)); - static constexpr size_t kBitsForRegister = 4u; - using BakerReadBarrierKindField = - BitField; - using BakerReadBarrierFirstRegField = - BitField; - using BakerReadBarrierSecondRegField = - BitField; - static constexpr size_t kBitsForBakerReadBarrierWidth = - MinimumBitsToStore(static_cast(BakerReadBarrierWidth::kLast)); - using BakerReadBarrierWidthField = BitField; - - static void CheckValidReg(uint32_t reg) { - DCHECK(reg < 12u && reg != kBakerCcEntrypointRegister) << reg; - } - - void CompileBakerReadBarrierThunk(arm::ArmVIXLAssembler& assembler, uint32_t encoded_data); - void SetInsn32(std::vector* code, uint32_t offset, uint32_t value); static uint32_t GetInsn32(ArrayRef code, uint32_t offset); diff --git a/compiler/linker/arm/relative_patcher_thumb2_test.cc b/compiler/linker/arm/relative_patcher_thumb2_test.cc index 2c22a352c2..e7b11bd16b 100644 --- a/compiler/linker/arm/relative_patcher_thumb2_test.cc +++ b/compiler/linker/arm/relative_patcher_thumb2_test.cc @@ -16,12 +16,15 @@ #include "linker/arm/relative_patcher_thumb2.h" +#include "arch/arm/instruction_set_features_arm.h" #include "base/casts.h" #include "linker/relative_patcher_test.h" #include "lock_word.h" #include "mirror/array-inl.h" #include "mirror/object.h" #include "oat_quick_method_header.h" +#include "optimizing/code_generator_arm_vixl.h" +#include "optimizing/optimizing_unit_test.h" namespace art { namespace linker { @@ -189,9 +192,42 @@ class Thumb2RelativePatcherTest : public RelativePatcherTest { return result.second - 1 /* thumb mode */; } + std::vector CompileThunk(const LinkerPatch& patch, + /*out*/ std::string* debug_name = nullptr) { + OptimizingUnitTestHelper helper; + HGraph* graph = helper.CreateGraph(); + std::string error_msg; + ArmFeaturesUniquePtr features = + ArmInstructionSetFeatures::FromVariant("default", &error_msg); + CompilerOptions options; + arm::CodeGeneratorARMVIXL codegen(graph, *features, options); + ArenaVector code(helper.GetAllocator()->Adapter()); + codegen.EmitThunkCode(patch, &code, debug_name); + return std::vector(code.begin(), code.end()); + } + + void AddCompiledMethod( + MethodReference method_ref, + const ArrayRef& code, + const ArrayRef& patches = ArrayRef()) { + RelativePatcherTest::AddCompiledMethod(method_ref, code, patches); + + // Make sure the ThunkProvider has all the necessary thunks. + for (const LinkerPatch& patch : patches) { + if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch || + patch.GetType() == LinkerPatch::Type::kCallRelative) { + std::string debug_name; + std::vector thunk_code = CompileThunk(patch, &debug_name); + thunk_provider_.SetThunkCode(patch, ArrayRef(thunk_code), debug_name); + } + } + } + std::vector CompileMethodCallThunk() { - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetMethodCallKey(); - return static_cast(patcher_.get())->CompileThunk(key); + LinkerPatch patch = LinkerPatch::RelativeCodePatch(/* literal_offset */ 0u, + /* target_dex_file*/ nullptr, + /* target_method_idx */ 0u); + return CompileThunk(patch); } uint32_t MethodCallThunkSize() { @@ -228,27 +264,38 @@ class Thumb2RelativePatcherTest : public RelativePatcherTest { void TestStringReference(uint32_t string_offset); void CheckPcRelativePatch(const ArrayRef& patches, uint32_t target_offset); + static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, + uint32_t holder_reg, + bool narrow) { + return arm::CodeGeneratorARMVIXL::EncodeBakerReadBarrierFieldData(base_reg, holder_reg, narrow); + } + + static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { + return arm::CodeGeneratorARMVIXL::EncodeBakerReadBarrierArrayData(base_reg); + } + + static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg, bool narrow) { + return arm::CodeGeneratorARMVIXL::EncodeBakerReadBarrierGcRootData(root_reg, narrow); + } + std::vector CompileBakerOffsetThunk(uint32_t base_reg, uint32_t holder_reg, bool narrow) { const LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - 0u, Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData(base_reg, holder_reg, narrow)); - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); - return down_cast(patcher_.get())->CompileThunk(key); + /* literal_offset */ 0u, EncodeBakerReadBarrierFieldData(base_reg, holder_reg, narrow)); + return CompileThunk(patch); } std::vector CompileBakerArrayThunk(uint32_t base_reg) { LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - 0u, Thumb2RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg)); - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); - return down_cast(patcher_.get())->CompileThunk(key); + /* literal_offset */ 0u, EncodeBakerReadBarrierArrayData(base_reg)); + return CompileThunk(patch); } std::vector CompileBakerGcRootThunk(uint32_t root_reg, bool narrow) { LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - 0u, Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg, narrow)); - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); - return down_cast(patcher_.get())->CompileThunk(key); + /* literal_offset */ 0u, EncodeBakerReadBarrierGcRootData(root_reg, narrow)); + return CompileThunk(patch); } uint32_t GetOutputInsn32(uint32_t offset) { @@ -594,7 +641,7 @@ void Thumb2RelativePatcherTest::TestBakerFieldWide(uint32_t offset, uint32_t ref const std::vector raw_code = RawCode({kBneWPlus0, ldr}); ASSERT_EQ(kMethodCodeSize, raw_code.size()); ArrayRef code(raw_code); - uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = EncodeBakerReadBarrierFieldData( base_reg, holder_reg, /* narrow */ false); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data), @@ -696,7 +743,7 @@ void Thumb2RelativePatcherTest::TestBakerFieldNarrow(uint32_t offset, uint32_t r const std::vector raw_code = RawCode({kBneWPlus0, ldr}); ASSERT_EQ(kMethodCodeSize, raw_code.size()); ArrayRef code(raw_code); - uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = EncodeBakerReadBarrierFieldData( base_reg, holder_reg, /* narrow */ true); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data), @@ -809,7 +856,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddle) { constexpr uint32_t kLiteralOffset1 = 6u; const std::vector raw_code1 = RawCode({kNopWInsn, kNopInsn, kBneWPlus0, kLdrWInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = EncodeBakerReadBarrierFieldData( /* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), @@ -877,7 +924,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkBeforeFiller) { constexpr uint32_t kLiteralOffset1 = 4u; const std::vector raw_code1 = RawCode({kNopWInsn, kBneWPlus0, kLdrWInsn, kNopInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = EncodeBakerReadBarrierFieldData( /* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), @@ -907,7 +954,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddleUnreachableFromLast constexpr uint32_t kLiteralOffset1 = 6u; const std::vector raw_code1 = RawCode({kNopWInsn, kNopInsn, kBneWPlus0, kLdrWInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( + uint32_t encoded_data = EncodeBakerReadBarrierFieldData( /* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), @@ -993,7 +1040,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerArray) { ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, Thumb2RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg)), + kLiteralOffset, EncodeBakerReadBarrierArrayData(base_reg)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1074,8 +1121,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootWide) { ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, - Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ false)), + kLiteralOffset, EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ false)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1134,8 +1180,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootNarrow) { ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, - Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ true)), + kLiteralOffset, EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ true)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1182,8 +1227,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootOffsetBits) { patches.reserve(num_patches); const uint32_t ldr = kLdrWInsn | (/* offset */ 8) | (/* base_reg */ 0 << 16) | (/* root_reg */ 0 << 12); - uint32_t encoded_data = - Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 0, /* narrow */ false); + uint32_t encoded_data = EncodeBakerReadBarrierGcRootData(/* root_reg */ 0, /* narrow */ false); for (size_t i = 0; i != num_patches; ++i) { PushBackInsn(&code, ldr); PushBackInsn(&code, kBneWPlus0); @@ -1264,10 +1308,8 @@ TEST_F(Thumb2RelativePatcherTest, BakerAndMethodCallInteraction) { ldr1, kBneWPlus0, // First GC root LDR with read barrier. ldr2, kBneWPlus0, // Second GC root LDR with read barrier. }); - uint32_t encoded_data1 = - Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 1, /* narrow */ false); - uint32_t encoded_data2 = - Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 2, /* narrow */ false); + uint32_t encoded_data1 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 1, /* narrow */ false); + uint32_t encoded_data2 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 2, /* narrow */ false); const LinkerPatch last_method_patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset1, encoded_data1), LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset2, encoded_data2), diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc index 4bfe99baeb..71d1287c87 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.cc +++ b/compiler/linker/arm64/relative_patcher_arm64.cc @@ -83,9 +83,10 @@ inline uint32_t MaxExtraSpace(size_t num_adrp, size_t code_size) { } // anonymous namespace -Arm64RelativePatcher::Arm64RelativePatcher(RelativePatcherTargetProvider* provider, +Arm64RelativePatcher::Arm64RelativePatcher(RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider, const Arm64InstructionSetFeatures* features) - : ArmBaseRelativePatcher(provider, InstructionSet::kArm64), + : ArmBaseRelativePatcher(thunk_provider, target_provider, InstructionSet::kArm64), fix_cortex_a53_843419_(features->NeedFixCortexA53_843419()), reserved_adrp_thunks_(0u), processed_adrp_thunks_(0u) { @@ -314,44 +315,6 @@ void Arm64RelativePatcher::PatchBakerReadBarrierBranch(std::vector* cod uint32_t insn = GetInsn(code, literal_offset); DCHECK_EQ(insn & 0xffffffe0u, 0xb5000000); // CBNZ Xt, +0 (unpatched) ThunkKey key = GetBakerThunkKey(patch); - if (kIsDebugBuild) { - const uint32_t encoded_data = key.GetCustomValue1(); - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - // Check that the next instruction matches the expected LDR. - switch (kind) { - case BakerReadBarrierKind::kField: { - DCHECK_GE(code->size() - literal_offset, 8u); - uint32_t next_insn = GetInsn(code, literal_offset + 4u); - // LDR (immediate) with correct base_reg. - CheckValidReg(next_insn & 0x1fu); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xffc003e0u, 0xb9400000u | (base_reg << 5)); - break; - } - case BakerReadBarrierKind::kArray: { - DCHECK_GE(code->size() - literal_offset, 8u); - uint32_t next_insn = GetInsn(code, literal_offset + 4u); - // LDR (register) with the correct base_reg, size=10 (32-bit), option=011 (extend = LSL), - // and S=1 (shift amount = 2 for 32-bit version), i.e. LDR Wt, [Xn, Xm, LSL #2]. - CheckValidReg(next_insn & 0x1fu); // Check destination register. - const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(next_insn & 0xffe0ffe0u, 0xb8607800u | (base_reg << 5)); - CheckValidReg((next_insn >> 16) & 0x1f); // Check index register - break; - } - case BakerReadBarrierKind::kGcRoot: { - DCHECK_GE(literal_offset, 4u); - uint32_t prev_insn = GetInsn(code, literal_offset - 4u); - // LDR (immediate) with correct root_reg. - const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); - CHECK_EQ(prev_insn & 0xffc0001fu, 0xb9400000u | root_reg); - break; - } - default: - LOG(FATAL) << "Unexpected kind: " << static_cast(kind); - UNREACHABLE(); - } - } uint32_t target_offset = GetThunkTargetOffset(key, patch_offset); DCHECK_ALIGNED(target_offset, 4u); uint32_t disp = target_offset - patch_offset; @@ -360,216 +323,6 @@ void Arm64RelativePatcher::PatchBakerReadBarrierBranch(std::vector* cod SetInsn(code, literal_offset, insn); } -#define __ assembler.GetVIXLAssembler()-> - -static void EmitGrayCheckAndFastPath(arm64::Arm64Assembler& assembler, - vixl::aarch64::Register base_reg, - vixl::aarch64::MemOperand& lock_word, - vixl::aarch64::Label* slow_path) { - using namespace vixl::aarch64; // NOLINT(build/namespaces) - // Load the lock word containing the rb_state. - __ Ldr(ip0.W(), lock_word); - // Given the numeric representation, it's enough to check the low bit of the rb_state. - static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0"); - static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1"); - __ Tbnz(ip0.W(), LockWord::kReadBarrierStateShift, slow_path); - static_assert( - BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET == BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET, - "Field and array LDR offsets must be the same to reuse the same code."); - // Adjust the return address back to the LDR (1 instruction; 2 for heap poisoning). - static_assert(BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4), - "Field LDR must be 1 instruction (4B) before the return address label; " - " 2 instructions (8B) for heap poisoning."); - __ Add(lr, lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET); - // Introduce a dependency on the lock_word including rb_state, - // to prevent load-load reordering, and without using - // a memory barrier (which would be more expensive). - __ Add(base_reg, base_reg, Operand(ip0, LSR, 32)); - __ Br(lr); // And return back to the function. - // Note: The fake dependency is unnecessary for the slow path. -} - -// Load the read barrier introspection entrypoint in register `entrypoint`. -static void LoadReadBarrierMarkIntrospectionEntrypoint(arm64::Arm64Assembler& assembler, - vixl::aarch64::Register entrypoint) { - using vixl::aarch64::MemOperand; - using vixl::aarch64::ip0; - // Thread Register. - const vixl::aarch64::Register tr = vixl::aarch64::x19; - - // entrypoint = Thread::Current()->pReadBarrierMarkReg16, i.e. pReadBarrierMarkIntrospection. - DCHECK_EQ(ip0.GetCode(), 16u); - const int32_t entry_point_offset = - Thread::ReadBarrierMarkEntryPointsOffset(ip0.GetCode()); - __ Ldr(entrypoint, MemOperand(tr, entry_point_offset)); -} - -void Arm64RelativePatcher::CompileBakerReadBarrierThunk(arm64::Arm64Assembler& assembler, - uint32_t encoded_data) { - using namespace vixl::aarch64; // NOLINT(build/namespaces) - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - switch (kind) { - case BakerReadBarrierKind::kField: { - // Check if the holder is gray and, if not, add fake dependency to the base register - // and return to the LDR instruction to load the reference. Otherwise, use introspection - // to load the reference and call the entrypoint (in IP1) that performs further checks - // on the reference and marks it if needed. - auto base_reg = - Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(base_reg.GetCode()); - auto holder_reg = - Register::GetXRegFromCode(BakerReadBarrierSecondRegField::Decode(encoded_data)); - CheckValidReg(holder_reg.GetCode()); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip0, ip1); - // If base_reg differs from holder_reg, the offset was too large and we must have - // emitted an explicit null check before the load. Otherwise, we need to null-check - // the holder as we do not necessarily do that check before going to the thunk. - vixl::aarch64::Label throw_npe; - if (holder_reg.Is(base_reg)) { - __ Cbz(holder_reg.W(), &throw_npe); - } - vixl::aarch64::Label slow_path; - MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value()); - EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path); - __ Bind(&slow_path); - MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET); - __ Ldr(ip0.W(), ldr_address); // Load the LDR (immediate) unsigned offset. - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); - __ Ubfx(ip0.W(), ip0.W(), 10, 12); // Extract the offset. - __ Ldr(ip0.W(), MemOperand(base_reg, ip0, LSL, 2)); // Load the reference. - // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference. - __ Br(ip1); // Jump to the entrypoint. - if (holder_reg.Is(base_reg)) { - // Add null check slow path. The stack map is at the address pointed to by LR. - __ Bind(&throw_npe); - int32_t offset = GetThreadOffset(kQuickThrowNullPointer).Int32Value(); - __ Ldr(ip0, MemOperand(/* Thread* */ vixl::aarch64::x19, offset)); - __ Br(ip0); - } - break; - } - case BakerReadBarrierKind::kArray: { - auto base_reg = - Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(base_reg.GetCode()); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip0, ip1); - vixl::aarch64::Label slow_path; - int32_t data_offset = - mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value(); - MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset); - DCHECK_LT(lock_word.GetOffset(), 0); - EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path); - __ Bind(&slow_path); - MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET); - __ Ldr(ip0.W(), ldr_address); // Load the LDR (register) unsigned offset. - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); - __ Ubfx(ip0, ip0, 16, 6); // Extract the index register, plus 32 (bit 21 is set). - __ Bfi(ip1, ip0, 3, 6); // Insert ip0 to the entrypoint address to create - // a switch case target based on the index register. - __ Mov(ip0, base_reg); // Move the base register to ip0. - __ Br(ip1); // Jump to the entrypoint's array switch case. - break; - } - case BakerReadBarrierKind::kGcRoot: { - // Check if the reference needs to be marked and if so (i.e. not null, not marked yet - // and it does not have a forwarding address), call the correct introspection entrypoint; - // otherwise return the reference (or the extracted forwarding address). - // There is no gray bit check for GC roots. - auto root_reg = - Register::GetWRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); - CheckValidReg(root_reg.GetCode()); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); - temps.Exclude(ip0, ip1); - vixl::aarch64::Label return_label, not_marked, forwarding_address; - __ Cbz(root_reg, &return_label); - MemOperand lock_word(root_reg.X(), mirror::Object::MonitorOffset().Int32Value()); - __ Ldr(ip0.W(), lock_word); - __ Tbz(ip0.W(), LockWord::kMarkBitStateShift, ¬_marked); - __ Bind(&return_label); - __ Br(lr); - __ Bind(¬_marked); - __ Tst(ip0.W(), Operand(ip0.W(), LSL, 1)); - __ B(&forwarding_address, mi); - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); - // Adjust the art_quick_read_barrier_mark_introspection address in IP1 to - // art_quick_read_barrier_mark_introspection_gc_roots. - __ Add(ip1, ip1, Operand(BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRYPOINT_OFFSET)); - __ Mov(ip0.W(), root_reg); - __ Br(ip1); - __ Bind(&forwarding_address); - __ Lsl(root_reg, ip0.W(), LockWord::kForwardingAddressShift); - __ Br(lr); - break; - } - default: - LOG(FATAL) << "Unexpected kind: " << static_cast(kind); - UNREACHABLE(); - } -} - -std::vector Arm64RelativePatcher::CompileThunk(const ThunkKey& key) { - MallocArenaPool pool; - ArenaAllocator allocator(&pool); - arm64::Arm64Assembler assembler(&allocator); - - switch (key.GetType()) { - case ThunkType::kMethodCall: { - // The thunk just uses the entry point in the ArtMethod. This works even for calls - // to the generic JNI and interpreter trampolines. - Offset offset(ArtMethod::EntryPointFromQuickCompiledCodeOffset( - kArm64PointerSize).Int32Value()); - assembler.JumpTo(ManagedRegister(arm64::X0), offset, ManagedRegister(arm64::IP0)); - break; - } - case ThunkType::kBakerReadBarrier: { - CompileBakerReadBarrierThunk(assembler, key.GetCustomValue1()); - break; - } - } - - // Ensure we emit the literal pool. - assembler.FinalizeCode(); - std::vector thunk_code(assembler.CodeSize()); - MemoryRegion code(thunk_code.data(), thunk_code.size()); - assembler.FinalizeInstructions(code); - return thunk_code; -} - -std::string Arm64RelativePatcher::GetThunkDebugName(const ThunkKey& key) { - switch (key.GetType()) { - case ThunkType::kMethodCall: - return "MethodCallThunk"; - - case ThunkType::kBakerReadBarrier: { - uint32_t encoded_data = key.GetCustomValue1(); - BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); - std::ostringstream oss; - oss << "BakerReadBarrierThunk"; - switch (kind) { - case BakerReadBarrierKind::kField: - oss << "Field_r" << BakerReadBarrierFirstRegField::Decode(encoded_data) - << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data); - break; - case BakerReadBarrierKind::kArray: - oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - break; - case BakerReadBarrierKind::kGcRoot: - oss << "GcRoot_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); - DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data)); - break; - } - return oss.str(); - } - } -} - -#undef __ - uint32_t Arm64RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) { switch (key.GetType()) { case ThunkType::kMethodCall: diff --git a/compiler/linker/arm64/relative_patcher_arm64.h b/compiler/linker/arm64/relative_patcher_arm64.h index 8ba59976e7..9dc289da44 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.h +++ b/compiler/linker/arm64/relative_patcher_arm64.h @@ -18,8 +18,6 @@ #define ART_COMPILER_LINKER_ARM64_RELATIVE_PATCHER_ARM64_H_ #include "base/array_ref.h" -#include "base/bit_field.h" -#include "base/bit_utils.h" #include "linker/arm/relative_patcher_arm_base.h" namespace art { @@ -32,29 +30,8 @@ namespace linker { class Arm64RelativePatcher FINAL : public ArmBaseRelativePatcher { public: - static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, uint32_t holder_reg) { - CheckValidReg(base_reg); - CheckValidReg(holder_reg); - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kField) | - BakerReadBarrierFirstRegField::Encode(base_reg) | - BakerReadBarrierSecondRegField::Encode(holder_reg); - } - - static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { - CheckValidReg(base_reg); - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kArray) | - BakerReadBarrierFirstRegField::Encode(base_reg) | - BakerReadBarrierSecondRegField::Encode(kInvalidEncodedReg); - } - - static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg) { - CheckValidReg(root_reg); - return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kGcRoot) | - BakerReadBarrierFirstRegField::Encode(root_reg) | - BakerReadBarrierSecondRegField::Encode(kInvalidEncodedReg); - } - - Arm64RelativePatcher(RelativePatcherTargetProvider* provider, + Arm64RelativePatcher(RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider, const Arm64InstructionSetFeatures* features); uint32_t ReserveSpace(uint32_t offset, @@ -75,37 +52,10 @@ class Arm64RelativePatcher FINAL : public ArmBaseRelativePatcher { uint32_t patch_offset) OVERRIDE; protected: - std::vector CompileThunk(const ThunkKey& key) OVERRIDE; - std::string GetThunkDebugName(const ThunkKey& key) OVERRIDE; uint32_t MaxPositiveDisplacement(const ThunkKey& key) OVERRIDE; uint32_t MaxNegativeDisplacement(const ThunkKey& key) OVERRIDE; private: - static constexpr uint32_t kInvalidEncodedReg = /* sp/zr is invalid */ 31u; - - enum class BakerReadBarrierKind : uint8_t { - kField, // Field get or array get with constant offset (i.e. constant index). - kArray, // Array get with index in register. - kGcRoot, // GC root load. - kLast = kGcRoot - }; - - static constexpr size_t kBitsForBakerReadBarrierKind = - MinimumBitsToStore(static_cast(BakerReadBarrierKind::kLast)); - static constexpr size_t kBitsForRegister = 5u; - using BakerReadBarrierKindField = - BitField; - using BakerReadBarrierFirstRegField = - BitField; - using BakerReadBarrierSecondRegField = - BitField; - - static void CheckValidReg(uint32_t reg) { - DCHECK(reg < 30u && reg != 16u && reg != 17u) << reg; - } - - void CompileBakerReadBarrierThunk(arm64::Arm64Assembler& assembler, uint32_t encoded_data); - static uint32_t PatchAdrp(uint32_t adrp, uint32_t disp); static bool NeedsErratum843419Thunk(ArrayRef code, uint32_t literal_offset, diff --git a/compiler/linker/arm64/relative_patcher_arm64_test.cc b/compiler/linker/arm64/relative_patcher_arm64_test.cc index 05459a2a82..393733dd0c 100644 --- a/compiler/linker/arm64/relative_patcher_arm64_test.cc +++ b/compiler/linker/arm64/relative_patcher_arm64_test.cc @@ -16,12 +16,15 @@ #include "linker/arm64/relative_patcher_arm64.h" +#include "arch/arm64/instruction_set_features_arm64.h" #include "base/casts.h" #include "linker/relative_patcher_test.h" #include "lock_word.h" #include "mirror/array-inl.h" #include "mirror/object.h" #include "oat_quick_method_header.h" +#include "optimizing/code_generator_arm64.h" +#include "optimizing/optimizing_unit_test.h" namespace art { namespace linker { @@ -168,9 +171,42 @@ class Arm64RelativePatcherTest : public RelativePatcherTest { return result.second; } + std::vector CompileThunk(const LinkerPatch& patch, + /*out*/ std::string* debug_name = nullptr) { + OptimizingUnitTestHelper helper; + HGraph* graph = helper.CreateGraph(); + std::string error_msg; + Arm64FeaturesUniquePtr features = + Arm64InstructionSetFeatures::FromVariant("default", &error_msg); + CompilerOptions options; + arm64::CodeGeneratorARM64 codegen(graph, *features, options); + ArenaVector code(helper.GetAllocator()->Adapter()); + codegen.EmitThunkCode(patch, &code, debug_name); + return std::vector(code.begin(), code.end()); + } + + void AddCompiledMethod( + MethodReference method_ref, + const ArrayRef& code, + const ArrayRef& patches = ArrayRef()) { + RelativePatcherTest::AddCompiledMethod(method_ref, code, patches); + + // Make sure the ThunkProvider has all the necessary thunks. + for (const LinkerPatch& patch : patches) { + if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch || + patch.GetType() == LinkerPatch::Type::kCallRelative) { + std::string debug_name; + std::vector thunk_code = CompileThunk(patch, &debug_name); + thunk_provider_.SetThunkCode(patch, ArrayRef(thunk_code), debug_name); + } + } + } + std::vector CompileMethodCallThunk() { - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetMethodCallKey(); - return down_cast(patcher_.get())->CompileThunk(key); + LinkerPatch patch = LinkerPatch::RelativeCodePatch(/* literal_offset */ 0u, + /* target_dex_file*/ nullptr, + /* target_method_idx */ 0u); + return CompileThunk(patch); } uint32_t MethodCallThunkSize() { @@ -475,25 +511,34 @@ class Arm64RelativePatcherTest : public RelativePatcherTest { TestAdrpInsn2Add(insn2, adrp_offset, has_thunk, string_offset); } + static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, uint32_t holder_reg) { + return arm64::CodeGeneratorARM64::EncodeBakerReadBarrierFieldData(base_reg, holder_reg); + } + + static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { + return arm64::CodeGeneratorARM64::EncodeBakerReadBarrierArrayData(base_reg); + } + + static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg) { + return arm64::CodeGeneratorARM64::EncodeBakerReadBarrierGcRootData(root_reg); + } + std::vector CompileBakerOffsetThunk(uint32_t base_reg, uint32_t holder_reg) { const LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - 0u, Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(base_reg, holder_reg)); - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); - return down_cast(patcher_.get())->CompileThunk(key); + /* literal_offset */ 0u, EncodeBakerReadBarrierFieldData(base_reg, holder_reg)); + return CompileThunk(patch); } std::vector CompileBakerArrayThunk(uint32_t base_reg) { LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - 0u, Arm64RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg)); - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); - return down_cast(patcher_.get())->CompileThunk(key); + /* literal_offset */ 0u, EncodeBakerReadBarrierArrayData(base_reg)); + return CompileThunk(patch); } std::vector CompileBakerGcRootThunk(uint32_t root_reg) { LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch( - 0u, Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg)); - ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch); - return down_cast(patcher_.get())->CompileThunk(key); + /* literal_offset */ 0u, EncodeBakerReadBarrierGcRootData(root_reg)); + return CompileThunk(patch); } uint32_t GetOutputInsn(uint32_t offset) { @@ -919,8 +964,7 @@ void Arm64RelativePatcherTest::TestBakerField(uint32_t offset, uint32_t ref_reg) const std::vector raw_code = RawCode({kCbnzIP1Plus0Insn, ldr}); ASSERT_EQ(kMethodCodeSize, raw_code.size()); ArrayRef code(raw_code); - uint32_t encoded_data = - Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(base_reg, holder_reg); + uint32_t encoded_data = EncodeBakerReadBarrierFieldData(base_reg, holder_reg); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data), }; @@ -1005,8 +1049,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddle) { constexpr uint32_t kLiteralOffset1 = 4; const std::vector raw_code1 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = - Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); + uint32_t encoded_data = EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), }; @@ -1066,8 +1109,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkBeforeFiller) { constexpr uint32_t kLiteralOffset1 = 0; const std::vector raw_code1 = RawCode({kCbnzIP1Plus0Insn, kLdrWInsn, kNopInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = - Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); + uint32_t encoded_data = EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), }; @@ -1096,8 +1138,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddleUnreachableFr constexpr uint32_t kLiteralOffset1 = 4; const std::vector raw_code1 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn}); ArrayRef code1(raw_code1); - uint32_t encoded_data = - Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); + uint32_t encoded_data = EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0); const LinkerPatch patches1[] = { LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data), }; @@ -1170,7 +1211,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerArray) { ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, Arm64RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg)), + kLiteralOffset, EncodeBakerReadBarrierArrayData(base_reg)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1247,7 +1288,7 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerGcRoot) { ArrayRef code(raw_code); const LinkerPatch patches[] = { LinkerPatch::BakerReadBarrierBranchPatch( - kLiteralOffset, Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg)), + kLiteralOffset, EncodeBakerReadBarrierGcRootData(root_reg)), }; AddCompiledMethod(MethodRef(method_idx), code, ArrayRef(patches)); } @@ -1343,8 +1384,8 @@ TEST_F(Arm64RelativePatcherTestDefault, BakerAndMethodCallInteraction) { kNopInsn, kNopInsn, // Padding before second GC root read barrier. ldr2, kCbnzIP1Plus0Insn, // Second GC root LDR with read barrier. }); - uint32_t encoded_data1 = Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 1); - uint32_t encoded_data2 = Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 2); + uint32_t encoded_data1 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 1); + uint32_t encoded_data2 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 2); const LinkerPatch last_method_patches[] = { LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset1, encoded_data1), LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset2, encoded_data2), diff --git a/compiler/linker/linker_patch.h b/compiler/linker/linker_patch.h index 710d8a690a..7b35fd9b0c 100644 --- a/compiler/linker/linker_patch.h +++ b/compiler/linker/linker_patch.h @@ -141,7 +141,7 @@ class LinkerPatch { static LinkerPatch BakerReadBarrierBranchPatch(size_t literal_offset, uint32_t custom_value1 = 0u, uint32_t custom_value2 = 0u) { - LinkerPatch patch(literal_offset, Type::kBakerReadBarrierBranch, nullptr); + LinkerPatch patch(literal_offset, Type::kBakerReadBarrierBranch, /* target_dex_file */ nullptr); patch.baker_custom_value1_ = custom_value1; patch.baker_custom_value2_ = custom_value2; return patch; diff --git a/compiler/linker/relative_patcher.cc b/compiler/linker/relative_patcher.cc index 13877f8f12..b82d15230d 100644 --- a/compiler/linker/relative_patcher.cc +++ b/compiler/linker/relative_patcher.cc @@ -43,7 +43,8 @@ namespace linker { std::unique_ptr RelativePatcher::Create( InstructionSet instruction_set, const InstructionSetFeatures* features, - RelativePatcherTargetProvider* provider) { + RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider) { class RelativePatcherNone FINAL : public RelativePatcher { public: RelativePatcherNone() { } @@ -92,7 +93,8 @@ std::unique_ptr RelativePatcher::Create( }; UNUSED(features); - UNUSED(provider); + UNUSED(thunk_provider); + UNUSED(target_provider); switch (instruction_set) { #ifdef ART_ENABLE_CODEGEN_x86 case InstructionSet::kX86: @@ -106,12 +108,15 @@ std::unique_ptr RelativePatcher::Create( case InstructionSet::kArm: // Fall through: we generate Thumb2 code for "arm". case InstructionSet::kThumb2: - return std::unique_ptr(new Thumb2RelativePatcher(provider)); + return std::unique_ptr( + new Thumb2RelativePatcher(thunk_provider, target_provider)); #endif #ifdef ART_ENABLE_CODEGEN_arm64 case InstructionSet::kArm64: return std::unique_ptr( - new Arm64RelativePatcher(provider, features->AsArm64InstructionSetFeatures())); + new Arm64RelativePatcher(thunk_provider, + target_provider, + features->AsArm64InstructionSetFeatures())); #endif #ifdef ART_ENABLE_CODEGEN_mips case InstructionSet::kMips: diff --git a/compiler/linker/relative_patcher.h b/compiler/linker/relative_patcher.h index b58e3dffbd..06c7e70d23 100644 --- a/compiler/linker/relative_patcher.h +++ b/compiler/linker/relative_patcher.h @@ -38,6 +38,27 @@ namespace linker { class LinkerPatch; class OutputStream; +/** + * @class RelativePatcherThunkProvider + * @brief Interface for providing method offsets for relative call targets. + */ +class RelativePatcherThunkProvider { + public: + /** + * Get the code and debug name of a thunk needed by the given linker patch. + * + * @param patch The patch for which we need to retrieve the thunk code. + * @param code A variable to receive the code of the thunk. This code must not be empty. + * @param debug_name A variable to receive the debug name of the thunk. + */ + virtual void GetThunkCode(const LinkerPatch& patch, + /*out*/ ArrayRef* code, + /*out*/ std::string* debug_name) = 0; + + protected: + virtual ~RelativePatcherThunkProvider() { } +}; + /** * @class RelativePatcherTargetProvider * @brief Interface for providing method offsets for relative call targets. @@ -70,8 +91,10 @@ class RelativePatcherTargetProvider { class RelativePatcher { public: static std::unique_ptr Create( - InstructionSet instruction_set, const InstructionSetFeatures* features, - RelativePatcherTargetProvider* provider); + InstructionSet instruction_set, + const InstructionSetFeatures* features, + RelativePatcherThunkProvider* thunk_provider, + RelativePatcherTargetProvider* target_provider); virtual ~RelativePatcher() { } diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h index d21f2795b9..af8dc4dbc9 100644 --- a/compiler/linker/relative_patcher_test.h +++ b/compiler/linker/relative_patcher_test.h @@ -58,7 +58,10 @@ class RelativePatcherTest : public testing::Test { instruction_set_(instruction_set), features_(InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg_)), method_offset_map_(), - patcher_(RelativePatcher::Create(instruction_set, features_.get(), &method_offset_map_)), + patcher_(RelativePatcher::Create(instruction_set, + features_.get(), + &thunk_provider_, + &method_offset_map_)), bss_begin_(0u), compiled_method_refs_(), compiled_methods_(), @@ -248,6 +251,72 @@ class RelativePatcherTest : public testing::Test { LOG(ERROR) << " " << diff_indicator_str; } + class ThunkProvider : public RelativePatcherThunkProvider { + public: + ThunkProvider() {} + + void SetThunkCode(const LinkerPatch& patch, + ArrayRef code, + const std::string& debug_name) { + thunk_map_.emplace(ThunkKey(patch), ThunkValue(code, debug_name)); + } + + void GetThunkCode(const LinkerPatch& patch, + /*out*/ ArrayRef* code, + /*out*/ std::string* debug_name) OVERRIDE { + auto it = thunk_map_.find(ThunkKey(patch)); + CHECK(it != thunk_map_.end()); + const ThunkValue& value = it->second; + CHECK(code != nullptr); + *code = value.GetCode(); + CHECK(debug_name != nullptr); + *debug_name = value.GetDebugName(); + } + + private: + class ThunkKey { + public: + explicit ThunkKey(const LinkerPatch& patch) + : type_(patch.GetType()), + custom_value1_(patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch + ? patch.GetBakerCustomValue1() : 0u), + custom_value2_(patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch + ? patch.GetBakerCustomValue2() : 0u) { + CHECK(patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch || + patch.GetType() == LinkerPatch::Type::kCallRelative); + } + + bool operator<(const ThunkKey& other) const { + if (custom_value1_ != other.custom_value1_) { + return custom_value1_ < other.custom_value1_; + } + if (custom_value2_ != other.custom_value2_) { + return custom_value2_ < other.custom_value2_; + } + return type_ < other.type_; + } + + private: + const LinkerPatch::Type type_; + const uint32_t custom_value1_; + const uint32_t custom_value2_; + }; + + class ThunkValue { + public: + ThunkValue(ArrayRef code, const std::string& debug_name) + : code_(code.begin(), code.end()), debug_name_(debug_name) {} + ArrayRef GetCode() const { return ArrayRef(code_); } + const std::string& GetDebugName() const { return debug_name_; } + + private: + const std::vector code_; + const std::string debug_name_; + }; + + std::map thunk_map_; + }; + // Map method reference to assinged offset. // Wrap the map in a class implementing RelativePatcherTargetProvider. class MethodOffsetMap FINAL : public RelativePatcherTargetProvider { @@ -272,6 +341,7 @@ class RelativePatcherTest : public testing::Test { std::string error_msg_; InstructionSet instruction_set_; std::unique_ptr features_; + ThunkProvider thunk_provider_; MethodOffsetMap method_offset_map_; std::unique_ptr patcher_; uint32_t bss_begin_; diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index c2ae7646b5..231017f55e 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -449,6 +449,18 @@ void CodeGenerator::EmitLinkerPatches( // No linker patches by default. } +bool CodeGenerator::NeedsThunkCode(const linker::LinkerPatch& patch ATTRIBUTE_UNUSED) const { + // Code generators that create patches requiring thunk compilation should override this function. + return false; +} + +void CodeGenerator::EmitThunkCode(const linker::LinkerPatch& patch ATTRIBUTE_UNUSED, + /*out*/ ArenaVector* code ATTRIBUTE_UNUSED, + /*out*/ std::string* debug_name ATTRIBUTE_UNUSED) { + // Code generators that create patches requiring thunk compilation should override this function. + LOG(FATAL) << "Unexpected call to EmitThunkCode()."; +} + void CodeGenerator::InitializeCodeGeneration(size_t number_of_spill_slots, size_t maximum_safepoint_spill_size, size_t number_of_out_slots, diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index e1f680fb99..62cacebaa1 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -21,6 +21,7 @@ #include "arch/instruction_set_features.h" #include "base/arena_containers.h" #include "base/arena_object.h" +#include "base/array_ref.h" #include "base/bit_field.h" #include "base/bit_utils.h" #include "base/enums.h" @@ -74,6 +75,7 @@ class CodeAllocator { virtual ~CodeAllocator() {} virtual uint8_t* Allocate(size_t size) = 0; + virtual ArrayRef GetMemory() const = 0; private: DISALLOW_COPY_AND_ASSIGN(CodeAllocator); @@ -210,6 +212,10 @@ class CodeGenerator : public DeletableArenaObject { virtual void Initialize() = 0; virtual void Finalize(CodeAllocator* allocator); virtual void EmitLinkerPatches(ArenaVector* linker_patches); + virtual bool NeedsThunkCode(const linker::LinkerPatch& patch) const; + virtual void EmitThunkCode(const linker::LinkerPatch& patch, + /*out*/ ArenaVector* code, + /*out*/ std::string* debug_name); virtual void GenerateFrameEntry() = 0; virtual void GenerateFrameExit() = 0; virtual void Bind(HBasicBlock* block) = 0; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 273346ab4a..31887d92e8 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -30,7 +30,6 @@ #include "heap_poisoning.h" #include "intrinsics.h" #include "intrinsics_arm64.h" -#include "linker/arm64/relative_patcher_arm64.h" #include "linker/linker_patch.h" #include "lock_word.h" #include "mirror/array-inl.h" @@ -1425,6 +1424,62 @@ void CodeGeneratorARM64::Finalize(CodeAllocator* allocator) { __ FinalizeCode(); CodeGenerator::Finalize(allocator); + + // Verify Baker read barrier linker patches. + if (kIsDebugBuild) { + ArrayRef code = allocator->GetMemory(); + for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) { + DCHECK(info.label.IsBound()); + uint32_t literal_offset = info.label.GetLocation(); + DCHECK_ALIGNED(literal_offset, 4u); + + auto GetInsn = [&code](uint32_t offset) { + DCHECK_ALIGNED(offset, 4u); + return + (static_cast(code[offset + 0]) << 0) + + (static_cast(code[offset + 1]) << 8) + + (static_cast(code[offset + 2]) << 16)+ + (static_cast(code[offset + 3]) << 24); + }; + + const uint32_t encoded_data = info.custom_data; + BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); + // Check that the next instruction matches the expected LDR. + switch (kind) { + case BakerReadBarrierKind::kField: { + DCHECK_GE(code.size() - literal_offset, 8u); + uint32_t next_insn = GetInsn(literal_offset + 4u); + // LDR (immediate) with correct base_reg. + CheckValidReg(next_insn & 0x1fu); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xffc003e0u, 0xb9400000u | (base_reg << 5)); + break; + } + case BakerReadBarrierKind::kArray: { + DCHECK_GE(code.size() - literal_offset, 8u); + uint32_t next_insn = GetInsn(literal_offset + 4u); + // LDR (register) with the correct base_reg, size=10 (32-bit), option=011 (extend = LSL), + // and S=1 (shift amount = 2 for 32-bit version), i.e. LDR Wt, [Xn, Xm, LSL #2]. + CheckValidReg(next_insn & 0x1fu); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xffe0ffe0u, 0xb8607800u | (base_reg << 5)); + CheckValidReg((next_insn >> 16) & 0x1f); // Check index register + break; + } + case BakerReadBarrierKind::kGcRoot: { + DCHECK_GE(literal_offset, 4u); + uint32_t prev_insn = GetInsn(literal_offset - 4u); + // LDR (immediate) with correct root_reg. + const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(prev_insn & 0xffc0001fu, 0xb9400000u | root_reg); + break; + } + default: + LOG(FATAL) << "Unexpected kind: " << static_cast(kind); + UNREACHABLE(); + } + } + } } void ParallelMoveResolverARM64::PrepareForEmitNativeCode() { @@ -4814,6 +4869,44 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector* lin DCHECK_EQ(size, linker_patches->size()); } +bool CodeGeneratorARM64::NeedsThunkCode(const linker::LinkerPatch& patch) const { + return patch.GetType() == linker::LinkerPatch::Type::kBakerReadBarrierBranch || + patch.GetType() == linker::LinkerPatch::Type::kCallRelative; +} + +void CodeGeneratorARM64::EmitThunkCode(const linker::LinkerPatch& patch, + /*out*/ ArenaVector* code, + /*out*/ std::string* debug_name) { + Arm64Assembler assembler(GetGraph()->GetAllocator()); + switch (patch.GetType()) { + case linker::LinkerPatch::Type::kCallRelative: { + // The thunk just uses the entry point in the ArtMethod. This works even for calls + // to the generic JNI and interpreter trampolines. + Offset offset(ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kArm64PointerSize).Int32Value()); + assembler.JumpTo(ManagedRegister(arm64::X0), offset, ManagedRegister(arm64::IP0)); + if (GetCompilerOptions().GenerateAnyDebugInfo()) { + *debug_name = "MethodCallThunk"; + } + break; + } + case linker::LinkerPatch::Type::kBakerReadBarrierBranch: { + DCHECK_EQ(patch.GetBakerCustomValue2(), 0u); + CompileBakerReadBarrierThunk(assembler, patch.GetBakerCustomValue1(), debug_name); + break; + } + default: + LOG(FATAL) << "Unexpected patch type " << patch.GetType(); + UNREACHABLE(); + } + + // Ensure we emit the literal pool if any. + assembler.FinalizeCode(); + code->resize(assembler.CodeSize()); + MemoryRegion code_region(code->data(), code->size()); + assembler.FinalizeInstructions(code_region); +} + vixl::aarch64::Literal* CodeGeneratorARM64::DeduplicateUint32Literal(uint32_t value) { return uint32_literals_.GetOrCreate( value, @@ -4954,12 +5047,12 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA DCHECK(!cls->MustGenerateClinitCheck()); // /* GcRoot */ out = current_method->declaring_class_ Register current_method = InputRegisterAt(cls, 0); - GenerateGcRootFieldLoad(cls, - out_loc, - current_method, - ArtMethod::DeclaringClassOffset().Int32Value(), - /* fixup_label */ nullptr, - read_barrier_option); + codegen_->GenerateGcRootFieldLoad(cls, + out_loc, + current_method, + ArtMethod::DeclaringClassOffset().Int32Value(), + /* fixup_label */ nullptr, + read_barrier_option); break; } case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { @@ -5006,12 +5099,12 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA vixl::aarch64::Label* ldr_label = codegen_->NewBssEntryTypePatch(dex_file, type_index, adrp_label); // /* GcRoot */ out = *(base_address + offset) /* PC-relative */ - GenerateGcRootFieldLoad(cls, - out_loc, - temp, - /* offset placeholder */ 0u, - ldr_label, - read_barrier_option); + codegen_->GenerateGcRootFieldLoad(cls, + out_loc, + temp, + /* offset placeholder */ 0u, + ldr_label, + read_barrier_option); generate_null_check = true; break; } @@ -5019,12 +5112,12 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(), cls->GetTypeIndex(), cls->GetClass())); - GenerateGcRootFieldLoad(cls, - out_loc, - out.X(), - /* offset */ 0, - /* fixup_label */ nullptr, - read_barrier_option); + codegen_->GenerateGcRootFieldLoad(cls, + out_loc, + out.X(), + /* offset */ 0, + /* fixup_label */ nullptr, + read_barrier_option); break; } case HLoadClass::LoadKind::kRuntimeCall: @@ -5167,12 +5260,12 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD vixl::aarch64::Label* ldr_label = codegen_->NewStringBssEntryPatch(dex_file, string_index, adrp_label); // /* GcRoot */ out = *(base_address + offset) /* PC-relative */ - GenerateGcRootFieldLoad(load, - out_loc, - temp, - /* offset placeholder */ 0u, - ldr_label, - kCompilerReadBarrierOption); + codegen_->GenerateGcRootFieldLoad(load, + out_loc, + temp, + /* offset placeholder */ 0u, + ldr_label, + kCompilerReadBarrierOption); SlowPathCodeARM64* slow_path = new (codegen_->GetScopedAllocator()) LoadStringSlowPathARM64(load); codegen_->AddSlowPath(slow_path); @@ -5185,12 +5278,12 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(), load->GetStringIndex(), load->GetString())); - GenerateGcRootFieldLoad(load, - out_loc, - out.X(), - /* offset */ 0, - /* fixup_label */ nullptr, - kCompilerReadBarrierOption); + codegen_->GenerateGcRootFieldLoad(load, + out_loc, + out.X(), + /* offset */ 0, + /* fixup_label */ nullptr, + kCompilerReadBarrierOption); return; } default: @@ -6139,7 +6232,7 @@ void InstructionCodeGeneratorARM64::GenerateReferenceLoadTwoRegisters( } } -void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad( +void CodeGeneratorARM64::GenerateGcRootFieldLoad( HInstruction* instruction, Location root, Register obj, @@ -6173,9 +6266,8 @@ void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad( DCHECK(temps.IsAvailable(ip0)); DCHECK(temps.IsAvailable(ip1)); temps.Exclude(ip0, ip1); - uint32_t custom_data = - linker::Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg.GetCode()); - vixl::aarch64::Label* cbnz_label = codegen_->NewBakerReadBarrierPatch(custom_data); + uint32_t custom_data = EncodeBakerReadBarrierGcRootData(root_reg.GetCode()); + vixl::aarch64::Label* cbnz_label = NewBakerReadBarrierPatch(custom_data); EmissionCheckScope guard(GetVIXLAssembler(), 3 * vixl::aarch64::kInstructionSize); vixl::aarch64::Label return_address; @@ -6204,14 +6296,14 @@ void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad( // Slow path marking the GC root `root`. The entrypoint will // be loaded by the slow path code. SlowPathCodeARM64* slow_path = - new (codegen_->GetScopedAllocator()) ReadBarrierMarkSlowPathARM64(instruction, root); - codegen_->AddSlowPath(slow_path); + new (GetScopedAllocator()) ReadBarrierMarkSlowPathARM64(instruction, root); + AddSlowPath(slow_path); // /* GcRoot */ root = *(obj + offset) if (fixup_label == nullptr) { __ Ldr(root_reg, MemOperand(obj, offset)); } else { - codegen_->EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj); + EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj); } static_assert( sizeof(mirror::CompressedReference) == sizeof(GcRoot), @@ -6231,10 +6323,10 @@ void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad( if (fixup_label == nullptr) { __ Add(root_reg.X(), obj.X(), offset); } else { - codegen_->EmitAddPlaceholder(fixup_label, root_reg.X(), obj.X()); + EmitAddPlaceholder(fixup_label, root_reg.X(), obj.X()); } // /* mirror::Object* */ root = root->Read() - codegen_->GenerateReadBarrierForRootSlow(instruction, root, root); + GenerateReadBarrierForRootSlow(instruction, root, root); } } else { // Plain GC root load with no read barrier. @@ -6242,12 +6334,12 @@ void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad( if (fixup_label == nullptr) { __ Ldr(root_reg, MemOperand(obj, offset)); } else { - codegen_->EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj.X()); + EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj.X()); } // Note that GC roots are not affected by heap poisoning, thus we // do not have to unpoison `root_reg` here. } - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__); + MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__); } void CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -6296,9 +6388,7 @@ void CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* ins DCHECK(temps.IsAvailable(ip0)); DCHECK(temps.IsAvailable(ip1)); temps.Exclude(ip0, ip1); - uint32_t custom_data = linker::Arm64RelativePatcher::EncodeBakerReadBarrierFieldData( - base.GetCode(), - obj.GetCode()); + uint32_t custom_data = EncodeBakerReadBarrierFieldData(base.GetCode(), obj.GetCode()); vixl::aarch64::Label* cbnz_label = NewBakerReadBarrierPatch(custom_data); { @@ -6383,8 +6473,7 @@ void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* ins DCHECK(temps.IsAvailable(ip0)); DCHECK(temps.IsAvailable(ip1)); temps.Exclude(ip0, ip1); - uint32_t custom_data = - linker::Arm64RelativePatcher::EncodeBakerReadBarrierArrayData(temp.GetCode()); + uint32_t custom_data = EncodeBakerReadBarrierArrayData(temp.GetCode()); vixl::aarch64::Label* cbnz_label = NewBakerReadBarrierPatch(custom_data); __ Add(temp.X(), obj.X(), Operand(data_offset)); @@ -6744,5 +6833,176 @@ void CodeGeneratorARM64::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_ #undef __ #undef QUICK_ENTRY_POINT +#define __ assembler.GetVIXLAssembler()-> + +static void EmitGrayCheckAndFastPath(arm64::Arm64Assembler& assembler, + vixl::aarch64::Register base_reg, + vixl::aarch64::MemOperand& lock_word, + vixl::aarch64::Label* slow_path) { + // Load the lock word containing the rb_state. + __ Ldr(ip0.W(), lock_word); + // Given the numeric representation, it's enough to check the low bit of the rb_state. + static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0"); + static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1"); + __ Tbnz(ip0.W(), LockWord::kReadBarrierStateShift, slow_path); + static_assert( + BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET == BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET, + "Field and array LDR offsets must be the same to reuse the same code."); + // Adjust the return address back to the LDR (1 instruction; 2 for heap poisoning). + static_assert(BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4), + "Field LDR must be 1 instruction (4B) before the return address label; " + " 2 instructions (8B) for heap poisoning."); + __ Add(lr, lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET); + // Introduce a dependency on the lock_word including rb_state, + // to prevent load-load reordering, and without using + // a memory barrier (which would be more expensive). + __ Add(base_reg, base_reg, Operand(ip0, LSR, 32)); + __ Br(lr); // And return back to the function. + // Note: The fake dependency is unnecessary for the slow path. +} + +// Load the read barrier introspection entrypoint in register `entrypoint`. +static void LoadReadBarrierMarkIntrospectionEntrypoint(arm64::Arm64Assembler& assembler, + vixl::aarch64::Register entrypoint) { + // entrypoint = Thread::Current()->pReadBarrierMarkReg16, i.e. pReadBarrierMarkIntrospection. + DCHECK_EQ(ip0.GetCode(), 16u); + const int32_t entry_point_offset = + Thread::ReadBarrierMarkEntryPointsOffset(ip0.GetCode()); + __ Ldr(entrypoint, MemOperand(tr, entry_point_offset)); +} + +void CodeGeneratorARM64::CompileBakerReadBarrierThunk(Arm64Assembler& assembler, + uint32_t encoded_data, + /*out*/ std::string* debug_name) { + BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); + switch (kind) { + case BakerReadBarrierKind::kField: { + // Check if the holder is gray and, if not, add fake dependency to the base register + // and return to the LDR instruction to load the reference. Otherwise, use introspection + // to load the reference and call the entrypoint (in IP1) that performs further checks + // on the reference and marks it if needed. + auto base_reg = + Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(base_reg.GetCode()); + auto holder_reg = + Register::GetXRegFromCode(BakerReadBarrierSecondRegField::Decode(encoded_data)); + CheckValidReg(holder_reg.GetCode()); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip0, ip1); + // If base_reg differs from holder_reg, the offset was too large and we must have + // emitted an explicit null check before the load. Otherwise, we need to null-check + // the holder as we do not necessarily do that check before going to the thunk. + vixl::aarch64::Label throw_npe; + if (holder_reg.Is(base_reg)) { + __ Cbz(holder_reg.W(), &throw_npe); + } + vixl::aarch64::Label slow_path; + MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value()); + EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path); + __ Bind(&slow_path); + MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET); + __ Ldr(ip0.W(), ldr_address); // Load the LDR (immediate) unsigned offset. + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); + __ Ubfx(ip0.W(), ip0.W(), 10, 12); // Extract the offset. + __ Ldr(ip0.W(), MemOperand(base_reg, ip0, LSL, 2)); // Load the reference. + // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference. + __ Br(ip1); // Jump to the entrypoint. + if (holder_reg.Is(base_reg)) { + // Add null check slow path. The stack map is at the address pointed to by LR. + __ Bind(&throw_npe); + int32_t offset = GetThreadOffset(kQuickThrowNullPointer).Int32Value(); + __ Ldr(ip0, MemOperand(/* Thread* */ vixl::aarch64::x19, offset)); + __ Br(ip0); + } + break; + } + case BakerReadBarrierKind::kArray: { + auto base_reg = + Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(base_reg.GetCode()); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip0, ip1); + vixl::aarch64::Label slow_path; + int32_t data_offset = + mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value(); + MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset); + DCHECK_LT(lock_word.GetOffset(), 0); + EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path); + __ Bind(&slow_path); + MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET); + __ Ldr(ip0.W(), ldr_address); // Load the LDR (register) unsigned offset. + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); + __ Ubfx(ip0, ip0, 16, 6); // Extract the index register, plus 32 (bit 21 is set). + __ Bfi(ip1, ip0, 3, 6); // Insert ip0 to the entrypoint address to create + // a switch case target based on the index register. + __ Mov(ip0, base_reg); // Move the base register to ip0. + __ Br(ip1); // Jump to the entrypoint's array switch case. + break; + } + case BakerReadBarrierKind::kGcRoot: { + // Check if the reference needs to be marked and if so (i.e. not null, not marked yet + // and it does not have a forwarding address), call the correct introspection entrypoint; + // otherwise return the reference (or the extracted forwarding address). + // There is no gray bit check for GC roots. + auto root_reg = + Register::GetWRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(root_reg.GetCode()); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip0, ip1); + vixl::aarch64::Label return_label, not_marked, forwarding_address; + __ Cbz(root_reg, &return_label); + MemOperand lock_word(root_reg.X(), mirror::Object::MonitorOffset().Int32Value()); + __ Ldr(ip0.W(), lock_word); + __ Tbz(ip0.W(), LockWord::kMarkBitStateShift, ¬_marked); + __ Bind(&return_label); + __ Br(lr); + __ Bind(¬_marked); + __ Tst(ip0.W(), Operand(ip0.W(), LSL, 1)); + __ B(&forwarding_address, mi); + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1); + // Adjust the art_quick_read_barrier_mark_introspection address in IP1 to + // art_quick_read_barrier_mark_introspection_gc_roots. + __ Add(ip1, ip1, Operand(BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRYPOINT_OFFSET)); + __ Mov(ip0.W(), root_reg); + __ Br(ip1); + __ Bind(&forwarding_address); + __ Lsl(root_reg, ip0.W(), LockWord::kForwardingAddressShift); + __ Br(lr); + break; + } + default: + LOG(FATAL) << "Unexpected kind: " << static_cast(kind); + UNREACHABLE(); + } + + if (GetCompilerOptions().GenerateAnyDebugInfo()) { + std::ostringstream oss; + oss << "BakerReadBarrierThunk"; + switch (kind) { + case BakerReadBarrierKind::kField: + oss << "Field_r" << BakerReadBarrierFirstRegField::Decode(encoded_data) + << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data); + break; + case BakerReadBarrierKind::kArray: + oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + break; + case BakerReadBarrierKind::kGcRoot: + oss << "GcRoot_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + break; + } + *debug_name = oss.str(); + } +} + +#undef __ + } // namespace arm64 } // namespace art diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 6a52eecbd3..aa343b1185 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -18,6 +18,7 @@ #define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_ #include "arch/arm64/quick_method_frame_info_arm64.h" +#include "base/bit_field.h" #include "code_generator.h" #include "common_arm64.h" #include "dex/dex_file_types.h" @@ -36,6 +37,11 @@ #pragma GCC diagnostic pop namespace art { + +namespace linker { +class Arm64RelativePatcherTest; +} // namespace linker + namespace arm64 { class CodeGeneratorARM64; @@ -309,17 +315,6 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { uint32_t offset, Location maybe_temp, ReadBarrierOption read_barrier_option); - // Generate a GC root reference load: - // - // root <- *(obj + offset) - // - // while honoring read barriers based on read_barrier_option. - void GenerateGcRootFieldLoad(HInstruction* instruction, - Location root, - vixl::aarch64::Register obj, - uint32_t offset, - vixl::aarch64::Label* fixup_label, - ReadBarrierOption read_barrier_option); // Generate a floating-point comparison. void GenerateFcmp(HInstruction* instruction); @@ -641,9 +636,24 @@ class CodeGeneratorARM64 : public CodeGenerator { vixl::aarch64::Register base); void EmitLinkerPatches(ArenaVector* linker_patches) OVERRIDE; + bool NeedsThunkCode(const linker::LinkerPatch& patch) const OVERRIDE; + void EmitThunkCode(const linker::LinkerPatch& patch, + /*out*/ ArenaVector* code, + /*out*/ std::string* debug_name) OVERRIDE; void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; + // Generate a GC root reference load: + // + // root <- *(obj + offset) + // + // while honoring read barriers based on read_barrier_option. + void GenerateGcRootFieldLoad(HInstruction* instruction, + Location root, + vixl::aarch64::Register obj, + uint32_t offset, + vixl::aarch64::Label* fixup_label, + ReadBarrierOption read_barrier_option); // Fast path implementation of ReadBarrier::Barrier for a heap // reference field load when Baker's read barriers are used. void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -778,6 +788,62 @@ class CodeGeneratorARM64 : public CodeGenerator { void GenerateExplicitNullCheck(HNullCheck* instruction) OVERRIDE; private: + // Encoding of thunk type and data for link-time generated thunks for Baker read barriers. + + enum class BakerReadBarrierKind : uint8_t { + kField, // Field get or array get with constant offset (i.e. constant index). + kArray, // Array get with index in register. + kGcRoot, // GC root load. + kLast = kGcRoot + }; + + static constexpr uint32_t kBakerReadBarrierInvalidEncodedReg = /* sp/zr is invalid */ 31u; + + static constexpr size_t kBitsForBakerReadBarrierKind = + MinimumBitsToStore(static_cast(BakerReadBarrierKind::kLast)); + static constexpr size_t kBakerReadBarrierBitsForRegister = + MinimumBitsToStore(kBakerReadBarrierInvalidEncodedReg); + using BakerReadBarrierKindField = + BitField; + using BakerReadBarrierFirstRegField = + BitField; + using BakerReadBarrierSecondRegField = + BitField; + + static void CheckValidReg(uint32_t reg) { + DCHECK(reg < vixl::aarch64::lr.GetCode() && + reg != vixl::aarch64::ip0.GetCode() && + reg != vixl::aarch64::ip1.GetCode()) << reg; + } + + static inline uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, uint32_t holder_reg) { + CheckValidReg(base_reg); + CheckValidReg(holder_reg); + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kField) | + BakerReadBarrierFirstRegField::Encode(base_reg) | + BakerReadBarrierSecondRegField::Encode(holder_reg); + } + + static inline uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { + CheckValidReg(base_reg); + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kArray) | + BakerReadBarrierFirstRegField::Encode(base_reg) | + BakerReadBarrierSecondRegField::Encode(kBakerReadBarrierInvalidEncodedReg); + } + + static inline uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg) { + CheckValidReg(root_reg); + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kGcRoot) | + BakerReadBarrierFirstRegField::Encode(root_reg) | + BakerReadBarrierSecondRegField::Encode(kBakerReadBarrierInvalidEncodedReg); + } + + void CompileBakerReadBarrierThunk(Arm64Assembler& assembler, + uint32_t encoded_data, + /*out*/ std::string* debug_name); + using Uint64ToLiteralMap = ArenaSafeMap*>; using Uint32ToLiteralMap = ArenaSafeMap*>; using StringToLiteralMap = ArenaSafeMapIsAvailable(ip)); temps->Exclude(ip); DCHECK(!temps->IsAvailable(kBakerCcEntrypointRegister)); - DCHECK_EQ(kBakerCcEntrypointRegister.GetCode(), - linker::Thumb2RelativePatcher::kBakerCcEntrypointRegister); DCHECK_NE(instruction->GetLocations()->GetTempCount(), 0u); DCHECK(RegisterFrom(instruction->GetLocations()->GetTemp( instruction->GetLocations()->GetTempCount() - 1u)).Is(kBakerCcEntrypointRegister)); @@ -2422,6 +2417,80 @@ void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) { FixJumpTables(); GetAssembler()->FinalizeCode(); CodeGenerator::Finalize(allocator); + + // Verify Baker read barrier linker patches. + if (kIsDebugBuild) { + ArrayRef code = allocator->GetMemory(); + for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) { + DCHECK(info.label.IsBound()); + uint32_t literal_offset = info.label.GetLocation(); + DCHECK_ALIGNED(literal_offset, 2u); + + auto GetInsn16 = [&code](uint32_t offset) { + DCHECK_ALIGNED(offset, 2u); + return (static_cast(code[offset + 0]) << 0) + + (static_cast(code[offset + 1]) << 8); + }; + auto GetInsn32 = [=](uint32_t offset) { + return (GetInsn16(offset) << 16) + (GetInsn16(offset + 2u) << 0); + }; + + uint32_t encoded_data = info.custom_data; + BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); + // Check that the next instruction matches the expected LDR. + switch (kind) { + case BakerReadBarrierKind::kField: { + BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); + if (width == BakerReadBarrierWidth::kWide) { + DCHECK_GE(code.size() - literal_offset, 8u); + uint32_t next_insn = GetInsn32(literal_offset + 4u); + // LDR (immediate), encoding T3, with correct base_reg. + CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xffff0000u, 0xf8d00000u | (base_reg << 16)); + } else { + DCHECK_GE(code.size() - literal_offset, 6u); + uint32_t next_insn = GetInsn16(literal_offset + 4u); + // LDR (immediate), encoding T1, with correct base_reg. + CheckValidReg(next_insn & 0x7u); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xf838u, 0x6800u | (base_reg << 3)); + } + break; + } + case BakerReadBarrierKind::kArray: { + DCHECK_GE(code.size() - literal_offset, 8u); + uint32_t next_insn = GetInsn32(literal_offset + 4u); + // LDR (register) with correct base_reg, S=1 and option=011 (LDR Wt, [Xn, Xm, LSL #2]). + CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register. + const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(next_insn & 0xffff0ff0u, 0xf8500020u | (base_reg << 16)); + CheckValidReg(next_insn & 0xf); // Check index register + break; + } + case BakerReadBarrierKind::kGcRoot: { + BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); + if (width == BakerReadBarrierWidth::kWide) { + DCHECK_GE(literal_offset, 4u); + uint32_t prev_insn = GetInsn32(literal_offset - 4u); + // LDR (immediate), encoding T3, with correct root_reg. + const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(prev_insn & 0xfff0f000u, 0xf8d00000u | (root_reg << 12)); + } else { + DCHECK_GE(literal_offset, 2u); + uint32_t prev_insn = GetInsn16(literal_offset - 2u); + // LDR (immediate), encoding T1, with correct root_reg. + const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data); + CHECK_EQ(prev_insn & 0xf807u, 0x6800u | root_reg); + } + break; + } + default: + LOG(FATAL) << "Unexpected kind: " << static_cast(kind); + UNREACHABLE(); + } + } + } } void CodeGeneratorARMVIXL::SetupBlockedRegisters() const { @@ -7413,11 +7482,11 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ DCHECK(!cls->MustGenerateClinitCheck()); // /* GcRoot */ out = current_method->declaring_class_ vixl32::Register current_method = InputRegisterAt(cls, 0); - GenerateGcRootFieldLoad(cls, - out_loc, - current_method, - ArtMethod::DeclaringClassOffset().Int32Value(), - read_barrier_option); + codegen_->GenerateGcRootFieldLoad(cls, + out_loc, + current_method, + ArtMethod::DeclaringClassOffset().Int32Value(), + read_barrier_option); break; } case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { @@ -7448,7 +7517,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); codegen_->EmitMovwMovtPlaceholder(labels, out); - GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); + codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); generate_null_check = true; break; } @@ -7457,7 +7526,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ cls->GetTypeIndex(), cls->GetClass())); // /* GcRoot */ out = *out - GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); + codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); break; } case HLoadClass::LoadKind::kRuntimeCall: @@ -7665,7 +7734,8 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex()); codegen_->EmitMovwMovtPlaceholder(labels, out); - GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); + codegen_->GenerateGcRootFieldLoad( + load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); LoadStringSlowPathARMVIXL* slow_path = new (codegen_->GetScopedAllocator()) LoadStringSlowPathARMVIXL(load); codegen_->AddSlowPath(slow_path); @@ -7679,7 +7749,8 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE load->GetStringIndex(), load->GetString())); // /* GcRoot */ out = *out - GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); + codegen_->GenerateGcRootFieldLoad( + load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); return; } default: @@ -8730,7 +8801,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadTwoRegisters( } } -void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( +void CodeGeneratorARMVIXL::GenerateGcRootFieldLoad( HInstruction* instruction, Location root, vixl32::Register obj, @@ -8761,9 +8832,8 @@ void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( UseScratchRegisterScope temps(GetVIXLAssembler()); ExcludeIPAndBakerCcEntrypointRegister(&temps, instruction); bool narrow = CanEmitNarrowLdr(root_reg, obj, offset); - uint32_t custom_data = linker::Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData( - root_reg.GetCode(), narrow); - vixl32::Label* bne_label = codegen_->NewBakerReadBarrierPatch(custom_data); + uint32_t custom_data = EncodeBakerReadBarrierGcRootData(root_reg.GetCode(), narrow); + vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data); vixl::EmissionCheckScope guard(GetVIXLAssembler(), 4 * vixl32::kMaxInstructionSizeInBytes); vixl32::Label return_address; @@ -8774,7 +8844,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( DCHECK_LT(offset, kReferenceLoadMinFarOffset); ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset(); __ ldr(EncodingSize(narrow ? Narrow : Wide), root_reg, MemOperand(obj, offset)); - EmitPlaceholderBne(codegen_, bne_label); + EmitPlaceholderBne(this, bne_label); __ Bind(&return_address); DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(), narrow ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET @@ -8794,8 +8864,8 @@ void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( // Slow path marking the GC root `root`. The entrypoint will // be loaded by the slow path code. SlowPathCodeARMVIXL* slow_path = - new (codegen_->GetScopedAllocator()) ReadBarrierMarkSlowPathARMVIXL(instruction, root); - codegen_->AddSlowPath(slow_path); + new (GetScopedAllocator()) ReadBarrierMarkSlowPathARMVIXL(instruction, root); + AddSlowPath(slow_path); // /* GcRoot */ root = *(obj + offset) GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset); @@ -8816,7 +8886,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( // /* GcRoot* */ root = obj + offset __ Add(root_reg, obj, offset); // /* mirror::Object* */ root = root->Read() - codegen_->GenerateReadBarrierForRootSlow(instruction, root, root); + GenerateReadBarrierForRootSlow(instruction, root, root); } } else { // Plain GC root load with no read barrier. @@ -8825,7 +8895,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( // Note that GC roots are not affected by heap poisoning, thus we // do not have to unpoison `root_reg` here. } - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 18); + MaybeGenerateMarkingRegisterCheck(/* code */ 18); } void CodeGeneratorARMVIXL::MaybeAddBakerCcEntrypointTempForFields(LocationSummary* locations) { @@ -8886,8 +8956,7 @@ void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* i } UseScratchRegisterScope temps(GetVIXLAssembler()); ExcludeIPAndBakerCcEntrypointRegister(&temps, instruction); - uint32_t custom_data = linker::Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData( - base.GetCode(), obj.GetCode(), narrow); + uint32_t custom_data = EncodeBakerReadBarrierFieldData(base.GetCode(), obj.GetCode(), narrow); vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data); { @@ -8973,8 +9042,7 @@ void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(HInstruction* i UseScratchRegisterScope temps(GetVIXLAssembler()); ExcludeIPAndBakerCcEntrypointRegister(&temps, instruction); - uint32_t custom_data = - linker::Thumb2RelativePatcher::EncodeBakerReadBarrierArrayData(data_reg.GetCode()); + uint32_t custom_data = EncodeBakerReadBarrierArrayData(data_reg.GetCode()); vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data); __ Add(data_reg, obj, Operand(data_offset)); @@ -9111,7 +9179,7 @@ void CodeGeneratorARMVIXL::UpdateReferenceFieldWithBakerReadBarrier(HInstruction void CodeGeneratorARMVIXL::GenerateRawReferenceLoad(HInstruction* instruction, Location ref, - vixl::aarch32::Register obj, + vixl32::Register obj, uint32_t offset, Location index, ScaleFactor scale_factor, @@ -9451,7 +9519,7 @@ CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativePa return &patches->back(); } -vixl::aarch32::Label* CodeGeneratorARMVIXL::NewBakerReadBarrierPatch(uint32_t custom_data) { +vixl32::Label* CodeGeneratorARMVIXL::NewBakerReadBarrierPatch(uint32_t custom_data) { baker_read_barrier_patches_.emplace_back(custom_data); return &baker_read_barrier_patches_.back().label; } @@ -9548,6 +9616,45 @@ void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector* l DCHECK_EQ(size, linker_patches->size()); } +bool CodeGeneratorARMVIXL::NeedsThunkCode(const linker::LinkerPatch& patch) const { + return patch.GetType() == linker::LinkerPatch::Type::kBakerReadBarrierBranch || + patch.GetType() == linker::LinkerPatch::Type::kCallRelative; +} + +void CodeGeneratorARMVIXL::EmitThunkCode(const linker::LinkerPatch& patch, + /*out*/ ArenaVector* code, + /*out*/ std::string* debug_name) { + arm::ArmVIXLAssembler assembler(GetGraph()->GetAllocator()); + switch (patch.GetType()) { + case linker::LinkerPatch::Type::kCallRelative: + // The thunk just uses the entry point in the ArtMethod. This works even for calls + // to the generic JNI and interpreter trampolines. + assembler.LoadFromOffset( + arm::kLoadWord, + vixl32::pc, + vixl32::r0, + ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value()); + assembler.GetVIXLAssembler()->Bkpt(0); + if (GetCompilerOptions().GenerateAnyDebugInfo()) { + *debug_name = "MethodCallThunk"; + } + break; + case linker::LinkerPatch::Type::kBakerReadBarrierBranch: + DCHECK_EQ(patch.GetBakerCustomValue2(), 0u); + CompileBakerReadBarrierThunk(assembler, patch.GetBakerCustomValue1(), debug_name); + break; + default: + LOG(FATAL) << "Unexpected patch type " << patch.GetType(); + UNREACHABLE(); + } + + // Ensure we emit the literal pool if any. + assembler.FinalizeCode(); + code->resize(assembler.CodeSize()); + MemoryRegion code_region(code->data(), code->size()); + assembler.FinalizeInstructions(code_region); +} + VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateUint32Literal( uint32_t value, Uint32ToLiteralMap* map) { @@ -9792,5 +9899,210 @@ void CodeGeneratorARMVIXL::EmitMovwMovtPlaceholder( #undef QUICK_ENTRY_POINT #undef TODO_VIXL32 +#define __ assembler.GetVIXLAssembler()-> + +static void EmitGrayCheckAndFastPath(ArmVIXLAssembler& assembler, + vixl32::Register base_reg, + vixl32::MemOperand& lock_word, + vixl32::Label* slow_path, + int32_t raw_ldr_offset) { + // Load the lock word containing the rb_state. + __ Ldr(ip, lock_word); + // Given the numeric representation, it's enough to check the low bit of the rb_state. + static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0"); + static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1"); + __ Tst(ip, Operand(LockWord::kReadBarrierStateMaskShifted)); + __ B(ne, slow_path, /* is_far_target */ false); + __ Add(lr, lr, raw_ldr_offset); + // Introduce a dependency on the lock_word including rb_state, + // to prevent load-load reordering, and without using + // a memory barrier (which would be more expensive). + __ Add(base_reg, base_reg, Operand(ip, LSR, 32)); + __ Bx(lr); // And return back to the function. + // Note: The fake dependency is unnecessary for the slow path. +} + +// Load the read barrier introspection entrypoint in register `entrypoint` +static void LoadReadBarrierMarkIntrospectionEntrypoint(ArmVIXLAssembler& assembler, + vixl32::Register entrypoint) { + // The register where the read barrier introspection entrypoint is loaded + // is fixed: `Thumb2RelativePatcher::kBakerCcEntrypointRegister` (R4). + DCHECK(entrypoint.Is(kBakerCcEntrypointRegister)); + // entrypoint = Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection. + DCHECK_EQ(ip.GetCode(), 12u); + const int32_t entry_point_offset = + Thread::ReadBarrierMarkEntryPointsOffset(ip.GetCode()); + __ Ldr(entrypoint, MemOperand(tr, entry_point_offset)); +} + +void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assembler, + uint32_t encoded_data, + /*out*/ std::string* debug_name) { + BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); + switch (kind) { + case BakerReadBarrierKind::kField: { + // Check if the holder is gray and, if not, add fake dependency to the base register + // and return to the LDR instruction to load the reference. Otherwise, use introspection + // to load the reference and call the entrypoint (in kBakerCcEntrypointRegister) + // that performs further checks on the reference and marks it if needed. + vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(base_reg.GetCode()); + vixl32::Register holder_reg(BakerReadBarrierSecondRegField::Decode(encoded_data)); + CheckValidReg(holder_reg.GetCode()); + BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip); + // If base_reg differs from holder_reg, the offset was too large and we must have + // emitted an explicit null check before the load. Otherwise, we need to null-check + // the holder as we do not necessarily do that check before going to the thunk. + vixl32::Label throw_npe; + if (holder_reg.Is(base_reg)) { + __ CompareAndBranchIfZero(holder_reg, &throw_npe, /* is_far_target */ false); + } + vixl32::Label slow_path; + MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value()); + const int32_t raw_ldr_offset = (width == BakerReadBarrierWidth::kWide) + ? BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET + : BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET; + EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset); + __ Bind(&slow_path); + const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 + + raw_ldr_offset; + vixl32::Register ep_reg(kBakerCcEntrypointRegister); + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); + if (width == BakerReadBarrierWidth::kWide) { + MemOperand ldr_half_address(lr, ldr_offset + 2); + __ Ldrh(ip, ldr_half_address); // Load the LDR immediate half-word with "Rt | imm12". + __ Ubfx(ip, ip, 0, 12); // Extract the offset imm12. + __ Ldr(ip, MemOperand(base_reg, ip)); // Load the reference. + } else { + MemOperand ldr_address(lr, ldr_offset); + __ Ldrh(ip, ldr_address); // Load the LDR immediate, encoding T1. + __ Add(ep_reg, // Adjust the entrypoint address to the entrypoint + ep_reg, // for narrow LDR. + Operand(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET)); + __ Ubfx(ip, ip, 6, 5); // Extract the imm5, i.e. offset / 4. + __ Ldr(ip, MemOperand(base_reg, ip, LSL, 2)); // Load the reference. + } + // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference. + __ Bx(ep_reg); // Jump to the entrypoint. + if (holder_reg.Is(base_reg)) { + // Add null check slow path. The stack map is at the address pointed to by LR. + __ Bind(&throw_npe); + int32_t offset = GetThreadOffset(kQuickThrowNullPointer).Int32Value(); + __ Ldr(ip, MemOperand(/* Thread* */ vixl32::r9, offset)); + __ Bx(ip); + } + break; + } + case BakerReadBarrierKind::kArray: { + vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(base_reg.GetCode()); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip); + vixl32::Label slow_path; + int32_t data_offset = + mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value(); + MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset); + DCHECK_LT(lock_word.GetOffsetImmediate(), 0); + const int32_t raw_ldr_offset = BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET; + EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset); + __ Bind(&slow_path); + const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 + + raw_ldr_offset; + MemOperand ldr_address(lr, ldr_offset + 2); + __ Ldrb(ip, ldr_address); // Load the LDR (register) byte with "00 | imm2 | Rm", + // i.e. Rm+32 because the scale in imm2 is 2. + vixl32::Register ep_reg(kBakerCcEntrypointRegister); + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); + __ Bfi(ep_reg, ip, 3, 6); // Insert ip to the entrypoint address to create + // a switch case target based on the index register. + __ Mov(ip, base_reg); // Move the base register to ip0. + __ Bx(ep_reg); // Jump to the entrypoint's array switch case. + break; + } + case BakerReadBarrierKind::kGcRoot: { + // Check if the reference needs to be marked and if so (i.e. not null, not marked yet + // and it does not have a forwarding address), call the correct introspection entrypoint; + // otherwise return the reference (or the extracted forwarding address). + // There is no gray bit check for GC roots. + vixl32::Register root_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); + CheckValidReg(root_reg.GetCode()); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); + UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); + temps.Exclude(ip); + vixl32::Label return_label, not_marked, forwarding_address; + __ CompareAndBranchIfZero(root_reg, &return_label, /* is_far_target */ false); + MemOperand lock_word(root_reg, mirror::Object::MonitorOffset().Int32Value()); + __ Ldr(ip, lock_word); + __ Tst(ip, LockWord::kMarkBitStateMaskShifted); + __ B(eq, ¬_marked); + __ Bind(&return_label); + __ Bx(lr); + __ Bind(¬_marked); + static_assert(LockWord::kStateShift == 30 && LockWord::kStateForwardingAddress == 3, + "To use 'CMP ip, #modified-immediate; BHS', we need the lock word state in " + " the highest bits and the 'forwarding address' state to have all bits set"); + __ Cmp(ip, Operand(0xc0000000)); + __ B(hs, &forwarding_address); + vixl32::Register ep_reg(kBakerCcEntrypointRegister); + LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); + // Adjust the art_quick_read_barrier_mark_introspection address in kBakerCcEntrypointRegister + // to art_quick_read_barrier_mark_introspection_gc_roots. + int32_t entrypoint_offset = (width == BakerReadBarrierWidth::kWide) + ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET + : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET; + __ Add(ep_reg, ep_reg, Operand(entrypoint_offset)); + __ Mov(ip, root_reg); + __ Bx(ep_reg); + __ Bind(&forwarding_address); + __ Lsl(root_reg, ip, LockWord::kForwardingAddressShift); + __ Bx(lr); + break; + } + default: + LOG(FATAL) << "Unexpected kind: " << static_cast(kind); + UNREACHABLE(); + } + + if (GetCompilerOptions().GenerateAnyDebugInfo()) { + std::ostringstream oss; + oss << "BakerReadBarrierThunk"; + switch (kind) { + case BakerReadBarrierKind::kField: + oss << "Field"; + if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) { + oss << "Wide"; + } + oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data) + << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data); + break; + case BakerReadBarrierKind::kArray: + oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide); + break; + case BakerReadBarrierKind::kGcRoot: + oss << "GcRoot"; + if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) { + oss << "Wide"; + } + oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data); + DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg, + BakerReadBarrierSecondRegField::Decode(encoded_data)); + break; + } + *debug_name = oss.str(); + } +} + +#undef __ + } // namespace arm } // namespace art diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 2114ea1ba1..6b9919ab15 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -36,6 +36,11 @@ #pragma GCC diagnostic pop namespace art { + +namespace linker { +class Thumb2RelativePatcherTest; +} // namespace linker + namespace arm { // This constant is used as an approximate margin when emission of veneer and literal pools @@ -108,6 +113,9 @@ static const vixl::aarch32::SRegister kRuntimeParameterFpuRegistersVIXL[] = { static const size_t kRuntimeParameterFpuRegistersLengthVIXL = arraysize(kRuntimeParameterFpuRegistersVIXL); +// The reserved entrypoint register for link-time generated thunks. +const vixl::aarch32::Register kBakerCcEntrypointRegister = vixl32::r4; + class LoadClassSlowPathARMVIXL; class CodeGeneratorARMVIXL; @@ -388,16 +396,6 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { uint32_t offset, Location maybe_temp, ReadBarrierOption read_barrier_option); - // Generate a GC root reference load: - // - // root <- *(obj + offset) - // - // while honoring read barriers based on read_barrier_option. - void GenerateGcRootFieldLoad(HInstruction* instruction, - Location root, - vixl::aarch32::Register obj, - uint32_t offset, - ReadBarrierOption read_barrier_option); void GenerateTestAndBranch(HInstruction* instruction, size_t condition_input_index, vixl::aarch32::Label* true_target, @@ -606,6 +604,10 @@ class CodeGeneratorARMVIXL : public CodeGenerator { Handle handle); void EmitLinkerPatches(ArenaVector* linker_patches) OVERRIDE; + bool NeedsThunkCode(const linker::LinkerPatch& patch) const OVERRIDE; + void EmitThunkCode(const linker::LinkerPatch& patch, + /*out*/ ArenaVector* code, + /*out*/ std::string* debug_name) OVERRIDE; void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; @@ -613,6 +615,16 @@ class CodeGeneratorARMVIXL : public CodeGenerator { // is added only for AOT compilation if link-time generated thunks for fields are enabled. void MaybeAddBakerCcEntrypointTempForFields(LocationSummary* locations); + // Generate a GC root reference load: + // + // root <- *(obj + offset) + // + // while honoring read barriers based on read_barrier_option. + void GenerateGcRootFieldLoad(HInstruction* instruction, + Location root, + vixl::aarch32::Register obj, + uint32_t offset, + ReadBarrierOption read_barrier_option); // Fast path implementation of ReadBarrier::Barrier for a heap // reference field load when Baker's read barriers are used. void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -767,6 +779,83 @@ class CodeGeneratorARMVIXL : public CodeGenerator { vixl::aarch32::Register temp = vixl32::Register()); private: + // Encoding of thunk type and data for link-time generated thunks for Baker read barriers. + + enum class BakerReadBarrierKind : uint8_t { + kField, // Field get or array get with constant offset (i.e. constant index). + kArray, // Array get with index in register. + kGcRoot, // GC root load. + kLast = kGcRoot + }; + + enum class BakerReadBarrierWidth : uint8_t { + kWide, // 32-bit LDR (and 32-bit NEG if heap poisoning is enabled). + kNarrow, // 16-bit LDR (and 16-bit NEG if heap poisoning is enabled). + kLast = kNarrow + }; + + static constexpr uint32_t kBakerReadBarrierInvalidEncodedReg = /* pc is invalid */ 15u; + + static constexpr size_t kBitsForBakerReadBarrierKind = + MinimumBitsToStore(static_cast(BakerReadBarrierKind::kLast)); + static constexpr size_t kBakerReadBarrierBitsForRegister = + MinimumBitsToStore(kBakerReadBarrierInvalidEncodedReg); + using BakerReadBarrierKindField = + BitField; + using BakerReadBarrierFirstRegField = + BitField; + using BakerReadBarrierSecondRegField = + BitField; + static constexpr size_t kBitsForBakerReadBarrierWidth = + MinimumBitsToStore(static_cast(BakerReadBarrierWidth::kLast)); + using BakerReadBarrierWidthField = + BitField; + + static void CheckValidReg(uint32_t reg) { + DCHECK(reg < vixl::aarch32::ip.GetCode() && reg != kBakerCcEntrypointRegister.GetCode()) << reg; + } + + static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, + uint32_t holder_reg, + bool narrow) { + CheckValidReg(base_reg); + CheckValidReg(holder_reg); + DCHECK(!narrow || base_reg < 8u) << base_reg; + BakerReadBarrierWidth width = + narrow ? BakerReadBarrierWidth::kNarrow : BakerReadBarrierWidth::kWide; + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kField) | + BakerReadBarrierFirstRegField::Encode(base_reg) | + BakerReadBarrierSecondRegField::Encode(holder_reg) | + BakerReadBarrierWidthField::Encode(width); + } + + static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) { + CheckValidReg(base_reg); + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kArray) | + BakerReadBarrierFirstRegField::Encode(base_reg) | + BakerReadBarrierSecondRegField::Encode(kBakerReadBarrierInvalidEncodedReg) | + BakerReadBarrierWidthField::Encode(BakerReadBarrierWidth::kWide); + } + + static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg, bool narrow) { + CheckValidReg(root_reg); + DCHECK(!narrow || root_reg < 8u) << root_reg; + BakerReadBarrierWidth width = + narrow ? BakerReadBarrierWidth::kNarrow : BakerReadBarrierWidth::kWide; + return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kGcRoot) | + BakerReadBarrierFirstRegField::Encode(root_reg) | + BakerReadBarrierSecondRegField::Encode(kBakerReadBarrierInvalidEncodedReg) | + BakerReadBarrierWidthField::Encode(width); + } + + void CompileBakerReadBarrierThunk(ArmVIXLAssembler& assembler, + uint32_t encoded_data, + /*out*/ std::string* debug_name); + vixl::aarch32::Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, vixl::aarch32::Register temp); @@ -829,6 +918,7 @@ class CodeGeneratorARMVIXL : public CodeGenerator { // Patches for class literals in JIT compiled code. TypeToLiteralMap jit_class_patches_; + friend class linker::Thumb2RelativePatcherTest; DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARMVIXL); }; diff --git a/compiler/optimizing/codegen_test_utils.h b/compiler/optimizing/codegen_test_utils.h index c41c290c8b..792cfb539a 100644 --- a/compiler/optimizing/codegen_test_utils.h +++ b/compiler/optimizing/codegen_test_utils.h @@ -195,7 +195,9 @@ class InternalCodeAllocator : public CodeAllocator { } size_t GetSize() const { return size_; } - uint8_t* GetMemory() const { return memory_.get(); } + ArrayRef GetMemory() const OVERRIDE { + return ArrayRef(memory_.get(), size_); + } private: size_t size_; @@ -269,8 +271,8 @@ static void Run(const InternalCodeAllocator& allocator, InstructionSet target_isa = codegen.GetInstructionSet(); typedef Expected (*fptr)(); - CommonCompilerTest::MakeExecutable(allocator.GetMemory(), allocator.GetSize()); - fptr f = reinterpret_cast(allocator.GetMemory()); + CommonCompilerTest::MakeExecutable(allocator.GetMemory().data(), allocator.GetMemory().size()); + fptr f = reinterpret_cast(reinterpret_cast(allocator.GetMemory().data())); if (target_isa == InstructionSet::kThumb2) { // For thumb we need the bottom bit set. f = reinterpret_cast(reinterpret_cast(f) + 1); diff --git a/compiler/optimizing/optimizing_cfi_test.cc b/compiler/optimizing/optimizing_cfi_test.cc index d20b681b49..2e189fdd14 100644 --- a/compiler/optimizing/optimizing_cfi_test.cc +++ b/compiler/optimizing/optimizing_cfi_test.cc @@ -105,15 +105,15 @@ class OptimizingCFITest : public CFITest, public OptimizingUnitTestHelper { const std::vector& expected_asm, const std::vector& expected_cfi) { // Get the outputs. - const std::vector& actual_asm = code_allocator_.GetMemory(); + ArrayRef actual_asm = code_allocator_.GetMemory(); Assembler* opt_asm = code_gen_->GetAssembler(); - const std::vector& actual_cfi = *(opt_asm->cfi().data()); + ArrayRef actual_cfi(*(opt_asm->cfi().data())); if (kGenerateExpected) { GenerateExpected(stdout, isa, isa_str, actual_asm, actual_cfi); } else { - EXPECT_EQ(expected_asm, actual_asm); - EXPECT_EQ(expected_cfi, actual_cfi); + EXPECT_EQ(ArrayRef(expected_asm), actual_asm); + EXPECT_EQ(ArrayRef(expected_cfi), actual_cfi); } } @@ -140,7 +140,7 @@ class OptimizingCFITest : public CFITest, public OptimizingUnitTestHelper { return memory_.data(); } - const std::vector& GetMemory() { return memory_; } + ArrayRef GetMemory() const OVERRIDE { return ArrayRef(memory_); } private: std::vector memory_; diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index e42dfc10ba..79165826d1 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -75,22 +75,18 @@ static constexpr const char* kPassNameSeparator = "$"; class CodeVectorAllocator FINAL : public CodeAllocator { public: explicit CodeVectorAllocator(ArenaAllocator* allocator) - : memory_(allocator->Adapter(kArenaAllocCodeBuffer)), - size_(0) {} + : memory_(allocator->Adapter(kArenaAllocCodeBuffer)) {} virtual uint8_t* Allocate(size_t size) { - size_ = size; memory_.resize(size); return &memory_[0]; } - size_t GetSize() const { return size_; } - const ArenaVector& GetMemory() const { return memory_; } + ArrayRef GetMemory() const OVERRIDE { return ArrayRef(memory_); } uint8_t* GetData() { return memory_.data(); } private: ArenaVector memory_; - size_t size_; DISALLOW_COPY_AND_ASSIGN(CodeVectorAllocator); }; @@ -719,7 +715,7 @@ CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod( GetCompilerDriver(), codegen->GetInstructionSet(), - ArrayRef(code_allocator->GetMemory()), + code_allocator->GetMemory(), // Follow Quick's behavior and set the frame size to zero if it is // considered "empty" (see the definition of // art::CodeGenerator::HasEmptyFrame). @@ -731,6 +727,16 @@ CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, ArrayRef(*codegen->GetAssembler()->cfi().data()), ArrayRef(linker_patches)); + CompiledMethodStorage* storage = GetCompilerDriver()->GetCompiledMethodStorage(); + for (const linker::LinkerPatch& patch : linker_patches) { + if (codegen->NeedsThunkCode(patch) && storage->GetThunkCode(patch).empty()) { + ArenaVector code(allocator->Adapter()); + std::string debug_name; + codegen->EmitThunkCode(patch, &code, &debug_name); + storage->SetThunkCode(patch, ArrayRef(code), debug_name); + } + } + return compiled_method; } @@ -1339,7 +1345,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, codegen->GetCoreSpillMask(), codegen->GetFpuSpillMask(), code_allocator.GetMemory().data(), - code_allocator.GetSize(), + code_allocator.GetMemory().size(), data_size, osr, roots, @@ -1369,7 +1375,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, info.is_optimized = true; info.is_code_address_text_relative = false; info.code_address = code_address; - info.code_size = code_allocator.GetSize(); + info.code_size = code_allocator.GetMemory().size(); info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); info.code_info = stack_map_size == 0 ? nullptr : stack_map_data; info.cfi = ArrayRef(*codegen->GetAssembler()->cfi().data()); @@ -1378,7 +1384,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, Runtime::Current()->GetJit()->AddMemoryUsage(method, allocator.BytesUsed()); if (jit_logger != nullptr) { - jit_logger->WriteLog(code, code_allocator.GetSize(), method); + jit_logger->WriteLog(code, code_allocator.GetMemory().size(), method); } if (kArenaAllocatorCountAllocations) { diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index ac5dd61062..ee570ad4de 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -2070,7 +2070,9 @@ class Dex2Oat FINAL { { TimingLogger::ScopedTiming t2("dex2oat Write ELF", timings_); - linker::MultiOatRelativePatcher patcher(instruction_set_, instruction_set_features_.get()); + linker::MultiOatRelativePatcher patcher(instruction_set_, + instruction_set_features_.get(), + driver_->GetCompiledMethodStorage()); for (size_t i = 0, size = oat_files_.size(); i != size; ++i) { std::unique_ptr& elf_writer = elf_writers_[i]; std::unique_ptr& oat_writer = oat_writers_[i]; diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index 7449191984..476a843821 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -299,7 +299,8 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, DCHECK_EQ(vdex_files.size(), oat_files.size()); for (size_t i = 0, size = oat_files.size(); i != size; ++i) { MultiOatRelativePatcher patcher(driver->GetInstructionSet(), - driver->GetInstructionSetFeatures()); + driver->GetInstructionSetFeatures(), + driver->GetCompiledMethodStorage()); OatWriter* const oat_writer = oat_writers[i].get(); ElfWriter* const elf_writer = elf_writers[i].get(); std::vector cur_dex_files(1u, class_path[i]); diff --git a/dex2oat/linker/multi_oat_relative_patcher.cc b/dex2oat/linker/multi_oat_relative_patcher.cc index 1abaf7dfd1..1449d478f9 100644 --- a/dex2oat/linker/multi_oat_relative_patcher.cc +++ b/dex2oat/linker/multi_oat_relative_patcher.cc @@ -20,14 +20,28 @@ #include "base/bit_utils.h" #include "globals.h" +#include "driver/compiled_method_storage.h" namespace art { namespace linker { +void MultiOatRelativePatcher::ThunkProvider::GetThunkCode(const LinkerPatch& patch, + /*out*/ ArrayRef* code, + /*out*/ std::string* debug_name) { + *code = storage_->GetThunkCode(patch, debug_name); + DCHECK(!code->empty()); +} + + MultiOatRelativePatcher::MultiOatRelativePatcher(InstructionSet instruction_set, - const InstructionSetFeatures* features) - : method_offset_map_(), - relative_patcher_(RelativePatcher::Create(instruction_set, features, &method_offset_map_)), + const InstructionSetFeatures* features, + CompiledMethodStorage* storage) + : thunk_provider_(storage), + method_offset_map_(), + relative_patcher_(RelativePatcher::Create(instruction_set, + features, + &thunk_provider_, + &method_offset_map_)), adjustment_(0u), instruction_set_(instruction_set), start_size_code_alignment_(0u), diff --git a/dex2oat/linker/multi_oat_relative_patcher.h b/dex2oat/linker/multi_oat_relative_patcher.h index bd33b95318..60fcfe8b58 100644 --- a/dex2oat/linker/multi_oat_relative_patcher.h +++ b/dex2oat/linker/multi_oat_relative_patcher.h @@ -26,6 +26,7 @@ namespace art { class CompiledMethod; +class CompiledMethodStorage; class InstructionSetFeatures; namespace linker { @@ -38,7 +39,9 @@ class MultiOatRelativePatcher FINAL { public: using const_iterator = SafeMap::const_iterator; - MultiOatRelativePatcher(InstructionSet instruction_set, const InstructionSetFeatures* features); + MultiOatRelativePatcher(InstructionSet instruction_set, + const InstructionSetFeatures* features, + CompiledMethodStorage* storage); // Mark the start of a new oat file (for statistics retrieval) and set the // adjustment for a new oat file to apply to all relative offsets that are @@ -129,6 +132,19 @@ class MultiOatRelativePatcher FINAL { uint32_t MiscThunksSize() const; private: + class ThunkProvider : public RelativePatcherThunkProvider { + public: + explicit ThunkProvider(CompiledMethodStorage* storage) + : storage_(storage) {} + + void GetThunkCode(const LinkerPatch& patch, + /*out*/ ArrayRef* code, + /*out*/ std::string* debug_name) OVERRIDE; + + private: + CompiledMethodStorage* storage_; + }; + // Map method reference to assigned offset. // Wrap the map in a class implementing RelativePatcherTargetProvider. class MethodOffsetMap : public RelativePatcherTargetProvider { @@ -137,6 +153,7 @@ class MultiOatRelativePatcher FINAL { SafeMap map; }; + ThunkProvider thunk_provider_; MethodOffsetMap method_offset_map_; std::unique_ptr relative_patcher_; uint32_t adjustment_; diff --git a/dex2oat/linker/multi_oat_relative_patcher_test.cc b/dex2oat/linker/multi_oat_relative_patcher_test.cc index ca9c5f1e84..05fe36a590 100644 --- a/dex2oat/linker/multi_oat_relative_patcher_test.cc +++ b/dex2oat/linker/multi_oat_relative_patcher_test.cc @@ -122,7 +122,7 @@ class MultiOatRelativePatcherTest : public testing::Test { MultiOatRelativePatcherTest() : instruction_set_features_(InstructionSetFeatures::FromCppDefines()), - patcher_(kRuntimeISA, instruction_set_features_.get()) { + patcher_(kRuntimeISA, instruction_set_features_.get(), /* storage */ nullptr) { std::unique_ptr mock(new MockPatcher()); mock_ = mock.get(); patcher_.relative_patcher_ = std::move(mock); diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 6e95393e80..ea4e210b74 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -213,7 +213,8 @@ class OatTest : public CommonCompilerTest { class_linker->RegisterDexFile(*dex_file, nullptr); } MultiOatRelativePatcher patcher(compiler_driver_->GetInstructionSet(), - instruction_set_features_.get()); + instruction_set_features_.get(), + compiler_driver_->GetCompiledMethodStorage()); oat_writer.Initialize(compiler_driver_.get(), nullptr, dex_files); oat_writer.PrepareLayout(&patcher); elf_writer->PrepareDynamicSection(oat_writer.GetOatHeader().GetExecutableOffset(), -- GitLab From 9574f493cb072466b7b491de97c381e6c2b8b639 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Tue, 10 Apr 2018 10:20:32 +0100 Subject: [PATCH 235/749] ART: Walk past j.l.i in stackwalk for Hidden API Bug: 77631986 Test: art/test.py --host 674-hidden-api (cherry picked from change 198a27e92a529115a2db2a17ec11d8b52bd6a315) Change-Id: I2364036a6b7bd8bb06d2e456fb22f4db9c6cce21 Merged-In: I2e3e0399765da7f554259fe14247b45e42110ef4 --- runtime/native/java_lang_Class.cc | 34 +++++--- test/674-hiddenapi/info.txt | 5 +- test/674-hiddenapi/src-ex/ChildClass.java | 74 ++++++++++++++--- test/674-hiddenapi/src-ex/JLI.java | 98 +++++++++++++++++++++++ 4 files changed, 190 insertions(+), 21 deletions(-) create mode 100644 test/674-hiddenapi/src-ex/JLI.java diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 82300fe9ba..510c5de7ed 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -33,6 +33,7 @@ #include "mirror/class_loader.h" #include "mirror/field-inl.h" #include "mirror/method.h" +#include "mirror/method_handles_lookup.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" @@ -49,12 +50,13 @@ namespace art { -// Returns true if the first non-ClassClass caller up the stack is in a platform dex file. +// Returns true if the first caller outside of the Class class or java.lang.invoke package +// is in a platform DEX file. static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - // Walk the stack and find the first frame not from java.lang.Class. + // Walk the stack and find the first frame not from java.lang.Class and not from java.lang.invoke. // This is very expensive. Save this till the last. - struct FirstNonClassClassCallerVisitor : public StackVisitor { - explicit FirstNonClassClassCallerVisitor(Thread* thread) + struct FirstExternalCallerVisitor : public StackVisitor { + explicit FirstExternalCallerVisitor(Thread* thread) : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), caller(nullptr) { } @@ -68,18 +70,30 @@ static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_l } else if (m->IsRuntimeMethod()) { // Internal runtime method, continue walking the stack. return true; - } else if (m->GetDeclaringClass()->IsClassClass()) { - return true; - } else { - caller = m; - return false; } + + ObjPtr declaring_class = m->GetDeclaringClass(); + if (declaring_class->IsBootStrapClassLoaded()) { + if (declaring_class->IsClassClass()) { + return true; + } + ObjPtr lookup_class = mirror::MethodHandlesLookup::StaticClass(); + if (declaring_class == lookup_class || declaring_class->IsInSamePackage(lookup_class)) { + // Check classes in the java.lang.invoke package. At the time of writing, the + // classes of interest are MethodHandles and MethodHandles.Lookup, but this + // is subject to change so conservatively cover the entire package. + return true; + } + } + + caller = m; + return false; } ArtMethod* caller; }; - FirstNonClassClassCallerVisitor visitor(self); + FirstExternalCallerVisitor visitor(self); visitor.WalkStack(); return visitor.caller != nullptr && hiddenapi::IsCallerInPlatformDex(visitor.caller->GetDeclaringClass()); diff --git a/test/674-hiddenapi/info.txt b/test/674-hiddenapi/info.txt index 25ac6ae78f..94aac1e6d9 100644 --- a/test/674-hiddenapi/info.txt +++ b/test/674-hiddenapi/info.txt @@ -1,7 +1,8 @@ Test whether hidden API access flags are being enforced. The test is composed of two JARs. The first (parent) defines methods and fields and the second (child) -tries to access them with reflection/JNI or link against them. Note that the -first is compiled twice - once with and once without hidden access flags. +tries to access them with reflection/JNI/MethodHandles or link against them. +Note that the first is compiled twice - once with and once without hidden access +flags. The test then proceeds to exercise the following combinations of class loading: (a) Both parent and child dex loaded with PathClassLoader, parent's class loader diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java index 822224c539..ea66f1651e 100644 --- a/test/674-hiddenapi/src-ex/ChildClass.java +++ b/test/674-hiddenapi/src-ex/ChildClass.java @@ -15,13 +15,8 @@ */ import dalvik.system.VMRuntime; -import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.util.function.Consumer; public class ChildClass { @@ -210,6 +205,37 @@ public class ChildClass { throwDiscoveryException(klass, name, true, "JNI", canDiscover); } + // Test discovery with MethodHandles.lookup() which is caller + // context sensitive. + + final MethodHandles.Lookup lookup = MethodHandles.lookup(); + if (JLI.canDiscoverWithLookupFindGetter(lookup, klass, name, int.class) + != canDiscover) { + throwDiscoveryException(klass, name, true, "MethodHandles.lookup().findGetter()", + canDiscover); + } + if (JLI.canDiscoverWithLookupFindStaticGetter(lookup, klass, name, int.class) + != canDiscover) { + throwDiscoveryException(klass, name, true, "MethodHandles.lookup().findStaticGetter()", + canDiscover); + } + + // Test discovery with MethodHandles.publicLookup() which can only + // see public fields. Looking up setters here and fields in + // interfaces are implicitly final. + + final MethodHandles.Lookup publicLookup = MethodHandles.publicLookup(); + if (JLI.canDiscoverWithLookupFindSetter(publicLookup, klass, name, int.class) + != canDiscover) { + throwDiscoveryException(klass, name, true, "MethodHandles.publicLookup().findSetter()", + canDiscover); + } + if (JLI.canDiscoverWithLookupFindStaticSetter(publicLookup, klass, name, int.class) + != canDiscover) { + throwDiscoveryException(klass, name, true, "MethodHandles.publicLookup().findStaticSetter()", + canDiscover); + } + // Finish here if we could not discover the field. if (canDiscover) { @@ -297,7 +323,21 @@ public class ChildClass { throwDiscoveryException(klass, name, false, "JNI", canDiscover); } - // Finish here if we could not discover the field. + // Test discovery with MethodHandles.lookup(). + + final MethodHandles.Lookup lookup = MethodHandles.lookup(); + final MethodType methodType = MethodType.methodType(int.class); + if (JLI.canDiscoverWithLookupFindVirtual(lookup, klass, name, methodType) != canDiscover) { + throwDiscoveryException(klass, name, false, "MethodHandles.lookup().findVirtual()", + canDiscover); + } + + if (JLI.canDiscoverWithLookupFindStatic(lookup, klass, name, methodType) != canDiscover) { + throwDiscoveryException(klass, name, false, "MethodHandles.lookup().findStatic()", + canDiscover); + } + + // Finish here if we could not discover the method. if (canDiscover) { // Test that modifiers are unaffected. @@ -353,6 +393,7 @@ public class ChildClass { hiddenness.mAssociatedType.mClass }; Object initargs[] = new Object[] { visibility.mAssociatedType.mDefaultValue, hiddenness.mAssociatedType.mDefaultValue }; + MethodType methodType = MethodType.methodType(void.class, args); boolean canDiscover = (behaviour != Behaviour.Denied); boolean setsWarning = (behaviour == Behaviour.Warning); @@ -383,7 +424,22 @@ public class ChildClass { throwDiscoveryException(klass, fullName, false, "JNI", canDiscover); } - // Finish here if we could not discover the field. + // Test discovery with MethodHandles.lookup() + + final MethodHandles.Lookup lookup = MethodHandles.lookup(); + if (JLI.canDiscoverWithLookupFindConstructor(lookup, klass, methodType) != canDiscover) { + throwDiscoveryException(klass, fullName, false, "MethodHandles.lookup().findConstructor", + canDiscover); + } + + final MethodHandles.Lookup publicLookup = MethodHandles.publicLookup(); + if (JLI.canDiscoverWithLookupFindConstructor(publicLookup, klass, methodType) != canDiscover) { + throwDiscoveryException(klass, fullName, false, + "MethodHandles.publicLookup().findConstructor", + canDiscover); + } + + // Finish here if we could not discover the constructor. if (!canDiscover) { return; diff --git a/test/674-hiddenapi/src-ex/JLI.java b/test/674-hiddenapi/src-ex/JLI.java new file mode 100644 index 0000000000..e4ca3bc902 --- /dev/null +++ b/test/674-hiddenapi/src-ex/JLI.java @@ -0,0 +1,98 @@ +/* + * 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.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +/** Class with helper methods for field and method lookups using java.lang.invoke. */ +public class JLI { + public static boolean canDiscoverWithLookupFindGetter( + MethodHandles.Lookup lookup, Class klass, String fieldName, Class fieldType) { + try { + return lookup.findGetter(klass, fieldName, fieldType) != null; + } catch (NoSuchFieldException e) { + return false; + } catch (IllegalAccessException e) { + return true; + } + } + + public static boolean canDiscoverWithLookupFindSetter( + MethodHandles.Lookup lookup, Class klass, String fieldName, Class fieldType) { + try { + return lookup.findSetter(klass, fieldName, fieldType) != null; + } catch (NoSuchFieldException e) { + return false; + } catch (IllegalAccessException e) { + return true; + } + } + + public static boolean canDiscoverWithLookupFindStaticGetter( + MethodHandles.Lookup lookup, Class klass, String fieldName, Class fieldType) { + try { + return lookup.findStaticGetter(klass, fieldName, fieldType) != null; + } catch (NoSuchFieldException e) { + return false; + } catch (IllegalAccessException e) { + return true; + } + } + + public static boolean canDiscoverWithLookupFindStaticSetter( + MethodHandles.Lookup lookup, Class klass, String fieldName, Class fieldType) { + try { + return lookup.findStaticSetter(klass, fieldName, fieldType) != null; + } catch (NoSuchFieldException e) { + return false; + } catch (IllegalAccessException e) { + return true; + } + } + + public static boolean canDiscoverWithLookupFindConstructor( + MethodHandles.Lookup lookup, Class klass, MethodType methodType) { + try { + return lookup.findConstructor(klass, methodType) != null; + } catch (NoSuchMethodException e) { + return false; + } catch (IllegalAccessException e) { + return true; + } + } + + public static boolean canDiscoverWithLookupFindVirtual( + MethodHandles.Lookup lookup, Class klass, String methodName, MethodType methodType) { + try { + return lookup.findVirtual(klass, methodName, methodType) != null; + } catch (NoSuchMethodException e) { + return false; + } catch (IllegalAccessException e) { + return true; + } + } + + public static boolean canDiscoverWithLookupFindStatic( + MethodHandles.Lookup lookup, Class klass, String methodName, MethodType methodType) { + try { + return lookup.findStatic(klass, methodName, methodType) != null; + } catch (NoSuchMethodException e) { + return false; + } catch (IllegalAccessException e) { + return true; + } + } +} -- GitLab From c0d988a9b5f2c34072fff100af9bd12464a6c55b Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Tue, 10 Apr 2018 14:32:35 +0100 Subject: [PATCH 236/749] ART: Stop Hidden API stackwalk for class initializers in j.l.i. Class initializers in java.lang.invoke retrieve field offsets. The hiddenapi stack walk does not need to progress past these initializers as they are permitted irrespective of how their invocation is triggered. Bug: 77631986 Test: art/test/run-test --host --64 674-hiddenapi (cherry picked from commit 29e64cfc32e17c9111a5ed2a6b141bebf891cbe3) Change-Id: I77a29999177850bd50ca4d043b0cd40c40692fe4 Merged-In: Iaabedc9c016d546e10072107d79c7b6701582c83 --- runtime/native/java_lang_Class.cc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 510c5de7ed..bfd7f69cef 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -77,11 +77,14 @@ static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_l if (declaring_class->IsClassClass()) { return true; } + // Check classes in the java.lang.invoke package. At the time of writing, the + // classes of interest are MethodHandles and MethodHandles.Lookup, but this + // is subject to change so conservatively cover the entire package. + // NB Static initializers within java.lang.invoke are permitted and do not + // need further stack inspection. ObjPtr lookup_class = mirror::MethodHandlesLookup::StaticClass(); - if (declaring_class == lookup_class || declaring_class->IsInSamePackage(lookup_class)) { - // Check classes in the java.lang.invoke package. At the time of writing, the - // classes of interest are MethodHandles and MethodHandles.Lookup, but this - // is subject to change so conservatively cover the entire package. + if ((declaring_class == lookup_class || declaring_class->IsInSamePackage(lookup_class)) + && !m->IsClassInitializer()) { return true; } } -- GitLab From 7b0e844d8f0a59c6e1e9ac0c441409d2e435ac36 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Wed, 11 Apr 2018 18:27:47 +0100 Subject: [PATCH 237/749] Distinguish the various pre-allocated OutOfMemoryError use cases. The pre-allocated exception "OutOfMemoryError thrown while trying to throw OutOfMemoryError; no stack trace available", can be thrown in three occasions: 1. OOME thrown while throwing an exception (not necessarily an OOME); 2. OOME thrown while throwing an OOME; 3. OOME thrown while handling a stack overflow. To improve error reports, replace the unique pre-allocated OOME exception with three different exceptions for these three use cases. Noe that we were already manually dumping a stack trace in case #2. This change implements this for case #1 too. Test: art/test.py Bug: 68251879 Bug: 77567088 Bug: 77844013 Change-Id: I84cd15b729b4d0bd7bf6c831caeb0eb93c458114 --- runtime/gc/heap.cc | 3 +- runtime/runtime.cc | 72 +++++++++++++++++++++++++++++++++++++--------- runtime/runtime.h | 17 +++++++++-- runtime/thread.cc | 5 ++-- 4 files changed, 79 insertions(+), 18 deletions(-) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 684922099b..e85824de70 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -1203,7 +1203,8 @@ void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType // If we're in a stack overflow, do not create a new exception. It would require running the // constructor, which will of course still be in a stack overflow. if (self->IsHandlingStackOverflow()) { - self->SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError()); + self->SetException( + Runtime::Current()->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow()); return; } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index f550a151bb..c394fefb38 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1503,19 +1503,34 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // TODO: move this to just be an Trace::Start argument Trace::SetDefaultClockSource(runtime_options.GetOrDefault(Opt::ProfileClock)); + // Pre-allocate an OutOfMemoryError for the case when we fail to + // allocate the exception to be thrown. + InitPreAllocatedException(self, + &Runtime::pre_allocated_OutOfMemoryError_when_throwing_exception_, + "Ljava/lang/OutOfMemoryError;", + "OutOfMemoryError thrown while trying to throw an exception; " + "no stack trace available"); // Pre-allocate an OutOfMemoryError for the double-OOME case. - self->ThrowNewException("Ljava/lang/OutOfMemoryError;", - "OutOfMemoryError thrown while trying to throw OutOfMemoryError; " - "no stack trace available"); - pre_allocated_OutOfMemoryError_ = GcRoot(self->GetException()); - self->ClearException(); + InitPreAllocatedException(self, + &Runtime::pre_allocated_OutOfMemoryError_when_throwing_oome_, + "Ljava/lang/OutOfMemoryError;", + "OutOfMemoryError thrown while trying to throw OutOfMemoryError; " + "no stack trace available"); + // Pre-allocate an OutOfMemoryError for the case when we fail to + // allocate while handling a stack overflow. + InitPreAllocatedException(self, + &Runtime::pre_allocated_OutOfMemoryError_when_handling_stack_overflow_, + "Ljava/lang/OutOfMemoryError;", + "OutOfMemoryError thrown while trying to handle a stack overflow; " + "no stack trace available"); // Pre-allocate a NoClassDefFoundError for the common case of failing to find a system class // ahead of checking the application's class loader. - self->ThrowNewException("Ljava/lang/NoClassDefFoundError;", - "Class not found using the boot class loader; no stack trace available"); - pre_allocated_NoClassDefFoundError_ = GcRoot(self->GetException()); - self->ClearException(); + InitPreAllocatedException(self, + &Runtime::pre_allocated_NoClassDefFoundError_, + "Ljava/lang/NoClassDefFoundError;", + "Class not found using the boot class loader; " + "no stack trace available"); // Runtime initialization is largely done now. // We load plugins first since that can modify the runtime state slightly. @@ -1666,6 +1681,16 @@ void Runtime::AttachAgent(JNIEnv* env, } } +void Runtime::InitPreAllocatedException(Thread* self, + GcRoot Runtime::* exception, + const char* exception_class_descriptor, + const char* msg) { + DCHECK_EQ(self, Thread::Current()); + self->ThrowNewException(exception_class_descriptor, msg); + this->*exception = GcRoot(self->GetException()); + self->ClearException(); +} + void Runtime::InitNativeMethods() { VLOG(startup) << "Runtime::InitNativeMethods entering"; Thread* self = Thread::Current(); @@ -1917,10 +1942,26 @@ void Runtime::DetachCurrentThread() { thread_list_->Unregister(self); } -mirror::Throwable* Runtime::GetPreAllocatedOutOfMemoryError() { - mirror::Throwable* oome = pre_allocated_OutOfMemoryError_.Read(); +mirror::Throwable* Runtime::GetPreAllocatedOutOfMemoryErrorWhenThrowingException() { + mirror::Throwable* oome = pre_allocated_OutOfMemoryError_when_throwing_exception_.Read(); + if (oome == nullptr) { + LOG(ERROR) << "Failed to return pre-allocated OOME-when-throwing-exception"; + } + return oome; +} + +mirror::Throwable* Runtime::GetPreAllocatedOutOfMemoryErrorWhenThrowingOOME() { + mirror::Throwable* oome = pre_allocated_OutOfMemoryError_when_throwing_oome_.Read(); + if (oome == nullptr) { + LOG(ERROR) << "Failed to return pre-allocated OOME-when-throwing-OOME"; + } + return oome; +} + +mirror::Throwable* Runtime::GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow() { + mirror::Throwable* oome = pre_allocated_OutOfMemoryError_when_handling_stack_overflow_.Read(); if (oome == nullptr) { - LOG(ERROR) << "Failed to return pre-allocated OOME"; + LOG(ERROR) << "Failed to return pre-allocated OOME-when-handling-stack-overflow"; } return oome; } @@ -2005,7 +2046,12 @@ void Runtime::VisitTransactionRoots(RootVisitor* visitor) { void Runtime::VisitNonThreadRoots(RootVisitor* visitor) { java_vm_->VisitRoots(visitor); sentinel_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); - pre_allocated_OutOfMemoryError_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); + pre_allocated_OutOfMemoryError_when_throwing_exception_ + .VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); + pre_allocated_OutOfMemoryError_when_throwing_oome_ + .VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); + pre_allocated_OutOfMemoryError_when_handling_stack_overflow_ + .VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); pre_allocated_NoClassDefFoundError_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); verifier::MethodVerifier::VisitStaticRoots(visitor); VisitTransactionRoots(visitor); diff --git a/runtime/runtime.h b/runtime/runtime.h index 03f17bc04a..3d4b596349 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -291,7 +291,12 @@ class Runtime { // Get the special object used to mark a cleared JNI weak global. mirror::Object* GetClearedJniWeakGlobal() REQUIRES_SHARED(Locks::mutator_lock_); - mirror::Throwable* GetPreAllocatedOutOfMemoryError() REQUIRES_SHARED(Locks::mutator_lock_); + mirror::Throwable* GetPreAllocatedOutOfMemoryErrorWhenThrowingException() + REQUIRES_SHARED(Locks::mutator_lock_); + mirror::Throwable* GetPreAllocatedOutOfMemoryErrorWhenThrowingOOME() + REQUIRES_SHARED(Locks::mutator_lock_); + mirror::Throwable* GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow() + REQUIRES_SHARED(Locks::mutator_lock_); mirror::Throwable* GetPreAllocatedNoClassDefFoundError() REQUIRES_SHARED(Locks::mutator_lock_); @@ -764,6 +769,11 @@ class Runtime { bool Init(RuntimeArgumentMap&& runtime_options) SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_); + void InitPreAllocatedException(Thread* self, + GcRoot Runtime::* exception, + const char* exception_class_descriptor, + const char* msg) + REQUIRES_SHARED(Locks::mutator_lock_); void InitNativeMethods() REQUIRES(!Locks::mutator_lock_); void RegisterRuntimeNativeMethods(JNIEnv* env); @@ -796,7 +806,10 @@ class Runtime { // 64 bit so that we can share the same asm offsets for both 32 and 64 bits. uint64_t callee_save_methods_[kCalleeSaveSize]; - GcRoot pre_allocated_OutOfMemoryError_; + // Pre-allocated exceptions (see Runtime::Init). + GcRoot pre_allocated_OutOfMemoryError_when_throwing_exception_; + GcRoot pre_allocated_OutOfMemoryError_when_throwing_oome_; + GcRoot pre_allocated_OutOfMemoryError_when_handling_stack_overflow_; GcRoot pre_allocated_NoClassDefFoundError_; ArtMethod* resolution_method_; ArtMethod* imt_conflict_method_; diff --git a/runtime/thread.cc b/runtime/thread.cc index d17f409a7d..f6ac64f7bd 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -3021,7 +3021,8 @@ void Thread::ThrowNewWrappedException(const char* exception_class_descriptor, // If we couldn't allocate the exception, throw the pre-allocated out of memory exception. if (exception == nullptr) { - SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError()); + Dump(LOG_STREAM(WARNING)); // The pre-allocated OOME has no stack, so help out and log one. + SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryErrorWhenThrowingException()); return; } @@ -3101,7 +3102,7 @@ void Thread::ThrowOutOfMemoryError(const char* msg) { tls32_.throwing_OutOfMemoryError = false; } else { Dump(LOG_STREAM(WARNING)); // The pre-allocated OOME has no stack, so help out and log one. - SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError()); + SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryErrorWhenThrowingOOME()); } } -- GitLab From f5abfc42f0d986b9f8eb782d7e761f1a08784210 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 23 Mar 2018 21:51:54 -0700 Subject: [PATCH 238/749] Add stored class loader context option Motivation: Enable having a different class loader context during preopt vs the one stored in the oat file. Added test. Bug: 70934104 Bug: 67345922 Test: test-art-host Change-Id: I6c0851370e0740e5f47faf25a5494022034f6fa4 --- dex2oat/dex2oat.cc | 45 +++++++++++++++++++++++++++++---- dex2oat/dex2oat_options.cc | 3 +++ dex2oat/dex2oat_options.def | 1 + dex2oat/dex2oat_test.cc | 29 +++++++++++++++++++++ runtime/class_loader_context.cc | 22 +++++++++++----- runtime/class_loader_context.h | 6 ++++- 6 files changed, 94 insertions(+), 12 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 6667fd6618..693ead56bb 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -428,6 +428,10 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(" --class-loader-context=: a string specifying the intended"); UsageError(" runtime loading context for the compiled dex files."); UsageError(""); + UsageError(" --stored-class-loader-context=: a string specifying the intended"); + UsageError(" runtime loading context that is stored in the oat file. Overrides"); + UsageError(" --class-loader-context. Note that this ignores the classpath_dir arg."); + UsageError(""); UsageError(" It describes how the class loader chain should be built in order to ensure"); UsageError(" classes are resolved during dex2aot as they would be resolved at runtime."); UsageError(" This spec will be encoded in the oat file. If at runtime the dex file is"); @@ -1260,11 +1264,32 @@ class Dex2Oat FINAL { ParseInstructionSetFeatures(*args.Get(M::TargetInstructionSetFeatures), parser_options.get()); } if (args.Exists(M::ClassLoaderContext)) { - class_loader_context_ = ClassLoaderContext::Create(*args.Get(M::ClassLoaderContext)); + std::string class_loader_context_arg = *args.Get(M::ClassLoaderContext); + class_loader_context_ = ClassLoaderContext::Create(class_loader_context_arg); if (class_loader_context_ == nullptr) { Usage("Option --class-loader-context has an incorrect format: %s", - args.Get(M::ClassLoaderContext)->c_str()); + class_loader_context_arg.c_str()); + } + if (args.Exists(M::StoredClassLoaderContext)) { + stored_class_loader_context_.reset(new std::string(*args.Get(M::StoredClassLoaderContext))); + std::unique_ptr temp_context = + ClassLoaderContext::Create(*stored_class_loader_context_); + if (temp_context == nullptr) { + Usage("Option --stored-class-loader-context has an incorrect format: %s", + stored_class_loader_context_->c_str()); + } else if (!class_loader_context_->VerifyClassLoaderContextMatch( + *stored_class_loader_context_, + /*verify_names*/ false, + /*verify_checksums*/ false)) { + Usage( + "Option --stored-class-loader-context '%s' mismatches --class-loader-context '%s'", + stored_class_loader_context_->c_str(), + class_loader_context_arg.c_str()); + } } + } else if (args.Exists(M::StoredClassLoaderContext)) { + Usage("Option --stored-class-loader-context should only be used if " + "--class-loader-context is also specified"); } if (!ReadCompilerOptions(args, compiler_options_.get(), &error_msg)) { @@ -1579,7 +1604,7 @@ class Dex2Oat FINAL { if (class_loader_context_ == nullptr) { // If no context was specified use the default one (which is an empty PathClassLoader). - class_loader_context_ = std::unique_ptr(ClassLoaderContext::Default()); + class_loader_context_ = ClassLoaderContext::Default(); } DCHECK_EQ(oat_writers_.size(), 1u); @@ -1605,8 +1630,15 @@ class Dex2Oat FINAL { } // Store the class loader context in the oat header. - key_value_store_->Put(OatHeader::kClassPathKey, - class_loader_context_->EncodeContextForOatFile(classpath_dir_)); + // TODO: deprecate this since store_class_loader_context should be enough to cover the users + // of classpath_dir as well. + std::string class_path_key; + if (stored_class_loader_context_ != nullptr) { + class_path_key = *stored_class_loader_context_; + } else { + class_path_key = class_loader_context_->EncodeContextForOatFile(classpath_dir_); + } + key_value_store_->Put(OatHeader::kClassPathKey, class_path_key); } // Now that we have finalized key_value_store_, start writing the oat file. @@ -2855,6 +2887,9 @@ class Dex2Oat FINAL { // The spec describing how the class loader should be setup for compilation. std::unique_ptr class_loader_context_; + // The class loader context stored in the oat file. May be equal to class_loader_context_. + std::unique_ptr stored_class_loader_context_; + size_t thread_count_; uint64_t start_ns_; uint64_t start_cputime_ns_; diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc index 0d68f4fab6..5843691a24 100644 --- a/dex2oat/dex2oat_options.cc +++ b/dex2oat/dex2oat_options.cc @@ -245,6 +245,9 @@ static Parser CreateArgumentParser() { .Define("--class-loader-context=_") .WithType() .IntoKey(M::ClassLoaderContext) + .Define("--stored-class-loader-context=_") + .WithType() + .IntoKey(M::StoredClassLoaderContext) .Define("--compact-dex-level=_") .WithType() .WithValueMap({{"none", CompactDexLevel::kCompactDexLevelNone}, diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def index 01f9d9425f..1a913a9bbf 100644 --- a/dex2oat/dex2oat_options.def +++ b/dex2oat/dex2oat_options.def @@ -88,6 +88,7 @@ DEX2OAT_OPTIONS_KEY (std::string, NoInlineFrom) DEX2OAT_OPTIONS_KEY (Unit, ForceDeterminism) DEX2OAT_OPTIONS_KEY (std::string, ClasspathDir) DEX2OAT_OPTIONS_KEY (std::string, ClassLoaderContext) +DEX2OAT_OPTIONS_KEY (std::string, StoredClassLoaderContext) DEX2OAT_OPTIONS_KEY (std::string, DirtyImageObjects) DEX2OAT_OPTIONS_KEY (std::vector, RuntimeOptions) DEX2OAT_OPTIONS_KEY (std::string, CompilationReason) diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index c890f8bef0..710a6af792 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -2125,4 +2125,33 @@ TEST_F(Dex2oatTest, AppImageNoProfile) { EXPECT_EQ(header.GetImageSection(ImageHeader::kSectionArtFields).Size(), 0u); } +TEST_F(Dex2oatClassLoaderContextTest, StoredClassLoaderContext) { + const std::string out_dir = GetScratchDir(); + const std::string odex_location = out_dir + "/base.odex"; + const std::string valid_context = "PCL[" + GetUsedDexLocation() + "]"; + const std::string stored_context = "PCL[/system/not_real_lib.jar]"; + // The class path should not be valid and should fail being stored. + GenerateOdexForTest(GetTestDexFileName("ManyMethods"), + odex_location, + CompilerFilter::Filter::kQuicken, + { "--class-loader-context=" + stored_context }, + true, // expect_success + false, // use_fd + [&](const OatFile& oat_file) { + EXPECT_NE(oat_file.GetClassLoaderContext(), stored_context); + EXPECT_NE(oat_file.GetClassLoaderContext(), valid_context); + }); + // The stored context should match what we expect even though it's invalid. + GenerateOdexForTest(GetTestDexFileName("ManyMethods"), + odex_location, + CompilerFilter::Filter::kQuicken, + { "--class-loader-context=" + valid_context, + "--stored-class-loader-context=" + stored_context }, + true, // expect_success + false, // use_fd + [&](const OatFile& oat_file) { + EXPECT_EQ(oat_file.GetClassLoaderContext(), stored_context); + }); +} + } // namespace art diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index e646520f3d..216ad8f794 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -649,12 +649,16 @@ static bool IsAbsoluteLocation(const std::string& location) { return !location.empty() && location[0] == '/'; } -bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec) const { - DCHECK(dex_files_open_attempted_); - DCHECK(dex_files_open_result_); +bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec, + bool verify_names, + bool verify_checksums) const { + if (verify_names || verify_checksums) { + DCHECK(dex_files_open_attempted_); + DCHECK(dex_files_open_result_); + } ClassLoaderContext expected_context; - if (!expected_context.Parse(context_spec, /*parse_checksums*/ true)) { + if (!expected_context.Parse(context_spec, verify_checksums)) { LOG(WARNING) << "Invalid class loader context: " << context_spec; return false; } @@ -693,8 +697,14 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex return false; } - DCHECK_EQ(info.classpath.size(), info.checksums.size()); - DCHECK_EQ(expected_info.classpath.size(), expected_info.checksums.size()); + if (verify_checksums) { + DCHECK_EQ(info.classpath.size(), info.checksums.size()); + DCHECK_EQ(expected_info.classpath.size(), expected_info.checksums.size()); + } + + if (!verify_names) { + continue; + } for (size_t k = 0; k < info.classpath.size(); k++) { // Compute the dex location that must be compared. diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h index 692a6cda5b..231acc46ac 100644 --- a/runtime/class_loader_context.h +++ b/runtime/class_loader_context.h @@ -104,7 +104,11 @@ class ClassLoaderContext { // - the class loader from the same position have the same classpath // (the order and checksum of the dex files matches) // This should be called after OpenDexFiles(). - bool VerifyClassLoaderContextMatch(const std::string& context_spec) const; + // Names are only verified if verify_names is true. + // Checksums are only verified if verify_checksums is true. + bool VerifyClassLoaderContextMatch(const std::string& context_spec, + bool verify_names = true, + bool verify_checksums = true) const; // Creates the class loader context from the given string. // The format: ClassLoaderType1[ClasspathElem1:ClasspathElem2...];ClassLoaderType2[...]... -- GitLab From 5d2dbf8958857d139e5f7e06940b2f6b713bc522 Mon Sep 17 00:00:00 2001 From: Dan Willemsen Date: Wed, 11 Apr 2018 12:31:11 -0700 Subject: [PATCH 239/749] Fix build warnings for x86+arm targets This likely doesn't fix all of the issues, but it at least removes the pages of overriding command warnings for these configurations. Test: lunch aosp_x86_arm-eng; m nothing Test: build-aosp_arm.ninja is identical Test: build-aosp_arm64.ninja is identical Change-Id: Iacdfd56d098eec08cf588c98ca00c7c7e10c6261 --- Android.mk | 2 +- build/Android.common.mk | 2 -- build/Android.gtest.mk | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Android.mk b/Android.mk index e4f4e74cb2..47bcaac343 100644 --- a/Android.mk +++ b/Android.mk @@ -298,7 +298,7 @@ test-art-target-jit$(ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test-jit $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) # Secondary target architecture variants: -ifdef TARGET_2ND_ARCH +ifdef 2ND_ART_PHONY_TEST_TARGET_SUFFIX .PHONY: test-art-target$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) test-art-target$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-gtest$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) \ test-art-target-run-test$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) diff --git a/build/Android.common.mk b/build/Android.common.mk index b0fa124e48..a6a9f0fc47 100644 --- a/build/Android.common.mk +++ b/build/Android.common.mk @@ -59,8 +59,6 @@ ifdef TARGET_2ND_ARCH ART_PHONY_TEST_TARGET_SUFFIX := 64 2ND_ART_PHONY_TEST_TARGET_SUFFIX := 32 else - # TODO: ??? - $(warning Do not know what to do with this multi-target configuration!) ART_PHONY_TEST_TARGET_SUFFIX := 32 2ND_ART_PHONY_TEST_TARGET_SUFFIX := endif diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 7ca7409f88..920c3b972c 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -688,7 +688,7 @@ endef # define-art-gtest-host-both ifeq ($(ART_BUILD_TARGET),true) $(foreach file,$(ART_TARGET_GTEST_FILES), $(eval $(call define-art-gtest-target,$(file),))) - ifdef TARGET_2ND_ARCH + ifdef 2ND_ART_PHONY_TEST_TARGET_SUFFIX $(foreach file,$(2ND_ART_TARGET_GTEST_FILES), $(eval $(call define-art-gtest-target,$(file),2ND_))) endif # Rules to run the different architecture versions of the gtest. @@ -748,7 +748,7 @@ $(eval $(call define-test-art-gtest-combination,target,TARGET,,)) $(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,)) $(eval $(call define-test-art-gtest-combination,target,TARGET,,$(ART_PHONY_TEST_TARGET_SUFFIX))) $(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,$(ART_PHONY_TEST_TARGET_SUFFIX))) -ifdef TARGET_2ND_ARCH +ifdef 2ND_ART_PHONY_TEST_TARGET_SUFFIX $(eval $(call define-test-art-gtest-combination,target,TARGET,,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX))) $(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX))) endif -- GitLab From 2e0478a62e314ff4ce2334da7608543fa93e7c68 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 6 Apr 2018 14:33:25 -0700 Subject: [PATCH 240/749] Add dexanalyze tool Used to gather statistics about dex files. Sample output: Cumulative analysis for 240 DEX files Num string ids: 9809636 Num method ids: 11334990 Num field ids: 8281881 Num type ids: 2291729 Num class defs: 1604550 Same class direct: 1577080 Other class direct: 5114002 Same class virtual: 2118445 Other class virtual: 15998142 Same class static: 693507 Other class static: 6496367 Num strings accessed from code: 5832974 Unique(per class) method ids accessed from code: 15169103 Unique(per class) string ids accessed from code: 4279695 Same class invoke: 4389032 Other class invoke: 27608511 Invokes from code: 31997543 Bug: 77709234 Bug: 77721545 Test: time dexanalyze -count-indices Test: mm test-art-host-gtest-dexanalyze_test -j64 Test: mm test-art-target-gtest-dexanalyze_test -j64 Change-Id: I3cd3fd10a9c540c7780942fc33f4eb4c7ff5d914 --- build/Android.gtest.mk | 9 + tools/dexanalyze/Android.bp | 50 +++++ tools/dexanalyze/dexanalyze.cc | 300 ++++++++++++++++++++++++++++ tools/dexanalyze/dexanalyze_test.cc | 47 +++++ 4 files changed, 406 insertions(+) create mode 100644 tools/dexanalyze/Android.bp create mode 100644 tools/dexanalyze/dexanalyze.cc create mode 100644 tools/dexanalyze/dexanalyze_test.cc diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 7ca7409f88..4fedfb9b7d 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -172,6 +172,7 @@ ART_GTEST_class_loader_context_test_DEX_DEPS := Main MultiDex MyClass ForClassLo ART_GTEST_class_table_test_DEX_DEPS := XandY ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes +ART_GTEST_dexanalyze_test_DEX_DEPS := MultiDex ART_GTEST_dexlayout_test_DEX_DEPS := ManyMethods ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ManyMethods Statics VerifierDeps MainUncompressed EmptyUncompressed ART_GTEST_dex2oat_image_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps @@ -311,6 +312,12 @@ ART_GTEST_imgdiag_test_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_DEFAULT_32) \ imgdiagd-target +# Dex analyze test requires dexanalyze. +ART_GTEST_dexanalyze_test_HOST_DEPS := \ + dexanalyze-host +ART_GTEST_dexanalyze_test_TARGET_DEPS := \ + dexanalyze-target + # Oatdump test requires an image and oatfile to dump. ART_GTEST_oatdump_test_HOST_DEPS := \ $(HOST_CORE_IMAGE_DEFAULT_64) \ @@ -353,6 +360,7 @@ ART_TEST_MODULES := \ art_compiler_tests \ art_compiler_host_tests \ art_dex2oat_tests \ + art_dexanalyze_tests \ art_dexdiag_tests \ art_dexdump_tests \ art_dexlayout_tests \ @@ -798,6 +806,7 @@ ART_GTEST_jni_internal_test_DEX_DEPS := ART_GTEST_oat_file_assistant_test_DEX_DEPS := ART_GTEST_oat_file_assistant_test_HOST_DEPS := ART_GTEST_oat_file_assistant_test_TARGET_DEPS := +ART_GTEST_dexanalyzer_test_DEX_DEPS := ART_GTEST_dexoptanalyzer_test_DEX_DEPS := ART_GTEST_dexoptanalyzer_test_HOST_DEPS := ART_GTEST_dexoptanalyzer_test_TARGET_DEPS := diff --git a/tools/dexanalyze/Android.bp b/tools/dexanalyze/Android.bp new file mode 100644 index 0000000000..34aaaac451 --- /dev/null +++ b/tools/dexanalyze/Android.bp @@ -0,0 +1,50 @@ +// +// 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. +// + +cc_defaults { + name: "dexanalyze-defaults", + defaults: ["art_defaults"], + host_supported: true, + srcs: ["dexanalyze.cc"], + target: { + android: { + shared_libs: ["libcutils"], + }, + }, + header_libs: [ + "art_cmdlineparser_headers", + ], +} + +art_cc_binary { + name: "dexanalyze", + defaults: ["dexanalyze-defaults"], + shared_libs: [ + "libdexfile", + "libbase", + ], +} + +art_cc_test { + name: "art_dexanalyze_tests", + required: ["dexanalyze"], + defaults: [ + "art_gtest_defaults", + ], + srcs: [ + "dexanalyze_test.cc", + ], +} diff --git a/tools/dexanalyze/dexanalyze.cc b/tools/dexanalyze/dexanalyze.cc new file mode 100644 index 0000000000..75fa13cc03 --- /dev/null +++ b/tools/dexanalyze/dexanalyze.cc @@ -0,0 +1,300 @@ +/* + * 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. + */ + +#include +#include +#include + +#include + +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_file.h" +#include "dex/dex_file_loader.h" +#include "dex/dex_instruction-inl.h" + +namespace art { + +// An experiment a stateful visitor that runs on dex files. Results are cumulative. +class Experiment { + public: + virtual ~Experiment() {} + virtual void ProcessDexFile(const DexFile& dex_file) = 0; + virtual void Dump(std::ostream& os) const = 0; +}; + +class CountDexIndices : public Experiment { + public: + void ProcessDexFile(const DexFile& dex_file) { + num_string_ids_ += dex_file.NumStringIds(); + num_method_ids_ += dex_file.NumMethodIds(); + num_field_ids_ += dex_file.NumFieldIds(); + num_type_ids_ += dex_file.NumTypeIds(); + num_class_defs_ += dex_file.NumClassDefs(); + for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); ++class_def_index) { + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); + const uint8_t* class_data = dex_file.GetClassData(class_def); + if (class_data == nullptr) { + continue; + } + ClassDataItemIterator it(dex_file, class_data); + it.SkipAllFields(); + std::set unique_method_ids; + std::set unique_string_ids; + while (it.HasNextMethod()) { + const DexFile::CodeItem* code_item = it.GetMethodCodeItem(); + if (code_item != nullptr) { + CodeItemInstructionAccessor instructions(dex_file, code_item); + const uint16_t* code_ptr = instructions.Insns(); + dex_code_bytes_ += instructions.InsnsSizeInCodeUnits() * sizeof(code_ptr[0]); + for (const DexInstructionPcPair& inst : instructions) { + switch (inst->Opcode()) { + case Instruction::CONST_STRING: { + const dex::StringIndex string_index(inst->VRegB_21c()); + unique_string_ids.insert(string_index.index_); + ++num_string_ids_from_code_; + break; + } + case Instruction::CONST_STRING_JUMBO: { + const dex::StringIndex string_index(inst->VRegB_31c()); + unique_string_ids.insert(string_index.index_); + ++num_string_ids_from_code_; + break; + } + // Invoke cases. + case Instruction::INVOKE_VIRTUAL: + case Instruction::INVOKE_VIRTUAL_RANGE: { + bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE); + uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); + if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) { + ++same_class_virtual_; + } else { + ++other_class_virtual_; + unique_method_ids.insert(method_idx); + } + break; + } + case Instruction::INVOKE_DIRECT: + case Instruction::INVOKE_DIRECT_RANGE: { + bool is_range = (inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE); + uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) { + ++same_class_direct_; + } else { + ++other_class_direct_; + unique_method_ids.insert(method_idx); + } + break; + } + case Instruction::INVOKE_STATIC: + case Instruction::INVOKE_STATIC_RANGE: { + bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE); + uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) { + ++same_class_static_; + } else { + ++other_class_static_; + unique_method_ids.insert(method_idx); + } + break; + } + default: + break; + } + } + } + it.Next(); + } + DCHECK(!it.HasNext()); + total_unique_method_idx_ += unique_method_ids.size(); + total_unique_string_ids_ += unique_string_ids.size(); + } + } + + void Dump(std::ostream& os) const { + os << "Num string ids: " << num_string_ids_ << "\n"; + os << "Num method ids: " << num_method_ids_ << "\n"; + os << "Num field ids: " << num_field_ids_ << "\n"; + os << "Num type ids: " << num_type_ids_ << "\n"; + os << "Num class defs: " << num_class_defs_ << "\n"; + os << "Same class direct: " << same_class_direct_ << "\n"; + os << "Other class direct: " << other_class_direct_ << "\n"; + os << "Same class virtual: " << same_class_virtual_ << "\n"; + os << "Other class virtual: " << other_class_virtual_ << "\n"; + os << "Same class static: " << same_class_static_ << "\n"; + os << "Other class static: " << other_class_static_ << "\n"; + os << "Num strings accessed from code: " << num_string_ids_from_code_ << "\n"; + os << "Unique(per class) method ids accessed from code: " << total_unique_method_idx_ << "\n"; + os << "Unique(per class) string ids accessed from code: " << total_unique_string_ids_ << "\n"; + size_t same_class_total = same_class_direct_ + same_class_virtual_ + same_class_static_; + size_t other_class_total = other_class_direct_ + other_class_virtual_ + other_class_static_; + os << "Same class invoke: " << same_class_total << "\n"; + os << "Other class invoke: " << other_class_total << "\n"; + os << "Invokes from code: " << (same_class_total + other_class_total) << "\n"; + } + + private: + // Total string ids loaded from dex code. + size_t num_string_ids_from_code_ = 0; + size_t total_unique_method_idx_ = 0; + size_t total_unique_string_ids_ = 0; + + // Other dex ids. + size_t dex_code_bytes_ = 0; + size_t num_string_ids_ = 0; + size_t num_method_ids_ = 0; + size_t num_field_ids_ = 0; + size_t num_type_ids_ = 0; + size_t num_class_defs_ = 0; + + // Invokes + size_t same_class_direct_ = 0; + size_t other_class_direct_ = 0; + size_t same_class_virtual_ = 0; + size_t other_class_virtual_ = 0; + size_t same_class_static_ = 0; + size_t other_class_static_ = 0; +}; + +class DexAnalyze { + static const int kExitCodeUsageError = 1; + + static int Usage(char** argv) { + LOG(ERROR) + << "Usage " << argv[0] << " [options] \n" + << " [options] is a combination of the following\n" + << " -count_indices (Count dex indices accessed from code items)\n" + << " -i (Ignore DEX checksum failure)\n" + << " -a (Run all experiments)\n" + << " -d (Dump on per DEX basis)\n"; + return kExitCodeUsageError; + } + + struct Options { + int Parse(int argc, char** argv) { + int i; + for (i = 1; i < argc; ++i) { + const std::string arg = argv[i]; + if (arg == "-i") { + verify_checksum_ = false; + } else if (arg == "-a") { + run_all_experiments_ = true; + } else if (arg == "-count-indices") { + exp_count_indices_ = true; + } else if (arg == "-d") { + dump_per_input_dex_ = true; + } else if (!arg.empty() && arg[0] == '-') { + return Usage(argv); + } else { + break; + } + } + filenames_.insert(filenames_.end(), argv + i, argv + argc); + if (filenames_.empty()) { + return Usage(argv); + } + return 0; + } + + bool verify_checksum_ = true; + bool run_dex_file_verifier_ = true; + bool dump_per_input_dex_ = false; + bool exp_count_indices_ = false; + bool run_all_experiments_ = false; + std::vector filenames_; + }; + + class Analysis { + public: + explicit Analysis(const Options* options) : options_(options) { + if (options->run_all_experiments_ || options->exp_count_indices_) { + experiments_.emplace_back(new CountDexIndices); + } + } + + bool ProcessDexFile(const DexFile& dex_file) { + for (std::unique_ptr& experiment : experiments_) { + experiment->ProcessDexFile(dex_file); + } + ++dex_count_; + return true; + } + + void Dump(std::ostream& os) { + for (std::unique_ptr& experiment : experiments_) { + experiment->Dump(os); + } + } + + const Options* const options_; + std::vector> experiments_; + size_t dex_count_ = 0; + }; + + public: + static int Run(int argc, char** argv) { + Options options; + int result = options.Parse(argc, argv); + if (result != 0) { + return result; + } + + std::string error_msg; + Analysis cumulative(&options); + for (const std::string& filename : options.filenames_) { + std::string content; + // TODO: once added, use an api to android::base to read a std::vector. + if (!android::base::ReadFileToString(filename.c_str(), &content)) { + LOG(ERROR) << "ReadFileToString failed for " + filename << std::endl; + continue; + } + std::vector> dex_files; + const DexFileLoader dex_file_loader; + if (!dex_file_loader.OpenAll(reinterpret_cast(content.data()), + content.size(), + filename.c_str(), + options.run_dex_file_verifier_, + options.verify_checksum_, + &error_msg, + &dex_files)) { + LOG(ERROR) << "OpenAll failed for " + filename << " with " << error_msg << std::endl; + continue; + } + for (std::unique_ptr& dex_file : dex_files) { + if (options.dump_per_input_dex_) { + Analysis current(&options); + if (!current.ProcessDexFile(*dex_file)) { + LOG(ERROR) << "Failed to process " << filename << " with error " << error_msg; + continue; + } + LOG(INFO) << "Analysis for " << dex_file->GetLocation() << std::endl; + current.Dump(LOG_STREAM(INFO)); + } + cumulative.ProcessDexFile(*dex_file); + } + } + LOG(INFO) << "Cumulative analysis for " << cumulative.dex_count_ << " DEX files" << std::endl; + cumulative.Dump(LOG_STREAM(INFO)); + return 0; + } +}; + +} // namespace art + +int main(int argc, char** argv) { + android::base::SetLogger(android::base::StderrLogger); + return art::DexAnalyze::Run(argc, argv); +} + diff --git a/tools/dexanalyze/dexanalyze_test.cc b/tools/dexanalyze/dexanalyze_test.cc new file mode 100644 index 0000000000..c9b8f53d24 --- /dev/null +++ b/tools/dexanalyze/dexanalyze_test.cc @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#include "common_runtime_test.h" +#include "exec_utils.h" + +namespace art { + +class DexAnalyzeTest : public CommonRuntimeTest { + public: + std::string GetDexAnalyzePath() { + return GetTestAndroidRoot() + "/bin/dexanalyze"; + } + + void DexAnalyzeExec(const std::vector& args, bool expect_success) { + std::string binary = GetDexAnalyzePath(); + CHECK(OS::FileExists(binary.c_str())) << binary << " should be a valid file path"; + std::vector argv; + argv.push_back(binary); + argv.insert(argv.end(), args.begin(), args.end()); + std::string error_msg; + ASSERT_EQ(::art::Exec(argv, &error_msg), expect_success) << error_msg; + } +}; + +TEST_F(DexAnalyzeTest, TestAnalyzeMultidex) { + DexAnalyzeExec({ "-a", GetTestDexFileName("MultiDex") }, /*expect_success*/ true); +} + +TEST_F(DexAnalyzeTest, TestInvalidArg) { + DexAnalyzeExec({ "-invalid-option" }, /*expect_success*/ false); +} + +} // namespace art -- GitLab From db20a4b08389e73d4f74308644100e5e202fe2bc Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 11 Apr 2018 15:44:19 -0700 Subject: [PATCH 241/749] ART: Fix core image detection Accept any .art name starting with "core-." Correctly detects images with multiple dashes. Bug: 64382372 Test: mmma art Test: art/test/testrunner/testrunner.py -b --host Test: Device boots Change-Id: I6c8006411b0ec16225b137dd349d53d9bc0ac03d --- compiler/driver/compiler_driver.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 682cf16d45..a5462eefe2 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -378,11 +378,11 @@ class CompilerDriver { if (!android::base::EndsWith(boot_image_filename, ".art")) { return false; } - size_t dash_pos = boot_image_filename.find_last_of("-/"); - if (dash_pos == std::string::npos || boot_image_filename[dash_pos] != '-') { - return false; + size_t slash_pos = boot_image_filename.rfind('/'); + if (slash_pos == std::string::npos) { + return android::base::StartsWith(boot_image_filename, "core-"); } - return (dash_pos >= 4u) && (boot_image_filename.compare(dash_pos - 4u, 4u, "core") == 0); + return boot_image_filename.compare(slash_pos + 1, 5u, "core-") == 0; } optimizer::DexToDexCompiler& GetDexToDexCompiler() { -- GitLab From 283ad2dfa5610e098db4791c9aded3a05fb62c40 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Mon, 26 Mar 2018 13:37:41 +0100 Subject: [PATCH 242/749] ART: Memory order updates to trace.{h,cc} Adds comments and DCHECKS to clarify memory semantics. Relaxes some atomic operations on Trace::cur_offset_ and makes its initialization sequentially-consistent. Adds GUARDED_BY annotations to fields only accessed with the streaming_lock_. Bug: 31023171 Test: art/test.py --host --stream -j32 Test: art/test.py --host --trace -j32 Test: art/test.py --target --stream -j4 Test: art/test.py --target --trace -j4 Change-Id: I31ad9a3861c7f258ed225207b3c2b39669ad8d36 --- runtime/Android.bp | 1 + runtime/trace.cc | 79 +++++++++++++++++++++++++++++++--------------- runtime/trace.h | 39 +++++++++++++++++++---- 3 files changed, 87 insertions(+), 32 deletions(-) diff --git a/runtime/Android.bp b/runtime/Android.bp index 00d4a6080a..e510d544a9 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -449,6 +449,7 @@ gensrcs { "thread.h", "thread_state.h", "ti/agent.h", + "trace.h", "verifier/verifier_enums.h", ], output_extension: "operator_out.cc", diff --git a/runtime/trace.cc b/runtime/trace.cc index bea510ab61..292cac6d0a 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -470,24 +470,30 @@ void Trace::StopTracing(bool finish_tracing, bool flush_file) { if (the_trace != nullptr) { stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0; + // Stop the trace sources adding more entries to the trace buffer and synchronise stores. + { + gc::ScopedGCCriticalSection gcs(self, + gc::kGcCauseInstrumentation, + gc::kCollectorTypeInstrumentation); + ScopedSuspendAll ssa(__FUNCTION__); + + if (the_trace->trace_mode_ == TraceMode::kSampling) { + MutexLock mu(self, *Locks::thread_list_lock_); + runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr); + } else { + runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey); + runtime->GetInstrumentation()->RemoveListener( + the_trace, instrumentation::Instrumentation::kMethodEntered | + instrumentation::Instrumentation::kMethodExited | + instrumentation::Instrumentation::kMethodUnwind); + } + } + // At this point, code may read buf_ as it's writers are shutdown + // and the ScopedSuspendAll above has ensured all stores to buf_ + // are now visible. if (finish_tracing) { the_trace->FinishTracing(); } - gc::ScopedGCCriticalSection gcs(self, - gc::kGcCauseInstrumentation, - gc::kCollectorTypeInstrumentation); - ScopedSuspendAll ssa(__FUNCTION__); - - if (the_trace->trace_mode_ == TraceMode::kSampling) { - MutexLock mu(self, *Locks::thread_list_lock_); - runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr); - } else { - runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey); - runtime->GetInstrumentation()->RemoveListener( - the_trace, instrumentation::Instrumentation::kMethodEntered | - instrumentation::Instrumentation::kMethodExited | - instrumentation::Instrumentation::kMethodUnwind); - } if (the_trace->trace_file_.get() != nullptr) { // Do not try to erase, so flush and close explicitly. if (flush_file) { @@ -653,7 +659,7 @@ Trace::Trace(File* trace_file, flags_(flags), trace_output_mode_(output_mode), trace_mode_(trace_mode), clock_source_(default_clock_source_), buffer_size_(std::max(kMinBufSize, buffer_size)), - start_time_(MicroTime()), clock_overhead_ns_(GetClockOverheadNanoSeconds()), cur_offset_(0), + start_time_(MicroTime()), clock_overhead_ns_(GetClockOverheadNanoSeconds()), overflow_(false), interval_us_(0), streaming_lock_(nullptr), unique_methods_lock_(new Mutex("unique methods lock", kTracingUniqueMethodsLock)) { CHECK(trace_file != nullptr || output_mode == TraceOutputMode::kDDMS); @@ -674,7 +680,6 @@ Trace::Trace(File* trace_file, } static_assert(18 <= kMinBufSize, "Minimum buffer size not large enough for trace header"); - // Update current offset. cur_offset_.store(kTraceHeaderLength, std::memory_order_relaxed); if (output_mode == TraceOutputMode::kStreaming) { @@ -711,10 +716,10 @@ void Trace::DumpBuf(uint8_t* buf, size_t buf_size, TraceClockSource clock_source void Trace::FinishTracing() { size_t final_offset = 0; - std::set visited_methods; if (trace_output_mode_ == TraceOutputMode::kStreaming) { // Clean up. + MutexLock mu(Thread::Current(), *streaming_lock_); STLDeleteValues(&seen_methods_); } else { final_offset = cur_offset_.load(std::memory_order_relaxed); @@ -759,7 +764,8 @@ void Trace::FinishTracing() { std::string header(os.str()); if (trace_output_mode_ == TraceOutputMode::kStreaming) { - MutexLock mu(Thread::Current(), *streaming_lock_); // To serialize writing. + // Protect access to buf_ and satisfy sanitizer for calls to WriteBuf / FlushBuf. + MutexLock mu(Thread::Current(), *streaming_lock_); // Write a special token to mark the end of trace records and the start of // trace summary. uint8_t buf[7]; @@ -944,6 +950,7 @@ std::string Trace::GetMethodLine(ArtMethod* method) { } void Trace::WriteToBuf(const uint8_t* src, size_t src_size) { + // Updates to cur_offset_ are done under the streaming_lock_ here as in streaming mode. int32_t old_offset = cur_offset_.load(std::memory_order_relaxed); int32_t new_offset = old_offset + static_cast(src_size); if (dchecked_integral_cast(new_offset) > buffer_size_) { @@ -957,46 +964,59 @@ void Trace::WriteToBuf(const uint8_t* src, size_t src_size) { if (!trace_file_->WriteFully(src, src_size)) { PLOG(WARNING) << "Failed streaming a tracing event."; } - cur_offset_.store(0, std::memory_order_release); // Buffer is empty now. + cur_offset_.store(0, std::memory_order_relaxed); // Buffer is empty now. return; } old_offset = 0; new_offset = static_cast(src_size); } - cur_offset_.store(new_offset, std::memory_order_release); + cur_offset_.store(new_offset, std::memory_order_relaxed); // Fill in data. memcpy(buf_.get() + old_offset, src, src_size); } void Trace::FlushBuf() { + // Updates to cur_offset_ are done under the streaming_lock_ here as in streaming mode. int32_t offset = cur_offset_.load(std::memory_order_relaxed); if (!trace_file_->WriteFully(buf_.get(), offset)) { PLOG(WARNING) << "Failed flush the remaining data in streaming."; } - cur_offset_.store(0, std::memory_order_release); + cur_offset_.store(0, std::memory_order_relaxed); } void Trace::LogMethodTraceEvent(Thread* thread, ArtMethod* method, instrumentation::Instrumentation::InstrumentationEvent event, uint32_t thread_clock_diff, uint32_t wall_clock_diff) { + // This method is called in both tracing modes (method and + // sampling). In sampling mode, this method is only called by the + // sampling thread. In method tracing mode, it can be called + // concurrently. + // Ensure we always use the non-obsolete version of the method so that entry/exit events have the // same pointer value. method = method->GetNonObsoleteMethod(); + // Advance cur_offset_ atomically. int32_t new_offset; int32_t old_offset = 0; - // We do a busy loop here trying to acquire the next offset. + // In the non-streaming case, we do a busy loop here trying to get + // an offset to write our record and advance cur_offset_ for the + // next use. if (trace_output_mode_ != TraceOutputMode::kStreaming) { + // Although multiple threads can call this method concurrently, + // the compare_exchange_weak here is still atomic (by definition). + // A succeeding update is visible to other cores when they pass + // through this point. + old_offset = cur_offset_.load(std::memory_order_relaxed); // Speculative read do { - old_offset = cur_offset_.load(std::memory_order_relaxed); new_offset = old_offset + GetRecordSize(clock_source_); if (static_cast(new_offset) > buffer_size_) { overflow_ = true; return; } - } while (!cur_offset_.CompareAndSetWeakSequentiallyConsistent(old_offset, new_offset)); + } while (!cur_offset_.compare_exchange_weak(old_offset, new_offset, std::memory_order_relaxed)); } TraceAction action = kTraceMethodEnter; @@ -1016,7 +1036,14 @@ void Trace::LogMethodTraceEvent(Thread* thread, ArtMethod* method, uint32_t method_value = EncodeTraceMethodAndAction(method, action); - // Write data + // Write data into the tracing buffer (if not streaming) or into a + // small buffer on the stack (if streaming) which we'll put into the + // tracing buffer below. + // + // These writes to the tracing buffer are synchronised with the + // future reads that (only) occur under FinishTracing(). The callers + // of FinishTracing() acquire locks and (implicitly) synchronise + // the buffer memory. uint8_t* ptr; static constexpr size_t kPacketSize = 14U; // The maximum size of data in a packet. uint8_t stack_buf[kPacketSize]; // Space to store a packet when in streaming mode. diff --git a/runtime/trace.h b/runtime/trace.h index 7171f759c9..b242d1596c 100644 --- a/runtime/trace.h +++ b/runtime/trace.h @@ -52,9 +52,10 @@ using ThreadIDBitSet = std::bitset; enum TracingMode { kTracingInactive, - kMethodTracingActive, - kSampleProfilingActive, + kMethodTracingActive, // Trace activity synchronous with method progress. + kSampleProfilingActive, // Trace activity captured by sampling thread. }; +std::ostream& operator<<(std::ostream& os, const TracingMode& rhs); // File format: // header @@ -98,6 +99,9 @@ enum TraceAction { kTraceMethodActionMask = 0x03, // two bits }; +// Class for recording event traces. Trace data is either collected +// synchronously during execution (TracingMode::kMethodTracingActive), +// or by a separate sampling thread (TracingMode::kSampleProfilingActive). class Trace FINAL : public instrumentation::InstrumentationListener { public: enum TraceFlag { @@ -316,7 +320,10 @@ class Trace FINAL : public instrumentation::InstrumentationListener { // File to write trace data out to, null if direct to ddms. std::unique_ptr trace_file_; - // Buffer to store trace data. + // Buffer to store trace data. In streaming mode, this is protected + // by the streaming_lock_. In non-streaming mode, reserved regions + // are atomically allocated (using cur_offset_) for log entries to + // be written. std::unique_ptr buf_; // Flags enabling extra tracing of things such as alloc counts. @@ -339,7 +346,27 @@ class Trace FINAL : public instrumentation::InstrumentationListener { // Clock overhead. const uint32_t clock_overhead_ns_; - // Offset into buf_. + // Offset into buf_. The field is atomic to allow multiple writers + // to concurrently reserve space in the buffer. The newly written + // buffer contents are not read without some other form of thread + // synchronization, such as suspending all potential writers or + // acquiring *streaming_lock_. Reading cur_offset_ is thus never + // used to ensure visibility of any other objects, and all accesses + // are memory_order_relaxed. + // + // All accesses to buf_ in streaming mode occur whilst holding the + // streaming lock. In streaming mode, the buffer may be written out + // so cur_offset_ can move forwards and backwards. + // + // When not in streaming mode, the buf_ writes can come from + // multiple threads when the trace mode is kMethodTracing. When + // trace mode is kSampling, writes only come from the sampling + // thread. + // + // Reads to the buffer happen after the event sources writing to the + // buffer have been shutdown and all stores have completed. The + // stores are made visible in StopTracing() when execution leaves + // the ScopedSuspendAll block. AtomicInteger cur_offset_; // Did we overflow the buffer recording traces? @@ -353,8 +380,8 @@ class Trace FINAL : public instrumentation::InstrumentationListener { // Streaming mode data. Mutex* streaming_lock_; - std::map seen_methods_; - std::unique_ptr seen_threads_; + std::map seen_methods_ GUARDED_BY(streaming_lock_); + std::unique_ptr seen_threads_ GUARDED_BY(streaming_lock_); // Bijective map from ArtMethod* to index. // Map from ArtMethod* to index in unique_methods_; -- GitLab From 3f4a0bc6a7d824e9da8ffbd2745f6856abaa8306 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 12 Apr 2018 10:08:58 +0100 Subject: [PATCH 243/749] Add extra output when patchoat_test fails. Print up to 16 bytes of the data from the first difference. Test: m test-art-host-gtest-patchoat_test Bug: 70918261 Change-Id: Icd0247c3a745bfca0bc9745c8756452cd9b89feb --- patchoat/patchoat_test.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/patchoat/patchoat_test.cc b/patchoat/patchoat_test.cc index 974ed3217d..69728ae051 100644 --- a/patchoat/patchoat_test.cc +++ b/patchoat/patchoat_test.cc @@ -24,6 +24,7 @@ #include "android-base/stringprintf.h" #include "android-base/strings.h" +#include "base/hex_dump.h" #include "base/leb128.h" #include "dexopt_test.h" #include "runtime.h" @@ -320,6 +321,12 @@ class PatchoatTest : public DexoptTest { if (image1[i] != image2[i]) { *error_msg = StringPrintf("%s and %s differ at offset %zu", filename1.c_str(), filename2.c_str(), i); + size_t hexdump_size = std::min(16u, size - i); + HexDump dump1(&image1[i], hexdump_size, /* show_actual_addresses */ false, /* prefix */ ""); + HexDump dump2(&image2[i], hexdump_size, /* show_actual_addresses */ false, /* prefix */ ""); + std::ostringstream oss; + oss << "\n" << dump1 << "\n" << dump2; + *error_msg += oss.str(); return true; } } -- GitLab From 957e708d11bfd2b72cdea1cfa109908537267b36 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 11 Apr 2018 17:25:42 +0100 Subject: [PATCH 244/749] Improve debug logging for bug 77342775. The previous logging did not show the class loader and space for `target_class`. Test: Rely on TreeHugger. Bug: 77342775 Bug: 77903751 (cherry picked from commit 2e3667ddabf98531290356bcb09929d481c70616) Change-Id: I03e35688e0227eb74dcd59b99ce7bcad493d7847 --- runtime/debug_print.cc | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/runtime/debug_print.cc b/runtime/debug_print.cc index c7530bec6e..60487670ac 100644 --- a/runtime/debug_print.cc +++ b/runtime/debug_print.cc @@ -73,11 +73,12 @@ std::string DescribeLoaders(ObjPtr loader, const char* clas oss << "BootClassLoader"; // This would be unexpected. } for (; loader != nullptr; loader = loader->GetParent()) { - oss << loader_separator << loader->GetClass()->PrettyDescriptor(); + ClassTable* table = Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(loader); + oss << loader_separator << loader->GetClass()->PrettyDescriptor() + << "/" << static_cast(table); loader_separator = ";"; // If we didn't find the class yet, try to find it in the current class loader. if (!found_class) { - ClassTable* table = Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(loader); ObjPtr klass = (table != nullptr) ? table->Lookup(class_descriptor, hash) : nullptr; if (klass != nullptr) { @@ -99,7 +100,8 @@ std::string DescribeLoaders(ObjPtr loader, const char* clas VisitClassLoaderDexFiles(soa, handle, [&](const DexFile* dex_file) { - oss << path_separator << dex_file->GetLocation(); + oss << path_separator << dex_file->GetLocation() + << "/" << static_cast(dex_file); path_separator = ":"; return true; // Continue with the next DexFile. }); @@ -135,8 +137,9 @@ void DumpB77342775DebugData(ObjPtr target_class, ObjPtrCount(); LOG(ERROR) << "Maybe bug 77342775, looking for " << target_descriptor - << " with loader " << DescribeLoaders(src_class->GetClassLoader(), target_descriptor) - << " in interface table for " << source_descriptor << " ifcount=" << ifcount; + << " with loader " << DescribeLoaders(target_class->GetClassLoader(), target_descriptor) + << " in interface table for " << source_descriptor << " ifcount=" << ifcount + << " with loader " << DescribeLoaders(src_class->GetClassLoader(), source_descriptor); for (size_t i = 0; i != ifcount; ++i) { ObjPtr iface = iftable->GetInterface(i); CHECK(iface != nullptr); @@ -145,8 +148,9 @@ void DumpB77342775DebugData(ObjPtr target_class, ObjPtrGetClassLoader(), target_descriptor) - << " in superclass chain for " << source_descriptor; + << " with loader " << DescribeLoaders(target_class->GetClassLoader(), target_descriptor) + << " in superclass chain for " << source_descriptor + << " with loader " << DescribeLoaders(src_class->GetClassLoader(), source_descriptor); for (ObjPtr klass = src_class; klass != nullptr; klass = klass->GetSuperClass()) { -- GitLab From 7a69505c2f65aef8f016891456c35475f8264d46 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 12 Apr 2018 10:26:50 +0100 Subject: [PATCH 245/749] Rewrite null checks in read barrier introspection thunks. Rely on the implicit null check in the fast path. Test: Manual; run-test --gdb 160, break in the introspection entrypoint, find the mf.testField0000 read barrier code in the caller (this one has a stack map for null check, most other reads do not need one), break there, step into the thunk, overwrite the base register with 0 and observe the NPE being thrown. Repeat with --64. Test: Pixel 2 XL boots. Test: testrunner.py --target --optimizing Bug: 36141117 Change-Id: I61f879f22f5697a4108f1021eb0e3add742c8755 --- compiler/optimizing/code_generator_arm64.cc | 38 ++++++++--------- .../optimizing/code_generator_arm_vixl.cc | 41 ++++++++++--------- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 31887d92e8..d4cfab82de 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -6838,7 +6838,8 @@ void CodeGeneratorARM64::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_ static void EmitGrayCheckAndFastPath(arm64::Arm64Assembler& assembler, vixl::aarch64::Register base_reg, vixl::aarch64::MemOperand& lock_word, - vixl::aarch64::Label* slow_path) { + vixl::aarch64::Label* slow_path, + vixl::aarch64::Label* throw_npe = nullptr) { // Load the lock word containing the rb_state. __ Ldr(ip0.W(), lock_word); // Given the numeric representation, it's enough to check the low bit of the rb_state. @@ -6848,6 +6849,10 @@ static void EmitGrayCheckAndFastPath(arm64::Arm64Assembler& assembler, static_assert( BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET == BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET, "Field and array LDR offsets must be the same to reuse the same code."); + // To throw NPE, we return to the fast path; the artificial dependence below does not matter. + if (throw_npe != nullptr) { + __ Bind(throw_npe); + } // Adjust the return address back to the LDR (1 instruction; 2 for heap poisoning). static_assert(BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4), "Field LDR must be 1 instruction (4B) before the return address label; " @@ -6877,10 +6882,6 @@ void CodeGeneratorARM64::CompileBakerReadBarrierThunk(Arm64Assembler& assembler, BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); switch (kind) { case BakerReadBarrierKind::kField: { - // Check if the holder is gray and, if not, add fake dependency to the base register - // and return to the LDR instruction to load the reference. Otherwise, use introspection - // to load the reference and call the entrypoint (in IP1) that performs further checks - // on the reference and marks it if needed. auto base_reg = Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data)); CheckValidReg(base_reg.GetCode()); @@ -6889,16 +6890,22 @@ void CodeGeneratorARM64::CompileBakerReadBarrierThunk(Arm64Assembler& assembler, CheckValidReg(holder_reg.GetCode()); UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); temps.Exclude(ip0, ip1); - // If base_reg differs from holder_reg, the offset was too large and we must have - // emitted an explicit null check before the load. Otherwise, we need to null-check - // the holder as we do not necessarily do that check before going to the thunk. - vixl::aarch64::Label throw_npe; - if (holder_reg.Is(base_reg)) { - __ Cbz(holder_reg.W(), &throw_npe); + // If base_reg differs from holder_reg, the offset was too large and we must have emitted + // an explicit null check before the load. Otherwise, for implicit null checks, we need to + // null-check the holder as we do not necessarily do that check before going to the thunk. + vixl::aarch64::Label throw_npe_label; + vixl::aarch64::Label* throw_npe = nullptr; + if (GetCompilerOptions().GetImplicitNullChecks() && holder_reg.Is(base_reg)) { + throw_npe = &throw_npe_label; + __ Cbz(holder_reg.W(), throw_npe); } + // Check if the holder is gray and, if not, add fake dependency to the base register + // and return to the LDR instruction to load the reference. Otherwise, use introspection + // to load the reference and call the entrypoint that performs further checks on the + // reference and marks it if needed. vixl::aarch64::Label slow_path; MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value()); - EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path); + EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, throw_npe); __ Bind(&slow_path); MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET); __ Ldr(ip0.W(), ldr_address); // Load the LDR (immediate) unsigned offset. @@ -6907,13 +6914,6 @@ void CodeGeneratorARM64::CompileBakerReadBarrierThunk(Arm64Assembler& assembler, __ Ldr(ip0.W(), MemOperand(base_reg, ip0, LSL, 2)); // Load the reference. // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference. __ Br(ip1); // Jump to the entrypoint. - if (holder_reg.Is(base_reg)) { - // Add null check slow path. The stack map is at the address pointed to by LR. - __ Bind(&throw_npe); - int32_t offset = GetThreadOffset(kQuickThrowNullPointer).Int32Value(); - __ Ldr(ip0, MemOperand(/* Thread* */ vixl::aarch64::x19, offset)); - __ Br(ip0); - } break; } case BakerReadBarrierKind::kArray: { diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 15d952608d..f3705866c2 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -9905,7 +9905,8 @@ static void EmitGrayCheckAndFastPath(ArmVIXLAssembler& assembler, vixl32::Register base_reg, vixl32::MemOperand& lock_word, vixl32::Label* slow_path, - int32_t raw_ldr_offset) { + int32_t raw_ldr_offset, + vixl32::Label* throw_npe = nullptr) { // Load the lock word containing the rb_state. __ Ldr(ip, lock_word); // Given the numeric representation, it's enough to check the low bit of the rb_state. @@ -9913,6 +9914,10 @@ static void EmitGrayCheckAndFastPath(ArmVIXLAssembler& assembler, static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1"); __ Tst(ip, Operand(LockWord::kReadBarrierStateMaskShifted)); __ B(ne, slow_path, /* is_far_target */ false); + // To throw NPE, we return to the fast path; the artificial dependence below does not matter. + if (throw_npe != nullptr) { + __ Bind(throw_npe); + } __ Add(lr, lr, raw_ldr_offset); // Introduce a dependency on the lock_word including rb_state, // to prevent load-load reordering, and without using @@ -9926,7 +9931,7 @@ static void EmitGrayCheckAndFastPath(ArmVIXLAssembler& assembler, static void LoadReadBarrierMarkIntrospectionEntrypoint(ArmVIXLAssembler& assembler, vixl32::Register entrypoint) { // The register where the read barrier introspection entrypoint is loaded - // is fixed: `Thumb2RelativePatcher::kBakerCcEntrypointRegister` (R4). + // is fixed: `kBakerCcEntrypointRegister` (R4). DCHECK(entrypoint.Is(kBakerCcEntrypointRegister)); // entrypoint = Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection. DCHECK_EQ(ip.GetCode(), 12u); @@ -9941,10 +9946,6 @@ void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assemb BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data); switch (kind) { case BakerReadBarrierKind::kField: { - // Check if the holder is gray and, if not, add fake dependency to the base register - // and return to the LDR instruction to load the reference. Otherwise, use introspection - // to load the reference and call the entrypoint (in kBakerCcEntrypointRegister) - // that performs further checks on the reference and marks it if needed. vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data)); CheckValidReg(base_reg.GetCode()); vixl32::Register holder_reg(BakerReadBarrierSecondRegField::Decode(encoded_data)); @@ -9952,19 +9953,26 @@ void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assemb BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data); UseScratchRegisterScope temps(assembler.GetVIXLAssembler()); temps.Exclude(ip); - // If base_reg differs from holder_reg, the offset was too large and we must have - // emitted an explicit null check before the load. Otherwise, we need to null-check - // the holder as we do not necessarily do that check before going to the thunk. - vixl32::Label throw_npe; - if (holder_reg.Is(base_reg)) { - __ CompareAndBranchIfZero(holder_reg, &throw_npe, /* is_far_target */ false); + // If base_reg differs from holder_reg, the offset was too large and we must have emitted + // an explicit null check before the load. Otherwise, for implicit null checks, we need to + // null-check the holder as we do not necessarily do that check before going to the thunk. + vixl32::Label throw_npe_label; + vixl32::Label* throw_npe = nullptr; + if (GetCompilerOptions().GetImplicitNullChecks() && holder_reg.Is(base_reg)) { + throw_npe = &throw_npe_label; + __ CompareAndBranchIfZero(holder_reg, throw_npe, /* is_far_target */ false); } + // Check if the holder is gray and, if not, add fake dependency to the base register + // and return to the LDR instruction to load the reference. Otherwise, use introspection + // to load the reference and call the entrypoint that performs further checks on the + // reference and marks it if needed. vixl32::Label slow_path; MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value()); const int32_t raw_ldr_offset = (width == BakerReadBarrierWidth::kWide) ? BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET : BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET; - EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset); + EmitGrayCheckAndFastPath( + assembler, base_reg, lock_word, &slow_path, raw_ldr_offset, throw_npe); __ Bind(&slow_path); const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 + raw_ldr_offset; @@ -9986,13 +9994,6 @@ void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assemb } // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference. __ Bx(ep_reg); // Jump to the entrypoint. - if (holder_reg.Is(base_reg)) { - // Add null check slow path. The stack map is at the address pointed to by LR. - __ Bind(&throw_npe); - int32_t offset = GetThreadOffset(kQuickThrowNullPointer).Int32Value(); - __ Ldr(ip, MemOperand(/* Thread* */ vixl32::r9, offset)); - __ Bx(ip); - } break; } case BakerReadBarrierKind::kArray: { -- GitLab From 6d66fcf87dee9d2207ea745969b1f89836b0f8c5 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 12 Apr 2018 13:15:27 +0100 Subject: [PATCH 246/749] Move RelativePatcher from libart-compiler.so to dex2oat. For AOSP master, aosp_taimen-userdebug: - before: /system/lib/libart-compiler.so: 2084640 /system/lib64/libart-compiler.so: 2921496 /bin/dex2oat: 528372 - after: /system/lib/libart-compiler.so: 2059264 (-24.8KiB) /system/lib64/libart-compiler.so: 2920856 (-640B) /bin/dex2oat: 550056 (+21.2KiB) Test: m Test: m test-art-host-gtest Bug: 77951326 Change-Id: I8687ab9e00049acc46a6d229e5121b36d5379737 --- compiler/Android.bp | 18 +---- .../optimizing/code_generator_arm_vixl.cc | 1 - dex2oat/Android.bp | 72 +++++++++++++++++++ .../linker/arm/relative_patcher_arm_base.cc | 0 .../linker/arm/relative_patcher_arm_base.h | 6 +- .../linker/arm/relative_patcher_thumb2.cc | 0 .../linker/arm/relative_patcher_thumb2.h | 6 +- .../arm/relative_patcher_thumb2_test.cc | 0 .../linker/arm64/relative_patcher_arm64.cc | 0 .../linker/arm64/relative_patcher_arm64.h | 6 +- .../arm64/relative_patcher_arm64_test.cc | 0 .../linker/mips/relative_patcher_mips.cc | 0 .../linker/mips/relative_patcher_mips.h | 6 +- .../mips/relative_patcher_mips32r6_test.cc | 0 .../linker/mips/relative_patcher_mips_test.cc | 0 .../linker/mips64/relative_patcher_mips64.cc | 0 .../linker/mips64/relative_patcher_mips64.h | 6 +- .../mips64/relative_patcher_mips64_test.cc | 0 .../linker/relative_patcher.cc | 2 +- .../linker/relative_patcher.h | 6 +- .../linker/relative_patcher_test.h | 8 +-- .../linker/x86/relative_patcher_x86.cc | 0 .../linker/x86/relative_patcher_x86.h | 6 +- .../linker/x86/relative_patcher_x86_base.cc | 0 .../linker/x86/relative_patcher_x86_base.h | 6 +- .../linker/x86/relative_patcher_x86_test.cc | 0 .../linker/x86_64/relative_patcher_x86_64.cc | 0 .../linker/x86_64/relative_patcher_x86_64.h | 6 +- .../x86_64/relative_patcher_x86_64_test.cc | 0 29 files changed, 106 insertions(+), 49 deletions(-) rename {compiler => dex2oat}/linker/arm/relative_patcher_arm_base.cc (100%) rename {compiler => dex2oat}/linker/arm/relative_patcher_arm_base.h (96%) rename {compiler => dex2oat}/linker/arm/relative_patcher_thumb2.cc (100%) rename {compiler => dex2oat}/linker/arm/relative_patcher_thumb2.h (93%) rename {compiler => dex2oat}/linker/arm/relative_patcher_thumb2_test.cc (100%) rename {compiler => dex2oat}/linker/arm64/relative_patcher_arm64.cc (100%) rename {compiler => dex2oat}/linker/arm64/relative_patcher_arm64.h (94%) rename {compiler => dex2oat}/linker/arm64/relative_patcher_arm64_test.cc (100%) rename {compiler => dex2oat}/linker/mips/relative_patcher_mips.cc (100%) rename {compiler => dex2oat}/linker/mips/relative_patcher_mips.h (92%) rename {compiler => dex2oat}/linker/mips/relative_patcher_mips32r6_test.cc (100%) rename {compiler => dex2oat}/linker/mips/relative_patcher_mips_test.cc (100%) rename {compiler => dex2oat}/linker/mips64/relative_patcher_mips64.cc (100%) rename {compiler => dex2oat}/linker/mips64/relative_patcher_mips64.h (90%) rename {compiler => dex2oat}/linker/mips64/relative_patcher_mips64_test.cc (100%) rename {compiler => dex2oat}/linker/relative_patcher.cc (99%) rename {compiler => dex2oat}/linker/relative_patcher.h (97%) rename {compiler => dex2oat}/linker/relative_patcher_test.h (98%) rename {compiler => dex2oat}/linker/x86/relative_patcher_x86.cc (100%) rename {compiler => dex2oat}/linker/x86/relative_patcher_x86.h (88%) rename {compiler => dex2oat}/linker/x86/relative_patcher_x86_base.cc (100%) rename {compiler => dex2oat}/linker/x86/relative_patcher_x86_base.h (90%) rename {compiler => dex2oat}/linker/x86/relative_patcher_x86_test.cc (100%) rename {compiler => dex2oat}/linker/x86_64/relative_patcher_x86_64.cc (100%) rename {compiler => dex2oat}/linker/x86_64/relative_patcher_x86_64.h (87%) rename {compiler => dex2oat}/linker/x86_64/relative_patcher_x86_64_test.cc (100%) diff --git a/compiler/Android.bp b/compiler/Android.bp index 6bed48e107..cde64b058c 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -39,7 +39,6 @@ art_cc_defaults { "linker/file_output_stream.cc", "linker/output_stream.cc", "linker/vector_output_stream.cc", - "linker/relative_patcher.cc", "jit/jit_compiler.cc", "jit/jit_logger.cc", "jni/quick/calling_convention.cc", @@ -102,8 +101,6 @@ art_cc_defaults { arm: { srcs: [ "jni/quick/arm/calling_convention_arm.cc", - "linker/arm/relative_patcher_arm_base.cc", - "linker/arm/relative_patcher_thumb2.cc", "optimizing/code_generator_arm_vixl.cc", "optimizing/code_generator_vector_arm_vixl.cc", "optimizing/instruction_simplifier_arm.cc", @@ -120,7 +117,6 @@ art_cc_defaults { arm64: { srcs: [ "jni/quick/arm64/calling_convention_arm64.cc", - "linker/arm64/relative_patcher_arm64.cc", "optimizing/code_generator_arm64.cc", "optimizing/code_generator_vector_arm64.cc", "optimizing/scheduler_arm64.cc", @@ -134,7 +130,6 @@ art_cc_defaults { mips: { srcs: [ "jni/quick/mips/calling_convention_mips.cc", - "linker/mips/relative_patcher_mips.cc", "optimizing/code_generator_mips.cc", "optimizing/code_generator_vector_mips.cc", "optimizing/instruction_simplifier_mips.cc", @@ -147,7 +142,6 @@ art_cc_defaults { mips64: { srcs: [ "jni/quick/mips64/calling_convention_mips64.cc", - "linker/mips64/relative_patcher_mips64.cc", "optimizing/code_generator_mips64.cc", "optimizing/code_generator_vector_mips64.cc", "optimizing/intrinsics_mips64.cc", @@ -158,8 +152,6 @@ art_cc_defaults { x86: { srcs: [ "jni/quick/x86/calling_convention_x86.cc", - "linker/x86/relative_patcher_x86.cc", - "linker/x86/relative_patcher_x86_base.cc", "optimizing/code_generator_x86.cc", "optimizing/code_generator_vector_x86.cc", "optimizing/intrinsics_x86.cc", @@ -173,7 +165,6 @@ art_cc_defaults { x86_64: { srcs: [ "jni/quick/x86_64/calling_convention_x86_64.cc", - "linker/x86_64/relative_patcher_x86_64.cc", "optimizing/intrinsics_x86_64.cc", "optimizing/code_generator_x86_64.cc", "optimizing/code_generator_vector_x86_64.cc", @@ -373,31 +364,25 @@ art_cc_test { codegen: { arm: { srcs: [ - "linker/arm/relative_patcher_thumb2_test.cc", "utils/arm/managed_register_arm_test.cc", ], }, arm64: { srcs: [ - "linker/arm64/relative_patcher_arm64_test.cc", "utils/arm64/managed_register_arm64_test.cc", ], }, mips: { srcs: [ - "linker/mips/relative_patcher_mips_test.cc", - "linker/mips/relative_patcher_mips32r6_test.cc", ], }, mips64: { srcs: [ - "linker/mips64/relative_patcher_mips64_test.cc", "utils/mips64/managed_register_mips64_test.cc", ], }, x86: { srcs: [ - "linker/x86/relative_patcher_x86_test.cc", "utils/x86/managed_register_x86_test.cc", // These tests are testing architecture-independent @@ -413,7 +398,8 @@ art_cc_test { }, x86_64: { srcs: [ - "linker/x86_64/relative_patcher_x86_64_test.cc", + // Is this test a bit-rotten copy of the x86 test? b/77951326 + // "utils/x86_64/managed_register_x86_64_test.cc", ], }, }, diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index f3705866c2..7350b146f9 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -29,7 +29,6 @@ #include "gc/accounting/card_table.h" #include "heap_poisoning.h" #include "intrinsics_arm_vixl.h" -#include "linker/arm/relative_patcher_thumb2.h" #include "linker/linker_patch.h" #include "mirror/array-inl.h" #include "mirror/class-inl.h" diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index b158231bc3..49b65fd35e 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -24,7 +24,44 @@ art_cc_defaults { "linker/image_writer.cc", "linker/multi_oat_relative_patcher.cc", "linker/oat_writer.cc", + "linker/relative_patcher.cc", ], + + codegen: { + arm: { + srcs: [ + "linker/arm/relative_patcher_arm_base.cc", + "linker/arm/relative_patcher_thumb2.cc", + ], + }, + arm64: { + srcs: [ + "linker/arm64/relative_patcher_arm64.cc", + ], + }, + mips: { + srcs: [ + "linker/mips/relative_patcher_mips.cc", + ], + }, + mips64: { + srcs: [ + "linker/mips64/relative_patcher_mips64.cc", + ], + }, + x86: { + srcs: [ + "linker/x86/relative_patcher_x86.cc", + "linker/x86/relative_patcher_x86_base.cc", + ], + }, + x86_64: { + srcs: [ + "linker/x86_64/relative_patcher_x86_64.cc", + ], + }, + }, + target: { android: { // For atrace. @@ -245,6 +282,41 @@ art_cc_test { "linker/multi_oat_relative_patcher_test.cc", "linker/oat_writer_test.cc", ], + + codegen: { + arm: { + srcs: [ + "linker/arm/relative_patcher_thumb2_test.cc", + ], + }, + arm64: { + srcs: [ + "linker/arm64/relative_patcher_arm64_test.cc", + ], + }, + mips: { + srcs: [ + "linker/mips/relative_patcher_mips_test.cc", + "linker/mips/relative_patcher_mips32r6_test.cc", + ], + }, + mips64: { + srcs: [ + "linker/mips64/relative_patcher_mips64_test.cc", + ], + }, + x86: { + srcs: [ + "linker/x86/relative_patcher_x86_test.cc", + ], + }, + x86_64: { + srcs: [ + "linker/x86_64/relative_patcher_x86_64_test.cc", + ], + }, + }, + header_libs: ["dex2oat_headers"], include_dirs: [ "external/zlib", diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/dex2oat/linker/arm/relative_patcher_arm_base.cc similarity index 100% rename from compiler/linker/arm/relative_patcher_arm_base.cc rename to dex2oat/linker/arm/relative_patcher_arm_base.cc diff --git a/compiler/linker/arm/relative_patcher_arm_base.h b/dex2oat/linker/arm/relative_patcher_arm_base.h similarity index 96% rename from compiler/linker/arm/relative_patcher_arm_base.h rename to dex2oat/linker/arm/relative_patcher_arm_base.h index 963d6690b0..f5a1395bdd 100644 --- a/compiler/linker/arm/relative_patcher_arm_base.h +++ b/dex2oat/linker/arm/relative_patcher_arm_base.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_ -#define ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_ +#ifndef ART_DEX2OAT_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_ +#define ART_DEX2OAT_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_ #include #include @@ -155,4 +155,4 @@ class ArmBaseRelativePatcher : public RelativePatcher { } // namespace linker } // namespace art -#endif // ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_ +#endif // ART_DEX2OAT_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_ diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/dex2oat/linker/arm/relative_patcher_thumb2.cc similarity index 100% rename from compiler/linker/arm/relative_patcher_thumb2.cc rename to dex2oat/linker/arm/relative_patcher_thumb2.cc diff --git a/compiler/linker/arm/relative_patcher_thumb2.h b/dex2oat/linker/arm/relative_patcher_thumb2.h similarity index 93% rename from compiler/linker/arm/relative_patcher_thumb2.h rename to dex2oat/linker/arm/relative_patcher_thumb2.h index 68610d69e1..3a42928466 100644 --- a/compiler/linker/arm/relative_patcher_thumb2.h +++ b/dex2oat/linker/arm/relative_patcher_thumb2.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_THUMB2_H_ -#define ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_THUMB2_H_ +#ifndef ART_DEX2OAT_LINKER_ARM_RELATIVE_PATCHER_THUMB2_H_ +#define ART_DEX2OAT_LINKER_ARM_RELATIVE_PATCHER_THUMB2_H_ #include "arch/arm/registers_arm.h" #include "base/array_ref.h" @@ -70,4 +70,4 @@ class Thumb2RelativePatcher FINAL : public ArmBaseRelativePatcher { } // namespace linker } // namespace art -#endif // ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_THUMB2_H_ +#endif // ART_DEX2OAT_LINKER_ARM_RELATIVE_PATCHER_THUMB2_H_ diff --git a/compiler/linker/arm/relative_patcher_thumb2_test.cc b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc similarity index 100% rename from compiler/linker/arm/relative_patcher_thumb2_test.cc rename to dex2oat/linker/arm/relative_patcher_thumb2_test.cc diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/dex2oat/linker/arm64/relative_patcher_arm64.cc similarity index 100% rename from compiler/linker/arm64/relative_patcher_arm64.cc rename to dex2oat/linker/arm64/relative_patcher_arm64.cc diff --git a/compiler/linker/arm64/relative_patcher_arm64.h b/dex2oat/linker/arm64/relative_patcher_arm64.h similarity index 94% rename from compiler/linker/arm64/relative_patcher_arm64.h rename to dex2oat/linker/arm64/relative_patcher_arm64.h index 9dc289da44..f7f673c1ba 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.h +++ b/dex2oat/linker/arm64/relative_patcher_arm64.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_LINKER_ARM64_RELATIVE_PATCHER_ARM64_H_ -#define ART_COMPILER_LINKER_ARM64_RELATIVE_PATCHER_ARM64_H_ +#ifndef ART_DEX2OAT_LINKER_ARM64_RELATIVE_PATCHER_ARM64_H_ +#define ART_DEX2OAT_LINKER_ARM64_RELATIVE_PATCHER_ARM64_H_ #include "base/array_ref.h" #include "linker/arm/relative_patcher_arm_base.h" @@ -81,4 +81,4 @@ class Arm64RelativePatcher FINAL : public ArmBaseRelativePatcher { } // namespace linker } // namespace art -#endif // ART_COMPILER_LINKER_ARM64_RELATIVE_PATCHER_ARM64_H_ +#endif // ART_DEX2OAT_LINKER_ARM64_RELATIVE_PATCHER_ARM64_H_ diff --git a/compiler/linker/arm64/relative_patcher_arm64_test.cc b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc similarity index 100% rename from compiler/linker/arm64/relative_patcher_arm64_test.cc rename to dex2oat/linker/arm64/relative_patcher_arm64_test.cc diff --git a/compiler/linker/mips/relative_patcher_mips.cc b/dex2oat/linker/mips/relative_patcher_mips.cc similarity index 100% rename from compiler/linker/mips/relative_patcher_mips.cc rename to dex2oat/linker/mips/relative_patcher_mips.cc diff --git a/compiler/linker/mips/relative_patcher_mips.h b/dex2oat/linker/mips/relative_patcher_mips.h similarity index 92% rename from compiler/linker/mips/relative_patcher_mips.h rename to dex2oat/linker/mips/relative_patcher_mips.h index 5714a7d1b0..d3a4c5a14f 100644 --- a/compiler/linker/mips/relative_patcher_mips.h +++ b/dex2oat/linker/mips/relative_patcher_mips.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_ -#define ART_COMPILER_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_ +#ifndef ART_DEX2OAT_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_ +#define ART_DEX2OAT_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_ #include "arch/mips/instruction_set_features_mips.h" #include "linker/relative_patcher.h" @@ -55,4 +55,4 @@ class MipsRelativePatcher FINAL : public RelativePatcher { } // namespace linker } // namespace art -#endif // ART_COMPILER_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_ +#endif // ART_DEX2OAT_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_ diff --git a/compiler/linker/mips/relative_patcher_mips32r6_test.cc b/dex2oat/linker/mips/relative_patcher_mips32r6_test.cc similarity index 100% rename from compiler/linker/mips/relative_patcher_mips32r6_test.cc rename to dex2oat/linker/mips/relative_patcher_mips32r6_test.cc diff --git a/compiler/linker/mips/relative_patcher_mips_test.cc b/dex2oat/linker/mips/relative_patcher_mips_test.cc similarity index 100% rename from compiler/linker/mips/relative_patcher_mips_test.cc rename to dex2oat/linker/mips/relative_patcher_mips_test.cc diff --git a/compiler/linker/mips64/relative_patcher_mips64.cc b/dex2oat/linker/mips64/relative_patcher_mips64.cc similarity index 100% rename from compiler/linker/mips64/relative_patcher_mips64.cc rename to dex2oat/linker/mips64/relative_patcher_mips64.cc diff --git a/compiler/linker/mips64/relative_patcher_mips64.h b/dex2oat/linker/mips64/relative_patcher_mips64.h similarity index 90% rename from compiler/linker/mips64/relative_patcher_mips64.h rename to dex2oat/linker/mips64/relative_patcher_mips64.h index 183bbedb39..9f5a125408 100644 --- a/compiler/linker/mips64/relative_patcher_mips64.h +++ b/dex2oat/linker/mips64/relative_patcher_mips64.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_ -#define ART_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_ +#ifndef ART_DEX2OAT_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_ +#define ART_DEX2OAT_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_ #include "linker/relative_patcher.h" @@ -51,4 +51,4 @@ class Mips64RelativePatcher FINAL : public RelativePatcher { } // namespace linker } // namespace art -#endif // ART_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_ +#endif // ART_DEX2OAT_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_ diff --git a/compiler/linker/mips64/relative_patcher_mips64_test.cc b/dex2oat/linker/mips64/relative_patcher_mips64_test.cc similarity index 100% rename from compiler/linker/mips64/relative_patcher_mips64_test.cc rename to dex2oat/linker/mips64/relative_patcher_mips64_test.cc diff --git a/compiler/linker/relative_patcher.cc b/dex2oat/linker/relative_patcher.cc similarity index 99% rename from compiler/linker/relative_patcher.cc rename to dex2oat/linker/relative_patcher.cc index b82d15230d..b6135c9b5f 100644 --- a/compiler/linker/relative_patcher.cc +++ b/dex2oat/linker/relative_patcher.cc @@ -35,7 +35,7 @@ #ifdef ART_ENABLE_CODEGEN_x86_64 #include "linker/x86_64/relative_patcher_x86_64.h" #endif -#include "output_stream.h" +#include "linker/output_stream.h" namespace art { namespace linker { diff --git a/compiler/linker/relative_patcher.h b/dex2oat/linker/relative_patcher.h similarity index 97% rename from compiler/linker/relative_patcher.h rename to dex2oat/linker/relative_patcher.h index 06c7e70d23..d80eaf94f7 100644 --- a/compiler/linker/relative_patcher.h +++ b/dex2oat/linker/relative_patcher.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_LINKER_RELATIVE_PATCHER_H_ -#define ART_COMPILER_LINKER_RELATIVE_PATCHER_H_ +#ifndef ART_DEX2OAT_LINKER_RELATIVE_PATCHER_H_ +#define ART_DEX2OAT_LINKER_RELATIVE_PATCHER_H_ #include @@ -167,4 +167,4 @@ class RelativePatcher { } // namespace linker } // namespace art -#endif // ART_COMPILER_LINKER_RELATIVE_PATCHER_H_ +#endif // ART_DEX2OAT_LINKER_RELATIVE_PATCHER_H_ diff --git a/compiler/linker/relative_patcher_test.h b/dex2oat/linker/relative_patcher_test.h similarity index 98% rename from compiler/linker/relative_patcher_test.h rename to dex2oat/linker/relative_patcher_test.h index af8dc4dbc9..3f7c8556c2 100644 --- a/compiler/linker/relative_patcher_test.h +++ b/dex2oat/linker/relative_patcher_test.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_LINKER_RELATIVE_PATCHER_TEST_H_ -#define ART_COMPILER_LINKER_RELATIVE_PATCHER_TEST_H_ +#ifndef ART_DEX2OAT_LINKER_RELATIVE_PATCHER_TEST_H_ +#define ART_DEX2OAT_LINKER_RELATIVE_PATCHER_TEST_H_ #include "arch/instruction_set.h" #include "arch/instruction_set_features.h" @@ -30,9 +30,9 @@ #include "globals.h" #include "gtest/gtest.h" #include "linker/relative_patcher.h" +#include "linker/vector_output_stream.h" #include "oat.h" #include "oat_quick_method_header.h" -#include "vector_output_stream.h" namespace art { namespace linker { @@ -356,4 +356,4 @@ class RelativePatcherTest : public testing::Test { } // namespace linker } // namespace art -#endif // ART_COMPILER_LINKER_RELATIVE_PATCHER_TEST_H_ +#endif // ART_DEX2OAT_LINKER_RELATIVE_PATCHER_TEST_H_ diff --git a/compiler/linker/x86/relative_patcher_x86.cc b/dex2oat/linker/x86/relative_patcher_x86.cc similarity index 100% rename from compiler/linker/x86/relative_patcher_x86.cc rename to dex2oat/linker/x86/relative_patcher_x86.cc diff --git a/compiler/linker/x86/relative_patcher_x86.h b/dex2oat/linker/x86/relative_patcher_x86.h similarity index 88% rename from compiler/linker/x86/relative_patcher_x86.h rename to dex2oat/linker/x86/relative_patcher_x86.h index 63a8338722..e723580dae 100644 --- a/compiler/linker/x86/relative_patcher_x86.h +++ b/dex2oat/linker/x86/relative_patcher_x86.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_LINKER_X86_RELATIVE_PATCHER_X86_H_ -#define ART_COMPILER_LINKER_X86_RELATIVE_PATCHER_X86_H_ +#ifndef ART_DEX2OAT_LINKER_X86_RELATIVE_PATCHER_X86_H_ +#define ART_DEX2OAT_LINKER_X86_RELATIVE_PATCHER_X86_H_ #include "linker/x86/relative_patcher_x86_base.h" @@ -38,4 +38,4 @@ class X86RelativePatcher FINAL : public X86BaseRelativePatcher { } // namespace linker } // namespace art -#endif // ART_COMPILER_LINKER_X86_RELATIVE_PATCHER_X86_H_ +#endif // ART_DEX2OAT_LINKER_X86_RELATIVE_PATCHER_X86_H_ diff --git a/compiler/linker/x86/relative_patcher_x86_base.cc b/dex2oat/linker/x86/relative_patcher_x86_base.cc similarity index 100% rename from compiler/linker/x86/relative_patcher_x86_base.cc rename to dex2oat/linker/x86/relative_patcher_x86_base.cc diff --git a/compiler/linker/x86/relative_patcher_x86_base.h b/dex2oat/linker/x86/relative_patcher_x86_base.h similarity index 90% rename from compiler/linker/x86/relative_patcher_x86_base.h rename to dex2oat/linker/x86/relative_patcher_x86_base.h index 6097345657..4cc7b07d2d 100644 --- a/compiler/linker/x86/relative_patcher_x86_base.h +++ b/dex2oat/linker/x86/relative_patcher_x86_base.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_LINKER_X86_RELATIVE_PATCHER_X86_BASE_H_ -#define ART_COMPILER_LINKER_X86_RELATIVE_PATCHER_X86_BASE_H_ +#ifndef ART_DEX2OAT_LINKER_X86_RELATIVE_PATCHER_X86_BASE_H_ +#define ART_DEX2OAT_LINKER_X86_RELATIVE_PATCHER_X86_BASE_H_ #include "linker/relative_patcher.h" @@ -50,4 +50,4 @@ class X86BaseRelativePatcher : public RelativePatcher { } // namespace linker } // namespace art -#endif // ART_COMPILER_LINKER_X86_RELATIVE_PATCHER_X86_BASE_H_ +#endif // ART_DEX2OAT_LINKER_X86_RELATIVE_PATCHER_X86_BASE_H_ diff --git a/compiler/linker/x86/relative_patcher_x86_test.cc b/dex2oat/linker/x86/relative_patcher_x86_test.cc similarity index 100% rename from compiler/linker/x86/relative_patcher_x86_test.cc rename to dex2oat/linker/x86/relative_patcher_x86_test.cc diff --git a/compiler/linker/x86_64/relative_patcher_x86_64.cc b/dex2oat/linker/x86_64/relative_patcher_x86_64.cc similarity index 100% rename from compiler/linker/x86_64/relative_patcher_x86_64.cc rename to dex2oat/linker/x86_64/relative_patcher_x86_64.cc diff --git a/compiler/linker/x86_64/relative_patcher_x86_64.h b/dex2oat/linker/x86_64/relative_patcher_x86_64.h similarity index 87% rename from compiler/linker/x86_64/relative_patcher_x86_64.h rename to dex2oat/linker/x86_64/relative_patcher_x86_64.h index 4f3ec498cb..a31e1ebfbb 100644 --- a/compiler/linker/x86_64/relative_patcher_x86_64.h +++ b/dex2oat/linker/x86_64/relative_patcher_x86_64.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_LINKER_X86_64_RELATIVE_PATCHER_X86_64_H_ -#define ART_COMPILER_LINKER_X86_64_RELATIVE_PATCHER_X86_64_H_ +#ifndef ART_DEX2OAT_LINKER_X86_64_RELATIVE_PATCHER_X86_64_H_ +#define ART_DEX2OAT_LINKER_X86_64_RELATIVE_PATCHER_X86_64_H_ #include "linker/x86/relative_patcher_x86_base.h" @@ -38,4 +38,4 @@ class X86_64RelativePatcher FINAL : public X86BaseRelativePatcher { } // namespace linker } // namespace art -#endif // ART_COMPILER_LINKER_X86_64_RELATIVE_PATCHER_X86_64_H_ +#endif // ART_DEX2OAT_LINKER_X86_64_RELATIVE_PATCHER_X86_64_H_ diff --git a/compiler/linker/x86_64/relative_patcher_x86_64_test.cc b/dex2oat/linker/x86_64/relative_patcher_x86_64_test.cc similarity index 100% rename from compiler/linker/x86_64/relative_patcher_x86_64_test.cc rename to dex2oat/linker/x86_64/relative_patcher_x86_64_test.cc -- GitLab From daf374cc169ebc9beca38f1c6edf1883f061ae74 Mon Sep 17 00:00:00 2001 From: Jeff Vander Stoep Date: Thu, 12 Apr 2018 06:51:01 -0700 Subject: [PATCH 247/749] profman: removed unused header While root-causing a spurious getattr selinux denial, I noticed sys/stat.h is no longer used. Remove. Test: mmm art/profman Change-Id: I3e35946b80fe91cead268f2b89210ebd85e89892 --- profman/profman.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/profman/profman.cc b/profman/profman.cc index 90e342d16a..59fedd8aac 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -19,7 +19,6 @@ #include #include #include -#include #include #include -- GitLab From df25b47cd8a423239cdc7095f7170414814fea1f Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Fri, 6 Apr 2018 20:27:49 +0100 Subject: [PATCH 248/749] Implement dead object poisoning in unevacuated regions. When an unevacuated region is cleared from the from-space, replace memory areas used by dead objects with a poison value, so as to catch dangling references to such objects earlier. This poisoning mechanism is only turned on in debug mode. Test: art/test.py Test: Device boot test with libartd Bug: 74064045 Change-Id: I2f89a31648d292baae09859494410f88eca21759 --- runtime/gc/space/region_space.cc | 67 +++++++++++++++++++++++++++++++- runtime/gc/space/region_space.h | 5 +++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc index 6a01c88c5d..74abe1c2ab 100644 --- a/runtime/gc/space/region_space.cc +++ b/runtime/gc/space/region_space.cc @@ -29,10 +29,19 @@ namespace space { // value of the region size, evaculate the region. static constexpr uint kEvacuateLivePercentThreshold = 75U; -// If we protect the cleared regions. +// Whether we protect the cleared regions. // Only protect for target builds to prevent flaky test failures (b/63131961). static constexpr bool kProtectClearedRegions = kIsTargetBuild; +// Wether we poison memory areas occupied by dead objects in unevacuated regions. +static constexpr bool kPoisonDeadObjectsInUnevacuatedRegions = kIsDebugBuild; + +// Special 32-bit value used to poison memory areas occupied by dead +// objects in unevacuated regions. Dereferencing this value is expected +// to trigger a memory protection fault, as it is unlikely that it +// points to a valid, non-protected memory area. +static constexpr uint32_t kPoisonDeadObject = 0xBADDB01D; // "BADDROID" + MemMap* RegionSpace::CreateMemMap(const std::string& name, size_t capacity, uint8_t* requested_begin) { CHECK_ALIGNED(capacity, kRegionSize); @@ -370,6 +379,13 @@ void RegionSpace::ClearFromSpace(/* out */ uint64_t* cleared_bytes, // as they are unevac regions that are live. // Subtract one for the for-loop. i += regions_to_clear_bitmap - 1; + } else { + // Only some allocated bytes are live in this unevac region. + // This should only happen for an allocated non-large region. + DCHECK(r->IsAllocated()) << r->State(); + if (kPoisonDeadObjectsInUnevacuatedRegions) { + PoisonDeadObjectsInUnevacuatedRegion(r); + } } } // Note r != last_checked_region if r->IsInUnevacFromSpace() was true above. @@ -388,6 +404,55 @@ void RegionSpace::ClearFromSpace(/* out */ uint64_t* cleared_bytes, num_evac_regions_ = 0; } +// Poison the memory area in range [`begin`, `end`) with value `kPoisonDeadObject`. +static void PoisonUnevacuatedRange(uint8_t* begin, uint8_t* end) { + static constexpr size_t kPoisonDeadObjectSize = sizeof(kPoisonDeadObject); + static_assert(IsPowerOfTwo(kPoisonDeadObjectSize) && + IsPowerOfTwo(RegionSpace::kAlignment) && + (kPoisonDeadObjectSize < RegionSpace::kAlignment), + "RegionSpace::kAlignment should be a multiple of kPoisonDeadObjectSize" + " and both should be powers of 2"); + DCHECK_ALIGNED(begin, kPoisonDeadObjectSize); + DCHECK_ALIGNED(end, kPoisonDeadObjectSize); + uint32_t* begin_addr = reinterpret_cast(begin); + uint32_t* end_addr = reinterpret_cast(end); + std::fill(begin_addr, end_addr, kPoisonDeadObject); +} + +void RegionSpace::PoisonDeadObjectsInUnevacuatedRegion(Region* r) { + // The live byte count of `r` should be different from -1, as this + // region should neither be a newly allocated region nor an + // evacuated region. + DCHECK_NE(r->LiveBytes(), static_cast(-1)); + + // Past-the-end address of the previously visited (live) object (or + // the beginning of the region, if `maybe_poison` has not run yet). + uint8_t* prev_obj_end = reinterpret_cast(r->Begin()); + + // Functor poisoning the space between `obj` and the previously + // visited (live) object (or the beginng of the region), if any. + auto maybe_poison = [this, &prev_obj_end](mirror::Object* obj) REQUIRES(Locks::mutator_lock_) { + DCHECK_ALIGNED(obj, kAlignment); + uint8_t* cur_obj_begin = reinterpret_cast(obj); + if (cur_obj_begin != prev_obj_end) { + // There is a gap (dead object(s)) between the previously + // visited (live) object (or the beginning of the region) and + // `obj`; poison that space. + PoisonUnevacuatedRange(prev_obj_end, cur_obj_begin); + } + prev_obj_end = reinterpret_cast(GetNextObject(obj)); + }; + + // Visit live objects in `r` and poison gaps (dead objects) between them. + GetLiveBitmap()->VisitMarkedRange(reinterpret_cast(r->Begin()), + reinterpret_cast(r->Top()), + maybe_poison); + // Poison memory between the last live object and the end of the region, if any. + if (prev_obj_end < r->Top()) { + PoisonUnevacuatedRange(prev_obj_end, r->Top()); + } +} + void RegionSpace::LogFragmentationAllocFailure(std::ostream& os, size_t /* failed_alloc_bytes */) { size_t max_contiguous_allocation = 0; diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h index c7e18885db..ab18b1bcb9 100644 --- a/runtime/gc/space/region_space.h +++ b/runtime/gc/space/region_space.h @@ -595,6 +595,11 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { /* out */ size_t* bytes_tl_bulk_allocated, /* out */ size_t* next_region = nullptr) REQUIRES(region_lock_); + // Poison memory areas used by dead objects within unevacuated + // region `r`. This is meant to detect dangling references to dead + // objects earlier in debug mode. + void PoisonDeadObjectsInUnevacuatedRegion(Region* r); + Mutex region_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; uint32_t time_; // The time as the number of collections since the startup. -- GitLab From 0be7fa708455e2ffbf3779166269acee7a4a6791 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 12 Apr 2018 10:13:29 -0700 Subject: [PATCH 249/749] Fix typo in clean vars for dexanalyze_test Bug: 77721545 Test: make Change-Id: If408ffc115ae998000ab00305706d06536be75a5 --- build/Android.gtest.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 4fedfb9b7d..3b2a5bef26 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -806,7 +806,7 @@ ART_GTEST_jni_internal_test_DEX_DEPS := ART_GTEST_oat_file_assistant_test_DEX_DEPS := ART_GTEST_oat_file_assistant_test_HOST_DEPS := ART_GTEST_oat_file_assistant_test_TARGET_DEPS := -ART_GTEST_dexanalyzer_test_DEX_DEPS := +ART_GTEST_dexanalyze_test_DEX_DEPS := ART_GTEST_dexoptanalyzer_test_DEX_DEPS := ART_GTEST_dexoptanalyzer_test_HOST_DEPS := ART_GTEST_dexoptanalyzer_test_TARGET_DEPS := -- GitLab From 03da784d893a784572cc8b18c503e85fb387a3b9 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 12 Apr 2018 11:12:52 -0700 Subject: [PATCH 250/749] ART: Fix oatdump check Resolution may not be possible, e.g., for missing superclasses. Weaken a CHECK. Test: mmma art Change-Id: I3992c4e10c03a11918a355c36c6b954845c420b0 --- oatdump/oatdump.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 7530a9c5a8..6a68b55fad 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1714,7 +1714,10 @@ class OatDumper { CHECK(dex_cache != nullptr); ArtMethod* method = runtime->GetClassLinker()->ResolveMethodWithoutInvokeType( dex_method_idx, dex_cache, *options_.class_loader_); - CHECK(method != nullptr); + if (method == nullptr) { + soa.Self()->ClearException(); + return nullptr; + } return verifier::MethodVerifier::VerifyMethodAndDump( soa.Self(), vios, dex_method_idx, dex_file, dex_cache, *options_.class_loader_, class_def, code_item, method, method_access_flags); -- GitLab From d5d597f672ea36b8ea6a806e783abd87d9382b49 Mon Sep 17 00:00:00 2001 From: Chih-Hung Hsieh Date: Fri, 9 Mar 2018 14:52:40 -0800 Subject: [PATCH 251/749] Disable lld for unrecognized --keep-unique flag. Bug: 77976998 Test: make checkbuild and boot Change-Id: I4db5ad37e9c126f8b6b0d163a35b772155b19b9b --- runtime/Android.bp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/runtime/Android.bp b/runtime/Android.bp index 8329b62618..b82edd6f7f 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -243,6 +243,9 @@ cc_defaults { "entrypoints/quick/quick_trampoline_entrypoints.cc", ], + // b/77976998, clang lld does not recognize the --keep-unique flag. + use_clang_lld: false, + arch: { arm: { clang_asflags: ["-no-integrated-as"], -- GitLab From ef2fa26b0c4549ccee4b6c2a2b2d2caae6b85edc Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 12 Apr 2018 15:14:07 -0700 Subject: [PATCH 252/749] Move dexanalyze experiments to their own file Reduce how much code is in the main file. Bug: 77721545 Test: test-art-host-gtest-dexanalyze_test Change-Id: I03e00b954170ff2e602edaa2e019f96e54016ecf --- tools/dexanalyze/Android.bp | 5 +- tools/dexanalyze/dexanalyze.cc | 142 +-------------------- tools/dexanalyze/dexanalyze_experiments.cc | 133 +++++++++++++++++++ tools/dexanalyze/dexanalyze_experiments.h | 68 ++++++++++ 4 files changed, 206 insertions(+), 142 deletions(-) create mode 100644 tools/dexanalyze/dexanalyze_experiments.cc create mode 100644 tools/dexanalyze/dexanalyze_experiments.h diff --git a/tools/dexanalyze/Android.bp b/tools/dexanalyze/Android.bp index 34aaaac451..2754e6445e 100644 --- a/tools/dexanalyze/Android.bp +++ b/tools/dexanalyze/Android.bp @@ -18,7 +18,10 @@ cc_defaults { name: "dexanalyze-defaults", defaults: ["art_defaults"], host_supported: true, - srcs: ["dexanalyze.cc"], + srcs: [ + "dexanalyze.cc", + "dexanalyze_experiments.cc", + ], target: { android: { shared_libs: ["libcutils"], diff --git a/tools/dexanalyze/dexanalyze.cc b/tools/dexanalyze/dexanalyze.cc index 75fa13cc03..a5f647cc56 100644 --- a/tools/dexanalyze/dexanalyze.cc +++ b/tools/dexanalyze/dexanalyze.cc @@ -20,6 +20,7 @@ #include +#include "dexanalyze_experiments.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" @@ -27,147 +28,6 @@ namespace art { -// An experiment a stateful visitor that runs on dex files. Results are cumulative. -class Experiment { - public: - virtual ~Experiment() {} - virtual void ProcessDexFile(const DexFile& dex_file) = 0; - virtual void Dump(std::ostream& os) const = 0; -}; - -class CountDexIndices : public Experiment { - public: - void ProcessDexFile(const DexFile& dex_file) { - num_string_ids_ += dex_file.NumStringIds(); - num_method_ids_ += dex_file.NumMethodIds(); - num_field_ids_ += dex_file.NumFieldIds(); - num_type_ids_ += dex_file.NumTypeIds(); - num_class_defs_ += dex_file.NumClassDefs(); - for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); ++class_def_index) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { - continue; - } - ClassDataItemIterator it(dex_file, class_data); - it.SkipAllFields(); - std::set unique_method_ids; - std::set unique_string_ids; - while (it.HasNextMethod()) { - const DexFile::CodeItem* code_item = it.GetMethodCodeItem(); - if (code_item != nullptr) { - CodeItemInstructionAccessor instructions(dex_file, code_item); - const uint16_t* code_ptr = instructions.Insns(); - dex_code_bytes_ += instructions.InsnsSizeInCodeUnits() * sizeof(code_ptr[0]); - for (const DexInstructionPcPair& inst : instructions) { - switch (inst->Opcode()) { - case Instruction::CONST_STRING: { - const dex::StringIndex string_index(inst->VRegB_21c()); - unique_string_ids.insert(string_index.index_); - ++num_string_ids_from_code_; - break; - } - case Instruction::CONST_STRING_JUMBO: { - const dex::StringIndex string_index(inst->VRegB_31c()); - unique_string_ids.insert(string_index.index_); - ++num_string_ids_from_code_; - break; - } - // Invoke cases. - case Instruction::INVOKE_VIRTUAL: - case Instruction::INVOKE_VIRTUAL_RANGE: { - bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE); - uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); - if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) { - ++same_class_virtual_; - } else { - ++other_class_virtual_; - unique_method_ids.insert(method_idx); - } - break; - } - case Instruction::INVOKE_DIRECT: - case Instruction::INVOKE_DIRECT_RANGE: { - bool is_range = (inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE); - uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) { - ++same_class_direct_; - } else { - ++other_class_direct_; - unique_method_ids.insert(method_idx); - } - break; - } - case Instruction::INVOKE_STATIC: - case Instruction::INVOKE_STATIC_RANGE: { - bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE); - uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) { - ++same_class_static_; - } else { - ++other_class_static_; - unique_method_ids.insert(method_idx); - } - break; - } - default: - break; - } - } - } - it.Next(); - } - DCHECK(!it.HasNext()); - total_unique_method_idx_ += unique_method_ids.size(); - total_unique_string_ids_ += unique_string_ids.size(); - } - } - - void Dump(std::ostream& os) const { - os << "Num string ids: " << num_string_ids_ << "\n"; - os << "Num method ids: " << num_method_ids_ << "\n"; - os << "Num field ids: " << num_field_ids_ << "\n"; - os << "Num type ids: " << num_type_ids_ << "\n"; - os << "Num class defs: " << num_class_defs_ << "\n"; - os << "Same class direct: " << same_class_direct_ << "\n"; - os << "Other class direct: " << other_class_direct_ << "\n"; - os << "Same class virtual: " << same_class_virtual_ << "\n"; - os << "Other class virtual: " << other_class_virtual_ << "\n"; - os << "Same class static: " << same_class_static_ << "\n"; - os << "Other class static: " << other_class_static_ << "\n"; - os << "Num strings accessed from code: " << num_string_ids_from_code_ << "\n"; - os << "Unique(per class) method ids accessed from code: " << total_unique_method_idx_ << "\n"; - os << "Unique(per class) string ids accessed from code: " << total_unique_string_ids_ << "\n"; - size_t same_class_total = same_class_direct_ + same_class_virtual_ + same_class_static_; - size_t other_class_total = other_class_direct_ + other_class_virtual_ + other_class_static_; - os << "Same class invoke: " << same_class_total << "\n"; - os << "Other class invoke: " << other_class_total << "\n"; - os << "Invokes from code: " << (same_class_total + other_class_total) << "\n"; - } - - private: - // Total string ids loaded from dex code. - size_t num_string_ids_from_code_ = 0; - size_t total_unique_method_idx_ = 0; - size_t total_unique_string_ids_ = 0; - - // Other dex ids. - size_t dex_code_bytes_ = 0; - size_t num_string_ids_ = 0; - size_t num_method_ids_ = 0; - size_t num_field_ids_ = 0; - size_t num_type_ids_ = 0; - size_t num_class_defs_ = 0; - - // Invokes - size_t same_class_direct_ = 0; - size_t other_class_direct_ = 0; - size_t same_class_virtual_ = 0; - size_t other_class_virtual_ = 0; - size_t same_class_static_ = 0; - size_t other_class_static_ = 0; -}; - class DexAnalyze { static const int kExitCodeUsageError = 1; diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc new file mode 100644 index 0000000000..e1f119df59 --- /dev/null +++ b/tools/dexanalyze/dexanalyze_experiments.cc @@ -0,0 +1,133 @@ +/* + * 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. + */ + +#include "dexanalyze_experiments.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_instruction-inl.h" +#include "dex/standard_dex_file.h" + +namespace art { + +void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { + num_string_ids_ += dex_file.NumStringIds(); + num_method_ids_ += dex_file.NumMethodIds(); + num_field_ids_ += dex_file.NumFieldIds(); + num_type_ids_ += dex_file.NumTypeIds(); + num_class_defs_ += dex_file.NumClassDefs(); + for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); ++class_def_index) { + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); + const uint8_t* class_data = dex_file.GetClassData(class_def); + if (class_data == nullptr) { + continue; + } + ClassDataItemIterator it(dex_file, class_data); + it.SkipAllFields(); + std::set unique_method_ids; + std::set unique_string_ids; + while (it.HasNextMethod()) { + const DexFile::CodeItem* code_item = it.GetMethodCodeItem(); + if (code_item != nullptr) { + CodeItemInstructionAccessor instructions(dex_file, code_item); + const uint16_t* code_ptr = instructions.Insns(); + dex_code_bytes_ += instructions.InsnsSizeInCodeUnits() * sizeof(code_ptr[0]); + for (const DexInstructionPcPair& inst : instructions) { + switch (inst->Opcode()) { + case Instruction::CONST_STRING: { + const dex::StringIndex string_index(inst->VRegB_21c()); + unique_string_ids.insert(string_index.index_); + ++num_string_ids_from_code_; + break; + } + case Instruction::CONST_STRING_JUMBO: { + const dex::StringIndex string_index(inst->VRegB_31c()); + unique_string_ids.insert(string_index.index_); + ++num_string_ids_from_code_; + break; + } + // Invoke cases. + case Instruction::INVOKE_VIRTUAL: + case Instruction::INVOKE_VIRTUAL_RANGE: { + bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE); + uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); + if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) { + ++same_class_virtual_; + } else { + ++other_class_virtual_; + unique_method_ids.insert(method_idx); + } + break; + } + case Instruction::INVOKE_DIRECT: + case Instruction::INVOKE_DIRECT_RANGE: { + bool is_range = (inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE); + uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) { + ++same_class_direct_; + } else { + ++other_class_direct_; + unique_method_ids.insert(method_idx); + } + break; + } + case Instruction::INVOKE_STATIC: + case Instruction::INVOKE_STATIC_RANGE: { + bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE); + uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) { + ++same_class_static_; + } else { + ++other_class_static_; + unique_method_ids.insert(method_idx); + } + break; + } + default: + break; + } + } + } + it.Next(); + } + DCHECK(!it.HasNext()); + total_unique_method_idx_ += unique_method_ids.size(); + total_unique_string_ids_ += unique_string_ids.size(); + } +} + +void CountDexIndices::Dump(std::ostream& os) const { + os << "Num string ids: " << num_string_ids_ << "\n"; + os << "Num method ids: " << num_method_ids_ << "\n"; + os << "Num field ids: " << num_field_ids_ << "\n"; + os << "Num type ids: " << num_type_ids_ << "\n"; + os << "Num class defs: " << num_class_defs_ << "\n"; + os << "Same class direct: " << same_class_direct_ << "\n"; + os << "Other class direct: " << other_class_direct_ << "\n"; + os << "Same class virtual: " << same_class_virtual_ << "\n"; + os << "Other class virtual: " << other_class_virtual_ << "\n"; + os << "Same class static: " << same_class_static_ << "\n"; + os << "Other class static: " << other_class_static_ << "\n"; + os << "Num strings accessed from code: " << num_string_ids_from_code_ << "\n"; + os << "Unique(per class) method ids accessed from code: " << total_unique_method_idx_ << "\n"; + os << "Unique(per class) string ids accessed from code: " << total_unique_string_ids_ << "\n"; + size_t same_class_total = same_class_direct_ + same_class_virtual_ + same_class_static_; + size_t other_class_total = other_class_direct_ + other_class_virtual_ + other_class_static_; + os << "Same class invoke: " << same_class_total << "\n"; + os << "Other class invoke: " << other_class_total << "\n"; + os << "Invokes from code: " << (same_class_total + other_class_total) << "\n"; +} + +} // namespace art + diff --git a/tools/dexanalyze/dexanalyze_experiments.h b/tools/dexanalyze/dexanalyze_experiments.h new file mode 100644 index 0000000000..5d0f51b821 --- /dev/null +++ b/tools/dexanalyze/dexanalyze_experiments.h @@ -0,0 +1,68 @@ +/* + * 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. + */ + +#ifndef ART_TOOLS_DEXANALYZE_DEXANALYZE_EXPERIMENTS_H_ +#define ART_TOOLS_DEXANALYZE_DEXANALYZE_EXPERIMENTS_H_ + +#include +#include + +namespace art { + +class DexFile; + +// An experiment a stateful visitor that runs on dex files. Results are cumulative. +class Experiment { + public: + virtual ~Experiment() {} + virtual void ProcessDexFile(const DexFile& dex_file) = 0; + virtual void Dump(std::ostream& os) const = 0; +}; + +// Count numbers of dex indices. +class CountDexIndices : public Experiment { + public: + void ProcessDexFile(const DexFile& dex_file); + + void Dump(std::ostream& os) const; + + private: + // Total string ids loaded from dex code. + size_t num_string_ids_from_code_ = 0; + size_t total_unique_method_idx_ = 0; + size_t total_unique_string_ids_ = 0; + + // Other dex ids. + size_t dex_code_bytes_ = 0; + size_t num_string_ids_ = 0; + size_t num_method_ids_ = 0; + size_t num_field_ids_ = 0; + size_t num_type_ids_ = 0; + size_t num_class_defs_ = 0; + + // Invokes + size_t same_class_direct_ = 0; + size_t other_class_direct_ = 0; + size_t same_class_virtual_ = 0; + size_t other_class_virtual_ = 0; + size_t same_class_static_ = 0; + size_t other_class_static_ = 0; +}; + +} // namespace art + +#endif // ART_TOOLS_DEXANALYZE_DEXANALYZE_EXPERIMENTS_H_ + -- GitLab From 79e2607ab50163bfdc283f4a49decec26a216df5 Mon Sep 17 00:00:00 2001 From: David Sehr Date: Fri, 6 Apr 2018 17:58:50 -0700 Subject: [PATCH 253/749] Move profile dependent modules to libartbase Move mem_map and zip_archive to libartbase. This should be the last two remaining modules that profile_compilation_info is dependent upon. Bug: 22322814 Test: make -j 50 checkbuild make and boot a device Change-Id: I136ee23e426aa8ec7441e3d3f1978f1bebf4b562 --- compiler/jni/jni_compiler_test.cc | 2 +- dex2oat/dex2oat.cc | 2 +- dex2oat/linker/image_writer.h | 2 +- dex2oat/linker/oat_writer.cc | 4 +- dex2oat/linker/oat_writer.h | 2 +- dexlayout/dexlayout.cc | 2 +- dexlayout/dexlayout_main.cc | 2 +- libartbase/Android.bp | 38 ++++++++++++- libartbase/base/logging.cc | 55 +++++++++++++++++++ libartbase/base/logging.h | 3 + {runtime => libartbase/base}/mem_map.cc | 4 +- {runtime => libartbase/base}/mem_map.h | 7 ++- {runtime => libartbase/base}/mem_map_test.cc | 0 {runtime => libartbase/base}/zip_archive.cc | 7 +-- {runtime => libartbase/base}/zip_archive.h | 9 ++- .../base}/zip_archive_test.cc | 2 +- openjdkjvmti/ti_class_definition.h | 2 +- openjdkjvmti/ti_class_loader.h | 2 +- openjdkjvmti/ti_redefine.h | 2 +- openjdkjvmti/transform.cc | 2 +- profman/profman.cc | 2 +- runtime/Android.bp | 4 -- runtime/base/file_utils.cc | 53 ------------------ runtime/base/file_utils.h | 1 - runtime/base/mem_map_arena_pool.cc | 2 +- runtime/common_runtime_test.cc | 2 +- runtime/dex/art_dex_file_loader.cc | 4 +- runtime/dex/art_dex_file_loader_test.cc | 2 +- runtime/dexopt_test.cc | 2 +- runtime/elf_file_impl.h | 2 +- runtime/gc/accounting/atomic_stack.h | 2 +- runtime/gc/accounting/bitmap.cc | 2 +- runtime/gc/accounting/card_table-inl.h | 2 +- runtime/gc/accounting/card_table.cc | 2 +- runtime/gc/accounting/read_barrier_table.h | 2 +- runtime/gc/accounting/space_bitmap.cc | 2 +- runtime/gc/allocator/rosalloc.cc | 2 +- runtime/gc/space/space.h | 2 +- runtime/gc/space/zygote_space.h | 2 +- runtime/interpreter/unstarted_runtime.cc | 2 +- runtime/jit/jit_code_cache.cc | 2 +- runtime/jit/profile_compilation_info.cc | 2 +- runtime/jit/profile_compilation_info.h | 2 +- runtime/native/dalvik_system_DexFile.cc | 2 +- runtime/native/java_lang_VMClassLoader.cc | 2 +- runtime/oat_file.cc | 2 +- runtime/runtime_callbacks_test.cc | 2 +- runtime/thread_pool.h | 2 +- runtime/vdex_file.h | 2 +- test/305-other-fault-handler/fault_handler.cc | 2 +- tools/hiddenapi/hiddenapi.cc | 2 +- tools/hiddenapi/hiddenapi_test.cc | 2 +- 52 files changed, 147 insertions(+), 120 deletions(-) rename {runtime => libartbase/base}/mem_map.cc (99%) rename {runtime => libartbase/base}/mem_map.h (98%) rename {runtime => libartbase/base}/mem_map_test.cc (100%) rename {runtime => libartbase/base}/zip_archive.cc (98%) rename {runtime => libartbase/base}/zip_archive.h (94%) rename {runtime => libartbase/base}/zip_archive_test.cc (98%) diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index 451a909965..730a1a63e8 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -21,6 +21,7 @@ #include "art_method-inl.h" #include "base/bit_utils.h" +#include "base/mem_map.h" #include "class_linker.h" #include "common_compiler_test.h" #include "compiler.h" @@ -29,7 +30,6 @@ #include "indirect_reference_table.h" #include "java_vm_ext.h" #include "jni_internal.h" -#include "mem_map.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index e2177697bf..fa711748cf 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -55,6 +55,7 @@ #include "base/timing_logger.h" #include "base/unix_file/fd_file.h" #include "base/utils.h" +#include "base/zip_archive.h" #include "class_linker.h" #include "class_loader_context.h" #include "cmdline_parser.h" @@ -98,7 +99,6 @@ #include "vdex_file.h" #include "verifier/verifier_deps.h" #include "well_known_classes.h" -#include "zip_archive.h" namespace art { diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h index c67835b455..9a82fd046e 100644 --- a/dex2oat/linker/image_writer.h +++ b/dex2oat/linker/image_writer.h @@ -33,6 +33,7 @@ #include "base/enums.h" #include "base/length_prefixed_array.h" #include "base/macros.h" +#include "base/mem_map.h" #include "base/os.h" #include "base/safe_map.h" #include "base/utils.h" @@ -41,7 +42,6 @@ #include "image.h" #include "intern_table.h" #include "lock_word.h" -#include "mem_map.h" #include "mirror/dex_cache.h" #include "oat_file.h" #include "obj_ptr.h" diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index bcc59098e5..6552e9582e 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -31,6 +31,7 @@ #include "base/safe_map.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" +#include "base/zip_archive.h" #include "class_linker.h" #include "class_table-inl.h" #include "compiled_method-inl.h" @@ -67,7 +68,6 @@ #include "utils/dex_cache_arrays_layout-inl.h" #include "vdex_file.h" #include "verifier/verifier_deps.h" -#include "zip_archive.h" namespace art { namespace linker { @@ -3398,7 +3398,7 @@ bool OatWriter::WriteDexFiles(OutputStream* out, break; } ZipEntry* entry = oat_dex_file.source_.GetZipEntry(); - if (!entry->IsUncompressed() || !entry->IsAlignedToDexHeader()) { + if (!entry->IsUncompressed() || !entry->IsAlignedTo(alignof(DexFile::Header))) { extract_dex_files_into_vdex_ = true; break; } diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index 7b7bd13c18..619743ef14 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -25,6 +25,7 @@ #include "base/array_ref.h" #include "base/dchecked_vector.h" #include "base/os.h" +#include "base/mem_map.h" #include "base/safe_map.h" #include "compiler.h" #include "debug/debug_info.h" @@ -33,7 +34,6 @@ #include "dex/string_reference.h" #include "dex/type_reference.h" #include "linker/relative_patcher.h" // For RelativePatcherTargetProvider. -#include "mem_map.h" #include "mirror/class.h" #include "oat.h" diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index ec0cbe6a60..7a8c31ba84 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -34,6 +34,7 @@ #include "android-base/stringprintf.h" #include "base/logging.h" // For VLOG_IS_ON. +#include "base/mem_map.h" #include "base/os.h" #include "base/utils.h" #include "dex/art_dex_file_loader.h" @@ -49,7 +50,6 @@ #include "dex_visualize.h" #include "dex_writer.h" #include "jit/profile_compilation_info.h" -#include "mem_map.h" namespace art { diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc index f30cfee4ec..f81d16cbf5 100644 --- a/dexlayout/dexlayout_main.cc +++ b/dexlayout/dexlayout_main.cc @@ -32,8 +32,8 @@ #include #include "base/logging.h" // For InitLogging. +#include "base/mem_map.h" #include "jit/profile_compilation_info.h" -#include "mem_map.h" #include "runtime.h" namespace art { diff --git a/libartbase/Android.bp b/libartbase/Android.bp index 62157e196d..6d76122373 100644 --- a/libartbase/Android.bp +++ b/libartbase/Android.bp @@ -28,6 +28,7 @@ cc_defaults { "base/logging.cc", "base/malloc_arena_pool.cc", "base/memory_region.cc", + "base/mem_map.cc", "base/os_linux.cc", "base/runtime_debug.cc", "base/safe_copy.cc", @@ -37,13 +38,36 @@ cc_defaults { "base/unix_file/fd_file.cc", "base/unix_file/random_access_file_utils.cc", "base/utils.cc", + "base/zip_archive.cc", ], + target: { + android: { + static_libs: [ + // ZipArchive support, the order matters here to get all symbols. + "libziparchive", + "libz", + ], + shared_libs: [ + // For android::FileMap used by libziparchive. + "libutils", + ], + }, + host: { + shared_libs: [ + "libziparchive", + "libz", + ], + }, + }, generated_sources: ["art_libartbase_operator_srcs"], cflags: ["-DBUILDING_LIBART=1"], shared_libs: [ + "libbacktrace", + "liblog", + // For ashmem. + "libcutils", // For common macros. "libbase", - "liblog", ], export_include_dirs: ["."], // ART's macros.h depends on libbase's macros.h. @@ -72,7 +96,10 @@ art_cc_library { strip: { keep_symbols: true, }, - shared_libs: ["libbase"], + shared_libs: [ + "libbase", + "libziparchive", + ], export_shared_lib_headers: ["libbase"], } @@ -82,7 +109,10 @@ art_cc_library { "art_debug_defaults", "libartbase_defaults", ], - shared_libs: ["libbase"], + shared_libs: [ + "libbase", + "libziparchive", + ], export_shared_lib_headers: ["libbase"], } @@ -107,6 +137,7 @@ art_cc_test { "base/leb128_test.cc", "base/logging_test.cc", "base/memory_region_test.cc", + "base/mem_map_test.cc", "base/safe_copy_test.cc", "base/scoped_flock_test.cc", "base/time_utils_test.cc", @@ -115,6 +146,7 @@ art_cc_test { "base/unix_file/fd_file_test.cc", "base/utils_test.cc", "base/variant_map_test.cc", + "base/zip_archive_test.cc", ], shared_libs: [ "libbase", diff --git a/libartbase/base/logging.cc b/libartbase/base/logging.cc index 37b1f8264a..fd2cc20af2 100644 --- a/libartbase/base/logging.cc +++ b/libartbase/base/logging.cc @@ -21,6 +21,8 @@ #include #include "aborting.h" +#include "base/os.h" +#include "base/unix_file/fd_file.h" // Headers for LogMessage::LogLine. #ifdef ART_TARGET_ANDROID @@ -140,4 +142,57 @@ void LogHelper::LogLineLowStack(const char* file, #endif // ART_TARGET_ANDROID } +bool PrintFileToLog(const std::string& file_name, android::base::LogSeverity level) { + File file(file_name, O_RDONLY, false); + if (!file.IsOpened()) { + return false; + } + + constexpr size_t kBufSize = 256; // Small buffer. Avoid stack overflow and stack size warnings. + char buf[kBufSize + 1]; // +1 for terminator. + size_t filled_to = 0; + while (true) { + DCHECK_LT(filled_to, kBufSize); + int64_t n = TEMP_FAILURE_RETRY(read(file.Fd(), &buf[filled_to], kBufSize - filled_to)); + if (n <= 0) { + // Print the rest of the buffer, if it exists. + if (filled_to > 0) { + buf[filled_to] = 0; + LOG(level) << buf; + } + return n == 0; + } + // Scan for '\n'. + size_t i = filled_to; + bool found_newline = false; + for (; i < filled_to + n; ++i) { + if (buf[i] == '\n') { + // Found a line break, that's something to print now. + buf[i] = 0; + LOG(level) << buf; + // Copy the rest to the front. + if (i + 1 < filled_to + n) { + memmove(&buf[0], &buf[i + 1], filled_to + n - i - 1); + filled_to = filled_to + n - i - 1; + } else { + filled_to = 0; + } + found_newline = true; + break; + } + } + if (found_newline) { + continue; + } else { + filled_to += n; + // Check if we must flush now. + if (filled_to == kBufSize) { + buf[kBufSize] = 0; + LOG(level) << buf; + filled_to = 0; + } + } + } +} + } // namespace art diff --git a/libartbase/base/logging.h b/libartbase/base/logging.h index fd5fc74383..986704ec98 100644 --- a/libartbase/base/logging.h +++ b/libartbase/base/logging.h @@ -98,6 +98,9 @@ class LogHelper { DISALLOW_COPY_AND_ASSIGN(LogHelper); }; +// Copy the contents of file_name to the log stream for level. +bool PrintFileToLog(const std::string& file_name, android::base::LogSeverity level); + // Is verbose logging enabled for the given module? Where the module is defined in LogVerbosity. #define VLOG_IS_ON(module) UNLIKELY(::art::gLogVerbosity.module) diff --git a/runtime/mem_map.cc b/libartbase/base/mem_map.cc similarity index 99% rename from runtime/mem_map.cc rename to libartbase/base/mem_map.cc index b9d51c1125..21634f86b6 100644 --- a/runtime/mem_map.cc +++ b/libartbase/base/mem_map.cc @@ -34,7 +34,6 @@ #include "base/allocator.h" #include "base/bit_utils.h" -#include "base/file_utils.h" #include "base/globals.h" #include "base/logging.h" // For VLOG_IS_ON. #include "base/memory_tool.h" @@ -207,7 +206,8 @@ static bool CheckNonOverlapping(uintptr_t begin, *error_msg = StringPrintf("Requested region 0x%08" PRIxPTR "-0x%08" PRIxPTR " overlaps with " "existing map 0x%08" PRIxPTR "-0x%08" PRIxPTR " (%s)\n%s", begin, end, - static_cast(entry->start), static_cast(entry->end), + static_cast(entry->start), + static_cast(entry->end), entry->name.c_str(), map_info.str().c_str()); return false; diff --git a/runtime/mem_map.h b/libartbase/base/mem_map.h similarity index 98% rename from runtime/mem_map.h rename to libartbase/base/mem_map.h index 0ecb414614..b7beb08dbc 100644 --- a/runtime/mem_map.h +++ b/libartbase/base/mem_map.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_MEM_MAP_H_ -#define ART_RUNTIME_MEM_MAP_H_ +#ifndef ART_LIBARTBASE_BASE_MEM_MAP_H_ +#define ART_LIBARTBASE_BASE_MEM_MAP_H_ #include #include @@ -25,6 +25,7 @@ #include #include "android-base/thread_annotations.h" +#include "base/macros.h" namespace art { @@ -297,4 +298,4 @@ void ZeroAndReleasePages(void* address, size_t length); } // namespace art -#endif // ART_RUNTIME_MEM_MAP_H_ +#endif // ART_LIBARTBASE_BASE_MEM_MAP_H_ diff --git a/runtime/mem_map_test.cc b/libartbase/base/mem_map_test.cc similarity index 100% rename from runtime/mem_map_test.cc rename to libartbase/base/mem_map_test.cc diff --git a/runtime/zip_archive.cc b/libartbase/base/zip_archive.cc similarity index 98% rename from runtime/zip_archive.cc rename to libartbase/base/zip_archive.cc index 5b3cab42d8..4185c227c8 100644 --- a/runtime/zip_archive.cc +++ b/libartbase/base/zip_archive.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "zip_archive.h" +#include "base/zip_archive.h" #include #include @@ -29,7 +29,6 @@ #include "base/bit_utils.h" #include "base/unix_file/fd_file.h" -#include "dex/dex_file.h" namespace art { @@ -55,10 +54,6 @@ bool ZipEntry::IsAlignedTo(size_t alignment) const { return IsAlignedParam(zip_entry_->offset, static_cast(alignment)); } -bool ZipEntry::IsAlignedToDexHeader() const { - return IsAlignedTo(alignof(DexFile::Header)); -} - ZipEntry::~ZipEntry() { delete zip_entry_; } diff --git a/runtime/zip_archive.h b/libartbase/base/zip_archive.h similarity index 94% rename from runtime/zip_archive.h rename to libartbase/base/zip_archive.h index aa54018574..39c8168519 100644 --- a/runtime/zip_archive.h +++ b/libartbase/base/zip_archive.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_ZIP_ARCHIVE_H_ -#define ART_RUNTIME_ZIP_ARCHIVE_H_ +#ifndef ART_LIBARTBASE_BASE_ZIP_ARCHIVE_H_ +#define ART_LIBARTBASE_BASE_ZIP_ARCHIVE_H_ #include #include @@ -24,10 +24,10 @@ #include #include "base/os.h" +#include "base/mem_map.h" #include "base/safe_map.h" #include "base/unix_file/random_access_file.h" #include "globals.h" -#include "mem_map.h" // system/core/zip_archive definitions. struct ZipEntry; @@ -64,7 +64,6 @@ class ZipEntry { bool IsUncompressed(); bool IsAlignedTo(size_t alignment) const; - bool IsAlignedToDexHeader() const; private: ZipEntry(ZipArchiveHandle handle, @@ -102,4 +101,4 @@ class ZipArchive { } // namespace art -#endif // ART_RUNTIME_ZIP_ARCHIVE_H_ +#endif // ART_LIBARTBASE_BASE_ZIP_ARCHIVE_H_ diff --git a/runtime/zip_archive_test.cc b/libartbase/base/zip_archive_test.cc similarity index 98% rename from runtime/zip_archive_test.cc rename to libartbase/base/zip_archive_test.cc index 48ee94ce8c..03f4cd4d54 100644 --- a/runtime/zip_archive_test.cc +++ b/libartbase/base/zip_archive_test.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "zip_archive.h" +#include "base/zip_archive.h" #include #include diff --git a/openjdkjvmti/ti_class_definition.h b/openjdkjvmti/ti_class_definition.h index 31c3611e72..f888a7474f 100644 --- a/openjdkjvmti/ti_class_definition.h +++ b/openjdkjvmti/ti_class_definition.h @@ -39,7 +39,7 @@ #include "art_jvmti.h" #include "base/array_ref.h" -#include "mem_map.h" +#include "base/mem_map.h" namespace openjdkjvmti { diff --git a/openjdkjvmti/ti_class_loader.h b/openjdkjvmti/ti_class_loader.h index 5c9497b0b5..dbe30da462 100644 --- a/openjdkjvmti/ti_class_loader.h +++ b/openjdkjvmti/ti_class_loader.h @@ -39,6 +39,7 @@ #include "art_jvmti.h" #include "art_method.h" #include "base/array_slice.h" +#include "base/mem_map.h" #include "class_linker.h" #include "dex/dex_file.h" #include "dex/utf.h" @@ -47,7 +48,6 @@ #include "jni_env_ext-inl.h" #include "jvmti.h" #include "linear_alloc.h" -#include "mem_map.h" #include "mirror/array-inl.h" #include "mirror/array.h" #include "mirror/class-inl.h" diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h index 778f87e68e..a8877f84cd 100644 --- a/openjdkjvmti/ti_redefine.h +++ b/openjdkjvmti/ti_redefine.h @@ -39,6 +39,7 @@ #include "art_jvmti.h" #include "art_method.h" #include "base/array_ref.h" +#include "base/mem_map.h" #include "class_linker.h" #include "dex/dex_file.h" #include "dex/utf.h" @@ -47,7 +48,6 @@ #include "jni_env_ext-inl.h" #include "jvmti.h" #include "linear_alloc.h" -#include "mem_map.h" #include "mirror/array-inl.h" #include "mirror/array.h" #include "mirror/class-inl.h" diff --git a/openjdkjvmti/transform.cc b/openjdkjvmti/transform.cc index 43b8fe94f4..e88ecef54a 100644 --- a/openjdkjvmti/transform.cc +++ b/openjdkjvmti/transform.cc @@ -39,6 +39,7 @@ #include "art_method.h" #include "base/array_ref.h" +#include "base/mem_map.h" #include "class_linker.h" #include "dex/dex_file.h" #include "dex/dex_file_types.h" @@ -51,7 +52,6 @@ #include "jvalue.h" #include "jvmti.h" #include "linear_alloc.h" -#include "mem_map.h" #include "mirror/array.h" #include "mirror/class-inl.h" #include "mirror/class_ext.h" diff --git a/profman/profman.cc b/profman/profman.cc index 90e342d16a..4e1ea23e08 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -40,6 +40,7 @@ #include "base/time_utils.h" #include "base/unix_file/fd_file.h" #include "base/utils.h" +#include "base/zip_archive.h" #include "boot_image_profile.h" #include "dex/art_dex_file_loader.h" #include "dex/bytecode_utils.h" @@ -51,7 +52,6 @@ #include "jit/profile_compilation_info.h" #include "profile_assistant.h" #include "runtime.h" -#include "zip_archive.h" namespace art { diff --git a/runtime/Android.bp b/runtime/Android.bp index 00d4a6080a..ec4c272cb7 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -119,7 +119,6 @@ cc_defaults { "jobject_comparator.cc", "linear_alloc.cc", "managed_stack.cc", - "mem_map.cc", "method_handles.cc", "mirror/array.cc", "mirror/call_site.cc", @@ -209,7 +208,6 @@ cc_defaults { "verifier/verifier_deps.cc", "verify_object.cc", "well_known_classes.cc", - "zip_archive.cc", "arch/context.cc", "arch/instruction_set.cc", @@ -580,7 +578,6 @@ art_cc_test { "jdwp/jdwp_options_test.cc", "java_vm_ext_test.cc", "jit/profile_compilation_info_test.cc", - "mem_map_test.cc", "method_handles_test.cc", "mirror/dex_cache_test.cc", "mirror/method_type_test.cc", @@ -602,7 +599,6 @@ art_cc_test { "vdex_file_test.cc", "verifier/method_verifier_test.cc", "verifier/reg_type_test.cc", - "zip_archive_test.cc", ], shared_libs: [ "libbacktrace", diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc index 2b3e360650..7b0796cf37 100644 --- a/runtime/base/file_utils.cc +++ b/runtime/base/file_utils.cc @@ -83,59 +83,6 @@ bool ReadFileToString(const std::string& file_name, std::string* result) { } } -bool PrintFileToLog(const std::string& file_name, android::base::LogSeverity level) { - File file(file_name, O_RDONLY, false); - if (!file.IsOpened()) { - return false; - } - - constexpr size_t kBufSize = 256; // Small buffer. Avoid stack overflow and stack size warnings. - char buf[kBufSize + 1]; // +1 for terminator. - size_t filled_to = 0; - while (true) { - DCHECK_LT(filled_to, kBufSize); - int64_t n = TEMP_FAILURE_RETRY(read(file.Fd(), &buf[filled_to], kBufSize - filled_to)); - if (n <= 0) { - // Print the rest of the buffer, if it exists. - if (filled_to > 0) { - buf[filled_to] = 0; - LOG(level) << buf; - } - return n == 0; - } - // Scan for '\n'. - size_t i = filled_to; - bool found_newline = false; - for (; i < filled_to + n; ++i) { - if (buf[i] == '\n') { - // Found a line break, that's something to print now. - buf[i] = 0; - LOG(level) << buf; - // Copy the rest to the front. - if (i + 1 < filled_to + n) { - memmove(&buf[0], &buf[i + 1], filled_to + n - i - 1); - filled_to = filled_to + n - i - 1; - } else { - filled_to = 0; - } - found_newline = true; - break; - } - } - if (found_newline) { - continue; - } else { - filled_to += n; - // Check if we must flush now. - if (filled_to == kBufSize) { - buf[kBufSize] = 0; - LOG(level) << buf; - filled_to = 0; - } - } - } -} - std::string GetAndroidRootSafe(std::string* error_msg) { // Prefer ANDROID_ROOT if it's set. const char* android_dir = getenv("ANDROID_ROOT"); diff --git a/runtime/base/file_utils.h b/runtime/base/file_utils.h index 8adb4f7bf8..d4f6c576c0 100644 --- a/runtime/base/file_utils.h +++ b/runtime/base/file_utils.h @@ -28,7 +28,6 @@ namespace art { bool ReadFileToString(const std::string& file_name, std::string* result); -bool PrintFileToLog(const std::string& file_name, android::base::LogSeverity level); // Find $ANDROID_ROOT, /system, or abort. std::string GetAndroidRoot(); diff --git a/runtime/base/mem_map_arena_pool.cc b/runtime/base/mem_map_arena_pool.cc index d5ea19b70e..9ac7886e5d 100644 --- a/runtime/base/mem_map_arena_pool.cc +++ b/runtime/base/mem_map_arena_pool.cc @@ -26,8 +26,8 @@ #include #include "base/arena_allocator-inl.h" +#include "base/mem_map.h" #include "base/systrace.h" -#include "mem_map.h" namespace art { diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 05159e253d..f6a5efc547 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -30,6 +30,7 @@ #include "base/file_utils.h" #include "base/logging.h" #include "base/macros.h" +#include "base/mem_map.h" #include "base/mutex.h" #include "base/os.h" #include "base/runtime_debug.h" @@ -49,7 +50,6 @@ #include "interpreter/unstarted_runtime.h" #include "java_vm_ext.h" #include "jni_internal.h" -#include "mem_map.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "native/dalvik_system_DexFile.h" diff --git a/runtime/dex/art_dex_file_loader.cc b/runtime/dex/art_dex_file_loader.cc index f3e6a69279..415e451098 100644 --- a/runtime/dex/art_dex_file_loader.cc +++ b/runtime/dex/art_dex_file_loader.cc @@ -26,11 +26,11 @@ #include "base/stl_util.h" #include "base/systrace.h" #include "base/unix_file/fd_file.h" +#include "base/zip_archive.h" #include "dex/compact_dex_file.h" #include "dex/dex_file.h" #include "dex/dex_file_verifier.h" #include "dex/standard_dex_file.h" -#include "zip_archive.h" namespace art { @@ -128,7 +128,7 @@ bool ArtDexFileLoader::GetMultiDexChecksums(const char* filename, do { if (zip_file_only_contains_uncompressed_dex != nullptr) { - if (!(zip_entry->IsUncompressed() && zip_entry->IsAlignedToDexHeader())) { + if (!(zip_entry->IsUncompressed() && zip_entry->IsAlignedTo(alignof(DexFile::Header)))) { *zip_file_only_contains_uncompressed_dex = false; } } diff --git a/runtime/dex/art_dex_file_loader_test.cc b/runtime/dex/art_dex_file_loader_test.cc index 3e0d6662c7..afc2599ea0 100644 --- a/runtime/dex/art_dex_file_loader_test.cc +++ b/runtime/dex/art_dex_file_loader_test.cc @@ -21,6 +21,7 @@ #include "art_dex_file_loader.h" #include "base/file_utils.h" +#include "base/mem_map.h" #include "base/os.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" @@ -31,7 +32,6 @@ #include "dex/dex_file.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" -#include "mem_map.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc index c19fa82877..8f0f9c61dc 100644 --- a/runtime/dexopt_test.cc +++ b/runtime/dexopt_test.cc @@ -21,12 +21,12 @@ #include #include "base/file_utils.h" +#include "base/mem_map.h" #include "common_runtime_test.h" #include "compiler_callbacks.h" #include "dex2oat_environment_test.h" #include "dexopt_test.h" #include "gc/space/image_space.h" -#include "mem_map.h" namespace art { void DexoptTest::SetUp() { diff --git a/runtime/elf_file_impl.h b/runtime/elf_file_impl.h index 3143df537f..a5808e27ba 100644 --- a/runtime/elf_file_impl.h +++ b/runtime/elf_file_impl.h @@ -24,7 +24,7 @@ // Explicitly include our own elf.h to avoid Linux and other dependencies. #include "./elf.h" -#include "mem_map.h" +#include "base/mem_map.h" namespace art { diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h index 7a4bd87b12..e30fef4fc2 100644 --- a/runtime/gc/accounting/atomic_stack.h +++ b/runtime/gc/accounting/atomic_stack.h @@ -27,7 +27,7 @@ #include "base/atomic.h" #include "base/macros.h" -#include "mem_map.h" +#include "base/mem_map.h" #include "stack_reference.h" // This implements a double-ended queue (deque) with various flavors of PushBack operations, diff --git a/runtime/gc/accounting/bitmap.cc b/runtime/gc/accounting/bitmap.cc index e5353807a9..d45a0cc018 100644 --- a/runtime/gc/accounting/bitmap.cc +++ b/runtime/gc/accounting/bitmap.cc @@ -19,9 +19,9 @@ #include // For the PROT_* and MAP_* constants. #include "base/bit_utils.h" +#include "base/mem_map.h" #include "card_table.h" #include "jit/jit_code_cache.h" -#include "mem_map.h" namespace art { namespace gc { diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h index d9c0418f4a..357a4985b6 100644 --- a/runtime/gc/accounting/card_table-inl.h +++ b/runtime/gc/accounting/card_table-inl.h @@ -23,7 +23,7 @@ #include "base/atomic.h" #include "base/bit_utils.h" -#include "mem_map.h" +#include "base/mem_map.h" #include "space_bitmap.h" namespace art { diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc index 4eea607c39..c7f936fb11 100644 --- a/runtime/gc/accounting/card_table.cc +++ b/runtime/gc/accounting/card_table.cc @@ -18,13 +18,13 @@ #include +#include "base/mem_map.h" #include "base/systrace.h" #include "base/utils.h" #include "card_table-inl.h" #include "gc/heap.h" #include "gc/space/space.h" #include "heap_bitmap.h" -#include "mem_map.h" #include "runtime.h" namespace art { diff --git a/runtime/gc/accounting/read_barrier_table.h b/runtime/gc/accounting/read_barrier_table.h index 775746f68d..57e4db9b2b 100644 --- a/runtime/gc/accounting/read_barrier_table.h +++ b/runtime/gc/accounting/read_barrier_table.h @@ -20,10 +20,10 @@ #include // For the PROT_* and MAP_* constants. #include "base/bit_utils.h" +#include "base/mem_map.h" #include "base/mutex.h" #include "gc/space/space.h" #include "globals.h" -#include "mem_map.h" namespace art { namespace gc { diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc index d84288f676..ced62cd249 100644 --- a/runtime/gc/accounting/space_bitmap.cc +++ b/runtime/gc/accounting/space_bitmap.cc @@ -19,8 +19,8 @@ #include "android-base/stringprintf.h" #include "art_field-inl.h" +#include "base/mem_map.h" #include "dex/dex_file-inl.h" -#include "mem_map.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array.h" diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc index 928abe873e..a4095d815f 100644 --- a/runtime/gc/allocator/rosalloc.cc +++ b/runtime/gc/allocator/rosalloc.cc @@ -25,9 +25,9 @@ #include "base/logging.h" // For VLOG #include "base/memory_tool.h" +#include "base/mem_map.h" #include "base/mutex-inl.h" #include "gc/space/memory_tool_settings.h" -#include "mem_map.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object.h" diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h index bc3ab48cf4..d888935ff2 100644 --- a/runtime/gc/space/space.h +++ b/runtime/gc/space/space.h @@ -22,11 +22,11 @@ #include "base/atomic.h" #include "base/macros.h" +#include "base/mem_map.h" #include "base/mutex.h" #include "gc/accounting/space_bitmap.h" #include "gc/collector/object_byte_pair.h" #include "globals.h" -#include "mem_map.h" namespace art { namespace mirror { diff --git a/runtime/gc/space/zygote_space.h b/runtime/gc/space/zygote_space.h index 10c1398001..6fe21d99a8 100644 --- a/runtime/gc/space/zygote_space.h +++ b/runtime/gc/space/zygote_space.h @@ -17,9 +17,9 @@ #ifndef ART_RUNTIME_GC_SPACE_ZYGOTE_SPACE_H_ #define ART_RUNTIME_GC_SPACE_ZYGOTE_SPACE_H_ +#include "base/mem_map.h" #include "gc/accounting/space_bitmap.h" #include "malloc_space.h" -#include "mem_map.h" namespace art { namespace gc { diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index dd8d7dd3e2..791ebf09b7 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -34,6 +34,7 @@ #include "base/enums.h" #include "base/macros.h" #include "base/quasi_atomic.h" +#include "base/zip_archive.h" #include "class_linker.h" #include "common_throws.h" #include "dex/descriptors_names.h" @@ -56,7 +57,6 @@ #include "thread-inl.h" #include "transaction.h" #include "well_known_classes.h" -#include "zip_archive.h" namespace art { namespace interpreter { diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index de64fdd2c9..6dcc87179b 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -22,6 +22,7 @@ #include "art_method-inl.h" #include "base/enums.h" #include "base/logging.h" // For VLOG. +#include "base/mem_map.h" #include "base/quasi_atomic.h" #include "base/stl_util.h" #include "base/systrace.h" @@ -37,7 +38,6 @@ #include "jit/jit.h" #include "jit/profiling_info.h" #include "linear_alloc.h" -#include "mem_map.h" #include "oat_file-inl.h" #include "oat_quick_method_header.h" #include "object_callbacks.h" diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index 6bbe78f3fd..f5c2715cda 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -44,9 +44,9 @@ #include "base/time_utils.h" #include "base/unix_file/fd_file.h" #include "base/utils.h" +#include "base/zip_archive.h" #include "dex/dex_file_loader.h" #include "jit/profiling_info.h" -#include "zip_archive.h" namespace art { diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h index f8334cea4f..c9ab4e584f 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -25,13 +25,13 @@ #include "base/atomic.h" #include "base/bit_memory_region.h" #include "base/malloc_arena_pool.h" +#include "base/mem_map.h" #include "base/safe_map.h" #include "dex/dex_cache_resolved_classes.h" #include "dex/dex_file.h" #include "dex/dex_file_types.h" #include "dex/method_reference.h" #include "dex/type_reference.h" -#include "mem_map.h" namespace art { diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 13319c4c57..8320d9c7ba 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -25,6 +25,7 @@ #include "base/os.h" #include "base/stl_util.h" #include "base/utils.h" +#include "base/zip_archive.h" #include "class_linker.h" #include #include "common_throws.h" @@ -48,7 +49,6 @@ #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "well_known_classes.h" -#include "zip_archive.h" namespace art { diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc index 3a0d76032e..44585fc453 100644 --- a/runtime/native/java_lang_VMClassLoader.cc +++ b/runtime/native/java_lang_VMClassLoader.cc @@ -16,6 +16,7 @@ #include "java_lang_VMClassLoader.h" +#include "base/zip_archive.h" #include "class_linker.h" #include "dex/descriptors_names.h" #include "dex/dex_file_loader.h" @@ -29,7 +30,6 @@ #include "obj_ptr.h" #include "scoped_fast_native_object_access-inl.h" #include "well_known_classes.h" -#include "zip_archive.h" namespace art { diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index cfbcda36b9..f6bee4d7e8 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -40,6 +40,7 @@ #include "base/enums.h" #include "base/file_utils.h" #include "base/logging.h" // For VLOG_IS_ON. +#include "base/mem_map.h" #include "base/os.h" #include "base/stl_util.h" #include "base/systrace.h" @@ -54,7 +55,6 @@ #include "elf_utils.h" #include "gc_root.h" #include "gc/space/image_space.h" -#include "mem_map.h" #include "mirror/class.h" #include "mirror/object-inl.h" #include "oat.h" diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc index 5603526177..72d9919971 100644 --- a/runtime/runtime_callbacks_test.cc +++ b/runtime/runtime_callbacks_test.cc @@ -28,13 +28,13 @@ #include "jni.h" #include "art_method-inl.h" +#include "base/mem_map.h" #include "base/mutex.h" #include "class_linker.h" #include "common_runtime_test.h" #include "dex/class_reference.h" #include "handle.h" #include "handle_scope-inl.h" -#include "mem_map.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "monitor.h" diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h index a465e11055..2784953d69 100644 --- a/runtime/thread_pool.h +++ b/runtime/thread_pool.h @@ -21,8 +21,8 @@ #include #include "barrier.h" +#include "base/mem_map.h" #include "base/mutex.h" -#include "mem_map.h" namespace art { diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index 326fcbc1fe..b7f28f0d03 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -22,9 +22,9 @@ #include "base/array_ref.h" #include "base/macros.h" +#include "base/mem_map.h" #include "base/os.h" #include "dex/compact_offset_table.h" -#include "mem_map.h" #include "quicken_info.h" namespace art { diff --git a/test/305-other-fault-handler/fault_handler.cc b/test/305-other-fault-handler/fault_handler.cc index f04832613b..a0831ca8c2 100644 --- a/test/305-other-fault-handler/fault_handler.cc +++ b/test/305-other-fault-handler/fault_handler.cc @@ -23,9 +23,9 @@ #include #include +#include "base/mem_map.h" #include "fault_handler.h" #include "globals.h" -#include "mem_map.h" namespace art { diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc index d22998ae1b..97e7f4cf3c 100644 --- a/tools/hiddenapi/hiddenapi.cc +++ b/tools/hiddenapi/hiddenapi.cc @@ -21,12 +21,12 @@ #include "android-base/stringprintf.h" #include "android-base/strings.h" +#include "base/mem_map.h" #include "base/os.h" #include "base/unix_file/fd_file.h" #include "dex/art_dex_file_loader.h" #include "dex/dex_file-inl.h" #include "dex/hidden_api_access_flags.h" -#include "mem_map.h" namespace art { diff --git a/tools/hiddenapi/hiddenapi_test.cc b/tools/hiddenapi/hiddenapi_test.cc index af1439520f..ed880e0e92 100644 --- a/tools/hiddenapi/hiddenapi_test.cc +++ b/tools/hiddenapi/hiddenapi_test.cc @@ -17,11 +17,11 @@ #include #include "base/unix_file/fd_file.h" +#include "base/zip_archive.h" #include "common_runtime_test.h" #include "dex/art_dex_file_loader.h" #include "dex/dex_file-inl.h" #include "exec_utils.h" -#include "zip_archive.h" namespace art { -- GitLab From 3f8e02c3603cf48c7a656b2dd8781e11481fe34b Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Tue, 10 Apr 2018 11:55:00 -0700 Subject: [PATCH 254/749] Bug fix in SIMD result detection. Rationale: Only 2-way phis are currently used in reductions to carry SIMD results. Note that this will go away when we introduce proper SIMD types. Note: So far I have not been able to make a small regression test for this. Any pointers on how to set up a catch phi? Test: test-art-host,target Bug: b/77725987 Change-Id: I8f8b2dbec35e906878b7f7ed62690c3234db4211 --- compiler/optimizing/nodes_vector.h | 5 +- test/682-double-catch-phi/expected.txt | 1 + test/682-double-catch-phi/info.txt | 1 + .../smali/DoubleCatchPhi.smali | 47 +++++++++++++++++++ test/682-double-catch-phi/src/Main.java | 26 ++++++++++ 5 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 test/682-double-catch-phi/expected.txt create mode 100644 test/682-double-catch-phi/info.txt create mode 100644 test/682-double-catch-phi/smali/DoubleCatchPhi.smali create mode 100644 test/682-double-catch-phi/src/Main.java diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h index 9b114eb1f7..1a484e1944 100644 --- a/compiler/optimizing/nodes_vector.h +++ b/compiler/optimizing/nodes_vector.h @@ -171,9 +171,12 @@ class HVecOperation : public HVariableInputSizeInstruction { if (instruction->IsVecOperation()) { return !instruction->IsVecExtractScalar(); // only scalar returning vec op } else if (instruction->IsPhi()) { + // Vectorizer only uses Phis in reductions, so checking for a 2-way phi + // with a direct vector operand as second argument suffices. return instruction->GetType() == kSIMDType && - instruction->InputAt(1)->IsVecOperation(); // vectorizer does not go deeper + instruction->InputCount() == 2 && + instruction->InputAt(1)->IsVecOperation(); } return false; } diff --git a/test/682-double-catch-phi/expected.txt b/test/682-double-catch-phi/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/682-double-catch-phi/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/682-double-catch-phi/info.txt b/test/682-double-catch-phi/info.txt new file mode 100644 index 0000000000..0ef2e59e9b --- /dev/null +++ b/test/682-double-catch-phi/info.txt @@ -0,0 +1 @@ +Regression test on double-typed catch phi diff --git a/test/682-double-catch-phi/smali/DoubleCatchPhi.smali b/test/682-double-catch-phi/smali/DoubleCatchPhi.smali new file mode 100644 index 0000000000..1d3f927bb2 --- /dev/null +++ b/test/682-double-catch-phi/smali/DoubleCatchPhi.smali @@ -0,0 +1,47 @@ +# 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. + +.class public LDoubleCatchPhi; + +.super Ljava/lang/Object; + +.field public mValue:D + +.method public constructor ()V +.registers 1 + invoke-direct {v0}, Ljava/lang/Object;->()V + return-void +.end method + +.method public strangeMethod(F)V +.registers 6 + move-object v2, v4 + monitor-enter v4 +:try_start1 + float-to-double v0, v5 + iput-wide v0, v4, LDoubleCatchPhi;->mValue:D + monitor-exit v2 +:try_end1 + goto :end_catch +:catch +:try_start2 + move-exception v3 + monitor-exit v2 +:try_end2 + throw v3 +:end_catch + return-void +.catchall {:try_start1 .. :try_end1} :catch +.catchall {:try_start2 .. :try_end2} :catch +.end method diff --git a/test/682-double-catch-phi/src/Main.java b/test/682-double-catch-phi/src/Main.java new file mode 100644 index 0000000000..e65bf0d4d4 --- /dev/null +++ b/test/682-double-catch-phi/src/Main.java @@ -0,0 +1,26 @@ +/* + * 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.Method; + +public class Main { + public static void main(String[] args) throws Exception { + if (System.getProperty("java.vm.name").equals("Dalvik")) { + Class c = Class.forName("DoubleCatchPhi"); + } + System.out.println("passed"); + } +} -- GitLab From bd600e3e203b3ddd34c2e44bdb705e6902ac7bce Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 12 Apr 2018 14:25:39 -0700 Subject: [PATCH 255/749] ART: Remove support for compiled-methods and compiled-classes This has been superseded by profile support. This reverts commit 70bef0d8f6aa30b0da5c6ca56e1bc5729f74654b. This reverts commit 4bf3ae9930a155f238dfd471413c866912b2579e. Bug: 76145463 Test: mmma art Test: m test-art-host Change-Id: I5a368cd01812e16869352ec219eae095df4919c4 --- compiler/common_compiler_test.cc | 16 ---- compiler/common_compiler_test.h | 8 -- .../driver/compiled_method_storage_test.cc | 2 - compiler/driver/compiler_driver.cc | 14 ---- compiler/driver/compiler_driver.h | 11 +-- compiler/driver/compiler_driver_test.cc | 53 ------------- compiler/jit/jit_compiler.cc | 2 - dex2oat/dex2oat.cc | 74 +------------------ dex2oat/dex2oat_image_test.cc | 43 +---------- dex2oat/dex2oat_options.cc | 14 +--- dex2oat/dex2oat_options.def | 4 - dex2oat/linker/oat_writer_test.cc | 2 - dex2oat/linker/relative_patcher_test.h | 2 - tools/bisection_search/bisection_search.py | 4 - 14 files changed, 6 insertions(+), 243 deletions(-) diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index d3e3a51f7a..96a0c1be4d 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -138,20 +138,6 @@ std::unordered_set* CommonCompilerTest::GetImageClasses() { return new std::unordered_set(); } -// Get the set of compiled classes given to the compiler-driver in SetUp. Note: the compiler -// driver assumes ownership of the set, so the test should properly release the set. -std::unordered_set* CommonCompilerTest::GetCompiledClasses() { - // Null, no selection of compiled-classes. - return nullptr; -} - -// Get the set of compiled methods given to the compiler-driver in SetUp. Note: the compiler -// driver assumes ownership of the set, so the test should properly release the set. -std::unordered_set* CommonCompilerTest::GetCompiledMethods() { - // Null, no selection of compiled-methods. - return nullptr; -} - // Get ProfileCompilationInfo that should be passed to the driver. ProfileCompilationInfo* CommonCompilerTest::GetProfileCompilationInfo() { // Null, profile information will not be taken into account. @@ -190,8 +176,6 @@ void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, isa, instruction_set_features_.get(), GetImageClasses(), - GetCompiledClasses(), - GetCompiledMethods(), number_of_threads, /* swap_fd */ -1, GetProfileCompilationInfo())); diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index 8af29d44f0..39c8bd817b 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -67,14 +67,6 @@ class CommonCompilerTest : public CommonRuntimeTest { // driver assumes ownership of the set, so the test should properly release the set. virtual std::unordered_set* GetImageClasses(); - // Get the set of compiled classes given to the compiler-driver in SetUp. Note: the compiler - // driver assumes ownership of the set, so the test should properly release the set. - virtual std::unordered_set* GetCompiledClasses(); - - // Get the set of compiled methods given to the compiler-driver in SetUp. Note: the compiler - // driver assumes ownership of the set, so the test should properly release the set. - virtual std::unordered_set* GetCompiledMethods(); - virtual ProfileCompilationInfo* GetProfileCompilationInfo(); virtual CompilerFilter::Filter GetCompilerFilter() const { diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc index 0769561d0e..42fbba5109 100644 --- a/compiler/driver/compiled_method_storage_test.cc +++ b/compiler/driver/compiled_method_storage_test.cc @@ -34,8 +34,6 @@ TEST(CompiledMethodStorage, Deduplicate) { /* instruction_set_ */ InstructionSet::kNone, /* instruction_set_features */ nullptr, /* image_classes */ nullptr, - /* compiled_classes */ nullptr, - /* compiled_methods */ nullptr, /* thread_count */ 1u, /* swap_fd */ -1, /* profile_compilation_info */ nullptr); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 4093833e0b..41b7e7be47 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -264,8 +264,6 @@ CompilerDriver::CompilerDriver( InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, std::unordered_set* image_classes, - std::unordered_set* compiled_classes, - std::unordered_set* compiled_methods, size_t thread_count, int swap_fd, const ProfileCompilationInfo* profile_compilation_info) @@ -279,8 +277,6 @@ CompilerDriver::CompilerDriver( requires_constructor_barrier_lock_("constructor barrier lock"), non_relative_linker_patch_count_(0u), image_classes_(image_classes), - classes_to_compile_(compiled_classes), - methods_to_compile_(compiled_methods), number_of_soft_verifier_failures_(0), had_hard_verifier_failure_(false), parallel_thread_count_(thread_count), @@ -638,7 +634,6 @@ static void CompileMethodQuick( (verified_method->GetEncounteredVerificationFailures() & (verifier::VERIFY_ERROR_FORCE_INTERPRETER | verifier::VERIFY_ERROR_LOCKING)) == 0 && // Is eligable for compilation by methods-to-compile filter. - driver->IsMethodToCompile(method_ref) && driver->ShouldCompileBasedOnProfile(method_ref); if (compile) { @@ -1065,15 +1060,6 @@ bool CompilerDriver::IsClassToCompile(const char* descriptor) const { return classes_to_compile_->find(descriptor) != classes_to_compile_->end(); } -bool CompilerDriver::IsMethodToCompile(const MethodReference& method_ref) const { - if (methods_to_compile_ == nullptr) { - return true; - } - - std::string tmp = method_ref.PrettyMethod(); - return methods_to_compile_->find(tmp.c_str()) != methods_to_compile_->end(); -} - bool CompilerDriver::ShouldCompileBasedOnProfile(const MethodReference& method_ref) const { // Profile compilation info may be null if no profile is passed. if (!CompilerFilter::DependsOnProfile(compiler_options_->GetCompilerFilter())) { diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index a5462eefe2..55f3561e3a 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -100,8 +100,6 @@ class CompilerDriver { InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, std::unordered_set* image_classes, - std::unordered_set* compiled_classes, - std::unordered_set* compiled_methods, size_t thread_count, int swap_fd, const ProfileCompilationInfo* profile_compilation_info); @@ -316,9 +314,6 @@ class CompilerDriver { // Checks whether the provided class should be compiled, i.e., is in classes_to_compile_. bool IsClassToCompile(const char* descriptor) const; - // Checks whether the provided method should be compiled, i.e., is in method_to_compile_. - bool IsMethodToCompile(const MethodReference& method_ref) const; - // Checks whether profile guided compilation is enabled and if the method should be compiled // according to the profile file. bool ShouldCompileBasedOnProfile(const MethodReference& method_ref) const; @@ -505,12 +500,8 @@ class CompilerDriver { // This option may be restricted to the boot image, depending on a flag in the implementation. std::unique_ptr> classes_to_compile_; - // Specifies the methods that will be compiled. Note that if methods_to_compile_ is null, - // all methods are eligible for compilation (compilation filters etc. will still apply). - // This option may be restricted to the boot image, depending on a flag in the implementation. - std::unique_ptr> methods_to_compile_; - std::atomic number_of_soft_verifier_failures_; + bool had_hard_verifier_failure_; // A thread pool that can (potentially) run tasks in parallel. diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index 162904c0e7..1332280d20 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -184,59 +184,6 @@ TEST_F(CompilerDriverTest, AbstractMethodErrorStub) { } } -class CompilerDriverMethodsTest : public CompilerDriverTest { - protected: - std::unordered_set* GetCompiledMethods() OVERRIDE { - return new std::unordered_set({ - "byte StaticLeafMethods.identity(byte)", - "int StaticLeafMethods.sum(int, int, int)", - "double StaticLeafMethods.sum(double, double, double, double)" - }); - } -}; - -TEST_F(CompilerDriverMethodsTest, Selection) { - Thread* self = Thread::Current(); - jobject class_loader; - { - ScopedObjectAccess soa(self); - class_loader = LoadDex("StaticLeafMethods"); - } - ASSERT_NE(class_loader, nullptr); - - // Need to enable dex-file writability. Methods rejected to be compiled will run through the - // dex-to-dex compiler. - for (const DexFile* dex_file : GetDexFiles(class_loader)) { - ASSERT_TRUE(dex_file->EnableWrite()); - } - - CompileAll(class_loader); - - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ScopedObjectAccess soa(self); - StackHandleScope<1> hs(self); - Handle h_loader( - hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(self, "LStaticLeafMethods;", h_loader); - ASSERT_NE(klass, nullptr); - - std::unique_ptr> expected(GetCompiledMethods()); - - const auto pointer_size = class_linker->GetImagePointerSize(); - for (auto& m : klass->GetDirectMethods(pointer_size)) { - std::string name = m.PrettyMethod(true); - const void* code = m.GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); - ASSERT_NE(code, nullptr); - if (expected->find(name) != expected->end()) { - expected->erase(name); - EXPECT_FALSE(class_linker->IsQuickToInterpreterBridge(code)); - } else { - EXPECT_TRUE(class_linker->IsQuickToInterpreterBridge(code)); - } - } - EXPECT_TRUE(expected->empty()); -} - class CompilerDriverProfileTest : public CompilerDriverTest { protected: ProfileCompilationInfo* GetProfileCompilationInfo() OVERRIDE { diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index ac5c6fb01f..0de00a82fa 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -142,8 +142,6 @@ JitCompiler::JitCompiler() { instruction_set, instruction_set_features_.get(), /* image_classes */ nullptr, - /* compiled_classes */ nullptr, - /* compiled_methods */ nullptr, /* thread_count */ 1, /* swap_fd */ -1, /* profile_compilation_info */ nullptr)); diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 2b4604e6ef..741bc64c96 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -629,10 +629,6 @@ class Dex2Oat FINAL { image_classes_zip_filename_(nullptr), image_classes_filename_(nullptr), image_storage_mode_(ImageHeader::kStorageModeUncompressed), - compiled_classes_zip_filename_(nullptr), - compiled_classes_filename_(nullptr), - compiled_methods_zip_filename_(nullptr), - compiled_methods_filename_(nullptr), passes_to_run_filename_(nullptr), dirty_image_objects_filename_(nullptr), multi_image_(false), @@ -818,18 +814,6 @@ class Dex2Oat FINAL { Usage("--image-classes-zip should be used with --image-classes"); } - if (compiled_classes_filename_ != nullptr && !IsBootImage()) { - Usage("--compiled-classes should only be used with --image"); - } - - if (compiled_classes_filename_ != nullptr && !boot_image_filename_.empty()) { - Usage("--compiled-classes should not be used with --boot-image"); - } - - if (compiled_classes_zip_filename_ != nullptr && compiled_classes_filename_ == nullptr) { - Usage("--compiled-classes-zip should be used with --compiled-classes"); - } - if (dex_filenames_.empty() && zip_fd_ == -1) { Usage("Input must be supplied with either --dex-file or --zip-fd"); } @@ -873,9 +857,7 @@ class Dex2Oat FINAL { } if (have_profile_file || have_profile_fd) { - if (compiled_classes_filename_ != nullptr || - compiled_classes_zip_filename_ != nullptr || - image_classes_filename_ != nullptr || + if (image_classes_filename_ != nullptr || image_classes_zip_filename_ != nullptr) { Usage("Profile based image creation is not supported with image or compiled classes"); } @@ -1210,10 +1192,6 @@ class Dex2Oat FINAL { AssignIfExists(args, M::Threads, &thread_count_); AssignIfExists(args, M::ImageClasses, &image_classes_filename_); AssignIfExists(args, M::ImageClassesZip, &image_classes_zip_filename_); - AssignIfExists(args, M::CompiledClasses, &compiled_classes_filename_); - AssignIfExists(args, M::CompiledClassesZip, &compiled_classes_zip_filename_); - AssignIfExists(args, M::CompiledMethods, &compiled_methods_filename_); - AssignIfExists(args, M::CompiledMethodsZip, &compiled_methods_zip_filename_); AssignIfExists(args, M::Passes, &passes_to_run_filename_); AssignIfExists(args, M::BootImage, &parser_options->boot_image_filename); AssignIfExists(args, M::AndroidRoot, &android_root_); @@ -1535,8 +1513,7 @@ class Dex2Oat FINAL { dex2oat::ReturnCode Setup() { TimingLogger::ScopedTiming t("dex2oat Setup", timings_); - if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods() || - !PrepareDirtyObjects()) { + if (!PrepareImageClasses() || !PrepareDirtyObjects()) { return dex2oat::ReturnCode::kOther; } @@ -1875,8 +1852,6 @@ class Dex2Oat FINAL { instruction_set_, instruction_set_features_.get(), image_classes_.release(), - compiled_classes_.release(), - compiled_methods_.release(), thread_count_, swap_fd_, profile_compilation_info_.get())); @@ -2421,21 +2396,6 @@ class Dex2Oat FINAL { return true; } - bool PrepareCompiledClasses() { - // If --compiled-classes was specified, calculate the full list of classes to compile in the - // image. - if (compiled_classes_filename_ != nullptr) { - compiled_classes_ = - ReadClasses(compiled_classes_zip_filename_, compiled_classes_filename_, "compiled"); - if (compiled_classes_ == nullptr) { - return false; - } - } else { - compiled_classes_.reset(nullptr); // By default compile everything. - } - return true; - } - static std::unique_ptr> ReadClasses(const char* zip_filename, const char* classes_filename, const char* tag) { @@ -2453,32 +2413,6 @@ class Dex2Oat FINAL { return classes; } - bool PrepareCompiledMethods() { - // If --compiled-methods was specified, read the methods to compile from the given file(s). - if (compiled_methods_filename_ != nullptr) { - std::string error_msg; - if (compiled_methods_zip_filename_ != nullptr) { - compiled_methods_.reset(ReadCommentedInputFromZip>( - compiled_methods_zip_filename_, - compiled_methods_filename_, - nullptr, // No post-processing. - &error_msg)); - } else { - compiled_methods_.reset(ReadCommentedInputFromFile>( - compiled_methods_filename_, - nullptr)); // No post-processing. - } - if (compiled_methods_.get() == nullptr) { - LOG(ERROR) << "Failed to create list of compiled methods from '" - << compiled_methods_filename_ << "': " << error_msg; - return false; - } - } else { - compiled_methods_.reset(nullptr); // By default compile everything. - } - return true; - } - bool PrepareDirtyObjects() { if (dirty_image_objects_filename_ != nullptr) { dirty_image_objects_.reset(ReadCommentedInputFromFile>( @@ -2919,10 +2853,6 @@ class Dex2Oat FINAL { const char* image_classes_zip_filename_; const char* image_classes_filename_; ImageHeader::StorageMode image_storage_mode_; - const char* compiled_classes_zip_filename_; - const char* compiled_classes_filename_; - const char* compiled_methods_zip_filename_; - const char* compiled_methods_filename_; const char* passes_to_run_filename_; const char* dirty_image_objects_filename_; std::unique_ptr> image_classes_; diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc index 6f1224916f..11c0c95060 100644 --- a/dex2oat/dex2oat_image_test.cc +++ b/dex2oat/dex2oat_image_test.cc @@ -239,9 +239,7 @@ TEST_F(Dex2oatImageTest, TestModesAndFilters) { ImageSizes base_sizes = CompileImageAndGetSizes({}); ImageSizes image_classes_sizes; ImageSizes compiled_classes_sizes; - ImageSizes compiled_all_classes_sizes; ImageSizes compiled_methods_sizes; - ImageSizes compiled_all_methods_sizes; ImageSizes profile_sizes; std::cout << "Base compile sizes " << base_sizes << std::endl; // Test image classes @@ -257,65 +255,28 @@ TEST_F(Dex2oatImageTest, TestModesAndFilters) { // Sanity check that dex is the same size. EXPECT_EQ(image_classes_sizes.vdex_size, base_sizes.vdex_size); } - // Test compiled classes with all the classes. - { - ScratchFile classes; - // Only compile every even class. - GenerateClasses(classes.GetFile(), /*frequency*/ 1u); - compiled_all_classes_sizes = CompileImageAndGetSizes( - {"--compiled-classes=" + classes.GetFilename()}); - classes.Close(); - std::cout << "Compiled all classes sizes " << compiled_all_classes_sizes << std::endl; - // Check that oat size is smaller since we didn't compile everything. - EXPECT_EQ(compiled_all_classes_sizes.art_size, base_sizes.art_size); - // TODO(mathieuc): Find a reliable way to check compiled code. - // EXPECT_EQ(compiled_all_classes_sizes.oat_size, base_sizes.oat_size); - EXPECT_EQ(compiled_all_classes_sizes.vdex_size, base_sizes.vdex_size); - } // Test compiled classes. { ScratchFile classes; // Only compile every even class. GenerateClasses(classes.GetFile(), /*frequency*/ 2u); compiled_classes_sizes = CompileImageAndGetSizes( - {"--image-classes=" + classes.GetFilename(), - "--compiled-classes=" + classes.GetFilename()}); + {"--image-classes=" + classes.GetFilename()}); classes.Close(); std::cout << "Compiled classes sizes " << compiled_classes_sizes << std::endl; - // Check that oat size is smaller since we didn't compile everything. - // TODO(mathieuc): Find a reliable way to check compiled code. - // EXPECT_LT(compiled_classes_sizes.oat_size, base_sizes.oat_size); // Art file should be smaller than image classes version since we included fewer classes in the // list. EXPECT_LT(compiled_classes_sizes.art_size, image_classes_sizes.art_size); } - // Test compiled methods. - { - ScratchFile methods; - // Only compile every even class. - GenerateMethods(methods.GetFile(), /*frequency*/ 1u); - compiled_all_methods_sizes = CompileImageAndGetSizes( - {"--compiled-methods=" + methods.GetFilename()}); - methods.Close(); - std::cout << "Compiled all methods sizes " << compiled_all_methods_sizes << std::endl; - EXPECT_EQ(compiled_all_classes_sizes.art_size, base_sizes.art_size); - // TODO(mathieuc): Find a reliable way to check compiled code. b/63746626 - // EXPECT_EQ(compiled_all_classes_sizes.oat_size, base_sizes.oat_size); - EXPECT_EQ(compiled_all_classes_sizes.vdex_size, base_sizes.vdex_size); - } static size_t kMethodFrequency = 3; static size_t kTypeFrequency = 4; // Test compiling fewer methods and classes. { - ScratchFile methods; ScratchFile classes; // Only compile every even class. - GenerateMethods(methods.GetFile(), kMethodFrequency); GenerateClasses(classes.GetFile(), kTypeFrequency); compiled_methods_sizes = CompileImageAndGetSizes( - {"--image-classes=" + classes.GetFilename(), - "--compiled-methods=" + methods.GetFilename()}); - methods.Close(); + {"--image-classes=" + classes.GetFilename()}); classes.Close(); std::cout << "Compiled fewer methods sizes " << compiled_methods_sizes << std::endl; } diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc index 5843691a24..dbb00c22e9 100644 --- a/dex2oat/dex2oat_options.cc +++ b/dex2oat/dex2oat_options.cc @@ -157,19 +157,7 @@ static void AddSwapMappings(Builder& builder) { static void AddCompilerMappings(Builder& builder) { builder. - Define("--compiled-classes=_") - .WithType() - .IntoKey(M::CompiledClasses) - .Define("--compiled-classes-zip=_") - .WithType() - .IntoKey(M::CompiledClassesZip) - .Define("--compiled-methods=_") - .WithType() - .IntoKey(M::CompiledMethods) - .Define("--compiled-methods-zip=_") - .WithType() - .IntoKey(M::CompiledMethodsZip) - .Define("--run-passes=_") + Define("--run-passes=_") .WithType() .IntoKey(M::Passes) .Define("--profile-file=_") diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def index 1a913a9bbf..7be8e56501 100644 --- a/dex2oat/dex2oat_options.def +++ b/dex2oat/dex2oat_options.def @@ -56,10 +56,6 @@ DEX2OAT_OPTIONS_KEY (std::vector, ImageFilenames) DEX2OAT_OPTIONS_KEY (std::string, ImageClasses) DEX2OAT_OPTIONS_KEY (std::string, ImageClassesZip) DEX2OAT_OPTIONS_KEY (ImageHeader::StorageMode, ImageFormat) -DEX2OAT_OPTIONS_KEY (std::string, CompiledClasses) -DEX2OAT_OPTIONS_KEY (std::string, CompiledClassesZip) -DEX2OAT_OPTIONS_KEY (std::string, CompiledMethods) -DEX2OAT_OPTIONS_KEY (std::string, CompiledMethodsZip) DEX2OAT_OPTIONS_KEY (std::string, Passes) DEX2OAT_OPTIONS_KEY (std::string, Base) // TODO: Hex string parsing. DEX2OAT_OPTIONS_KEY (std::string, BootImage) diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index ea4e210b74..37e69f7706 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -110,8 +110,6 @@ class OatTest : public CommonCompilerTest { insn_set, insn_features_.get(), /* image_classes */ nullptr, - /* compiled_classes */ nullptr, - /* compiled_methods */ nullptr, /* thread_count */ 2, /* swap_fd */ -1, /* profile_compilation_info */ nullptr)); diff --git a/dex2oat/linker/relative_patcher_test.h b/dex2oat/linker/relative_patcher_test.h index 3f7c8556c2..9cd51e9b46 100644 --- a/dex2oat/linker/relative_patcher_test.h +++ b/dex2oat/linker/relative_patcher_test.h @@ -49,8 +49,6 @@ class RelativePatcherTest : public testing::Test { instruction_set, /* instruction_set_features*/ nullptr, /* image_classes */ nullptr, - /* compiled_classes */ nullptr, - /* compiled_methods */ nullptr, /* thread_count */ 1u, /* swap_fd */ -1, /* profile_compilation_info */ nullptr), diff --git a/tools/bisection_search/bisection_search.py b/tools/bisection_search/bisection_search.py index a1ac72df9f..250b5d178a 100755 --- a/tools/bisection_search/bisection_search.py +++ b/tools/bisection_search/bisection_search.py @@ -166,10 +166,6 @@ class Dex2OatWrapperTestable(object): """Prepare command to run.""" cmd = self._base_cmd[0:self._arguments_position] # insert additional arguments before the first argument - if compiled_methods is not None: - self._test_env.WriteLines(self._compiled_methods_path, compiled_methods) - cmd += ['-Xcompiler-option', '--compiled-methods={0}'.format( - self._compiled_methods_path)] if passes_to_run is not None: self._test_env.WriteLines(self._passes_to_run_path, passes_to_run) cmd += ['-Xcompiler-option', '--run-passes={0}'.format( -- GitLab From c3d5b84592b6d9c529090cfd1e5e9fb72a00f80b Mon Sep 17 00:00:00 2001 From: Daniel Colascione Date: Sun, 15 Apr 2018 10:52:18 -0700 Subject: [PATCH 256/749] Change monitor doc comment to reflect lock deflation Test: no code change Change-Id: Ief5a9ebc71ab5cee3fe96fe5b00c104b3119b25a --- runtime/monitor.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/monitor.cc b/runtime/monitor.cc index f246d8b1c0..2c38de5dae 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -59,8 +59,8 @@ static constexpr uint64_t kLongWaitMs = 100 * kDebugThresholdFudgeFactor; * though, because we have a full 32 bits to work with. * * The two states of an Object's lock are referred to as "thin" and "fat". A lock may transition - * from the "thin" state to the "fat" state and this transition is referred to as inflation. Once - * a lock has been inflated it remains in the "fat" state indefinitely. + * from the "thin" state to the "fat" state and this transition is referred to as inflation. We + * deflate locks from time to time as part of heap trimming. * * The lock value itself is stored in mirror::Object::monitor_ and the representation is described * in the LockWord value type. -- GitLab From b4dfca56fbf5a2c3612732568dce4d200b626a3a Mon Sep 17 00:00:00 2001 From: Daniel Colascione Date: Sun, 15 Apr 2018 11:15:14 -0700 Subject: [PATCH 257/749] The test for futex support should be a test for Linux Test: code inspection Change-Id: Ic07f1eff2704064cc4416fc58df81c471ff54f4e --- runtime/base/mutex.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index b0eb23d327..1cf4ddded4 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -31,10 +31,10 @@ #include "base/globals.h" #include "base/macros.h" -#if defined(__APPLE__) -#define ART_USE_FUTEXES 0 -#else +#if defined(__linux__) #define ART_USE_FUTEXES 1 +#else +#define ART_USE_FUTEXES 0 #endif // Currently Darwin doesn't support locks with timeouts. -- GitLab From b16949aeb2d0de0867bcb55628932518007fc79f Mon Sep 17 00:00:00 2001 From: Daniel Colascione Date: Sun, 15 Apr 2018 11:21:02 -0700 Subject: [PATCH 258/749] Trivial comment typo fix Test: no logic changes Change-Id: Ie10f49639177927e36cad6a4400ab66c9f2b6c01 --- runtime/lock_word.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/lock_word.h b/runtime/lock_word.h index e89beb6d41..09d856f89b 100644 --- a/runtime/lock_word.h +++ b/runtime/lock_word.h @@ -32,9 +32,9 @@ class Object; class Monitor; -/* The lock value itself as stored in mirror::Object::monitor_. The two most significant bits of - * the state. The four possible states are fat locked, thin/unlocked, hash code, and forwarding - * address. +/* The lock value itself as stored in mirror::Object::monitor_. The two most significant bits + * encode the state. The four possible states are fat locked, thin/unlocked, hash code, and + * forwarding address. * * When the lock word is in the "thin" state and its bits are formatted as follows: * -- GitLab From 72411e6b3b286d91e4da894cd5b12e7a3dc88f40 Mon Sep 17 00:00:00 2001 From: Artem Serov Date: Thu, 19 Oct 2017 16:18:07 +0100 Subject: [PATCH 259/749] ART: Implement scalar loop peeling. Implement scalar loop peeling for invariant exits elimination (on arm64). If the loop exit condition is loop invariant then loop peeling + GVN + DCE can eliminate this exit in the loop body. Note: GVN and DCE aren't applied during loop optimizations. Note: this functionality is turned off by default now. Test: test-art-host, test-art-target, boot-to-gui. Change-Id: I98d20054a431838b452dc06bd25c075eb445960c --- compiler/optimizing/loop_analysis.cc | 25 ++++- compiler/optimizing/loop_analysis.h | 43 +++++++-- compiler/optimizing/loop_optimization.cc | 115 +++++++++++++++++------ compiler/optimizing/loop_optimization.h | 13 +-- compiler/optimizing/superblock_cloner.cc | 21 ++--- compiler/optimizing/superblock_cloner.h | 3 + 6 files changed, 159 insertions(+), 61 deletions(-) diff --git a/compiler/optimizing/loop_analysis.cc b/compiler/optimizing/loop_analysis.cc index cd3bdaf016..a0760eff69 100644 --- a/compiler/optimizing/loop_analysis.cc +++ b/compiler/optimizing/loop_analysis.cc @@ -16,6 +16,8 @@ #include "loop_analysis.h" +#include "base/bit_vector-inl.h" + namespace art { void LoopAnalysis::CalculateLoopBasicProperties(HLoopInformation* loop_info, @@ -33,7 +35,8 @@ void LoopAnalysis::CalculateLoopBasicProperties(HLoopInformation* loop_info, for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* instruction = it.Current(); - if (MakesScalarUnrollingNonBeneficial(instruction)) { + if (MakesScalarPeelingUnrollingNonBeneficial(instruction)) { + analysis_results->has_instructions_preventing_scalar_peeling_ = true; analysis_results->has_instructions_preventing_scalar_unrolling_ = true; } analysis_results->instr_num_++; @@ -42,6 +45,22 @@ void LoopAnalysis::CalculateLoopBasicProperties(HLoopInformation* loop_info, } } +bool LoopAnalysis::HasLoopAtLeastOneInvariantExit(HLoopInformation* loop_info) { + HGraph* graph = loop_info->GetHeader()->GetGraph(); + for (uint32_t block_id : loop_info->GetBlocks().Indexes()) { + HBasicBlock* block = graph->GetBlocks()[block_id]; + DCHECK(block != nullptr); + if (block->EndsWithIf()) { + HIf* hif = block->GetLastInstruction()->AsIf(); + HInstruction* input = hif->InputAt(0); + if (IsLoopExit(loop_info, hif) && !loop_info->Contains(*input->GetBlock())) { + return true; + } + } + } + return false; +} + class Arm64LoopHelper : public ArchDefaultLoopHelper { public: // Scalar loop unrolling parameters and heuristics. @@ -60,7 +79,7 @@ class Arm64LoopHelper : public ArchDefaultLoopHelper { // Loop's maximum instruction count. Loops with higher count will not be unrolled. static constexpr uint32_t kArm64SimdHeuristicMaxBodySizeInstr = 50; - bool IsLoopTooBigForScalarUnrolling(LoopAnalysisInfo* loop_analysis_info) const OVERRIDE { + bool IsLoopTooBigForScalarPeelingUnrolling(LoopAnalysisInfo* loop_analysis_info) const OVERRIDE { size_t instr_num = loop_analysis_info->GetNumberOfInstructions(); size_t bb_num = loop_analysis_info->GetNumberOfBasicBlocks(); return (instr_num >= kArm64ScalarHeuristicMaxBodySizeInstr || @@ -77,6 +96,8 @@ class Arm64LoopHelper : public ArchDefaultLoopHelper { return desired_unrolling_factor; } + bool IsLoopPeelingEnabled() const OVERRIDE { return true; } + uint32_t GetSIMDUnrollingFactor(HBasicBlock* block, int64_t trip_count, uint32_t max_peel, diff --git a/compiler/optimizing/loop_analysis.h b/compiler/optimizing/loop_analysis.h index bad406f10b..ece9858136 100644 --- a/compiler/optimizing/loop_analysis.h +++ b/compiler/optimizing/loop_analysis.h @@ -33,6 +33,7 @@ class LoopAnalysisInfo : public ValueObject { : bb_num_(0), instr_num_(0), exits_num_(0), + has_instructions_preventing_scalar_peeling_(false), has_instructions_preventing_scalar_unrolling_(false), loop_info_(loop_info) {} @@ -40,6 +41,10 @@ class LoopAnalysisInfo : public ValueObject { size_t GetNumberOfInstructions() const { return instr_num_; } size_t GetNumberOfExits() const { return exits_num_; } + bool HasInstructionsPreventingScalarPeeling() const { + return has_instructions_preventing_scalar_peeling_; + } + bool HasInstructionsPreventingScalarUnrolling() const { return has_instructions_preventing_scalar_unrolling_; } @@ -53,6 +58,8 @@ class LoopAnalysisInfo : public ValueObject { size_t instr_num_; // Number of loop's exits. size_t exits_num_; + // Whether the loop has instructions which make scalar loop peeling non-beneficial. + bool has_instructions_preventing_scalar_peeling_; // Whether the loop has instructions which make scalar loop unrolling non-beneficial. bool has_instructions_preventing_scalar_unrolling_; @@ -71,22 +78,35 @@ class LoopAnalysis : public ValueObject { static void CalculateLoopBasicProperties(HLoopInformation* loop_info, LoopAnalysisInfo* analysis_results); + // Returns whether the loop has at least one loop invariant exit. + static bool HasLoopAtLeastOneInvariantExit(HLoopInformation* loop_info); + + // Returns whether HIf's true or false successor is outside the specified loop. + // + // Prerequisite: HIf must be in the specified loop. + static bool IsLoopExit(HLoopInformation* loop_info, const HIf* hif) { + DCHECK(loop_info->Contains(*hif->GetBlock())); + HBasicBlock* true_succ = hif->IfTrueSuccessor(); + HBasicBlock* false_succ = hif->IfFalseSuccessor(); + return (!loop_info->Contains(*true_succ) || !loop_info->Contains(*false_succ)); + } + private: - // Returns whether an instruction makes scalar loop unrolling non-beneficial. + // Returns whether an instruction makes scalar loop peeling/unrolling non-beneficial. // // If in the loop body we have a dex/runtime call then its contribution to the whole - // loop performance will probably prevail. So unrolling optimization will not bring - // any noticeable performance improvement however will increase the code size. - static bool MakesScalarUnrollingNonBeneficial(HInstruction* instruction) { + // loop performance will probably prevail. So peeling/unrolling optimization will not bring + // any noticeable performance improvement. It will increase the code size. + static bool MakesScalarPeelingUnrollingNonBeneficial(HInstruction* instruction) { return (instruction->IsNewArray() || instruction->IsNewInstance() || instruction->IsUnresolvedInstanceFieldGet() || instruction->IsUnresolvedInstanceFieldSet() || instruction->IsUnresolvedStaticFieldGet() || instruction->IsUnresolvedStaticFieldSet() || - // TODO: Unroll loops with intrinsified invokes. + // TODO: Support loops with intrinsified invokes. instruction->IsInvoke() || - // TODO: Unroll loops with ClinitChecks. + // TODO: Support loops with ClinitChecks. instruction->IsClinitCheck()); } }; @@ -105,14 +125,14 @@ class ArchDefaultLoopHelper : public ArenaObject { // doesn't support loop peeling and unrolling. static ArchDefaultLoopHelper* Create(InstructionSet isa, ArenaAllocator* allocator); - // Returns whether the loop is too big for loop unrolling by checking its total number of + // Returns whether the loop is too big for loop peeling/unrolling by checking its total number of // basic blocks and instructions. // - // If the loop body has too many instructions then unrolling optimization will not bring + // If the loop body has too many instructions then peeling/unrolling optimization will not bring // any noticeable performance improvement however will increase the code size. // // Returns 'true' by default, should be overridden by particular target loop helper. - virtual bool IsLoopTooBigForScalarUnrolling( + virtual bool IsLoopTooBigForScalarPeelingUnrolling( LoopAnalysisInfo* loop_analysis_info ATTRIBUTE_UNUSED) const { return true; } // Returns optimal scalar unrolling factor for the loop. @@ -123,6 +143,11 @@ class ArchDefaultLoopHelper : public ArenaObject { return kNoUnrollingFactor; } + // Returns whether scalar loop peeling is enabled, + // + // Returns 'false' by default, should be overridden by particular target loop helper. + virtual bool IsLoopPeelingEnabled() const { return false; } + // Returns optimal SIMD unrolling factor for the loop. // // Returns kNoUnrollingFactor by default, should be overridden by particular target loop helper. diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index b41b659083..c0c721de63 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -34,7 +34,7 @@ namespace art { static constexpr bool kEnableVectorization = true; // Enables scalar loop unrolling in the loop optimizer. -static constexpr bool kEnableScalarUnrolling = false; +static constexpr bool kEnableScalarPeelingUnrolling = false; // // Static helpers. @@ -533,6 +533,43 @@ static bool CheckInductionSetFullyRemoved(ScopedArenaSet* iset) { return true; } +// Tries to statically evaluate condition of the specified "HIf" for other condition checks. +static void TryToEvaluateIfCondition(HIf* instruction, HGraph* graph) { + HInstruction* cond = instruction->InputAt(0); + + // If a condition 'cond' is evaluated in an HIf instruction then in the successors of the + // IF_BLOCK we statically know the value of the condition 'cond' (TRUE in TRUE_SUCC, FALSE in + // FALSE_SUCC). Using that we can replace another evaluation (use) EVAL of the same 'cond' + // with TRUE value (FALSE value) if every path from the ENTRY_BLOCK to EVAL_BLOCK contains the + // edge HIF_BLOCK->TRUE_SUCC (HIF_BLOCK->FALSE_SUCC). + // if (cond) { if(cond) { + // if (cond) {} if (1) {} + // } else { =======> } else { + // if (cond) {} if (0) {} + // } } + if (!cond->IsConstant()) { + HBasicBlock* true_succ = instruction->IfTrueSuccessor(); + HBasicBlock* false_succ = instruction->IfFalseSuccessor(); + + DCHECK_EQ(true_succ->GetPredecessors().size(), 1u); + DCHECK_EQ(false_succ->GetPredecessors().size(), 1u); + + const HUseList& uses = cond->GetUses(); + for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) { + HInstruction* user = it->GetUser(); + size_t index = it->GetIndex(); + HBasicBlock* user_block = user->GetBlock(); + // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput(). + ++it; + if (true_succ->Dominates(user_block)) { + user->ReplaceInput(graph->GetIntConstant(1), index); + } else if (false_succ->Dominates(user_block)) { + user->ReplaceInput(graph->GetIntConstant(0), index); + } + } + } +} + // // Public methods. // @@ -851,40 +888,24 @@ bool HLoopOptimization::TryOptimizeInnerLoopFinite(LoopNode* node) { bool HLoopOptimization::OptimizeInnerLoop(LoopNode* node) { return TryOptimizeInnerLoopFinite(node) || + TryPeelingForLoopInvariantExitsElimination(node) || TryUnrollingForBranchPenaltyReduction(node); } -void HLoopOptimization::PeelOrUnrollOnce(LoopNode* loop_node, - bool do_unrolling, - SuperblockCloner::HBasicBlockMap* bb_map, - SuperblockCloner::HInstructionMap* hir_map) { - // TODO: peel loop nests. - DCHECK(loop_node->inner == nullptr); - - // Check that loop info is up-to-date. - HLoopInformation* loop_info = loop_node->loop_info; - HBasicBlock* header = loop_info->GetHeader(); - DCHECK(loop_info == header->GetLoopInformation()); - PeelUnrollHelper helper(loop_info, bb_map, hir_map); - DCHECK(helper.IsLoopClonable()); - HBasicBlock* new_header = do_unrolling ? helper.DoUnrolling() : helper.DoPeeling(); - DCHECK(header == new_header); - DCHECK(loop_info == new_header->GetLoopInformation()); -} // // Loop unrolling: generic part methods. // -bool HLoopOptimization::TryUnrollingForBranchPenaltyReduction(LoopNode* loop_node) { +bool HLoopOptimization::TryUnrollingForBranchPenaltyReduction(LoopNode* node) { // Don't run peeling/unrolling if compiler_driver_ is nullptr (i.e., running under tests) // as InstructionSet is needed. - if (!kEnableScalarUnrolling || compiler_driver_ == nullptr) { + if (!kEnableScalarPeelingUnrolling || compiler_driver_ == nullptr) { return false; } - HLoopInformation* loop_info = loop_node->loop_info; + HLoopInformation* loop_info = node->loop_info; int64_t trip_count = 0; // Only unroll loops with a known tripcount. if (!induction_range_.HasKnownTripCount(loop_info, &trip_count)) { @@ -900,7 +921,7 @@ bool HLoopOptimization::TryUnrollingForBranchPenaltyReduction(LoopNode* loop_nod LoopAnalysis::CalculateLoopBasicProperties(loop_info, &loop_analysis_info); // Check "IsLoopClonable" last as it can be time-consuming. - if (arch_loop_helper_->IsLoopTooBigForScalarUnrolling(&loop_analysis_info) || + if (arch_loop_helper_->IsLoopTooBigForScalarPeelingUnrolling(&loop_analysis_info) || (loop_analysis_info.GetNumberOfExits() > 1) || loop_analysis_info.HasInstructionsPreventingScalarUnrolling() || !PeelUnrollHelper::IsLoopClonable(loop_info)) { @@ -911,21 +932,57 @@ bool HLoopOptimization::TryUnrollingForBranchPenaltyReduction(LoopNode* loop_nod DCHECK_EQ(unrolling_factor, 2u); // Perform unrolling. - ArenaAllocator* arena = loop_info->GetHeader()->GetGraph()->GetAllocator(); - SuperblockCloner::HBasicBlockMap bb_map( - std::less(), arena->Adapter(kArenaAllocSuperblockCloner)); - SuperblockCloner::HInstructionMap hir_map( - std::less(), arena->Adapter(kArenaAllocSuperblockCloner)); - PeelOrUnrollOnce(loop_node, /* unrolling */ true, &bb_map, &hir_map); + PeelUnrollSimpleHelper helper(loop_info); + helper.DoUnrolling(); // Remove the redundant loop check after unrolling. - HIf* copy_hif = bb_map.Get(loop_info->GetHeader())->GetLastInstruction()->AsIf(); + HIf* copy_hif = + helper.GetBasicBlockMap()->Get(loop_info->GetHeader())->GetLastInstruction()->AsIf(); int32_t constant = loop_info->Contains(*copy_hif->IfTrueSuccessor()) ? 1 : 0; copy_hif->ReplaceInput(graph_->GetIntConstant(constant), 0u); return true; } +bool HLoopOptimization::TryPeelingForLoopInvariantExitsElimination(LoopNode* node) { + // Don't run peeling/unrolling if compiler_driver_ is nullptr (i.e., running under tests) + // as InstructionSet is needed. + if (!kEnableScalarPeelingUnrolling || compiler_driver_ == nullptr) { + return false; + } + + HLoopInformation* loop_info = node->loop_info; + // Check 'IsLoopClonable' the last as it might be time-consuming. + if (!arch_loop_helper_->IsLoopPeelingEnabled()) { + return false; + } + + LoopAnalysisInfo loop_analysis_info(loop_info); + LoopAnalysis::CalculateLoopBasicProperties(loop_info, &loop_analysis_info); + + // Check "IsLoopClonable" last as it can be time-consuming. + if (arch_loop_helper_->IsLoopTooBigForScalarPeelingUnrolling(&loop_analysis_info) || + loop_analysis_info.HasInstructionsPreventingScalarPeeling() || + !LoopAnalysis::HasLoopAtLeastOneInvariantExit(loop_info) || + !PeelUnrollHelper::IsLoopClonable(loop_info)) { + return false; + } + + // Perform peeling. + PeelUnrollSimpleHelper helper(loop_info); + helper.DoPeeling(); + + const SuperblockCloner::HInstructionMap* hir_map = helper.GetInstructionMap(); + for (auto entry : *hir_map) { + HInstruction* copy = entry.second; + if (copy->IsIf()) { + TryToEvaluateIfCondition(copy->AsIf(), graph_); + } + } + + return true; +} + // // Loop vectorization. The implementation is based on the book by Aart J.C. Bik: // "The Software Vectorization Handbook. Applying Multimedia Extensions for Maximum Performance." diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h index 0120cffa56..f9a31a34d4 100644 --- a/compiler/optimizing/loop_optimization.h +++ b/compiler/optimizing/loop_optimization.h @@ -145,19 +145,14 @@ class HLoopOptimization : public HOptimization { // Performs optimizations specific to inner loop. Returns true if anything changed. bool OptimizeInnerLoop(LoopNode* node); - // Performs loop peeling/unrolling once (depends on the 'do_unrolling'); the transformation - // preserves the header and the loop info. - // - // Note: the function records copying information about blocks and instructions. - void PeelOrUnrollOnce(LoopNode* loop_node, - bool do_unrolling, - SuperblockCloner::HBasicBlockMap* bb_map, - SuperblockCloner::HInstructionMap* hir_map); - // Tries to apply loop unrolling for branch penalty reduction and better instruction scheduling // opportunities. Returns whether transformation happened. bool TryUnrollingForBranchPenaltyReduction(LoopNode* loop_node); + // Tries to apply loop peeling for loop invariant exits elimination. Returns whether + // transformation happened. + bool TryPeelingForLoopInvariantExitsElimination(LoopNode* loop_node); + // // Vectorization analysis and synthesis. // diff --git a/compiler/optimizing/superblock_cloner.cc b/compiler/optimizing/superblock_cloner.cc index ee74f1001f..fad7729956 100644 --- a/compiler/optimizing/superblock_cloner.cc +++ b/compiler/optimizing/superblock_cloner.cc @@ -28,11 +28,6 @@ using HInstructionMap = SuperblockCloner::HInstructionMap; using HBasicBlockSet = SuperblockCloner::HBasicBlockSet; using HEdgeSet = SuperblockCloner::HEdgeSet; -// When doing peeling we can choose whether to keep original loop (made of original basic blocks) -// and form a peeled iteration of the copy blocks (preserve the header) or transfer original loop -// blocks to the peeled iteration and create new loop from the copy blocks. Similar for unrolling. -static const bool kPeelUnrollPreserveHeader = true; - void HEdge::Dump(std::ostream& stream) const { stream << "(" << from_ << "->" << to_ << ")"; } @@ -926,16 +921,12 @@ void CollectRemappingInfoForPeelUnroll(bool to_unroll, remap_orig_internal->Insert(e); remap_copy_internal->Insert(e); } else { - if (kPeelUnrollPreserveHeader) { - remap_copy_internal->Insert(e); - } else { - remap_orig_internal->Insert(e); - } + remap_copy_internal->Insert(e); } } // Set up remap_incoming edges set. - if (to_unroll != kPeelUnrollPreserveHeader) { + if (!to_unroll) { remap_incoming->Insert(HEdge(loop_info->GetPreHeader(), loop_header)); } } @@ -992,6 +983,9 @@ HBasicBlock* PeelUnrollHelper::DoPeelUnrollImpl(bool to_unroll) { DCHECK(!loop_info_->IsIrreducible()); HBasicBlock* loop_header = loop_info_->GetHeader(); + // Check that loop info is up-to-date. + DCHECK(loop_info_ == loop_header->GetLoopInformation()); + HGraph* graph = loop_header->GetGraph(); ArenaAllocator allocator(graph->GetAllocator()->GetArenaPool()); @@ -1009,7 +1003,10 @@ HBasicBlock* PeelUnrollHelper::DoPeelUnrollImpl(bool to_unroll) { cloner_.Run(); cloner_.CleanUp(); - return kPeelUnrollPreserveHeader ? loop_header : cloner_.GetBlockCopy(loop_header); + // Check that loop info is preserved. + DCHECK(loop_info_ == loop_header->GetLoopInformation()); + + return loop_header; } PeelUnrollSimpleHelper::PeelUnrollSimpleHelper(HLoopInformation* info) diff --git a/compiler/optimizing/superblock_cloner.h b/compiler/optimizing/superblock_cloner.h index afd5a5d6e7..e0931674cb 100644 --- a/compiler/optimizing/superblock_cloner.h +++ b/compiler/optimizing/superblock_cloner.h @@ -372,6 +372,9 @@ class PeelUnrollSimpleHelper : public ValueObject { HBasicBlock* DoUnrolling() { return helper_.DoUnrolling(); } HLoopInformation* GetRegionToBeAdjusted() const { return helper_.GetRegionToBeAdjusted(); } + const SuperblockCloner::HBasicBlockMap* GetBasicBlockMap() const { return &bb_map_; } + const SuperblockCloner::HInstructionMap* GetInstructionMap() const { return &hir_map_; } + private: SuperblockCloner::HBasicBlockMap bb_map_; SuperblockCloner::HInstructionMap hir_map_; -- GitLab From bd2a4e2c68790d1a3d56cd2f9de3471ca816c140 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Tue, 17 Apr 2018 09:07:37 -0700 Subject: [PATCH 260/749] Make adbconnection try to start the JIT if it is not running. The new adbconnection jdwp-provider forces processes to throw out compiled code when a debugger attaches. This is normally not a problem since the JIT will pick up the slack in short order. If one is debugging system_server (or a few other similar system services) performance will be permanently degraded since these processes run with the JIT disabled. In order to improve the experience of debugging these processes we make adbconnection attempt to start the JIT if it is not currently running. In order for this to work the user will need to run 'adb shell setenforce 0' to disable selinux prior to attaching the debugger. Bug: 78119634 Test: adb root && adb shell setenforce 0 && adb forward tcp:12345 jdwp:`adb shell pidof system_server` && jdb -attach localhost:12345; Change-Id: Iffb382460a1dbcd055d533a44367c4ccb3830f10 --- adbconnection/adbconnection.cc | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc index 06ded26a2d..ee89717043 100644 --- a/adbconnection/adbconnection.cc +++ b/adbconnection/adbconnection.cc @@ -824,11 +824,21 @@ void AdbConnectionState::PerformHandshake() { } void AdbConnectionState::AttachJdwpAgent(art::Thread* self) { + art::Runtime* runtime = art::Runtime::Current(); + if (runtime->GetJit() == nullptr && !runtime->GetInstrumentation()->IsForcedInterpretOnly()) { + // If we don't have a JIT we should try to start the jit for performance reasons. + runtime->CreateJit(); + if (runtime->GetJit() == nullptr) { + LOG(WARNING) << "Could not start jit for debugging. This process might be quite slow as it " + << "is running entirely in the interpreter. Try running 'setenforce 0' and " + << "starting the debugging session over."; + } + } self->AssertNoPendingException(); - art::Runtime::Current()->AttachAgent(/* JNIEnv */ nullptr, - MakeAgentArg(), - /* classloader */ nullptr, - /*allow_non_debuggable_tooling*/ true); + runtime->AttachAgent(/* JNIEnv */ nullptr, + MakeAgentArg(), + /* classloader */ nullptr, + /*allow_non_debuggable_tooling*/ true); if (self->IsExceptionPending()) { LOG(ERROR) << "Failed to load agent " << agent_name_; art::ScopedObjectAccess soa(self); -- GitLab From c4440775c5627fed9701e14a1f9d14dca27ed176 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 16 Apr 2018 14:40:56 -0700 Subject: [PATCH 261/749] Add arg for overwriting class loader class path Added stored_context srgument to EncodeContextForOatFile that overwrites the class path when non-null. This is used by the --stored-class-loader-context argument. Fixed the test. Bug: 70934104 Bug: 67345922 Test: test-art-host-gtest Change-Id: If877d8cfe9d34eeaa941e9f6df2e12539d9c4a6f --- dex2oat/dex2oat.cc | 26 ++++++++++-------------- dex2oat/dex2oat_test.cc | 24 ++++++++++++++++++---- runtime/class_loader_context.cc | 36 ++++++++++++++++++++++++++------- runtime/class_loader_context.h | 11 ++++++++-- 4 files changed, 69 insertions(+), 28 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 741bc64c96..3fe9c477d5 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1249,20 +1249,19 @@ class Dex2Oat FINAL { class_loader_context_arg.c_str()); } if (args.Exists(M::StoredClassLoaderContext)) { - stored_class_loader_context_.reset(new std::string(*args.Get(M::StoredClassLoaderContext))); - std::unique_ptr temp_context = - ClassLoaderContext::Create(*stored_class_loader_context_); - if (temp_context == nullptr) { + const std::string stored_context_arg = *args.Get(M::StoredClassLoaderContext); + stored_class_loader_context_ = ClassLoaderContext::Create(stored_context_arg); + if (stored_class_loader_context_ == nullptr) { Usage("Option --stored-class-loader-context has an incorrect format: %s", - stored_class_loader_context_->c_str()); + stored_context_arg.c_str()); } else if (!class_loader_context_->VerifyClassLoaderContextMatch( - *stored_class_loader_context_, + stored_context_arg, /*verify_names*/ false, /*verify_checksums*/ false)) { Usage( "Option --stored-class-loader-context '%s' mismatches --class-loader-context '%s'", - stored_class_loader_context_->c_str(), - class_loader_context_arg.c_str()); + stored_context_arg.c_str(), + class_loader_context_arg.c_str()); } } } else if (args.Exists(M::StoredClassLoaderContext)) { @@ -1609,12 +1608,9 @@ class Dex2Oat FINAL { // Store the class loader context in the oat header. // TODO: deprecate this since store_class_loader_context should be enough to cover the users // of classpath_dir as well. - std::string class_path_key; - if (stored_class_loader_context_ != nullptr) { - class_path_key = *stored_class_loader_context_; - } else { - class_path_key = class_loader_context_->EncodeContextForOatFile(classpath_dir_); - } + std::string class_path_key = + class_loader_context_->EncodeContextForOatFile(classpath_dir_, + stored_class_loader_context_.get()); key_value_store_->Put(OatHeader::kClassPathKey, class_path_key); } @@ -2822,7 +2818,7 @@ class Dex2Oat FINAL { std::unique_ptr class_loader_context_; // The class loader context stored in the oat file. May be equal to class_loader_context_. - std::unique_ptr stored_class_loader_context_; + std::unique_ptr stored_class_loader_context_; size_t thread_count_; uint64_t start_ns_; diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 710a6af792..bc8468e12f 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -2126,10 +2126,26 @@ TEST_F(Dex2oatTest, AppImageNoProfile) { } TEST_F(Dex2oatClassLoaderContextTest, StoredClassLoaderContext) { + std::vector> dex_files = OpenTestDexFiles("MultiDex"); const std::string out_dir = GetScratchDir(); const std::string odex_location = out_dir + "/base.odex"; - const std::string valid_context = "PCL[" + GetUsedDexLocation() + "]"; + const std::string valid_context = "PCL[" + dex_files[0]->GetLocation() + "]"; const std::string stored_context = "PCL[/system/not_real_lib.jar]"; + std::string expected_stored_context = "PCL["; + size_t index = 1; + for (const std::unique_ptr& dex_file : dex_files) { + const bool is_first = index == 1u; + if (!is_first) { + expected_stored_context += ":"; + } + expected_stored_context += "/system/not_real_lib.jar"; + if (!is_first) { + expected_stored_context += "!classes" + std::to_string(index) + ".dex"; + } + expected_stored_context += "*" + std::to_string(dex_file->GetLocationChecksum()); + ++index; + } + expected_stored_context += + "]"; // The class path should not be valid and should fail being stored. GenerateOdexForTest(GetTestDexFileName("ManyMethods"), odex_location, @@ -2138,8 +2154,8 @@ TEST_F(Dex2oatClassLoaderContextTest, StoredClassLoaderContext) { true, // expect_success false, // use_fd [&](const OatFile& oat_file) { - EXPECT_NE(oat_file.GetClassLoaderContext(), stored_context); - EXPECT_NE(oat_file.GetClassLoaderContext(), valid_context); + EXPECT_NE(oat_file.GetClassLoaderContext(), stored_context) << output_; + EXPECT_NE(oat_file.GetClassLoaderContext(), valid_context) << output_; }); // The stored context should match what we expect even though it's invalid. GenerateOdexForTest(GetTestDexFileName("ManyMethods"), @@ -2150,7 +2166,7 @@ TEST_F(Dex2oatClassLoaderContextTest, StoredClassLoaderContext) { true, // expect_success false, // use_fd [&](const OatFile& oat_file) { - EXPECT_EQ(oat_file.GetClassLoaderContext(), stored_context); + EXPECT_EQ(oat_file.GetClassLoaderContext(), expected_stored_context) << output_; }); } diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index 216ad8f794..4afc44cb91 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -254,6 +254,7 @@ bool ClassLoaderContext::OpenDexFiles(InstructionSet isa, const std::string& cla // This will allow the context to VerifyClassLoaderContextMatch which expects or multidex // location in the class paths. // Note that this will also remove the paths that could not be opened. + info.original_classpath = std::move(info.classpath); info.classpath.clear(); info.checksums.clear(); for (size_t k = opened_dex_files_index; k < info.opened_dex_files.size(); k++) { @@ -294,20 +295,26 @@ bool ClassLoaderContext::RemoveLocationsFromClassPaths( } std::string ClassLoaderContext::EncodeContextForDex2oat(const std::string& base_dir) const { - return EncodeContext(base_dir, /*for_dex2oat*/ true); + return EncodeContext(base_dir, /*for_dex2oat*/ true, /*stored_context*/ nullptr); } -std::string ClassLoaderContext::EncodeContextForOatFile(const std::string& base_dir) const { - return EncodeContext(base_dir, /*for_dex2oat*/ false); +std::string ClassLoaderContext::EncodeContextForOatFile(const std::string& base_dir, + ClassLoaderContext* stored_context) const { + return EncodeContext(base_dir, /*for_dex2oat*/ false, stored_context); } std::string ClassLoaderContext::EncodeContext(const std::string& base_dir, - bool for_dex2oat) const { + bool for_dex2oat, + ClassLoaderContext* stored_context) const { CheckDexFilesOpened("EncodeContextForOatFile"); if (special_shared_library_) { return OatFile::kSpecialSharedLibrary; } + if (stored_context != nullptr) { + DCHECK_EQ(class_loader_chain_.size(), stored_context->class_loader_chain_.size()); + } + std::ostringstream out; if (class_loader_chain_.empty()) { // We can get in this situation if the context was created with a class path containing the @@ -326,6 +333,15 @@ std::string ClassLoaderContext::EncodeContext(const std::string& base_dir, out << GetClassLoaderTypeName(info.type); out << kClassLoaderOpeningMark; std::set seen_locations; + SafeMap remap; + if (stored_context != nullptr) { + DCHECK_EQ(info.original_classpath.size(), + stored_context->class_loader_chain_[i].classpath.size()); + for (size_t k = 0; k < info.original_classpath.size(); ++k) { + // Note that we don't care if the same name appears twice. + remap.Put(info.original_classpath[k], stored_context->class_loader_chain_[i].classpath[k]); + } + } for (size_t k = 0; k < info.opened_dex_files.size(); k++) { const std::unique_ptr& dex_file = info.opened_dex_files[k]; if (for_dex2oat) { @@ -337,7 +353,14 @@ std::string ClassLoaderContext::EncodeContext(const std::string& base_dir, continue; } } - const std::string& location = dex_file->GetLocation(); + std::string location = dex_file->GetLocation(); + // If there is a stored class loader remap, fix up the multidex strings. + if (!remap.empty()) { + std::string base_dex_location = DexFileLoader::GetBaseLocation(location); + auto it = remap.find(base_dex_location); + CHECK(it != remap.end()) << base_dex_location; + location = it->second + DexFileLoader::GetMultiDexSuffix(location); + } if (k > 0) { out << kClasspathSeparator; } @@ -345,7 +368,7 @@ std::string ClassLoaderContext::EncodeContext(const std::string& base_dir, if (!base_dir.empty() && location.substr(0, base_dir.length()) == base_dir) { out << location.substr(base_dir.length() + 1).c_str(); } else { - out << dex_file->GetLocation().c_str(); + out << location.c_str(); } // dex2oat does not need the checksums. if (!for_dex2oat) { @@ -776,4 +799,3 @@ jclass ClassLoaderContext::GetClassLoaderClass(ClassLoaderType type) { } } // namespace art - diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h index 231acc46ac..1c83007f41 100644 --- a/runtime/class_loader_context.h +++ b/runtime/class_loader_context.h @@ -84,9 +84,12 @@ class ClassLoaderContext { // (so that it can be read and verified at runtime against the actual class // loader hierarchy). // Should only be called if OpenDexFiles() returned true. + // If stored context is non-null, the stored names are overwritten by the class path from the + // stored context. // E.g. if the context is PCL[a.dex:b.dex] this will return // "PCL[a.dex*a_checksum*b.dex*a_checksum]". - std::string EncodeContextForOatFile(const std::string& base_dir) const; + std::string EncodeContextForOatFile(const std::string& base_dir, + ClassLoaderContext* stored_context = nullptr) const; // Encodes the context as a string suitable to be passed to dex2oat. // This is the same as EncodeContextForOatFile but without adding the checksums @@ -150,6 +153,8 @@ class ClassLoaderContext { // The list of class path elements that this loader loads. // Note that this list may contain relative paths. std::vector classpath; + // Original opened class path (ignoring multidex). + std::vector original_classpath; // The list of class path elements checksums. // May be empty if the checksums are not given when the context is created. std::vector checksums; @@ -202,7 +207,9 @@ class ClassLoaderContext { // location). Otherwise, for oat files, the encoding adds all the dex files (including multidex) // together with their checksums. // Should only be called if OpenDexFiles() returned true. - std::string EncodeContext(const std::string& base_dir, bool for_dex2oat) const; + std::string EncodeContext(const std::string& base_dir, + bool for_dex2oat, + ClassLoaderContext* stored_context) const; // Extracts the class loader type from the given spec. // Return ClassLoaderContext::kInvalidClassLoader if the class loader type is not -- GitLab From 6d05700c620d2bca95fd046969753f71aa015ab4 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Mon, 9 Apr 2018 15:39:58 -0700 Subject: [PATCH 262/749] Run GVN earlier. Rationale: Running GVN earlier allows for better subsequent instruction simplifation. For example, running GVN before select generation also finds the MIN in: if (x > a[i]) x = a[i]; Bug: b/74026074 Test: test-art-host,target Change-Id: I633046375637c7809a3603fdf7c5cf77e8f21167 --- compiler/optimizing/loop_optimization.cc | 5 + compiler/optimizing/optimizing_compiler.cc | 8 +- compiler/optimizing/select_generator.cc | 30 +++- libartbase/base/arena_allocator.cc | 1 + libartbase/base/arena_allocator.h | 1 + .../src/Main.java | 2 +- .../551-checker-shifter-operand/src/Main.java | 49 ++----- .../src/Main.java | 12 +- .../src/ByteSimdMinMax.java | 35 ++++- .../src/IntSimdMinMax.java | 38 +++++ test/679-checker-minmax/src/Main.java | 130 +++++++++++++++++- test/681-checker-abs/src/Main.java | 39 ++++++ 12 files changed, 297 insertions(+), 53 deletions(-) diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index c0c721de63..1462404932 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -1353,6 +1353,11 @@ bool HLoopOptimization::VectorizeDef(LoopNode* node, HInstruction* index = instruction->InputAt(1); HInstruction* value = instruction->InputAt(2); HInstruction* offset = nullptr; + // For narrow types, explicit type conversion may have been + // optimized way, so set the no hi bits restriction here. + if (DataType::Size(type) <= 2) { + restrictions |= kNoHiBits; + } if (TrySetVectorType(type, &restrictions) && node->loop_info->IsDefinedOutOfTheLoop(base) && induction_range_.IsUnitStride(instruction, index, graph_, &offset) && diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 79165826d1..cadefc3b01 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -643,15 +643,13 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph, MaybeRunInliner(graph, codegen, dex_compilation_unit, pass_observer, handles); OptimizationDef optimizations2[] = { - // SelectGenerator depends on the InstructionSimplifier removing - // redundant suspend checks to recognize empty blocks. + OptDef(OptimizationPass::kSideEffectsAnalysis, "side_effects$before_gvn"), + OptDef(OptimizationPass::kGlobalValueNumbering), OptDef(OptimizationPass::kSelectGenerator), - // TODO: if we don't inline we can also skip fold2. OptDef(OptimizationPass::kConstantFolding, "constant_folding$after_inlining"), OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$after_inlining"), OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$after_inlining"), - OptDef(OptimizationPass::kSideEffectsAnalysis, "side_effects$before_gvn"), - OptDef(OptimizationPass::kGlobalValueNumbering), + OptDef(OptimizationPass::kSideEffectsAnalysis, "side_effects$before_licm"), OptDef(OptimizationPass::kInvariantCodeMotion), OptDef(OptimizationPass::kInductionVarAnalysis), OptDef(OptimizationPass::kBoundsCheckElimination), diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc index 3f52bdd13c..f9acf5aa9a 100644 --- a/compiler/optimizing/select_generator.cc +++ b/compiler/optimizing/select_generator.cc @@ -16,6 +16,7 @@ #include "select_generator.h" +#include "base/scoped_arena_containers.h" #include "reference_type_propagation.h" namespace art { @@ -90,9 +91,13 @@ static HPhi* GetSingleChangedPhi(HBasicBlock* block, size_t index1, size_t index } void HSelectGenerator::Run() { + // Select cache with local allocator. + ScopedArenaAllocator allocator(graph_->GetArenaStack()); + ScopedArenaSafeMap cache( + std::less(), allocator.Adapter(kArenaAllocSelectGenerator)); + // Iterate in post order in the unlikely case that removing one occurrence of // the selection pattern empties a branch block of another occurrence. - // Otherwise the order does not matter. for (HBasicBlock* block : graph_->GetPostOrder()) { if (!block->EndsWithIf()) continue; @@ -143,7 +148,8 @@ void HSelectGenerator::Run() { DCHECK(both_successors_return || phi != nullptr); // Create the Select instruction and insert it in front of the If. - HSelect* select = new (graph_->GetAllocator()) HSelect(if_instruction->InputAt(0), + HInstruction* condition = if_instruction->InputAt(0); + HSelect* select = new (graph_->GetAllocator()) HSelect(condition, true_value, false_value, if_instruction->GetDexPc()); @@ -180,6 +186,26 @@ void HSelectGenerator::Run() { MaybeRecordStat(stats_, MethodCompilationStat::kSelectGenerated); + // Very simple way of finding common subexpressions in the generated HSelect statements + // (since this runs after GVN). Lookup by condition, and reuse latest one if possible + // (due to post order, latest select is most likely replacement). If needed, we could + // improve this by e.g. using the operands in the map as well. + auto it = cache.find(condition); + if (it == cache.end()) { + cache.Put(condition, select); + } else { + // Found cached value. See if latest can replace cached in the HIR. + HSelect* cached = it->second; + DCHECK_EQ(cached->GetCondition(), select->GetCondition()); + if (cached->GetTrueValue() == select->GetTrueValue() && + cached->GetFalseValue() == select->GetFalseValue() && + select->StrictlyDominates(cached)) { + cached->ReplaceWith(select); + cached->GetBlock()->RemoveInstruction(cached); + } + it->second = select; // always cache latest + } + // No need to update dominance information, as we are simplifying // a simple diamond shape, where the join block is merged with the // entry block. Any following blocks would have had the join block diff --git a/libartbase/base/arena_allocator.cc b/libartbase/base/arena_allocator.cc index 348a812e44..183e5c9f74 100644 --- a/libartbase/base/arena_allocator.cc +++ b/libartbase/base/arena_allocator.cc @@ -77,6 +77,7 @@ const char* const ArenaAllocatorStatsImpl::kAllocNames[] = { "SsaLiveness ", "SsaPhiElim ", "RefTypeProp ", + "SelectGen ", "SideEffects ", "RegAllocator ", "RegAllocVldt ", diff --git a/libartbase/base/arena_allocator.h b/libartbase/base/arena_allocator.h index 3143fbac4c..4d535df1d4 100644 --- a/libartbase/base/arena_allocator.h +++ b/libartbase/base/arena_allocator.h @@ -87,6 +87,7 @@ enum ArenaAllocKind { kArenaAllocSsaLiveness, kArenaAllocSsaPhiElimination, kArenaAllocReferenceTypePropagation, + kArenaAllocSelectGenerator, kArenaAllocSideEffectsAnalysis, kArenaAllocRegisterAllocator, kArenaAllocRegisterAllocatorValidate, diff --git a/test/458-checker-instruct-simplification/src/Main.java b/test/458-checker-instruct-simplification/src/Main.java index 7797f31867..444b4557ce 100644 --- a/test/458-checker-instruct-simplification/src/Main.java +++ b/test/458-checker-instruct-simplification/src/Main.java @@ -2572,7 +2572,7 @@ public class Main { /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 255 /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> And [<>,<>,<>] /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java index 3177ec0a3c..fb76904677 100644 --- a/test/551-checker-shifter-operand/src/Main.java +++ b/test/551-checker-shifter-operand/src/Main.java @@ -728,40 +728,9 @@ public class Main { /// CHECK: UShr /// CHECK-NOT: UShr // - // Note: simplification followed by GVN exposes the common subexpressions between shifts with larger distance - // `b << 62`, `b << 63` etc. and the equivalent smaller distances. - // - /// CHECK-START: void Main.$opt$validateShiftInt(int, int) GVN (after) - /// CHECK: Shl - /// CHECK: Shl - /// CHECK: Shl - /// CHECK: Shl - /// CHECK: Shl - /// CHECK: Shl - /// CHECK: Shl - /// CHECK: Shl - /// CHECK: Shl - /// CHECK-NOT: Shl - /// CHECK: Shr - /// CHECK: Shr - /// CHECK: Shr - /// CHECK: Shr - /// CHECK: Shr - /// CHECK: Shr - /// CHECK: Shr - /// CHECK: Shr - /// CHECK: Shr - /// CHECK-NOT: Shl - /// CHECK: UShr - /// CHECK: UShr - /// CHECK: UShr - /// CHECK: UShr - /// CHECK: UShr - /// CHECK: UShr - /// CHECK: UShr - /// CHECK: UShr - /// CHECK: UShr - /// CHECK-NOT: UShr + // Note: running extra simplification before GVN would expose the common subexpressions between + // shifts with larger distance `b << 62`, `b << 63` etc. and the equivalent smaller distances. + // TODO: b/78171933 // /// CHECK-START-ARM: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm (after) /// CHECK: DataProcWithShifterOp @@ -791,6 +760,12 @@ public class Main { /// CHECK: DataProcWithShifterOp /// CHECK: DataProcWithShifterOp /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp /// CHECK-NOT: DataProcWithShifterOp /// CHECK-START-ARM: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm (after) @@ -826,6 +801,12 @@ public class Main { /// CHECK: DataProcWithShifterOp /// CHECK: DataProcWithShifterOp /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp /// CHECK-NOT: DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm64 (after) diff --git a/test/565-checker-doublenegbitwise/src/Main.java b/test/565-checker-doublenegbitwise/src/Main.java index 816cafc2ba..80358cd2ba 100644 --- a/test/565-checker-doublenegbitwise/src/Main.java +++ b/test/565-checker-doublenegbitwise/src/Main.java @@ -101,13 +101,13 @@ public class Main { /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> And [<>,<>] + /// CHECK-DAG: <> And [<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_inlining (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> Or [<>,<>] + /// CHECK-DAG: <> Or [<>,<>] /// CHECK-DAG: <> BooleanNot [<>] /// CHECK-DAG: Return [<>] @@ -172,13 +172,13 @@ public class Main { /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> Or [<>,<>] + /// CHECK-DAG: <> Or [<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_inlining (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> And [<>,<>] + /// CHECK-DAG: <> And [<>,<>] /// CHECK-DAG: <> BooleanNot [<>] /// CHECK-DAG: Return [<>] @@ -282,13 +282,13 @@ public class Main { /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> Xor [<>,<>] + /// CHECK-DAG: <> Xor [<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_inlining (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> Xor [<>,<>] + /// CHECK-DAG: <> Xor [<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_bce (after) diff --git a/test/651-checker-simd-minmax/src/ByteSimdMinMax.java b/test/651-checker-simd-minmax/src/ByteSimdMinMax.java index 8dacd5d51e..fff15faeaa 100644 --- a/test/651-checker-simd-minmax/src/ByteSimdMinMax.java +++ b/test/651-checker-simd-minmax/src/ByteSimdMinMax.java @@ -174,6 +174,30 @@ public class ByteSimdMinMax { } } + /// CHECK-START-{ARM,ARM64}: void ByteSimdMinMax.doitMinAlt(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none + /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none + private static void doitMinAlt(byte[] x, byte[] y, byte[] z) { + int n = Math.min(x.length, Math.min(y.length, z.length)); + for (int i = 0; i < n; ++i) { + x[i] = y[i] < z[i] ? y[i] : z[i]; + } + } + + /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMaxAlt(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none + /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Int8 loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none + private static void doitMaxAlt(byte[] x, byte[] y, byte[] z) { + int n = Math.min(x.length, Math.min(y.length, z.length)); + for (int i = 0; i < n; ++i) { + x[i] = y[i] > z[i] ? y[i] : z[i]; + } + } + public static void main() { // Initialize cross-values for all possible values. int total = 256 * 256; @@ -228,7 +252,16 @@ public class ByteSimdMinMax { byte expected = (byte) (u < 11 ? 11 : (u > 23 ? 23 : u)); expectEquals(expected, x[i]); } - + doitMinAlt(x, y, z); + for (int i = 0; i < total; i++) { + byte expected = (byte) Math.min(y[i], z[i]); + expectEquals(expected, x[i]); + } + doitMaxAlt(x, y, z); + for (int i = 0; i < total; i++) { + byte expected = (byte) Math.max(y[i], z[i]); + expectEquals(expected, x[i]); + } System.out.println("ByteSimdMinMax passed"); } diff --git a/test/651-checker-simd-minmax/src/IntSimdMinMax.java b/test/651-checker-simd-minmax/src/IntSimdMinMax.java index 6373ae10eb..ad88843175 100644 --- a/test/651-checker-simd-minmax/src/IntSimdMinMax.java +++ b/test/651-checker-simd-minmax/src/IntSimdMinMax.java @@ -57,6 +57,38 @@ public class IntSimdMinMax { } } + /// CHECK-START-{ARM,ARM64}: int IntSimdMinMax.findMin(int[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> VecMin [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecReduce [<>] loop:none + /// CHECK-DAG: VecExtractScalar [<>] loop:none + private static int findMin(int[] a) { + int x = Integer.MAX_VALUE; + for (int i = 0; i < a.length; i++) { + if (a[i] < x) + x = a[i]; + } + return x; + } + + /// CHECK-START-{ARM,ARM64}: int IntSimdMinMax.findMax(int[]) loop_optimization (after) + /// CHECK-DAG: <> VecReplicateScalar loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> VecMax [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecReduce [<>] loop:none + /// CHECK-DAG: VecExtractScalar [<>] loop:none + private static int findMax(int[] a) { + int x = Integer.MIN_VALUE; + for (int i = 0; i < a.length; i++) { + if (a[i] > x) + x = a[i]; + } + return x; + } + public static void main() { int[] interesting = { 0x00000000, 0x00000001, 0x00007fff, 0x00008000, 0x00008001, 0x0000ffff, @@ -92,6 +124,12 @@ public class IntSimdMinMax { int expected = Math.max(y[i], z[i]); expectEquals(expected, x[i]); } + expectEquals(Integer.MIN_VALUE, findMin(x)); + expectEquals(Integer.MAX_VALUE, findMax(x)); + expectEquals(Integer.MIN_VALUE, findMin(y)); + expectEquals(Integer.MAX_VALUE, findMax(y)); + expectEquals(Integer.MIN_VALUE, findMin(z)); + expectEquals(Integer.MAX_VALUE, findMax(z)); System.out.println("IntSimdMinMax passed"); } diff --git a/test/679-checker-minmax/src/Main.java b/test/679-checker-minmax/src/Main.java index e330a5370e..48de1da291 100644 --- a/test/679-checker-minmax/src/Main.java +++ b/test/679-checker-minmax/src/Main.java @@ -309,19 +309,42 @@ public class Main { return a >= b ? a : b; } - // // Complications. // - // TODO: coming soon, under discussion + /// CHECK-START: int Main.min0(int[], int[]) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> ArrayGet [{{l\d+}},{{i\d+}}] + /// CHECK-DAG: <> ArrayGet [{{l\d+}},{{i\d+}}] + /// CHECK-DAG: <> GreaterThan [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.min0(int[], int[]) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> Min + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.min0(int[], int[]) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select public static int min0(int[] a, int[] b) { // Repeat of array references needs finding the common subexpressions // prior to doing the select and min/max recognition. return a[0] <= b[0] ? a[0] : b[0]; } - // TODO: coming soon, under discussion + /// CHECK-START: int Main.max0(int[], int[]) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> ArrayGet [{{l\d+}},{{i\d+}}] + /// CHECK-DAG: <> ArrayGet [{{l\d+}},{{i\d+}}] + /// CHECK-DAG: <> LessThan [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.max0(int[], int[]) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> Max + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.max0(int[], int[]) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select public static int max0(int[] a, int[] b) { // Repeat of array references needs finding the common subexpressions // prior to doing the select and min/max recognition. @@ -417,8 +440,102 @@ public class Main { return (x < -100) ? -100 : ((x > 100) ? 100 : x); } + /// CHECK-START: int Main.minmaxCSEScalar(int, int) select_generator (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> LessThanOrEqual [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.minmaxCSEScalar(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> Max [<>,<>] + /// CHECK-DAG: <> Min [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: Return [<>] + public static int minmaxCSEScalar(int x, int y) { + int t1 = (x > y) ? x : y; + int t2 = (x < y) ? x : y; + int t3 = (x > y) ? x : y; + int t4 = (x < y) ? x : y; + int t5 = (x > y) ? x : y; + int t6 = (x < y) ? x : y; + // Make sure min/max is CSEed. + return t1 + t2 + t3 + t4 + t5 + t6; + } + + /// CHECK-START: int Main.minmaxCSEArray(int[], int[]) select_generator (after) + /// CHECK-DAG: <> ArrayGet + /// CHECK-DAG: <> ArrayGet + /// CHECK-DAG: <> LessThanOrEqual [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.minmaxCSEArray(int[], int[]) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> ArrayGet + /// CHECK-DAG: <> ArrayGet + /// CHECK-DAG: <> Max [<>,<>] + /// CHECK-DAG: <> Min [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: Return [<>] + public static int minmaxCSEArray(int[] x, int[] y) { + int t1 = (x[0] > y[0]) ? x[0] : y[0]; + int t2 = (x[0] < y[0]) ? x[0] : y[0]; + int t3 = (x[0] > y[0]) ? x[0] : y[0]; + int t4 = (x[0] < y[0]) ? x[0] : y[0]; + int t5 = (x[0] > y[0]) ? x[0] : y[0]; + int t6 = (x[0] < y[0]) ? x[0] : y[0]; + // Make sure min/max is CSEed. + return t1 + t2 + t3 + t4 + t5 + t6; + } + + /// CHECK-START: int Main.minmaxCSEScalarAndCond(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> Max [<>,<>] + /// CHECK-DAG: <> Min [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: Return [<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: Return [<>] + public static int minmaxCSEScalarAndCond(int x, int y) { + int t1 = (x > y) ? x : y; + int t2 = (x < y) ? x : y; + if (x == y) + return t1 + t2; + int t3 = (x > y) ? x : y; + int t4 = (x < y) ? x : y; + // Make sure min/max is CSEed. + return t1 + t2 + t3 + t4; + } + public static void main(String[] args) { - // Types. + // Intrinsics. expectEquals(10, minI(10)); expectEquals(20, minI(25)); expectEquals(10L, minL(10L)); @@ -427,6 +544,7 @@ public class Main { expectEquals(25, maxI(25)); expectEquals(20L, maxL(10L)); expectEquals(25L, maxL(25L)); + // Types. expectEquals(10, min1(10, 20)); expectEquals(10, min2(10, 20)); expectEquals(10, min3(10, 20)); @@ -458,6 +576,10 @@ public class Main { expectEquals(-100, minmax4(-200)); expectEquals(10, minmax4(10)); expectEquals(100, minmax4(200)); + expectEquals(90, minmaxCSEScalar(10, 20)); + expectEquals(90, minmaxCSEArray(a, b)); + expectEquals(20, minmaxCSEScalarAndCond(10, 10)); + expectEquals(60, minmaxCSEScalarAndCond(10, 20)); System.out.println("passed"); } diff --git a/test/681-checker-abs/src/Main.java b/test/681-checker-abs/src/Main.java index d1ba7c6851..2b95a8d56e 100644 --- a/test/681-checker-abs/src/Main.java +++ b/test/681-checker-abs/src/Main.java @@ -19,6 +19,10 @@ */ public class Main { + // + // Intrinsics. + // + /// CHECK-START: int Main.absI(int) instruction_simplifier (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> InvokeStaticOrDirect [<>] intrinsic:MathAbsInt @@ -51,6 +55,10 @@ public class Main { return Math.abs(a); } + // + // Types. + // + /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_inlining (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 0 @@ -184,6 +192,29 @@ public class Main { return a >= 0 ? a : -a; } + // + // Complications. + // + + /// CHECK-START: int Main.abs0(int[]) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> ArrayGet [{{l\d+}},{{i\d+}}] + /// CHECK-DAG: <> LessThan [<>,<>] + /// CHECK-DAG: <> [<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.abs0(int[]) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <> ArrayGet [{{l\d+}},{{i\d+}}] + /// CHECK-DAG: <> Abs [<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.abs0(int[]) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int abs0(int[] a) { + return a[0] >= 0 ? a[0] : -a[0]; + } + // // Nop zero extension. // @@ -248,10 +279,12 @@ public class Main { } public static void main(String[] args) { + // Intrinsics. expectEquals(10, absI(-10)); expectEquals(20, absI(20)); expectEquals(10L, absL(-10L)); expectEquals(20L, absL(20L)); + // Types. expectEquals(10, abs1(-10)); expectEquals(20, abs1(20)); expectEquals(10, abs2(-10)); @@ -266,6 +299,12 @@ public class Main { expectEquals(20, abs6((byte) 20)); expectEquals(10L, abs7(-10L)); expectEquals(20L, abs7(20L)); + // Complications. + int[] a = { 13 }; + int[] b = { -11 }; + expectEquals(13, abs0(a)); + expectEquals(11, abs0(b)); + // Nop zero extension. expectEquals(1, zabs1((byte) 1)); expectEquals(0xff, zabs1((byte) -1)); expectEquals(1, zabs2((short) 1)); -- GitLab From 783bdf8ed586566a0f49678388503ecfcaff0a2b Mon Sep 17 00:00:00 2001 From: Alex Light Date: Tue, 17 Apr 2018 17:03:16 -0700 Subject: [PATCH 263/749] Add more logging to test 1935 This test is very occasionally failing on buildbots. This adds more information that will be useful for figuring out exactly what state the runtime is in when the test fails. Bug: 77306669 Test: ./test.py --host -j50 Change-Id: If7017a265a6d39b796e50eb8c3d02a62718e85cd --- test/1935-get-set-current-frame-jit/src/Main.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/1935-get-set-current-frame-jit/src/Main.java b/test/1935-get-set-current-frame-jit/src/Main.java index 378aaf7a94..70e94c439f 100644 --- a/test/1935-get-set-current-frame-jit/src/Main.java +++ b/test/1935-get-set-current-frame-jit/src/Main.java @@ -74,9 +74,11 @@ public class Main { if (hasJit()) { boolean inOsr = Main.isInOsrCode("run"); if (expectOsr && !inOsr) { - throw new Error("Expected to be in OSR but was not."); + throw new Error( + "Expected to be in OSR but was not. interpreter: " + Main.isInterpreted()); } else if (!expectOsr && inOsr) { - throw new Error("Expected not to be in OSR but was."); + throw new Error( + "Expected not to be in OSR but was. interpreter: " + Main.isInterpreted()); } } reportValue(TARGET); -- GitLab From 2905de1c0e5b6a0c995be474b3f0efdfdc6a41c4 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Tue, 17 Apr 2018 14:56:29 -0700 Subject: [PATCH 264/749] Deopt does not throw Rationale: "CanThrow" of deopt was possibly misused to prevents some optimizations. However, the instruction technically cannot throw an exception, and indeed crashed the graph verifier for some corner cases. This Cl sets that right. Bug: 29868356 Test: test-art-host,target Change-Id: Icb551d3b2935282a70ad673a0544e4fe01104da1 --- compiler/optimizing/nodes.cc | 9 ---- compiler/optimizing/nodes.h | 7 +-- test/683-deopt-regression/expected.txt | 3 ++ test/683-deopt-regression/info.txt | 1 + test/683-deopt-regression/smali/Deopt.smali | 60 +++++++++++++++++++++ test/683-deopt-regression/src/Main.java | 45 ++++++++++++++++ 6 files changed, 113 insertions(+), 12 deletions(-) create mode 100644 test/683-deopt-regression/expected.txt create mode 100644 test/683-deopt-regression/info.txt create mode 100644 test/683-deopt-regression/smali/Deopt.smali create mode 100644 test/683-deopt-regression/src/Main.java diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index f784f8f7f3..79bb70b9aa 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1916,15 +1916,6 @@ const HTryBoundary* HBasicBlock::ComputeTryEntryOfSuccessors() const { } } -bool HBasicBlock::HasThrowingInstructions() const { - for (HInstructionIterator it(GetInstructions()); !it.Done(); it.Advance()) { - if (it.Current()->CanThrow()) { - return true; - } - } - return false; -} - static bool HasOnlyOneInstruction(const HBasicBlock& block) { return block.GetPhis().IsEmpty() && !block.GetInstructions().IsEmpty() diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 79d733060b..b315c81693 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1272,8 +1272,6 @@ class HBasicBlock : public ArenaObject { // the appropriate try entry will be returned. const HTryBoundary* ComputeTryEntryOfSuccessors() const; - bool HasThrowingInstructions() const; - // Returns whether this block dominates the blocked passed as parameter. bool Dominates(HBasicBlock* block) const; @@ -2132,6 +2130,7 @@ class HInstruction : public ArenaObject { !CanThrow() && !IsSuspendCheck() && !IsControlFlow() && + !IsDeoptimize() && !IsNativeDebugInfo() && !IsParameterValue() && // If we added an explicit barrier then we should keep it. @@ -3238,7 +3237,9 @@ class HDeoptimize FINAL : public HVariableInputSizeInstruction { bool NeedsEnvironment() const OVERRIDE { return true; } - bool CanThrow() const OVERRIDE { return true; } + // Even though deoptimize is often used for "exceptional cases" to go back to + // the interpreter, it never throws an exception. + bool CanThrow() const OVERRIDE { return false; } DeoptimizationKind GetDeoptimizationKind() const { return GetPackedField(); } diff --git a/test/683-deopt-regression/expected.txt b/test/683-deopt-regression/expected.txt new file mode 100644 index 0000000000..54ce0514ff --- /dev/null +++ b/test/683-deopt-regression/expected.txt @@ -0,0 +1,3 @@ +120 +0 +passed diff --git a/test/683-deopt-regression/info.txt b/test/683-deopt-regression/info.txt new file mode 100644 index 0000000000..0c2cb81af7 --- /dev/null +++ b/test/683-deopt-regression/info.txt @@ -0,0 +1 @@ +Regression test on deopt from BCE diff --git a/test/683-deopt-regression/smali/Deopt.smali b/test/683-deopt-regression/smali/Deopt.smali new file mode 100644 index 0000000000..3bd9f6cf75 --- /dev/null +++ b/test/683-deopt-regression/smali/Deopt.smali @@ -0,0 +1,60 @@ +# 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. + +.class public LDeopt; + +.super Ljava/lang/Object; + +.method public constructor ()V +.registers 1 + invoke-direct {v0}, Ljava/lang/Object;->()V + return-void +.end method + +.method public static testCase([I)I + .registers 8 + + const v0, 0x0 # counter + const v1, 0xF # loop max + const v2, 0x0 # result + + :try_start + # Something throwing to start the try block. v6 contains a reference. + move-object v6, p0 + aget v3, p0, v0 + + # Invalidate v6 before entering the loop. + const-wide v5, 0x0 + + :loop_start + # Set v6 to a different reference (creates a catch phi). + const v6, 0x0 + + aget v3, p0, v0 + add-int/2addr v2, v3 + add-int/lit8 v0, v0, 0x1 + if-lt v0, v1, :loop_start + + :try_end + .catchall {:try_start .. :try_end} :catch + + :exit + return v2 + + :catch + invoke-virtual {v6}, Ljava/lang/Object;->hashCode()I # use v6 as a reference + goto :exit + +.end method + diff --git a/test/683-deopt-regression/src/Main.java b/test/683-deopt-regression/src/Main.java new file mode 100644 index 0000000000..fdf749612f --- /dev/null +++ b/test/683-deopt-regression/src/Main.java @@ -0,0 +1,45 @@ +/* + * 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.Method; + +public class Main { + public static void main(String[] args) throws Exception { + if (System.getProperty("java.vm.name").equals("Dalvik")) { + Class c = Class.forName("Deopt"); + Method m = c.getMethod("testCase", int[].class); + int[] p = null; + try { + m.invoke(null, p); + System.out.println("should not reach"); + } catch (Exception e) { + // Tried to invoke hashCode on incoming null. + } + int[] q = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + System.out.println((Integer) m.invoke(null, q)); + int[] r = { }; + System.out.println((Integer) m.invoke(null, r)); + int[] s = { 1 }; + try { + System.out.println((Integer) m.invoke(null, s)); + System.out.println("should not reach"); + } catch (Exception e) { + // Tried to invoke hashCode on generated null. + } + } + System.out.println("passed"); + } +} -- GitLab From fb8f75c160f5f38efcb85988c98033650d5649bc Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 17 Apr 2018 11:08:23 -0700 Subject: [PATCH 265/749] ART: Properly check for attached thread in CheckJNI The previous implementation checked too late, as the required ScopedObjectAccess would have already aborted. Instead, preface all necessary functions with an explicit check. Performance should be almost neutral, as the test is only moved earlier, but not duplicated. Bug: 78135453 Test: m test-art-host-gtest-jni_internal_test Change-Id: I7b98744e9a3895e84ba9e2661975ce29722076c3 --- runtime/check_jni.cc | 104 +++++++++++++++++++++++++++++++++-- runtime/jni_internal_test.cc | 28 ++++++++++ 2 files changed, 128 insertions(+), 4 deletions(-) diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index c625a9700c..9a43790575 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -275,6 +275,43 @@ class VarArgs { }; }; +// Check whether the current thread is attached. This is usually required +// to be the first check, as ScopedCheck needs a ScopedObjectAccess for +// checking heap values (and that will fail with unattached threads). +bool CheckAttachedThread(const char* function_name) { + Thread* self = Thread::Current(); + if (UNLIKELY(self == nullptr)) { + // Need to attach this thread for a proper abort to work. We prefer this + // to get reasonable stacks and environment, rather than relying on + // tombstoned. + JNIEnv* env; + Runtime::Current()->GetJavaVM()->AttachCurrentThread(&env, /* thread_args */ nullptr); + + std::string tmp = android::base::StringPrintf( + "a thread (tid %" PRId64 " is making JNI calls without being attached", + static_cast(GetTid())); + Runtime::Current()->GetJavaVM()->JniAbort(function_name, tmp.c_str()); + + CHECK_NE(Runtime::Current()->GetJavaVM()->DetachCurrentThread(), JNI_ERR); + return false; + } + return true; +} + +// Macro helpers for the above. +#define CHECK_ATTACHED_THREAD(function_name, fail_val) \ + do { \ + if (!CheckAttachedThread((function_name))) { \ + return fail_val; \ + } \ + } while (false) +#define CHECK_ATTACHED_THREAD_VOID(function_name) \ + do { \ + if (!CheckAttachedThread((function_name))) { \ + return; \ + } \ + } while (false) + class ScopedCheck { public: ScopedCheck(uint16_t flags, const char* functionName, bool has_method = true) @@ -1255,10 +1292,7 @@ class ScopedCheck { bool CheckThread(JNIEnv* env) REQUIRES_SHARED(Locks::mutator_lock_) { Thread* self = Thread::Current(); - if (self == nullptr) { - AbortF("a thread (tid %d) is making JNI calls without being attached", GetTid()); - return false; - } + CHECK(self != nullptr); // Get the current thread's JNIEnv by going through our TLS pointer. JNIEnvExt* threadEnv = self->GetJniEnv(); @@ -1708,6 +1742,7 @@ const char* const GuardedCopy::kCanary = "JNI BUFFER RED ZONE"; class CheckJNI { public: static jint GetVersion(JNIEnv* env) { + CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[1] = {{.E = env }}; @@ -1722,6 +1757,7 @@ class CheckJNI { } static jint GetJavaVM(JNIEnv *env, JavaVM **vm) { + CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[2] = {{.E = env }, {.p = vm}}; @@ -1736,6 +1772,7 @@ class CheckJNI { } static jint RegisterNatives(JNIEnv* env, jclass c, const JNINativeMethod* methods, jint nMethods) { + CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[4] = {{.E = env }, {.c = c}, {.p = methods}, {.I = nMethods}}; @@ -1750,6 +1787,7 @@ class CheckJNI { } static jint UnregisterNatives(JNIEnv* env, jclass c) { + CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[2] = {{.E = env }, {.c = c}}; @@ -1764,6 +1802,7 @@ class CheckJNI { } static jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj) { + CHECK_ATTACHED_THREAD(__FUNCTION__, JNIInvalidRefType); // Note: we use "EL" here but "Ep" has been used in the past on the basis that we'd like to // know the object is invalid. The spec says that passing invalid objects or even ones that // are deleted isn't supported. @@ -1782,6 +1821,7 @@ class CheckJNI { static jclass DefineClass(JNIEnv* env, const char* name, jobject loader, const jbyte* buf, jsize bufLen) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[5] = {{.E = env}, {.u = name}, {.L = loader}, {.p = buf}, {.z = bufLen}}; @@ -1796,6 +1836,7 @@ class CheckJNI { } static jclass FindClass(JNIEnv* env, const char* name) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[2] = {{.E = env}, {.u = name}}; @@ -1810,6 +1851,7 @@ class CheckJNI { } static jclass GetSuperclass(JNIEnv* env, jclass c) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[2] = {{.E = env}, {.c = c}}; @@ -1824,6 +1866,7 @@ class CheckJNI { } static jboolean IsAssignableFrom(JNIEnv* env, jclass c1, jclass c2) { + CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_FALSE); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[3] = {{.E = env}, {.c = c1}, {.c = c2}}; @@ -1838,6 +1881,7 @@ class CheckJNI { } static jmethodID FromReflectedMethod(JNIEnv* env, jobject method) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[2] = {{.E = env}, {.L = method}}; @@ -1852,6 +1896,7 @@ class CheckJNI { } static jfieldID FromReflectedField(JNIEnv* env, jobject field) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[2] = {{.E = env}, {.L = field}}; @@ -1866,6 +1911,7 @@ class CheckJNI { } static jobject ToReflectedMethod(JNIEnv* env, jclass cls, jmethodID mid, jboolean isStatic) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[4] = {{.E = env}, {.c = cls}, {.m = mid}, {.I = isStatic}}; @@ -1881,6 +1927,7 @@ class CheckJNI { } static jobject ToReflectedField(JNIEnv* env, jclass cls, jfieldID fid, jboolean isStatic) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[4] = {{.E = env}, {.c = cls}, {.f = fid}, {.I = isStatic}}; @@ -1896,6 +1943,7 @@ class CheckJNI { } static jint Throw(JNIEnv* env, jthrowable obj) { + CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[2] = {{.E = env}, {.t = obj}}; @@ -1910,6 +1958,7 @@ class CheckJNI { } static jint ThrowNew(JNIEnv* env, jclass c, const char* message) { + CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_NullableUtf, __FUNCTION__); JniValueType args[3] = {{.E = env}, {.c = c}, {.u = message}}; @@ -1924,6 +1973,7 @@ class CheckJNI { } static jthrowable ExceptionOccurred(JNIEnv* env) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__); JniValueType args[1] = {{.E = env}}; @@ -1938,6 +1988,7 @@ class CheckJNI { } static void ExceptionDescribe(JNIEnv* env) { + CHECK_ATTACHED_THREAD_VOID(__FUNCTION__); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__); JniValueType args[1] = {{.E = env}}; @@ -1950,6 +2001,7 @@ class CheckJNI { } static void ExceptionClear(JNIEnv* env) { + CHECK_ATTACHED_THREAD_VOID(__FUNCTION__); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__); JniValueType args[1] = {{.E = env}}; @@ -1962,6 +2014,7 @@ class CheckJNI { } static jboolean ExceptionCheck(JNIEnv* env) { + CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_FALSE); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_CritOkay | kFlag_ExcepOkay, __FUNCTION__); JniValueType args[1] = {{.E = env}}; @@ -1976,6 +2029,7 @@ class CheckJNI { } static void FatalError(JNIEnv* env, const char* msg) { + CHECK_ATTACHED_THREAD_VOID(__FUNCTION__); // The JNI specification doesn't say it's okay to call FatalError with a pending exception, // but you're about to abort anyway, and it's quite likely that you have a pending exception, // and it's not unimaginable that you don't know that you do. So we allow it. @@ -1992,6 +2046,7 @@ class CheckJNI { } static jint PushLocalFrame(JNIEnv* env, jint capacity) { + CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__); JniValueType args[2] = {{.E = env}, {.I = capacity}}; @@ -2006,6 +2061,7 @@ class CheckJNI { } static jobject PopLocalFrame(JNIEnv* env, jobject res) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__); JniValueType args[2] = {{.E = env}, {.L = res}}; @@ -2043,6 +2099,7 @@ class CheckJNI { } static jint EnsureLocalCapacity(JNIEnv *env, jint capacity) { + CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[2] = {{.E = env}, {.I = capacity}}; @@ -2057,6 +2114,7 @@ class CheckJNI { } static jboolean IsSameObject(JNIEnv* env, jobject ref1, jobject ref2) { + CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_FALSE); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[3] = {{.E = env}, {.L = ref1}, {.L = ref2}}; @@ -2071,6 +2129,7 @@ class CheckJNI { } static jobject AllocObject(JNIEnv* env, jclass c) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[2] = {{.E = env}, {.c = c}}; @@ -2085,6 +2144,7 @@ class CheckJNI { } static jobject NewObjectV(JNIEnv* env, jclass c, jmethodID mid, va_list vargs) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); VarArgs rest(mid, vargs); @@ -2101,6 +2161,7 @@ class CheckJNI { } static jobject NewObject(JNIEnv* env, jclass c, jmethodID mid, ...) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); va_list args; va_start(args, mid); jobject result = NewObjectV(env, c, mid, args); @@ -2109,6 +2170,7 @@ class CheckJNI { } static jobject NewObjectA(JNIEnv* env, jclass c, jmethodID mid, jvalue* vargs) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); VarArgs rest(mid, vargs); @@ -2125,6 +2187,7 @@ class CheckJNI { } static jclass GetObjectClass(JNIEnv* env, jobject obj) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[2] = {{.E = env}, {.L = obj}}; @@ -2139,6 +2202,7 @@ class CheckJNI { } static jboolean IsInstanceOf(JNIEnv* env, jobject obj, jclass c) { + CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_FALSE); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[3] = {{.E = env}, {.L = obj}, {.c = c}}; @@ -2314,6 +2378,7 @@ class CheckJNI { #undef CALL static jstring NewString(JNIEnv* env, const jchar* unicode_chars, jsize len) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[3] = {{.E = env}, {.p = unicode_chars}, {.z = len}}; @@ -2328,6 +2393,7 @@ class CheckJNI { } static jstring NewStringUTF(JNIEnv* env, const char* chars) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_NullableUtf, __FUNCTION__); JniValueType args[2] = {{.E = env}, {.u = chars}}; @@ -2343,6 +2409,7 @@ class CheckJNI { } static jsize GetStringLength(JNIEnv* env, jstring string) { + CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_CritOkay, __FUNCTION__); JniValueType args[2] = {{.E = env}, {.s = string}}; @@ -2357,6 +2424,7 @@ class CheckJNI { } static jsize GetStringUTFLength(JNIEnv* env, jstring string) { + CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_CritOkay, __FUNCTION__); JniValueType args[2] = {{.E = env}, {.s = string}}; @@ -2398,6 +2466,7 @@ class CheckJNI { } static void GetStringRegion(JNIEnv* env, jstring string, jsize start, jsize len, jchar* buf) { + CHECK_ATTACHED_THREAD_VOID(__FUNCTION__); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_CritOkay, __FUNCTION__); JniValueType args[5] = {{.E = env}, {.s = string}, {.z = start}, {.z = len}, {.p = buf}}; @@ -2412,6 +2481,7 @@ class CheckJNI { } static void GetStringUTFRegion(JNIEnv* env, jstring string, jsize start, jsize len, char* buf) { + CHECK_ATTACHED_THREAD_VOID(__FUNCTION__); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_CritOkay, __FUNCTION__); JniValueType args[5] = {{.E = env}, {.s = string}, {.z = start}, {.z = len}, {.p = buf}}; @@ -2426,6 +2496,7 @@ class CheckJNI { } static jsize GetArrayLength(JNIEnv* env, jarray array) { + CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_CritOkay, __FUNCTION__); JniValueType args[2] = {{.E = env}, {.a = array}}; @@ -2441,6 +2512,7 @@ class CheckJNI { static jobjectArray NewObjectArray(JNIEnv* env, jsize length, jclass element_class, jobject initial_element) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[4] = @@ -2457,6 +2529,7 @@ class CheckJNI { } static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[3] = {{.E = env}, {.a = array}, {.z = index}}; @@ -2471,6 +2544,7 @@ class CheckJNI { } static void SetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index, jobject value) { + CHECK_ATTACHED_THREAD_VOID(__FUNCTION__); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[4] = {{.E = env}, {.a = array}, {.z = index}, {.L = value}}; @@ -2557,6 +2631,7 @@ class CheckJNI { #undef PRIMITIVE_ARRAY_FUNCTIONS static jint MonitorEnter(JNIEnv* env, jobject obj) { + CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[2] = {{.E = env}, {.L = obj}}; @@ -2574,6 +2649,7 @@ class CheckJNI { } static jint MonitorExit(JNIEnv* env, jobject obj) { + CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__); JniValueType args[2] = {{.E = env}, {.L = obj}}; @@ -2591,6 +2667,7 @@ class CheckJNI { } static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray array, jboolean* is_copy) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_CritGet, __FUNCTION__); JniValueType args[3] = {{.E = env}, {.a = array}, {.p = is_copy}}; @@ -2609,6 +2686,7 @@ class CheckJNI { } static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, void* carray, jint mode) { + CHECK_ATTACHED_THREAD_VOID(__FUNCTION__); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_CritRelease | kFlag_ExcepOkay, __FUNCTION__); sc.CheckNonNull(carray); @@ -2625,6 +2703,7 @@ class CheckJNI { } static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[3] = {{.E = env}, {.p = address}, {.J = capacity}}; @@ -2640,6 +2719,7 @@ class CheckJNI { } static void* GetDirectBufferAddress(JNIEnv* env, jobject buf) { + CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[2] = {{.E = env}, {.L = buf}}; @@ -2656,6 +2736,7 @@ class CheckJNI { } static jlong GetDirectBufferCapacity(JNIEnv* env, jobject buf) { + CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); JniValueType args[2] = {{.E = env}, {.L = buf}}; @@ -2681,6 +2762,7 @@ class CheckJNI { } static jobject NewRef(const char* function_name, JNIEnv* env, jobject obj, IndirectRefKind kind) { + CHECK_ATTACHED_THREAD(function_name, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, function_name); JniValueType args[2] = {{.E = env}, {.L = obj}}; @@ -2709,6 +2791,7 @@ class CheckJNI { } static void DeleteRef(const char* function_name, JNIEnv* env, jobject obj, IndirectRefKind kind) { + CHECK_ATTACHED_THREAD_VOID(function_name); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_ExcepOkay, function_name); JniValueType args[2] = {{.E = env}, {.L = obj}}; @@ -2735,6 +2818,7 @@ class CheckJNI { static jmethodID GetMethodIDInternal(const char* function_name, JNIEnv* env, jclass c, const char* name, const char* sig, bool is_static) { + CHECK_ATTACHED_THREAD(function_name, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, function_name); JniValueType args[4] = {{.E = env}, {.c = c}, {.u = name}, {.u = sig}}; @@ -2754,6 +2838,7 @@ class CheckJNI { static jfieldID GetFieldIDInternal(const char* function_name, JNIEnv* env, jclass c, const char* name, const char* sig, bool is_static) { + CHECK_ATTACHED_THREAD(function_name, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, function_name); JniValueType args[4] = {{.E = env}, {.c = c}, {.u = name}, {.u = sig}}; @@ -2773,6 +2858,7 @@ class CheckJNI { static JniValueType GetField(const char* function_name, JNIEnv* env, jobject obj, jfieldID fid, bool is_static, Primitive::Type type) { + CHECK_ATTACHED_THREAD(function_name, JniValueType()); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, function_name); JniValueType args[3] = {{.E = env}, {.L = obj}, {.f = fid}}; @@ -2867,6 +2953,7 @@ class CheckJNI { static void SetField(const char* function_name, JNIEnv* env, jobject obj, jfieldID fid, bool is_static, Primitive::Type type, JniValueType value) { + CHECK_ATTACHED_THREAD_VOID(function_name); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, function_name); JniValueType args[4] = {{.E = env}, {.L = obj}, {.f = fid}, value}; @@ -2981,6 +3068,7 @@ class CheckJNI { static JniValueType CallMethodA(const char* function_name, JNIEnv* env, jobject obj, jclass c, jmethodID mid, jvalue* vargs, Primitive::Type type, InvokeType invoke) { + CHECK_ATTACHED_THREAD(function_name, JniValueType()); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, function_name); JniValueType result; @@ -3165,6 +3253,7 @@ class CheckJNI { static JniValueType CallMethodV(const char* function_name, JNIEnv* env, jobject obj, jclass c, jmethodID mid, va_list vargs, Primitive::Type type, InvokeType invoke) { + CHECK_ATTACHED_THREAD(function_name, JniValueType()); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, function_name); JniValueType result; @@ -3348,6 +3437,7 @@ class CheckJNI { static const void* GetStringCharsInternal(const char* function_name, JNIEnv* env, jstring string, jboolean* is_copy, bool utf, bool critical) { + CHECK_ATTACHED_THREAD(function_name, nullptr); ScopedObjectAccess soa(env); int flags = critical ? kFlag_CritGet : kFlag_CritOkay; ScopedCheck sc(flags, function_name); @@ -3388,6 +3478,7 @@ class CheckJNI { static void ReleaseStringCharsInternal(const char* function_name, JNIEnv* env, jstring string, const void* chars, bool utf, bool critical) { + CHECK_ATTACHED_THREAD_VOID(function_name); ScopedObjectAccess soa(env); int flags = kFlag_ExcepOkay | kFlag_Release; if (critical) { @@ -3420,6 +3511,7 @@ class CheckJNI { static jarray NewPrimitiveArray(const char* function_name, JNIEnv* env, jsize length, Primitive::Type type) { + CHECK_ATTACHED_THREAD(function_name, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, function_name); JniValueType args[2] = {{.E = env}, {.z = length}}; @@ -3462,6 +3554,7 @@ class CheckJNI { static void* GetPrimitiveArrayElements(const char* function_name, Primitive::Type type, JNIEnv* env, jarray array, jboolean* is_copy) { + CHECK_ATTACHED_THREAD(function_name, nullptr); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, function_name); JniValueType args[3] = {{.E = env}, {.a = array}, {.p = is_copy}}; @@ -3513,6 +3606,7 @@ class CheckJNI { static void ReleasePrimitiveArrayElements(const char* function_name, Primitive::Type type, JNIEnv* env, jarray array, void* elems, jint mode) { + CHECK_ATTACHED_THREAD_VOID(function_name); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_ExcepOkay, function_name); if (sc.CheckNonNull(elems) && sc.CheckPrimitiveArrayType(soa, array, type)) { @@ -3568,6 +3662,7 @@ class CheckJNI { static void GetPrimitiveArrayRegion(const char* function_name, Primitive::Type type, JNIEnv* env, jarray array, jsize start, jsize len, void* buf) { + CHECK_ATTACHED_THREAD_VOID(function_name); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, function_name); JniValueType args[5] = {{.E = env}, {.a = array}, {.z = start}, {.z = len}, {.p = buf}}; @@ -3618,6 +3713,7 @@ class CheckJNI { static void SetPrimitiveArrayRegion(const char* function_name, Primitive::Type type, JNIEnv* env, jarray array, jsize start, jsize len, const void* buf) { + CHECK_ATTACHED_THREAD_VOID(function_name); ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, function_name); JniValueType args[5] = {{.E = env}, {.a = array}, {.z = start}, {.z = len}, {.p = buf}}; diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index 293e18a5b5..5d74181cef 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -2517,4 +2517,32 @@ TEST_F(JniInternalTest, JNIEnvExtTableOverride) { env_->DeleteGlobalRef(global2); } +TEST_F(JniInternalTest, NonAttachedThread) { + // This tests leads to warnings and errors in the log. + ScopedLogSeverity sls(LogSeverity::FATAL); + CheckJniAbortCatcher check_jni_abort_catcher; + + auto callee = [](void* env_ptr) -> void* { + JNIEnv* env = reinterpret_cast(env_ptr); + env->NewStringUTF("test"); + return nullptr; + }; + + bool old_check_jni = vm_->SetCheckJniEnabled(false); + vm_->SetCheckJniEnabled(true); + { + pthread_t pthread; + int pthread_create_result = pthread_create(&pthread, + /* pthread_attr */ nullptr, + callee, + reinterpret_cast(env_)); + CHECK_EQ(pthread_create_result, 0); + int pthread_join_result = pthread_join(pthread, /* thread_return */ nullptr); + CHECK_EQ(pthread_join_result, 0); + } + vm_->SetCheckJniEnabled(old_check_jni); + + check_jni_abort_catcher.Check("is making JNI calls without being attached"); +} + } // namespace art -- GitLab From 103bd381adeb62822e5a52f44f1d26bb56843271 Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Wed, 18 Apr 2018 18:27:07 -0700 Subject: [PATCH 266/749] Remove dexoptanalyzer fast file check We mostly use FDs nowadays and this may cause selinux denials for symlinks. Bug: 77853712 Test: test-art-host-gtest-dexoptanalyzer_test Change-Id: I0e33ac3f85ae8f9194219d3da764a77ad7bf1d6f --- dexoptanalyzer/dexoptanalyzer.cc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc index febccf1950..871cd081e7 100644 --- a/dexoptanalyzer/dexoptanalyzer.cc +++ b/dexoptanalyzer/dexoptanalyzer.cc @@ -247,11 +247,6 @@ class DexoptAnalyzer FINAL { } int GetDexOptNeeded() { - // If the file does not exist there's nothing to do. - // This is a fast path to avoid creating the runtime (b/34385298). - if (!OS::FileExists(dex_file_.c_str())) { - return kNoDexOptNeeded; - } if (!CreateRuntime()) { return kErrorCannotCreateRuntime; } -- GitLab From 080820c8e7a25bc3fb28d7c5a1524b0e5537c7a9 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Thu, 19 Apr 2018 04:02:36 +0000 Subject: [PATCH 267/749] Revert "Deopt does not throw" This reverts commit 2905de1c0e5b6a0c995be474b3f0efdfdc6a41c4. Reason for revert: test breaks on git_master-art-host; art-test-javac (linux) Change-Id: Ic39d442776858c74d2163683c78bd3a8072ad45e --- compiler/optimizing/nodes.cc | 9 ++++ compiler/optimizing/nodes.h | 7 ++- test/683-deopt-regression/expected.txt | 3 -- test/683-deopt-regression/info.txt | 1 - test/683-deopt-regression/smali/Deopt.smali | 60 --------------------- test/683-deopt-regression/src/Main.java | 45 ---------------- 6 files changed, 12 insertions(+), 113 deletions(-) delete mode 100644 test/683-deopt-regression/expected.txt delete mode 100644 test/683-deopt-regression/info.txt delete mode 100644 test/683-deopt-regression/smali/Deopt.smali delete mode 100644 test/683-deopt-regression/src/Main.java diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 79bb70b9aa..f784f8f7f3 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1916,6 +1916,15 @@ const HTryBoundary* HBasicBlock::ComputeTryEntryOfSuccessors() const { } } +bool HBasicBlock::HasThrowingInstructions() const { + for (HInstructionIterator it(GetInstructions()); !it.Done(); it.Advance()) { + if (it.Current()->CanThrow()) { + return true; + } + } + return false; +} + static bool HasOnlyOneInstruction(const HBasicBlock& block) { return block.GetPhis().IsEmpty() && !block.GetInstructions().IsEmpty() diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index b315c81693..79d733060b 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1272,6 +1272,8 @@ class HBasicBlock : public ArenaObject { // the appropriate try entry will be returned. const HTryBoundary* ComputeTryEntryOfSuccessors() const; + bool HasThrowingInstructions() const; + // Returns whether this block dominates the blocked passed as parameter. bool Dominates(HBasicBlock* block) const; @@ -2130,7 +2132,6 @@ class HInstruction : public ArenaObject { !CanThrow() && !IsSuspendCheck() && !IsControlFlow() && - !IsDeoptimize() && !IsNativeDebugInfo() && !IsParameterValue() && // If we added an explicit barrier then we should keep it. @@ -3237,9 +3238,7 @@ class HDeoptimize FINAL : public HVariableInputSizeInstruction { bool NeedsEnvironment() const OVERRIDE { return true; } - // Even though deoptimize is often used for "exceptional cases" to go back to - // the interpreter, it never throws an exception. - bool CanThrow() const OVERRIDE { return false; } + bool CanThrow() const OVERRIDE { return true; } DeoptimizationKind GetDeoptimizationKind() const { return GetPackedField(); } diff --git a/test/683-deopt-regression/expected.txt b/test/683-deopt-regression/expected.txt deleted file mode 100644 index 54ce0514ff..0000000000 --- a/test/683-deopt-regression/expected.txt +++ /dev/null @@ -1,3 +0,0 @@ -120 -0 -passed diff --git a/test/683-deopt-regression/info.txt b/test/683-deopt-regression/info.txt deleted file mode 100644 index 0c2cb81af7..0000000000 --- a/test/683-deopt-regression/info.txt +++ /dev/null @@ -1 +0,0 @@ -Regression test on deopt from BCE diff --git a/test/683-deopt-regression/smali/Deopt.smali b/test/683-deopt-regression/smali/Deopt.smali deleted file mode 100644 index 3bd9f6cf75..0000000000 --- a/test/683-deopt-regression/smali/Deopt.smali +++ /dev/null @@ -1,60 +0,0 @@ -# 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. - -.class public LDeopt; - -.super Ljava/lang/Object; - -.method public constructor ()V -.registers 1 - invoke-direct {v0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public static testCase([I)I - .registers 8 - - const v0, 0x0 # counter - const v1, 0xF # loop max - const v2, 0x0 # result - - :try_start - # Something throwing to start the try block. v6 contains a reference. - move-object v6, p0 - aget v3, p0, v0 - - # Invalidate v6 before entering the loop. - const-wide v5, 0x0 - - :loop_start - # Set v6 to a different reference (creates a catch phi). - const v6, 0x0 - - aget v3, p0, v0 - add-int/2addr v2, v3 - add-int/lit8 v0, v0, 0x1 - if-lt v0, v1, :loop_start - - :try_end - .catchall {:try_start .. :try_end} :catch - - :exit - return v2 - - :catch - invoke-virtual {v6}, Ljava/lang/Object;->hashCode()I # use v6 as a reference - goto :exit - -.end method - diff --git a/test/683-deopt-regression/src/Main.java b/test/683-deopt-regression/src/Main.java deleted file mode 100644 index fdf749612f..0000000000 --- a/test/683-deopt-regression/src/Main.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.Method; - -public class Main { - public static void main(String[] args) throws Exception { - if (System.getProperty("java.vm.name").equals("Dalvik")) { - Class c = Class.forName("Deopt"); - Method m = c.getMethod("testCase", int[].class); - int[] p = null; - try { - m.invoke(null, p); - System.out.println("should not reach"); - } catch (Exception e) { - // Tried to invoke hashCode on incoming null. - } - int[] q = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; - System.out.println((Integer) m.invoke(null, q)); - int[] r = { }; - System.out.println((Integer) m.invoke(null, r)); - int[] s = { 1 }; - try { - System.out.println((Integer) m.invoke(null, s)); - System.out.println("should not reach"); - } catch (Exception e) { - // Tried to invoke hashCode on generated null. - } - } - System.out.println("passed"); - } -} -- GitLab From 42d5805b30b095eb1abd39d99ed71d9fa2b7c03b Mon Sep 17 00:00:00 2001 From: Pirama Arumuga Nainar Date: Wed, 18 Apr 2018 22:59:36 -0700 Subject: [PATCH 268/749] Separate out PGO profile files per arch Bug: http://b/78259283 Specify separate PGO profiles for AArch64/ARM and x86_64/x86. This allows per-arch profile collection that exercises ART's code generation for the corresponding 32-bit and 64-bit ABIs. Test: Build Sailfish and x86_64 targets and verify that corresponding profile files are read. Change-Id: Iad3a657411676952de5ed446db4dd4e754ea9e11 --- compiler/Android.bp | 10 ++++------ dex2oat/Android.bp | 35 ++++++++++++++++++++++++++++++++--- dexlayout/Android.bp | 10 ++++------ 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/compiler/Android.bp b/compiler/Android.bp index cde64b058c..ec9fef7492 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -210,7 +210,10 @@ gensrcs { art_cc_library { name: "libart-compiler", - defaults: ["libart-compiler-defaults"], + defaults: [ + "libart-compiler-defaults", + "dex2oat-pgo-defaults", + ], codegen: { arm: { // VIXL assembly support for ARM targets. @@ -244,11 +247,6 @@ art_cc_library { "libdexfile", ], - pgo: { - instrumentation: true, - profile_file: "art/dex2oat.profdata", - benchmarks: ["dex2oat"], - }, target: { android: { lto: { diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index 49b65fd35e..5d3bc4fa9f 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -149,10 +149,41 @@ cc_defaults { ], } +cc_defaults { + name: "dex2oat-pgo-defaults", + pgo: { + instrumentation: true, + benchmarks: ["dex2oat"], + }, + target: { + android_arm64: { + pgo: { + profile_file: "art/dex2oat_arm_arm64.profdata", + }, + }, + android_arm: { + pgo: { + profile_file: "art/dex2oat_arm_arm64.profdata", + }, + }, + android_x86_64: { + pgo: { + profile_file: "art/dex2oat_x86_x86_64.profdata", + }, + }, + android_x86: { + pgo: { + profile_file: "art/dex2oat_x86_x86_64.profdata", + }, + }, + }, +} + art_cc_binary { name: "dex2oat", defaults: [ "dex2oat-defaults", + "dex2oat-pgo-defaults", ], shared_libs: [ "libart-compiler", @@ -168,9 +199,7 @@ art_cc_binary { ], pgo: { - instrumentation: true, - profile_file: "art/dex2oat.profdata", - benchmarks: ["dex2oat"], + // Additional cflags just for dex2oat during PGO instrumentation cflags: [ // Ignore frame-size increase resulting from instrumentation. "-Wno-frame-larger-than=", diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp index facda11c60..33ba58f5f7 100644 --- a/dexlayout/Android.bp +++ b/dexlayout/Android.bp @@ -34,17 +34,15 @@ art_cc_defaults { art_cc_library { name: "libart-dexlayout", - defaults: ["libart-dexlayout-defaults"], + defaults: [ + "libart-dexlayout-defaults", + "dex2oat-pgo-defaults", + ], shared_libs: [ "libart", "libdexfile", ], - pgo: { - instrumentation: true, - profile_file: "art/dex2oat.profdata", - benchmarks: ["dex2oat"], - }, target: { android: { lto: { -- GitLab From 4f6e523093e9ca3f1962068bcb38df406afed91f Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 18 Apr 2018 12:54:04 +0100 Subject: [PATCH 269/749] [veridex] Reflective usage detection improvements. - Convert a string name to the internal name - Recognize ClassLoader.loadClass. - Do a dummy merge in VeriFlow by just overwriting registers. bug: 77513322 Test: m (cherry picked from commit 295cba006e5212c06fde5ec956c81c507b2974e6) Change-Id: I888ff742197f4aec9aee68a03daca84f165e4114 --- tools/veridex/flow_analysis.cc | 10 +++++++++- tools/veridex/flow_analysis.h | 16 +++++++++++++--- tools/veridex/hidden_api.h | 6 ++++++ tools/veridex/hidden_api_finder.cc | 4 +--- tools/veridex/veridex.cc | 5 +++++ tools/veridex/veridex.h | 2 ++ 6 files changed, 36 insertions(+), 7 deletions(-) diff --git a/tools/veridex/flow_analysis.cc b/tools/veridex/flow_analysis.cc index abd0b9b28c..a4553f9613 100644 --- a/tools/veridex/flow_analysis.cc +++ b/tools/veridex/flow_analysis.cc @@ -41,7 +41,11 @@ bool VeriFlowAnalysis::IsBranchTarget(uint32_t dex_pc) { bool VeriFlowAnalysis::MergeRegisterValues(uint32_t dex_pc) { // TODO: Do the merging. Right now, just return that we should continue // the iteration if the instruction has not been visited. - return !instruction_infos_[dex_pc].has_been_visited; + if (!instruction_infos_[dex_pc].has_been_visited) { + dex_registers_[dex_pc]->assign(current_registers_.begin(), current_registers_.end()); + return true; + } + return false; } void VeriFlowAnalysis::SetVisited(uint32_t dex_pc) { @@ -260,6 +264,10 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { RegisterValue obj = GetRegister(args[0]); last_result_ = RegisterValue( obj.GetSource(), obj.GetDexFileReference(), VeriClass::class_); + } else if (method == VeriClass::loadClass_) { + RegisterValue value = GetRegister(args[1]); + last_result_ = RegisterValue( + value.GetSource(), value.GetDexFileReference(), VeriClass::class_); } else { last_result_ = GetReturnType(instruction.VRegB_35c()); } diff --git a/tools/veridex/flow_analysis.h b/tools/veridex/flow_analysis.h index c065fb8c24..80ae5fc9df 100644 --- a/tools/veridex/flow_analysis.h +++ b/tools/veridex/flow_analysis.h @@ -20,6 +20,7 @@ #include "dex/code_item_accessors.h" #include "dex/dex_file_reference.h" #include "dex/method_reference.h" +#include "hidden_api.h" #include "veridex.h" namespace art { @@ -52,10 +53,19 @@ class RegisterValue { DexFileReference GetDexFileReference() const { return reference_; } const VeriClass* GetType() const { return type_; } - const char* ToString() const { + std::string ToString() const { switch (source_) { - case RegisterSource::kString: - return reference_.dex_file->StringDataByIdx(dex::StringIndex(reference_.index)); + case RegisterSource::kString: { + const char* str = reference_.dex_file->StringDataByIdx(dex::StringIndex(reference_.index)); + if (type_ == VeriClass::class_) { + // Class names at the Java level are of the form x.y.z, but the list encodes + // them of the form Lx/y/z;. Inner classes have '$' for both Java level class + // names in strings, and hidden API lists. + return HiddenApi::ToInternalName(str); + } else { + return str; + } + } case RegisterSource::kClass: return reference_.dex_file->StringByTypeIdx(dex::TypeIndex(reference_.index)); default: diff --git a/tools/veridex/hidden_api.h b/tools/veridex/hidden_api.h index 4c67768a00..b1c8559374 100644 --- a/tools/veridex/hidden_api.h +++ b/tools/veridex/hidden_api.h @@ -63,6 +63,12 @@ class HiddenApi { return HiddenApi::GetApiMethodName(*ref.dex_file, ref.index); } + static std::string ToInternalName(const std::string& str) { + std::string val = str; + std::replace(val.begin(), val.end(), '.', '/'); + return "L" + val + ";"; + } + private: static bool IsInList(const std::string& name, const std::set& list) { return list.find(name) != list.end(); diff --git a/tools/veridex/hidden_api_finder.cc b/tools/veridex/hidden_api_finder.cc index b9be618ed7..b1ae7dd804 100644 --- a/tools/veridex/hidden_api_finder.cc +++ b/tools/veridex/hidden_api_finder.cc @@ -95,9 +95,7 @@ void HiddenApiFinder::CollectAccesses(VeridexResolver* resolver) { // Class names at the Java level are of the form x.y.z, but the list encodes // them of the form Lx/y/z;. Inner classes have '$' for both Java level class // names in strings, and hidden API lists. - std::string str = name; - std::replace(str.begin(), str.end(), '.', '/'); - str = "L" + str + ";"; + std::string str = HiddenApi::ToInternalName(name); // Note: we can query the lists directly, as HiddenApi added classes that own // private methods and fields in them. // We don't add class names to the `strings_` set as we know method/field names diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc index 6e72faaf57..dc7ea94032 100644 --- a/tools/veridex/veridex.cc +++ b/tools/veridex/veridex.cc @@ -52,6 +52,7 @@ VeriClass* VeriClass::void_ = &v_; // Will be set after boot classpath has been resolved. VeriClass* VeriClass::object_ = nullptr; VeriClass* VeriClass::class_ = nullptr; +VeriClass* VeriClass::class_loader_ = nullptr; VeriClass* VeriClass::string_ = nullptr; VeriClass* VeriClass::throwable_ = nullptr; VeriMethod VeriClass::forName_ = nullptr; @@ -60,6 +61,7 @@ VeriMethod VeriClass::getDeclaredField_ = nullptr; VeriMethod VeriClass::getMethod_ = nullptr; VeriMethod VeriClass::getDeclaredMethod_ = nullptr; VeriMethod VeriClass::getClass_ = nullptr; +VeriMethod VeriClass::loadClass_ = nullptr; struct VeridexOptions { const char* dex_file = nullptr; @@ -176,6 +178,7 @@ class Veridex { // methods. VeriClass::object_ = type_map["Ljava/lang/Object;"]; VeriClass::class_ = type_map["Ljava/lang/Class;"]; + VeriClass::class_loader_ = type_map["Ljava/lang/ClassLoader;"]; VeriClass::string_ = type_map["Ljava/lang/String;"]; VeriClass::throwable_ = type_map["Ljava/lang/Throwable;"]; VeriClass::forName_ = boot_resolvers[0]->LookupDeclaredMethodIn( @@ -194,6 +197,8 @@ class Veridex { "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"); VeriClass::getClass_ = boot_resolvers[0]->LookupDeclaredMethodIn( *VeriClass::object_, "getClass", "()Ljava/lang/Class;"); + VeriClass::loadClass_ = boot_resolvers[0]->LookupDeclaredMethodIn( + *VeriClass::class_loader_, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); std::vector> app_resolvers; Resolve(app_dex_files, resolver_map, type_map, &app_resolvers); diff --git a/tools/veridex/veridex.h b/tools/veridex/veridex.h index 75e4845293..9c0a158174 100644 --- a/tools/veridex/veridex.h +++ b/tools/veridex/veridex.h @@ -65,6 +65,7 @@ class VeriClass { static VeriClass* object_; static VeriClass* class_; + static VeriClass* class_loader_; static VeriClass* string_; static VeriClass* throwable_; static VeriClass* boolean_; @@ -83,6 +84,7 @@ class VeriClass { static VeriMethod getMethod_; static VeriMethod getDeclaredMethod_; static VeriMethod getClass_; + static VeriMethod loadClass_; private: Primitive::Type kind_; -- GitLab From 240384b7d96c2d579bf58ed7a3e9c9ba1911dae3 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Tue, 17 Apr 2018 14:56:29 -0700 Subject: [PATCH 270/749] Revert^2: Deopt does not throw Rationale: "CanThrow" of deopt was possibly misused to prevents some optimizations. However, the instruction technically cannot throw an exception, and indeed crashed the graph verifier for some corner cases. This Cl sets that right. Bug: 29868356 Test: test-art-host,target (revert^2 of commit 2905de1c0e5b6a0c995be474b3f0efdfdc6a41c4) Change-Id: I4d4e6c00eff52140aa1845332998224ececc92ef --- compiler/optimizing/nodes.cc | 9 ---- compiler/optimizing/nodes.h | 7 +-- test/683-deopt-regression/expected.txt | 1 + test/683-deopt-regression/info.txt | 1 + test/683-deopt-regression/smali/Deopt.smali | 60 +++++++++++++++++++++ test/683-deopt-regression/src/Main.java | 54 +++++++++++++++++++ 6 files changed, 120 insertions(+), 12 deletions(-) create mode 100644 test/683-deopt-regression/expected.txt create mode 100644 test/683-deopt-regression/info.txt create mode 100644 test/683-deopt-regression/smali/Deopt.smali create mode 100644 test/683-deopt-regression/src/Main.java diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index f784f8f7f3..79bb70b9aa 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1916,15 +1916,6 @@ const HTryBoundary* HBasicBlock::ComputeTryEntryOfSuccessors() const { } } -bool HBasicBlock::HasThrowingInstructions() const { - for (HInstructionIterator it(GetInstructions()); !it.Done(); it.Advance()) { - if (it.Current()->CanThrow()) { - return true; - } - } - return false; -} - static bool HasOnlyOneInstruction(const HBasicBlock& block) { return block.GetPhis().IsEmpty() && !block.GetInstructions().IsEmpty() diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 79d733060b..b315c81693 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1272,8 +1272,6 @@ class HBasicBlock : public ArenaObject { // the appropriate try entry will be returned. const HTryBoundary* ComputeTryEntryOfSuccessors() const; - bool HasThrowingInstructions() const; - // Returns whether this block dominates the blocked passed as parameter. bool Dominates(HBasicBlock* block) const; @@ -2132,6 +2130,7 @@ class HInstruction : public ArenaObject { !CanThrow() && !IsSuspendCheck() && !IsControlFlow() && + !IsDeoptimize() && !IsNativeDebugInfo() && !IsParameterValue() && // If we added an explicit barrier then we should keep it. @@ -3238,7 +3237,9 @@ class HDeoptimize FINAL : public HVariableInputSizeInstruction { bool NeedsEnvironment() const OVERRIDE { return true; } - bool CanThrow() const OVERRIDE { return true; } + // Even though deoptimize is often used for "exceptional cases" to go back to + // the interpreter, it never throws an exception. + bool CanThrow() const OVERRIDE { return false; } DeoptimizationKind GetDeoptimizationKind() const { return GetPackedField(); } diff --git a/test/683-deopt-regression/expected.txt b/test/683-deopt-regression/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/683-deopt-regression/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/683-deopt-regression/info.txt b/test/683-deopt-regression/info.txt new file mode 100644 index 0000000000..0c2cb81af7 --- /dev/null +++ b/test/683-deopt-regression/info.txt @@ -0,0 +1 @@ +Regression test on deopt from BCE diff --git a/test/683-deopt-regression/smali/Deopt.smali b/test/683-deopt-regression/smali/Deopt.smali new file mode 100644 index 0000000000..3bd9f6cf75 --- /dev/null +++ b/test/683-deopt-regression/smali/Deopt.smali @@ -0,0 +1,60 @@ +# 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. + +.class public LDeopt; + +.super Ljava/lang/Object; + +.method public constructor ()V +.registers 1 + invoke-direct {v0}, Ljava/lang/Object;->()V + return-void +.end method + +.method public static testCase([I)I + .registers 8 + + const v0, 0x0 # counter + const v1, 0xF # loop max + const v2, 0x0 # result + + :try_start + # Something throwing to start the try block. v6 contains a reference. + move-object v6, p0 + aget v3, p0, v0 + + # Invalidate v6 before entering the loop. + const-wide v5, 0x0 + + :loop_start + # Set v6 to a different reference (creates a catch phi). + const v6, 0x0 + + aget v3, p0, v0 + add-int/2addr v2, v3 + add-int/lit8 v0, v0, 0x1 + if-lt v0, v1, :loop_start + + :try_end + .catchall {:try_start .. :try_end} :catch + + :exit + return v2 + + :catch + invoke-virtual {v6}, Ljava/lang/Object;->hashCode()I # use v6 as a reference + goto :exit + +.end method + diff --git a/test/683-deopt-regression/src/Main.java b/test/683-deopt-regression/src/Main.java new file mode 100644 index 0000000000..326fe47c89 --- /dev/null +++ b/test/683-deopt-regression/src/Main.java @@ -0,0 +1,54 @@ +/* + * 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.Method; + +public class Main { + public static void main(String[] args) throws Exception { + if (System.getProperty("java.vm.name").equals("Dalvik")) { + Class c = Class.forName("Deopt"); + Method m = c.getMethod("testCase", int[].class); + int[] p = null; + try { + m.invoke(null, p); + System.out.println("should not reach"); + } catch (Exception e) { + // Tried to invoke hashCode on incoming null. + } + int result; + int[] q = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + result = ((Integer) m.invoke(null, q)); + expectEquals(120, result); + int[] r = { }; + result = ((Integer) m.invoke(null, r)); + expectEquals(0, result); + int[] s = { 1 }; + try { + m.invoke(null, s); + System.out.println("should not reach"); + } catch (Exception e) { + // Tried to invoke hashCode on generated null. + } + } + System.out.println("passed"); + } + + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} -- GitLab From f889c70e79643373320f5742cc719d6c467531b9 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Fri, 23 Feb 2018 15:25:45 -0800 Subject: [PATCH 271/749] Always allow agent attach on userdebug builds with kArtTiVersion We added support for the jvmti-alike ArtTi for use by debuggers on userdebug/eng builds of android. Extend this support to allow any agent to be loaded on any process of a userdebug device. These agents will need to make use of kArtTiVersion (0x70010200) envs. Test: build Test: ./test.py --host -j50 Test: ensure AS profiler continues to work with userdebug devices Bug: 78195998 Change-Id: I984d1ea937eb49afb376a48bea3d67085192020e --- adbconnection/adbconnection.cc | 3 +- runtime/runtime.cc | 15 +++++---- runtime/runtime.h | 5 +-- test/909-attach-agent/attach.cc | 15 ++++++++- test/909-attach-agent/disallow_debugging.cc | 27 ++++++++++++++++ test/909-attach-agent/expected.txt | 20 ++++++++++-- .../interpreter-expected.patch | 4 +++ test/909-attach-agent/run | 20 ++++++++++-- test/909-attach-agent/src-art/Main.java | 31 +++++++++++++------ test/Android.bp | 1 + 10 files changed, 113 insertions(+), 28 deletions(-) create mode 100644 test/909-attach-agent/disallow_debugging.cc create mode 100644 test/909-attach-agent/interpreter-expected.patch diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc index ee89717043..3c5f735b34 100644 --- a/adbconnection/adbconnection.cc +++ b/adbconnection/adbconnection.cc @@ -837,8 +837,7 @@ void AdbConnectionState::AttachJdwpAgent(art::Thread* self) { self->AssertNoPendingException(); runtime->AttachAgent(/* JNIEnv */ nullptr, MakeAgentArg(), - /* classloader */ nullptr, - /*allow_non_debuggable_tooling*/ true); + /* classloader */ nullptr); if (self->IsExceptionPending()) { LOG(ERROR) << "Failed to load agent " << agent_name_; art::ScopedObjectAccess soa(self); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index c394fefb38..8f5295cec6 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1619,7 +1619,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { } static bool EnsureJvmtiPlugin(Runtime* runtime, - bool allow_non_debuggable_tooling, std::vector* plugins, std::string* error_msg) { constexpr const char* plugin_name = kIsDebugBuild ? "libopenjdkjvmtid.so" : "libopenjdkjvmti.so"; @@ -1631,10 +1630,13 @@ static bool EnsureJvmtiPlugin(Runtime* runtime, } } + // TODO Rename Dbg::IsJdwpAllowed is IsDebuggingAllowed. + DCHECK(Dbg::IsJdwpAllowed() || !runtime->IsJavaDebuggable()) + << "Being debuggable requires that jdwp (i.e. debugging) is allowed."; // Is the process debuggable? Otherwise, do not attempt to load the plugin unless we are // specifically allowed. - if (!allow_non_debuggable_tooling && !runtime->IsJavaDebuggable()) { - *error_msg = "Process is not debuggable."; + if (!Dbg::IsJdwpAllowed()) { + *error_msg = "Process is not allowed to load openjdkjvmti plugin. Process must be debuggable"; return false; } @@ -1654,12 +1656,9 @@ static bool EnsureJvmtiPlugin(Runtime* runtime, // revisit this and make sure we're doing this on the right thread // (and we synchronize access to any shared data structures like "agents_") // -void Runtime::AttachAgent(JNIEnv* env, - const std::string& agent_arg, - jobject class_loader, - bool allow_non_debuggable_tooling) { +void Runtime::AttachAgent(JNIEnv* env, const std::string& agent_arg, jobject class_loader) { std::string error_msg; - if (!EnsureJvmtiPlugin(this, allow_non_debuggable_tooling, &plugins_, &error_msg)) { + if (!EnsureJvmtiPlugin(this, &plugins_, &error_msg)) { LOG(WARNING) << "Could not load plugin: " << error_msg; ScopedObjectAccess soa(Thread::Current()); ThrowIOException("%s", error_msg.c_str()); diff --git a/runtime/runtime.h b/runtime/runtime.h index 3d4b596349..1b7663cbdf 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -710,10 +710,7 @@ class Runtime { void AddSystemWeakHolder(gc::AbstractSystemWeakHolder* holder); void RemoveSystemWeakHolder(gc::AbstractSystemWeakHolder* holder); - void AttachAgent(JNIEnv* env, - const std::string& agent_arg, - jobject class_loader, - bool allow_non_debuggable_tooling = false); + void AttachAgent(JNIEnv* env, const std::string& agent_arg, jobject class_loader); const std::list>& GetAgents() const { return agents_; diff --git a/test/909-attach-agent/attach.cc b/test/909-attach-agent/attach.cc index 3a6788a8e3..50ab26a374 100644 --- a/test/909-attach-agent/attach.cc +++ b/test/909-attach-agent/attach.cc @@ -32,6 +32,8 @@ static void Println(const char* c) { fflush(stdout); } +static constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000; + jint OnAttach(JavaVM* vm, char* options ATTRIBUTE_UNUSED, void* reserved ATTRIBUTE_UNUSED) { @@ -47,7 +49,18 @@ jint OnAttach(JavaVM* vm, } \ } while (false) - CHECK_CALL_SUCCESS(vm->GetEnv(reinterpret_cast(&env), JVMTI_VERSION_1_0)); + if (vm->GetEnv(reinterpret_cast(&env), kArtTiVersion) == JNI_OK) { + Println("Created env for kArtTiVersion"); + CHECK_CALL_SUCCESS(env->DisposeEnvironment()); + env = nullptr; + } else { + Println("Failed to create env for kArtTiVersion"); + return -1; + } + if (vm->GetEnv(reinterpret_cast(&env), JVMTI_VERSION_1_0) != JNI_OK) { + Println("Unable to create env for JVMTI_VERSION_1_0"); + return 0; + } CHECK_CALL_SUCCESS(vm->GetEnv(reinterpret_cast(&env2), JVMTI_VERSION_1_0)); if (env == env2) { Println("GetEnv returned same environment twice!"); diff --git a/test/909-attach-agent/disallow_debugging.cc b/test/909-attach-agent/disallow_debugging.cc new file mode 100644 index 0000000000..4c70f62e55 --- /dev/null +++ b/test/909-attach-agent/disallow_debugging.cc @@ -0,0 +1,27 @@ +/* + * 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. + */ + +#include "debugger.h" + +namespace art { +namespace Test909AttachAgent { + +extern "C" JNIEXPORT void JNICALL Java_Main_setDebuggingAllowed(JNIEnv*, jclass, jboolean val) { + Dbg::SetJdwpAllowed(val); +} + +} // namespace Test909AttachAgent +} // namespace art diff --git a/test/909-attach-agent/expected.txt b/test/909-attach-agent/expected.txt index 4d687f531e..eec767d703 100644 --- a/test/909-attach-agent/expected.txt +++ b/test/909-attach-agent/expected.txt @@ -1,12 +1,28 @@ +JNI_OnLoad called Hello, world! Attached Agent for test 909-attach-agent +Created env for kArtTiVersion Attached Agent for test 909-attach-agent +Created env for kArtTiVersion Goodbye! +JNI_OnLoad called Hello, world! Attached Agent for test 909-attach-agent +Created env for kArtTiVersion Attached Agent for test 909-attach-agent +Created env for kArtTiVersion Goodbye! +JNI_OnLoad called Hello, world! -Process is not debuggable. -Process is not debuggable. +Attached Agent for test 909-attach-agent +Created env for kArtTiVersion +version 0x30010000 is not valid!Unable to create env for JVMTI_VERSION_1_0 +Attached Agent for test 909-attach-agent +Created env for kArtTiVersion +version 0x30010000 is not valid!Unable to create env for JVMTI_VERSION_1_0 +Goodbye! +JNI_OnLoad called +Hello, world! +Can't attach agent, process is not debuggable. +Can't attach agent, process is not debuggable. Goodbye! diff --git a/test/909-attach-agent/interpreter-expected.patch b/test/909-attach-agent/interpreter-expected.patch new file mode 100644 index 0000000000..5035c6a4cc --- /dev/null +++ b/test/909-attach-agent/interpreter-expected.patch @@ -0,0 +1,4 @@ +19d18 +< version 0x30010000 is not valid!Unable to create env for JVMTI_VERSION_1_0 +22d20 +< version 0x30010000 is not valid!Unable to create env for JVMTI_VERSION_1_0 diff --git a/test/909-attach-agent/run b/test/909-attach-agent/run index a556bbaffe..fd45abde6b 100755 --- a/test/909-attach-agent/run +++ b/test/909-attach-agent/run @@ -21,6 +21,14 @@ if [[ "$@" == *"-O"* ]]; then plugin=libopenjdkjvmti.so fi +if [[ "$@" == *"--interpreter"* ]]; then + # On interpreter we are fully capable of providing the full jvmti api so we + # have a slightly different expected output. + # TODO We should really be changing this in the 'check' script. + patch -s expected.txt CreateJit(); - if (runtime->GetJit() == nullptr) { - LOG(WARNING) << "Could not start jit for debugging. This process might be quite slow as it " - << "is running entirely in the interpreter. Try running 'setenforce 0' and " - << "starting the debugging session over."; - } - } self->AssertNoPendingException(); runtime->AttachAgent(/* JNIEnv */ nullptr, MakeAgentArg(), diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc index a6f1207f34..d4e5df1d67 100644 --- a/openjdkjvmti/deopt_manager.cc +++ b/openjdkjvmti/deopt_manager.cc @@ -40,6 +40,7 @@ #include "dex/dex_file_annotations.h" #include "dex/modifiers.h" #include "events-inl.h" +#include "jit/jit.h" #include "jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object_array-inl.h" @@ -127,6 +128,22 @@ void DeoptManager::FinishSetup() { << "loaded too late to change runtime state to DEBUGGABLE. Only kArtTiVersion " << "(0x" << std::hex << kArtTiVersion << ") environments are available. Some " << "functionality might not work properly."; + if (runtime->GetJit() == nullptr && + runtime->GetJITOptions()->UseJitCompilation() && + !runtime->GetInstrumentation()->IsForcedInterpretOnly()) { + // If we don't have a jit we should try to start the jit for performance reasons. We only + // need to do this for late attach on non-debuggable processes because for debuggable + // processes we already rely on jit and we cannot force this jit to start if we are still in + // OnLoad since the runtime hasn't started up sufficiently. This is only expected to happen + // on userdebug/eng builds. + LOG(INFO) << "Attempting to start jit for openjdkjvmti plugin."; + runtime->CreateJit(); + if (runtime->GetJit() == nullptr) { + LOG(WARNING) << "Could not start jit for openjdkjvmti plugin. This process might be " + << "quite slow as it is running entirely in the interpreter. Try running " + << "'setenforce 0' and restarting this process."; + } + } } runtime->DeoptimizeBootImage(); } -- GitLab From b5529ba975f295d0fb3319dd814f5e5475bccb03 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Wed, 18 Apr 2018 18:25:52 +0100 Subject: [PATCH 273/749] Disable failing libcore.libcore.icu.RelativeDateTimeFormatterTest Bug: 78228743 Change-Id: I93e23df7c081e2513b869585fc99dfe89a9b5eee --- tools/libcore_gcstress_debug_failures.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt index 0029b0a01a..06f6339d76 100644 --- a/tools/libcore_gcstress_debug_failures.txt +++ b/tools/libcore_gcstress_debug_failures.txt @@ -17,5 +17,22 @@ "libcore.java.util.TimeZoneTest#testSetDefaultDeadlock", "libcore.javax.crypto.CipherBasicsTest#testBasicEncryption", "org.apache.harmony.tests.java.util.TimerTest#testThrowingTaskKillsTimerThread"] +}, +{ + description: "Sometimes times out with gcstress and debug.", + result: EXEC_FAILED, + bug: 78228743, + names: [ + "libcore.libcore.icu.RelativeDateTimeFormatterTest#test_combineDateAndTime_apostrophe", + "libcore.libcore.icu.RelativeDateTimeFormatterTest#test_getRelativeDateTimeString", + "libcore.libcore.icu.RelativeDateTimeFormatterTest#test_getRelativeDateTimeStringCTS", + "libcore.libcore.icu.RelativeDateTimeFormatterTest#test_getRelativeDateTimeStringDST", + "libcore.libcore.icu.RelativeDateTimeFormatterTest#test_getRelativeDateTimeStringItalian", + "libcore.libcore.icu.RelativeDateTimeFormatterTest#test_getRelativeTimeSpanString", + "libcore.libcore.icu.RelativeDateTimeFormatterTest#test_getRelativeTimeSpanStringAbbrev", + "libcore.libcore.icu.RelativeDateTimeFormatterTest#test_getRelativeTimeSpanStringCTS", + "libcore.libcore.icu.RelativeDateTimeFormatterTest#test_getRelativeTimeSpanStringFrench", + "libcore.libcore.icu.RelativeDateTimeFormatterTest#test_getRelativeTimeSpanStringGerman" + ] } ] -- GitLab From 811da57c941beae1b3da5416802310acf669b0d8 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 16 Apr 2018 15:14:08 +0100 Subject: [PATCH 274/749] Further improve debug logging for 77342775 Previous logging had long log lines that were truncated. For multidex, replace the base location for other than the first dex file with "+". Split the first line into two, so that if there are too many dex files in the class loader, we still get to see at least some of them also for the other class loader. Test: Manual; change 108-check-cast to multi-dex and disable the Lorg/apache/http/ filtering, check error message. Bug: 77342775 (cherry picked from commit d54f1f92b3b6c99f1877b50fe98ed95a51fb59fd) Change-Id: Ic1c6d80ed11dc720f21b0cde1c892573a76c0c3e --- runtime/debug_print.cc | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/runtime/debug_print.cc b/runtime/debug_print.cc index 60487670ac..7e075ce352 100644 --- a/runtime/debug_print.cc +++ b/runtime/debug_print.cc @@ -97,14 +97,27 @@ std::string DescribeLoaders(ObjPtr loader, const char* clas StackHandleScope<1> hs(soa.Self()); Handle handle(hs.NewHandle(loader)); const char* path_separator = ""; - VisitClassLoaderDexFiles(soa, - handle, - [&](const DexFile* dex_file) { - oss << path_separator << dex_file->GetLocation() - << "/" << static_cast(dex_file); - path_separator = ":"; - return true; // Continue with the next DexFile. - }); + const DexFile* base_dex_file = nullptr; + VisitClassLoaderDexFiles( + soa, + handle, + [&](const DexFile* dex_file) { + oss << path_separator; + path_separator = ":"; + if (base_dex_file != nullptr && + dex_file->GetLocation().length() > base_dex_file->GetLocation().length() && + dex_file->GetLocation().compare(0u, + base_dex_file->GetLocation().length(), + base_dex_file->GetLocation()) == 0) { + // Replace the base location with "+" to shorten the output. + oss << "+" << dex_file->GetLocation().substr(base_dex_file->GetLocation().length()); + } else { + oss << dex_file->GetLocation(); + base_dex_file = dex_file; + } + oss << "/" << static_cast(dex_file); + return true; // Continue with the next DexFile. + }); oss << ")"; } } @@ -132,13 +145,13 @@ void DumpB77342775DebugData(ObjPtr target_class, ObjPtrGetDescriptor(&source_descriptor_storage); + LOG(ERROR) << "Maybe bug 77342775, looking for " << target_descriptor + << " with loader " << DescribeLoaders(target_class->GetClassLoader(), target_descriptor); if (target_class->IsInterface()) { ObjPtr iftable = src_class->GetIfTable(); CHECK(iftable != nullptr); size_t ifcount = iftable->Count(); - LOG(ERROR) << "Maybe bug 77342775, looking for " << target_descriptor - << " with loader " << DescribeLoaders(target_class->GetClassLoader(), target_descriptor) - << " in interface table for " << source_descriptor << " ifcount=" << ifcount + LOG(ERROR) << " in interface table for " << source_descriptor << " ifcount=" << ifcount << " with loader " << DescribeLoaders(src_class->GetClassLoader(), source_descriptor); for (size_t i = 0; i != ifcount; ++i) { ObjPtr iface = iftable->GetInterface(i); @@ -147,9 +160,7 @@ void DumpB77342775DebugData(ObjPtr target_class, ObjPtrGetClassLoader(), target_descriptor) - << " in superclass chain for " << source_descriptor + LOG(ERROR) << " in superclass chain for " << source_descriptor << " with loader " << DescribeLoaders(src_class->GetClassLoader(), source_descriptor); for (ObjPtr klass = src_class; klass != nullptr; -- GitLab From f3c670e465f835f8d27fc646eca545dab4c0450f Mon Sep 17 00:00:00 2001 From: Alex Light Date: Fri, 20 Apr 2018 09:39:21 -0700 Subject: [PATCH 275/749] Make test 906 retry to avoid gc races Test 906 counts the total number of uncollected (note not necessarily reachable) objects in the java heap. Due to the api design it cannot do this in any sort of synchronized or atomic way. This is usually not a problem though because the test does very little allocation so the number of objects should not generally be changing. When run on CTS, however, it is possible that objects might be created or collected from other threads. This could cause the test to incorrectly fail. We made this test retry the counting of objects up to 10 times in order to hopefully get a count without racing other threads. Test: ./test/run-test --host 906 Bug: 78344479 Change-Id: I80da119ed58193aae8d08e6f82ee4537fc979c85 --- test/906-iterate-heap/src/art/Test906.java | 73 ++++++++++++++-------- 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/test/906-iterate-heap/src/art/Test906.java b/test/906-iterate-heap/src/art/Test906.java index 65c2c8c560..be9663a6d4 100644 --- a/test/906-iterate-heap/src/art/Test906.java +++ b/test/906-iterate-heap/src/art/Test906.java @@ -24,6 +24,51 @@ public class Test906 { doTest(); } + // Number of times we will try to count the heap in various ways. If we are unlucky and end up in + // the middle of a GC we could incorrectly fail. This is expected to be incredibly rare so 10 + // retries should be more than sufficient. + private static final int ITERATE_RETRIES = 10; + private static void testHeapCount() throws Exception { + IllegalStateException lastThrow = new IllegalStateException( + "Failed to get consistent counts after " + ITERATE_RETRIES + " retries"); + for (int i = 0; i < ITERATE_RETRIES; i++) { + try { + int all = iterateThroughHeapCount(0, null, Integer.MAX_VALUE); + int tagged = iterateThroughHeapCount(HEAP_FILTER_OUT_UNTAGGED, null, Integer.MAX_VALUE); + int untagged = iterateThroughHeapCount(HEAP_FILTER_OUT_TAGGED, null, Integer.MAX_VALUE); + int taggedClass = iterateThroughHeapCount(HEAP_FILTER_OUT_CLASS_UNTAGGED, null, + Integer.MAX_VALUE); + int untaggedClass = iterateThroughHeapCount(HEAP_FILTER_OUT_CLASS_TAGGED, null, + Integer.MAX_VALUE); + + if (all != tagged + untagged) { + throw new IllegalStateException("Instances: " + all + " != " + tagged + " + " + untagged); + } + if (all != taggedClass + untaggedClass) { + throw new IllegalStateException("By class: " + all + " != " + taggedClass + " + " + + untaggedClass); + } + if (tagged != 6) { + throw new IllegalStateException(tagged + " tagged objects"); + } + if (taggedClass != 2) { + throw new IllegalStateException(tagged + " objects with tagged class"); + } + if (all == tagged) { + throw new IllegalStateException("All objects tagged"); + } + if (all == taggedClass) { + throw new IllegalStateException("All objects have tagged class"); + } + // Everything worked! + return; + } catch (IllegalStateException e) { + lastThrow.addSuppressed(e); + } + } + throw lastThrow; + } + public static void doTest() throws Exception { A a = new A(); B b = new B(); @@ -39,33 +84,7 @@ public class Test906 { setTag(s, 5); setTag(B.class, 100); - int all = iterateThroughHeapCount(0, null, Integer.MAX_VALUE); - int tagged = iterateThroughHeapCount(HEAP_FILTER_OUT_UNTAGGED, null, Integer.MAX_VALUE); - int untagged = iterateThroughHeapCount(HEAP_FILTER_OUT_TAGGED, null, Integer.MAX_VALUE); - int taggedClass = iterateThroughHeapCount(HEAP_FILTER_OUT_CLASS_UNTAGGED, null, - Integer.MAX_VALUE); - int untaggedClass = iterateThroughHeapCount(HEAP_FILTER_OUT_CLASS_TAGGED, null, - Integer.MAX_VALUE); - - if (all != tagged + untagged) { - throw new IllegalStateException("Instances: " + all + " != " + tagged + " + " + untagged); - } - if (all != taggedClass + untaggedClass) { - throw new IllegalStateException("By class: " + all + " != " + taggedClass + " + " + - untaggedClass); - } - if (tagged != 6) { - throw new IllegalStateException(tagged + " tagged objects"); - } - if (taggedClass != 2) { - throw new IllegalStateException(tagged + " objects with tagged class"); - } - if (all == tagged) { - throw new IllegalStateException("All objects tagged"); - } - if (all == taggedClass) { - throw new IllegalStateException("All objects have tagged class"); - } + testHeapCount(); long classTags[] = new long[100]; long sizes[] = new long[100]; -- GitLab From 6f5b4d21feecf8d507ad039e8cba92d491a407ba Mon Sep 17 00:00:00 2001 From: Pirama Arumuga Nainar Date: Fri, 20 Apr 2018 12:02:02 -0700 Subject: [PATCH 276/749] Use lld for building dex2oatds, oatdumpds Bug: http://b/77581732 'lld' may help avoid a segfault when linking these executables with ld.gold. Test: build Change-Id: I385643c0124963b132b0c20f00bbb9c69923e80e --- dex2oat/Android.bp | 3 +++ oatdump/Android.bp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index 49b65fd35e..9c3d80a0ae 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -247,6 +247,9 @@ art_cc_binary { darwin: { enabled: false, }, + linux_glibc_x86_64: { + use_clang_lld: true, + }, }, ldflags: [ // We need this because GC stress mode makes use of diff --git a/oatdump/Android.bp b/oatdump/Android.bp index 71e276db10..8c21538f23 100644 --- a/oatdump/Android.bp +++ b/oatdump/Android.bp @@ -96,6 +96,9 @@ art_cc_binary { darwin: { enabled: false, }, + linux_glibc_x86_64: { + use_clang_lld: true, + }, }, ldflags: [ // We need this because GC stress mode makes use of -- GitLab From 4a68b66f468e1df989cc6f1526dadd1618e4f793 Mon Sep 17 00:00:00 2001 From: Pirama Arumuga Nainar Date: Fri, 20 Apr 2018 12:16:36 -0700 Subject: [PATCH 277/749] Remove unnecessary definition of 'art::Split' Bug: http://b/78352390 Fix duplicate symbol warning found with lld Test: build with https://android-review.googlesource.com/c/platform/art/+/669149 and verify that the duplicate symbol warning is no longer generated. Change-Id: Ie77d1bfaa84ec034ef9bc39dd63422d859e0f7c5 --- libdexfile/dex/descriptors_names.cc | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/libdexfile/dex/descriptors_names.cc b/libdexfile/dex/descriptors_names.cc index 8124e7256f..e338b55a9f 100644 --- a/libdexfile/dex/descriptors_names.cc +++ b/libdexfile/dex/descriptors_names.cc @@ -403,22 +403,6 @@ bool IsValidDescriptor(const char* s) { return IsValidClassName(s); } -void Split(const std::string& s, char separator, std::vector* result) { - const char* p = s.data(); - const char* end = p + s.size(); - while (p != end) { - if (*p == separator) { - ++p; - } else { - const char* start = p; - while (++p != end && *p != separator) { - // Skip to the next occurrence of the separator. - } - result->push_back(std::string(start, p - start)); - } - } -} - std::string PrettyDescriptor(Primitive::Type type) { return PrettyDescriptor(Primitive::Descriptor(type)); } -- GitLab From fc1de878e6a327f1bc9b15059a25599de81cb4d1 Mon Sep 17 00:00:00 2001 From: Pirama Arumuga Nainar Date: Fri, 20 Apr 2018 14:11:12 -0700 Subject: [PATCH 278/749] Specify dex2oat PGO profile files for Mips Bug: http://b/78259283 This breaks mips targets. Test: lunch m_e_mips-eng; make Change-Id: Ia8db413acbd066772d8e53702bc6ae262985a992 --- dex2oat/Android.bp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index 5d3bc4fa9f..9353246716 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -176,6 +176,16 @@ cc_defaults { profile_file: "art/dex2oat_x86_x86_64.profdata", }, }, + android_mips64: { + pgo: { + profile_file: "art/dex2oat_mips_mips64.profdata", + }, + }, + android_mips: { + pgo: { + profile_file: "art/dex2oat_mips_mips64.profdata", + }, + }, }, } -- GitLab From 75ff2c96c37485ff8c74cf9942c0a4bf0710e91c Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Sat, 21 Apr 2018 01:28:11 +0000 Subject: [PATCH 279/749] Revert "Revert^2: Deopt does not throw" This reverts commit 240384b7d96c2d579bf58ed7a3e9c9ba1911dae3. Reason for revert: P1 b/78360004 points to this so reverting for now (will triage later) Change-Id: I041c1d9fc045391c73e887268c3b1b9b69fcc216 --- compiler/optimizing/nodes.cc | 9 ++++ compiler/optimizing/nodes.h | 7 ++- test/683-deopt-regression/expected.txt | 1 - test/683-deopt-regression/info.txt | 1 - test/683-deopt-regression/smali/Deopt.smali | 60 --------------------- test/683-deopt-regression/src/Main.java | 54 ------------------- 6 files changed, 12 insertions(+), 120 deletions(-) delete mode 100644 test/683-deopt-regression/expected.txt delete mode 100644 test/683-deopt-regression/info.txt delete mode 100644 test/683-deopt-regression/smali/Deopt.smali delete mode 100644 test/683-deopt-regression/src/Main.java diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 79bb70b9aa..f784f8f7f3 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1916,6 +1916,15 @@ const HTryBoundary* HBasicBlock::ComputeTryEntryOfSuccessors() const { } } +bool HBasicBlock::HasThrowingInstructions() const { + for (HInstructionIterator it(GetInstructions()); !it.Done(); it.Advance()) { + if (it.Current()->CanThrow()) { + return true; + } + } + return false; +} + static bool HasOnlyOneInstruction(const HBasicBlock& block) { return block.GetPhis().IsEmpty() && !block.GetInstructions().IsEmpty() diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index b315c81693..79d733060b 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1272,6 +1272,8 @@ class HBasicBlock : public ArenaObject { // the appropriate try entry will be returned. const HTryBoundary* ComputeTryEntryOfSuccessors() const; + bool HasThrowingInstructions() const; + // Returns whether this block dominates the blocked passed as parameter. bool Dominates(HBasicBlock* block) const; @@ -2130,7 +2132,6 @@ class HInstruction : public ArenaObject { !CanThrow() && !IsSuspendCheck() && !IsControlFlow() && - !IsDeoptimize() && !IsNativeDebugInfo() && !IsParameterValue() && // If we added an explicit barrier then we should keep it. @@ -3237,9 +3238,7 @@ class HDeoptimize FINAL : public HVariableInputSizeInstruction { bool NeedsEnvironment() const OVERRIDE { return true; } - // Even though deoptimize is often used for "exceptional cases" to go back to - // the interpreter, it never throws an exception. - bool CanThrow() const OVERRIDE { return false; } + bool CanThrow() const OVERRIDE { return true; } DeoptimizationKind GetDeoptimizationKind() const { return GetPackedField(); } diff --git a/test/683-deopt-regression/expected.txt b/test/683-deopt-regression/expected.txt deleted file mode 100644 index b0aad4deb5..0000000000 --- a/test/683-deopt-regression/expected.txt +++ /dev/null @@ -1 +0,0 @@ -passed diff --git a/test/683-deopt-regression/info.txt b/test/683-deopt-regression/info.txt deleted file mode 100644 index 0c2cb81af7..0000000000 --- a/test/683-deopt-regression/info.txt +++ /dev/null @@ -1 +0,0 @@ -Regression test on deopt from BCE diff --git a/test/683-deopt-regression/smali/Deopt.smali b/test/683-deopt-regression/smali/Deopt.smali deleted file mode 100644 index 3bd9f6cf75..0000000000 --- a/test/683-deopt-regression/smali/Deopt.smali +++ /dev/null @@ -1,60 +0,0 @@ -# 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. - -.class public LDeopt; - -.super Ljava/lang/Object; - -.method public constructor ()V -.registers 1 - invoke-direct {v0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public static testCase([I)I - .registers 8 - - const v0, 0x0 # counter - const v1, 0xF # loop max - const v2, 0x0 # result - - :try_start - # Something throwing to start the try block. v6 contains a reference. - move-object v6, p0 - aget v3, p0, v0 - - # Invalidate v6 before entering the loop. - const-wide v5, 0x0 - - :loop_start - # Set v6 to a different reference (creates a catch phi). - const v6, 0x0 - - aget v3, p0, v0 - add-int/2addr v2, v3 - add-int/lit8 v0, v0, 0x1 - if-lt v0, v1, :loop_start - - :try_end - .catchall {:try_start .. :try_end} :catch - - :exit - return v2 - - :catch - invoke-virtual {v6}, Ljava/lang/Object;->hashCode()I # use v6 as a reference - goto :exit - -.end method - diff --git a/test/683-deopt-regression/src/Main.java b/test/683-deopt-regression/src/Main.java deleted file mode 100644 index 326fe47c89..0000000000 --- a/test/683-deopt-regression/src/Main.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.Method; - -public class Main { - public static void main(String[] args) throws Exception { - if (System.getProperty("java.vm.name").equals("Dalvik")) { - Class c = Class.forName("Deopt"); - Method m = c.getMethod("testCase", int[].class); - int[] p = null; - try { - m.invoke(null, p); - System.out.println("should not reach"); - } catch (Exception e) { - // Tried to invoke hashCode on incoming null. - } - int result; - int[] q = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; - result = ((Integer) m.invoke(null, q)); - expectEquals(120, result); - int[] r = { }; - result = ((Integer) m.invoke(null, r)); - expectEquals(0, result); - int[] s = { 1 }; - try { - m.invoke(null, s); - System.out.println("should not reach"); - } catch (Exception e) { - // Tried to invoke hashCode on generated null. - } - } - System.out.println("passed"); - } - - private static void expectEquals(int expected, int result) { - if (expected != result) { - throw new Error("Expected: " + expected + ", found: " + result); - } - } -} -- GitLab From ee06c5ad9f3f5acf1f30caf9d4006bd3d6adb983 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Mon, 23 Apr 2018 13:46:24 +0100 Subject: [PATCH 280/749] ART: Additional info for run-test --dev --host Adds environment variables required to reproduce a test execution on host in dev mode. Test: art/test/run-test --host --dev 956 Change-Id: If8560a0f4ba5efe5f8c4eb63ce3cc2033e7fb5b2 --- test/etc/run-test-jar | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index e9127a8101..c5277548eb 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -963,6 +963,9 @@ else fi if [ "$DEV_MODE" = "y" ]; then + for var in ANDROID_PRINTF_LOG ANDROID_DATA ANDROID_ROOT LD_LIBRARY_PATH DYLD_LIBRARY_PATH PATH LD_USE_LOAD_BIAS; do + echo EXPORT $var=${!var} + done echo "mkdir -p ${mkdir_locations} && $profman_cmdline && $dex2oat_cmdline && $dm_cmdline && $vdex_cmdline && $strip_cmdline && $sync_cmdline && $cmdline" fi -- GitLab From 909b1277e450c1eebfe2db40f5a4a72652d32950 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 23 Apr 2018 13:43:31 +0100 Subject: [PATCH 281/749] [veridex] New veridex improvements. 1) Take the type of the receiver on a getClass call. 2) Dedupe reflection uses in precise hidden API finder. 3) Don't do a cheap check that a method is resolved, in case the app itself is defining a blacklisted API. bug: 77513322 Test: m (cherry picked from commit 57b2a550a2215261569a33838a14978c7a9ccde2) Change-Id: Ia441a637386b2232cf9d7f71f6a612d476b37041 --- tools/veridex/flow_analysis.cc | 13 +++++++++++-- tools/veridex/hidden_api_finder.cc | 14 ++++---------- tools/veridex/precise_hidden_api_finder.cc | 22 +++++++++++++++------- tools/veridex/resolver.h | 4 ++++ 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/tools/veridex/flow_analysis.cc b/tools/veridex/flow_analysis.cc index a4553f9613..736abb7f17 100644 --- a/tools/veridex/flow_analysis.cc +++ b/tools/veridex/flow_analysis.cc @@ -262,8 +262,17 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { last_result_ = GetReturnType(instruction.VRegB_35c()); } else if (method == VeriClass::getClass_) { RegisterValue obj = GetRegister(args[0]); - last_result_ = RegisterValue( - obj.GetSource(), obj.GetDexFileReference(), VeriClass::class_); + const VeriClass* cls = obj.GetType(); + if (cls != nullptr && cls->GetClassDef() != nullptr) { + const DexFile::ClassDef* def = cls->GetClassDef(); + last_result_ = RegisterValue( + RegisterSource::kClass, + DexFileReference(&resolver_->GetDexFileOf(*cls), def->class_idx_.index_), + VeriClass::class_); + } else { + last_result_ = RegisterValue( + obj.GetSource(), obj.GetDexFileReference(), VeriClass::class_); + } } else if (method == VeriClass::loadClass_) { RegisterValue value = GetRegister(args[1]); last_result_ = RegisterValue( diff --git a/tools/veridex/hidden_api_finder.cc b/tools/veridex/hidden_api_finder.cc index b1ae7dd804..8c6139f75a 100644 --- a/tools/veridex/hidden_api_finder.cc +++ b/tools/veridex/hidden_api_finder.cc @@ -31,11 +31,8 @@ namespace art { void HiddenApiFinder::CheckMethod(uint32_t method_id, VeridexResolver* resolver, MethodReference ref) { - // Cheap check that the method is resolved. If it is, we know it's not in - // a restricted list. - if (resolver->GetMethod(method_id) != nullptr) { - return; - } + // Note: we always query whether a method is in a list, as the app + // might define blacklisted APIs (which won't be used at runtime). std::string name = HiddenApi::GetApiMethodName(resolver->GetDexFile(), method_id); if (hidden_api_.IsInRestrictionList(name)) { method_locations_[name].push_back(ref); @@ -45,11 +42,8 @@ void HiddenApiFinder::CheckMethod(uint32_t method_id, void HiddenApiFinder::CheckField(uint32_t field_id, VeridexResolver* resolver, MethodReference ref) { - // Cheap check that the field is resolved. If it is, we know it's not in - // a restricted list. - if (resolver->GetField(field_id) != nullptr) { - return; - } + // Note: we always query whether a field is in a list, as the app + // might define blacklisted APIs (which won't be used at runtime). std::string name = HiddenApi::GetApiFieldName(resolver->GetDexFile(), field_id); if (hidden_api_.IsInRestrictionList(name)) { field_locations_[name].push_back(ref); diff --git a/tools/veridex/precise_hidden_api_finder.cc b/tools/veridex/precise_hidden_api_finder.cc index 2092af3d75..4ae5769014 100644 --- a/tools/veridex/precise_hidden_api_finder.cc +++ b/tools/veridex/precise_hidden_api_finder.cc @@ -63,6 +63,7 @@ void PreciseHiddenApiFinder::Run(const std::vector> uses; for (auto kinds : { field_uses_, method_uses_ }) { for (auto it : kinds) { MethodReference ref = it.first; @@ -74,19 +75,26 @@ void PreciseHiddenApiFinder::Dump(std::ostream& os, HiddenApiStats* stats) { std::string name(info.second.ToString()); std::string full_name = cls + "->" + name; HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name); - stats->api_counts[api_list]++; if (api_list != HiddenApiAccessFlags::kWhitelist) { - ++stats->reflection_count; - os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name - << " use:"; - os << std::endl; - os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl; - os << std::endl; + uses[full_name].push_back(ref); } } } } } + + for (auto it : uses) { + ++stats->reflection_count; + const std::string& full_name = it.first; + HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name); + stats->api_counts[api_list]++; + os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name << " use(s):"; + os << std::endl; + for (const MethodReference& ref : it.second) { + os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl; + } + os << std::endl; + } } } // namespace art diff --git a/tools/veridex/resolver.h b/tools/veridex/resolver.h index 52b15fe0ed..65b296154e 100644 --- a/tools/veridex/resolver.h +++ b/tools/veridex/resolver.h @@ -79,6 +79,10 @@ class VeridexResolver { return dex_file_; } + const DexFile& GetDexFileOf(const VeriClass& kls) { + return GetResolverOf(kls)->dex_file_; + } + private: // Return the resolver where `kls` is from. VeridexResolver* GetResolverOf(const VeriClass& kls) const; -- GitLab From fb008f4e87fbe612abd6b0d0ac6fa897a0033a13 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 23 Apr 2018 10:01:16 -0700 Subject: [PATCH 282/749] ART: Add timing logger granularity to compiler Add more subcounts to open+write of dex files during compilation. Bug: 78201536 Test: m test-art-host Test: inspect output of --dump-timings Change-Id: Ib8d874b0b408669e01292c48c8206b0ebc14cbc5 --- dex2oat/linker/oat_writer.cc | 57 +++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 7078b05a1b..d3f4754bd5 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -3629,18 +3629,22 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil const ArtDexFileLoader dex_file_loader; if (oat_dex_file->source_.IsZipEntry()) { ZipEntry* zip_entry = oat_dex_file->source_.GetZipEntry(); - std::unique_ptr mem_map( - zip_entry->ExtractToMemMap(location.c_str(), "classes.dex", &error_msg)); + std::unique_ptr mem_map; + { + TimingLogger::ScopedTiming extract("Unzip", timings_); + mem_map.reset(zip_entry->ExtractToMemMap(location.c_str(), "classes.dex", &error_msg)); + } if (mem_map == nullptr) { LOG(ERROR) << "Failed to extract dex file to mem map for layout: " << error_msg; return false; } + TimingLogger::ScopedTiming extract("Open", timings_); dex_file = dex_file_loader.Open(location, - zip_entry->GetCrc32(), - std::move(mem_map), - /* verify */ !compiling_boot_image_, - /* verify_checksum */ true, - &error_msg); + zip_entry->GetCrc32(), + std::move(mem_map), + /* verify */ !compiling_boot_image_, + /* verify_checksum */ true, + &error_msg); } else if (oat_dex_file->source_.IsRawFile()) { File* raw_file = oat_dex_file->source_.GetRawFile(); int dup_fd = dup(raw_file->Fd()); @@ -3648,6 +3652,7 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil PLOG(ERROR) << "Failed to dup dex file descriptor (" << raw_file->Fd() << ") at " << location; return false; } + TimingLogger::ScopedTiming extract("Open", timings_); dex_file = dex_file_loader.OpenDex(dup_fd, location, /* verify */ !compiling_boot_image_, /* verify_checksum */ true, @@ -3682,21 +3687,31 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil options.update_checksum_ = true; DexLayout dex_layout(options, profile_compilation_info_, /*file*/ nullptr, /*header*/ nullptr); const uint8_t* dex_src = nullptr; - if (dex_layout.ProcessDexFile(location.c_str(), dex_file.get(), 0, &dex_container_, &error_msg)) { - oat_dex_file->dex_sections_layout_ = dex_layout.GetSections(); - // Dex layout can affect the size of the dex file, so we update here what we have set - // when adding the dex file as a source. - const UnalignedDexFileHeader* header = - AsUnalignedDexFileHeader(dex_container_->GetMainSection()->Begin()); - oat_dex_file->dex_file_size_ = header->file_size_; - dex_src = dex_container_->GetMainSection()->Begin(); - } else { - LOG(WARNING) << "Failed to run dex layout, reason:" << error_msg; - // Since we failed to convert the dex, just copy the input dex. - dex_src = dex_file->Begin(); + { + TimingLogger::ScopedTiming extract("ProcessDexFile", timings_); + if (dex_layout.ProcessDexFile(location.c_str(), + dex_file.get(), + 0, + &dex_container_, + &error_msg)) { + oat_dex_file->dex_sections_layout_ = dex_layout.GetSections(); + // Dex layout can affect the size of the dex file, so we update here what we have set + // when adding the dex file as a source. + const UnalignedDexFileHeader* header = + AsUnalignedDexFileHeader(dex_container_->GetMainSection()->Begin()); + oat_dex_file->dex_file_size_ = header->file_size_; + dex_src = dex_container_->GetMainSection()->Begin(); + } else { + LOG(WARNING) << "Failed to run dex layout, reason:" << error_msg; + // Since we failed to convert the dex, just copy the input dex. + dex_src = dex_file->Begin(); + } } - if (!WriteDexFile(out, oat_dex_file, dex_src, /* update_input_vdex */ false)) { - return false; + { + TimingLogger::ScopedTiming extract("WriteDexFile", timings_); + if (!WriteDexFile(out, oat_dex_file, dex_src, /* update_input_vdex */ false)) { + return false; + } } if (dex_container_ != nullptr) { // Clear the main section in case we write more data into the container. -- GitLab From f120ffc1b4864c28bc2aa249e714b51da11f3ea0 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 23 Apr 2018 11:27:31 -0700 Subject: [PATCH 283/749] Dump profile checksums Also dump profile checksums. This helps diagnose checksum mismatches. Test: manual Bug: 70292748 Change-Id: Iaab4b34378240a265d11d196dcad6db12dc3545d --- profman/profile_assistant_test.cc | 12 ++++++------ runtime/jit/profile_compilation_info.cc | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index 9494f04a5a..72c285a3f5 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -1147,18 +1147,18 @@ TEST_F(ProfileAssistantTest, DumpOnly) { // Check the actual contents of the dump by looking at the offsets of the methods. for (uint32_t m : hot_methods) { const size_t pos = output.find(std::to_string(m) + "[],", hot_offset); - ASSERT_NE(pos, std::string::npos); - EXPECT_LT(pos, startup_offset); + ASSERT_NE(pos, std::string::npos) << output; + EXPECT_LT(pos, startup_offset) << output; } for (uint32_t m : startup_methods) { const size_t pos = output.find(std::to_string(m) + ",", startup_offset); - ASSERT_NE(pos, std::string::npos); - EXPECT_LT(pos, post_startup_offset); + ASSERT_NE(pos, std::string::npos) << output; + EXPECT_LT(pos, post_startup_offset) << output; } for (uint32_t m : post_startup_methods) { const size_t pos = output.find(std::to_string(m) + ",", post_startup_offset); - ASSERT_NE(pos, std::string::npos); - EXPECT_LT(pos, classes_offset); + ASSERT_NE(pos, std::string::npos) << output; + EXPECT_LT(pos, classes_offset) << output; } } diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index f5c2715cda..7c21916997 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -1675,6 +1675,7 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector* os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix); } os << " [index=" << static_cast(dex_data->profile_index) << "]"; + os << " [checksum=" << std::hex << dex_data->checksum << "]" << std::dec; const DexFile* dex_file = nullptr; if (dex_files != nullptr) { for (size_t i = 0; i < dex_files->size(); i++) { -- GitLab From 837f3f04793851278bb1d2cca21b5a0fbd7e3029 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 23 Apr 2018 15:50:00 -0700 Subject: [PATCH 284/749] ART: Fix SCOPED_TRACE macro Correctly evaluate __LINE__ through a preprocessor indirection. Avoid duplicate-variable errors for multiple SCOPED_TRACE uses. Test: mmma art Change-Id: Ib1960b5e2c6ddcbaf766b7d1cb2927ccf5925593 --- libartbase/base/systrace.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libartbase/base/systrace.h b/libartbase/base/systrace.h index 2ff33e8c84..d995dce285 100644 --- a/libartbase/base/systrace.h +++ b/libartbase/base/systrace.h @@ -24,6 +24,7 @@ #include #include "android-base/stringprintf.h" +#include "macros.h" namespace art { @@ -75,7 +76,7 @@ class ScopedTraceNoStart { }; #define SCOPED_TRACE \ - ::art::ScopedTraceNoStart trace ## __LINE__; \ + ::art::ScopedTraceNoStart APPEND_TOKENS_AFTER_EVAL(trace, __LINE__) ; \ (ATRACE_ENABLED()) && ::art::ScopedTraceNoStart::ScopedTraceMessageHelper().stream() } // namespace art -- GitLab From 30025095524e471ec347633e39f26ed0606bea65 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 19 Apr 2018 14:43:29 +0100 Subject: [PATCH 285/749] Pass the fd to OatFile::Setup to avoid selinux errors. bug: 77853712 Test: test.py Change-Id: I069cda5296b561284d71b067924e1bc4a8562710 --- dex2oat/dex2oat_test.cc | 36 +++++++---- dex2oat/linker/oat_writer_test.cc | 15 +++-- dexlayout/dexdiag_test.cc | 3 +- oatdump/oatdump.cc | 15 +++-- runtime/dexopt_test.cc | 3 +- runtime/gc/space/image_space.cc | 3 +- runtime/gc/space/image_space_test.cc | 3 +- runtime/oat_file.cc | 91 ++++++++++++++++++---------- runtime/oat_file.h | 20 ++++-- runtime/oat_file_assistant.cc | 14 +++-- runtime/oat_file_assistant.h | 7 ++- runtime/oat_file_test.cc | 9 ++- runtime/runtime.cc | 3 +- 13 files changed, 150 insertions(+), 72 deletions(-) diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index bc8468e12f..a229d7dd71 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -135,7 +135,8 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { ASSERT_TRUE(success) << error_msg << std::endl << output_; // Verify the odex file was generated as expected. - std::unique_ptr odex_file(OatFile::Open(odex_location.c_str(), + std::unique_ptr odex_file(OatFile::Open(/* zip_fd */ -1, + odex_location.c_str(), odex_location.c_str(), nullptr, nullptr, @@ -154,7 +155,8 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { if (!test_accepts_odex_file_on_failure) { // Verify there's no loadable odex file. - std::unique_ptr odex_file(OatFile::Open(odex_location.c_str(), + std::unique_ptr odex_file(OatFile::Open(/* zip_fd */ -1, + odex_location.c_str(), odex_location.c_str(), nullptr, nullptr, @@ -542,7 +544,8 @@ class Dex2oatVeryLargeTest : public Dex2oatTest { } // Host/target independent checks. std::string error_msg; - std::unique_ptr odex_file(OatFile::Open(odex_location.c_str(), + std::unique_ptr odex_file(OatFile::Open(/* zip_fd */ -1, + odex_location.c_str(), odex_location.c_str(), nullptr, nullptr, @@ -812,7 +815,8 @@ class Dex2oatLayoutTest : public Dex2oatTest { const std::string& app_image_file_name) { // Host/target independent checks. std::string error_msg; - std::unique_ptr odex_file(OatFile::Open(odex_location.c_str(), + std::unique_ptr odex_file(OatFile::Open(/* zip_fd */ -1, + odex_location.c_str(), odex_location.c_str(), nullptr, nullptr, @@ -973,7 +977,8 @@ class Dex2oatUnquickenTest : public Dex2oatTest { void CheckResult(const std::string& dex_location, const std::string& odex_location) { std::string error_msg; - std::unique_ptr odex_file(OatFile::Open(odex_location.c_str(), + std::unique_ptr odex_file(OatFile::Open(/* zip_fd */ -1, + odex_location.c_str(), odex_location.c_str(), nullptr, nullptr, @@ -1366,7 +1371,8 @@ TEST_F(Dex2oatTest, LayoutSections) { EXPECT_EQ(res, 0); // Open our generated oat file. - std::unique_ptr odex_file(OatFile::Open(oat_filename.c_str(), + std::unique_ptr odex_file(OatFile::Open(/* zip_fd */ -1, + oat_filename.c_str(), oat_filename.c_str(), nullptr, nullptr, @@ -1479,7 +1485,8 @@ TEST_F(Dex2oatTest, GenerateCompactDex) { {"--compact-dex-level=fast"}); EXPECT_EQ(res, 0); // Open our generated oat file. - std::unique_ptr odex_file(OatFile::Open(oat_filename.c_str(), + std::unique_ptr odex_file(OatFile::Open(/* zip_fd */ -1, + oat_filename.c_str(), oat_filename.c_str(), nullptr, nullptr, @@ -1724,7 +1731,8 @@ TEST_F(Dex2oatTest, CompactDexGenerationFailure) { }); // Open our generated oat file. std::string error_msg; - std::unique_ptr odex_file(OatFile::Open(oat_filename.c_str(), + std::unique_ptr odex_file(OatFile::Open(/* zip_fd */ -1, + oat_filename.c_str(), oat_filename.c_str(), nullptr, nullptr, @@ -1801,7 +1809,8 @@ TEST_F(Dex2oatTest, VerifyCompilationReason) { { "--compilation-reason=install" }, true); std::string error_msg; - std::unique_ptr odex_file(OatFile::Open(odex_location.c_str(), + std::unique_ptr odex_file(OatFile::Open(/* zip_fd */ -1, + odex_location.c_str(), odex_location.c_str(), nullptr, nullptr, @@ -1826,7 +1835,8 @@ TEST_F(Dex2oatTest, VerifyNoCompilationReason) { {}, true); std::string error_msg; - std::unique_ptr odex_file(OatFile::Open(odex_location.c_str(), + std::unique_ptr odex_file(OatFile::Open(/* zip_fd */ -1, + odex_location.c_str(), odex_location.c_str(), nullptr, nullptr, @@ -1863,7 +1873,8 @@ TEST_F(Dex2oatTest, DontExtract) { ASSERT_TRUE(vdex != nullptr); EXPECT_FALSE(vdex->HasDexSection()) << output_; } - std::unique_ptr odex_file(OatFile::Open(odex_location.c_str(), + std::unique_ptr odex_file(OatFile::Open(/* zip_fd */ -1, + odex_location.c_str(), odex_location.c_str(), nullptr, nullptr, @@ -2106,7 +2117,8 @@ TEST_F(Dex2oatTest, AppImageNoProfile) { [](const OatFile&) {}); // Open our generated oat file. std::string error_msg; - std::unique_ptr odex_file(OatFile::Open(odex_location.c_str(), + std::unique_ptr odex_file(OatFile::Open(/* zip_fd */ -1, + odex_location.c_str(), odex_location.c_str(), nullptr, nullptr, diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 37e69f7706..208e96fc6f 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -426,7 +426,8 @@ TEST_F(OatTest, WriteRead) { if (kCompile) { // OatWriter strips the code, regenerate to compare compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings); } - std::unique_ptr oat_file(OatFile::Open(tmp_oat.GetFilename(), + std::unique_ptr oat_file(OatFile::Open(/* zip_fd */ -1, + tmp_oat.GetFilename(), tmp_oat.GetFilename(), nullptr, nullptr, @@ -560,7 +561,8 @@ TEST_F(OatTest, EmptyTextSection) { /* verify */ false); ASSERT_TRUE(success); - std::unique_ptr oat_file(OatFile::Open(tmp_oat.GetFilename(), + std::unique_ptr oat_file(OatFile::Open(/* zip_fd */ -1, + tmp_oat.GetFilename(), tmp_oat.GetFilename(), nullptr, nullptr, @@ -637,7 +639,8 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) { ASSERT_TRUE(success); std::string error_msg; - std::unique_ptr opened_oat_file(OatFile::Open(tmp_oat.GetFilename(), + std::unique_ptr opened_oat_file(OatFile::Open(/* zip_fd */ -1, + tmp_oat.GetFilename(), tmp_oat.GetFilename(), nullptr, nullptr, @@ -767,7 +770,8 @@ void OatTest::TestZipFileInput(bool verify) { ASSERT_TRUE(success); std::string error_msg; - std::unique_ptr opened_oat_file(OatFile::Open(tmp_oat.GetFilename(), + std::unique_ptr opened_oat_file(OatFile::Open(/* zip_fd */ -1, + tmp_oat.GetFilename(), tmp_oat.GetFilename(), nullptr, nullptr, @@ -816,7 +820,8 @@ void OatTest::TestZipFileInput(bool verify) { ASSERT_TRUE(success); std::string error_msg; - std::unique_ptr opened_oat_file(OatFile::Open(tmp_oat.GetFilename(), + std::unique_ptr opened_oat_file(OatFile::Open(/* zip_fd */ -1, + tmp_oat.GetFilename(), tmp_oat.GetFilename(), nullptr, nullptr, diff --git a/dexlayout/dexdiag_test.cc b/dexlayout/dexdiag_test.cc index 068949ceba..53145c22fa 100644 --- a/dexlayout/dexdiag_test.cc +++ b/dexlayout/dexdiag_test.cc @@ -68,7 +68,8 @@ class DexDiagTest : public CommonRuntimeTest { EXPECT_TRUE(!oat_location.empty()); std::cout << "==" << oat_location << std::endl; std::string error_msg; - std::unique_ptr oat(OatFile::Open(oat_location.c_str(), + std::unique_ptr oat(OatFile::Open(/* zip_fd */ -1, + oat_location.c_str(), oat_location.c_str(), nullptr, nullptr, diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 6a68b55fad..44050ff8ed 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -2156,7 +2156,8 @@ class ImageDumper { oat_file = runtime->GetOatFileManager().FindOpenedOatFileFromOatLocation(oat_location); } if (oat_file == nullptr) { - oat_file = OatFile::Open(oat_location, + oat_file = OatFile::Open(/* zip_fd */ -1, + oat_location, oat_location, nullptr, nullptr, @@ -3044,7 +3045,8 @@ static int DumpImages(Runtime* runtime, OatDumperOptions* options, std::ostream* // We need to map the oat file in the low 4gb or else the fixup wont be able to fit oat file // pointers into 32 bit pointer sized ArtMethods. std::string error_msg; - std::unique_ptr oat_file(OatFile::Open(options->app_oat_, + std::unique_ptr oat_file(OatFile::Open(/* zip_fd */ -1, + options->app_oat_, options->app_oat_, nullptr, nullptr, @@ -3167,7 +3169,8 @@ static int DumpOat(Runtime* runtime, << "oatdump might fail if the oat file does not contain the dex code."; } std::string error_msg; - std::unique_ptr oat_file(OatFile::Open(oat_filename, + std::unique_ptr oat_file(OatFile::Open(/* zip_fd */ -1, + oat_filename, oat_filename, nullptr, nullptr, @@ -3192,7 +3195,8 @@ static int SymbolizeOat(const char* oat_filename, std::string& output_name, bool no_bits) { std::string error_msg; - std::unique_ptr oat_file(OatFile::Open(oat_filename, + std::unique_ptr oat_file(OatFile::Open(/* zip_fd */ -1, + oat_filename, oat_filename, nullptr, nullptr, @@ -3239,7 +3243,8 @@ class IMTDumper { if (oat_filename != nullptr) { std::string error_msg; - std::unique_ptr oat_file(OatFile::Open(oat_filename, + std::unique_ptr oat_file(OatFile::Open(/* zip_fd */ -1, + oat_filename, oat_filename, nullptr, nullptr, diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc index 8f0f9c61dc..f8388f315d 100644 --- a/runtime/dexopt_test.cc +++ b/runtime/dexopt_test.cc @@ -105,7 +105,8 @@ void DexoptTest::GenerateOatForTest(const std::string& dex_location, } // Verify the odex file was generated as expected. - std::unique_ptr odex_file(OatFile::Open(oat_location.c_str(), + std::unique_ptr odex_file(OatFile::Open(/* zip_fd */ -1, + oat_location.c_str(), oat_location.c_str(), nullptr, nullptr, diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index e2154b8e4d..dbe09e8c5b 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1418,7 +1418,8 @@ class ImageSpaceLoader { CHECK(image_header.GetOatDataBegin() != nullptr); - std::unique_ptr oat_file(OatFile::Open(oat_filename, + std::unique_ptr oat_file(OatFile::Open(/* zip_fd */ -1, + oat_filename, oat_filename, image_header.GetOatDataBegin(), image_header.GetOatFileBegin(), diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc index fcc47d45fc..f202a43be9 100644 --- a/runtime/gc/space/image_space_test.cc +++ b/runtime/gc/space/image_space_test.cc @@ -43,7 +43,8 @@ TEST_F(DexoptTest, ValidateOatFile) { args.push_back("--oat-file=" + oat_location); ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; - std::unique_ptr oat(OatFile::Open(oat_location.c_str(), + std::unique_ptr oat(OatFile::Open(/* zip_fd */ -1, + oat_location.c_str(), oat_location.c_str(), nullptr, nullptr, diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index c7a558cc94..371678d4d9 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -97,7 +97,8 @@ class OatFileBase : public OatFile { virtual ~OatFileBase() {} template - static OatFileBase* OpenOatFile(const std::string& vdex_filename, + static OatFileBase* OpenOatFile(int zip_fd, + const std::string& vdex_filename, const std::string& elf_filename, const std::string& location, uint8_t* requested_base, @@ -109,7 +110,8 @@ class OatFileBase : public OatFile { std::string* error_msg); template - static OatFileBase* OpenOatFile(int vdex_fd, + static OatFileBase* OpenOatFile(int zip_fd, + int vdex_fd, int oat_fd, const std::string& vdex_filename, const std::string& oat_filename, @@ -160,7 +162,7 @@ class OatFileBase : public OatFile { virtual void PreSetup(const std::string& elf_filename) = 0; - bool Setup(const char* abs_dex_location, std::string* error_msg); + bool Setup(int zip_fd, const char* abs_dex_location, std::string* error_msg); // Setters exposed for ElfOatFile. @@ -181,7 +183,8 @@ class OatFileBase : public OatFile { }; template -OatFileBase* OatFileBase::OpenOatFile(const std::string& vdex_filename, +OatFileBase* OatFileBase::OpenOatFile(int zip_fd, + const std::string& vdex_filename, const std::string& elf_filename, const std::string& location, uint8_t* requested_base, @@ -214,7 +217,7 @@ OatFileBase* OatFileBase::OpenOatFile(const std::string& vdex_filename, ret->PreSetup(elf_filename); - if (!ret->Setup(abs_dex_location, error_msg)) { + if (!ret->Setup(zip_fd, abs_dex_location, error_msg)) { return nullptr; } @@ -222,7 +225,8 @@ OatFileBase* OatFileBase::OpenOatFile(const std::string& vdex_filename, } template -OatFileBase* OatFileBase::OpenOatFile(int vdex_fd, +OatFileBase* OatFileBase::OpenOatFile(int zip_fd, + int vdex_fd, int oat_fd, const std::string& vdex_location, const std::string& oat_location, @@ -254,7 +258,7 @@ OatFileBase* OatFileBase::OpenOatFile(int vdex_fd, ret->PreSetup(oat_location); - if (!ret->Setup(abs_dex_location, error_msg)) { + if (!ret->Setup(zip_fd, abs_dex_location, error_msg)) { return nullptr; } @@ -485,7 +489,7 @@ static void DCheckIndexToBssMapping(OatFile* oat_file, } } -bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { +bool OatFileBase::Setup(int zip_fd, const char* abs_dex_location, std::string* error_msg) { if (!GetOatHeader().IsValid()) { std::string cause = GetOatHeader().GetValidationErrorMessage(); *error_msg = StringPrintf("Invalid oat header for '%s': %s", @@ -641,12 +645,23 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { uncompressed_dex_files_.reset(new std::vector>()); // No dex files, load it from location. const ArtDexFileLoader dex_file_loader; - if (!dex_file_loader.Open(dex_file_location.c_str(), - dex_file_location, - /* verify */ false, - /* verify_checksum */ false, - error_msg, - uncompressed_dex_files_.get())) { + bool loaded = false; + if (zip_fd != -1) { + loaded = dex_file_loader.OpenZip(zip_fd, + dex_file_location, + /* verify */ false, + /* verify_checksum */ false, + error_msg, + uncompressed_dex_files_.get()); + } else { + loaded = dex_file_loader.Open(dex_file_location.c_str(), + dex_file_location, + /* verify */ false, + /* verify_checksum */ false, + error_msg, + uncompressed_dex_files_.get()); + } + if (!loaded) { if (Runtime::Current() == nullptr) { // If there's no runtime, we're running oatdump, so return // a half constructed oat file that oatdump knows how to deal with. @@ -1144,7 +1159,8 @@ class ElfOatFile FINAL : public OatFileBase { public: ElfOatFile(const std::string& filename, bool executable) : OatFileBase(filename, executable) {} - static ElfOatFile* OpenElfFile(File* file, + static ElfOatFile* OpenElfFile(int zip_fd, + File* file, const std::string& location, uint8_t* requested_base, uint8_t* oat_file_begin, // Override base if not null @@ -1154,7 +1170,8 @@ class ElfOatFile FINAL : public OatFileBase { const char* abs_dex_location, std::string* error_msg); - bool InitializeFromElfFile(ElfFile* elf_file, + bool InitializeFromElfFile(int zip_fd, + ElfFile* elf_file, VdexFile* vdex_file, const char* abs_dex_location, std::string* error_msg); @@ -1204,7 +1221,8 @@ class ElfOatFile FINAL : public OatFileBase { DISALLOW_COPY_AND_ASSIGN(ElfOatFile); }; -ElfOatFile* ElfOatFile::OpenElfFile(File* file, +ElfOatFile* ElfOatFile::OpenElfFile(int zip_fd, + File* file, const std::string& location, uint8_t* requested_base, uint8_t* oat_file_begin, // Override base if not null @@ -1231,14 +1249,15 @@ ElfOatFile* ElfOatFile::OpenElfFile(File* file, return nullptr; } - if (!oat_file->Setup(abs_dex_location, error_msg)) { + if (!oat_file->Setup(zip_fd, abs_dex_location, error_msg)) { return nullptr; } return oat_file.release(); } -bool ElfOatFile::InitializeFromElfFile(ElfFile* elf_file, +bool ElfOatFile::InitializeFromElfFile(int zip_fd, + ElfFile* elf_file, VdexFile* vdex_file, const char* abs_dex_location, std::string* error_msg) { @@ -1255,7 +1274,7 @@ bool ElfOatFile::InitializeFromElfFile(ElfFile* elf_file, SetBegin(elf_file->Begin() + offset); SetEnd(elf_file->Begin() + size + offset); // Ignore the optional .bss section when opening non-executable. - return Setup(abs_dex_location, error_msg); + return Setup(zip_fd, abs_dex_location, error_msg); } bool ElfOatFile::Load(const std::string& elf_filename, @@ -1356,18 +1375,20 @@ static void CheckLocation(const std::string& location) { CHECK(!location.empty()); } -OatFile* OatFile::OpenWithElfFile(ElfFile* elf_file, +OatFile* OatFile::OpenWithElfFile(int zip_fd, + ElfFile* elf_file, VdexFile* vdex_file, const std::string& location, const char* abs_dex_location, std::string* error_msg) { std::unique_ptr oat_file(new ElfOatFile(location, false /* executable */)); - return oat_file->InitializeFromElfFile(elf_file, vdex_file, abs_dex_location, error_msg) + return oat_file->InitializeFromElfFile(zip_fd, elf_file, vdex_file, abs_dex_location, error_msg) ? oat_file.release() : nullptr; } -OatFile* OatFile::Open(const std::string& oat_filename, +OatFile* OatFile::Open(int zip_fd, + const std::string& oat_filename, const std::string& oat_location, uint8_t* requested_base, uint8_t* oat_file_begin, @@ -1392,7 +1413,8 @@ OatFile* OatFile::Open(const std::string& oat_filename, // Try dlopen first, as it is required for native debuggability. This will fail fast if dlopen is // disabled. - OatFile* with_dlopen = OatFileBase::OpenOatFile(vdex_filename, + OatFile* with_dlopen = OatFileBase::OpenOatFile(zip_fd, + vdex_filename, oat_filename, oat_location, requested_base, @@ -1421,7 +1443,8 @@ OatFile* OatFile::Open(const std::string& oat_filename, // // Another independent reason is the absolute placement of boot.oat. dlopen on the host usually // does honor the virtual address encoded in the ELF file only for ET_EXEC files, not ET_DYN. - OatFile* with_internal = OatFileBase::OpenOatFile(vdex_filename, + OatFile* with_internal = OatFileBase::OpenOatFile(zip_fd, + vdex_filename, oat_filename, oat_location, requested_base, @@ -1434,7 +1457,8 @@ OatFile* OatFile::Open(const std::string& oat_filename, return with_internal; } -OatFile* OatFile::Open(int vdex_fd, +OatFile* OatFile::Open(int zip_fd, + int vdex_fd, int oat_fd, const std::string& oat_location, uint8_t* requested_base, @@ -1447,7 +1471,8 @@ OatFile* OatFile::Open(int vdex_fd, std::string vdex_location = GetVdexFilename(oat_location); - OatFile* with_internal = OatFileBase::OpenOatFile(vdex_fd, + OatFile* with_internal = OatFileBase::OpenOatFile(zip_fd, + vdex_fd, oat_fd, vdex_location, oat_location, @@ -1461,12 +1486,14 @@ OatFile* OatFile::Open(int vdex_fd, return with_internal; } -OatFile* OatFile::OpenWritable(File* file, +OatFile* OatFile::OpenWritable(int zip_fd, + File* file, const std::string& location, const char* abs_dex_location, std::string* error_msg) { CheckLocation(location); - return ElfOatFile::OpenElfFile(file, + return ElfOatFile::OpenElfFile(zip_fd, + file, location, nullptr, nullptr, @@ -1477,12 +1504,14 @@ OatFile* OatFile::OpenWritable(File* file, error_msg); } -OatFile* OatFile::OpenReadable(File* file, +OatFile* OatFile::OpenReadable(int zip_fd, + File* file, const std::string& location, const char* abs_dex_location, std::string* error_msg) { CheckLocation(location); - return ElfOatFile::OpenElfFile(file, + return ElfOatFile::OpenElfFile(zip_fd, + file, location, nullptr, nullptr, diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 6494b4c58e..8e18cee729 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -74,7 +74,8 @@ class OatFile { // Opens an oat file contained within the given elf file. This is always opened as // non-executable at the moment. - static OatFile* OpenWithElfFile(ElfFile* elf_file, + static OatFile* OpenWithElfFile(int zip_fd, + ElfFile* elf_file, VdexFile* vdex_file, const std::string& location, const char* abs_dex_location, @@ -83,7 +84,8 @@ class OatFile { // optionally be used to request where the file should be loaded. // See the ResolveRelativeEncodedDexLocation for a description of how the // abs_dex_location argument is used. - static OatFile* Open(const std::string& filename, + static OatFile* Open(int zip_fd, + const std::string& filename, const std::string& location, uint8_t* requested_base, uint8_t* oat_file_begin, @@ -93,8 +95,10 @@ class OatFile { std::string* error_msg); // Similar to OatFile::Open(const std::string...), but accepts input vdex and - // odex files as file descriptors. - static OatFile* Open(int vdex_fd, + // odex files as file descriptors. We also take zip_fd in case the vdex does not + // contain the dex code, and we need to read it from the zip file. + static OatFile* Open(int zip_fd, + int vdex_fd, int oat_fd, const std::string& oat_location, uint8_t* requested_base, @@ -109,11 +113,15 @@ class OatFile { // where relocations may be required. Currently used from // ImageWriter which wants to open a writable version from an existing // file descriptor for patching. - static OatFile* OpenWritable(File* file, const std::string& location, + static OatFile* OpenWritable(int zip_fd, + File* file, + const std::string& location, const char* abs_dex_location, std::string* error_msg); // Open an oat file from an already opened File. Maps it PROT_READ, MAP_PRIVATE. - static OatFile* OpenReadable(File* file, const std::string& location, + static OatFile* OpenReadable(int zip_fd, + File* file, + const std::string& location, const char* abs_dex_location, std::string* error_msg); diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 718f9176e9..9c8b6512a7 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -118,7 +118,7 @@ OatFileAssistant::OatFileAssistant(const char* dex_location, std::string error_msg; std::string odex_file_name; if (DexLocationToOdexFilename(dex_location_, isa_, &odex_file_name, &error_msg)) { - odex_.Reset(odex_file_name, UseFdToReadFiles(), vdex_fd, oat_fd); + odex_.Reset(odex_file_name, UseFdToReadFiles(), zip_fd, vdex_fd, oat_fd); } else { LOG(WARNING) << "Failed to determine odex file name: " << error_msg; } @@ -1148,7 +1148,8 @@ const OatFile* OatFileAssistant::OatFileInfo::GetFile() { std::string error_msg; if (use_fd_) { if (oat_fd_ >= 0 && vdex_fd_ >= 0) { - file_.reset(OatFile::Open(vdex_fd_, + file_.reset(OatFile::Open(zip_fd_, + vdex_fd_, oat_fd_, filename_.c_str(), nullptr, @@ -1159,7 +1160,8 @@ const OatFile* OatFileAssistant::OatFileInfo::GetFile() { &error_msg)); } } else { - file_.reset(OatFile::Open(filename_.c_str(), + file_.reset(OatFile::Open(/* zip_fd */ -1, + filename_.c_str(), filename_.c_str(), nullptr, nullptr, @@ -1235,11 +1237,15 @@ void OatFileAssistant::OatFileInfo::Reset() { status_attempted_ = false; } -void OatFileAssistant::OatFileInfo::Reset(const std::string& filename, bool use_fd, int vdex_fd, +void OatFileAssistant::OatFileInfo::Reset(const std::string& filename, + bool use_fd, + int zip_fd, + int vdex_fd, int oat_fd) { filename_provided_ = true; filename_ = filename; use_fd_ = use_fd; + zip_fd_ = zip_fd; vdex_fd_ = vdex_fd; oat_fd_ = oat_fd; Reset(); diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index 8d6ec0014a..a6d0961835 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -378,7 +378,11 @@ class OatFileAssistant { // Clear any cached information and switch to getting info about the oat // file with the given filename. - void Reset(const std::string& filename, bool use_fd, int vdex_fd = -1, int oat_fd = -1); + void Reset(const std::string& filename, + bool use_fd, + int zip_fd = -1, + int vdex_fd = -1, + int oat_fd = -1); // Release the loaded oat file for runtime use. // Returns null if the oat file hasn't been loaded or is out of date. @@ -415,6 +419,7 @@ class OatFileAssistant { bool filename_provided_ = false; std::string filename_; + int zip_fd_ = -1; int oat_fd_ = -1; int vdex_fd_ = -1; bool use_fd_ = false; diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc index 89812f370f..12dfe20d56 100644 --- a/runtime/oat_file_test.cc +++ b/runtime/oat_file_test.cc @@ -74,7 +74,8 @@ TEST_F(OatFileTest, LoadOat) { std::string error_msg; ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename( dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg; - std::unique_ptr odex_file(OatFile::Open(oat_location.c_str(), + std::unique_ptr odex_file(OatFile::Open(/* zip_fd */ -1, + oat_location.c_str(), oat_location.c_str(), nullptr, nullptr, @@ -101,7 +102,8 @@ TEST_F(OatFileTest, ChangingMultiDexUncompressed) { // Ensure we can load that file. Just a precondition. { - std::unique_ptr odex_file(OatFile::Open(oat_location.c_str(), + std::unique_ptr odex_file(OatFile::Open(/* zip_fd */ -1, + oat_location.c_str(), oat_location.c_str(), nullptr, nullptr, @@ -117,7 +119,8 @@ TEST_F(OatFileTest, ChangingMultiDexUncompressed) { Copy(GetTestDexFileName("MainUncompressed"), dex_location); // And try to load again. - std::unique_ptr odex_file(OatFile::Open(oat_location.c_str(), + std::unique_ptr odex_file(OatFile::Open(/* zip_fd */ -1, + oat_location.c_str(), oat_location.c_str(), nullptr, nullptr, diff --git a/runtime/runtime.cc b/runtime/runtime.cc index c394fefb38..bfed326960 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1002,7 +1002,8 @@ static bool OpenDexFilesFromImage(const std::string& image_location, return false; } std::unique_ptr oat_file( - OatFile::OpenWithElfFile(elf_file.release(), + OatFile::OpenWithElfFile(/* zip_fd */ -1, + elf_file.release(), vdex_file.release(), oat_location, nullptr, -- GitLab From 31d7a57b70c2b68aa19a58d4177dcbde06914441 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 24 Apr 2018 12:35:32 +0100 Subject: [PATCH 286/749] Improve debug logging of classes for 77342775. Log the reference, space and defining dex file of the source and target class. Log the start of image space and replace image space file name with '+' if it's the same as the image name. Test: Manual; change 108-check-cast to multi-dex and disable the Lorg/apache/http/ filtering, check error message. Repeat with a profile to put LB; and LD; into app image. Bug: 77342775 (cherry picked from commit cbb7bd2795b9afe242efe658b4e5736cbe378921) Change-Id: Iee935c463816fff50510371c4d8acb1807980039 --- runtime/debug_print.cc | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/runtime/debug_print.cc b/runtime/debug_print.cc index 7e075ce352..c5bb4d57e6 100644 --- a/runtime/debug_print.cc +++ b/runtime/debug_print.cc @@ -40,7 +40,11 @@ std::string DescribeSpace(ObjPtr klass) { heap->FindContinuousSpaceFromObject(klass.Ptr(), /* fail_ok */ true); if (cs != nullptr) { if (cs->IsImageSpace()) { - oss << "image;" << cs->GetName() << ";" << cs->AsImageSpace()->GetImageFilename(); + gc::space::ImageSpace* ispace = cs->AsImageSpace(); + oss << "image;" << ispace->GetName() << ";" + // If the file name is the same as the name, output "+" instead to shorten the output. + << (ispace->GetImageFilename() == cs->GetName() ? "+" : ispace->GetImageFilename()) + << ";" << static_cast(ispace->Begin()); } else { oss << "continuous;" << cs->GetName(); } @@ -146,13 +150,20 @@ void DumpB77342775DebugData(ObjPtr target_class, ObjPtrGetDescriptor(&source_descriptor_storage); LOG(ERROR) << "Maybe bug 77342775, looking for " << target_descriptor - << " with loader " << DescribeLoaders(target_class->GetClassLoader(), target_descriptor); + << " " << target_class.Ptr() << "[" << DescribeSpace(target_class) << "]" + << " defined in " << target_class->GetDexFile().GetLocation() + << "/" << static_cast(&target_class->GetDexFile()) + << "\n with loader: " << DescribeLoaders(target_class->GetClassLoader(), target_descriptor); if (target_class->IsInterface()) { ObjPtr iftable = src_class->GetIfTable(); CHECK(iftable != nullptr); size_t ifcount = iftable->Count(); - LOG(ERROR) << " in interface table for " << source_descriptor << " ifcount=" << ifcount - << " with loader " << DescribeLoaders(src_class->GetClassLoader(), source_descriptor); + LOG(ERROR) << " in interface table for " << source_descriptor + << " " << src_class.Ptr() << "[" << DescribeSpace(src_class) << "]" + << " defined in " << src_class->GetDexFile().GetLocation() + << "/" << static_cast(&src_class->GetDexFile()) + << " ifcount=" << ifcount + << "\n with loader " << DescribeLoaders(src_class->GetClassLoader(), source_descriptor); for (size_t i = 0; i != ifcount; ++i) { ObjPtr iface = iftable->GetInterface(i); CHECK(iface != nullptr); @@ -161,7 +172,10 @@ void DumpB77342775DebugData(ObjPtr target_class, ObjPtrGetClassLoader(), source_descriptor); + << " " << src_class.Ptr() << "[" << DescribeSpace(src_class) << "]" + << " defined in " << src_class->GetDexFile().GetLocation() + << "/" << static_cast(&src_class->GetDexFile()) + << "\n with loader " << DescribeLoaders(src_class->GetClassLoader(), source_descriptor); for (ObjPtr klass = src_class; klass != nullptr; klass = klass->GetSuperClass()) { -- GitLab From dcd117e04b0831e4539544c38c524799114f3e66 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 19 Apr 2018 11:54:00 +0100 Subject: [PATCH 287/749] ARM: Use rMR for Baker RB introspection marking. The marking register (r8 on ARM) is known to be 1 when entering the introspection marking entrypoint, so we can clobber it, use it as a temporaray register (instead or r4) in the runtime entrypoint, and reload the 1 before returning. The immediate benefits are minor, see below, but this shall allow further improvements, for example we could try to change rMR to r4 which would reduce code size of every marking register check by 2 bytes. ARM boot image (boot*.oat) size in aosp_taimen-userdebug: - before: 17861724 - after: 17858088 (-3636) Test: Pixel 2 XL boots. Test: m test-art-host-gtest Test: testrunner.py --target --optimizing --32 Test: Repeat the above tests with heap poisoning enabled. Bug: 36141117 Change-Id: I0f625dec3a6b3ee1786f7e5f4377be42b9bc37d3 --- .../optimizing/code_generator_arm_vixl.cc | 84 ++------ compiler/optimizing/code_generator_arm_vixl.h | 9 +- compiler/optimizing/intrinsics_arm_vixl.cc | 2 - .../arm/relative_patcher_thumb2_test.cc | 57 +++-- runtime/arch/arm/asm_support_arm.h | 4 +- runtime/arch/arm/quick_entrypoints_arm.S | 195 ++++++++++-------- runtime/oat.h | 4 +- 7 files changed, 161 insertions(+), 194 deletions(-) diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 7350b146f9..58ce9aa9f0 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -107,16 +107,6 @@ constexpr int kMarkingRegisterCheckBreakCodeBaseCode = 0x10; // Marker that code is yet to be, and must, be implemented. #define TODO_VIXL32(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented " -static inline void ExcludeIPAndBakerCcEntrypointRegister(UseScratchRegisterScope* temps, - HInstruction* instruction) { - DCHECK(temps->IsAvailable(ip)); - temps->Exclude(ip); - DCHECK(!temps->IsAvailable(kBakerCcEntrypointRegister)); - DCHECK_NE(instruction->GetLocations()->GetTempCount(), 0u); - DCHECK(RegisterFrom(instruction->GetLocations()->GetTemp( - instruction->GetLocations()->GetTempCount() - 1u)).Is(kBakerCcEntrypointRegister)); -} - static inline void EmitPlaceholderBne(CodeGeneratorARMVIXL* codegen, vixl32::Label* patch_label) { ExactAssemblyScope eas(codegen->GetVIXLAssembler(), kMaxInstructionSizeInBytes); __ bind(patch_label); @@ -5973,8 +5963,6 @@ void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction, if (field_info.GetFieldOffset().Uint32Value() >= kReferenceLoadMinFarOffset) { locations->AddTemp(Location::RequiresRegister()); } - // And we always need the reserved entrypoint register. - locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister.GetCode())); } else { locations->AddTemp(Location::RequiresRegister()); } @@ -6087,11 +6075,11 @@ void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction, case DataType::Type::kReference: { // /* HeapReference */ out = *(base + offset) if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { - Location temp_loc = locations->GetTemp(0); + Location maybe_temp = (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location(); // Note that a potential implicit null check is handled in this // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier call. codegen_->GenerateFieldLoadWithBakerReadBarrier( - instruction, out, base, offset, temp_loc, /* needs_null_check */ true); + instruction, out, base, offset, maybe_temp, /* needs_null_check */ true); if (is_volatile) { codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny); } @@ -6390,8 +6378,6 @@ void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) { object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap); } if (object_array_get_with_read_barrier && kUseBakerReadBarrier) { - // We need a temporary register for the read barrier marking slow - // path in CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier. if (kBakerReadBarrierLinkTimeThunksEnableForFields && !Runtime::Current()->UseJitCompilation() && instruction->GetIndex()->IsConstant()) { @@ -6404,16 +6390,10 @@ void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) { if (offset >= kReferenceLoadMinFarOffset) { locations->AddTemp(Location::RequiresRegister()); } - // And we always need the reserved entrypoint register. - locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister.GetCode())); - } else if (kBakerReadBarrierLinkTimeThunksEnableForArrays && - !Runtime::Current()->UseJitCompilation() && - !instruction->GetIndex()->IsConstant()) { - // We need a non-scratch temporary for the array data pointer. - locations->AddTemp(Location::RequiresRegister()); - // And we always need the reserved entrypoint register. - locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister.GetCode())); } else { + // If using introspection, we need a non-scratch temporary for the array data pointer. + // Otherwise, we need a temporary register for the read barrier marking slow + // path in CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier. locations->AddTemp(Location::RequiresRegister()); } } else if (mirror::kUseStringCompression && instruction->IsStringCharAt()) { @@ -6526,20 +6506,22 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { // /* HeapReference */ out = // *(obj + data_offset + index * sizeof(HeapReference)) if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { - Location temp = locations->GetTemp(0); // Note that a potential implicit null check is handled in this // CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier call. DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0))); if (index.IsConstant()) { // Array load with a constant index can be treated as a field load. + Location maybe_temp = + (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location(); data_offset += Int32ConstantFrom(index) << DataType::SizeShift(type); codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction, out_loc, obj, data_offset, - locations->GetTemp(0), + maybe_temp, /* needs_null_check */ false); } else { + Location temp = locations->GetTemp(0); codegen_->GenerateArrayLoadWithBakerReadBarrier( instruction, out_loc, obj, data_offset, index, temp, /* needs_null_check */ false); } @@ -7447,13 +7429,6 @@ void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) { // For non-Baker read barrier we have a temp-clobbering call. } } - if (kUseBakerReadBarrier && kBakerReadBarrierLinkTimeThunksEnableForGcRoots) { - if (load_kind == HLoadClass::LoadKind::kBssEntry || - (load_kind == HLoadClass::LoadKind::kReferrersClass && - !Runtime::Current()->UseJitCompilation())) { - locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister.GetCode())); - } - } } // NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not @@ -7687,9 +7662,6 @@ void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) { // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK() // that the the kPrimNot result register is the same as the first argument register. locations->SetCustomSlowPathCallerSaves(caller_saves); - if (kUseBakerReadBarrier && kBakerReadBarrierLinkTimeThunksEnableForGcRoots) { - locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister.GetCode())); - } } else { // For non-Baker read barrier we have a temp-clobbering call. } @@ -7866,9 +7838,6 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) { // Note that TypeCheckSlowPathARM uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind)); - if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { - codegen_->MaybeAddBakerCcEntrypointTempForFields(locations); - } } void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) { @@ -8829,7 +8798,7 @@ void CodeGeneratorARMVIXL::GenerateGcRootFieldLoad( // return_address: UseScratchRegisterScope temps(GetVIXLAssembler()); - ExcludeIPAndBakerCcEntrypointRegister(&temps, instruction); + temps.Exclude(ip); bool narrow = CanEmitNarrowLdr(root_reg, obj, offset); uint32_t custom_data = EncodeBakerReadBarrierGcRootData(root_reg.GetCode(), narrow); vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data); @@ -8897,16 +8866,6 @@ void CodeGeneratorARMVIXL::GenerateGcRootFieldLoad( MaybeGenerateMarkingRegisterCheck(/* code */ 18); } -void CodeGeneratorARMVIXL::MaybeAddBakerCcEntrypointTempForFields(LocationSummary* locations) { - DCHECK(kEmitCompilerReadBarrier); - DCHECK(kUseBakerReadBarrier); - if (kBakerReadBarrierLinkTimeThunksEnableForFields) { - if (!Runtime::Current()->UseJitCompilation()) { - locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister.GetCode())); - } - } -} - void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, Location ref, vixl32::Register obj, @@ -8944,7 +8903,6 @@ void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* i vixl32::Register base = obj; if (offset >= kReferenceLoadMinFarOffset) { base = RegisterFrom(temp); - DCHECK(!base.Is(kBakerCcEntrypointRegister)); static_assert(IsPowerOfTwo(kReferenceLoadMinFarOffset), "Expecting a power of 2."); __ Add(base, obj, Operand(offset & ~(kReferenceLoadMinFarOffset - 1u))); offset &= (kReferenceLoadMinFarOffset - 1u); @@ -8954,7 +8912,7 @@ void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* i DCHECK(!narrow); } UseScratchRegisterScope temps(GetVIXLAssembler()); - ExcludeIPAndBakerCcEntrypointRegister(&temps, instruction); + temps.Exclude(ip); uint32_t custom_data = EncodeBakerReadBarrierFieldData(base.GetCode(), obj.GetCode(), narrow); vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data); @@ -9037,10 +8995,9 @@ void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(HInstruction* i vixl32::Register index_reg = RegisterFrom(index, DataType::Type::kInt32); vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference); vixl32::Register data_reg = RegisterFrom(temp, DataType::Type::kInt32); // Raw pointer. - DCHECK(!data_reg.Is(kBakerCcEntrypointRegister)); UseScratchRegisterScope temps(GetVIXLAssembler()); - ExcludeIPAndBakerCcEntrypointRegister(&temps, instruction); + temps.Exclude(ip); uint32_t custom_data = EncodeBakerReadBarrierArrayData(data_reg.GetCode()); vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data); @@ -9927,16 +9884,16 @@ static void EmitGrayCheckAndFastPath(ArmVIXLAssembler& assembler, } // Load the read barrier introspection entrypoint in register `entrypoint` -static void LoadReadBarrierMarkIntrospectionEntrypoint(ArmVIXLAssembler& assembler, - vixl32::Register entrypoint) { +static vixl32::Register LoadReadBarrierMarkIntrospectionEntrypoint(ArmVIXLAssembler& assembler) { // The register where the read barrier introspection entrypoint is loaded - // is fixed: `kBakerCcEntrypointRegister` (R4). - DCHECK(entrypoint.Is(kBakerCcEntrypointRegister)); + // is the marking register. We clobber it here and the entrypoint restores it to 1. + vixl32::Register entrypoint = mr; // entrypoint = Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection. DCHECK_EQ(ip.GetCode(), 12u); const int32_t entry_point_offset = Thread::ReadBarrierMarkEntryPointsOffset(ip.GetCode()); __ Ldr(entrypoint, MemOperand(tr, entry_point_offset)); + return entrypoint; } void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assembler, @@ -9975,8 +9932,7 @@ void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assemb __ Bind(&slow_path); const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 + raw_ldr_offset; - vixl32::Register ep_reg(kBakerCcEntrypointRegister); - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); + vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler); if (width == BakerReadBarrierWidth::kWide) { MemOperand ldr_half_address(lr, ldr_offset + 2); __ Ldrh(ip, ldr_half_address); // Load the LDR immediate half-word with "Rt | imm12". @@ -10016,8 +9972,7 @@ void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assemb MemOperand ldr_address(lr, ldr_offset + 2); __ Ldrb(ip, ldr_address); // Load the LDR (register) byte with "00 | imm2 | Rm", // i.e. Rm+32 because the scale in imm2 is 2. - vixl32::Register ep_reg(kBakerCcEntrypointRegister); - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); + vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler); __ Bfi(ep_reg, ip, 3, 6); // Insert ip to the entrypoint address to create // a switch case target based on the index register. __ Mov(ip, base_reg); // Move the base register to ip0. @@ -10050,8 +10005,7 @@ void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assemb " the highest bits and the 'forwarding address' state to have all bits set"); __ Cmp(ip, Operand(0xc0000000)); __ B(hs, &forwarding_address); - vixl32::Register ep_reg(kBakerCcEntrypointRegister); - LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ep_reg); + vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler); // Adjust the art_quick_read_barrier_mark_introspection address in kBakerCcEntrypointRegister // to art_quick_read_barrier_mark_introspection_gc_roots. int32_t entrypoint_offset = (width == BakerReadBarrierWidth::kWide) diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 6b9919ab15..d5b739bd7c 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -113,9 +113,6 @@ static const vixl::aarch32::SRegister kRuntimeParameterFpuRegistersVIXL[] = { static const size_t kRuntimeParameterFpuRegistersLengthVIXL = arraysize(kRuntimeParameterFpuRegistersVIXL); -// The reserved entrypoint register for link-time generated thunks. -const vixl::aarch32::Register kBakerCcEntrypointRegister = vixl32::r4; - class LoadClassSlowPathARMVIXL; class CodeGeneratorARMVIXL; @@ -611,10 +608,6 @@ class CodeGeneratorARMVIXL : public CodeGenerator { void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; - // Maybe add the reserved entrypoint register as a temporary for field load. This temp - // is added only for AOT compilation if link-time generated thunks for fields are enabled. - void MaybeAddBakerCcEntrypointTempForFields(LocationSummary* locations); - // Generate a GC root reference load: // // root <- *(obj + offset) @@ -816,7 +809,7 @@ class CodeGeneratorARMVIXL : public CodeGenerator { kBitsForBakerReadBarrierWidth>; static void CheckValidReg(uint32_t reg) { - DCHECK(reg < vixl::aarch32::ip.GetCode() && reg != kBakerCcEntrypointRegister.GetCode()) << reg; + DCHECK(reg < vixl::aarch32::ip.GetCode() && reg != mr.GetCode()) << reg; } static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index 29aecbc097..5287b4b2fa 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -1802,8 +1802,6 @@ void IntrinsicLocationsBuilderARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { // is clobbered by ReadBarrierMarkRegX entry points). Get an extra // temporary register from the register allocator. locations->AddTemp(Location::RequiresRegister()); - CodeGeneratorARMVIXL* arm_codegen = down_cast(codegen_); - arm_codegen->MaybeAddBakerCcEntrypointTempForFields(locations); } } diff --git a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc index e7b11bd16b..3fe97e146c 100644 --- a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc +++ b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc @@ -625,18 +625,23 @@ TEST_F(Thumb2RelativePatcherTest, StringReference4) { ASSERT_LT(GetMethodOffset(1u), 0xfcu); } +const uint32_t kBakerValidRegs[] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 9, 10, 11, // r8 (rMR), IP, SP, LR and PC are reserved. +}; + +const uint32_t kBakerValidRegsNarrow[] = { + 0, 1, 2, 3, 4, 5, 6, 7, +}; + void Thumb2RelativePatcherTest::TestBakerFieldWide(uint32_t offset, uint32_t ref_reg) { - uint32_t valid_regs[] = { - 0, 1, 2, 3, 5, 6, 7, // R4 is reserved for entrypoint address. - 8, 9, 10, 11, // IP, SP, LR and PC are reserved. - }; DCHECK_ALIGNED(offset, 4u); DCHECK_LT(offset, 4 * KB); constexpr size_t kMethodCodeSize = 8u; constexpr size_t kLiteralOffset = 0u; uint32_t method_idx = 0u; - for (uint32_t base_reg : valid_regs) { - for (uint32_t holder_reg : valid_regs) { + for (uint32_t base_reg : kBakerValidRegs) { + for (uint32_t holder_reg : kBakerValidRegs) { uint32_t ldr = kLdrWInsn | offset | (base_reg << 16) | (ref_reg << 12); const std::vector raw_code = RawCode({kBneWPlus0, ldr}); ASSERT_EQ(kMethodCodeSize, raw_code.size()); @@ -655,8 +660,8 @@ void Thumb2RelativePatcherTest::TestBakerFieldWide(uint32_t offset, uint32_t ref // All thunks are at the end. uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment); method_idx = 0u; - for (uint32_t base_reg : valid_regs) { - for (uint32_t holder_reg : valid_regs) { + for (uint32_t base_reg : kBakerValidRegs) { + for (uint32_t holder_reg : kBakerValidRegs) { ++method_idx; uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset); uint32_t ldr = kLdrWInsn | offset | (base_reg << 16) | (ref_reg << 12); @@ -725,20 +730,16 @@ void Thumb2RelativePatcherTest::TestBakerFieldWide(uint32_t offset, uint32_t ref } void Thumb2RelativePatcherTest::TestBakerFieldNarrow(uint32_t offset, uint32_t ref_reg) { - uint32_t valid_regs[] = { - 0, 1, 2, 3, 5, 6, 7, // R4 is reserved for entrypoint address. - 8, 9, 10, 11, // IP, SP, LR and PC are reserved. - }; DCHECK_ALIGNED(offset, 4u); DCHECK_LT(offset, 32u); constexpr size_t kMethodCodeSize = 6u; constexpr size_t kLiteralOffset = 0u; uint32_t method_idx = 0u; - for (uint32_t base_reg : valid_regs) { + for (uint32_t base_reg : kBakerValidRegs) { if (base_reg >= 8u) { continue; } - for (uint32_t holder_reg : valid_regs) { + for (uint32_t holder_reg : kBakerValidRegs) { uint32_t ldr = kLdrInsn | (offset << (6 - 2)) | (base_reg << 3) | ref_reg; const std::vector raw_code = RawCode({kBneWPlus0, ldr}); ASSERT_EQ(kMethodCodeSize, raw_code.size()); @@ -757,11 +758,11 @@ void Thumb2RelativePatcherTest::TestBakerFieldNarrow(uint32_t offset, uint32_t r // All thunks are at the end. uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment); method_idx = 0u; - for (uint32_t base_reg : valid_regs) { + for (uint32_t base_reg : kBakerValidRegs) { if (base_reg >= 8u) { continue; } - for (uint32_t holder_reg : valid_regs) { + for (uint32_t holder_reg : kBakerValidRegs) { ++method_idx; uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset); uint32_t ldr = kLdrInsn | (offset << (6 - 2)) | (base_reg << 3) | ref_reg; @@ -1021,10 +1022,6 @@ TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddleUnreachableFromLast } TEST_F(Thumb2RelativePatcherTest, BakerArray) { - uint32_t valid_regs[] = { - 0, 1, 2, 3, 5, 6, 7, // R4 is reserved for entrypoint address. - 8, 9, 10, 11, // IP, SP, LR and PC are reserved. - }; auto ldr = [](uint32_t base_reg) { uint32_t index_reg = (base_reg == 0u) ? 1u : 0u; uint32_t ref_reg = (base_reg == 2) ? 3u : 2u; @@ -1033,7 +1030,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerArray) { constexpr size_t kMethodCodeSize = 8u; constexpr size_t kLiteralOffset = 0u; uint32_t method_idx = 0u; - for (uint32_t base_reg : valid_regs) { + for (uint32_t base_reg : kBakerValidRegs) { ++method_idx; const std::vector raw_code = RawCode({kBneWPlus0, ldr(base_reg)}); ASSERT_EQ(kMethodCodeSize, raw_code.size()); @@ -1049,7 +1046,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerArray) { // All thunks are at the end. uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment); method_idx = 0u; - for (uint32_t base_reg : valid_regs) { + for (uint32_t base_reg : kBakerValidRegs) { ++method_idx; uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset); const std::vector expected_code = RawCode({bne, ldr(base_reg)}); @@ -1106,14 +1103,10 @@ TEST_F(Thumb2RelativePatcherTest, BakerArray) { } TEST_F(Thumb2RelativePatcherTest, BakerGcRootWide) { - uint32_t valid_regs[] = { - 0, 1, 2, 3, 5, 6, 7, // R4 is reserved for entrypoint address. - 8, 9, 10, 11, // IP, SP, LR and PC are reserved. - }; constexpr size_t kMethodCodeSize = 8u; constexpr size_t kLiteralOffset = 4u; uint32_t method_idx = 0u; - for (uint32_t root_reg : valid_regs) { + for (uint32_t root_reg : kBakerValidRegs) { ++method_idx; uint32_t ldr = kLdrWInsn | (/* offset */ 8) | (/* base_reg */ 0 << 16) | (root_reg << 12); const std::vector raw_code = RawCode({ldr, kBneWPlus0}); @@ -1130,7 +1123,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootWide) { // All thunks are at the end. uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment); method_idx = 0u; - for (uint32_t root_reg : valid_regs) { + for (uint32_t root_reg : kBakerValidRegs) { ++method_idx; uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset); uint32_t ldr = kLdrWInsn | (/* offset */ 8) | (/* base_reg */ 0 << 16) | (root_reg << 12); @@ -1165,14 +1158,10 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootWide) { } TEST_F(Thumb2RelativePatcherTest, BakerGcRootNarrow) { - uint32_t valid_regs[] = { - 0, 1, 2, 3, 5, 6, 7, // R4 is reserved for entrypoint address. - // Not appplicable to high registers. - }; constexpr size_t kMethodCodeSize = 6u; constexpr size_t kLiteralOffset = 2u; uint32_t method_idx = 0u; - for (uint32_t root_reg : valid_regs) { + for (uint32_t root_reg : kBakerValidRegsNarrow) { ++method_idx; uint32_t ldr = kLdrInsn | (/* offset */ 8 << (6 - 2)) | (/* base_reg */ 0 << 3) | root_reg; const std::vector raw_code = RawCode({ldr, kBneWPlus0}); @@ -1189,7 +1178,7 @@ TEST_F(Thumb2RelativePatcherTest, BakerGcRootNarrow) { // All thunks are at the end. uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment); method_idx = 0u; - for (uint32_t root_reg : valid_regs) { + for (uint32_t root_reg : kBakerValidRegsNarrow) { ++method_idx; uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset); uint32_t ldr = kLdrInsn | (/* offset */ 8 << (6 - 2)) | (/* base_reg */ 0 << 3) | root_reg; diff --git a/runtime/arch/arm/asm_support_arm.h b/runtime/arch/arm/asm_support_arm.h index ac17303cf9..7123ae73b4 100644 --- a/runtime/arch/arm/asm_support_arm.h +++ b/runtime/arch/arm/asm_support_arm.h @@ -32,8 +32,8 @@ #define BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET 0x20 // The offsets from art_quick_read_barrier_mark_introspection to the GC root entrypoints, // i.e. art_quick_read_barrier_mark_introspection_gc_roots_{wide,narrow}. -#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET 0x80 -#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET 0xc0 +#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET 0xc0 +#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET 0xe0 // The offset from art_quick_read_barrier_mark_introspection to the array switch cases, // i.e. art_quick_read_barrier_mark_introspection_arrays. #define BAKER_MARK_INTROSPECTION_ARRAY_SWITCH_OFFSET 0x100 diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 0fd239a244..526960b79d 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -2362,23 +2362,19 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg10, r10 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg11, r11 // Helper macros for Baker CC read barrier mark introspection (BRBMI). -.macro BRBMI_FOR_12_REGISTERS macro_for_register, macro_for_reserved_register +.macro BRBMI_FOR_REGISTERS macro_for_register, macro_for_reserved_register \macro_for_register r0 \macro_for_register r1 \macro_for_register r2 \macro_for_register r3 - \macro_for_reserved_register // R4 is reserved for the entrypoint address. + \macro_for_register r4 \macro_for_register r5 \macro_for_register r6 \macro_for_register r7 - \macro_for_register r8 + \macro_for_reserved_register // r8 (rMR) is the marking register. \macro_for_register r9 \macro_for_register r10 \macro_for_register r11 -.endm - -.macro BRBMI_FOR_REGISTERS macro_for_register, macro_for_reserved_register - BRBMI_FOR_12_REGISTERS \macro_for_register, \macro_for_reserved_register \macro_for_reserved_register // IP is reserved. \macro_for_reserved_register // SP is reserved. \macro_for_reserved_register // LR is reserved. @@ -2386,16 +2382,13 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg11, r11 .endm .macro BRBMI_RETURN_SWITCH_CASE reg + .balign 8 .Lmark_introspection_return_switch_case_\reg: + mov rMR, #1 mov \reg, ip bx lr .endm -.macro BRBMI_BAD_RETURN_SWITCH_CASE -.Lmark_introspection_return_switch_case_bad: - BRBMI_BKPT_FILL_4B -.endm - .macro BRBMI_RETURN_SWITCH_CASE_OFFSET reg .byte (.Lmark_introspection_return_switch_case_\reg - .Lmark_introspection_return_table) / 2 .endm @@ -2458,9 +2451,9 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg11, r11 // If reference is null, just return it in the right register. cmp ip, #0 beq .Lmark_introspection_return\label_suffix - // Use R4 as temp and check the mark bit of the reference. - ldr r4, [ip, #MIRROR_OBJECT_LOCK_WORD_OFFSET] - tst r4, #LOCK_WORD_MARK_BIT_MASK_SHIFTED + // Use rMR as temp and check the mark bit of the reference. + ldr rMR, [ip, #MIRROR_OBJECT_LOCK_WORD_OFFSET] + tst rMR, #LOCK_WORD_MARK_BIT_MASK_SHIFTED beq .Lmark_introspection_unmarked\label_suffix .Lmark_introspection_return\label_suffix: .endm @@ -2473,7 +2466,7 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg11, r11 // the highest bits and the "forwarding address" state to have all bits set. #error "Unexpected lock word state shift or forwarding address state value." #endif - cmp r4, #(LOCK_WORD_STATE_FORWARDING_ADDRESS << LOCK_WORD_STATE_SHIFT) + cmp rMR, #(LOCK_WORD_STATE_FORWARDING_ADDRESS << LOCK_WORD_STATE_SHIFT) bhs .Lmark_introspection_forwarding_address\label_suffix .endm @@ -2483,41 +2476,50 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg11, r11 // Shift left by the forwarding address shift. This clears out the state bits since they are // in the top 2 bits of the lock word. - lsl ip, r4, #LOCK_WORD_STATE_FORWARDING_ADDRESS_SHIFT + lsl ip, rMR, #LOCK_WORD_STATE_FORWARDING_ADDRESS_SHIFT b .Lmark_introspection_return\label_suffix .endm .macro BRBMI_LOAD_RETURN_REG_FROM_CODE_wide ldr_offset // Load the half of the instruction that contains Rt. Adjust for the thumb state in LR. - ldrh r4, [lr, #(-1 + \ldr_offset + 2)] + ldrh rMR, [lr, #(-1 + \ldr_offset + 2)] .endm .macro BRBMI_LOAD_RETURN_REG_FROM_CODE_narrow ldr_offset // Load the 16-bit instruction. Adjust for the thumb state in LR. - ldrh r4, [lr, #(-1 + \ldr_offset)] + ldrh rMR, [lr, #(-1 + \ldr_offset)] .endm -.macro BRBMI_GC_ROOT_AND_FIELD_SLOW_PATH gc_root_ldr_offset, label_suffix - .balign 64 +.macro BRBMI_EXTRACT_RETURN_REG_wide + lsr rMR, rMR, #12 // Extract `ref_reg`. +.endm + +.macro BRBMI_EXTRACT_RETURN_REG_narrow + and rMR, rMR, #7 // Extract `ref_reg`. +.endm + +.macro BRBMI_LOAD_AND_EXTRACT_RETURN_REG ldr_offset, label_suffix + BRBMI_LOAD_RETURN_REG_FROM_CODE\label_suffix \ldr_offset + BRBMI_EXTRACT_RETURN_REG\label_suffix +.endm + +.macro BRBMI_GC_ROOT gc_root_ldr_offset, label_suffix + .balign 32 .thumb_func .type art_quick_read_barrier_mark_introspection_gc_roots\label_suffix, #function .hidden art_quick_read_barrier_mark_introspection_gc_roots\label_suffix .global art_quick_read_barrier_mark_introspection_gc_roots\label_suffix art_quick_read_barrier_mark_introspection_gc_roots\label_suffix: - BRBMI_RUNTIME_CALL - // Load the LDR (or the half of it) that contains Rt. - BRBMI_LOAD_RETURN_REG_FROM_CODE\label_suffix \gc_root_ldr_offset - b .Lmark_introspection_extract_register_and_return\label_suffix - // We've used 28 bytes since the "gc_roots" entrypoint (22 bytes for - // BRBMI_RUNTIME_CALL, 4 bytes for LDRH and 2 bytes for the branch). Squeeze - // the 6 byte forwarding address extraction here across the 32-byte boundary. - BRBMI_EXTRACT_FORWARDING_ADDRESS \label_suffix - // And the slow path taking exactly 30 bytes (6 bytes for the forwarding - // address check, 22 bytes for BRBMI_RUNTIME_CALL and 2 bytes for the near - // branch) shall take the rest of the 32-byte section (within a cache line). + BRBMI_LOAD_AND_EXTRACT_RETURN_REG \gc_root_ldr_offset, \label_suffix +.endm + +.macro BRBMI_FIELD_SLOW_PATH ldr_offset, label_suffix + .balign 16 +.Lmark_introspection_unmarked\label_suffix: + // Note: Generates exactly 16 bytes of code. BRBMI_UNMARKED_FORWARDING_ADDRESS_CHECK \label_suffix - BRBMI_RUNTIME_CALL - b .Lmark_introspection_return\label_suffix + BRBMI_LOAD_AND_EXTRACT_RETURN_REG \ldr_offset, \label_suffix + b .Lmark_introspection_runtime_call .endm /* @@ -2540,9 +2542,12 @@ art_quick_read_barrier_mark_introspection_gc_roots\label_suffix: * not do the gray bit check. * * For field accesses and array loads with a constant index the thunk loads - * the reference into IP using introspection and calls the main entrypoint, - * art_quick_read_barrier_mark_introspection. With heap poisoning enabled, - * the passed reference is poisoned. + * the reference into IP using introspection and calls the main entrypoint + * ("wide", for 32-bit LDR) art_quick_read_barrier_mark_introspection or + * the "narrow" entrypoint (for 16-bit LDR). The latter is at a known + * offset (BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET) + * from the main entrypoint and the thunk adjusts the entrypoint pointer. + * With heap poisoning enabled, the passed reference is poisoned. * * For array accesses with non-constant index, the thunk inserts the bits * 0-5 of the LDR instruction to the entrypoint address, effectively @@ -2560,53 +2565,61 @@ art_quick_read_barrier_mark_introspection_gc_roots\label_suffix: * (And even with heap poisoning enabled, GC roots are not poisoned.) * To re-use the same entrypoint pointer in generated code, we make sure * that the gc root entrypoint (a copy of the entrypoint with a different - * offset for introspection loads) is located at a known offset (128 bytes, - * or BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRYPOINT_OFFSET) from the main - * entrypoint and the GC root thunk adjusts the entrypoint pointer, moves - * the root register to IP and jumps to the customized entrypoint, - * art_quick_read_barrier_mark_introspection_gc_roots. The thunk also - * performs all the fast-path checks, so we need just the slow path. + * offset for introspection loads) is located at a known offset (0xc0/0xe0 + * bytes, or BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET/ + * BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET) from the + * main entrypoint and the GC root thunk adjusts the entrypoint pointer, + * moves the root register to IP and jumps to the customized entrypoint, + * art_quick_read_barrier_mark_introspection_gc_roots_{wide,narrow}. + * The thunk also performs all the fast-path checks, so we need just the + * slow path. * * The code structure is - * art_quick_read_barrier_mark_introspection: + * art_quick_read_barrier_mark_introspection: // @0x00 * Up to 32 bytes code for main entrypoint fast-path code for fields * (and array elements with constant offset) with LDR encoding T3; * jumps to the switch in the "narrow" entrypoint. - * Padding to 32 bytes if needed. - * art_quick_read_barrier_mark_introspection_narrow: + * art_quick_read_barrier_mark_introspection_narrow: // @0x20 * Up to 48 bytes code for fast path code for fields (and array * elements with constant offset) with LDR encoding T1, ending in the * return switch instruction TBB and the table with switch offsets. - * Padding to 80 bytes if needed. - * .Lmark_introspection_return_switch_case_r0: - * Exactly 48 bytes of code for the return switch cases (12 cases, - * including BKPT for the reserved registers). - * Ends at 128 bytes total. - * art_quick_read_barrier_mark_introspection_gc_roots_wide: - * GC root entrypoint code for LDR encoding T3 (28 bytes). - * Forwarding address extraction for LDR encoding T3 (6 bytes). - * Slow path for main entrypoint for LDR encoding T3 (30 bytes). - * Ends at 192 bytes total. - * art_quick_read_barrier_mark_introspection_gc_roots_narrow: - * GC root entrypoint code for LDR encoding T1 (28 bytes). - * Forwarding address extraction for LDR encoding T1 (6 bytes). - * Slow path for main entrypoint for LDR encoding T1 (30 bytes). - * Ends at 256 bytes total. - * art_quick_read_barrier_mark_introspection_arrays: + * .Lmark_introspection_return_switch_case_r0: // @0x50 + * Exactly 88 bytes of code for the return switch cases (8 bytes per + * case, 11 cases; no code for reserved registers). + * .Lmark_introspection_forwarding_address_narrow: // @0xa8 + * Exactly 6 bytes to extract the forwarding address and jump to the + * "narrow" entrypoint fast path. + * .Lmark_introspection_return_switch_case_bad: // @0xae + * Exactly 2 bytes, bkpt for unexpected return register. + * .Lmark_introspection_unmarked_narrow: // @0xb0 + * Exactly 16 bytes for "narrow" entrypoint slow path. + * art_quick_read_barrier_mark_introspection_gc_roots_wide: // @0xc0 + * GC root entrypoint code for LDR encoding T3 (10 bytes); loads and + * extracts the return register and jumps to the runtime call. + * .Lmark_introspection_forwarding_address_wide: // @0xca + * Exactly 6 bytes to extract the forwarding address and jump to the + * "wide" entrypoint fast path. + * .Lmark_introspection_unmarked_wide: // @0xd0 + * Exactly 16 bytes for "wide" entrypoint slow path. + * art_quick_read_barrier_mark_introspection_gc_roots_narrow: // @0xe0 + * GC root entrypoint code for LDR encoding T1 (8 bytes); loads and + * extracts the return register and falls through to the runtime call. + * .Lmark_introspection_runtime_call: // @0xe8 + * Exactly 24 bytes for the runtime call to MarkReg() and jump to the + * return switch. + * art_quick_read_barrier_mark_introspection_arrays: // @0x100 * Exactly 128 bytes for array load switch cases (16x2 instructions). */ .balign 512 ENTRY art_quick_read_barrier_mark_introspection - // At this point, IP contains the reference, R4 can be freely used. - // (R4 is reserved for the entrypoint address.) + // At this point, IP contains the reference, rMR is clobbered by the thunk + // and can be freely used as it will be set back to 1 before returning. // For heap poisoning, the reference is poisoned, so unpoison it first. UNPOISON_HEAP_REF ip - // Check for null or marked, lock word is loaded into IP. + // Check for null or marked, lock word is loaded into rMR. BRBMI_CHECK_NULL_AND_MARKED _wide - // Load the half of the instruction that contains Rt. - BRBMI_LOAD_RETURN_REG_FROM_CODE_wide BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET -.Lmark_introspection_extract_register_and_return_wide: - lsr r4, r4, #12 // Extract `ref_reg`. + // Load and extract the return register from the instruction. + BRBMI_LOAD_AND_EXTRACT_RETURN_REG BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET, _wide b .Lmark_introspection_return_switch .balign 32 @@ -2615,25 +2628,45 @@ ENTRY art_quick_read_barrier_mark_introspection .hidden art_quick_read_barrier_mark_introspection_narrow .global art_quick_read_barrier_mark_introspection_narrow art_quick_read_barrier_mark_introspection_narrow: - // At this point, IP contains the reference, R4 can be freely used. - // (R4 is reserved for the entrypoint address.) + // At this point, IP contains the reference, rMR is clobbered by the thunk + // and can be freely used as it will be set back to 1 before returning. // For heap poisoning, the reference is poisoned, so unpoison it first. UNPOISON_HEAP_REF ip - // Check for null or marked, lock word is loaded into R4. + // Check for null or marked, lock word is loaded into rMR. BRBMI_CHECK_NULL_AND_MARKED _narrow - // Load the 16-bit instruction. - BRBMI_LOAD_RETURN_REG_FROM_CODE_narrow BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET -.Lmark_introspection_extract_register_and_return_narrow: - and r4, r4, #7 // Extract `ref_reg`. + // Load and extract the return register from the instruction. + BRBMI_LOAD_AND_EXTRACT_RETURN_REG BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET, _narrow .Lmark_introspection_return_switch: - tbb [pc, r4] // Jump to the switch case. + tbb [pc, rMR] // Jump to the switch case. .Lmark_introspection_return_table: BRBMI_FOR_REGISTERS BRBMI_RETURN_SWITCH_CASE_OFFSET, BRBMI_BAD_RETURN_SWITCH_CASE_OFFSET - .balign 16 - BRBMI_FOR_12_REGISTERS BRBMI_RETURN_SWITCH_CASE, BRBMI_BAD_RETURN_SWITCH_CASE + BRBMI_FOR_REGISTERS BRBMI_RETURN_SWITCH_CASE, /* no code */ + + .balign 8 + BRBMI_EXTRACT_FORWARDING_ADDRESS _narrow // 6 bytes +.Lmark_introspection_return_switch_case_bad: + bkpt // 2 bytes + + BRBMI_FIELD_SLOW_PATH BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET, _narrow + + // 8 bytes for the loading and extracting of the return register. + BRBMI_GC_ROOT BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET, _wide + // 2 bytes for near branch to the runtime call. + b .Lmark_introspection_runtime_call + + BRBMI_EXTRACT_FORWARDING_ADDRESS _wide // Not even 4-byte aligned. + + BRBMI_FIELD_SLOW_PATH BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET, _wide + + // 8 bytes for the loading and extracting of the return register. + BRBMI_GC_ROOT BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET, _narrow + // And the runtime call and branch to the switch taking exactly 24 bytes + // (22 bytes for BRBMI_RUNTIME_CALL and 2 bytes for the near branch) + // shall take the rest of the 32-byte section (within a cache line). +.Lmark_introspection_runtime_call: + BRBMI_RUNTIME_CALL + b .Lmark_introspection_return_switch - BRBMI_GC_ROOT_AND_FIELD_SLOW_PATH BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET, _wide - BRBMI_GC_ROOT_AND_FIELD_SLOW_PATH BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET, _narrow .balign 256 .thumb_func diff --git a/runtime/oat.h b/runtime/oat.h index 01d391401d..0318606f87 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: Retrieve Class* and String* from .data.bimg.rel.ro . - static constexpr uint8_t kOatVersion[] = { '1', '4', '0', '\0' }; + // Last oat version changed reason: Use rMR as temp in Baker RB introspection marking. + static constexpr uint8_t kOatVersion[] = { '1', '4', '1', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; -- GitLab From 5713f668ea0374a1c3020fb466aa0ef6e8c3be7f Mon Sep 17 00:00:00 2001 From: Daniel Colascione Date: Tue, 24 Apr 2018 08:55:18 -0700 Subject: [PATCH 288/749] Use named constant instead of magic number in RegisterLine Test: host Change-Id: I61a8cd8378c9fd6eeafffe2a01fa38f25c01dca8 --- runtime/verifier/register_line.cc | 6 ++++-- runtime/verifier/register_line.h | 11 +++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc index ea30e05487..1bbf5a693a 100644 --- a/runtime/verifier/register_line.cc +++ b/runtime/verifier/register_line.cc @@ -148,7 +148,9 @@ std::string RegisterLine::Dump(MethodVerifier* verifier) const { result += StringPrintf("{%d},", monitor); } for (auto& pairs : reg_to_lock_depths_) { - result += StringPrintf("<%d -> %x>", pairs.first, pairs.second); + result += StringPrintf("<%d -> %" PRIx64 ">", + pairs.first, + static_cast(pairs.second)); } return result; } @@ -337,7 +339,7 @@ void RegisterLine::PushMonitor(MethodVerifier* verifier, uint32_t reg_idx, int32 if (!reg_type.IsReferenceTypes()) { verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-enter on non-object (" << reg_type << ")"; - } else if (monitors_.size() >= 32) { + } else if (monitors_.size() >= kMaxMonitorStackDepth) { verifier->Fail(VERIFY_ERROR_LOCKING); if (kDumpLockFailures) { VLOG(verifier) << "monitor-enter stack overflow while verifying " diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h index 168eb7bb63..9bb60bb7c5 100644 --- a/runtime/verifier/register_line.h +++ b/runtime/verifier/register_line.h @@ -17,6 +17,7 @@ #ifndef ART_RUNTIME_VERIFIER_REGISTER_LINE_H_ #define ART_RUNTIME_VERIFIER_REGISTER_LINE_H_ +#include #include #include @@ -62,8 +63,14 @@ enum class LockOp { // stack of entered monitors (identified by code unit offset). class RegisterLine { public: + using RegisterStackMask = uint32_t; // A map from register to a bit vector of indices into the monitors_ stack. - using RegToLockDepthsMap = ScopedArenaSafeMap; + using RegToLockDepthsMap = ScopedArenaSafeMap; + + // Maximum number of nested monitors to track before giving up and + // taking the slow path. + static constexpr size_t kMaxMonitorStackDepth = + std::numeric_limits::digits; // Create a register line of num_regs registers. static RegisterLine* Create(size_t num_regs, MethodVerifier* verifier); @@ -391,7 +398,7 @@ class RegisterLine { } bool SetRegToLockDepth(size_t reg, size_t depth) { - CHECK_LT(depth, 32u); + CHECK_LT(depth, kMaxMonitorStackDepth); if (IsSetLockDepth(reg, depth)) { return false; // Register already holds lock so locking twice is erroneous. } -- GitLab From bb30d5d917a47d352c209473b3faf332736fa6ae Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 23 Apr 2018 09:59:25 -0700 Subject: [PATCH 289/749] ART: Skip duplicate classes during compilation If a class doesn't resolve to the current dex file, processing can be skipped. It must have been handled before. For a large test app, compiling with -j1, compile phase only: Before After Quicken 14.270 13.541 Speed 156.621 153.591 Bug: 78201536 Test: m test-art-host Change-Id: Ia5df3f7f6da2be114a5e9051c18d5716a5f48515 --- compiler/driver/compiler_driver.cc | 6 ++++++ compiler/verifier_deps_test.cc | 2 ++ 2 files changed, 8 insertions(+) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 41b7e7be47..723c619b3e 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -2160,6 +2160,9 @@ class VerifyClassVisitor : public CompilationVisitor { DCHECK(failure_kind == verifier::FailureKind::kNoFailure) << failure_kind; failure_kind = verifier::FailureKind::kSoftFailure; } + } else if (&klass->GetDexFile() != &dex_file) { + // Skip a duplicate class (as the resolved class is from another, earlier dex file). + return; // Do not update state. } else if (!SkipClass(jclass_loader, dex_file, klass.Get())) { CHECK(klass->IsResolved()) << klass->PrettyClass(); failure_kind = class_linker->VerifyClass(soa.Self(), klass, log_level_); @@ -2804,6 +2807,9 @@ static void CompileDexFile(CompilerDriver* driver, dex_cache = hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)); } else if (SkipClass(jclass_loader, dex_file, klass.Get())) { return; + } else if (&klass->GetDexFile() != &dex_file) { + // Skip a duplicate class (as the resolved class is from another, earlier dex file). + return; // Do not update state. } else { dex_cache = hs.NewHandle(klass->GetDexCache()); } diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 553d131e2f..06f0bcdec7 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -236,6 +236,8 @@ class VerifierDepsTest : public CommonCompilerTest { if (cls == nullptr) { CHECK(soa.Self()->IsExceptionPending()); soa.Self()->ClearException(); + } else if (&cls->GetDexFile() != dex_file) { + // Ignore classes from different dex files. } else if (unverified_classes.find(class_def.class_idx_) == unverified_classes.end()) { ASSERT_EQ(cls->GetStatus(), ClassStatus::kVerified); } else { -- GitLab From e3872ed887afbc4574f69a35f8ec91c73a01f2a9 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 25 Apr 2018 10:46:09 +0100 Subject: [PATCH 290/749] Fix non-Baker build. rMR is defined only for Baker read barriers. Replace the entire introspection marking entrypoint with "bkpt" for other configs so that we do not use rMR if unavailable. Test: m ART_USE_READ_BARRIER=false Bug: 36141117 Change-Id: I12e856a7f0841d342477d1dcbd4f00652ee11efa --- runtime/arch/arm/entrypoints_init_arm.cc | 52 +++++++++++++----------- runtime/arch/arm/quick_entrypoints_arm.S | 6 +++ 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc index 80080e9832..b4e9036084 100644 --- a/runtime/arch/arm/entrypoints_init_arm.cc +++ b/runtime/arch/arm/entrypoints_init_arm.cc @@ -90,30 +90,34 @@ void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_active) { qpoints->pReadBarrierMarkReg10 = is_active ? art_quick_read_barrier_mark_reg10 : nullptr; qpoints->pReadBarrierMarkReg11 = is_active ? art_quick_read_barrier_mark_reg11 : nullptr; - // For the alignment check, strip the Thumb mode bit. - DCHECK_ALIGNED(reinterpret_cast(art_quick_read_barrier_mark_introspection) - 1u, 256u); - // Check the field narrow entrypoint offset from the introspection entrypoint. - intptr_t narrow_diff = - reinterpret_cast(art_quick_read_barrier_mark_introspection_narrow) - - reinterpret_cast(art_quick_read_barrier_mark_introspection); - DCHECK_EQ(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET, narrow_diff); - // Check array switch cases offsets from the introspection entrypoint. - intptr_t array_diff = - reinterpret_cast(art_quick_read_barrier_mark_introspection_arrays) - - reinterpret_cast(art_quick_read_barrier_mark_introspection); - DCHECK_EQ(BAKER_MARK_INTROSPECTION_ARRAY_SWITCH_OFFSET, array_diff); - // Check the GC root entrypoint offsets from the introspection entrypoint. - intptr_t gc_roots_wide_diff = - reinterpret_cast(art_quick_read_barrier_mark_introspection_gc_roots_wide) - - reinterpret_cast(art_quick_read_barrier_mark_introspection); - DCHECK_EQ(BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET, gc_roots_wide_diff); - intptr_t gc_roots_narrow_diff = - reinterpret_cast(art_quick_read_barrier_mark_introspection_gc_roots_narrow) - - reinterpret_cast(art_quick_read_barrier_mark_introspection); - DCHECK_EQ(BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET, gc_roots_narrow_diff); - // The register 12, i.e. IP, is reserved, so there is no art_quick_read_barrier_mark_reg12. - // We're using the entry to hold a pointer to the introspection entrypoint instead. - qpoints->pReadBarrierMarkReg12 = is_active ? art_quick_read_barrier_mark_introspection : nullptr; + if (kUseReadBarrier && kUseBakerReadBarrier) { + // For the alignment check, strip the Thumb mode bit. + DCHECK_ALIGNED(reinterpret_cast(art_quick_read_barrier_mark_introspection) - 1u, + 256u); + // Check the field narrow entrypoint offset from the introspection entrypoint. + intptr_t narrow_diff = + reinterpret_cast(art_quick_read_barrier_mark_introspection_narrow) - + reinterpret_cast(art_quick_read_barrier_mark_introspection); + DCHECK_EQ(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET, narrow_diff); + // Check array switch cases offsets from the introspection entrypoint. + intptr_t array_diff = + reinterpret_cast(art_quick_read_barrier_mark_introspection_arrays) - + reinterpret_cast(art_quick_read_barrier_mark_introspection); + DCHECK_EQ(BAKER_MARK_INTROSPECTION_ARRAY_SWITCH_OFFSET, array_diff); + // Check the GC root entrypoint offsets from the introspection entrypoint. + intptr_t gc_roots_wide_diff = + reinterpret_cast(art_quick_read_barrier_mark_introspection_gc_roots_wide) - + reinterpret_cast(art_quick_read_barrier_mark_introspection); + DCHECK_EQ(BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET, gc_roots_wide_diff); + intptr_t gc_roots_narrow_diff = + reinterpret_cast(art_quick_read_barrier_mark_introspection_gc_roots_narrow) - + reinterpret_cast(art_quick_read_barrier_mark_introspection); + DCHECK_EQ(BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET, gc_roots_narrow_diff); + // The register 12, i.e. IP, is reserved, so there is no art_quick_read_barrier_mark_reg12. + // We're using the entry to hold a pointer to the introspection entrypoint instead. + qpoints->pReadBarrierMarkReg12 = + is_active ? art_quick_read_barrier_mark_introspection : nullptr; + } } void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 526960b79d..a930cc494e 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -2610,6 +2610,7 @@ art_quick_read_barrier_mark_introspection_gc_roots\label_suffix: * art_quick_read_barrier_mark_introspection_arrays: // @0x100 * Exactly 128 bytes for array load switch cases (16x2 instructions). */ +#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER) .balign 512 ENTRY art_quick_read_barrier_mark_introspection // At this point, IP contains the reference, rMR is clobbered by the thunk @@ -2676,6 +2677,11 @@ art_quick_read_barrier_mark_introspection_narrow: art_quick_read_barrier_mark_introspection_arrays: BRBMI_FOR_REGISTERS BRBMI_ARRAY_LOAD, BRBMI_BKPT_FILL_8B END art_quick_read_barrier_mark_introspection +#else // defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER) +ENTRY art_quick_read_barrier_mark_introspection + bkpt // Unreachable. +END art_quick_read_barrier_mark_introspection +#endif // defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER) .extern artInvokePolymorphic ENTRY art_quick_invoke_polymorphic -- GitLab From c24fa5d93102688097eaf5bebaf6c653ca5c168d Mon Sep 17 00:00:00 2001 From: Rico Wind Date: Wed, 25 Apr 2018 12:44:58 +0200 Subject: [PATCH 291/749] Allow INVOKE_VIRTUAL_RANGE as single invoke Test: new D8 dexer works Change-Id: I7480ca3d7a180f76d370de6ebf803ba07c76c5e0 --- profman/profman.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/profman/profman.cc b/profman/profman.cc index f2cec47da3..0ecc88c661 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -841,7 +841,8 @@ class ProfMan FINAL { bool found_invoke = false; for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(*dex_file, code_item)) { - if (inst->Opcode() == Instruction::INVOKE_VIRTUAL) { + if (inst->Opcode() == Instruction::INVOKE_VIRTUAL || + inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE) { if (found_invoke) { LOG(ERROR) << "Multiple invoke INVOKE_VIRTUAL found: " << dex_file->PrettyMethod(method_index); @@ -1314,4 +1315,3 @@ static int profman(int argc, char** argv) { int main(int argc, char **argv) { return art::profman(argc, argv); } - -- GitLab From b6cf2ad4861629fecd55e6d13500b572f413bc7d Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Wed, 25 Apr 2018 16:07:24 +0100 Subject: [PATCH 292/749] Reorder debug logging statements when marking a reference in an unused region. Crash reports do not always contain the full logs and sometimes only show the first line printed with level FATAL. Reorder debug logging to dump essential information about the faulty reference first. Test: art/test.py Bug: 75967215 Change-Id: I3a2448bcf5b3a097fb69fa6137c37e51fe3a97a0 --- runtime/gc/collector/concurrent_copying-inl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/gc/collector/concurrent_copying-inl.h b/runtime/gc/collector/concurrent_copying-inl.h index 6e345fb2f2..b331e975fd 100644 --- a/runtime/gc/collector/concurrent_copying-inl.h +++ b/runtime/gc/collector/concurrent_copying-inl.h @@ -146,8 +146,8 @@ inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref, return MarkUnevacFromSpaceRegion(from_ref, region_space_bitmap_); default: // The reference is in an unused region. - region_space_->DumpNonFreeRegions(LOG_STREAM(FATAL_WITHOUT_ABORT)); LOG(FATAL_WITHOUT_ABORT) << DumpHeapReference(holder, offset, from_ref); + region_space_->DumpNonFreeRegions(LOG_STREAM(FATAL_WITHOUT_ABORT)); heap_->GetVerification()->LogHeapCorruption(holder, offset, from_ref, /* fatal */ true); UNREACHABLE(); } -- GitLab From cf6dfae034ec91dd89829ebca2632ec9fd49ab21 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Wed, 25 Apr 2018 16:23:13 +0100 Subject: [PATCH 293/749] Always enable cyclic region allocation in ART's region space. This mechanism was previously enabled in debug mode only. Test: art/test.py Test: Device boot test Bug: 74064045 Change-Id: Ie719109e3f7975369589c0c5dbfe9fde118cfb02 --- runtime/gc/space/region_space.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h index ab18b1bcb9..811d6db876 100644 --- a/runtime/gc/space/region_space.h +++ b/runtime/gc/space/region_space.h @@ -36,7 +36,7 @@ namespace space { // region space, but from the last allocated region. This allocation // strategy reduces region reuse and should help catch some GC bugs // earlier. -static constexpr bool kCyclicRegionAllocation = kIsDebugBuild; +static constexpr bool kCyclicRegionAllocation = true; // A space that consists of equal-sized regions. class RegionSpace FINAL : public ContinuousMemMapAllocSpace { -- GitLab From e1d644da688d0863b1a0ee70f9db855e9d10d5f6 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Wed, 25 Apr 2018 16:27:05 +0100 Subject: [PATCH 294/749] Always enable dead object poisoning in unevacuated regions. This mechanism was previously enabled in debug mode only. Test: art/test.py Test: Device boot test Bug: 74064045 Change-Id: If8183bb2cf5afefb35e5ebb0b78cd440607fa0bd --- runtime/gc/space/region_space.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc index 74abe1c2ab..0701330e81 100644 --- a/runtime/gc/space/region_space.cc +++ b/runtime/gc/space/region_space.cc @@ -34,7 +34,7 @@ static constexpr uint kEvacuateLivePercentThreshold = 75U; static constexpr bool kProtectClearedRegions = kIsTargetBuild; // Wether we poison memory areas occupied by dead objects in unevacuated regions. -static constexpr bool kPoisonDeadObjectsInUnevacuatedRegions = kIsDebugBuild; +static constexpr bool kPoisonDeadObjectsInUnevacuatedRegions = true; // Special 32-bit value used to poison memory areas occupied by dead // objects in unevacuated regions. Dereferencing this value is expected -- GitLab From 8047d8477c5723ae8cc9411531d0c5d6444ed974 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 25 Apr 2018 08:40:42 -0700 Subject: [PATCH 295/749] ART: Change direct-method overlap detection Avoid allocations in a hash set when trying to ensure that there are no method-ids denoting both direct and virtual methods. Instead use a copy of the item iterator, as the methods are guaranteed to be sorted. Saves 1% of instructions for a compiler-filter=extract compact-dex-level=none compile: Before After Small app 545,345,563 540,654,732 Large app 8,040,713,801 7,956,413,657 Bug: 78568168 Test: m test-art-host Change-Id: Ia2a0fbb82dad5d9fa781cbab46fe543d1dd0645e --- libdexfile/dex/dex_file.h | 7 ------ libdexfile/dex/dex_file_verifier.cc | 33 ++++++++++++++++++----------- libdexfile/dex/dex_file_verifier.h | 4 ++-- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index 4098b4250a..4ca735ad66 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -1346,9 +1346,6 @@ class ClassDataItemIterator { uint32_t field_idx_delta_; // delta of index into the field_ids array for FieldId uint32_t access_flags_; // access flags for the field ClassDataField() : field_idx_delta_(0), access_flags_(0) {} - - private: - DISALLOW_COPY_AND_ASSIGN(ClassDataField); }; ClassDataField field_; @@ -1361,9 +1358,6 @@ class ClassDataItemIterator { uint32_t access_flags_; uint32_t code_off_; ClassDataMethod() : method_idx_delta_(0), access_flags_(0), code_off_(0) {} - - private: - DISALLOW_COPY_AND_ASSIGN(ClassDataMethod); }; ClassDataMethod method_; @@ -1374,7 +1368,6 @@ class ClassDataItemIterator { size_t pos_; // integral number of items passed const uint8_t* ptr_pos_; // pointer into stream of class_data_item uint32_t last_idx_; // last read field or method index to apply delta to - DISALLOW_IMPLICIT_CONSTRUCTORS(ClassDataItemIterator); }; class EncodedArrayValueIterator { diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc index 68bd19ea86..e0bfdcff4b 100644 --- a/libdexfile/dex/dex_file_verifier.cc +++ b/libdexfile/dex/dex_file_verifier.cc @@ -674,9 +674,9 @@ bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, uint32_t class_access_flags, dex::TypeIndex class_type_index, uint32_t code_offset, - std::unordered_set* direct_method_indexes, + ClassDataItemIterator* direct_it, bool expect_direct) { - DCHECK(direct_method_indexes != nullptr); + DCHECK(expect_direct || direct_it != nullptr); // Check for overflow. if (!CheckIndex(idx, header_->method_ids_size_, "class_data_item method_idx")) { return false; @@ -694,11 +694,19 @@ bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, } // Check that it's not defined as both direct and virtual. - if (expect_direct) { - direct_method_indexes->insert(idx); - } else if (direct_method_indexes->find(idx) != direct_method_indexes->end()) { - ErrorStringPrintf("Found virtual method with same index as direct method: %d", idx); - return false; + if (!expect_direct) { + // The direct methods are already known to be in ascending index order. So just keep up + // with the current index. + for (; direct_it->HasNextDirectMethod(); direct_it->Next()) { + uint32_t direct_idx = direct_it->GetMemberIndex(); + if (direct_idx > idx) { + break; + } + if (direct_idx == idx) { + ErrorStringPrintf("Found virtual method with same index as direct method: %d", idx); + return false; + } + } } std::string error_msg; @@ -1146,7 +1154,7 @@ bool DexFileVerifier::CheckIntraClassDataItemFields(ClassDataItemIterator* it, template bool DexFileVerifier::CheckIntraClassDataItemMethods( ClassDataItemIterator* it, - std::unordered_set* direct_method_indexes, + ClassDataItemIterator* direct_it, bool* have_class, dex::TypeIndex* class_type_index, const DexFile::ClassDef** class_def) { @@ -1168,7 +1176,7 @@ bool DexFileVerifier::CheckIntraClassDataItemMethods( (*class_def)->access_flags_, *class_type_index, it->GetMethodCodeItemOffset(), - direct_method_indexes, + direct_it, kDirect)) { return false; } @@ -1181,7 +1189,6 @@ bool DexFileVerifier::CheckIntraClassDataItemMethods( bool DexFileVerifier::CheckIntraClassDataItem() { ClassDataItemIterator it(*dex_file_, ptr_); - std::unordered_set direct_method_indexes; // This code is complicated by the fact that we don't directly know which class this belongs to. // So we need to explicitly search with the first item we find (either field or method), and then, @@ -1205,15 +1212,17 @@ bool DexFileVerifier::CheckIntraClassDataItem() { } // Check methods. + ClassDataItemIterator direct_it = it; + if (!CheckIntraClassDataItemMethods(&it, - &direct_method_indexes, + nullptr /* direct_it */, &have_class, &class_type_index, &class_def)) { return false; } if (!CheckIntraClassDataItemMethods(&it, - &direct_method_indexes, + &direct_it, &have_class, &class_type_index, &class_def)) { diff --git a/libdexfile/dex/dex_file_verifier.h b/libdexfile/dex/dex_file_verifier.h index a80a9d569a..3bddc77338 100644 --- a/libdexfile/dex/dex_file_verifier.h +++ b/libdexfile/dex/dex_file_verifier.h @@ -85,7 +85,7 @@ class DexFileVerifier { uint32_t class_access_flags, dex::TypeIndex class_type_index, uint32_t code_offset, - std::unordered_set* direct_method_indexes, + ClassDataItemIterator* direct_it, bool expect_direct); bool CheckOrderAndGetClassDef(bool is_field, const char* type_descr, @@ -113,7 +113,7 @@ class DexFileVerifier { // method, if necessary (and return it), or use the given values. template bool CheckIntraClassDataItemMethods(ClassDataItemIterator* it, - std::unordered_set* direct_method_indexes, + ClassDataItemIterator* direct_it, bool* have_class, dex::TypeIndex* class_type_index, const DexFile::ClassDef** class_def); -- GitLab From f0cf86fd4085c725b667058b416e0f8a6a7dfcc2 Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Wed, 25 Apr 2018 16:47:25 -0700 Subject: [PATCH 296/749] Fix profile filtered loading The computation for the profile line size of wrong. We need to multiply the number of classes by the sizeof(uint16_t). Test: m test-art-host-gtest Bug: 78596914 Change-Id: I2e84cb0afd70c3c600c72c74997cc3097838a5e0 --- runtime/jit/profile_compilation_info.cc | 2 +- runtime/jit/profile_compilation_info_test.cc | 38 +++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index 7c21916997..d27465dd6e 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -1351,7 +1351,7 @@ ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::LoadInternal( if (!filter_fn(profile_line_headers[k].dex_location, profile_line_headers[k].checksum)) { // We have to skip the line. Advanced the current pointer of the buffer. size_t profile_line_size = - profile_line_headers[k].class_set_size + + profile_line_headers[k].class_set_size * sizeof(uint16_t) + profile_line_headers[k].method_region_size_bytes + DexFileData::ComputeBitmapStorage(profile_line_headers[k].num_method_ids); uncompressed_data.Advance(profile_line_size); diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc index 4e3774e389..0ebadc00fe 100644 --- a/runtime/jit/profile_compilation_info_test.cc +++ b/runtime/jit/profile_compilation_info_test.cc @@ -1160,7 +1160,7 @@ TEST_F(ProfileCompilationInfoTest, FilteredLoading) { ProfileCompilationInfo loaded_info; ASSERT_TRUE(profile.GetFile()->ResetOffset()); - // Filter out dex locations. Keep only dex_location1 and dex_location2. + // Filter out dex locations. Keep only dex_location1 and dex_location3. ProfileCompilationInfo::ProfileLoadFilterFn filter_fn = [](const std::string& dex_location, uint32_t checksum) -> bool { return (dex_location == "dex_location1" && checksum == 1) @@ -1303,4 +1303,40 @@ TEST_F(ProfileCompilationInfoTest, FilteredLoadingKeepAll) { } } +// Regression test: we were failing to do a filtering loading when the filtered dex file +// contained profiled classes. +TEST_F(ProfileCompilationInfoTest, FilteredLoadingWithClasses) { + ScratchFile profile; + + // Save a profile with 2 dex files containing just classes. + ProfileCompilationInfo saved_info; + uint16_t item_count = 1000; + for (uint16_t i = 0; i < item_count; i++) { + ASSERT_TRUE(AddClass("dex_location1", /* checksum */ 1, dex::TypeIndex(i), &saved_info)); + ASSERT_TRUE(AddClass("dex_location2", /* checksum */ 2, dex::TypeIndex(i), &saved_info)); + } + + ASSERT_TRUE(saved_info.Save(GetFd(profile))); + ASSERT_EQ(0, profile.GetFile()->Flush()); + + + // Filter out dex locations: kepp only dex_location2. + ProfileCompilationInfo loaded_info; + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + ProfileCompilationInfo::ProfileLoadFilterFn filter_fn = + [](const std::string& dex_location, uint32_t checksum) -> bool { + return (dex_location == "dex_location2" && checksum == 2); + }; + ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn)); + + // Compute the expectation. + ProfileCompilationInfo expected_info; + for (uint16_t i = 0; i < item_count; i++) { + ASSERT_TRUE(AddClass("dex_location2", /* checksum */ 2, dex::TypeIndex(i), &expected_info)); + } + + // Validate the expectation. + ASSERT_TRUE(loaded_info.Equals(expected_info)); +} + } // namespace art -- GitLab From 63a9f3e9e1b9fb8d98d8ca9abe626f3aa11e5692 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 26 Apr 2018 09:18:10 +0100 Subject: [PATCH 297/749] AOT inlined method lookup should stay within dex file. Rewrite the AOT inlined method lookup and guard against crossing dex file boundary. The compiler does not currently support inlining across dex files, so this is an indication of multiple dex files defining the same class and the AOT code having used a definition which is not used at runtime. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing --jit Test: Pixel 2 XL boots. Test: testrunner.py --target --optimizing --jit Bug: 74410240 Change-Id: Ic2a595ee798e7973156bf29d1a1bcc8615405407 --- runtime/entrypoints/entrypoint_utils-inl.h | 68 ++++++++++------------ 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 270bce2129..137eb4fe1e 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -70,45 +70,41 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, } // Find which method did the call in the inlining hierarchy. - ArtMethod* caller = outer_method; - if (inlining_depth != 0) { - caller = GetResolvedMethod(outer_method, - method_info, - inline_info, - encoding, - inlining_depth - 1); - } - - // Lookup the declaring class of the inlined method. - ObjPtr dex_cache = caller->GetDexCache(); - 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(); - mirror::ClassLoader* class_loader = caller->GetDeclaringClass()->GetClassLoader(); - mirror::Class* klass = class_linker->LookupClass(self, descriptor, class_loader); - if (klass == nullptr) { - LOG(FATAL) << "Could not find an inlined method from an .oat file: the class " << descriptor - << " was not found in the class loader of " << caller->PrettyMethod() << ". " - << "This must be due to playing wrongly with class loaders"; - } - - inlined_method = class_linker->FindResolvedMethod(klass, dex_cache, class_loader, method_index); - if (inlined_method == nullptr) { - LOG(FATAL) << "Could not find an inlined method from an .oat file: the class " << descriptor - << " does not have " << dex_file->GetMethodName(method_id) - << dex_file->GetMethodSignature(method_id) << " declared. " - << "This must be due to duplicate classes or playing wrongly with class loaders"; + ArtMethod* method = outer_method; + for (uint32_t depth = 0, end = inlining_depth + 1u; depth != end; ++depth) { + DCHECK(!inline_info.EncodesArtMethodAtDepth(encoding, depth)); + DCHECK_NE(inline_info.GetDexPcAtDepth(encoding, depth), static_cast(-1)); + method_index = inline_info.GetMethodIndexAtDepth(encoding, method_info, depth); + ArtMethod* inlined_method = class_linker->LookupResolvedMethod(method_index, + method->GetDexCache(), + method->GetClassLoader()); + if (UNLIKELY(inlined_method == nullptr)) { + LOG(FATAL) << "Could not find an inlined method from an .oat file: " + << method->GetDexFile()->PrettyMethod(method_index) << " . " + << "This must be due to duplicate classes or playing wrongly with class loaders"; + UNREACHABLE(); + } + DCHECK(!inlined_method->IsRuntimeMethod()); + if (UNLIKELY(inlined_method->GetDexFile() != method->GetDexFile())) { + // TODO: We could permit inlining within a multi-dex oat file and the boot image, + // even going back from boot image methods to the same oat file. However, this is + // not currently implemented in the compiler. Therefore crossing dex file boundary + // indicates that the inlined definition is not the same as the one used at runtime. + LOG(FATAL) << "Inlined method resolution crossed dex file boundary: from " + << method->PrettyMethod() + << " in " << method->GetDexFile()->GetLocation() << "/" + << static_cast(method->GetDexFile()) + << " to " << inlined_method->PrettyMethod() + << " in " << inlined_method->GetDexFile()->GetLocation() << "/" + << static_cast(inlined_method->GetDexFile()) << ". " + << "This must be due to duplicate classes or playing wrongly with class loaders"; + UNREACHABLE(); + } + method = inlined_method; } - return inlined_method; + return method; } ALWAYS_INLINE inline mirror::Class* CheckObjectAlloc(mirror::Class* klass, -- GitLab From 2477320a8d9de58ede68e2645ea53c10f71dcd57 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Thu, 26 Apr 2018 10:28:51 -0700 Subject: [PATCH 298/749] Step 1 of 2: conditional passes. Rationale: The change adds a return value to Run() in preparation of conditional pass execution. The value returned by Run() is best effort, returning false means no optimizations were applied or no useful information was obtained. I filled in a few cases with more exact information, others still just return true. In addition, it integrates inlining as a regular pass, avoiding the ugly "break" into optimizations1 and optimziations2. Bug: b/78171933, b/74026074 Test: test-art-host,target Change-Id: Ia39c5c83c01dcd79841e4b623917d61c754cf075 --- .../optimizing/bounds_check_elimination.cc | 6 +- .../optimizing/bounds_check_elimination.h | 2 +- compiler/optimizing/cha_guard_optimization.cc | 5 +- compiler/optimizing/cha_guard_optimization.h | 2 +- compiler/optimizing/code_sinking.cc | 5 +- compiler/optimizing/code_sinking.h | 2 +- compiler/optimizing/constant_folding.cc | 3 +- compiler/optimizing/constant_folding.h | 2 +- ...onstructor_fence_redundancy_elimination.cc | 3 +- ...constructor_fence_redundancy_elimination.h | 2 +- compiler/optimizing/dead_code_elimination.cc | 3 +- compiler/optimizing/dead_code_elimination.h | 3 +- compiler/optimizing/gvn.cc | 9 +- compiler/optimizing/gvn.h | 2 +- compiler/optimizing/induction_var_analysis.cc | 3 +- compiler/optimizing/induction_var_analysis.h | 2 +- compiler/optimizing/inliner.cc | 21 ++- compiler/optimizing/inliner.h | 2 +- compiler/optimizing/instruction_simplifier.cc | 13 +- compiler/optimizing/instruction_simplifier.h | 2 +- .../optimizing/instruction_simplifier_arm.cc | 3 +- .../optimizing/instruction_simplifier_arm.h | 2 +- .../instruction_simplifier_arm64.cc | 3 +- .../optimizing/instruction_simplifier_arm64.h | 2 +- .../optimizing/instruction_simplifier_mips.cc | 3 +- .../optimizing/instruction_simplifier_mips.h | 2 +- compiler/optimizing/intrinsics.cc | 5 +- compiler/optimizing/intrinsics.h | 2 +- compiler/optimizing/licm.cc | 5 +- compiler/optimizing/licm.h | 2 +- compiler/optimizing/load_store_analysis.cc | 9 +- compiler/optimizing/load_store_analysis.h | 2 +- compiler/optimizing/load_store_elimination.cc | 9 +- compiler/optimizing/load_store_elimination.h | 2 +- compiler/optimizing/loop_optimization.cc | 14 +- compiler/optimizing/loop_optimization.h | 4 +- compiler/optimizing/optimization.h | 5 +- compiler/optimizing/optimizing_compiler.cc | 139 ++++++------------ .../optimizing/pc_relative_fixups_mips.cc | 7 +- compiler/optimizing/pc_relative_fixups_mips.h | 2 +- compiler/optimizing/pc_relative_fixups_x86.cc | 3 +- compiler/optimizing/pc_relative_fixups_x86.h | 2 +- .../optimizing/reference_type_propagation.cc | 3 +- .../optimizing/reference_type_propagation.h | 2 +- compiler/optimizing/scheduler.cc | 3 +- compiler/optimizing/scheduler.h | 7 +- compiler/optimizing/select_generator.cc | 5 +- compiler/optimizing/select_generator.h | 2 +- compiler/optimizing/sharpening.cc | 3 +- compiler/optimizing/sharpening.h | 2 +- compiler/optimizing/side_effects_analysis.cc | 3 +- compiler/optimizing/side_effects_analysis.h | 2 +- compiler/optimizing/ssa_phi_elimination.cc | 6 +- compiler/optimizing/ssa_phi_elimination.h | 4 +- compiler/optimizing/x86_memory_gen.cc | 3 +- compiler/optimizing/x86_memory_gen.h | 2 +- 56 files changed, 190 insertions(+), 176 deletions(-) diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc index d893cc88c4..dfefa524bf 100644 --- a/compiler/optimizing/bounds_check_elimination.cc +++ b/compiler/optimizing/bounds_check_elimination.cc @@ -1938,9 +1938,9 @@ class BCEVisitor : public HGraphVisitor { DISALLOW_COPY_AND_ASSIGN(BCEVisitor); }; -void BoundsCheckElimination::Run() { +bool BoundsCheckElimination::Run() { if (!graph_->HasBoundsChecks()) { - return; + return false; } // Reverse post order guarantees a node's dominators are visited first. @@ -1968,6 +1968,8 @@ void BoundsCheckElimination::Run() { // Perform cleanup. visitor.Finish(); + + return true; } } // namespace art diff --git a/compiler/optimizing/bounds_check_elimination.h b/compiler/optimizing/bounds_check_elimination.h index 79c67a8c7a..92ab7984c8 100644 --- a/compiler/optimizing/bounds_check_elimination.h +++ b/compiler/optimizing/bounds_check_elimination.h @@ -34,7 +34,7 @@ class BoundsCheckElimination : public HOptimization { side_effects_(side_effects), induction_analysis_(induction_analysis) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kBoundsCheckEliminationPassName = "BCE"; diff --git a/compiler/optimizing/cha_guard_optimization.cc b/compiler/optimizing/cha_guard_optimization.cc index 3addaeecd9..bdc395b52d 100644 --- a/compiler/optimizing/cha_guard_optimization.cc +++ b/compiler/optimizing/cha_guard_optimization.cc @@ -241,14 +241,15 @@ void CHAGuardVisitor::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) { GetGraph()->IncrementNumberOfCHAGuards(); } -void CHAGuardOptimization::Run() { +bool CHAGuardOptimization::Run() { if (graph_->GetNumberOfCHAGuards() == 0) { - return; + return false; } CHAGuardVisitor visitor(graph_); for (HBasicBlock* block : graph_->GetReversePostOrder()) { visitor.VisitBasicBlock(block); } + return true; } } // namespace art diff --git a/compiler/optimizing/cha_guard_optimization.h b/compiler/optimizing/cha_guard_optimization.h index f14e07bd6c..d2c5a344b7 100644 --- a/compiler/optimizing/cha_guard_optimization.h +++ b/compiler/optimizing/cha_guard_optimization.h @@ -30,7 +30,7 @@ class CHAGuardOptimization : public HOptimization { const char* name = kCHAGuardOptimizationPassName) : HOptimization(graph, name) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kCHAGuardOptimizationPassName = "cha_guard_optimization"; diff --git a/compiler/optimizing/code_sinking.cc b/compiler/optimizing/code_sinking.cc index 2e31d35584..d6c97552dc 100644 --- a/compiler/optimizing/code_sinking.cc +++ b/compiler/optimizing/code_sinking.cc @@ -25,11 +25,11 @@ namespace art { -void CodeSinking::Run() { +bool CodeSinking::Run() { HBasicBlock* exit = graph_->GetExitBlock(); if (exit == nullptr) { // Infinite loop, just bail. - return; + return false; } // TODO(ngeoffray): we do not profile branches yet, so use throw instructions // as an indicator of an uncommon branch. @@ -40,6 +40,7 @@ void CodeSinking::Run() { SinkCodeToUncommonBranch(exit_predecessor); } } + return true; } static bool IsInterestingInstruction(HInstruction* instruction) { diff --git a/compiler/optimizing/code_sinking.h b/compiler/optimizing/code_sinking.h index 836d9d4f67..5db0b6dcc5 100644 --- a/compiler/optimizing/code_sinking.h +++ b/compiler/optimizing/code_sinking.h @@ -33,7 +33,7 @@ class CodeSinking : public HOptimization { const char* name = kCodeSinkingPassName) : HOptimization(graph, name, stats) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kCodeSinkingPassName = "code_sinking"; diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc index 6f11e628ee..bb78c2357e 100644 --- a/compiler/optimizing/constant_folding.cc +++ b/compiler/optimizing/constant_folding.cc @@ -68,13 +68,14 @@ class InstructionWithAbsorbingInputSimplifier : public HGraphVisitor { }; -void HConstantFolding::Run() { +bool HConstantFolding::Run() { HConstantFoldingVisitor visitor(graph_); // Process basic blocks in reverse post-order in the dominator tree, // so that an instruction turned into a constant, used as input of // another instruction, may possibly be used to turn that second // instruction into a constant as well. visitor.VisitReversePostOrder(); + return true; } diff --git a/compiler/optimizing/constant_folding.h b/compiler/optimizing/constant_folding.h index 05c6df4a93..f4dbc805c4 100644 --- a/compiler/optimizing/constant_folding.h +++ b/compiler/optimizing/constant_folding.h @@ -41,7 +41,7 @@ class HConstantFolding : public HOptimization { public: HConstantFolding(HGraph* graph, const char* name) : HOptimization(graph, name) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kConstantFoldingPassName = "constant_folding"; diff --git a/compiler/optimizing/constructor_fence_redundancy_elimination.cc b/compiler/optimizing/constructor_fence_redundancy_elimination.cc index 4a66cd2265..1a7f9266e9 100644 --- a/compiler/optimizing/constructor_fence_redundancy_elimination.cc +++ b/compiler/optimizing/constructor_fence_redundancy_elimination.cc @@ -250,13 +250,14 @@ class CFREVisitor : public HGraphVisitor { DISALLOW_COPY_AND_ASSIGN(CFREVisitor); }; -void ConstructorFenceRedundancyElimination::Run() { +bool ConstructorFenceRedundancyElimination::Run() { CFREVisitor cfre_visitor(graph_, stats_); // Arbitrarily visit in reverse-post order. // The exact block visit order does not matter, as the algorithm // only operates on a single block at a time. cfre_visitor.VisitReversePostOrder(); + return true; } } // namespace art diff --git a/compiler/optimizing/constructor_fence_redundancy_elimination.h b/compiler/optimizing/constructor_fence_redundancy_elimination.h index f4b06d5544..367d9f21a0 100644 --- a/compiler/optimizing/constructor_fence_redundancy_elimination.h +++ b/compiler/optimizing/constructor_fence_redundancy_elimination.h @@ -52,7 +52,7 @@ class ConstructorFenceRedundancyElimination : public HOptimization { const char* name = kCFREPassName) : HOptimization(graph, name, stats) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kCFREPassName = "constructor_fence_redundancy_elimination"; diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc index 9fa0f72e80..1dc10948cc 100644 --- a/compiler/optimizing/dead_code_elimination.cc +++ b/compiler/optimizing/dead_code_elimination.cc @@ -508,7 +508,7 @@ void HDeadCodeElimination::RemoveDeadInstructions() { } } -void HDeadCodeElimination::Run() { +bool HDeadCodeElimination::Run() { // Do not eliminate dead blocks if the graph has irreducible loops. We could // support it, but that would require changes in our loop representation to handle // multiple entry points. We decided it was not worth the complexity. @@ -526,6 +526,7 @@ void HDeadCodeElimination::Run() { } SsaRedundantPhiElimination(graph_).Run(); RemoveDeadInstructions(); + return true; } } // namespace art diff --git a/compiler/optimizing/dead_code_elimination.h b/compiler/optimizing/dead_code_elimination.h index 92a7f562e1..90caa53764 100644 --- a/compiler/optimizing/dead_code_elimination.h +++ b/compiler/optimizing/dead_code_elimination.h @@ -32,7 +32,8 @@ class HDeadCodeElimination : public HOptimization { HDeadCodeElimination(HGraph* graph, OptimizingCompilerStats* stats, const char* name) : HOptimization(graph, name, stats) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; + static constexpr const char* kDeadCodeEliminationPassName = "dead_code_elimination"; private: diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc index f05159b735..4863718518 100644 --- a/compiler/optimizing/gvn.cc +++ b/compiler/optimizing/gvn.cc @@ -352,7 +352,7 @@ class GlobalValueNumberer : public ValueObject { visited_blocks_.ClearAllBits(); } - void Run(); + bool Run(); private: // Per-block GVN. Will also update the ValueSet of the dominated and @@ -397,7 +397,7 @@ class GlobalValueNumberer : public ValueObject { DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer); }; -void GlobalValueNumberer::Run() { +bool GlobalValueNumberer::Run() { DCHECK(side_effects_.HasRun()); sets_[graph_->GetEntryBlock()->GetBlockId()] = new (&allocator_) ValueSet(&allocator_); @@ -406,6 +406,7 @@ void GlobalValueNumberer::Run() { for (HBasicBlock* block : graph_->GetReversePostOrder()) { VisitBasicBlock(block); } + return true; } void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) { @@ -557,9 +558,9 @@ HBasicBlock* GlobalValueNumberer::FindVisitedBlockWithRecyclableSet( return secondary_match; } -void GVNOptimization::Run() { +bool GVNOptimization::Run() { GlobalValueNumberer gvn(graph_, side_effects_); - gvn.Run(); + return gvn.Run(); } } // namespace art diff --git a/compiler/optimizing/gvn.h b/compiler/optimizing/gvn.h index 4fdba26ebd..75cfff2140 100644 --- a/compiler/optimizing/gvn.h +++ b/compiler/optimizing/gvn.h @@ -31,7 +31,7 @@ class GVNOptimization : public HOptimization { const char* pass_name = kGlobalValueNumberingPassName) : HOptimization(graph, pass_name), side_effects_(side_effects) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kGlobalValueNumberingPassName = "GVN"; diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index d270c6a28e..a4d638f4c6 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -243,7 +243,7 @@ HInductionVarAnalysis::HInductionVarAnalysis(HGraph* graph, const char* name) graph->GetAllocator()->Adapter(kArenaAllocInductionVarAnalysis)) { } -void HInductionVarAnalysis::Run() { +bool HInductionVarAnalysis::Run() { // Detects sequence variables (generalized induction variables) during an outer to inner // traversal of all loops using Gerlek's algorithm. The order is important to enable // range analysis on outer loop while visiting inner loops. @@ -253,6 +253,7 @@ void HInductionVarAnalysis::Run() { VisitLoop(graph_block->GetLoopInformation()); } } + return !induction_.empty(); } void HInductionVarAnalysis::VisitLoop(HLoopInformation* loop) { diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h index acad77d35f..89fed2ec64 100644 --- a/compiler/optimizing/induction_var_analysis.h +++ b/compiler/optimizing/induction_var_analysis.h @@ -37,7 +37,7 @@ class HInductionVarAnalysis : public HOptimization { public: explicit HInductionVarAnalysis(HGraph* graph, const char* name = kInductionPassName); - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kInductionPassName = "induction_var_analysis"; diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 8b10a78212..3800c96937 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -124,13 +124,18 @@ void HInliner::UpdateInliningBudget() { } } -void HInliner::Run() { - if (graph_->IsDebuggable()) { +bool HInliner::Run() { + if (compiler_driver_->GetCompilerOptions().GetInlineMaxCodeUnits() == 0) { + // Inlining effectively disabled. + return false; + } else if (graph_->IsDebuggable()) { // For simplicity, we currently never inline when the graph is debuggable. This avoids // doing some logic in the runtime to discover if a method could have been inlined. - return; + return false; } + bool didInline = false; + // Initialize the number of instructions for the method being compiled. Recursive calls // to HInliner::Run have already updated the instruction count. if (outermost_graph_ == graph_) { @@ -171,7 +176,9 @@ void HInliner::Run() { call->GetDexMethodIndex(), /* with_signature */ false); // Tests prevent inlining by having $noinline$ in their method names. if (callee_name.find("$noinline$") == std::string::npos) { - if (!TryInline(call) && honor_inline_directives) { + if (TryInline(call)) { + didInline = true; + } else { bool should_have_inlined = (callee_name.find("$inline$") != std::string::npos); CHECK(!should_have_inlined) << "Could not inline " << callee_name; } @@ -179,12 +186,16 @@ void HInliner::Run() { } else { DCHECK(!honor_inline_directives); // Normal case: try to inline. - TryInline(call); + if (TryInline(call)) { + didInline = true; + } } } instruction = next; } } + + return didInline; } static bool IsMethodOrDeclaringClassFinal(ArtMethod* method) diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index 02465d37ba..fb1c9af896 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -60,7 +60,7 @@ class HInliner : public HOptimization { handles_(handles), inline_stats_(nullptr) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kInlinerPassName = "inliner"; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index d3cf9568c2..0fe16725f3 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -42,7 +42,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { compiler_driver_(compiler_driver), stats_(stats) {} - void Run(); + bool Run(); private: void RecordSimplification() { @@ -136,17 +136,18 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { static constexpr int kMaxSamePositionSimplifications = 50; }; -void InstructionSimplifier::Run() { +bool InstructionSimplifier::Run() { if (kTestInstructionClonerExhaustively) { CloneAndReplaceInstructionVisitor visitor(graph_); visitor.VisitReversePostOrder(); } InstructionSimplifierVisitor visitor(graph_, codegen_, compiler_driver_, stats_); - visitor.Run(); + return visitor.Run(); } -void InstructionSimplifierVisitor::Run() { +bool InstructionSimplifierVisitor::Run() { + bool didSimplify = false; // Iterate in reverse post order to open up more simplifications to users // of instructions that got simplified. for (HBasicBlock* block : GetGraph()->GetReversePostOrder()) { @@ -156,10 +157,14 @@ void InstructionSimplifierVisitor::Run() { do { simplification_occurred_ = false; VisitBasicBlock(block); + if (simplification_occurred_) { + didSimplify = true; + } } while (simplification_occurred_ && (simplifications_at_current_position_ < kMaxSamePositionSimplifications)); simplifications_at_current_position_ = 0; } + return didSimplify; } namespace { diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h index 5e2045580b..f409e873de 100644 --- a/compiler/optimizing/instruction_simplifier.h +++ b/compiler/optimizing/instruction_simplifier.h @@ -49,7 +49,7 @@ class InstructionSimplifier : public HOptimization { static constexpr const char* kInstructionSimplifierPassName = "instruction_simplifier"; - void Run() OVERRIDE; + bool Run() OVERRIDE; private: CodeGenerator* codegen_; diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc index 92081e30b1..37fcdb9d5c 100644 --- a/compiler/optimizing/instruction_simplifier_arm.cc +++ b/compiler/optimizing/instruction_simplifier_arm.cc @@ -283,9 +283,10 @@ void InstructionSimplifierArmVisitor::VisitUShr(HUShr* instruction) { } } -void InstructionSimplifierArm::Run() { +bool InstructionSimplifierArm::Run() { InstructionSimplifierArmVisitor visitor(graph_, stats_); visitor.VisitReversePostOrder(); + return true; } } // namespace arm diff --git a/compiler/optimizing/instruction_simplifier_arm.h b/compiler/optimizing/instruction_simplifier_arm.h index 2f6572931f..f1a16efc61 100644 --- a/compiler/optimizing/instruction_simplifier_arm.h +++ b/compiler/optimizing/instruction_simplifier_arm.h @@ -30,7 +30,7 @@ class InstructionSimplifierArm : public HOptimization { static constexpr const char* kInstructionSimplifierArmPassName = "instruction_simplifier_arm"; - void Run() OVERRIDE; + bool Run() OVERRIDE; }; } // namespace arm diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc index 1c44e5ac49..e0a627994d 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.cc +++ b/compiler/optimizing/instruction_simplifier_arm64.cc @@ -278,9 +278,10 @@ void InstructionSimplifierArm64Visitor::VisitVecStore(HVecStore* instruction) { } } -void InstructionSimplifierArm64::Run() { +bool InstructionSimplifierArm64::Run() { InstructionSimplifierArm64Visitor visitor(graph_, stats_); visitor.VisitReversePostOrder(); + return true; } } // namespace arm64 diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h index d180a8dc46..8659c1f5f4 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.h +++ b/compiler/optimizing/instruction_simplifier_arm64.h @@ -30,7 +30,7 @@ class InstructionSimplifierArm64 : public HOptimization { static constexpr const char* kInstructionSimplifierArm64PassName = "instruction_simplifier_arm64"; - void Run() OVERRIDE; + bool Run() OVERRIDE; }; } // namespace arm64 diff --git a/compiler/optimizing/instruction_simplifier_mips.cc b/compiler/optimizing/instruction_simplifier_mips.cc index fa97401a0c..3bdf90f652 100644 --- a/compiler/optimizing/instruction_simplifier_mips.cc +++ b/compiler/optimizing/instruction_simplifier_mips.cc @@ -131,9 +131,10 @@ void InstructionSimplifierMipsVisitor::VisitArraySet(HArraySet* instruction) { } } -void InstructionSimplifierMips::Run() { +bool InstructionSimplifierMips::Run() { InstructionSimplifierMipsVisitor visitor(graph_, codegen_, stats_); visitor.VisitReversePostOrder(); + return true; } } // namespace mips diff --git a/compiler/optimizing/instruction_simplifier_mips.h b/compiler/optimizing/instruction_simplifier_mips.h index 6cb8affe85..94ef73d425 100644 --- a/compiler/optimizing/instruction_simplifier_mips.h +++ b/compiler/optimizing/instruction_simplifier_mips.h @@ -35,7 +35,7 @@ class InstructionSimplifierMips : public HOptimization { static constexpr const char* kInstructionSimplifierMipsPassName = "instruction_simplifier_mips"; - void Run() OVERRIDE; + bool Run() OVERRIDE; private: CodeGeneratorMIPS* codegen_; diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index f8dc316e45..dfe6d791c6 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -178,7 +178,8 @@ bool IntrinsicsRecognizer::Recognize(HInvoke* invoke, return true; } -void IntrinsicsRecognizer::Run() { +bool IntrinsicsRecognizer::Run() { + bool didRecognize = false; ScopedObjectAccess soa(Thread::Current()); for (HBasicBlock* block : graph_->GetReversePostOrder()) { for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done(); @@ -187,6 +188,7 @@ void IntrinsicsRecognizer::Run() { if (inst->IsInvoke()) { bool wrong_invoke_type = false; if (Recognize(inst->AsInvoke(), /* art_method */ nullptr, &wrong_invoke_type)) { + didRecognize = true; MaybeRecordStat(stats_, MethodCompilationStat::kIntrinsicRecognized); } else if (wrong_invoke_type) { LOG(WARNING) @@ -197,6 +199,7 @@ void IntrinsicsRecognizer::Run() { } } } + return didRecognize; } std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) { diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index 1035cbc2c4..30cffac015 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -42,7 +42,7 @@ class IntrinsicsRecognizer : public HOptimization { const char* name = kIntrinsicsRecognizerPassName) : HOptimization(graph, name, stats) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; // Static helper that recognizes intrinsic call. Returns true on success. // If it fails due to invoke type mismatch, wrong_invoke_type is set. diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc index d3a0376e9c..0edb23b857 100644 --- a/compiler/optimizing/licm.cc +++ b/compiler/optimizing/licm.cc @@ -78,7 +78,8 @@ static void UpdateLoopPhisIn(HEnvironment* environment, HLoopInformation* info) } } -void LICM::Run() { +bool LICM::Run() { + bool didLICM = false; DCHECK(side_effects_.HasRun()); // Only used during debug. @@ -157,6 +158,7 @@ void LICM::Run() { } instruction->MoveBefore(pre_header->GetLastInstruction()); MaybeRecordStat(stats_, MethodCompilationStat::kLoopInvariantMoved); + didLICM = true; } if (!can_move && (instruction->CanThrow() || instruction->DoesAnyWrite())) { @@ -167,6 +169,7 @@ void LICM::Run() { } } } + return didLICM; } } // namespace art diff --git a/compiler/optimizing/licm.h b/compiler/optimizing/licm.h index ee567aeb20..f72d195ab2 100644 --- a/compiler/optimizing/licm.h +++ b/compiler/optimizing/licm.h @@ -33,7 +33,7 @@ class LICM : public HOptimization { : HOptimization(graph, name, stats), side_effects_(side_effects) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kLoopInvariantCodeMotionPassName = "licm"; diff --git a/compiler/optimizing/load_store_analysis.cc b/compiler/optimizing/load_store_analysis.cc index 8b1812a6de..7d7bb94933 100644 --- a/compiler/optimizing/load_store_analysis.cc +++ b/compiler/optimizing/load_store_analysis.cc @@ -152,7 +152,7 @@ bool HeapLocationCollector::CanArrayElementsAlias(const HInstruction* idx1, return true; } -void LoadStoreAnalysis::Run() { +bool LoadStoreAnalysis::Run() { for (HBasicBlock* block : graph_->GetReversePostOrder()) { heap_location_collector_.VisitBasicBlock(block); } @@ -160,22 +160,23 @@ void LoadStoreAnalysis::Run() { if (heap_location_collector_.GetNumberOfHeapLocations() > kMaxNumberOfHeapLocations) { // Bail out if there are too many heap locations to deal with. heap_location_collector_.CleanUp(); - return; + return false; } if (!heap_location_collector_.HasHeapStores()) { // Without heap stores, this pass would act mostly as GVN on heap accesses. heap_location_collector_.CleanUp(); - return; + return false; } if (heap_location_collector_.HasVolatile() || heap_location_collector_.HasMonitorOps()) { // Don't do load/store elimination if the method has volatile field accesses or // monitor operations, for now. // TODO: do it right. heap_location_collector_.CleanUp(); - return; + return false; } heap_location_collector_.BuildAliasingMatrix(); + return true; } } // namespace art diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h index 437e6be418..f84846d1b0 100644 --- a/compiler/optimizing/load_store_analysis.h +++ b/compiler/optimizing/load_store_analysis.h @@ -572,7 +572,7 @@ class LoadStoreAnalysis : public HOptimization { return heap_location_collector_; } - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kLoadStoreAnalysisPassName = "load_store_analysis"; diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 237ecd3c10..d598ff592d 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -948,22 +948,22 @@ class LSEVisitor : public HGraphDelegateVisitor { DISALLOW_COPY_AND_ASSIGN(LSEVisitor); }; -void LoadStoreElimination::Run() { +bool LoadStoreElimination::Run() { if (graph_->IsDebuggable() || graph_->HasTryCatch()) { // Debugger may set heap values or trigger deoptimization of callers. // Try/catch support not implemented yet. // Skip this optimization. - return; + return false; } const HeapLocationCollector& heap_location_collector = lsa_.GetHeapLocationCollector(); if (heap_location_collector.GetNumberOfHeapLocations() == 0) { // No HeapLocation information from LSA, skip this optimization. - return; + return false; } // TODO: analyze VecLoad/VecStore better. if (graph_->HasSIMD()) { - return; + return false; } LSEVisitor lse_visitor(graph_, heap_location_collector, side_effects_, stats_); @@ -971,6 +971,7 @@ void LoadStoreElimination::Run() { lse_visitor.VisitBasicBlock(block); } lse_visitor.RemoveInstructions(); + return true; } } // namespace art diff --git a/compiler/optimizing/load_store_elimination.h b/compiler/optimizing/load_store_elimination.h index 7153541baf..408386bd82 100644 --- a/compiler/optimizing/load_store_elimination.h +++ b/compiler/optimizing/load_store_elimination.h @@ -35,7 +35,7 @@ class LoadStoreElimination : public HOptimization { side_effects_(side_effects), lsa_(lsa) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kLoadStoreEliminationPassName = "load_store_elimination"; diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 1462404932..7f1b319c12 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -608,11 +608,11 @@ HLoopOptimization::HLoopOptimization(HGraph* graph, global_allocator_)) { } -void HLoopOptimization::Run() { +bool HLoopOptimization::Run() { // Skip if there is no loop or the graph has try-catch/irreducible loops. // TODO: make this less of a sledgehammer. if (!graph_->HasLoops() || graph_->HasTryCatch() || graph_->HasIrreducibleLoops()) { - return; + return false; } // Phase-local allocator. @@ -620,7 +620,7 @@ void HLoopOptimization::Run() { loop_allocator_ = &allocator; // Perform loop optimizations. - LocalRun(); + bool didLoopOpt = LocalRun(); if (top_loop_ == nullptr) { graph_->SetHasLoops(false); // no more loops } @@ -628,13 +628,16 @@ void HLoopOptimization::Run() { // Detach. loop_allocator_ = nullptr; last_loop_ = top_loop_ = nullptr; + + return didLoopOpt; } // // Loop setup and traversal. // -void HLoopOptimization::LocalRun() { +bool HLoopOptimization::LocalRun() { + bool didLoopOpt = false; // Build the linear order using the phase-local allocator. This step enables building // a loop hierarchy that properly reflects the outer-inner and previous-next relation. ScopedArenaVector linear_order(loop_allocator_->Adapter(kArenaAllocLinearOrder)); @@ -666,7 +669,7 @@ void HLoopOptimization::LocalRun() { vector_map_ = ↦ vector_permanent_map_ = &perm; // Traverse. - TraverseLoopsInnerToOuter(top_loop_); + didLoopOpt = TraverseLoopsInnerToOuter(top_loop_); // Detach. iset_ = nullptr; reductions_ = nullptr; @@ -674,6 +677,7 @@ void HLoopOptimization::LocalRun() { vector_map_ = nullptr; vector_permanent_map_ = nullptr; } + return didLoopOpt; } void HLoopOptimization::AddLoop(HLoopInformation* loop_info) { diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h index f9a31a34d4..11e969875e 100644 --- a/compiler/optimizing/loop_optimization.h +++ b/compiler/optimizing/loop_optimization.h @@ -43,7 +43,7 @@ class HLoopOptimization : public HOptimization { OptimizingCompilerStats* stats, const char* name = kLoopOptimizationPassName); - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kLoopOptimizationPassName = "loop_optimization"; @@ -123,7 +123,7 @@ class HLoopOptimization : public HOptimization { // Loop setup and traversal. // - void LocalRun(); + bool LocalRun(); void AddLoop(HLoopInformation* loop_info); void RemoveLoop(LoopNode* node); diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h index c170f155fa..b00d686e5f 100644 --- a/compiler/optimizing/optimization.h +++ b/compiler/optimizing/optimization.h @@ -47,8 +47,9 @@ class HOptimization : public ArenaObject { // 'instruction_simplifier$before_codegen'. const char* GetPassName() const { return pass_name_; } - // Perform the analysis itself. - virtual void Run() = 0; + // Perform the pass or analysis. Returns false if no optimizations occurred or no useful + // information was computed (this is best effort, returning true is always ok). + virtual bool Run() = 0; protected: HGraph* const graph_; diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index cadefc3b01..f68bcbe59f 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -294,7 +294,7 @@ class OptimizingCompiler FINAL : public Compiler { REQUIRES_SHARED(Locks::mutator_lock_); private: - void RunOptimizations(HGraph* graph, + bool RunOptimizations(HGraph* graph, CodeGenerator* codegen, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, @@ -314,20 +314,22 @@ class OptimizingCompiler FINAL : public Compiler { handles); DCHECK_EQ(length, optimizations.size()); // Run the optimization passes one by one. + bool change = false; for (size_t i = 0; i < length; ++i) { PassScope scope(optimizations[i]->GetPassName(), pass_observer); - optimizations[i]->Run(); + change |= optimizations[i]->Run(); } + return change; } - template void RunOptimizations( + template bool RunOptimizations( HGraph* graph, CodeGenerator* codegen, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, VariableSizedHandleScope* handles, const OptimizationDef (&definitions)[length]) const { - RunOptimizations( + return RunOptimizations( graph, codegen, dex_compilation_unit, pass_observer, handles, definitions, length); } @@ -366,13 +368,7 @@ class OptimizingCompiler FINAL : public Compiler { ArtMethod* method, VariableSizedHandleScope* handles) const; - void MaybeRunInliner(HGraph* graph, - CodeGenerator* codegen, - const DexCompilationUnit& dex_compilation_unit, - PassObserver* pass_observer, - VariableSizedHandleScope* handles) const; - - void RunArchOptimizations(HGraph* graph, + bool RunArchOptimizations(HGraph* graph, CodeGenerator* codegen, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, @@ -435,28 +431,7 @@ static bool IsInstructionSetSupported(InstructionSet instruction_set) { || instruction_set == InstructionSet::kX86_64; } -void OptimizingCompiler::MaybeRunInliner(HGraph* graph, - CodeGenerator* codegen, - const DexCompilationUnit& dex_compilation_unit, - PassObserver* pass_observer, - VariableSizedHandleScope* handles) const { - const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); - bool should_inline = (compiler_options.GetInlineMaxCodeUnits() > 0); - if (!should_inline) { - return; - } - OptimizationDef optimizations[] = { - OptDef(OptimizationPass::kInliner) - }; - RunOptimizations(graph, - codegen, - dex_compilation_unit, - pass_observer, - handles, - optimizations); -} - -void OptimizingCompiler::RunArchOptimizations(HGraph* graph, +bool OptimizingCompiler::RunArchOptimizations(HGraph* graph, CodeGenerator* codegen, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, @@ -471,13 +446,12 @@ void OptimizingCompiler::RunArchOptimizations(HGraph* graph, OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), OptDef(OptimizationPass::kScheduling) }; - RunOptimizations(graph, - codegen, - dex_compilation_unit, - pass_observer, - handles, - arm_optimizations); - break; + return RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + arm_optimizations); } #endif #ifdef ART_ENABLE_CODEGEN_arm64 @@ -488,13 +462,12 @@ void OptimizingCompiler::RunArchOptimizations(HGraph* graph, OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), OptDef(OptimizationPass::kScheduling) }; - RunOptimizations(graph, - codegen, - dex_compilation_unit, - pass_observer, - handles, - arm64_optimizations); - break; + return RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + arm64_optimizations); } #endif #ifdef ART_ENABLE_CODEGEN_mips @@ -505,13 +478,12 @@ void OptimizingCompiler::RunArchOptimizations(HGraph* graph, OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), OptDef(OptimizationPass::kPcRelativeFixupsMips) }; - RunOptimizations(graph, - codegen, - dex_compilation_unit, - pass_observer, - handles, - mips_optimizations); - break; + return RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + mips_optimizations); } #endif #ifdef ART_ENABLE_CODEGEN_mips64 @@ -520,13 +492,12 @@ void OptimizingCompiler::RunArchOptimizations(HGraph* graph, OptDef(OptimizationPass::kSideEffectsAnalysis), OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch") }; - RunOptimizations(graph, - codegen, - dex_compilation_unit, - pass_observer, - handles, - mips64_optimizations); - break; + return RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + mips64_optimizations); } #endif #ifdef ART_ENABLE_CODEGEN_x86 @@ -537,13 +508,12 @@ void OptimizingCompiler::RunArchOptimizations(HGraph* graph, OptDef(OptimizationPass::kPcRelativeFixupsX86), OptDef(OptimizationPass::kX86MemoryOperandGeneration) }; - RunOptimizations(graph, - codegen, - dex_compilation_unit, - pass_observer, - handles, - x86_optimizations); - break; + return RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + x86_optimizations); } #endif #ifdef ART_ENABLE_CODEGEN_x86_64 @@ -553,17 +523,16 @@ void OptimizingCompiler::RunArchOptimizations(HGraph* graph, OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), OptDef(OptimizationPass::kX86MemoryOperandGeneration) }; - RunOptimizations(graph, - codegen, - dex_compilation_unit, - pass_observer, - handles, - x86_64_optimizations); - break; + return RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + x86_64_optimizations); } #endif default: - break; + return false; } } @@ -626,23 +595,13 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph, return; } - OptimizationDef optimizations1[] = { + OptimizationDef optimizations[] = { OptDef(OptimizationPass::kIntrinsicsRecognizer), OptDef(OptimizationPass::kSharpening), OptDef(OptimizationPass::kConstantFolding), OptDef(OptimizationPass::kInstructionSimplifier), - OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$initial") - }; - RunOptimizations(graph, - codegen, - dex_compilation_unit, - pass_observer, - handles, - optimizations1); - - MaybeRunInliner(graph, codegen, dex_compilation_unit, pass_observer, handles); - - OptimizationDef optimizations2[] = { + OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$initial"), + OptDef(OptimizationPass::kInliner), OptDef(OptimizationPass::kSideEffectsAnalysis, "side_effects$before_gvn"), OptDef(OptimizationPass::kGlobalValueNumbering), OptDef(OptimizationPass::kSelectGenerator), @@ -676,7 +635,7 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph, dex_compilation_unit, pass_observer, handles, - optimizations2); + optimizations); RunArchOptimizations(graph, codegen, dex_compilation_unit, pass_observer, handles); } diff --git a/compiler/optimizing/pc_relative_fixups_mips.cc b/compiler/optimizing/pc_relative_fixups_mips.cc index 0102254206..f18ecc1458 100644 --- a/compiler/optimizing/pc_relative_fixups_mips.cc +++ b/compiler/optimizing/pc_relative_fixups_mips.cc @@ -128,20 +128,21 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { HMipsComputeBaseMethodAddress* base_; }; -void PcRelativeFixups::Run() { +bool PcRelativeFixups::Run() { CodeGeneratorMIPS* mips_codegen = down_cast(codegen_); if (mips_codegen->GetInstructionSetFeatures().IsR6()) { // Do nothing for R6 because it has PC-relative addressing. - return; + return false; } if (graph_->HasIrreducibleLoops()) { // Do not run this optimization, as irreducible loops do not work with an instruction // that can be live-in at the irreducible loop header. - return; + return false; } PCRelativeHandlerVisitor visitor(graph_, codegen_); visitor.VisitInsertionOrder(); visitor.MoveBaseIfNeeded(); + return true; } } // namespace mips diff --git a/compiler/optimizing/pc_relative_fixups_mips.h b/compiler/optimizing/pc_relative_fixups_mips.h index ec2c711f8d..6dd1ee0db2 100644 --- a/compiler/optimizing/pc_relative_fixups_mips.h +++ b/compiler/optimizing/pc_relative_fixups_mips.h @@ -34,7 +34,7 @@ class PcRelativeFixups : public HOptimization { static constexpr const char* kPcRelativeFixupsMipsPassName = "pc_relative_fixups_mips"; - void Run() OVERRIDE; + bool Run() OVERRIDE; private: CodeGenerator* codegen_; diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc index 647336b6b9..9049457da5 100644 --- a/compiler/optimizing/pc_relative_fixups_x86.cc +++ b/compiler/optimizing/pc_relative_fixups_x86.cc @@ -256,10 +256,11 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { HX86ComputeBaseMethodAddress* base_; }; -void PcRelativeFixups::Run() { +bool PcRelativeFixups::Run() { PCRelativeHandlerVisitor visitor(graph_, codegen_); visitor.VisitInsertionOrder(); visitor.MoveBaseIfNeeded(); + return true; } } // namespace x86 diff --git a/compiler/optimizing/pc_relative_fixups_x86.h b/compiler/optimizing/pc_relative_fixups_x86.h index 72fa71ea94..db56b7f053 100644 --- a/compiler/optimizing/pc_relative_fixups_x86.h +++ b/compiler/optimizing/pc_relative_fixups_x86.h @@ -34,7 +34,7 @@ class PcRelativeFixups : public HOptimization { static constexpr const char* kPcRelativeFixupsX86PassName = "pc_relative_fixups_x86"; - void Run() OVERRIDE; + bool Run() OVERRIDE; private: CodeGenerator* codegen_; diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 4030883a57..c47c69af67 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -348,7 +348,7 @@ static void BoundTypeForClassCheck(HInstruction* check) { } } -void ReferenceTypePropagation::Run() { +bool ReferenceTypePropagation::Run() { RTPVisitor visitor(graph_, class_loader_, hint_dex_cache_, &handle_cache_, is_first_run_); // To properly propagate type info we need to visit in the dominator-based order. @@ -360,6 +360,7 @@ void ReferenceTypePropagation::Run() { visitor.ProcessWorklist(); ValidateTypes(); + return true; } void ReferenceTypePropagation::RTPVisitor::VisitBasicBlock(HBasicBlock* block) { diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h index fd4dad2b45..400852f4dc 100644 --- a/compiler/optimizing/reference_type_propagation.h +++ b/compiler/optimizing/reference_type_propagation.h @@ -40,7 +40,7 @@ class ReferenceTypePropagation : public HOptimization { // Visit a single instruction. void Visit(HInstruction* instruction); - void Run() OVERRIDE; + bool Run() OVERRIDE; // Returns true if klass is admissible to the propagation: non-null and resolved. // For an array type, we also check if the component type is admissible. diff --git a/compiler/optimizing/scheduler.cc b/compiler/optimizing/scheduler.cc index bca538fb17..e014efaf5c 100644 --- a/compiler/optimizing/scheduler.cc +++ b/compiler/optimizing/scheduler.cc @@ -774,7 +774,7 @@ bool HScheduler::IsSchedulingBarrier(const HInstruction* instr) const { instr->IsSuspendCheck(); } -void HInstructionScheduling::Run(bool only_optimize_loop_blocks, +bool HInstructionScheduling::Run(bool only_optimize_loop_blocks, bool schedule_randomly) { #if defined(ART_ENABLE_CODEGEN_arm64) || defined(ART_ENABLE_CODEGEN_arm) // Phase-local allocator that allocates scheduler internal data structures like @@ -814,6 +814,7 @@ void HInstructionScheduling::Run(bool only_optimize_loop_blocks, default: break; } + return true; } } // namespace art diff --git a/compiler/optimizing/scheduler.h b/compiler/optimizing/scheduler.h index dfa077f7de..51cd20aea9 100644 --- a/compiler/optimizing/scheduler.h +++ b/compiler/optimizing/scheduler.h @@ -508,10 +508,11 @@ class HInstructionScheduling : public HOptimization { codegen_(cg), instruction_set_(instruction_set) {} - void Run() { - Run(/*only_optimize_loop_blocks*/ true, /*schedule_randomly*/ false); + bool Run() OVERRIDE { + return Run(/*only_optimize_loop_blocks*/ true, /*schedule_randomly*/ false); } - void Run(bool only_optimize_loop_blocks, bool schedule_randomly); + + bool Run(bool only_optimize_loop_blocks, bool schedule_randomly); static constexpr const char* kInstructionSchedulingPassName = "scheduler"; diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc index f9acf5aa9a..0d0f7cc748 100644 --- a/compiler/optimizing/select_generator.cc +++ b/compiler/optimizing/select_generator.cc @@ -90,7 +90,8 @@ static HPhi* GetSingleChangedPhi(HBasicBlock* block, size_t index1, size_t index return select_phi; } -void HSelectGenerator::Run() { +bool HSelectGenerator::Run() { + bool didSelect = false; // Select cache with local allocator. ScopedArenaAllocator allocator(graph_->GetArenaStack()); ScopedArenaSafeMap cache( @@ -211,7 +212,9 @@ void HSelectGenerator::Run() { // entry block. Any following blocks would have had the join block // as a dominator, and `MergeWith` handles changing that to the // entry block. + didSelect = true; } + return didSelect; } } // namespace art diff --git a/compiler/optimizing/select_generator.h b/compiler/optimizing/select_generator.h index bda57fd5c8..d24d2264b2 100644 --- a/compiler/optimizing/select_generator.h +++ b/compiler/optimizing/select_generator.h @@ -68,7 +68,7 @@ class HSelectGenerator : public HOptimization { OptimizingCompilerStats* stats, const char* name = kSelectGeneratorPassName); - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kSelectGeneratorPassName = "select_generator"; diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index 70b45763af..6541043046 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -36,7 +36,7 @@ namespace art { -void HSharpening::Run() { +bool HSharpening::Run() { // We don't care about the order of the blocks here. for (HBasicBlock* block : graph_->GetReversePostOrder()) { for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { @@ -51,6 +51,7 @@ void HSharpening::Run() { // because we know the type better when inlining. } } + return true; } static bool IsInBootImage(ArtMethod* method) { diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h index fa3e948eeb..9ccbcaf220 100644 --- a/compiler/optimizing/sharpening.h +++ b/compiler/optimizing/sharpening.h @@ -40,7 +40,7 @@ class HSharpening : public HOptimization { codegen_(codegen), compiler_driver_(compiler_driver) { } - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kSharpeningPassName = "sharpening"; diff --git a/compiler/optimizing/side_effects_analysis.cc b/compiler/optimizing/side_effects_analysis.cc index 6d82e8e06d..ba97b43de9 100644 --- a/compiler/optimizing/side_effects_analysis.cc +++ b/compiler/optimizing/side_effects_analysis.cc @@ -18,7 +18,7 @@ namespace art { -void SideEffectsAnalysis::Run() { +bool SideEffectsAnalysis::Run() { // Inlining might have created more blocks, so we need to increase the size // if needed. block_effects_.resize(graph_->GetBlocks().size()); @@ -69,6 +69,7 @@ void SideEffectsAnalysis::Run() { } } has_run_ = true; + return true; } SideEffects SideEffectsAnalysis::GetLoopEffects(HBasicBlock* block) const { diff --git a/compiler/optimizing/side_effects_analysis.h b/compiler/optimizing/side_effects_analysis.h index c0f81a9c54..56a01e63f1 100644 --- a/compiler/optimizing/side_effects_analysis.h +++ b/compiler/optimizing/side_effects_analysis.h @@ -37,7 +37,7 @@ class SideEffectsAnalysis : public HOptimization { SideEffects GetBlockEffects(HBasicBlock* block) const; // Compute side effects of individual blocks and loops. - void Run(); + bool Run(); bool HasRun() const { return has_run_; } diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc index cb27ded17a..5370f43b4f 100644 --- a/compiler/optimizing/ssa_phi_elimination.cc +++ b/compiler/optimizing/ssa_phi_elimination.cc @@ -23,9 +23,10 @@ namespace art { -void SsaDeadPhiElimination::Run() { +bool SsaDeadPhiElimination::Run() { MarkDeadPhis(); EliminateDeadPhis(); + return true; } void SsaDeadPhiElimination::MarkDeadPhis() { @@ -122,7 +123,7 @@ void SsaDeadPhiElimination::EliminateDeadPhis() { } } -void SsaRedundantPhiElimination::Run() { +bool SsaRedundantPhiElimination::Run() { // Use local allocator for allocating memory used by this optimization. ScopedArenaAllocator allocator(graph_->GetArenaStack()); @@ -255,6 +256,7 @@ void SsaRedundantPhiElimination::Run() { current->GetBlock()->RemovePhi(current); } } + return true; } } // namespace art diff --git a/compiler/optimizing/ssa_phi_elimination.h b/compiler/optimizing/ssa_phi_elimination.h index 11d5837eb5..ee859e834c 100644 --- a/compiler/optimizing/ssa_phi_elimination.h +++ b/compiler/optimizing/ssa_phi_elimination.h @@ -31,7 +31,7 @@ class SsaDeadPhiElimination : public HOptimization { explicit SsaDeadPhiElimination(HGraph* graph) : HOptimization(graph, kSsaDeadPhiEliminationPassName) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; void MarkDeadPhis(); void EliminateDeadPhis(); @@ -53,7 +53,7 @@ class SsaRedundantPhiElimination : public HOptimization { explicit SsaRedundantPhiElimination(HGraph* graph) : HOptimization(graph, kSsaRedundantPhiEliminationPassName) {} - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kSsaRedundantPhiEliminationPassName = "redundant_phi_elimination"; diff --git a/compiler/optimizing/x86_memory_gen.cc b/compiler/optimizing/x86_memory_gen.cc index 0271850f29..f0069c0e09 100644 --- a/compiler/optimizing/x86_memory_gen.cc +++ b/compiler/optimizing/x86_memory_gen.cc @@ -76,9 +76,10 @@ X86MemoryOperandGeneration::X86MemoryOperandGeneration(HGraph* graph, do_implicit_null_checks_(codegen->GetCompilerOptions().GetImplicitNullChecks()) { } -void X86MemoryOperandGeneration::Run() { +bool X86MemoryOperandGeneration::Run() { MemoryOperandVisitor visitor(graph_, do_implicit_null_checks_); visitor.VisitInsertionOrder(); + return true; } } // namespace x86 diff --git a/compiler/optimizing/x86_memory_gen.h b/compiler/optimizing/x86_memory_gen.h index 5f15d9f1e6..b254000f28 100644 --- a/compiler/optimizing/x86_memory_gen.h +++ b/compiler/optimizing/x86_memory_gen.h @@ -31,7 +31,7 @@ class X86MemoryOperandGeneration : public HOptimization { CodeGenerator* codegen, OptimizingCompilerStats* stats); - void Run() OVERRIDE; + bool Run() OVERRIDE; static constexpr const char* kX86MemoryOperandGenerationPassName = "x86_memory_operand_generation"; -- GitLab From 82d046e7c9daad9b706cbec7df7025c5a4ef9163 Mon Sep 17 00:00:00 2001 From: David Sehr Date: Mon, 23 Apr 2018 08:14:19 -0700 Subject: [PATCH 299/749] Add a profiling library Move profile_compilation_info to a separate library. Another step towards building many of our tools without libart[d]. Bug: 78459333 Test: make -j 50 checkbuild Change-Id: Ib281d3d1fde6d06ebb429c5d39d62a7038af0f44 --- Android.bp | 1 + compiler/Android.bp | 3 + compiler/driver/compiler_driver.cc | 2 +- compiler/driver/compiler_driver_test.cc | 2 +- compiler/optimizing/inliner.h | 2 +- dex2oat/Android.bp | 6 ++ dex2oat/dex2oat.cc | 2 +- dex2oat/dex2oat_image_test.cc | 2 +- dex2oat/dex2oat_test.cc | 2 +- dex2oat/linker/oat_writer.cc | 2 +- dex2oat/linker/oat_writer_test.cc | 2 +- dexlayout/Android.bp | 9 +- dexlayout/dex_visualize.cc | 2 +- dexlayout/dexlayout.cc | 2 +- dexlayout/dexlayout_main.cc | 2 +- dexlayout/dexlayout_test.cc | 2 +- libprofile/Android.bp | 102 ++++++++++++++++++ .../profile}/profile_compilation_info.cc | 17 ++- .../profile}/profile_compilation_info.h | 8 +- .../profile}/profile_compilation_info_test.cc | 2 +- oatdump/Android.bp | 4 + profman/Android.bp | 5 + profman/boot_image_profile.cc | 2 +- profman/profile_assistant.h | 2 +- profman/profile_assistant_test.cc | 2 +- profman/profman.cc | 3 +- runtime/Android.bp | 4 +- runtime/class_linker.cc | 2 +- runtime/jit/jit.cc | 2 +- runtime/jit/jit_code_cache.cc | 2 +- runtime/jit/profile_saver.cc | 7 +- runtime/jit/profile_saver.h | 2 +- test/595-profile-saving/profile-saving.cc | 2 +- test/Android.bp | 9 ++ test/common/runtime_state.cc | 2 +- 35 files changed, 182 insertions(+), 40 deletions(-) create mode 100644 libprofile/Android.bp rename {runtime/jit => libprofile/profile}/profile_compilation_info.cc (99%) rename {runtime/jit => libprofile/profile}/profile_compilation_info.h (99%) rename {runtime/jit => libprofile/profile}/profile_compilation_info_test.cc (99%) diff --git a/Android.bp b/Android.bp index e09b7740c1..32a96e13ba 100644 --- a/Android.bp +++ b/Android.bp @@ -37,6 +37,7 @@ subdirs = [ "imgdiag", "libartbase", "libdexfile", + "libprofile", "oatdump", "openjdkjvm", "openjdkjvmti", diff --git a/compiler/Android.bp b/compiler/Android.bp index ec9fef7492..5884a548be 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -244,6 +244,7 @@ art_cc_library { }, shared_libs: [ "libart", + "libprofile", "libdexfile", ], @@ -292,6 +293,7 @@ art_cc_library { }, shared_libs: [ "libartd", + "libprofiled", "libdexfiled", ], } @@ -408,6 +410,7 @@ art_cc_test { ], shared_libs: [ + "libprofiled", "libartd-compiler", "libartd-simulator-container", "libvixld-arm", diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 41b7e7be47..fbbb4e960f 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -57,7 +57,6 @@ #include "gc/space/space.h" #include "handle_scope-inl.h" #include "intrinsics_enum.h" -#include "jit/profile_compilation_info.h" #include "jni_internal.h" #include "linker/linker_patch.h" #include "mirror/class-inl.h" @@ -69,6 +68,7 @@ #include "mirror/throwable.h" #include "nativehelper/ScopedLocalRef.h" #include "object_lock.h" +#include "profile/profile_compilation_info.h" #include "runtime.h" #include "runtime_intrinsics.h" #include "scoped_thread_state_change-inl.h" diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index 1332280d20..856cb36266 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -30,12 +30,12 @@ #include "dex/dex_file_types.h" #include "gc/heap.h" #include "handle_scope-inl.h" -#include "jit/profile_compilation_info.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/dex_cache-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" +#include "profile/profile_compilation_info.h" #include "scoped_thread_state_change-inl.h" namespace art { diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index 02465d37ba..6db3b13e2c 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -19,8 +19,8 @@ #include "dex/dex_file_types.h" #include "dex/invoke_type.h" -#include "jit/profile_compilation_info.h" #include "optimization.h" +#include "profile/profile_compilation_info.h" namespace art { diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index 623732f9ad..4fafca9e1b 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -70,6 +70,7 @@ art_cc_defaults { }, generated_sources: ["art_dex2oat_operator_srcs"], shared_libs: [ + "libprofile", "libbase", "liblz4", "liblzma", @@ -196,6 +197,7 @@ art_cc_binary { "dex2oat-pgo-defaults", ], shared_libs: [ + "libprofile", "libart-compiler", "libart-dexlayout", "libart", @@ -232,6 +234,7 @@ art_cc_binary { "dex2oat-defaults", ], shared_libs: [ + "libprofiled", "libartd-compiler", "libartd-dexlayout", "libartd", @@ -264,6 +267,7 @@ art_cc_binary { "-z muldefs", ], static_libs: [ + "libprofile", "libart-dex2oat", "libart-compiler", "libart-dexlayout", @@ -303,6 +307,7 @@ art_cc_binary { "libartd-compiler", "libartd-dexlayout", "libartd", + "libprofiled", "libdexfiled", "libvixld-arm", "libvixld-arm64", @@ -364,6 +369,7 @@ art_cc_test { "external/zlib", ], shared_libs: [ + "libprofiled", "libartd-compiler", "libartd-dexlayout", "libbase", diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 3fe9c477d5..df38ee3a34 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -79,7 +79,6 @@ #include "gc/verification.h" #include "interpreter/unstarted_runtime.h" #include "java_vm_ext.h" -#include "jit/profile_compilation_info.h" #include "linker/buffered_output_stream.h" #include "linker/elf_writer.h" #include "linker/elf_writer_quick.h" @@ -93,6 +92,7 @@ #include "mirror/object_array-inl.h" #include "oat_file.h" #include "oat_file_assistant.h" +#include "profile/profile_compilation_info.h" #include "runtime.h" #include "runtime_options.h" #include "scoped_thread_state_change-inl.h" diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc index 11c0c95060..03664673c3 100644 --- a/dex2oat/dex2oat_image_test.cc +++ b/dex2oat/dex2oat_image_test.cc @@ -34,7 +34,7 @@ #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "dex/method_reference.h" -#include "jit/profile_compilation_info.h" +#include "profile/profile_compilation_info.h" #include "runtime.h" namespace art { diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index a229d7dd71..2fe16f7cb7 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -38,9 +38,9 @@ #include "dex/dex_file_loader.h" #include "dex2oat_environment_test.h" #include "dex2oat_return_codes.h" -#include "jit/profile_compilation_info.h" #include "oat.h" #include "oat_file.h" +#include "profile/profile_compilation_info.h" #include "vdex_file.h" #include "ziparchive/zip_writer.h" diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index d3f4754bd5..4046dc101f 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -51,7 +51,6 @@ #include "gc/space/space.h" #include "handle_scope-inl.h" #include "image_writer.h" -#include "jit/profile_compilation_info.h" #include "linker/buffered_output_stream.h" #include "linker/file_output_stream.h" #include "linker/index_bss_mapping_encoder.h" @@ -63,6 +62,7 @@ #include "mirror/dex_cache-inl.h" #include "mirror/object-inl.h" #include "oat_quick_method_header.h" +#include "profile/profile_compilation_info.h" #include "quicken_info.h" #include "scoped_thread_state_change-inl.h" #include "utils/dex_cache_arrays_layout-inl.h" diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 208e96fc6f..1699153e7e 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -33,7 +33,6 @@ #include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "jit/profile_compilation_info.h" #include "linker/buffered_output_stream.h" #include "linker/elf_writer.h" #include "linker/elf_writer_quick.h" @@ -45,6 +44,7 @@ #include "mirror/object_array-inl.h" #include "oat_file-inl.h" #include "oat_writer.h" +#include "profile/profile_compilation_info.h" #include "scoped_thread_state_change-inl.h" #include "vdex_file.h" diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp index 33ba58f5f7..b009774582 100644 --- a/dexlayout/Android.bp +++ b/dexlayout/Android.bp @@ -41,6 +41,7 @@ art_cc_library { shared_libs: [ "libart", "libdexfile", + "libprofile", ], target: { @@ -61,6 +62,7 @@ art_cc_library { shared_libs: [ "libartd", "libdexfiled", + "libprofiled", ], } @@ -78,6 +80,7 @@ art_cc_binary { name: "dexlayout", defaults: ["dexlayout-defaults"], shared_libs: [ + "libprofile", "libart", "libart-dexlayout", ], @@ -90,6 +93,7 @@ art_cc_binary { "dexlayout-defaults", ], shared_libs: [ + "libprofiled", "libartd", "libartd-dexlayout", ], @@ -98,7 +102,10 @@ art_cc_binary { art_cc_test { name: "art_dexlayout_tests", defaults: ["art_gtest_defaults"], - shared_libs: ["libart-dexlayout"], + shared_libs: [ + "libprofiled", + "libartd-dexlayout", + ], srcs: ["dexlayout_test.cc"], } diff --git a/dexlayout/dex_visualize.cc b/dexlayout/dex_visualize.cc index 516a3382fd..c8aac941ff 100644 --- a/dexlayout/dex_visualize.cc +++ b/dexlayout/dex_visualize.cc @@ -33,7 +33,7 @@ #include "dex_ir.h" #include "dexlayout.h" -#include "jit/profile_compilation_info.h" +#include "profile/profile_compilation_info.h" namespace art { diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index 7a8c31ba84..62dd1a9554 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -49,7 +49,7 @@ #include "dex_verify.h" #include "dex_visualize.h" #include "dex_writer.h" -#include "jit/profile_compilation_info.h" +#include "profile/profile_compilation_info.h" namespace art { diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc index f81d16cbf5..185c1420ab 100644 --- a/dexlayout/dexlayout_main.cc +++ b/dexlayout/dexlayout_main.cc @@ -33,7 +33,7 @@ #include "base/logging.h" // For InitLogging. #include "base/mem_map.h" -#include "jit/profile_compilation_info.h" +#include "profile/profile_compilation_info.h" #include "runtime.h" namespace art { diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index 6719d0848c..f148b94f3d 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -31,7 +31,7 @@ #include "dex/dex_file_loader.h" #include "dexlayout.h" #include "exec_utils.h" -#include "jit/profile_compilation_info.h" +#include "profile/profile_compilation_info.h" namespace art { diff --git a/libprofile/Android.bp b/libprofile/Android.bp new file mode 100644 index 0000000000..bcb90cb680 --- /dev/null +++ b/libprofile/Android.bp @@ -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. +// + +cc_defaults { + name: "libprofile_defaults", + defaults: ["art_defaults"], + host_supported: true, + srcs: [ + "profile/profile_compilation_info.cc", + ], + target: { + android: { + static_libs: [ + // ZipArchive support, the order matters here to get all symbols. + "libziparchive", + "libz", + ], + shared_libs: [ + // For android::FileMap used by libziparchive. + "libutils", + ], + }, + host: { + shared_libs: [ + "libziparchive", + "libz", + ], + }, + }, + //generated_sources: ["art_libartbase_operator_srcs"], + cflags: ["-DBUILDING_LIBART=1"], + shared_libs: [ + "libartbase", + "libdexfile", + // For atrace. + "libcutils", + ], + export_include_dirs: ["."], + // ART's macros.h depends on libbase's macros.h. + // Note: runtime_options.h depends on cmdline. But we don't really want to export this + // generically. dex2oat takes care of it itself. + export_shared_lib_headers: ["libbase"], +} + +art_cc_library { + name: "libprofile", + defaults: ["libprofile_defaults"], + // Leave the symbols in the shared library so that stack unwinders can + // produce meaningful name resolution. + strip: { + keep_symbols: true, + }, + shared_libs: [ + "libbase", + "libziparchive", + ], + export_shared_lib_headers: ["libbase"], +} + +art_cc_library { + name: "libprofiled", + defaults: [ + "art_debug_defaults", + "libprofile_defaults", + ], + shared_libs: [ + "libbase", + "libziparchive", + ], + export_shared_lib_headers: ["libbase"], +} + +// For now many of these tests still use CommonRuntimeTest, almost universally because of +// ScratchFile and related. +// TODO: Remove CommonRuntimeTest dependency from these tests. +art_cc_test { + name: "art_libprofile_tests", + defaults: [ + "art_gtest_defaults", + ], + srcs: [ + "profile/profile_compilation_info_test.cc", + ], + shared_libs: [ + "libartbased", + "libdexfiled", + "libziparchive", + ], +} diff --git a/runtime/jit/profile_compilation_info.cc b/libprofile/profile/profile_compilation_info.cc similarity index 99% rename from runtime/jit/profile_compilation_info.cc rename to libprofile/profile/profile_compilation_info.cc index d27465dd6e..0e0c3c5116 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/libprofile/profile/profile_compilation_info.cc @@ -46,7 +46,6 @@ #include "base/utils.h" #include "base/zip_archive.h" #include "dex/dex_file_loader.h" -#include "jit/profiling_info.h" namespace art { @@ -70,12 +69,12 @@ static constexpr bool kDebugIgnoreChecksum = false; static constexpr uint8_t kIsMissingTypesEncoding = 6; static constexpr uint8_t kIsMegamorphicEncoding = 7; -static_assert(sizeof(InlineCache::kIndividualCacheSize) == sizeof(uint8_t), - "InlineCache::kIndividualCacheSize does not have the expect type size"); -static_assert(InlineCache::kIndividualCacheSize < kIsMegamorphicEncoding, - "InlineCache::kIndividualCacheSize is larger than expected"); -static_assert(InlineCache::kIndividualCacheSize < kIsMissingTypesEncoding, - "InlineCache::kIndividualCacheSize is larger than expected"); +static_assert(sizeof(ProfileCompilationInfo::kIndividualInlineCacheSize) == sizeof(uint8_t), + "InlineCache::kIndividualInlineCacheSize does not have the expect type size"); +static_assert(ProfileCompilationInfo::kIndividualInlineCacheSize < kIsMegamorphicEncoding, + "InlineCache::kIndividualInlineCacheSize is larger than expected"); +static_assert(ProfileCompilationInfo::kIndividualInlineCacheSize < kIsMissingTypesEncoding, + "InlineCache::kIndividualInlineCacheSize is larger than expected"); static bool ChecksumMatch(uint32_t dex_file_checksum, uint32_t checksum) { return kDebugIgnoreChecksum || dex_file_checksum == checksum; @@ -120,7 +119,7 @@ void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx, } // Check if the adding the type will cause the cache to become megamorphic. - if (classes.size() + 1 >= InlineCache::kIndividualCacheSize) { + if (classes.size() + 1 >= ProfileCompilationInfo::kIndividualInlineCacheSize) { is_megamorphic = true; classes.clear(); return; @@ -503,7 +502,7 @@ void ProfileCompilationInfo::AddInlineCacheToBuffer(std::vector* buffer continue; } - DCHECK_LT(classes.size(), InlineCache::kIndividualCacheSize); + DCHECK_LT(classes.size(), ProfileCompilationInfo::kIndividualInlineCacheSize); DCHECK_NE(classes.size(), 0u) << "InlineCache contains a dex_pc with 0 classes"; SafeMap> dex_to_classes_map; diff --git a/runtime/jit/profile_compilation_info.h b/libprofile/profile/profile_compilation_info.h similarity index 99% rename from runtime/jit/profile_compilation_info.h rename to libprofile/profile/profile_compilation_info.h index f4c8c723b3..32c796c363 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/libprofile/profile/profile_compilation_info.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_JIT_PROFILE_COMPILATION_INFO_H_ -#define ART_RUNTIME_JIT_PROFILE_COMPILATION_INFO_H_ +#ifndef ART_LIBPROFILE_PROFILE_PROFILE_COMPILATION_INFO_H_ +#define ART_LIBPROFILE_PROFILE_PROFILE_COMPILATION_INFO_H_ #include #include @@ -75,6 +75,8 @@ class ProfileCompilationInfo { static const char* kDexMetadataProfileEntry; + static constexpr uint8_t kIndividualInlineCacheSize = 5; + // Data structures for encoding the offline representation of inline caches. // This is exposed as public in order to make it available to dex2oat compilations // (see compiler/optimizing/inliner.cc). @@ -809,4 +811,4 @@ class ProfileCompilationInfo { } // namespace art -#endif // ART_RUNTIME_JIT_PROFILE_COMPILATION_INFO_H_ +#endif // ART_LIBPROFILE_PROFILE_PROFILE_COMPILATION_INFO_H_ diff --git a/runtime/jit/profile_compilation_info_test.cc b/libprofile/profile/profile_compilation_info_test.cc similarity index 99% rename from runtime/jit/profile_compilation_info_test.cc rename to libprofile/profile/profile_compilation_info_test.cc index 0ebadc00fe..b0f96492df 100644 --- a/runtime/jit/profile_compilation_info_test.cc +++ b/libprofile/profile/profile_compilation_info_test.cc @@ -26,10 +26,10 @@ #include "dex/method_reference.h" #include "dex/type_reference.h" #include "handle_scope-inl.h" -#include "jit/profile_compilation_info.h" #include "linear_alloc.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" +#include "profile/profile_compilation_info.h" #include "scoped_thread_state_change-inl.h" #include "ziparchive/zip_writer.h" diff --git a/oatdump/Android.bp b/oatdump/Android.bp index 8c21538f23..be12c8e406 100644 --- a/oatdump/Android.bp +++ b/oatdump/Android.bp @@ -37,6 +37,7 @@ art_cc_binary { "libart-compiler", "libart-disassembler", "libdexfile", + "libprofile", "libbase", ], } @@ -52,6 +53,7 @@ art_cc_binary { "libartd-compiler", "libartd-disassembler", "libdexfiled", + "libprofiled", "libbase", ], } @@ -77,6 +79,7 @@ art_cc_binary { static_libs: [ "libart", "libdexfile", + "libprofile", "libart-compiler", "libart-disassembler", "libvixl-arm", @@ -111,6 +114,7 @@ art_cc_binary { static_libs: [ "libartd", "libdexfiled", + "libprofiled", "libartd-compiler", "libartd-disassembler", "libvixld-arm", diff --git a/profman/Android.bp b/profman/Android.bp index 163be2b64f..3c8c72c34a 100644 --- a/profman/Android.bp +++ b/profman/Android.bp @@ -40,6 +40,7 @@ art_cc_binary { defaults: ["profman-defaults"], shared_libs: [ "libart", + "libprofile", "libdexfile", ], } @@ -52,6 +53,7 @@ art_cc_binary { ], shared_libs: [ "libartd", + "libprofiled", "libdexfiled", ], } @@ -61,5 +63,8 @@ art_cc_test { defaults: [ "art_gtest_defaults", ], + shared_libs: [ + "libprofiled", + ], srcs: ["profile_assistant_test.cc"], } diff --git a/profman/boot_image_profile.cc b/profman/boot_image_profile.cc index 60238e2082..89c9eb8b03 100644 --- a/profman/boot_image_profile.cc +++ b/profman/boot_image_profile.cc @@ -21,7 +21,7 @@ #include "dex/dex_file-inl.h" #include "dex/method_reference.h" #include "dex/type_reference.h" -#include "jit/profile_compilation_info.h" +#include "profile/profile_compilation_info.h" namespace art { diff --git a/profman/profile_assistant.h b/profman/profile_assistant.h index ee555840d7..c1d6f8e7a9 100644 --- a/profman/profile_assistant.h +++ b/profman/profile_assistant.h @@ -21,7 +21,7 @@ #include #include "base/scoped_flock.h" -#include "jit/profile_compilation_info.h" +#include "profile/profile_compilation_info.h" namespace art { diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index 72c285a3f5..17b7af17a2 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -23,10 +23,10 @@ #include "common_runtime_test.h" #include "dex/descriptors_names.h" #include "exec_utils.h" -#include "jit/profile_compilation_info.h" #include "linear_alloc.h" #include "mirror/class-inl.h" #include "obj_ptr-inl.h" +#include "profile/profile_compilation_info.h" #include "profile_assistant.h" #include "scoped_thread_state_change-inl.h" diff --git a/profman/profman.cc b/profman/profman.cc index 0ecc88c661..12bcdc636a 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -33,7 +33,6 @@ #include "base/dumpable.h" #include "base/logging.h" // For InitLogging. -#include "base/mutex.h" #include "base/scoped_flock.h" #include "base/stringpiece.h" #include "base/time_utils.h" @@ -48,7 +47,7 @@ #include "dex/dex_file_loader.h" #include "dex/dex_file_types.h" #include "dex/type_reference.h" -#include "jit/profile_compilation_info.h" +#include "profile/profile_compilation_info.h" #include "profile_assistant.h" #include "runtime.h" diff --git a/runtime/Android.bp b/runtime/Android.bp index 6b432058d9..f39562313d 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -112,7 +112,6 @@ cc_defaults { "jit/debugger_interface.cc", "jit/jit.cc", "jit/jit_code_cache.cc", - "jit/profile_compilation_info.cc", "jit/profiling_info.cc", "jit/profile_saver.cc", "jni_internal.cc", @@ -471,6 +470,7 @@ art_cc_library { ], shared_libs: [ "libdexfile", + "libprofile", ], export_shared_lib_headers: [ "libdexfile", @@ -495,6 +495,7 @@ art_cc_library { ], shared_libs: [ "libdexfiled", + "libprofiled", ], export_shared_lib_headers: [ "libdexfiled", @@ -579,7 +580,6 @@ art_cc_test { "interpreter/unstarted_runtime_test.cc", "jdwp/jdwp_options_test.cc", "java_vm_ext_test.cc", - "jit/profile_compilation_info_test.cc", "method_handles_test.cc", "mirror/dex_cache_test.cc", "mirror/method_type_test.cc", diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 3f33f7976e..57c0d9dab2 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -83,7 +83,6 @@ #include "jit/debugger_interface.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" -#include "jit/profile_compilation_info.h" #include "jni_internal.h" #include "linear_alloc.h" #include "mirror/call_site.h" @@ -116,6 +115,7 @@ #include "oat_file_assistant.h" #include "oat_file_manager.h" #include "object_lock.h" +#include "profile/profile_compilation_info.h" #include "runtime.h" #include "runtime_callbacks.h" #include "scoped_thread_state_change-inl.h" diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 813430f0bb..d20f760c17 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -31,7 +31,7 @@ #include "jit_code_cache.h" #include "oat_file_manager.h" #include "oat_quick_method_header.h" -#include "profile_compilation_info.h" +#include "profile/profile_compilation_info.h" #include "profile_saver.h" #include "runtime.h" #include "runtime_options.h" diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 6dcc87179b..1c8c26cf5d 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -41,7 +41,7 @@ #include "oat_file-inl.h" #include "oat_quick_method_header.h" #include "object_callbacks.h" -#include "profile_compilation_info.h" +#include "profile/profile_compilation_info.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" #include "thread-current-inl.h" diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 53f48644f2..618fde8f00 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -37,8 +37,9 @@ #include "gc/collector_type.h" #include "gc/gc_cause.h" #include "gc/scoped_gc_critical_section.h" -#include "jit/profile_compilation_info.h" +#include "jit/profiling_info.h" #include "oat_file_manager.h" +#include "profile/profile_compilation_info.h" #include "scoped_thread_state_change-inl.h" namespace art { @@ -46,6 +47,10 @@ namespace art { ProfileSaver* ProfileSaver::instance_ = nullptr; pthread_t ProfileSaver::profiler_pthread_ = 0U; +static_assert(ProfileCompilationInfo::kIndividualInlineCacheSize == + InlineCache::kIndividualCacheSize, + "InlineCache and ProfileCompilationInfo do not agree on kIndividualCacheSize"); + // At what priority to schedule the saver threads. 9 is the lowest foreground priority on device. static constexpr int kProfileSaverPthreadPriority = 9; diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h index afbb3c122d..02c8cd1474 100644 --- a/runtime/jit/profile_saver.h +++ b/runtime/jit/profile_saver.h @@ -21,7 +21,7 @@ #include "base/safe_map.h" #include "dex/method_reference.h" #include "jit_code_cache.h" -#include "profile_compilation_info.h" +#include "profile/profile_compilation_info.h" #include "profile_saver_options.h" namespace art { diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc index bb9ab84fb5..b22d61e38d 100644 --- a/test/595-profile-saving/profile-saving.cc +++ b/test/595-profile-saving/profile-saving.cc @@ -18,7 +18,6 @@ #include "art_method-inl.h" #include "dex/method_reference.h" -#include "jit/profile_compilation_info.h" #include "jit/profile_saver.h" #include "jni.h" #include "mirror/class-inl.h" @@ -26,6 +25,7 @@ #include "nativehelper/ScopedUtfChars.h" #include "oat_file_assistant.h" #include "oat_file_manager.h" +#include "profile/profile_compilation_info.h" #include "scoped_thread_state_change-inl.h" #include "thread.h" diff --git a/test/Android.bp b/test/Android.bp index b9312c8f15..16d14cdf30 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -63,6 +63,7 @@ art_cc_defaults { "libvixld-arm64", "libart-gtest", "libdexfiled", + "libprofiled", "libbase", "libicuuc", @@ -115,6 +116,7 @@ art_cc_defaults { "libartd", "libartd-compiler", "libdexfiled", + "libprofiled", ], static_libs: [ "libgtest", @@ -152,6 +154,7 @@ art_cc_library { "libartd", "libartd-compiler", "libdexfiled", + "libprofiled", "libbase", "libbacktrace", ], @@ -182,6 +185,7 @@ art_cc_test_library { shared_libs: [ "libart", "libdexfile", + "libprofile", ], } @@ -195,6 +199,7 @@ art_cc_test_library { shared_libs: [ "libartd", "libdexfiled", + "libprofiled", ], } @@ -315,6 +320,7 @@ art_cc_test_library { shared_libs: [ "libart", "libdexfile", + "libprofile", ], } @@ -327,6 +333,7 @@ art_cc_test_library { shared_libs: [ "libartd", "libdexfiled", + "libprofiled", ], } @@ -458,6 +465,7 @@ art_cc_test_library { shared_libs: [ "libart", "libdexfile", + "libprofile", ], } @@ -470,6 +478,7 @@ art_cc_test_library { shared_libs: [ "libartd", "libdexfiled", + "libprofiled", ], } diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index 2203bdca01..a55cc79ef9 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -25,12 +25,12 @@ #include "instrumentation.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" -#include "jit/profile_compilation_info.h" #include "jit/profiling_info.h" #include "mirror/class-inl.h" #include "nativehelper/ScopedUtfChars.h" #include "oat_file.h" #include "oat_quick_method_header.h" +#include "profile/profile_compilation_info.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" -- GitLab From 1979c64214bd505c013d573bc8729ee94f7bdea5 Mon Sep 17 00:00:00 2001 From: David Sehr Date: Thu, 26 Apr 2018 14:41:18 -0700 Subject: [PATCH 300/749] Clean up include paths Remove runtime/globals.h and make clients point to the right globals.h (libartbase/base/globals.h). Also make within-libartbase includes relative rather than using base/, etc. Bug: 22322814 Test: make -j 40 checkbuild Change-Id: I99de63fc851d48946ab401e2369de944419041c7 --- compiler/driver/compiler_options.h | 2 +- compiler/linker/buffered_output_stream.h | 2 +- compiler/optimizing/code_generator.h | 2 +- compiler/optimizing/optimizing_compiler.h | 2 +- compiler/utils/arm/constants_arm.h | 2 +- compiler/utils/arm/managed_register_arm.cc | 2 +- .../utils/arm/managed_register_arm_test.cc | 2 +- .../utils/arm64/managed_register_arm64.cc | 2 +- .../arm64/managed_register_arm64_test.cc | 2 +- compiler/utils/assembler.cc | 2 +- compiler/utils/jni_macro_assembler.cc | 2 +- compiler/utils/mips/assembler_mips.h | 2 +- compiler/utils/mips/constants_mips.h | 2 +- compiler/utils/mips/managed_register_mips.cc | 2 +- compiler/utils/mips64/assembler_mips64.h | 2 +- compiler/utils/mips64/constants_mips64.h | 2 +- .../utils/mips64/managed_register_mips64.cc | 2 +- .../mips64/managed_register_mips64_test.cc | 3 ++- compiler/utils/x86/assembler_x86.h | 2 +- compiler/utils/x86/constants_x86.h | 2 +- compiler/utils/x86/managed_register_x86.cc | 2 +- .../utils/x86/managed_register_x86_test.cc | 2 +- compiler/utils/x86_64/assembler_x86_64.h | 2 +- compiler/utils/x86_64/constants_x86_64.h | 2 +- .../utils/x86_64/managed_register_x86_64.cc | 2 +- .../x86_64/managed_register_x86_64_test.cc | 2 +- dex2oat/linker/elf_writer_quick.cc | 2 +- dex2oat/linker/image_writer.cc | 2 +- dex2oat/linker/multi_oat_relative_patcher.cc | 2 +- dex2oat/linker/relative_patcher_test.h | 2 +- libartbase/base/allocator.cc | 2 +- libartbase/base/allocator.h | 4 ++-- libartbase/base/arena_allocator.h | 10 ++++---- libartbase/base/arena_allocator_test.cc | 8 +++---- libartbase/base/arena_bit_vector.cc | 4 ++-- libartbase/base/arena_bit_vector.h | 4 ++-- libartbase/base/arena_containers.h | 8 +++---- libartbase/base/arena_object.h | 2 +- libartbase/base/array_slice.h | 6 ++--- libartbase/base/atomic.h | 2 +- libartbase/base/bit_string.h | 4 ++-- libartbase/base/bit_string_test.cc | 2 +- libartbase/base/bit_struct.h | 2 +- libartbase/base/bit_struct_detail.h | 2 +- libartbase/base/bit_utils.h | 2 +- libartbase/base/bit_utils_iterator.h | 6 ++--- libartbase/base/bit_vector-inl.h | 2 +- libartbase/base/bit_vector.h | 2 +- libartbase/base/bounded_fifo.h | 2 +- libartbase/base/dumpable.h | 2 +- libartbase/base/file_magic.cc | 2 +- libartbase/base/file_magic.h | 2 +- libartbase/base/hex_dump.h | 2 +- libartbase/base/histogram-inl.h | 6 ++--- libartbase/base/indenter.h | 2 +- libartbase/base/leb128.h | 6 ++--- libartbase/base/leb128_test.cc | 5 ++-- libartbase/base/length_prefixed_array.h | 8 +++---- libartbase/base/logging.cc | 4 ++-- libartbase/base/logging.h | 2 +- libartbase/base/logging_test.cc | 4 ++-- libartbase/base/malloc_arena_pool.cc | 2 +- libartbase/base/malloc_arena_pool.h | 2 +- libartbase/base/mem_map.cc | 12 +++++----- libartbase/base/mem_map.h | 2 +- libartbase/base/mem_map_test.cc | 4 ++-- libartbase/base/memory_region.h | 10 ++++---- libartbase/base/os_linux.cc | 4 ++-- libartbase/base/safe_copy.cc | 2 +- libartbase/base/safe_copy_test.cc | 2 +- libartbase/base/scoped_arena_allocator.cc | 2 +- libartbase/base/scoped_arena_allocator.h | 6 ++--- libartbase/base/scoped_arena_containers.h | 4 ++-- libartbase/base/scoped_flock.cc | 2 +- libartbase/base/scoped_flock.h | 6 ++--- libartbase/base/time_utils.cc | 2 +- libartbase/base/time_utils.h | 2 +- libartbase/base/tracking_safe_map.h | 4 ++-- libartbase/base/transform_array_ref.h | 4 ++-- libartbase/base/transform_array_ref_test.cc | 3 +-- libartbase/base/transform_iterator.h | 2 +- libartbase/base/transform_iterator_test.cc | 3 +-- libartbase/base/unix_file/fd_file.cc | 4 ++-- libartbase/base/unix_file/fd_file.h | 2 +- libartbase/base/unix_file/fd_file_test.cc | 4 ++-- .../unix_file/random_access_file_utils.cc | 4 ++-- libartbase/base/utils.cc | 2 +- libartbase/base/utils.h | 10 ++++---- libartbase/base/value_object.h | 2 +- libartbase/base/variant_map.h | 2 +- libartbase/base/zip_archive.cc | 6 ++--- libartbase/base/zip_archive.h | 8 +++---- libartbase/base/zip_archive_test.cc | 6 ++--- openjdkjvmti/jvmti_weak_table.h | 2 +- openjdkjvmti/object_tagging.h | 2 +- openjdkjvmti/ti_class_loader.h | 2 +- openjdkjvmti/ti_redefine.h | 2 +- openjdkjvmti/transform.cc | 2 +- runtime/arch/arm/fault_handler_arm.cc | 2 +- runtime/arch/arm64/fault_handler_arm64.cc | 2 +- .../arm64/quick_method_frame_info_arm64.h | 2 +- runtime/arch/instruction_set.cc | 2 +- runtime/arch/mips/fault_handler_mips.cc | 2 +- runtime/arch/mips/registers_mips.h | 2 +- runtime/arch/mips64/fault_handler_mips64.cc | 2 +- runtime/arch/mips64/registers_mips64.h | 2 +- runtime/arch/x86/fault_handler_x86.cc | 2 +- runtime/arch/x86/registers_x86.h | 2 +- runtime/arch/x86_64/registers_x86_64.h | 2 +- runtime/base/file_utils.cc | 4 ++-- runtime/common_runtime_test.h | 2 +- runtime/gc/accounting/bitmap.h | 2 +- runtime/gc/accounting/card_table.h | 2 +- runtime/gc/accounting/mod_union_table.h | 2 +- runtime/gc/accounting/read_barrier_table.h | 2 +- runtime/gc/accounting/space_bitmap.h | 2 +- runtime/gc/accounting/space_bitmap_test.cc | 2 +- runtime/gc/allocator/rosalloc.h | 2 +- runtime/gc/gc_cause.cc | 2 +- runtime/gc/heap.h | 2 +- runtime/gc/reference_processor.h | 2 +- runtime/gc/reference_queue.h | 2 +- runtime/gc/space/space.h | 2 +- runtime/gc/space/space_test.h | 2 +- runtime/gc/task_processor.h | 2 +- runtime/globals.h | 23 ------------------- runtime/hprof/hprof.cc | 2 +- runtime/image.h | 2 +- runtime/mirror/dex_cache.cc | 2 +- runtime/mirror/object.h | 2 +- runtime/mirror/object_reference.h | 2 +- runtime/obj_ptr.h | 2 +- runtime/offsets.h | 2 +- runtime/parsed_options.h | 2 +- runtime/thread.h | 2 +- runtime/trace.h | 2 +- runtime/utils/dex_cache_arrays_layout-inl.h | 2 +- runtime/verify_object.cc | 2 +- simulator/code_simulator_container.cc | 2 +- test/305-other-fault-handler/fault_handler.cc | 2 +- .../cpp-define-generator/constant_globals.def | 2 +- 141 files changed, 202 insertions(+), 227 deletions(-) delete mode 100644 runtime/globals.h diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 05d8805e81..cdd9d4de00 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -21,10 +21,10 @@ #include #include +#include "base/globals.h" #include "base/macros.h" #include "base/utils.h" #include "compiler_filter.h" -#include "globals.h" #include "optimizing/register_allocator.h" namespace art { diff --git a/compiler/linker/buffered_output_stream.h b/compiler/linker/buffered_output_stream.h index 66994e82a1..512409cb2f 100644 --- a/compiler/linker/buffered_output_stream.h +++ b/compiler/linker/buffered_output_stream.h @@ -21,7 +21,7 @@ #include "output_stream.h" -#include "globals.h" +#include "base/globals.h" namespace art { namespace linker { diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 62cacebaa1..f0c4ee01cc 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -25,10 +25,10 @@ #include "base/bit_field.h" #include "base/bit_utils.h" #include "base/enums.h" +#include "base/globals.h" #include "base/memory_region.h" #include "dex/string_reference.h" #include "dex/type_reference.h" -#include "globals.h" #include "graph_visualizer.h" #include "locations.h" #include "nodes.h" diff --git a/compiler/optimizing/optimizing_compiler.h b/compiler/optimizing/optimizing_compiler.h index d8cea30a6b..6ee9c70fdb 100644 --- a/compiler/optimizing/optimizing_compiler.h +++ b/compiler/optimizing/optimizing_compiler.h @@ -17,8 +17,8 @@ #ifndef ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_ #define ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_ +#include "base/globals.h" #include "base/mutex.h" -#include "globals.h" namespace art { diff --git a/compiler/utils/arm/constants_arm.h b/compiler/utils/arm/constants_arm.h index 66252bed86..3e316c8e84 100644 --- a/compiler/utils/arm/constants_arm.h +++ b/compiler/utils/arm/constants_arm.h @@ -25,7 +25,7 @@ #include "arch/arm/registers_arm.h" #include "base/casts.h" -#include "globals.h" +#include "base/globals.h" namespace art { namespace arm { diff --git a/compiler/utils/arm/managed_register_arm.cc b/compiler/utils/arm/managed_register_arm.cc index 1fdc110dcf..deff658b4f 100644 --- a/compiler/utils/arm/managed_register_arm.cc +++ b/compiler/utils/arm/managed_register_arm.cc @@ -16,7 +16,7 @@ #include "managed_register_arm.h" -#include "globals.h" +#include "base/globals.h" namespace art { namespace arm { diff --git a/compiler/utils/arm/managed_register_arm_test.cc b/compiler/utils/arm/managed_register_arm_test.cc index 43b0b516dc..6f440a7c81 100644 --- a/compiler/utils/arm/managed_register_arm_test.cc +++ b/compiler/utils/arm/managed_register_arm_test.cc @@ -15,7 +15,7 @@ */ #include "managed_register_arm.h" -#include "globals.h" +#include "base/globals.h" #include "gtest/gtest.h" namespace art { diff --git a/compiler/utils/arm64/managed_register_arm64.cc b/compiler/utils/arm64/managed_register_arm64.cc index 47924bf99f..5632265646 100644 --- a/compiler/utils/arm64/managed_register_arm64.cc +++ b/compiler/utils/arm64/managed_register_arm64.cc @@ -15,7 +15,7 @@ */ #include "managed_register_arm64.h" -#include "globals.h" +#include "base/globals.h" namespace art { namespace arm64 { diff --git a/compiler/utils/arm64/managed_register_arm64_test.cc b/compiler/utils/arm64/managed_register_arm64_test.cc index 2a79313be5..d151ac99e7 100644 --- a/compiler/utils/arm64/managed_register_arm64_test.cc +++ b/compiler/utils/arm64/managed_register_arm64_test.cc @@ -17,7 +17,7 @@ #include "managed_register_arm64.h" #include "assembler_arm64.h" -#include "globals.h" +#include "base/globals.h" #include "gtest/gtest.h" namespace art { diff --git a/compiler/utils/assembler.cc b/compiler/utils/assembler.cc index 421c1b6089..d1d2a3d556 100644 --- a/compiler/utils/assembler.cc +++ b/compiler/utils/assembler.cc @@ -20,8 +20,8 @@ #include #include "base/casts.h" +#include "base/globals.h" #include "base/memory_region.h" -#include "globals.h" namespace art { diff --git a/compiler/utils/jni_macro_assembler.cc b/compiler/utils/jni_macro_assembler.cc index 0c34aa4f1d..5f405f348c 100644 --- a/compiler/utils/jni_macro_assembler.cc +++ b/compiler/utils/jni_macro_assembler.cc @@ -38,8 +38,8 @@ #include "x86_64/jni_macro_assembler_x86_64.h" #endif #include "base/casts.h" +#include "base/globals.h" #include "base/memory_region.h" -#include "globals.h" namespace art { diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h index c6ce62b4f4..af3d7a06ba 100644 --- a/compiler/utils/mips/assembler_mips.h +++ b/compiler/utils/mips/assembler_mips.h @@ -24,10 +24,10 @@ #include "arch/mips/instruction_set_features_mips.h" #include "base/arena_containers.h" #include "base/enums.h" +#include "base/globals.h" #include "base/macros.h" #include "base/stl_util_identity.h" #include "constants_mips.h" -#include "globals.h" #include "heap_poisoning.h" #include "managed_register_mips.h" #include "offsets.h" diff --git a/compiler/utils/mips/constants_mips.h b/compiler/utils/mips/constants_mips.h index 016c0dbb2e..07d8b7de0e 100644 --- a/compiler/utils/mips/constants_mips.h +++ b/compiler/utils/mips/constants_mips.h @@ -22,8 +22,8 @@ #include #include "arch/mips/registers_mips.h" +#include "base/globals.h" #include "base/macros.h" -#include "globals.h" namespace art { namespace mips { diff --git a/compiler/utils/mips/managed_register_mips.cc b/compiler/utils/mips/managed_register_mips.cc index 5a8c0481a5..9b3ed79d2f 100644 --- a/compiler/utils/mips/managed_register_mips.cc +++ b/compiler/utils/mips/managed_register_mips.cc @@ -16,7 +16,7 @@ #include "managed_register_mips.h" -#include "globals.h" +#include "base/globals.h" namespace art { namespace mips { diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h index 542dbafc87..19f23b7e95 100644 --- a/compiler/utils/mips64/assembler_mips64.h +++ b/compiler/utils/mips64/assembler_mips64.h @@ -24,10 +24,10 @@ #include "arch/mips64/instruction_set_features_mips64.h" #include "base/arena_containers.h" #include "base/enums.h" +#include "base/globals.h" #include "base/macros.h" #include "base/stl_util_identity.h" #include "constants_mips64.h" -#include "globals.h" #include "heap_poisoning.h" #include "managed_register_mips64.h" #include "offsets.h" diff --git a/compiler/utils/mips64/constants_mips64.h b/compiler/utils/mips64/constants_mips64.h index 310f23c287..41eb77c9ae 100644 --- a/compiler/utils/mips64/constants_mips64.h +++ b/compiler/utils/mips64/constants_mips64.h @@ -22,8 +22,8 @@ #include #include "arch/mips64/registers_mips64.h" +#include "base/globals.h" #include "base/macros.h" -#include "globals.h" namespace art { namespace mips64 { diff --git a/compiler/utils/mips64/managed_register_mips64.cc b/compiler/utils/mips64/managed_register_mips64.cc index 42d061ec15..01cb6ddfe2 100644 --- a/compiler/utils/mips64/managed_register_mips64.cc +++ b/compiler/utils/mips64/managed_register_mips64.cc @@ -16,7 +16,7 @@ #include "managed_register_mips64.h" -#include "globals.h" +#include "base/globals.h" namespace art { namespace mips64 { diff --git a/compiler/utils/mips64/managed_register_mips64_test.cc b/compiler/utils/mips64/managed_register_mips64_test.cc index 8b72d7e61d..bbfeeee20f 100644 --- a/compiler/utils/mips64/managed_register_mips64_test.cc +++ b/compiler/utils/mips64/managed_register_mips64_test.cc @@ -15,7 +15,8 @@ */ #include "managed_register_mips64.h" -#include "globals.h" + +#include "base/globals.h" #include "gtest/gtest.h" namespace art { diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index 22eaedce61..e42c4c986a 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -23,9 +23,9 @@ #include "base/array_ref.h" #include "base/bit_utils.h" #include "base/enums.h" +#include "base/globals.h" #include "base/macros.h" #include "constants_x86.h" -#include "globals.h" #include "heap_poisoning.h" #include "managed_register_x86.h" #include "offsets.h" diff --git a/compiler/utils/x86/constants_x86.h b/compiler/utils/x86/constants_x86.h index 2e03b9fc3c..73ef028075 100644 --- a/compiler/utils/x86/constants_x86.h +++ b/compiler/utils/x86/constants_x86.h @@ -22,8 +22,8 @@ #include #include "arch/x86/registers_x86.h" +#include "base/globals.h" #include "base/macros.h" -#include "globals.h" namespace art { namespace x86 { diff --git a/compiler/utils/x86/managed_register_x86.cc b/compiler/utils/x86/managed_register_x86.cc index 69e6fce5c4..cc7cedf93e 100644 --- a/compiler/utils/x86/managed_register_x86.cc +++ b/compiler/utils/x86/managed_register_x86.cc @@ -16,7 +16,7 @@ #include "managed_register_x86.h" -#include "globals.h" +#include "base/globals.h" namespace art { namespace x86 { diff --git a/compiler/utils/x86/managed_register_x86_test.cc b/compiler/utils/x86/managed_register_x86_test.cc index 0ed5c36fe4..28af5313c7 100644 --- a/compiler/utils/x86/managed_register_x86_test.cc +++ b/compiler/utils/x86/managed_register_x86_test.cc @@ -16,7 +16,7 @@ #include "managed_register_x86.h" -#include "globals.h" +#include "base/globals.h" #include "gtest/gtest.h" namespace art { diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index ab761fb1fc..e4d72a7ba2 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -22,9 +22,9 @@ #include "base/arena_containers.h" #include "base/array_ref.h" #include "base/bit_utils.h" +#include "base/globals.h" #include "base/macros.h" #include "constants_x86_64.h" -#include "globals.h" #include "heap_poisoning.h" #include "managed_register_x86_64.h" #include "offsets.h" diff --git a/compiler/utils/x86_64/constants_x86_64.h b/compiler/utils/x86_64/constants_x86_64.h index 2af3e7be16..b02e246842 100644 --- a/compiler/utils/x86_64/constants_x86_64.h +++ b/compiler/utils/x86_64/constants_x86_64.h @@ -22,8 +22,8 @@ #include #include "arch/x86_64/registers_x86_64.h" +#include "base/globals.h" #include "base/macros.h" -#include "globals.h" namespace art { namespace x86_64 { diff --git a/compiler/utils/x86_64/managed_register_x86_64.cc b/compiler/utils/x86_64/managed_register_x86_64.cc index b8c2db2d2e..c0eec9d86c 100644 --- a/compiler/utils/x86_64/managed_register_x86_64.cc +++ b/compiler/utils/x86_64/managed_register_x86_64.cc @@ -16,7 +16,7 @@ #include "managed_register_x86_64.h" -#include "globals.h" +#include "base/globals.h" namespace art { namespace x86_64 { diff --git a/compiler/utils/x86_64/managed_register_x86_64_test.cc b/compiler/utils/x86_64/managed_register_x86_64_test.cc index e43d717385..46a405ffaf 100644 --- a/compiler/utils/x86_64/managed_register_x86_64_test.cc +++ b/compiler/utils/x86_64/managed_register_x86_64_test.cc @@ -15,7 +15,7 @@ */ #include "managed_register_x86_64.h" -#include "globals.h" +#include "base/globals.h" #include "gtest/gtest.h" namespace art { diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc index 4ab2012376..58bd1b0f1f 100644 --- a/dex2oat/linker/elf_writer_quick.cc +++ b/dex2oat/linker/elf_writer_quick.cc @@ -23,6 +23,7 @@ #include #include "base/casts.h" +#include "base/globals.h" #include "base/leb128.h" #include "base/utils.h" #include "compiled_method.h" @@ -31,7 +32,6 @@ #include "driver/compiler_options.h" #include "elf.h" #include "elf_utils.h" -#include "globals.h" #include "linker/buffered_output_stream.h" #include "linker/elf_builder.h" #include "linker/file_output_stream.h" diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index c7a30a06ed..bd1e6df93d 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -29,6 +29,7 @@ #include "art_method-inl.h" #include "base/callee_save_type.h" #include "base/enums.h" +#include "base/globals.h" #include "base/logging.h" // For VLOG. #include "base/unix_file/fd_file.h" #include "class_linker-inl.h" @@ -47,7 +48,6 @@ #include "gc/space/large_object_space.h" #include "gc/space/space-inl.h" #include "gc/verification.h" -#include "globals.h" #include "handle_scope-inl.h" #include "image.h" #include "imt_conflict_table.h" diff --git a/dex2oat/linker/multi_oat_relative_patcher.cc b/dex2oat/linker/multi_oat_relative_patcher.cc index 1449d478f9..a6797ffb8a 100644 --- a/dex2oat/linker/multi_oat_relative_patcher.cc +++ b/dex2oat/linker/multi_oat_relative_patcher.cc @@ -19,7 +19,7 @@ #include #include "base/bit_utils.h" -#include "globals.h" +#include "base/globals.h" #include "driver/compiled_method_storage.h" namespace art { diff --git a/dex2oat/linker/relative_patcher_test.h b/dex2oat/linker/relative_patcher_test.h index 9cd51e9b46..b4123eea3e 100644 --- a/dex2oat/linker/relative_patcher_test.h +++ b/dex2oat/linker/relative_patcher_test.h @@ -20,6 +20,7 @@ #include "arch/instruction_set.h" #include "arch/instruction_set_features.h" #include "base/array_ref.h" +#include "base/globals.h" #include "base/macros.h" #include "compiled_method-inl.h" #include "dex/verification_results.h" @@ -27,7 +28,6 @@ #include "dex/string_reference.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" -#include "globals.h" #include "gtest/gtest.h" #include "linker/relative_patcher.h" #include "linker/vector_output_stream.h" diff --git a/libartbase/base/allocator.cc b/libartbase/base/allocator.cc index c7be4e0161..c4ac180a15 100644 --- a/libartbase/base/allocator.cc +++ b/libartbase/base/allocator.cc @@ -21,7 +21,7 @@ #include -#include "base/atomic.h" +#include "atomic.h" namespace art { diff --git a/libartbase/base/allocator.h b/libartbase/base/allocator.h index 662f78e448..5eb6ea6b4c 100644 --- a/libartbase/base/allocator.h +++ b/libartbase/base/allocator.h @@ -19,8 +19,8 @@ #include -#include "base/atomic.h" -#include "base/macros.h" +#include "atomic.h" +#include "macros.h" namespace art { diff --git a/libartbase/base/arena_allocator.h b/libartbase/base/arena_allocator.h index 4d535df1d4..211ff4f6ad 100644 --- a/libartbase/base/arena_allocator.h +++ b/libartbase/base/arena_allocator.h @@ -20,11 +20,11 @@ #include #include -#include "base/bit_utils.h" -#include "base/debug_stack.h" -#include "base/dchecked_vector.h" -#include "base/macros.h" -#include "base/memory_tool.h" +#include "bit_utils.h" +#include "debug_stack.h" +#include "dchecked_vector.h" +#include "macros.h" +#include "memory_tool.h" namespace art { diff --git a/libartbase/base/arena_allocator_test.cc b/libartbase/base/arena_allocator_test.cc index 68e99f4be0..e358710ca6 100644 --- a/libartbase/base/arena_allocator_test.cc +++ b/libartbase/base/arena_allocator_test.cc @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "base/arena_allocator-inl.h" -#include "base/arena_bit_vector.h" -#include "base/malloc_arena_pool.h" -#include "base/memory_tool.h" +#include "arena_allocator-inl.h" +#include "arena_bit_vector.h" #include "gtest/gtest.h" +#include "malloc_arena_pool.h" +#include "memory_tool.h" namespace art { diff --git a/libartbase/base/arena_bit_vector.cc b/libartbase/base/arena_bit_vector.cc index 1542e9d5f7..01f9013737 100644 --- a/libartbase/base/arena_bit_vector.cc +++ b/libartbase/base/arena_bit_vector.cc @@ -16,8 +16,8 @@ #include "arena_bit_vector.h" -#include "base/allocator.h" -#include "base/arena_allocator.h" +#include "allocator.h" +#include "arena_allocator.h" namespace art { diff --git a/libartbase/base/arena_bit_vector.h b/libartbase/base/arena_bit_vector.h index 2b2322e74f..0fb6bbf775 100644 --- a/libartbase/base/arena_bit_vector.h +++ b/libartbase/base/arena_bit_vector.h @@ -17,8 +17,8 @@ #ifndef ART_LIBARTBASE_BASE_ARENA_BIT_VECTOR_H_ #define ART_LIBARTBASE_BASE_ARENA_BIT_VECTOR_H_ -#include "base/arena_object.h" -#include "base/bit_vector.h" +#include "arena_object.h" +#include "bit_vector.h" namespace art { diff --git a/libartbase/base/arena_containers.h b/libartbase/base/arena_containers.h index 40cf23c9fb..bd57fb1cfc 100644 --- a/libartbase/base/arena_containers.h +++ b/libartbase/base/arena_containers.h @@ -25,10 +25,10 @@ #include #include "arena_allocator.h" -#include "base/dchecked_vector.h" -#include "base/hash_map.h" -#include "base/hash_set.h" -#include "base/safe_map.h" +#include "dchecked_vector.h" +#include "hash_map.h" +#include "hash_set.h" +#include "safe_map.h" namespace art { diff --git a/libartbase/base/arena_object.h b/libartbase/base/arena_object.h index de7cb64a75..ed0922546d 100644 --- a/libartbase/base/arena_object.h +++ b/libartbase/base/arena_object.h @@ -20,7 +20,7 @@ #include #include "arena_allocator.h" -#include "base/macros.h" +#include "macros.h" #include "scoped_arena_allocator.h" namespace art { diff --git a/libartbase/base/array_slice.h b/libartbase/base/array_slice.h index 1ef2fbad8b..fb3da6b476 100644 --- a/libartbase/base/array_slice.h +++ b/libartbase/base/array_slice.h @@ -17,9 +17,9 @@ #ifndef ART_LIBARTBASE_BASE_ARRAY_SLICE_H_ #define ART_LIBARTBASE_BASE_ARRAY_SLICE_H_ -#include "base/bit_utils.h" -#include "base/casts.h" -#include "base/iteration_range.h" +#include "bit_utils.h" +#include "casts.h" +#include "iteration_range.h" #include "stride_iterator.h" namespace art { diff --git a/libartbase/base/atomic.h b/libartbase/base/atomic.h index f736667ca8..b68f867bfa 100644 --- a/libartbase/base/atomic.h +++ b/libartbase/base/atomic.h @@ -24,7 +24,7 @@ #include -#include "base/macros.h" +#include "macros.h" namespace art { diff --git a/libartbase/base/bit_string.h b/libartbase/base/bit_string.h index 0e051f358b..d995f8dbb1 100644 --- a/libartbase/base/bit_string.h +++ b/libartbase/base/bit_string.h @@ -17,8 +17,8 @@ #ifndef ART_LIBARTBASE_BASE_BIT_STRING_H_ #define ART_LIBARTBASE_BASE_BIT_STRING_H_ -#include "base/bit_struct.h" -#include "base/bit_utils.h" +#include "bit_struct.h" +#include "bit_utils.h" #include diff --git a/libartbase/base/bit_string_test.cc b/libartbase/base/bit_string_test.cc index 23274e3f2f..89a71a1894 100644 --- a/libartbase/base/bit_string_test.cc +++ b/libartbase/base/bit_string_test.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "base/bit_string.h" +#include "bit_string.h" #include "gtest/gtest.h" #include "android-base/logging.h" diff --git a/libartbase/base/bit_struct.h b/libartbase/base/bit_struct.h index 386b896073..9814fd4f3a 100644 --- a/libartbase/base/bit_struct.h +++ b/libartbase/base/bit_struct.h @@ -17,8 +17,8 @@ #ifndef ART_LIBARTBASE_BASE_BIT_STRUCT_H_ #define ART_LIBARTBASE_BASE_BIT_STRUCT_H_ -#include "base/bit_utils.h" #include "bit_struct_detail.h" +#include "bit_utils.h" // // Zero-cost, type-safe, well-defined "structs" of bit fields. diff --git a/libartbase/base/bit_struct_detail.h b/libartbase/base/bit_struct_detail.h index facfa61efb..68c2e4461f 100644 --- a/libartbase/base/bit_struct_detail.h +++ b/libartbase/base/bit_struct_detail.h @@ -17,7 +17,7 @@ #ifndef ART_LIBARTBASE_BASE_BIT_STRUCT_DETAIL_H_ #define ART_LIBARTBASE_BASE_BIT_STRUCT_DETAIL_H_ -#include "base/bit_utils.h" +#include "bit_utils.h" #include "globals.h" #include diff --git a/libartbase/base/bit_utils.h b/libartbase/base/bit_utils.h index ff6c160680..04f0e8518c 100644 --- a/libartbase/base/bit_utils.h +++ b/libartbase/base/bit_utils.h @@ -22,7 +22,7 @@ #include -#include "base/stl_util_identity.h" +#include "stl_util_identity.h" namespace art { diff --git a/libartbase/base/bit_utils_iterator.h b/libartbase/base/bit_utils_iterator.h index 3fab15a3db..4975ebfb44 100644 --- a/libartbase/base/bit_utils_iterator.h +++ b/libartbase/base/bit_utils_iterator.h @@ -23,9 +23,9 @@ #include -#include "base/bit_utils.h" -#include "base/iteration_range.h" -#include "base/stl_util.h" +#include "bit_utils.h" +#include "iteration_range.h" +#include "stl_util.h" namespace art { diff --git a/libartbase/base/bit_vector-inl.h b/libartbase/base/bit_vector-inl.h index 7a9f4650da..2bdc14ebe9 100644 --- a/libartbase/base/bit_vector-inl.h +++ b/libartbase/base/bit_vector-inl.h @@ -21,7 +21,7 @@ #include -#include "base/bit_utils.h" +#include "bit_utils.h" namespace art { diff --git a/libartbase/base/bit_vector.h b/libartbase/base/bit_vector.h index 2ffa2aa9c9..a930f4e556 100644 --- a/libartbase/base/bit_vector.h +++ b/libartbase/base/bit_vector.h @@ -20,7 +20,7 @@ #include #include -#include "base/bit_utils.h" +#include "bit_utils.h" #include "globals.h" namespace art { diff --git a/libartbase/base/bounded_fifo.h b/libartbase/base/bounded_fifo.h index 444f31a55b..43d14f4cef 100644 --- a/libartbase/base/bounded_fifo.h +++ b/libartbase/base/bounded_fifo.h @@ -19,7 +19,7 @@ #include -#include "base/bit_utils.h" +#include "bit_utils.h" namespace art { diff --git a/libartbase/base/dumpable.h b/libartbase/base/dumpable.h index 66213972f7..0c00505461 100644 --- a/libartbase/base/dumpable.h +++ b/libartbase/base/dumpable.h @@ -19,7 +19,7 @@ #include -#include "base/macros.h" +#include "macros.h" namespace art { diff --git a/libartbase/base/file_magic.cc b/libartbase/base/file_magic.cc index 2b9bed0397..d8d843beeb 100644 --- a/libartbase/base/file_magic.cc +++ b/libartbase/base/file_magic.cc @@ -23,7 +23,7 @@ #include #include -#include "base/unix_file/fd_file.h" +#include "unix_file/fd_file.h" namespace art { diff --git a/libartbase/base/file_magic.h b/libartbase/base/file_magic.h index 53f551cb3a..0d0322c470 100644 --- a/libartbase/base/file_magic.h +++ b/libartbase/base/file_magic.h @@ -20,7 +20,7 @@ #include #include -#include "base/os.h" +#include "os.h" namespace art { diff --git a/libartbase/base/hex_dump.h b/libartbase/base/hex_dump.h index 55f4d53c2e..d13595dc02 100644 --- a/libartbase/base/hex_dump.h +++ b/libartbase/base/hex_dump.h @@ -17,7 +17,7 @@ #ifndef ART_LIBARTBASE_BASE_HEX_DUMP_H_ #define ART_LIBARTBASE_BASE_HEX_DUMP_H_ -#include "base/macros.h" +#include "macros.h" #include diff --git a/libartbase/base/histogram-inl.h b/libartbase/base/histogram-inl.h index 26d01b2883..9832f03d90 100644 --- a/libartbase/base/histogram-inl.h +++ b/libartbase/base/histogram-inl.h @@ -26,9 +26,9 @@ #include -#include "base/bit_utils.h" -#include "base/time_utils.h" -#include "base/utils.h" +#include "bit_utils.h" +#include "time_utils.h" +#include "utils.h" namespace art { diff --git a/libartbase/base/indenter.h b/libartbase/base/indenter.h index 850b7c4f73..06e7340d36 100644 --- a/libartbase/base/indenter.h +++ b/libartbase/base/indenter.h @@ -22,7 +22,7 @@ #include -#include "base/macros.h" +#include "macros.h" namespace art { diff --git a/libartbase/base/leb128.h b/libartbase/base/leb128.h index ab19daa108..d5847fd6c6 100644 --- a/libartbase/base/leb128.h +++ b/libartbase/base/leb128.h @@ -21,9 +21,9 @@ #include -#include "base/bit_utils.h" -#include "base/globals.h" -#include "base/macros.h" +#include "bit_utils.h" +#include "globals.h" +#include "macros.h" namespace art { diff --git a/libartbase/base/leb128_test.cc b/libartbase/base/leb128_test.cc index 747fc19f5d..58b0d07786 100644 --- a/libartbase/base/leb128_test.cc +++ b/libartbase/base/leb128_test.cc @@ -17,9 +17,8 @@ #include "leb128.h" #include "gtest/gtest.h" - -#include "base/histogram-inl.h" -#include "base/time_utils.h" +#include "histogram-inl.h" +#include "time_utils.h" namespace art { diff --git a/libartbase/base/length_prefixed_array.h b/libartbase/base/length_prefixed_array.h index 7c09bddd55..9238e81566 100644 --- a/libartbase/base/length_prefixed_array.h +++ b/libartbase/base/length_prefixed_array.h @@ -20,10 +20,10 @@ #include // for offsetof() #include // for memset() -#include "base/bit_utils.h" -#include "base/casts.h" -#include "base/iteration_range.h" -#include "base/stride_iterator.h" +#include "bit_utils.h" +#include "casts.h" +#include "iteration_range.h" +#include "stride_iterator.h" namespace art { diff --git a/libartbase/base/logging.cc b/libartbase/base/logging.cc index fd2cc20af2..a66a7e3635 100644 --- a/libartbase/base/logging.cc +++ b/libartbase/base/logging.cc @@ -21,8 +21,8 @@ #include #include "aborting.h" -#include "base/os.h" -#include "base/unix_file/fd_file.h" +#include "os.h" +#include "unix_file/fd_file.h" // Headers for LogMessage::LogLine. #ifdef ART_TARGET_ANDROID diff --git a/libartbase/base/logging.h b/libartbase/base/logging.h index 986704ec98..d2c0a02399 100644 --- a/libartbase/base/logging.h +++ b/libartbase/base/logging.h @@ -21,7 +21,7 @@ #include #include "android-base/logging.h" -#include "base/macros.h" +#include "macros.h" namespace art { diff --git a/libartbase/base/logging_test.cc b/libartbase/base/logging_test.cc index 1456eb30fa..46ba41bb1f 100644 --- a/libartbase/base/logging_test.cc +++ b/libartbase/base/logging_test.cc @@ -19,9 +19,9 @@ #include #include "android-base/logging.h" -#include "base/bit_utils.h" -#include "base/macros.h" +#include "bit_utils.h" #include "gtest/gtest.h" +#include "macros.h" #include "runtime_debug.h" namespace art { diff --git a/libartbase/base/malloc_arena_pool.cc b/libartbase/base/malloc_arena_pool.cc index 7df4aef25b..144b06ceb9 100644 --- a/libartbase/base/malloc_arena_pool.cc +++ b/libartbase/base/malloc_arena_pool.cc @@ -24,7 +24,7 @@ #include #include -#include "base/arena_allocator-inl.h" +#include "arena_allocator-inl.h" namespace art { diff --git a/libartbase/base/malloc_arena_pool.h b/libartbase/base/malloc_arena_pool.h index 8720189343..c48be59eb5 100644 --- a/libartbase/base/malloc_arena_pool.h +++ b/libartbase/base/malloc_arena_pool.h @@ -19,7 +19,7 @@ #include -#include "base/arena_allocator.h" +#include "arena_allocator.h" namespace art { diff --git a/libartbase/base/mem_map.cc b/libartbase/base/mem_map.cc index 21634f86b6..9a1392ceee 100644 --- a/libartbase/base/mem_map.cc +++ b/libartbase/base/mem_map.cc @@ -32,12 +32,12 @@ #include "backtrace/BacktraceMap.h" #include "cutils/ashmem.h" -#include "base/allocator.h" -#include "base/bit_utils.h" -#include "base/globals.h" -#include "base/logging.h" // For VLOG_IS_ON. -#include "base/memory_tool.h" -#include "base/utils.h" +#include "allocator.h" +#include "bit_utils.h" +#include "globals.h" +#include "logging.h" // For VLOG_IS_ON. +#include "memory_tool.h" +#include "utils.h" #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON diff --git a/libartbase/base/mem_map.h b/libartbase/base/mem_map.h index b7beb08dbc..3a324b2dc5 100644 --- a/libartbase/base/mem_map.h +++ b/libartbase/base/mem_map.h @@ -25,7 +25,7 @@ #include #include "android-base/thread_annotations.h" -#include "base/macros.h" +#include "macros.h" namespace art { diff --git a/libartbase/base/mem_map_test.cc b/libartbase/base/mem_map_test.cc index 3adbf18a7a..4408f5502a 100644 --- a/libartbase/base/mem_map_test.cc +++ b/libartbase/base/mem_map_test.cc @@ -21,9 +21,9 @@ #include #include -#include "base/memory_tool.h" -#include "base/unix_file/fd_file.h" #include "common_runtime_test.h" +#include "memory_tool.h" +#include "unix_file/fd_file.h" namespace art { diff --git a/libartbase/base/memory_region.h b/libartbase/base/memory_region.h index 7add466cc7..3d00f5b939 100644 --- a/libartbase/base/memory_region.h +++ b/libartbase/base/memory_region.h @@ -22,12 +22,12 @@ #include -#include "base/bit_utils.h" -#include "base/casts.h" -#include "base/enums.h" -#include "base/macros.h" -#include "base/value_object.h" +#include "bit_utils.h" +#include "casts.h" +#include "enums.h" #include "globals.h" +#include "macros.h" +#include "value_object.h" namespace art { diff --git a/libartbase/base/os_linux.cc b/libartbase/base/os_linux.cc index cb228bd853..f8b31cf0d8 100644 --- a/libartbase/base/os_linux.cc +++ b/libartbase/base/os_linux.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "base/os.h" +#include "os.h" #include #include @@ -25,7 +25,7 @@ #include -#include "base/unix_file/fd_file.h" +#include "unix_file/fd_file.h" namespace art { diff --git a/libartbase/base/safe_copy.cc b/libartbase/base/safe_copy.cc index 7ba5cbd3e6..b46b921307 100644 --- a/libartbase/base/safe_copy.cc +++ b/libartbase/base/safe_copy.cc @@ -24,7 +24,7 @@ #include -#include "base/bit_utils.h" +#include "bit_utils.h" namespace art { diff --git a/libartbase/base/safe_copy_test.cc b/libartbase/base/safe_copy_test.cc index f1d7c55e77..c23651f7a7 100644 --- a/libartbase/base/safe_copy_test.cc +++ b/libartbase/base/safe_copy_test.cc @@ -22,7 +22,7 @@ #include #include "android-base/logging.h" -#include "base/globals.h" +#include "globals.h" #include "gtest/gtest.h" diff --git a/libartbase/base/scoped_arena_allocator.cc b/libartbase/base/scoped_arena_allocator.cc index 7240842d55..ab05c6041a 100644 --- a/libartbase/base/scoped_arena_allocator.cc +++ b/libartbase/base/scoped_arena_allocator.cc @@ -17,7 +17,7 @@ #include "scoped_arena_allocator.h" #include "arena_allocator-inl.h" -#include "base/memory_tool.h" +#include "memory_tool.h" namespace art { diff --git a/libartbase/base/scoped_arena_allocator.h b/libartbase/base/scoped_arena_allocator.h index d5f6df82cc..7eaec5e3c3 100644 --- a/libartbase/base/scoped_arena_allocator.h +++ b/libartbase/base/scoped_arena_allocator.h @@ -20,9 +20,9 @@ #include #include "arena_allocator.h" -#include "base/debug_stack.h" -#include "base/globals.h" -#include "base/macros.h" +#include "debug_stack.h" +#include "globals.h" +#include "macros.h" namespace art { diff --git a/libartbase/base/scoped_arena_containers.h b/libartbase/base/scoped_arena_containers.h index 4df02b6205..41939816f5 100644 --- a/libartbase/base/scoped_arena_containers.h +++ b/libartbase/base/scoped_arena_containers.h @@ -25,8 +25,8 @@ #include #include "arena_containers.h" // For ArenaAllocatorAdapterKind. -#include "base/dchecked_vector.h" -#include "base/safe_map.h" +#include "dchecked_vector.h" +#include "safe_map.h" #include "scoped_arena_allocator.h" namespace art { diff --git a/libartbase/base/scoped_flock.cc b/libartbase/base/scoped_flock.cc index 514b97bfb1..d679328cef 100644 --- a/libartbase/base/scoped_flock.cc +++ b/libartbase/base/scoped_flock.cc @@ -22,7 +22,7 @@ #include #include -#include "base/unix_file/fd_file.h" +#include "unix_file/fd_file.h" namespace art { diff --git a/libartbase/base/scoped_flock.h b/libartbase/base/scoped_flock.h index 476b25748b..39b36b4fdd 100644 --- a/libartbase/base/scoped_flock.h +++ b/libartbase/base/scoped_flock.h @@ -22,9 +22,9 @@ #include -#include "base/macros.h" -#include "base/os.h" -#include "base/unix_file/fd_file.h" +#include "macros.h" +#include "os.h" +#include "unix_file/fd_file.h" namespace art { diff --git a/libartbase/base/time_utils.cc b/libartbase/base/time_utils.cc index 3c09d5a36f..89a1109a7e 100644 --- a/libartbase/base/time_utils.cc +++ b/libartbase/base/time_utils.cc @@ -22,7 +22,7 @@ #include "android-base/stringprintf.h" -#include "base/logging.h" +#include "logging.h" #if defined(__APPLE__) #include diff --git a/libartbase/base/time_utils.h b/libartbase/base/time_utils.h index 811af5d682..431d3e19ac 100644 --- a/libartbase/base/time_utils.h +++ b/libartbase/base/time_utils.h @@ -22,7 +22,7 @@ #include -#include "base/macros.h" +#include "macros.h" namespace art { diff --git a/libartbase/base/tracking_safe_map.h b/libartbase/base/tracking_safe_map.h index 2750de1b94..9b015c4ea5 100644 --- a/libartbase/base/tracking_safe_map.h +++ b/libartbase/base/tracking_safe_map.h @@ -17,8 +17,8 @@ #ifndef ART_LIBARTBASE_BASE_TRACKING_SAFE_MAP_H_ #define ART_LIBARTBASE_BASE_TRACKING_SAFE_MAP_H_ -#include "base/allocator.h" -#include "base/safe_map.h" +#include "allocator.h" +#include "safe_map.h" namespace art { diff --git a/libartbase/base/transform_array_ref.h b/libartbase/base/transform_array_ref.h index de2739e2fc..ef2957332b 100644 --- a/libartbase/base/transform_array_ref.h +++ b/libartbase/base/transform_array_ref.h @@ -19,8 +19,8 @@ #include -#include "base/array_ref.h" -#include "base/transform_iterator.h" +#include "array_ref.h" +#include "transform_iterator.h" namespace art { diff --git a/libartbase/base/transform_array_ref_test.cc b/libartbase/base/transform_array_ref_test.cc index da0340d36b..fc73d56779 100644 --- a/libartbase/base/transform_array_ref_test.cc +++ b/libartbase/base/transform_array_ref_test.cc @@ -18,8 +18,7 @@ #include #include "gtest/gtest.h" - -#include "base/transform_array_ref.h" +#include "transform_array_ref.h" namespace art { diff --git a/libartbase/base/transform_iterator.h b/libartbase/base/transform_iterator.h index 82d9f9e325..92655438f4 100644 --- a/libartbase/base/transform_iterator.h +++ b/libartbase/base/transform_iterator.h @@ -20,7 +20,7 @@ #include #include -#include "base/iteration_range.h" +#include "iteration_range.h" namespace art { diff --git a/libartbase/base/transform_iterator_test.cc b/libartbase/base/transform_iterator_test.cc index 63b6e4f531..5a5c37d11d 100644 --- a/libartbase/base/transform_iterator_test.cc +++ b/libartbase/base/transform_iterator_test.cc @@ -21,8 +21,7 @@ #include #include "gtest/gtest.h" - -#include "base/transform_iterator.h" +#include "transform_iterator.h" namespace art { diff --git a/libartbase/base/unix_file/fd_file.cc b/libartbase/base/unix_file/fd_file.cc index b2881b8ed0..c5313e969d 100644 --- a/libartbase/base/unix_file/fd_file.cc +++ b/libartbase/base/unix_file/fd_file.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "base/unix_file/fd_file.h" +#include "fd_file.h" #include #include @@ -30,8 +30,8 @@ #include #else #include -#include "base/stl_util.h" #include "base/globals.h" +#include "base/stl_util.h" #endif namespace unix_file { diff --git a/libartbase/base/unix_file/fd_file.h b/libartbase/base/unix_file/fd_file.h index fe3317f64e..d61dab6ce3 100644 --- a/libartbase/base/unix_file/fd_file.h +++ b/libartbase/base/unix_file/fd_file.h @@ -22,7 +22,7 @@ #include #include "base/macros.h" -#include "base/unix_file/random_access_file.h" +#include "random_access_file.h" namespace unix_file { diff --git a/libartbase/base/unix_file/fd_file_test.cc b/libartbase/base/unix_file/fd_file_test.cc index 042fbc9284..7fc057b9e8 100644 --- a/libartbase/base/unix_file/fd_file_test.cc +++ b/libartbase/base/unix_file/fd_file_test.cc @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "base/unix_file/fd_file.h" -#include "base/unix_file/random_access_file_test.h" #include "common_runtime_test.h" // For ScratchFile #include "gtest/gtest.h" +#include "fd_file.h" +#include "random_access_file_test.h" namespace unix_file { diff --git a/libartbase/base/unix_file/random_access_file_utils.cc b/libartbase/base/unix_file/random_access_file_utils.cc index aae65c1cde..10d829964c 100644 --- a/libartbase/base/unix_file/random_access_file_utils.cc +++ b/libartbase/base/unix_file/random_access_file_utils.cc @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "base/unix_file/random_access_file_utils.h" +#include "random_access_file_utils.h" #include -#include "base/unix_file/random_access_file.h" +#include "random_access_file.h" namespace unix_file { diff --git a/libartbase/base/utils.cc b/libartbase/base/utils.cc index 029cffd3ab..b7a542fbbe 100644 --- a/libartbase/base/utils.cc +++ b/libartbase/base/utils.cc @@ -30,7 +30,7 @@ #include "android-base/stringprintf.h" #include "android-base/strings.h" -#include "base/os.h" +#include "os.h" #if defined(__APPLE__) #include diff --git a/libartbase/base/utils.h b/libartbase/base/utils.h index c8c5b72bf7..73c1c226f9 100644 --- a/libartbase/base/utils.h +++ b/libartbase/base/utils.h @@ -25,11 +25,11 @@ #include -#include "base/casts.h" -#include "base/enums.h" -#include "base/globals.h" -#include "base/macros.h" -#include "base/stringpiece.h" +#include "casts.h" +#include "enums.h" +#include "globals.h" +#include "macros.h" +#include "stringpiece.h" namespace art { diff --git a/libartbase/base/value_object.h b/libartbase/base/value_object.h index 441bd1a384..dab6b76b26 100644 --- a/libartbase/base/value_object.h +++ b/libartbase/base/value_object.h @@ -17,7 +17,7 @@ #ifndef ART_LIBARTBASE_BASE_VALUE_OBJECT_H_ #define ART_LIBARTBASE_BASE_VALUE_OBJECT_H_ -#include "base/macros.h" +#include "macros.h" namespace art { diff --git a/libartbase/base/variant_map.h b/libartbase/base/variant_map.h index 4e02c54499..581bc234cc 100644 --- a/libartbase/base/variant_map.h +++ b/libartbase/base/variant_map.h @@ -23,7 +23,7 @@ #include #include "android-base/logging.h" -#include "base/stl_util_identity.h" +#include "stl_util_identity.h" namespace art { diff --git a/libartbase/base/zip_archive.cc b/libartbase/base/zip_archive.cc index 4185c227c8..b5f946e5a2 100644 --- a/libartbase/base/zip_archive.cc +++ b/libartbase/base/zip_archive.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "base/zip_archive.h" +#include "zip_archive.h" #include #include @@ -27,8 +27,8 @@ #include "android-base/stringprintf.h" #include "ziparchive/zip_archive.h" -#include "base/bit_utils.h" -#include "base/unix_file/fd_file.h" +#include "bit_utils.h" +#include "unix_file/fd_file.h" namespace art { diff --git a/libartbase/base/zip_archive.h b/libartbase/base/zip_archive.h index 39c8168519..73495da96a 100644 --- a/libartbase/base/zip_archive.h +++ b/libartbase/base/zip_archive.h @@ -23,11 +23,11 @@ #include -#include "base/os.h" -#include "base/mem_map.h" -#include "base/safe_map.h" -#include "base/unix_file/random_access_file.h" #include "globals.h" +#include "mem_map.h" +#include "os.h" +#include "safe_map.h" +#include "unix_file/random_access_file.h" // system/core/zip_archive definitions. struct ZipEntry; diff --git a/libartbase/base/zip_archive_test.cc b/libartbase/base/zip_archive_test.cc index 03f4cd4d54..63743f754c 100644 --- a/libartbase/base/zip_archive_test.cc +++ b/libartbase/base/zip_archive_test.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "base/zip_archive.h" +#include "zip_archive.h" #include #include @@ -22,9 +22,9 @@ #include #include -#include "base/os.h" -#include "base/unix_file/fd_file.h" #include "common_runtime_test.h" +#include "os.h" +#include "unix_file/fd_file.h" namespace art { diff --git a/openjdkjvmti/jvmti_weak_table.h b/openjdkjvmti/jvmti_weak_table.h index 5a821c964b..cba8ef06a7 100644 --- a/openjdkjvmti/jvmti_weak_table.h +++ b/openjdkjvmti/jvmti_weak_table.h @@ -34,11 +34,11 @@ #include +#include "base/globals.h" #include "base/macros.h" #include "base/mutex.h" #include "gc/system_weak.h" #include "gc_root-inl.h" -#include "globals.h" #include "jvmti.h" #include "jvmti_allocator.h" #include "mirror/object.h" diff --git a/openjdkjvmti/object_tagging.h b/openjdkjvmti/object_tagging.h index b474845566..1b8366a501 100644 --- a/openjdkjvmti/object_tagging.h +++ b/openjdkjvmti/object_tagging.h @@ -34,8 +34,8 @@ #include +#include "base/globals.h" #include "base/mutex.h" -#include "globals.h" #include "jvmti.h" #include "jvmti_weak_table.h" #include "mirror/object.h" diff --git a/openjdkjvmti/ti_class_loader.h b/openjdkjvmti/ti_class_loader.h index dbe30da462..142e2e1588 100644 --- a/openjdkjvmti/ti_class_loader.h +++ b/openjdkjvmti/ti_class_loader.h @@ -39,12 +39,12 @@ #include "art_jvmti.h" #include "art_method.h" #include "base/array_slice.h" +#include "base/globals.h" #include "base/mem_map.h" #include "class_linker.h" #include "dex/dex_file.h" #include "dex/utf.h" #include "gc_root-inl.h" -#include "globals.h" #include "jni_env_ext-inl.h" #include "jvmti.h" #include "linear_alloc.h" diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h index 03238565cc..e14b7ae1c8 100644 --- a/openjdkjvmti/ti_redefine.h +++ b/openjdkjvmti/ti_redefine.h @@ -39,12 +39,12 @@ #include "art_jvmti.h" #include "art_method.h" #include "base/array_ref.h" +#include "base/globals.h" #include "base/mem_map.h" #include "class_linker.h" #include "dex/dex_file.h" #include "dex/utf.h" #include "gc_root-inl.h" -#include "globals.h" #include "jni_env_ext-inl.h" #include "jvmti.h" #include "linear_alloc.h" diff --git a/openjdkjvmti/transform.cc b/openjdkjvmti/transform.cc index f31486ba58..29a9b10ca2 100644 --- a/openjdkjvmti/transform.cc +++ b/openjdkjvmti/transform.cc @@ -39,6 +39,7 @@ #include "art_method.h" #include "base/array_ref.h" +#include "base/globals.h" #include "base/mem_map.h" #include "class_linker.h" #include "dex/dex_file.h" @@ -47,7 +48,6 @@ #include "events-inl.h" #include "fault_handler.h" #include "gc_root-inl.h" -#include "globals.h" #include "jni_env_ext-inl.h" #include "jvalue.h" #include "jvmti.h" diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc index 315bf957cc..bb33a273b8 100644 --- a/runtime/arch/arm/fault_handler_arm.cc +++ b/runtime/arch/arm/fault_handler_arm.cc @@ -20,10 +20,10 @@ #include "art_method.h" #include "base/enums.h" +#include "base/globals.h" #include "base/hex_dump.h" #include "base/logging.h" // For VLOG. #include "base/macros.h" -#include "globals.h" #include "thread-current-inl.h" // diff --git a/runtime/arch/arm64/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc index d282c8cfc0..e8b4627f86 100644 --- a/runtime/arch/arm64/fault_handler_arm64.cc +++ b/runtime/arch/arm64/fault_handler_arm64.cc @@ -20,10 +20,10 @@ #include "art_method.h" #include "base/enums.h" +#include "base/globals.h" #include "base/hex_dump.h" #include "base/logging.h" // For VLOG. #include "base/macros.h" -#include "globals.h" #include "registers_arm64.h" #include "thread-current-inl.h" diff --git a/runtime/arch/arm64/quick_method_frame_info_arm64.h b/runtime/arch/arm64/quick_method_frame_info_arm64.h index 3e6f6c6e3b..2d2b500ae9 100644 --- a/runtime/arch/arm64/quick_method_frame_info_arm64.h +++ b/runtime/arch/arm64/quick_method_frame_info_arm64.h @@ -21,7 +21,7 @@ #include "base/bit_utils.h" #include "base/callee_save_type.h" #include "base/enums.h" -#include "globals.h" +#include "base/globals.h" #include "quick/quick_method_frame_info.h" #include "registers_arm64.h" diff --git a/runtime/arch/instruction_set.cc b/runtime/arch/instruction_set.cc index ecccdcf7eb..b848eb27fc 100644 --- a/runtime/arch/instruction_set.cc +++ b/runtime/arch/instruction_set.cc @@ -20,7 +20,7 @@ #include "../elf.h" #include "android-base/logging.h" #include "base/bit_utils.h" -#include "globals.h" +#include "base/globals.h" namespace art { diff --git a/runtime/arch/mips/fault_handler_mips.cc b/runtime/arch/mips/fault_handler_mips.cc index f82dc08cb2..d5a9b1589e 100644 --- a/runtime/arch/mips/fault_handler_mips.cc +++ b/runtime/arch/mips/fault_handler_mips.cc @@ -19,10 +19,10 @@ #include "art_method.h" #include "base/callee_save_type.h" +#include "base/globals.h" #include "base/hex_dump.h" #include "base/logging.h" // For VLOG. #include "base/macros.h" -#include "globals.h" #include "quick_method_frame_info_mips.h" #include "registers_mips.h" #include "thread-current-inl.h" diff --git a/runtime/arch/mips/registers_mips.h b/runtime/arch/mips/registers_mips.h index c7f9a3e74e..34f2f9684d 100644 --- a/runtime/arch/mips/registers_mips.h +++ b/runtime/arch/mips/registers_mips.h @@ -21,8 +21,8 @@ #include +#include "base/globals.h" #include "base/macros.h" -#include "globals.h" namespace art { namespace mips { diff --git a/runtime/arch/mips64/fault_handler_mips64.cc b/runtime/arch/mips64/fault_handler_mips64.cc index ba6fff05ad..695da4794b 100644 --- a/runtime/arch/mips64/fault_handler_mips64.cc +++ b/runtime/arch/mips64/fault_handler_mips64.cc @@ -20,10 +20,10 @@ #include "art_method.h" #include "base/callee_save_type.h" +#include "base/globals.h" #include "base/hex_dump.h" #include "base/logging.h" // For VLOG. #include "base/macros.h" -#include "globals.h" #include "quick_method_frame_info_mips64.h" #include "registers_mips64.h" #include "thread-current-inl.h" diff --git a/runtime/arch/mips64/registers_mips64.h b/runtime/arch/mips64/registers_mips64.h index d3a24b6202..a3fa2ac426 100644 --- a/runtime/arch/mips64/registers_mips64.h +++ b/runtime/arch/mips64/registers_mips64.h @@ -21,8 +21,8 @@ #include +#include "base/globals.h" #include "base/macros.h" -#include "globals.h" namespace art { namespace mips64 { diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc index e6a91247cb..8b243342f9 100644 --- a/runtime/arch/x86/fault_handler_x86.cc +++ b/runtime/arch/x86/fault_handler_x86.cc @@ -20,11 +20,11 @@ #include "art_method.h" #include "base/enums.h" +#include "base/globals.h" #include "base/hex_dump.h" #include "base/logging.h" // For VLOG. #include "base/macros.h" #include "base/safe_copy.h" -#include "globals.h" #include "thread-current-inl.h" #if defined(__APPLE__) diff --git a/runtime/arch/x86/registers_x86.h b/runtime/arch/x86/registers_x86.h index ded3520c76..5a5d226319 100644 --- a/runtime/arch/x86/registers_x86.h +++ b/runtime/arch/x86/registers_x86.h @@ -21,8 +21,8 @@ #include +#include "base/globals.h" #include "base/macros.h" -#include "globals.h" namespace art { namespace x86 { diff --git a/runtime/arch/x86_64/registers_x86_64.h b/runtime/arch/x86_64/registers_x86_64.h index 4f2243170e..66aea705d2 100644 --- a/runtime/arch/x86_64/registers_x86_64.h +++ b/runtime/arch/x86_64/registers_x86_64.h @@ -21,8 +21,8 @@ #include +#include "base/globals.h" #include "base/macros.h" -#include "globals.h" namespace art { namespace x86_64 { diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc index 7b0796cf37..7921985b15 100644 --- a/runtime/base/file_utils.cc +++ b/runtime/base/file_utils.cc @@ -43,11 +43,11 @@ #include "android-base/strings.h" #include "base/bit_utils.h" -#include "base/stl_util.h" +#include "base/globals.h" #include "base/os.h" +#include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "dex/dex_file_loader.h" -#include "globals.h" #if defined(__APPLE__) #include diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 83a1f9a58a..186b1cd509 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -25,12 +25,12 @@ #include #include "arch/instruction_set.h" +#include "base/globals.h" #include "base/mutex.h" #include "base/os.h" #include "base/unix_file/fd_file.h" #include "dex/art_dex_file_loader.h" #include "dex/compact_dex_level.h" -#include "globals.h" // TODO: Add inl file and avoid including inl. #include "obj_ptr-inl.h" #include "scoped_thread_state_change-inl.h" diff --git a/runtime/gc/accounting/bitmap.h b/runtime/gc/accounting/bitmap.h index d039d88770..2d83a8ad2e 100644 --- a/runtime/gc/accounting/bitmap.h +++ b/runtime/gc/accounting/bitmap.h @@ -23,8 +23,8 @@ #include #include +#include "base/globals.h" #include "base/mutex.h" -#include "globals.h" namespace art { diff --git a/runtime/gc/accounting/card_table.h b/runtime/gc/accounting/card_table.h index 766c976c98..f3548f7ce5 100644 --- a/runtime/gc/accounting/card_table.h +++ b/runtime/gc/accounting/card_table.h @@ -19,8 +19,8 @@ #include +#include "base/globals.h" #include "base/mutex.h" -#include "globals.h" namespace art { diff --git a/runtime/gc/accounting/mod_union_table.h b/runtime/gc/accounting/mod_union_table.h index 766e0f5d33..7a3c06a281 100644 --- a/runtime/gc/accounting/mod_union_table.h +++ b/runtime/gc/accounting/mod_union_table.h @@ -18,11 +18,11 @@ #define ART_RUNTIME_GC_ACCOUNTING_MOD_UNION_TABLE_H_ #include "base/allocator.h" +#include "base/globals.h" #include "base/safe_map.h" #include "base/tracking_safe_map.h" #include "bitmap.h" #include "card_table.h" -#include "globals.h" #include "mirror/object_reference.h" #include diff --git a/runtime/gc/accounting/read_barrier_table.h b/runtime/gc/accounting/read_barrier_table.h index 57e4db9b2b..4b5a8c61c1 100644 --- a/runtime/gc/accounting/read_barrier_table.h +++ b/runtime/gc/accounting/read_barrier_table.h @@ -20,10 +20,10 @@ #include // For the PROT_* and MAP_* constants. #include "base/bit_utils.h" +#include "base/globals.h" #include "base/mem_map.h" #include "base/mutex.h" #include "gc/space/space.h" -#include "globals.h" namespace art { namespace gc { diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h index 437aecc2b1..1237f6e8b5 100644 --- a/runtime/gc/accounting/space_bitmap.h +++ b/runtime/gc/accounting/space_bitmap.h @@ -23,8 +23,8 @@ #include #include +#include "base/globals.h" #include "base/mutex.h" -#include "globals.h" namespace art { diff --git a/runtime/gc/accounting/space_bitmap_test.cc b/runtime/gc/accounting/space_bitmap_test.cc index 1ca3fd6d12..22529b83c2 100644 --- a/runtime/gc/accounting/space_bitmap_test.cc +++ b/runtime/gc/accounting/space_bitmap_test.cc @@ -19,9 +19,9 @@ #include #include +#include "base/globals.h" #include "base/mutex.h" #include "common_runtime_test.h" -#include "globals.h" #include "space_bitmap-inl.h" namespace art { diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h index 6e5cf0ede8..150fe956ae 100644 --- a/runtime/gc/allocator/rosalloc.h +++ b/runtime/gc/allocator/rosalloc.h @@ -30,8 +30,8 @@ #include "base/allocator.h" #include "base/bit_utils.h" +#include "base/globals.h" #include "base/mutex.h" -#include "globals.h" #include "thread.h" namespace art { diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc index 508d76535e..ee7ac7dae0 100644 --- a/runtime/gc/gc_cause.cc +++ b/runtime/gc/gc_cause.cc @@ -18,8 +18,8 @@ #include +#include "base/globals.h" #include "base/macros.h" -#include "globals.h" #include diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 99ebab9357..609d2ab30e 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -27,6 +27,7 @@ #include "allocator_type.h" #include "arch/instruction_set.h" #include "base/atomic.h" +#include "base/globals.h" #include "base/macros.h" #include "base/mutex.h" #include "base/runtime_debug.h" @@ -37,7 +38,6 @@ #include "gc/collector_type.h" #include "gc/gc_cause.h" #include "gc/space/large_object_space.h" -#include "globals.h" #include "handle.h" #include "obj_ptr.h" #include "offsets.h" diff --git a/runtime/gc/reference_processor.h b/runtime/gc/reference_processor.h index 1d984eb768..c6c78367b0 100644 --- a/runtime/gc/reference_processor.h +++ b/runtime/gc/reference_processor.h @@ -17,8 +17,8 @@ #ifndef ART_RUNTIME_GC_REFERENCE_PROCESSOR_H_ #define ART_RUNTIME_GC_REFERENCE_PROCESSOR_H_ +#include "base/globals.h" #include "base/mutex.h" -#include "globals.h" #include "jni.h" #include "reference_queue.h" diff --git a/runtime/gc/reference_queue.h b/runtime/gc/reference_queue.h index af7788130b..09ab51a045 100644 --- a/runtime/gc/reference_queue.h +++ b/runtime/gc/reference_queue.h @@ -22,9 +22,9 @@ #include #include "base/atomic.h" +#include "base/globals.h" #include "base/mutex.h" #include "base/timing_logger.h" -#include "globals.h" #include "jni.h" #include "obj_ptr.h" #include "offsets.h" diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h index d888935ff2..4f43d9f5c5 100644 --- a/runtime/gc/space/space.h +++ b/runtime/gc/space/space.h @@ -21,12 +21,12 @@ #include #include "base/atomic.h" +#include "base/globals.h" #include "base/macros.h" #include "base/mem_map.h" #include "base/mutex.h" #include "gc/accounting/space_bitmap.h" #include "gc/collector/object_byte_pair.h" -#include "globals.h" namespace art { namespace mirror { diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h index 1fe3fb2e86..d5861ed5d8 100644 --- a/runtime/gc/space/space_test.h +++ b/runtime/gc/space/space_test.h @@ -20,8 +20,8 @@ #include #include +#include "base/globals.h" #include "common_runtime_test.h" -#include "globals.h" #include "mirror/array-inl.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" diff --git a/runtime/gc/task_processor.h b/runtime/gc/task_processor.h index f6b5607037..6db3c37689 100644 --- a/runtime/gc/task_processor.h +++ b/runtime/gc/task_processor.h @@ -20,8 +20,8 @@ #include #include +#include "base/globals.h" #include "base/mutex.h" -#include "globals.h" #include "thread_pool.h" namespace art { diff --git a/runtime/globals.h b/runtime/globals.h deleted file mode 100644 index bdc2177c7c..0000000000 --- a/runtime/globals.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2011 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_GLOBALS_H_ -#define ART_RUNTIME_GLOBALS_H_ - -// TODO: remove this file in favor of libartbase/base/globals.h -#include "base/globals.h" - -#endif // ART_RUNTIME_GLOBALS_H_ diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index aa716f12ac..3b6487449b 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -42,6 +42,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" #include "base/array_ref.h" +#include "base/globals.h" #include "base/macros.h" #include "base/mutex.h" #include "base/os.h" @@ -59,7 +60,6 @@ #include "gc/scoped_gc_critical_section.h" #include "gc/space/space.h" #include "gc_root.h" -#include "globals.h" #include "jdwp/jdwp.h" #include "jdwp/jdwp_priv.h" #include "mirror/class-inl.h" diff --git a/runtime/image.h b/runtime/image.h index fe544cc44b..8acd5bc4c4 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -20,7 +20,7 @@ #include #include "base/enums.h" -#include "globals.h" +#include "base/globals.h" #include "mirror/object.h" namespace art { diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc index eb4db00ccd..661f954ef5 100644 --- a/runtime/mirror/dex_cache.cc +++ b/runtime/mirror/dex_cache.cc @@ -17,10 +17,10 @@ #include "dex_cache-inl.h" #include "art_method-inl.h" +#include "base/globals.h" #include "class_linker.h" #include "gc/accounting/card_table-inl.h" #include "gc/heap.h" -#include "globals.h" #include "linear_alloc.h" #include "oat_file.h" #include "object-inl.h" diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index d00c90bcc0..82045c7b66 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -20,7 +20,7 @@ #include "base/atomic.h" #include "base/casts.h" #include "base/enums.h" -#include "globals.h" +#include "base/globals.h" #include "obj_ptr.h" #include "object_reference.h" #include "offsets.h" diff --git a/runtime/mirror/object_reference.h b/runtime/mirror/object_reference.h index 356fef0d26..77154e2cda 100644 --- a/runtime/mirror/object_reference.h +++ b/runtime/mirror/object_reference.h @@ -18,8 +18,8 @@ #define ART_RUNTIME_MIRROR_OBJECT_REFERENCE_H_ #include "base/atomic.h" +#include "base/globals.h" #include "base/mutex.h" // For Locks::mutator_lock_. -#include "globals.h" #include "heap_poisoning.h" #include "obj_ptr.h" diff --git a/runtime/obj_ptr.h b/runtime/obj_ptr.h index 14fdba31d9..e421d878ff 100644 --- a/runtime/obj_ptr.h +++ b/runtime/obj_ptr.h @@ -20,9 +20,9 @@ #include #include +#include "base/globals.h" #include "base/macros.h" #include "base/mutex.h" // For Locks::mutator_lock_. -#include "globals.h" namespace art { diff --git a/runtime/offsets.h b/runtime/offsets.h index aaf5c0c120..4df9b27f61 100644 --- a/runtime/offsets.h +++ b/runtime/offsets.h @@ -20,7 +20,7 @@ #include #include "base/enums.h" -#include "globals.h" +#include "base/globals.h" namespace art { diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h index 0f8555a829..8c77d39f7b 100644 --- a/runtime/parsed_options.h +++ b/runtime/parsed_options.h @@ -23,9 +23,9 @@ #include #include "arch/instruction_set.h" +#include "base/globals.h" #include "gc/collector_type.h" #include "gc/space/large_object_space.h" -#include "globals.h" // #include "jit/profile_saver_options.h" #include "runtime_options.h" diff --git a/runtime/thread.h b/runtime/thread.h index 22b77eea64..3ec050a5eb 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -30,11 +30,11 @@ #include "arch/instruction_set.h" #include "base/atomic.h" #include "base/enums.h" +#include "base/globals.h" #include "base/macros.h" #include "base/mutex.h" #include "entrypoints/jni/jni_entrypoints.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "globals.h" #include "handle_scope.h" #include "instrumentation.h" #include "jvalue.h" diff --git a/runtime/trace.h b/runtime/trace.h index b242d1596c..1fae250d77 100644 --- a/runtime/trace.h +++ b/runtime/trace.h @@ -27,10 +27,10 @@ #include #include "base/atomic.h" +#include "base/globals.h" #include "base/macros.h" #include "base/os.h" #include "base/safe_map.h" -#include "globals.h" #include "instrumentation.h" namespace unix_file { diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h index 68a5760f7e..c0ea6be5a3 100644 --- a/runtime/utils/dex_cache_arrays_layout-inl.h +++ b/runtime/utils/dex_cache_arrays_layout-inl.h @@ -22,9 +22,9 @@ #include #include "base/bit_utils.h" +#include "base/globals.h" #include "dex/primitive.h" #include "gc_root.h" -#include "globals.h" #include "mirror/dex_cache.h" namespace art { diff --git a/runtime/verify_object.cc b/runtime/verify_object.cc index a031a07a94..70ca13f913 100644 --- a/runtime/verify_object.cc +++ b/runtime/verify_object.cc @@ -17,8 +17,8 @@ #include "verify_object-inl.h" #include "base/bit_utils.h" +#include "base/globals.h" #include "gc/heap.h" -#include "globals.h" #include "mirror/object-inl.h" #include "obj_ptr-inl.h" #include "runtime.h" diff --git a/simulator/code_simulator_container.cc b/simulator/code_simulator_container.cc index 9f52b320f2..3206bc7844 100644 --- a/simulator/code_simulator_container.cc +++ b/simulator/code_simulator_container.cc @@ -18,9 +18,9 @@ #include "code_simulator_container.h" +#include "base/globals.h" #include "base/logging.h" // For VLOG. #include "code_simulator.h" -#include "globals.h" namespace art { diff --git a/test/305-other-fault-handler/fault_handler.cc b/test/305-other-fault-handler/fault_handler.cc index a0831ca8c2..211d142a2b 100644 --- a/test/305-other-fault-handler/fault_handler.cc +++ b/test/305-other-fault-handler/fault_handler.cc @@ -23,9 +23,9 @@ #include #include +#include "base/globals.h" #include "base/mem_map.h" #include "fault_handler.h" -#include "globals.h" namespace art { diff --git a/tools/cpp-define-generator/constant_globals.def b/tools/cpp-define-generator/constant_globals.def index 539633e0b3..d0d6350f80 100644 --- a/tools/cpp-define-generator/constant_globals.def +++ b/tools/cpp-define-generator/constant_globals.def @@ -18,8 +18,8 @@ #if defined(DEFINE_INCLUDE_DEPENDENCIES) #include // std::memory_order_relaxed +#include "base/globals.h" // art::kObjectAlignment #include "dex/modifiers.h" -#include "globals.h" // art::kObjectAlignment #endif DEFINE_EXPR(STD_MEMORY_ORDER_RELAXED, int32_t, std::memory_order_relaxed) -- GitLab From 83b7419bdf104420267c5463e78009e836b44728 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 27 Apr 2018 10:58:45 +0100 Subject: [PATCH 301/749] Add libprofile/libprofiled to public libraries. Test: m Change-Id: I9209234513ed6cf7da01058f44f7806e29b26e20 --- Android.mk | 3 ++- test/etc/run-test-jar | 4 ++-- tools/public.libraries.buildbot.txt | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Android.mk b/Android.mk index 47bcaac343..64b9400f66 100644 --- a/Android.mk +++ b/Android.mk @@ -471,10 +471,11 @@ build-art-target-golem: dex2oat dalvikvm patchoat linker libstdc++ \ $(ART_TARGET_SHARED_LIBRARY_BENCHMARK) \ $(TARGET_CORE_IMG_OUT_BASE).art \ $(TARGET_CORE_IMG_OUT_BASE)-interpreter.art - # remove libartd.so and libdexfiled.so from public.libraries.txt because golem builds + # remove debug libraries from public.libraries.txt because golem builds # won't have it. sed -i '/libartd.so/d' $(TARGET_OUT)/etc/public.libraries.txt sed -i '/libdexfiled.so/d' $(TARGET_OUT)/etc/public.libraries.txt + sed -i '/libprofiled.so/d' $(TARGET_OUT)/etc/public.libraries.txt ######################################################################## # Phony target for building what go/lem requires on host. diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index c5277548eb..be1296b990 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -871,9 +871,9 @@ if [ "$HOST" = "n" ]; then # System libraries needed by libarttestd.so PUBLIC_LIBS=libc++.so:libbacktrace.so:libbase.so:libnativehelper.so if [ "$TEST_IS_NDEBUG" = "y" ]; then - PUBLIC_LIBS=$PUBLIC_LIBS:libart.so:libdexfile.so + PUBLIC_LIBS=$PUBLIC_LIBS:libart.so:libdexfile.so:libprofile.so else - PUBLIC_LIBS=$PUBLIC_LIBS:libartd.so:libdexfiled.so + PUBLIC_LIBS=$PUBLIC_LIBS:libartd.so:libdexfiled.so:libprofiled.so fi # Create a script with the command. The command can get longer than the longest diff --git a/tools/public.libraries.buildbot.txt b/tools/public.libraries.buildbot.txt index de636a813a..6c8114558e 100644 --- a/tools/public.libraries.buildbot.txt +++ b/tools/public.libraries.buildbot.txt @@ -8,3 +8,5 @@ libc++.so libdl.so libm.so libnativehelper.so +libprofile.so +libprofiled.so -- GitLab From 756fddf396712b2e583b345a348d3911860d7151 Mon Sep 17 00:00:00 2001 From: Anton Hansson Date: Fri, 27 Apr 2018 14:07:21 +0100 Subject: [PATCH 302/749] Update art references to prebuilts. These stubs have been copied to a new location (see ref'd bug). Make art use the new ones, so that the old ones can be deleted Bug: 77525052 Test: m out/target/common/obj/PACKAGING/core_dex_intermediates/classes.dex Test: m out/target/common/obj/PACKAGING/oahl_dex_intermediates/classes.dex Change-Id: Iba754278415be8c8db3350223ef45e448edd0f8e --- tools/veridex/Android.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/veridex/Android.mk b/tools/veridex/Android.mk index 4183054193..51d924a3c1 100644 --- a/tools/veridex/Android.mk +++ b/tools/veridex/Android.mk @@ -18,13 +18,13 @@ LOCAL_PATH := $(call my-dir) system_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/core_dex_intermediates/classes.dex $(system_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000 -$(system_stub_dex): $(TOPDIR)prebuilts/sdk/system_current/android.jar | $(ZIP2ZIP) $(DX) +$(system_stub_dex): $(call resolve-prebuilt-sdk-jar-path,system_current) | $(ZIP2ZIP) $(DX) $(transform-classes-d8.jar-to-dex) oahl_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/oahl_dex_intermediates/classes.dex $(oahl_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000 -$(oahl_stub_dex): $(TOPDIR)prebuilts/sdk/org.apache.http.legacy/org.apache.http.legacy.jar | $(ZIP2ZIP) $(DX) +$(oahl_stub_dex): $(call get-prebuilt-sdk-dir,current)/org.apache.http.legacy.jar | $(ZIP2ZIP) $(DX) $(transform-classes-d8.jar-to-dex) .PHONY: appcompat -- GitLab From a111ab1f5703ce839983a86caffae9bcad34c86f Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 25 Apr 2018 11:41:13 -0700 Subject: [PATCH 303/749] ART: Refactor class-def retrieval Peel the first loop iteration to avoid excessive branches and loads. Bug: 78568168 Test: m test-art-host Change-Id: I22f403254b01d34837dde1decc05e2d00700ffeb --- libdexfile/dex/dex_file_verifier.cc | 73 +++++++++++++++-------------- libdexfile/dex/dex_file_verifier.h | 9 +--- 2 files changed, 40 insertions(+), 42 deletions(-) diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc index e0bfdcff4b..3f72a25940 100644 --- a/libdexfile/dex/dex_file_verifier.cc +++ b/libdexfile/dex/dex_file_verifier.cc @@ -994,31 +994,16 @@ bool DexFileVerifier::FindClassIndexAndDef(uint32_t index, return false; } -bool DexFileVerifier::CheckOrderAndGetClassDef(bool is_field, - const char* type_descr, - uint32_t curr_index, - uint32_t prev_index, - bool* have_class, - dex::TypeIndex* class_type_index, - const DexFile::ClassDef** class_def) { - if (curr_index < prev_index) { +bool DexFileVerifier::CheckOrder(const char* type_descr, + uint32_t curr_index, + uint32_t prev_index) { + if (UNLIKELY(curr_index < prev_index)) { ErrorStringPrintf("out-of-order %s indexes %" PRIu32 " and %" PRIu32, type_descr, prev_index, curr_index); return false; } - - if (!*have_class) { - *have_class = FindClassIndexAndDef(curr_index, is_field, class_type_index, class_def); - if (!*have_class) { - // Should have really found one. - ErrorStringPrintf("could not find declaring class for %s index %" PRIu32, - type_descr, - curr_index); - return false; - } - } return true; } @@ -1123,20 +1108,29 @@ bool DexFileVerifier::CheckIntraClassDataItemFields(ClassDataItemIterator* it, dex::TypeIndex* class_type_index, const DexFile::ClassDef** class_def) { DCHECK(it != nullptr); + constexpr const char* kTypeDescr = kStatic ? "static field" : "instance field"; + // These calls use the raw access flags to check whether the whole dex field is valid. + + if (!*have_class && (kStatic ? it->HasNextStaticField() : it->HasNextInstanceField())) { + *have_class = FindClassIndexAndDef(it->GetMemberIndex(), true, class_type_index, class_def); + if (!*have_class) { + // Should have really found one. + ErrorStringPrintf("could not find declaring class for %s index %" PRIu32, + kTypeDescr, + it->GetMemberIndex()); + return false; + } + } + DCHECK(*class_def != nullptr || + !(kStatic ? it->HasNextStaticField() : it->HasNextInstanceField())); + uint32_t prev_index = 0; for (; kStatic ? it->HasNextStaticField() : it->HasNextInstanceField(); it->Next()) { uint32_t curr_index = it->GetMemberIndex(); - if (!CheckOrderAndGetClassDef(true, - kStatic ? "static field" : "instance field", - curr_index, - prev_index, - have_class, - class_type_index, - class_def)) { + if (!CheckOrder(kTypeDescr, curr_index, prev_index)) { return false; } - DCHECK(class_def != nullptr); if (!CheckClassDataItemField(curr_index, it->GetRawMemberAccessFlags(), (*class_def)->access_flags_, @@ -1158,19 +1152,28 @@ bool DexFileVerifier::CheckIntraClassDataItemMethods( bool* have_class, dex::TypeIndex* class_type_index, const DexFile::ClassDef** class_def) { + DCHECK(it != nullptr); + constexpr const char* kTypeDescr = kDirect ? "direct method" : "virtual method"; + + if (!*have_class && (kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod())) { + *have_class = FindClassIndexAndDef(it->GetMemberIndex(), false, class_type_index, class_def); + if (!*have_class) { + // Should have really found one. + ErrorStringPrintf("could not find declaring class for %s index %" PRIu32, + kTypeDescr, + it->GetMemberIndex()); + return false; + } + } + DCHECK(*class_def != nullptr || + !(kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod())); + uint32_t prev_index = 0; for (; kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod(); it->Next()) { uint32_t curr_index = it->GetMemberIndex(); - if (!CheckOrderAndGetClassDef(false, - kDirect ? "direct method" : "virtual method", - curr_index, - prev_index, - have_class, - class_type_index, - class_def)) { + if (!CheckOrder(kTypeDescr, curr_index, prev_index)) { return false; } - DCHECK(class_def != nullptr); if (!CheckClassDataItemMethod(curr_index, it->GetRawMemberAccessFlags(), (*class_def)->access_flags_, diff --git a/libdexfile/dex/dex_file_verifier.h b/libdexfile/dex/dex_file_verifier.h index 3bddc77338..4b733639d5 100644 --- a/libdexfile/dex/dex_file_verifier.h +++ b/libdexfile/dex/dex_file_verifier.h @@ -87,13 +87,8 @@ class DexFileVerifier { uint32_t code_offset, ClassDataItemIterator* direct_it, bool expect_direct); - bool CheckOrderAndGetClassDef(bool is_field, - const char* type_descr, - uint32_t curr_index, - uint32_t prev_index, - bool* have_class, - dex::TypeIndex* class_type_index, - const DexFile::ClassDef** class_def); + ALWAYS_INLINE + bool CheckOrder(const char* type_descr, uint32_t curr_index, uint32_t prev_index); bool CheckStaticFieldTypes(const DexFile::ClassDef* class_def); bool CheckPadding(size_t offset, uint32_t aligned_offset, DexFile::MapItemType type); -- GitLab From 3153915714c48a57fecb5e41ca10b7fb16df52f6 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 25 Apr 2018 13:34:29 -0700 Subject: [PATCH 304/749] ART: Use alloca to avoid an expensive allocation Attempt to avoid a new[] for common cases. Bug: 78568168 Test: m test-art-host Change-Id: I393ecdeef58f885f6e7a6ffa216ed37a4726c227 --- libdexfile/dex/dex_file_verifier.cc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc index 3f72a25940..a91567fa1a 100644 --- a/libdexfile/dex/dex_file_verifier.cc +++ b/libdexfile/dex/dex_file_verifier.cc @@ -1300,7 +1300,19 @@ bool DexFileVerifier::CheckIntraCodeItem() { return false; } - std::unique_ptr handler_offsets(new uint32_t[handlers_size]); + // Avoid an expensive allocation, if possible. + std::unique_ptr handler_offsets_uptr; + uint32_t* handler_offsets; + constexpr size_t kAllocaMaxSize = 1024; + if (handlers_size < kAllocaMaxSize/sizeof(uint32_t)) { + handler_offsets = + AlignDown(reinterpret_cast(alloca((handlers_size + 1) * sizeof(uint32_t))), + alignof(uint32_t[])); + } else { + handler_offsets_uptr.reset(new uint32_t[handlers_size]); + handler_offsets = handler_offsets_uptr.get(); + } + if (!CheckAndGetHandlerOffsets(code_item, &handler_offsets[0], handlers_size)) { return false; } -- GitLab From c0147ed51f4e4d6ecfc9fe71e8d7619d79bf1d6a Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 25 Apr 2018 13:40:09 -0700 Subject: [PATCH 305/749] ART: Templatize dex-file-verifier check code Templatize by item type to avoid repeated checks. This saves about 1.4% of instructions. Bug: 78568168 Test: m test-art-host Change-Id: I002c99ac1241a33ada85c1a991fb77ff06f76ebe --- libdexfile/dex/dex_file_verifier.cc | 132 ++++++++++++++++++++++++---- libdexfile/dex/dex_file_verifier.h | 7 +- 2 files changed, 122 insertions(+), 17 deletions(-) diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc index a91567fa1a..73be1665d0 100644 --- a/libdexfile/dex/dex_file_verifier.cc +++ b/libdexfile/dex/dex_file_verifier.cc @@ -1637,11 +1637,11 @@ bool DexFileVerifier::CheckIntraAnnotationsDirectoryItem() { return true; } -bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_count, - DexFile::MapItemType type) { +template +bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_count) { // Get the right alignment mask for the type of section. size_t alignment_mask; - switch (type) { + switch (kType) { case DexFile::kDexTypeClassDataItem: case DexFile::kDexTypeStringDataItem: case DexFile::kDexTypeDebugInfoItem: @@ -1659,13 +1659,13 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_c size_t aligned_offset = (offset + alignment_mask) & ~alignment_mask; // Check the padding between items. - if (!CheckPadding(offset, aligned_offset, type)) { + if (!CheckPadding(offset, aligned_offset, kType)) { return false; } // Check depending on the section type. const uint8_t* start_ptr = ptr_; - switch (type) { + switch (kType) { case DexFile::kDexTypeStringIdItem: { if (!CheckListSize(ptr_, 1, sizeof(DexFile::StringId), "string_ids")) { return false; @@ -1788,17 +1788,17 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_c } if (start_ptr == ptr_) { - ErrorStringPrintf("Unknown map item type %x", type); + ErrorStringPrintf("Unknown map item type %x", kType); return false; } - if (IsDataSectionType(type)) { + if (IsDataSectionType(kType)) { if (aligned_offset == 0u) { ErrorStringPrintf("Item %d offset is 0", i); return false; } DCHECK(offset_to_type_map_.Find(aligned_offset) == offset_to_type_map_.end()); - offset_to_type_map_.Insert(std::pair(aligned_offset, type)); + offset_to_type_map_.Insert(std::pair(aligned_offset, kType)); } aligned_offset = ptr_ - begin_; @@ -1813,6 +1813,55 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_c return true; } +bool DexFileVerifier::CheckIntraSectionIterateByType(size_t offset, + uint32_t count, + DexFile::MapItemType type) { + switch (type) { + case DexFile::kDexTypeHeaderItem: + return CheckIntraSectionIterate(offset, count); + case DexFile::kDexTypeStringIdItem: + return CheckIntraSectionIterate(offset, count); + case DexFile::kDexTypeTypeIdItem: + return CheckIntraSectionIterate(offset, count); + case DexFile::kDexTypeProtoIdItem: + return CheckIntraSectionIterate(offset, count); + case DexFile::kDexTypeFieldIdItem: + return CheckIntraSectionIterate(offset, count); + case DexFile::kDexTypeMethodIdItem: + return CheckIntraSectionIterate(offset, count); + case DexFile::kDexTypeClassDefItem: + return CheckIntraSectionIterate(offset, count); + case DexFile::kDexTypeCallSiteIdItem: + return CheckIntraSectionIterate(offset, count); + case DexFile::kDexTypeMethodHandleItem: + return CheckIntraSectionIterate(offset, count); + case DexFile::kDexTypeMapList: + return CheckIntraSectionIterate(offset, count); + case DexFile::kDexTypeTypeList: + return CheckIntraSectionIterate(offset, count); + case DexFile::kDexTypeAnnotationSetRefList: + return CheckIntraSectionIterate(offset, count); + case DexFile::kDexTypeAnnotationSetItem: + return CheckIntraSectionIterate(offset, count); + case DexFile::kDexTypeClassDataItem: + return CheckIntraSectionIterate(offset, count); + case DexFile::kDexTypeCodeItem: + return CheckIntraSectionIterate(offset, count); + case DexFile::kDexTypeStringDataItem: + return CheckIntraSectionIterate(offset, count); + case DexFile::kDexTypeDebugInfoItem: + return CheckIntraSectionIterate(offset, count); + case DexFile::kDexTypeAnnotationItem: + return CheckIntraSectionIterate(offset, count); + case DexFile::kDexTypeEncodedArrayItem: + return CheckIntraSectionIterate(offset, count); + case DexFile::kDexTypeAnnotationsDirectoryItem: + return CheckIntraSectionIterate(offset, count); + } + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); +} + bool DexFileVerifier::CheckIntraIdSection(size_t offset, uint32_t count, DexFile::MapItemType type) { @@ -1860,12 +1909,11 @@ bool DexFileVerifier::CheckIntraIdSection(size_t offset, return false; } - return CheckIntraSectionIterate(offset, count, type); + return CheckIntraSectionIterateByType(offset, count, type); } -bool DexFileVerifier::CheckIntraDataSection(size_t offset, - uint32_t count, - DexFile::MapItemType type) { +template +bool DexFileVerifier::CheckIntraDataSection(size_t offset, uint32_t count) { size_t data_start = header_->data_off_; size_t data_end = data_start + header_->data_size_; @@ -1875,7 +1923,7 @@ bool DexFileVerifier::CheckIntraDataSection(size_t offset, return false; } - if (!CheckIntraSectionIterate(offset, count, type)) { + if (!CheckIntraSectionIterate(offset, count)) { return false; } @@ -1952,21 +2000,75 @@ bool DexFileVerifier::CheckIntraSection() { offset = section_offset + sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem)); break; case DexFile::kDexTypeMethodHandleItem: + CheckIntraSectionIterate(section_offset, section_count); + offset = ptr_ - begin_; + break; case DexFile::kDexTypeCallSiteIdItem: - CheckIntraSectionIterate(section_offset, section_count, type); + CheckIntraSectionIterate(section_offset, section_count); offset = ptr_ - begin_; break; case DexFile::kDexTypeTypeList: + if (!CheckIntraDataSection(section_offset, section_count)) { + return false; + } + offset = ptr_ - begin_; + break; case DexFile::kDexTypeAnnotationSetRefList: + if (!CheckIntraDataSection(section_offset, + section_count)) { + return false; + } + offset = ptr_ - begin_; + break; case DexFile::kDexTypeAnnotationSetItem: + if (!CheckIntraDataSection(section_offset, + section_count)) { + return false; + } + offset = ptr_ - begin_; + break; case DexFile::kDexTypeClassDataItem: + if (!CheckIntraDataSection(section_offset, section_count)) { + return false; + } + offset = ptr_ - begin_; + break; case DexFile::kDexTypeCodeItem: + if (!CheckIntraDataSection(section_offset, section_count)) { + return false; + } + offset = ptr_ - begin_; + break; case DexFile::kDexTypeStringDataItem: + if (!CheckIntraDataSection(section_offset, + section_count)) { + return false; + } + offset = ptr_ - begin_; + break; case DexFile::kDexTypeDebugInfoItem: + if (!CheckIntraDataSection(section_offset, section_count)) { + return false; + } + offset = ptr_ - begin_; + break; case DexFile::kDexTypeAnnotationItem: + if (!CheckIntraDataSection(section_offset, + section_count)) { + return false; + } + offset = ptr_ - begin_; + break; case DexFile::kDexTypeEncodedArrayItem: + if (!CheckIntraDataSection(section_offset, + section_count)) { + return false; + } + offset = ptr_ - begin_; + break; case DexFile::kDexTypeAnnotationsDirectoryItem: - if (!CheckIntraDataSection(section_offset, section_count, type)) { + if (!CheckIntraDataSection(section_offset, + section_count)) { return false; } offset = ptr_ - begin_; diff --git a/libdexfile/dex/dex_file_verifier.h b/libdexfile/dex/dex_file_verifier.h index 4b733639d5..a4c23bb08a 100644 --- a/libdexfile/dex/dex_file_verifier.h +++ b/libdexfile/dex/dex_file_verifier.h @@ -119,9 +119,12 @@ class DexFileVerifier { bool CheckIntraAnnotationItem(); bool CheckIntraAnnotationsDirectoryItem(); - bool CheckIntraSectionIterate(size_t offset, uint32_t count, DexFile::MapItemType type); + template + bool CheckIntraSectionIterate(size_t offset, uint32_t count); + bool CheckIntraSectionIterateByType(size_t offset, uint32_t count, DexFile::MapItemType type); bool CheckIntraIdSection(size_t offset, uint32_t count, DexFile::MapItemType type); - bool CheckIntraDataSection(size_t offset, uint32_t count, DexFile::MapItemType type); + template + bool CheckIntraDataSection(size_t offset, uint32_t count); bool CheckIntraSection(); bool CheckOffsetToTypeMap(size_t offset, uint16_t type); -- GitLab From 0b9b1c6d5bb8b10aac7bd686fe5e4b947fdfaccc Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 25 Apr 2018 13:20:30 -0700 Subject: [PATCH 306/749] ART: Improve bit-test function Improve the test for single-bit-set by using POPCOUNT. Saves about .1% of instructions. Bug: 78568168 Test: m test-art-host Change-Id: Ifaa5987f1ca9e7507ac6c57dac7da5fae6dcef8d --- libdexfile/dex/dex_file_verifier.cc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc index 73be1665d0..5f7b4e8ba6 100644 --- a/libdexfile/dex/dex_file_verifier.cc +++ b/libdexfile/dex/dex_file_verifier.cc @@ -3015,11 +3015,14 @@ void DexFileVerifier::ErrorStringPrintf(const char* fmt, ...) { } // Fields and methods may have only one of public/protected/private. -static bool CheckAtMostOneOfPublicProtectedPrivate(uint32_t flags) { - size_t count = (((flags & kAccPublic) == 0) ? 0 : 1) + - (((flags & kAccProtected) == 0) ? 0 : 1) + - (((flags & kAccPrivate) == 0) ? 0 : 1); - return count <= 1; +ALWAYS_INLINE +static constexpr bool CheckAtMostOneOfPublicProtectedPrivate(uint32_t flags) { + // Semantically we want 'return POPCOUNT(flags & kAcc) <= 1;'. + static_assert(IsPowerOfTwo(0), "0 not marked as power of two"); + static_assert(IsPowerOfTwo(kAccPublic), "kAccPublic not marked as power of two"); + static_assert(IsPowerOfTwo(kAccProtected), "kAccProtected not marked as power of two"); + static_assert(IsPowerOfTwo(kAccPrivate), "kAccPrivate not marked as power of two"); + return IsPowerOfTwo(flags & (kAccPublic | kAccProtected | kAccPrivate)); } // Helper functions to retrieve names from the dex file. We do not want to rely on DexFile -- GitLab From 395071d7118acdd1718f522be692aab54b9c4afb Mon Sep 17 00:00:00 2001 From: Steve Austin Date: Wed, 25 Apr 2018 14:07:45 -0700 Subject: [PATCH 307/749] Set GS register for Fuchsia Test: Tested with Fuchsia build system and toolchain Change-Id: I85f9bdb59090df5cf4c21ef585f813730d84ba3f --- runtime/arch/x86_64/thread_x86_64.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/runtime/arch/x86_64/thread_x86_64.cc b/runtime/arch/x86_64/thread_x86_64.cc index 19d25f6990..5c0446fb4c 100644 --- a/runtime/arch/x86_64/thread_x86_64.cc +++ b/runtime/arch/x86_64/thread_x86_64.cc @@ -25,6 +25,10 @@ #include #include #include +#elif defined(__Fuchsia__) +#include +#include +#include #endif namespace art { @@ -40,6 +44,13 @@ void Thread::InitCpu() { #if defined(__linux__) arch_prctl(ARCH_SET_GS, this); +#elif defined(__Fuchsia__) + Thread* thread_ptr = this; + zx_status_t status = zx_object_set_property(zx_thread_self(), + ZX_PROP_REGISTER_GS, + &thread_ptr, + sizeof(thread_ptr)); + CHECK_EQ(status, ZX_OK) << "failed to set GS register"; #else UNIMPLEMENTED(FATAL) << "Need to set GS"; #endif -- GitLab From 1bfe4bd4c444017cf40ccadcfbf20cb0ceb37880 Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Thu, 26 Apr 2018 16:00:11 -0700 Subject: [PATCH 308/749] Do not verify apks when processing profiles It's a waste of time and it causes problems when taking snapshots for the boot image profile. Bug: 73313191 Test: profile_assistant_test Change-Id: I8e838af9515b41402eda455c23741a855c48ff98 --- profman/profile_assistant_test.cc | 26 ++++++++------------------ profman/profman.cc | 13 +++---------- tools/generate-boot-image-profile.sh | 2 +- 3 files changed, 12 insertions(+), 29 deletions(-) diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index 72c285a3f5..3106384665 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -243,8 +243,7 @@ class ProfileAssistantTest : public CommonRuntimeTest { bool CreateProfile(const std::string& profile_file_contents, const std::string& filename, - const std::string& dex_location, - bool skip_verification) { + const std::string& dex_location) { ScratchFile class_names_file; File* file = class_names_file.GetFile(); EXPECT_TRUE(file->WriteFully(profile_file_contents.c_str(), profile_file_contents.length())); @@ -257,9 +256,6 @@ class ProfileAssistantTest : public CommonRuntimeTest { argv_str.push_back("--reference-profile-file=" + filename); argv_str.push_back("--apk=" + dex_location); argv_str.push_back("--dex-location=" + dex_location); - if (skip_verification) { - argv_str.push_back("--skip-apk-verification"); - } std::string error; EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0); return true; @@ -276,7 +272,6 @@ class ProfileAssistantTest : public CommonRuntimeTest { argv_str.push_back("--profile-file=" + filename); argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]); argv_str.push_back("--dex-location=" + GetLibCoreDexFileNames()[0]); - argv_str.push_back("--skip-apk-verification"); argv_str.push_back("--dump-output-to-fd=" + std::to_string(GetFd(output_file))); std::string error; EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0); @@ -307,8 +302,7 @@ class ProfileAssistantTest : public CommonRuntimeTest { ScratchFile profile_file; EXPECT_TRUE(CreateProfile(input_file_contents, profile_file.GetFilename(), - GetLibCoreDexFileNames()[0], - /* skip_verification */ true)); + GetLibCoreDexFileNames()[0])); profile_file.GetFile()->ResetOffset(); EXPECT_TRUE(DumpClassesAndMethods(profile_file.GetFilename(), output_file_contents)); return true; @@ -715,8 +709,7 @@ TEST_F(ProfileAssistantTest, TestProfileCreationGenerateMethods) { ScratchFile profile_file; EXPECT_TRUE(CreateProfile(input_file_contents, profile_file.GetFilename(), - GetLibCoreDexFileNames()[0], - /* skip_verification */ true)); + GetLibCoreDexFileNames()[0])); ProfileCompilationInfo info; profile_file.GetFile()->ResetOffset(); ASSERT_TRUE(info.Load(GetFd(profile_file))); @@ -773,7 +766,7 @@ TEST_F(ProfileAssistantTest, TestBootImageProfile) { kUncommonDirtyClass; profiles.emplace_back(ScratchFile()); EXPECT_TRUE(CreateProfile( - dex1, profiles.back().GetFilename(), core_dex, /* skip_verification */ true)); + dex1, profiles.back().GetFilename(), core_dex)); // Create a bunch of boot profiles. std::string dex2 = @@ -784,7 +777,7 @@ TEST_F(ProfileAssistantTest, TestBootImageProfile) { kUncommonDirtyClass; profiles.emplace_back(ScratchFile()); EXPECT_TRUE(CreateProfile( - dex2, profiles.back().GetFilename(), core_dex, /* skip_verification */ true)); + dex2, profiles.back().GetFilename(), core_dex)); // Create a bunch of boot profiles. std::string dex3 = @@ -794,7 +787,7 @@ TEST_F(ProfileAssistantTest, TestBootImageProfile) { kDirtyClass + "\n"; profiles.emplace_back(ScratchFile()); EXPECT_TRUE(CreateProfile( - dex3, profiles.back().GetFilename(), core_dex, /* skip_verification */ true)); + dex3, profiles.back().GetFilename(), core_dex)); // Generate the boot profile. ScratchFile out_profile; @@ -807,7 +800,6 @@ TEST_F(ProfileAssistantTest, TestBootImageProfile) { args.push_back("--reference-profile-file=" + out_profile.GetFilename()); args.push_back("--apk=" + core_dex); args.push_back("--dex-location=" + core_dex); - args.push_back("--skip-apk-verification"); for (const ScratchFile& profile : profiles) { args.push_back("--profile-file=" + profile.GetFilename()); } @@ -903,8 +895,7 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) { ScratchFile profile_file; ASSERT_TRUE(CreateProfile(input_file_contents, profile_file.GetFilename(), - GetTestDexFileName("ProfileTestMultiDex"), - /* skip_verification */ false)); + GetTestDexFileName("ProfileTestMultiDex"))); // Load the profile from disk. ProfileCompilationInfo info; @@ -1054,8 +1045,7 @@ TEST_F(ProfileAssistantTest, TestProfileCreateWithInvalidData) { std::string dex_filename = GetTestDexFileName("ProfileTestMultiDex"); ASSERT_TRUE(CreateProfile(input_file_contents, profile_file.GetFilename(), - dex_filename, - /* skip_verification */ false)); + dex_filename)); // Load the profile from disk. ProfileCompilationInfo info; diff --git a/profman/profman.cc b/profman/profman.cc index f2cec47da3..b9f0209346 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -187,7 +187,6 @@ class ProfMan FINAL { dump_only_(false), dump_classes_and_methods_(false), generate_boot_image_profile_(false), - skip_apk_verification_(false), dump_output_to_fd_(kInvalidFd), test_profile_num_dex_(kDefaultTestProfileNumDex), test_profile_method_percerntage_(kDefaultTestProfileMethodPercentage), @@ -231,8 +230,6 @@ class ProfMan FINAL { ParseUintOption(option, "--dump-output-to-fd", &dump_output_to_fd_, Usage); } else if (option == "--generate-boot-image-profile") { generate_boot_image_profile_ = true; - } else if (option == "--skip-apk-verification") { - skip_apk_verification_ = true; } else if (option.starts_with("--boot-image-class-threshold=")) { ParseUintOption(option, "--boot-image-class-threshold", @@ -369,10 +366,6 @@ class ProfMan FINAL { return result; } - bool ShouldSkipApkVerification() const { - return skip_apk_verification_; - } - bool GetProfileFilterKeyFromApks(std::set* profile_filter_keys) { auto process_fn = [profile_filter_keys](std::unique_ptr&& dex_file) { // Store the profile key of the location instead of the location itself. @@ -424,10 +417,11 @@ class ProfMan FINAL { std::string error_msg; const ArtDexFileLoader dex_file_loader; std::vector> dex_files_for_location; + // We do not need to verify the apk for processing profiles. if (use_apk_fd_list) { if (dex_file_loader.OpenZip(apks_fd_[i], dex_locations_[i], - /* verify */ !ShouldSkipApkVerification(), + /* verify */ false, kVerifyChecksum, &error_msg, &dex_files_for_location)) { @@ -438,7 +432,7 @@ class ProfMan FINAL { } else { if (dex_file_loader.Open(apk_files_[i].c_str(), dex_locations_[i], - /* verify */ !ShouldSkipApkVerification(), + /* verify */ false, kVerifyChecksum, &error_msg, &dex_files_for_location)) { @@ -1260,7 +1254,6 @@ class ProfMan FINAL { bool dump_only_; bool dump_classes_and_methods_; bool generate_boot_image_profile_; - bool skip_apk_verification_; int dump_output_to_fd_; BootImageOptions boot_image_options_; std::string test_profile_; diff --git a/tools/generate-boot-image-profile.sh b/tools/generate-boot-image-profile.sh index ee53f43865..44c64d21bf 100755 --- a/tools/generate-boot-image-profile.sh +++ b/tools/generate-boot-image-profile.sh @@ -48,7 +48,7 @@ done # Boot jars have hidden API access flags which do not pass dex file # verification. Skip it. -jar_args=("--skip-apk-verification") +jar_args=() boot_jars=$("$ANDROID_BUILD_TOP"/art/tools/bootjars.sh --target) jar_dir=$ANDROID_BUILD_TOP/$(get_build_var TARGET_OUT_JAVA_LIBRARIES) for file in $boot_jars; do -- GitLab From 10a3f319c3d925b7038262ef8c3a6f552abd62fa Mon Sep 17 00:00:00 2001 From: Alex Light Date: Fri, 27 Apr 2018 13:15:20 -0700 Subject: [PATCH 309/749] Add additional logging to test 1940 Test 1940 occasionally times out on test servers. Add additional logging to the test so we can figure out where exactly it is getting stuck. Test: ./test.py --host -j50 Bug: 70988713 Change-Id: I04e5e9ecbc02907f8606c4a14578994d5f53de45 --- test/1940-ddms-ext/expected.txt | 4 ++++ test/1940-ddms-ext/src-art/art/Test1940.java | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/test/1940-ddms-ext/expected.txt b/test/1940-ddms-ext/expected.txt index 1a457a01a5..5af111676b 100644 --- a/test/1940-ddms-ext/expected.txt +++ b/test/1940-ddms-ext/expected.txt @@ -16,6 +16,10 @@ JVMTI returned chunk: Chunk(Type: 0xFADE7357, Len: 0, data: []) Sending data [1] to chunk handler 305419896 MyDdmHandler: Chunk received: Chunk(Type: 0x12345678, Len: 1, data: [1]) Got error: JVMTI_ERROR_INTERNAL +threadNotify started! +Target thread started! +Target thread finished! +threadNotify Disabled! Saw expected thread events. Expected chunk type published: 1213221190 Expected chunk type published: 1297109829 diff --git a/test/1940-ddms-ext/src-art/art/Test1940.java b/test/1940-ddms-ext/src-art/art/Test1940.java index 226fe350bd..2957f632ba 100644 --- a/test/1940-ddms-ext/src-art/art/Test1940.java +++ b/test/1940-ddms-ext/src-art/art/Test1940.java @@ -178,10 +178,14 @@ public class Test1940 { } }; DdmVmInternal.threadNotify(true); + System.out.println("threadNotify started!"); final Thread thr = new Thread(() -> { return; }, "THREAD"); thr.start(); + System.out.println("Target thread started!"); thr.join(); + System.out.println("Target thread finished!"); DdmVmInternal.threadNotify(false); + System.out.println("threadNotify Disabled!"); // Make sure we saw at least one of Thread-create, Thread name, & thread death. if (!types_seen[0] || !types_seen[1] || !types_seen[2]) { System.out.println("Didn't see expected chunks for thread creation! got: " + -- GitLab From 54e45c59de68fabbc06ffda38fcb1ed57e1b913a Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Fri, 27 Apr 2018 13:57:21 -0700 Subject: [PATCH 310/749] Bug fix: add condition back Rationale: CL https://android-review.googlesource.com/c/platform/art/+/673413 left out this important condition Test: test-art-host Change-Id: Id233f07f2089e5269e7982146ff45864dc3e6acf --- compiler/optimizing/inliner.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 3800c96937..ffa000e34e 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -178,7 +178,7 @@ bool HInliner::Run() { if (callee_name.find("$noinline$") == std::string::npos) { if (TryInline(call)) { didInline = true; - } else { + } else if (honor_inline_directives) { bool should_have_inlined = (callee_name.find("$inline$") != std::string::npos); CHECK(!should_have_inlined) << "Could not inline " << callee_name; } -- GitLab From 60eff82f4a6eb3d86a79537d0d10053f12fd3c06 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Fri, 27 Apr 2018 14:19:41 -0700 Subject: [PATCH 311/749] Make test 1935 avoid entering jit code before test thread starts Test 1935 was checking that a method was in OSR code but it was possible that the JIT actually managed to fully compile the method before we began executing it, meaning we would be running full JIT code. This would cause the test to fail. To fix this we changed the test to leave the JIT disabled until the test method is already executing. Test: ./test.py --host -j50 Bug: 77306669 Change-Id: I1d6053b32ef4b1d0fcca39be71cf83a34c7e96ce --- test/1935-get-set-current-frame-jit/src/Main.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/test/1935-get-set-current-frame-jit/src/Main.java b/test/1935-get-set-current-frame-jit/src/Main.java index 70e94c439f..97f0973823 100644 --- a/test/1935-get-set-current-frame-jit/src/Main.java +++ b/test/1935-get-set-current-frame-jit/src/Main.java @@ -58,6 +58,10 @@ public class Main { } public void run() { int TARGET = 42; + if (hasJit() && expectOsr && !Main.isInterpreted()) { + System.out.println("Unexpectedly in jit code prior to restarting the JIT!"); + } + startJit(); // We will suspend the thread during this loop. while (continueBusyLoop) { inBusyLoop = true; @@ -91,7 +95,9 @@ public class Main { public static void runGet() throws Exception { Method target = IntRunner.class.getDeclaredMethod("run"); - // Get Int + // Stop jit temporarily. It will be restarted by the test itself. + stopJit(); + // Get Int. IntRunner int_runner = new IntRunner(true); Thread target_get = new Thread(int_runner, "GetLocalInt - Target"); target_get.start(); @@ -121,7 +127,9 @@ public class Main { public static void runSet() throws Exception { Method target = IntRunner.class.getDeclaredMethod("run"); - // Set Int + // Stop jit temporarily. It will be restarted by the test itself. + stopJit(); + // Set Int. Even if we start out in JIT code somehow we should be pushed out of it. IntRunner int_runner = new IntRunner(false); Thread target_set = new Thread(int_runner, "SetLocalInt - Target"); target_set.start(); @@ -173,5 +181,7 @@ public class Main { public static native boolean isInterpreted(); public static native boolean isInOsrCode(String methodName); + public static native boolean stopJit(); + public static native boolean startJit(); public static native boolean hasJit(); } -- GitLab From 2e14868d43b4f8c21f8567f336a9bf1a2576dc1b Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Wed, 18 Apr 2018 16:11:12 -0700 Subject: [PATCH 312/749] Step 2 of 2: conditional passes. Rationale: The change introduces actual conditional passes (dependence on inliner). This ensures more cases are optimized downstream without needlessly introducing compile-time. NOTE: Some checker tests needed to be rewritten due to subtle changes in the phase ordering. No optimizations were harmed in the process, though. Bug: b/78171933, b/74026074 Test: test-art-host,target Change-Id: I335260df780e14ba1f22499ad74d79060c7be44d --- compiler/optimizing/optimization.cc | 59 +++++---- compiler/optimizing/optimization.h | 21 ++- compiler/optimizing/optimizing_compiler.cc | 69 +++++++--- .../src/Main.java | 12 +- .../src/Main.java | 113 ++++++++-------- .../smali/TestCase.smali | 22 +++- .../smali/TestCase.smali | 4 +- .../551-checker-shifter-operand/src/Main.java | 50 ++++--- .../src/Main.java | 12 +- .../smali/SmaliTests.smali | 4 +- test/631-checker-get-class/src/Main.java | 36 ++++-- test/660-checker-sad-byte/src/Main.java | 32 ++--- test/660-checker-sad-char/src/Main.java | 32 ++--- test/660-checker-sad-int/src/Main.java | 34 ++--- test/660-checker-sad-long/src/Main.java | 18 +-- test/660-checker-sad-short/src/Main.java | 32 ++--- test/679-checker-minmax/src/Main.java | 122 +++++++++--------- test/681-checker-abs/src/Main.java | 52 ++++---- 18 files changed, 416 insertions(+), 308 deletions(-) diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc index 57db7a634c..d37c43db81 100644 --- a/compiler/optimizing/optimization.cc +++ b/compiler/optimizing/optimization.cc @@ -121,12 +121,15 @@ const char* OptimizationPassName(OptimizationPass pass) { case OptimizationPass::kX86MemoryOperandGeneration: return x86::X86MemoryOperandGeneration::kX86MemoryOperandGenerationPassName; #endif + case OptimizationPass::kNone: + LOG(FATAL) << "kNone does not represent an actual pass"; + UNREACHABLE(); } } -#define X(x) if (name == OptimizationPassName((x))) return (x) +#define X(x) if (pass_name == OptimizationPassName((x))) return (x) -OptimizationPass OptimizationPassByName(const std::string& name) { +OptimizationPass OptimizationPassByName(const std::string& pass_name) { X(OptimizationPass::kBoundsCheckElimination); X(OptimizationPass::kCHAGuardOptimization); X(OptimizationPass::kCodeSinking); @@ -160,7 +163,7 @@ OptimizationPass OptimizationPassByName(const std::string& name) { X(OptimizationPass::kPcRelativeFixupsX86); X(OptimizationPass::kX86MemoryOperandGeneration); #endif - LOG(FATAL) << "Cannot find optimization " << name; + LOG(FATAL) << "Cannot find optimization " << pass_name; UNREACHABLE(); } @@ -187,9 +190,9 @@ ArenaVector ConstructOptimizations( // Loop over the requested optimizations. for (size_t i = 0; i < length; i++) { - OptimizationPass pass = definitions[i].first; - const char* alt_name = definitions[i].second; - const char* name = alt_name != nullptr + OptimizationPass pass = definitions[i].pass; + const char* alt_name = definitions[i].pass_name; + const char* pass_name = alt_name != nullptr ? alt_name : OptimizationPassName(pass); HOptimization* opt = nullptr; @@ -199,47 +202,48 @@ ArenaVector ConstructOptimizations( // Analysis passes (kept in most recent for subsequent passes). // case OptimizationPass::kSideEffectsAnalysis: - opt = most_recent_side_effects = new (allocator) SideEffectsAnalysis(graph, name); + opt = most_recent_side_effects = new (allocator) SideEffectsAnalysis(graph, pass_name); break; case OptimizationPass::kInductionVarAnalysis: - opt = most_recent_induction = new (allocator) HInductionVarAnalysis(graph, name); + opt = most_recent_induction = new (allocator) HInductionVarAnalysis(graph, pass_name); break; case OptimizationPass::kLoadStoreAnalysis: - opt = most_recent_lsa = new (allocator) LoadStoreAnalysis(graph, name); + opt = most_recent_lsa = new (allocator) LoadStoreAnalysis(graph, pass_name); break; // // Passes that need prior analysis. // case OptimizationPass::kGlobalValueNumbering: CHECK(most_recent_side_effects != nullptr); - opt = new (allocator) GVNOptimization(graph, *most_recent_side_effects, name); + opt = new (allocator) GVNOptimization(graph, *most_recent_side_effects, pass_name); break; case OptimizationPass::kInvariantCodeMotion: CHECK(most_recent_side_effects != nullptr); - opt = new (allocator) LICM(graph, *most_recent_side_effects, stats, name); + opt = new (allocator) LICM(graph, *most_recent_side_effects, stats, pass_name); break; case OptimizationPass::kLoopOptimization: CHECK(most_recent_induction != nullptr); - opt = new (allocator) HLoopOptimization(graph, driver, most_recent_induction, stats, name); + opt = new (allocator) HLoopOptimization( + graph, driver, most_recent_induction, stats, pass_name); break; case OptimizationPass::kBoundsCheckElimination: CHECK(most_recent_side_effects != nullptr && most_recent_induction != nullptr); opt = new (allocator) BoundsCheckElimination( - graph, *most_recent_side_effects, most_recent_induction, name); + graph, *most_recent_side_effects, most_recent_induction, pass_name); break; case OptimizationPass::kLoadStoreElimination: CHECK(most_recent_side_effects != nullptr && most_recent_induction != nullptr); opt = new (allocator) LoadStoreElimination( - graph, *most_recent_side_effects, *most_recent_lsa, stats, name); + graph, *most_recent_side_effects, *most_recent_lsa, stats, pass_name); break; // // Regular passes. // case OptimizationPass::kConstantFolding: - opt = new (allocator) HConstantFolding(graph, name); + opt = new (allocator) HConstantFolding(graph, pass_name); break; case OptimizationPass::kDeadCodeElimination: - opt = new (allocator) HDeadCodeElimination(graph, stats, name); + opt = new (allocator) HDeadCodeElimination(graph, stats, pass_name); break; case OptimizationPass::kInliner: { CodeItemDataAccessor accessor(*dex_compilation_unit.GetDexFile(), @@ -256,33 +260,33 @@ ArenaVector ConstructOptimizations( /* total_number_of_instructions */ 0, /* parent */ nullptr, /* depth */ 0, - name); + pass_name); break; } case OptimizationPass::kSharpening: - opt = new (allocator) HSharpening(graph, codegen, driver, name); + opt = new (allocator) HSharpening(graph, codegen, driver, pass_name); break; case OptimizationPass::kSelectGenerator: - opt = new (allocator) HSelectGenerator(graph, handles, stats, name); + opt = new (allocator) HSelectGenerator(graph, handles, stats, pass_name); break; case OptimizationPass::kInstructionSimplifier: - opt = new (allocator) InstructionSimplifier(graph, codegen, driver, stats, name); + opt = new (allocator) InstructionSimplifier(graph, codegen, driver, stats, pass_name); break; case OptimizationPass::kIntrinsicsRecognizer: - opt = new (allocator) IntrinsicsRecognizer(graph, stats, name); + opt = new (allocator) IntrinsicsRecognizer(graph, stats, pass_name); break; case OptimizationPass::kCHAGuardOptimization: - opt = new (allocator) CHAGuardOptimization(graph, name); + opt = new (allocator) CHAGuardOptimization(graph, pass_name); break; case OptimizationPass::kCodeSinking: - opt = new (allocator) CodeSinking(graph, stats, name); + opt = new (allocator) CodeSinking(graph, stats, pass_name); break; case OptimizationPass::kConstructorFenceRedundancyElimination: - opt = new (allocator) ConstructorFenceRedundancyElimination(graph, stats, name); + opt = new (allocator) ConstructorFenceRedundancyElimination(graph, stats, pass_name); break; case OptimizationPass::kScheduling: opt = new (allocator) HInstructionScheduling( - graph, driver->GetInstructionSet(), codegen, name); + graph, driver->GetInstructionSet(), codegen, pass_name); break; // // Arch-specific passes. @@ -319,11 +323,14 @@ ArenaVector ConstructOptimizations( opt = new (allocator) x86::X86MemoryOperandGeneration(graph, codegen, stats); break; #endif + case OptimizationPass::kNone: + LOG(FATAL) << "kNone does not represent an actual pass"; + UNREACHABLE(); } // switch // Add each next optimization to result vector. CHECK(opt != nullptr); - DCHECK_STREQ(name, opt->GetPassName()); // sanity + DCHECK_STREQ(pass_name, opt->GetPassName()); // sanity optimizations.push_back(opt); } diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h index b00d686e5f..88b283cebf 100644 --- a/compiler/optimizing/optimization.h +++ b/compiler/optimizing/optimization.h @@ -102,21 +102,32 @@ enum class OptimizationPass { #if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64) kX86MemoryOperandGeneration, #endif + kNone, + kLast = kNone }; // Lookup name of optimization pass. const char* OptimizationPassName(OptimizationPass pass); // Lookup optimization pass by name. -OptimizationPass OptimizationPassByName(const std::string& name); +OptimizationPass OptimizationPassByName(const std::string& pass_name); // Optimization definition consisting of an optimization pass -// and an optional alternative name (nullptr denotes default). -typedef std::pair OptimizationDef; +// an optional alternative name (nullptr denotes default), and +// an optional pass dependence (kNone denotes no dependence). +struct OptimizationDef { + OptimizationDef(OptimizationPass p, const char* pn, OptimizationPass d) + : pass(p), pass_name(pn), depends_on(d) {} + OptimizationPass pass; + const char* pass_name; + OptimizationPass depends_on; +}; // Helper method for optimization definition array entries. -inline OptimizationDef OptDef(OptimizationPass pass, const char* name = nullptr) { - return std::make_pair(pass, name); +inline OptimizationDef OptDef(OptimizationPass pass, + const char* pass_name = nullptr, + OptimizationPass depends_on = OptimizationPass::kNone) { + return OptimizationDef(pass, pass_name, depends_on); } // Helper method to construct series of optimization passes. diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index f68bcbe59f..a6163a7fcf 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -313,11 +313,22 @@ class OptimizingCompiler FINAL : public Compiler { dex_compilation_unit, handles); DCHECK_EQ(length, optimizations.size()); - // Run the optimization passes one by one. + // Run the optimization passes one by one. Any "depends_on" pass refers back to + // the most recent occurrence of that pass, skipped or executed. + std::bitset(OptimizationPass::kLast) + 1u> pass_changes; + pass_changes[static_cast(OptimizationPass::kNone)] = true; bool change = false; for (size_t i = 0; i < length; ++i) { - PassScope scope(optimizations[i]->GetPassName(), pass_observer); - change |= optimizations[i]->Run(); + if (pass_changes[static_cast(definitions[i].depends_on)]) { + // Execute the pass and record whether it changed anything. + PassScope scope(optimizations[i]->GetPassName(), pass_observer); + bool pass_change = optimizations[i]->Run(); + pass_changes[static_cast(definitions[i].pass)] = pass_change; + change |= pass_change; + } else { + // Skip the pass and record that nothing changed. + pass_changes[static_cast(definitions[i].pass)] = false; + } } return change; } @@ -579,6 +590,7 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph, if (pass_names != nullptr) { // If passes were defined on command-line, build the optimization // passes and run these instead of the built-in optimizations. + // TODO: a way to define depends_on via command-line? const size_t length = pass_names->size(); std::vector optimizations; for (const std::string& pass_name : *pass_names) { @@ -596,36 +608,63 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph, } OptimizationDef optimizations[] = { + // Initial optimizations. OptDef(OptimizationPass::kIntrinsicsRecognizer), OptDef(OptimizationPass::kSharpening), OptDef(OptimizationPass::kConstantFolding), OptDef(OptimizationPass::kInstructionSimplifier), - OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$initial"), + OptDef(OptimizationPass::kDeadCodeElimination, + "dead_code_elimination$initial"), + // Inlining. OptDef(OptimizationPass::kInliner), - OptDef(OptimizationPass::kSideEffectsAnalysis, "side_effects$before_gvn"), + // Simplification (only if inlining occurred). + OptDef(OptimizationPass::kConstantFolding, + "constant_folding$after_inlining", + OptimizationPass::kInliner), + OptDef(OptimizationPass::kInstructionSimplifier, + "instruction_simplifier$after_inlining", + OptimizationPass::kInliner), + OptDef(OptimizationPass::kDeadCodeElimination, + "dead_code_elimination$after_inlining", + OptimizationPass::kInliner), + // GVN. + OptDef(OptimizationPass::kSideEffectsAnalysis, + "side_effects$before_gvn"), OptDef(OptimizationPass::kGlobalValueNumbering), + // Simplification (TODO: only if GVN occurred). OptDef(OptimizationPass::kSelectGenerator), - OptDef(OptimizationPass::kConstantFolding, "constant_folding$after_inlining"), - OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$after_inlining"), - OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$after_inlining"), - OptDef(OptimizationPass::kSideEffectsAnalysis, "side_effects$before_licm"), + OptDef(OptimizationPass::kConstantFolding, + "constant_folding$after_gvn"), + OptDef(OptimizationPass::kInstructionSimplifier, + "instruction_simplifier$after_gvn"), + OptDef(OptimizationPass::kDeadCodeElimination, + "dead_code_elimination$after_gvn"), + // High-level optimizations. + OptDef(OptimizationPass::kSideEffectsAnalysis, + "side_effects$before_licm"), OptDef(OptimizationPass::kInvariantCodeMotion), OptDef(OptimizationPass::kInductionVarAnalysis), OptDef(OptimizationPass::kBoundsCheckElimination), OptDef(OptimizationPass::kLoopOptimization), - // Evaluates code generated by dynamic bce. - OptDef(OptimizationPass::kConstantFolding, "constant_folding$after_bce"), - OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$after_bce"), - OptDef(OptimizationPass::kSideEffectsAnalysis, "side_effects$before_lse"), + // Simplification. + OptDef(OptimizationPass::kConstantFolding, + "constant_folding$after_bce"), + OptDef(OptimizationPass::kInstructionSimplifier, + "instruction_simplifier$after_bce"), + // Other high-level optimizations. + OptDef(OptimizationPass::kSideEffectsAnalysis, + "side_effects$before_lse"), OptDef(OptimizationPass::kLoadStoreAnalysis), OptDef(OptimizationPass::kLoadStoreElimination), OptDef(OptimizationPass::kCHAGuardOptimization), - OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$final"), + OptDef(OptimizationPass::kDeadCodeElimination, + "dead_code_elimination$final"), OptDef(OptimizationPass::kCodeSinking), // The codegen has a few assumptions that only the instruction simplifier // can satisfy. For example, the code generator does not expect to see a // HTypeConversion from a type to the same type. - OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$before_codegen"), + OptDef(OptimizationPass::kInstructionSimplifier, + "instruction_simplifier$before_codegen"), // Eliminate constructor fences after code sinking to avoid // complicated sinking logic to split a fence with many inputs. OptDef(OptimizationPass::kConstructorFenceRedundancyElimination) diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java index 95c19eaabc..fcc3c1a852 100644 --- a/test/442-checker-constant-folding/src/Main.java +++ b/test/442-checker-constant-folding/src/Main.java @@ -991,15 +991,23 @@ public class Main { /// CHECK-START: int Main.StaticConditionNulls() constant_folding$after_inlining (before) /// CHECK-DAG: <> NullConstant /// CHECK-DAG: <> NotEqual [<>,<>] - /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<>] + /// CHECK-DAG: If [<>] /// CHECK-START: int Main.StaticConditionNulls() constant_folding$after_inlining (after) /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<>] + /// CHECK-DAG: If [<>] /// CHECK-START: int Main.StaticConditionNulls() constant_folding$after_inlining (after) /// CHECK-NOT: NotEqual + /// CHECK-START: int Main.StaticConditionNulls() dead_code_elimination$after_inlining (before) + /// CHECK-DAG: <> Phi + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.StaticConditionNulls() dead_code_elimination$after_inlining (after) + /// CHECK-DAG: <> IntConstant 5 + /// CHECK-DAG: Return [<>] + private static Object getNull() { return null; } diff --git a/test/458-checker-instruct-simplification/src/Main.java b/test/458-checker-instruct-simplification/src/Main.java index 444b4557ce..b24cfcb775 100644 --- a/test/458-checker-instruct-simplification/src/Main.java +++ b/test/458-checker-instruct-simplification/src/Main.java @@ -882,7 +882,7 @@ public class Main { /// CHECK-NOT: Neg /// CHECK-NOT: Add - /// CHECK-START: int Main.$noinline$NegNeg2(int) constant_folding$after_inlining (after) + /// CHECK-START: int Main.$noinline$NegNeg2(int) constant_folding$after_gvn (after) /// CHECK: <> IntConstant 0 /// CHECK-NOT: Neg /// CHECK-NOT: Add @@ -1128,17 +1128,19 @@ public class Main { return res; } - /// CHECK-START: boolean Main.$noinline$EqualBoolVsIntConst(boolean) instruction_simplifier$after_inlining (before) + /// CHECK-START: boolean Main.$noinline$EqualBoolVsIntConst(boolean) dead_code_elimination$after_inlining (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> Equal [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: boolean Main.$noinline$EqualBoolVsIntConst(boolean) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: <> Equal [<>,<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: Return [<>] + + /// CHECK-START: boolean Main.$noinline$EqualBoolVsIntConst(boolean) dead_code_elimination$after_inlining (after) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: Return [<>] @@ -1157,12 +1159,14 @@ public class Main { /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> NotEqual [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: boolean Main.$noinline$NotEqualBoolVsIntConst(boolean) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: <> NotEqual [<>,<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: Return [<>] + + /// CHECK-START: boolean Main.$noinline$NotEqualBoolVsIntConst(boolean) dead_code_elimination$after_inlining (after) /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: Return [<>] @@ -1196,30 +1200,16 @@ public class Main { /// CHECK-DAG: <> Phi [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-NOT: BooleanNot [<>] - /// CHECK-NOT: Phi - /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK: BooleanNot [<>] - /// CHECK-NEXT: Goto - - /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (after) - /// CHECK-NOT: Select + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: Return [<>] - /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) dead_code_elimination$final (after) + /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ParameterValue - /// CHECK-NOT: BooleanNot [<>] /// CHECK-DAG: Return [<>] public static boolean NegateValue(boolean arg) { @@ -1348,10 +1338,11 @@ public class Main { /// CHECK-DAG: <> IntConstant 54 /// CHECK-DAG: <> StaticFieldGet /// CHECK-DAG: <> NotEqual [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] + /// CHECK-DAG: <> Equal [<>,<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.$noinline$booleanFieldEqualZero() instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.$noinline$booleanFieldEqualZero() select_generator (after) /// CHECK-DAG: <> StaticFieldGet /// CHECK-DAG: <> IntConstant 13 /// CHECK-DAG: <> IntConstant 54 @@ -1390,18 +1382,20 @@ public class Main { /// CHECK-DAG: <> IntConstant 42 /// CHECK-DAG: <> IntConstant 54 /// CHECK-DAG: <> LessThanOrEqual [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> NotEqual [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$noinline$intConditionNotEqualOne(int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: <> NotEqual [<>,<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.$noinline$intConditionNotEqualOne(int) select_generator (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 13 /// CHECK-DAG: <> IntConstant 42 /// CHECK-DAG: <> IntConstant 54 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> LessThanOrEqual [<>,<>] + /// CHECK-DAG: <> LessThanOrEqual [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // Note that we match `LE` from Select because there are two identical // LessThanOrEqual instructions. @@ -1418,18 +1412,20 @@ public class Main { /// CHECK-DAG: <> IntConstant 42 /// CHECK-DAG: <> IntConstant 54 /// CHECK-DAG: <> LessThanOrEqual [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> Equal [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$noinline$intConditionEqualZero(int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: <> Equal [<>,<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.$noinline$intConditionEqualZero(int) select_generator (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 13 /// CHECK-DAG: <> IntConstant 42 /// CHECK-DAG: <> IntConstant 54 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> LessThanOrEqual [<>,<>] + /// CHECK-DAG: <> LessThanOrEqual [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // Note that we match `LE` from Select because there are two identical // LessThanOrEqual instructions. @@ -2571,12 +2567,13 @@ public class Main { /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 255 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> And [<>,<>,<>] reg:3 is_catch_phi:true +## CHECK-DAG: Phi [<>,<>,<>] reg:3 is_catch_phi:true ## CHECK-START: int TestCase.testCatchPhiInputs_DefinedInTryBlock(int, int, int, int) dead_code_elimination$after_inlining (after) ## CHECK-DAG: <> IntConstant 11 diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java index fb76904677..b3e4a60e9a 100644 --- a/test/551-checker-shifter-operand/src/Main.java +++ b/test/551-checker-shifter-operand/src/Main.java @@ -728,9 +728,41 @@ public class Main { /// CHECK: UShr /// CHECK-NOT: UShr // - // Note: running extra simplification before GVN would expose the common subexpressions between - // shifts with larger distance `b << 62`, `b << 63` etc. and the equivalent smaller distances. - // TODO: b/78171933 + // Note: running extra simplification after inlining and before GVN exposes the common + // subexpressions between shifts with larger distance `b << 62`, `b << 63` etc. + // and the equivalent smaller distances. + // + /// CHECK-START: void Main.$opt$validateShiftInt(int, int) GVN (after) + /// CHECK: Shl + /// CHECK: Shl + /// CHECK: Shl + /// CHECK: Shl + /// CHECK: Shl + /// CHECK: Shl + /// CHECK: Shl + /// CHECK: Shl + /// CHECK: Shl + /// CHECK-NOT: Shl + /// CHECK: Shr + /// CHECK: Shr + /// CHECK: Shr + /// CHECK: Shr + /// CHECK: Shr + /// CHECK: Shr + /// CHECK: Shr + /// CHECK: Shr + /// CHECK: Shr + /// CHECK-NOT: Shl + /// CHECK: UShr + /// CHECK: UShr + /// CHECK: UShr + /// CHECK: UShr + /// CHECK: UShr + /// CHECK: UShr + /// CHECK: UShr + /// CHECK: UShr + /// CHECK: UShr + /// CHECK-NOT: UShr // /// CHECK-START-ARM: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm (after) /// CHECK: DataProcWithShifterOp @@ -760,12 +792,6 @@ public class Main { /// CHECK: DataProcWithShifterOp /// CHECK: DataProcWithShifterOp /// CHECK: DataProcWithShifterOp - /// CHECK: DataProcWithShifterOp - /// CHECK: DataProcWithShifterOp - /// CHECK: DataProcWithShifterOp - /// CHECK: DataProcWithShifterOp - /// CHECK: DataProcWithShifterOp - /// CHECK: DataProcWithShifterOp /// CHECK-NOT: DataProcWithShifterOp /// CHECK-START-ARM: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm (after) @@ -801,12 +827,6 @@ public class Main { /// CHECK: DataProcWithShifterOp /// CHECK: DataProcWithShifterOp /// CHECK: DataProcWithShifterOp - /// CHECK: DataProcWithShifterOp - /// CHECK: DataProcWithShifterOp - /// CHECK: DataProcWithShifterOp - /// CHECK: DataProcWithShifterOp - /// CHECK: DataProcWithShifterOp - /// CHECK: DataProcWithShifterOp /// CHECK-NOT: DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm64 (after) diff --git a/test/565-checker-doublenegbitwise/src/Main.java b/test/565-checker-doublenegbitwise/src/Main.java index 80358cd2ba..e36a2bab40 100644 --- a/test/565-checker-doublenegbitwise/src/Main.java +++ b/test/565-checker-doublenegbitwise/src/Main.java @@ -94,7 +94,7 @@ public class Main { * same pass. */ - /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_inlining (before) + /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 0 @@ -104,7 +104,7 @@ public class Main { /// CHECK-DAG: <> And [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_inlining (after) + /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> Or [<>,<>] @@ -165,7 +165,7 @@ public class Main { * same pass. */ - /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_inlining (before) + /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 0 @@ -175,7 +175,7 @@ public class Main { /// CHECK-DAG: <> Or [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_inlining (after) + /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> And [<>,<>] @@ -275,7 +275,7 @@ public class Main { * same pass. */ - /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_inlining (before) + /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 0 @@ -285,7 +285,7 @@ public class Main { /// CHECK-DAG: <> Xor [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_inlining (after) + /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> Xor [<>,<>] diff --git a/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali b/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali index 494ab95434..f74e88f580 100644 --- a/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali +++ b/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali @@ -233,9 +233,7 @@ ## CHECK-DAG: <> IntConstant 1 ## CHECK-DAG: <> StaticFieldGet ## CHECK-DAG: <> Select [<>,<>,<>] -## CHECK-DAG: <> TypeConversion [<>] -## CHECK-DAG: <> TypeConversion [<>] -## CHECK-DAG: Return [<>] +## CHECK-DAG: Return [<>] ## CHECK-START: int SmaliTests.longToIntOfBoolean() instruction_simplifier$after_bce (after) ## CHECK-DAG: <> CurrentMethod diff --git a/test/631-checker-get-class/src/Main.java b/test/631-checker-get-class/src/Main.java index 61c0adf624..b318168713 100644 --- a/test/631-checker-get-class/src/Main.java +++ b/test/631-checker-get-class/src/Main.java @@ -34,11 +34,14 @@ public class Main { } /// CHECK-START: boolean Main.classEquality1() instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> {{Equal|NotEqual}} - /// CHECK-DAG: <> Select [{{i\d+}},{{i\d+}},<>] - /// CHECK-DAG: Return [<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: Return [<>] - /// CHECK-START: boolean Main.classEquality2() instruction_simplifier$after_inlining (after) + /// CHECK-START: boolean Main.classEquality2() dead_code_elimination$after_inlining (after) /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: Return [<>] public static boolean classEquality2() { @@ -59,11 +65,14 @@ public class Main { } /// CHECK-START: boolean Main.classEquality3() instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> {{Equal|NotEqual}} - /// CHECK-DAG: <> Select [{{i\d+}},{{i\d+}},<>] - /// CHECK-DAG: Return [<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: Return [<>] - /// CHECK-START: boolean Main.classEquality4() instruction_simplifier$after_inlining (after) + /// CHECK-START: boolean Main.classEquality4() dead_code_elimination$after_inlining (after) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: Return [<>] public static boolean classEquality4() { diff --git a/test/660-checker-sad-byte/src/Main.java b/test/660-checker-sad-byte/src/Main.java index cd7fbcbdb9..bcd62c4852 100644 --- a/test/660-checker-sad-byte/src/Main.java +++ b/test/660-checker-sad-byte/src/Main.java @@ -19,22 +19,22 @@ */ public class Main { - /// CHECK-START: int Main.sad1(byte, byte) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.sad1(byte, byte) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.sad2(byte, byte) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.sad2(byte, byte) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad2(byte x, byte y) { @@ -43,11 +43,11 @@ public class Main { return diff; } - /// CHECK-START: int Main.sad3(byte, byte) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.sad3(byte, byte) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.sad3Alt(byte, byte) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.sad3Alt(byte, byte) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad3Alt(byte x, byte y) { @@ -67,11 +67,11 @@ public class Main { return 0 <= diff ? diff : -diff; } - /// CHECK-START: long Main.sadL1(byte, byte) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sadL1(byte, byte) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sadL2(byte, byte) instruction_simplifier$after_inlining (after) + /// CHECK-START: long Main.sadL2(byte, byte) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL2(byte x, byte y) { @@ -93,11 +93,11 @@ public class Main { return diff; } - /// CHECK-START: long Main.sadL3(byte, byte) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sadL3(byte, byte) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sadL3Alt(byte, byte) instruction_simplifier$after_inlining (after) + /// CHECK-START: long Main.sadL3Alt(byte, byte) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL3Alt(byte x, byte y) { diff --git a/test/660-checker-sad-char/src/Main.java b/test/660-checker-sad-char/src/Main.java index ecf748ae07..998ec33119 100644 --- a/test/660-checker-sad-char/src/Main.java +++ b/test/660-checker-sad-char/src/Main.java @@ -19,22 +19,22 @@ */ public class Main { - /// CHECK-START: int Main.sad1(char, char) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.sad1(char, char) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.sad2(char, char) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.sad2(char, char) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad2(char x, char y) { @@ -43,11 +43,11 @@ public class Main { return diff; } - /// CHECK-START: int Main.sad3(char, char) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.sad3(char, char) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.sad3Alt(char, char) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.sad3Alt(char, char) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad3Alt(char x, char y) { @@ -67,11 +67,11 @@ public class Main { return 0 <= diff ? diff : -diff; } - /// CHECK-START: long Main.sadL1(char, char) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sadL1(char, char) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sadL2(char, char) instruction_simplifier$after_inlining (after) + /// CHECK-START: long Main.sadL2(char, char) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL2(char x, char y) { @@ -93,11 +93,11 @@ public class Main { return diff; } - /// CHECK-START: long Main.sadL3(char, char) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sadL3(char, char) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sadL3Alt(char, char) instruction_simplifier$after_inlining (after) + /// CHECK-START: long Main.sadL3Alt(char, char) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL3Alt(char x, char y) { diff --git a/test/660-checker-sad-int/src/Main.java b/test/660-checker-sad-int/src/Main.java index 280dd66a51..09878a5052 100644 --- a/test/660-checker-sad-int/src/Main.java +++ b/test/660-checker-sad-int/src/Main.java @@ -19,15 +19,15 @@ */ public class Main { - /// CHECK-START: int Main.sad1(int, int) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.sad1(int, int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.sad1(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.sad1(int, int) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Abs // // NOTE: for direct 32-bit operands, this is not an ABS. @@ -35,11 +35,11 @@ public class Main { return x >= y ? x - y : y - x; } - /// CHECK-START: int Main.sad2(int, int) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.sad2(int, int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.sad3(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.sad3(int, int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad3(int x, int y) { @@ -60,11 +60,11 @@ public class Main { return diff >= 0 ? diff : -diff; } - /// CHECK-START: int Main.sad3Alt(int, int) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.sad3Alt(int, int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sadL1(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: long Main.sadL1(int, int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL1(int x, int y) { @@ -85,11 +85,11 @@ public class Main { return xl >= yl ? xl - yl : yl - xl; } - /// CHECK-START: long Main.sadL2(int, int) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sadL2(int, int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sadL3(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: long Main.sadL3(int, int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL3(int x, int y) { @@ -110,11 +110,11 @@ public class Main { return diff >= 0L ? diff : -diff; } - /// CHECK-START: long Main.sadL3Alt(int, int) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sadL3Alt(int, int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sad1(long, long) instruction_simplifier$after_inlining (after) + /// CHECK-START: long Main.sad1(long, long) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sad2(long, long) instruction_simplifier$after_inlining (after) + /// CHECK-START: long Main.sad2(long, long) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sad2(long x, long y) { @@ -48,11 +48,11 @@ public class Main { return diff; } - /// CHECK-START: long Main.sad3(long, long) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sad3(long, long) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sad3Alt(long, long) instruction_simplifier$after_inlining (after) + /// CHECK-START: long Main.sad3Alt(long, long) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sad3Alt(long x, long y) { diff --git a/test/660-checker-sad-short/src/Main.java b/test/660-checker-sad-short/src/Main.java index b712a146f4..0a1a4dc92e 100644 --- a/test/660-checker-sad-short/src/Main.java +++ b/test/660-checker-sad-short/src/Main.java @@ -19,22 +19,22 @@ */ public class Main { - /// CHECK-START: int Main.sad1(short, short) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.sad1(short, short) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.sad2(short, short) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.sad2(short, short) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad2(short x, short y) { @@ -43,11 +43,11 @@ public class Main { return diff; } - /// CHECK-START: int Main.sad3(short, short) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.sad3(short, short) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.sad3Alt(short, short) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.sad3Alt(short, short) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static int sad3Alt(short x, short y) { @@ -67,11 +67,11 @@ public class Main { return 0 <= diff ? diff : -diff; } - /// CHECK-START: long Main.sadL1(short, short) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sadL1(short, short) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sadL2(short, short) instruction_simplifier$after_inlining (after) + /// CHECK-START: long Main.sadL2(short, short) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL2(short x, short y) { @@ -93,11 +93,11 @@ public class Main { return diff; } - /// CHECK-START: long Main.sadL3(short, short) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.sadL3(short, short) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> Select /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.sadL3Alt(short, short) instruction_simplifier$after_inlining (after) + /// CHECK-START: long Main.sadL3Alt(short, short) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Abs /// CHECK-DAG: Return [<>] static long sadL3Alt(short x, short y) { diff --git a/test/679-checker-minmax/src/Main.java b/test/679-checker-minmax/src/Main.java index 48de1da291..abf8c279da 100644 --- a/test/679-checker-minmax/src/Main.java +++ b/test/679-checker-minmax/src/Main.java @@ -99,211 +99,211 @@ public class Main { // Different types. // - /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Min /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_gvn (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-START: int Main.min2(int, int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> GreaterThan [<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.min2(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.min2(int, int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Min /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.min2(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.min2(int, int) instruction_simplifier$after_gvn (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-START: int Main.min3(int, int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> LessThanOrEqual [<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.min3(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.min3(int, int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Min /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.min3(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.min3(int, int) instruction_simplifier$after_gvn (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-START: int Main.min4(int, int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> LessThan [<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.min4(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.min4(int, int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Min /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.min4(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.min4(int, int) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static int min4(int a, int b) { return a >= b ? b : a; } - /// CHECK-START: int Main.min5(short, short) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.min5(short, short) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> LessThan [<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.min5(short, short) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.min5(short, short) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Min /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.min5(short, short) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.min5(short, short) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static int min5(short a, short b) { return a >= b ? b : a; } - /// CHECK-START: int Main.min6(byte, byte) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.min6(byte, byte) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> LessThan [<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.min6(byte, byte) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.min6(byte, byte) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Min /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.min6(byte, byte) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.min6(byte, byte) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static int min6(byte a, byte b) { return a >= b ? b : a; } - /// CHECK-START: long Main.min7(long, long) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.min7(long, long) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> LessThan [<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.min7(long, long) instruction_simplifier$after_inlining (after) + /// CHECK-START: long Main.min7(long, long) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Min /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.min7(long, long) instruction_simplifier$after_inlining (after) + /// CHECK-START: long Main.min7(long, long) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static long min7(long a, long b) { return a >= b ? b : a; } - /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Max /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.max1(int, int) instruction_simplifier$after_gvn (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-START: int Main.max2(int, int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> GreaterThan [<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.max2(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.max2(int, int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Max /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.max2(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.max2(int, int) instruction_simplifier$after_gvn (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-START: int Main.max3(int, int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> LessThanOrEqual [<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.max3(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.max3(int, int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Max /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.max3(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.max3(int, int) instruction_simplifier$after_gvn (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-START: int Main.max4(int, int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> LessThan [<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.max4(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.max4(int, int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Max /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.max4(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.max4(int, int) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static int max4(int a, int b) { return a >= b ? a : b; } - /// CHECK-START: int Main.max5(short, short) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.max5(short, short) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> LessThan [<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.max5(short, short) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.max5(short, short) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Max /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.max5(short, short) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.max5(short, short) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static int max5(short a, short b) { return a >= b ? a : b; } - /// CHECK-START: int Main.max6(byte, byte) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.max6(byte, byte) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> LessThan [<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.max6(byte, byte) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.max6(byte, byte) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Max /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.max6(byte, byte) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.max6(byte, byte) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static int max6(byte a, byte b) { return a >= b ? a : b; } - /// CHECK-START: long Main.max7(long, long) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.max7(long, long) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> LessThan [<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.max7(long, long) instruction_simplifier$after_inlining (after) + /// CHECK-START: long Main.max7(long, long) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Max /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.max7(long, long) instruction_simplifier$after_inlining (after) + /// CHECK-START: long Main.max7(long, long) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static long max7(long a, long b) { return a >= b ? a : b; @@ -313,18 +313,18 @@ public class Main { // Complications. // - /// CHECK-START: int Main.min0(int[], int[]) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.min0(int[], int[]) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> ArrayGet [{{l\d+}},{{i\d+}}] /// CHECK-DAG: <> ArrayGet [{{l\d+}},{{i\d+}}] /// CHECK-DAG: <> GreaterThan [<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.min0(int[], int[]) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.min0(int[], int[]) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Min /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.min0(int[], int[]) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.min0(int[], int[]) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static int min0(int[] a, int[] b) { // Repeat of array references needs finding the common subexpressions @@ -332,18 +332,18 @@ public class Main { return a[0] <= b[0] ? a[0] : b[0]; } - /// CHECK-START: int Main.max0(int[], int[]) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.max0(int[], int[]) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> ArrayGet [{{l\d+}},{{i\d+}}] /// CHECK-DAG: <> ArrayGet [{{l\d+}},{{i\d+}}] /// CHECK-DAG: <> LessThan [<>,<>] /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.max0(int[], int[]) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.max0(int[], int[]) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> Max /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.max0(int[], int[]) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.max0(int[], int[]) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static int max0(int[] a, int[] b) { // Repeat of array references needs finding the common subexpressions @@ -351,7 +351,7 @@ public class Main { return a[0] >= b[0] ? a[0] : b[0]; } - /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 100 /// CHECK-DAG: <> IntConstant -100 @@ -361,7 +361,7 @@ public class Main { /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 100 /// CHECK-DAG: <> IntConstant -100 @@ -369,7 +369,7 @@ public class Main { /// CHECK-DAG: <> Max [<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static int minmax1(int x) { // Simple if-if gives clean select sequence. @@ -382,7 +382,7 @@ public class Main { return x; } - /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 100 /// CHECK-DAG: <> IntConstant -100 @@ -392,7 +392,7 @@ public class Main { /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 100 /// CHECK-DAG: <> IntConstant -100 @@ -400,7 +400,7 @@ public class Main { /// CHECK-DAG: <> Min [<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static int minmax2(int x) { // Simple if-else requires inspecting bounds of resulting selects. @@ -412,7 +412,7 @@ public class Main { return x; } - /// CHECK-START: int Main.minmax3(int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.minmax3(int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 100 /// CHECK-DAG: <> IntConstant -100 @@ -420,13 +420,13 @@ public class Main { /// CHECK-DAG: <> Min [<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.minmax3(int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.minmax3(int) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static int minmax3(int x) { return (x > 100) ? 100 : ((x < -100) ? -100 : x); } - /// CHECK-START: int Main.minmax4(int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.minmax4(int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 100 /// CHECK-DAG: <> IntConstant -100 @@ -434,7 +434,7 @@ public class Main { /// CHECK-DAG: <> Max [<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.minmax4(int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.minmax4(int) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static int minmax4(int x) { return (x < -100) ? -100 : ((x > 100) ? 100 : x); @@ -454,7 +454,7 @@ public class Main { /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.minmaxCSEScalar(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.minmaxCSEScalar(int, int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> Max [<>,<>] @@ -490,7 +490,7 @@ public class Main { /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.minmaxCSEArray(int[], int[]) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.minmaxCSEArray(int[], int[]) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ArrayGet /// CHECK-DAG: <> ArrayGet /// CHECK-DAG: <> Max [<>,<>] @@ -512,7 +512,7 @@ public class Main { return t1 + t2 + t3 + t4 + t5 + t6; } - /// CHECK-START: int Main.minmaxCSEScalarAndCond(int, int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.minmaxCSEScalarAndCond(int, int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> Max [<>,<>] diff --git a/test/681-checker-abs/src/Main.java b/test/681-checker-abs/src/Main.java index 2b95a8d56e..00390c3145 100644 --- a/test/681-checker-abs/src/Main.java +++ b/test/681-checker-abs/src/Main.java @@ -59,7 +59,7 @@ public class Main { // Types. // - /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] @@ -67,18 +67,18 @@ public class Main { /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> Abs [<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static int abs1(int a) { return a < 0 ? -a : a; } - /// CHECK-START: int Main.abs2(int) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.abs2(int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> GreaterThan [<>,<>] @@ -86,18 +86,18 @@ public class Main { /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.abs2(int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.abs2(int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> Abs [<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.abs2(int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.abs2(int) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static int abs2(int a) { return a <= 0 ? -a : a; } - /// CHECK-START: int Main.abs3(int) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.abs3(int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> LessThanOrEqual [<>,<>] @@ -105,18 +105,18 @@ public class Main { /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.abs3(int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.abs3(int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> Abs [<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.abs3(int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.abs3(int) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static int abs3(int a) { return a > 0 ? a : -a; } - /// CHECK-START: int Main.abs4(int) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.abs4(int) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> LessThan [<>,<>] @@ -124,18 +124,18 @@ public class Main { /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.abs4(int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.abs4(int) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> Abs [<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.abs4(int) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.abs4(int) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static int abs4(int a) { return a >= 0 ? a : -a; } - /// CHECK-START: int Main.abs5(short) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.abs5(short) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> LessThan [<>,<>] @@ -143,18 +143,18 @@ public class Main { /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.abs5(short) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.abs5(short) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> Abs [<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.abs5(short) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.abs5(short) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static int abs5(short a) { return a >= 0 ? a : -a; } - /// CHECK-START: int Main.abs6(byte) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.abs6(byte) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> LessThan [<>,<>] @@ -162,18 +162,18 @@ public class Main { /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.abs6(byte) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.abs6(byte) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> Abs [<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.abs6(byte) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.abs6(byte) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static int abs6(byte a) { return a >= 0 ? a : -a; } - /// CHECK-START: long Main.abs7(long) instruction_simplifier$after_inlining (before) + /// CHECK-START: long Main.abs7(long) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> LongConstant 0 /// CHECK-DAG: <> LessThan [<>,<>] @@ -181,12 +181,12 @@ public class Main { /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.abs7(long) instruction_simplifier$after_inlining (after) + /// CHECK-START: long Main.abs7(long) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> Abs [<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: long Main.abs7(long) instruction_simplifier$after_inlining (after) + /// CHECK-START: long Main.abs7(long) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static long abs7(long a) { return a >= 0 ? a : -a; @@ -196,7 +196,7 @@ public class Main { // Complications. // - /// CHECK-START: int Main.abs0(int[]) instruction_simplifier$after_inlining (before) + /// CHECK-START: int Main.abs0(int[]) instruction_simplifier$after_gvn (before) /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> ArrayGet [{{l\d+}},{{i\d+}}] /// CHECK-DAG: <> LessThan [<>,<>] @@ -204,12 +204,12 @@ public class Main { /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.abs0(int[]) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.abs0(int[]) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ArrayGet [{{l\d+}},{{i\d+}}] /// CHECK-DAG: <> Abs [<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.abs0(int[]) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.abs0(int[]) instruction_simplifier$after_gvn (after) /// CHECK-NOT: Select public static int abs0(int[] a) { return a[0] >= 0 ? a[0] : -a[0]; @@ -267,11 +267,11 @@ public class Main { /// CHECK-DAG: <> Abs [<>] /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.zabs3(char) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.zabs3(char) instruction_simplifier$after_gvn (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: Return [<>] // - /// CHECK-START: int Main.zabs3(char) instruction_simplifier$after_inlining (after) + /// CHECK-START: int Main.zabs3(char) instruction_simplifier$after_gvn (after) /// CHECK-NOT: InvokeStaticOrDirect /// CHECK-NOT: Abs public static int zabs3(char a) { -- GitLab From d4102bafc1037007ac4cdeef9b215ca1bef6831f Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 30 Apr 2018 12:47:22 -0700 Subject: [PATCH 313/749] Limit test 904 to examining test threads Test 904 could flake due to capturing allocations from unrelated android system threads. To fix this we filter the allocations we have collected for those from threads created for/running the test. Test: ./test.py --host -j50 Change-Id: Id4636dc153ab5a8208cd5f7bb255e29939dc2b02 --- .../src/art/Test904.java | 26 ++++--- test/904-object-allocation/tracking.cc | 74 ++++++++++++++++--- 2 files changed, 81 insertions(+), 19 deletions(-) diff --git a/test/904-object-allocation/src/art/Test904.java b/test/904-object-allocation/src/art/Test904.java index fda89852c2..a2848fb917 100644 --- a/test/904-object-allocation/src/art/Test904.java +++ b/test/904-object-allocation/src/art/Test904.java @@ -47,7 +47,8 @@ public class Test904 { // Enable actual logging callback. setupObjectAllocCallback(true); - System.out.println(Arrays.toString(getTrackingEventMessages())); + System.out.println(Arrays.toString(getTrackingEventMessages( + new Thread[] { Thread.currentThread(), }))); enableAllocationTracking(null, true); @@ -66,22 +67,25 @@ public class Test904 { l.add(new Byte((byte)0)); - System.out.println(Arrays.toString(getTrackingEventMessages())); + System.out.println(Arrays.toString(getTrackingEventMessages( + new Thread[] { Thread.currentThread(), }))); System.out.println("Tracking on same thread"); - testThread(l, true, true); + Thread test_thread = testThread(l, true, true); l.add(new Byte((byte)0)); - System.out.println(Arrays.toString(getTrackingEventMessages())); + System.out.println(Arrays.toString(getTrackingEventMessages( + new Thread[] { Thread.currentThread(), test_thread, }))); System.out.println("Tracking on same thread, not disabling tracking"); - testThread(l, true, false); + test_thread = testThread(l, true, false); - System.out.println(Arrays.toString(getTrackingEventMessages())); + System.out.println(Arrays.toString(getTrackingEventMessages( + new Thread[] { Thread.currentThread(), test_thread, }))); System.out.println("Tracking on different thread"); - testThread(l, false, true); + test_thread = testThread(l, false, true); l.add(new Byte((byte)0)); @@ -89,12 +93,13 @@ public class Test904 { // check that shutdown works correctly. setupObjectAllocCallback(false); - System.out.println(Arrays.toString(getTrackingEventMessages())); + System.out.println(Arrays.toString(getTrackingEventMessages( + new Thread[] { Thread.currentThread(), test_thread, }))); enableAllocationTracking(null, true); } - private static void testThread(final ArrayList l, final boolean sameThread, + private static Thread testThread(final ArrayList l, final boolean sameThread, final boolean disableTracking) throws Exception { final SimpleBarrier startBarrier = new SimpleBarrier(1); final SimpleBarrier trackBarrier = new SimpleBarrier(1); @@ -126,6 +131,7 @@ public class Test904 { trackBarrier.dec(); t.join(); + return t; } private static class SimpleBarrier { @@ -149,5 +155,5 @@ public class Test904 { private static native void setupObjectAllocCallback(boolean enable); private static native void enableAllocationTracking(Thread thread, boolean enable); - private static native String[] getTrackingEventMessages(); + private static native String[] getTrackingEventMessages(Thread[] threads); } diff --git a/test/904-object-allocation/tracking.cc b/test/904-object-allocation/tracking.cc index 9d2592a675..f7296b1136 100644 --- a/test/904-object-allocation/tracking.cc +++ b/test/904-object-allocation/tracking.cc @@ -35,6 +35,8 @@ namespace art { namespace Test904ObjectAllocation { +static JavaVM* vm; + static std::string GetClassName(JNIEnv* jni_env, jclass cls) { ScopedLocalRef class_class(jni_env, jni_env->GetObjectClass(cls)); jmethodID mid = jni_env->GetMethodID(class_class.get(), "getName", "()Ljava/lang/String;"); @@ -44,12 +46,45 @@ static std::string GetClassName(JNIEnv* jni_env, jclass cls) { return utf_chars.c_str(); } +template +class ScopedGlobalRef { + public: + ScopedGlobalRef(JNIEnv* env, T obj) : obj_(env->NewGlobalRef(obj)) {} + ScopedGlobalRef(const ScopedGlobalRef& src) noexcept + : obj_(GetEnv()->NewGlobalRef(src.obj_)) {} + ScopedGlobalRef(ScopedGlobalRef&& src) noexcept : obj_(src.obj_) { + src.obj_ = nullptr; + } + + ~ScopedGlobalRef() { + GetEnv()->DeleteGlobalRef(obj_); + } + + T Get(JNIEnv* env) const { + return env->NewLocalRef(obj_); + } + + private: + JNIEnv* GetEnv() const { + JNIEnv* env = nullptr; + CHECK_EQ(vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6), 0); + return env; + } + + jobject obj_; +}; + +struct EventLog { + std::string msg_; + ScopedGlobalRef thr_; +}; + static std::mutex gEventsMutex; -static std::vector gEvents; +static std::vector gEvents; static void JNICALL ObjectAllocated(jvmtiEnv* ti_env ATTRIBUTE_UNUSED, JNIEnv* jni_env, - jthread thread ATTRIBUTE_UNUSED, + jthread thread, jobject object, jclass object_klass, jlong size) { @@ -58,14 +93,16 @@ static void JNICALL ObjectAllocated(jvmtiEnv* ti_env ATTRIBUTE_UNUSED, std::string object_klass_descriptor2 = GetClassName(jni_env, object_klass2.get()); std::lock_guard guard(gEventsMutex); - gEvents.push_back(android::base::StringPrintf("ObjectAllocated type %s/%s size %zu", - object_klass_descriptor.c_str(), - object_klass_descriptor2.c_str(), - static_cast(size))); + gEvents.push_back({android::base::StringPrintf("ObjectAllocated type %s/%s size %zu", + object_klass_descriptor.c_str(), + object_klass_descriptor2.c_str(), + static_cast(size)), + ScopedGlobalRef(jni_env, thread)}); } extern "C" JNIEXPORT void JNICALL Java_art_Test904_setupObjectAllocCallback( JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jboolean enable) { + env->GetJavaVM(&vm); jvmtiEventCallbacks callbacks; memset(&callbacks, 0, sizeof(jvmtiEventCallbacks)); callbacks.VMObjectAlloc = enable ? ObjectAllocated : nullptr; @@ -84,13 +121,32 @@ extern "C" JNIEXPORT void JNICALL Java_art_Test904_enableAllocationTracking( } extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test904_getTrackingEventMessages( - JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jobjectArray threads) { std::lock_guard guard(gEventsMutex); + std::vector real_events; + std::vector thread_lst; + jint nthreads = env->GetArrayLength(threads); + { + env->PushLocalFrame(nthreads + 1); + for (jint i = 0; i < nthreads; i++) { + thread_lst.push_back(reinterpret_cast(env->GetObjectArrayElement(threads, i))); + } + for (const EventLog& ev : gEvents) { + ScopedLocalRef thr(env, ev.thr_.Get(env)); + for (jthread req_thread : thread_lst) { + if (env->IsSameObject(req_thread, thr.get())) { + real_events.push_back(ev.msg_); + break; + } + } + } + env->PopLocalFrame(nullptr); + } jobjectArray ret = CreateObjectArray(env, - static_cast(gEvents.size()), + static_cast(real_events.size()), "java/lang/String", [&](jint i) { - return env->NewStringUTF(gEvents[i].c_str()); + return env->NewStringUTF(real_events[i].c_str()); }); gEvents.clear(); return ret; -- GitLab From 20da73363c6b1208b7bec2610534c036eca51946 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 30 Apr 2018 12:55:21 -0700 Subject: [PATCH 314/749] Remove skip of test 904 for JIT Test appears to pass consistently. Unable to find reason it was skipped initially. Test: ./test.py --host -j50 Change-Id: I4a084367c1297b4da6e40f805dbaf6afc70c1e20 --- test/knownfailures.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/knownfailures.json b/test/knownfailures.json index 6d8abe13b2..369f927270 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -253,10 +253,6 @@ "runs to be able to have precise checks."], "variant": "jit" }, - { - "tests": ["904-object-allocation"], - "variant": "jit" - }, { "tests": ["570-checker-select", "484-checker-register-hints"], -- GitLab From 0e4e814e09bea5c3c169c73122e8ebd53a8d04f4 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 25 Apr 2018 12:19:34 -0700 Subject: [PATCH 315/749] ART: Find and cache indices for method names Cache the range of string indices starting with '<,' especially the well-defined "" and "," to optimize the constructor-flags check. Saves about .2% of instructions. Bug: 78568168 Test: m test-art-host Change-Id: I9ed26bb0382a169d945bcec4067c0166c200e227 --- libdexfile/dex/dex_file_verifier.cc | 146 +++++++++++------------ libdexfile/dex/dex_file_verifier.h | 23 +++- libdexfile/dex/dex_file_verifier_test.cc | 2 +- 3 files changed, 96 insertions(+), 75 deletions(-) diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc index 5f7b4e8ba6..2380f48d3a 100644 --- a/libdexfile/dex/dex_file_verifier.cc +++ b/libdexfile/dex/dex_file_verifier.cc @@ -18,7 +18,6 @@ #include -#include #include #include "android-base/stringprintf.h" @@ -106,66 +105,6 @@ const char* DexFileVerifier::CheckLoadStringByIdx(dex::StringIndex idx, const ch return dex_file_->StringDataByIdx(idx); } -// Try to find the name of the method with the given index. We do not want to rely on DexFile -// infrastructure at this point, so do it all by hand. begin and header correspond to begin_ and -// header_ of the DexFileVerifier. str will contain the pointer to the method name on success -// (flagged by the return value), otherwise error_msg will contain an error string. -static bool FindMethodName(uint32_t method_index, - const uint8_t* begin, - const DexFile::Header* header, - const char** str, - std::string* error_msg) { - if (method_index >= header->method_ids_size_) { - *error_msg = "Method index not available for method flags verification"; - return false; - } - uint32_t string_idx = - (reinterpret_cast(begin + header->method_ids_off_) + - method_index)->name_idx_.index_; - if (string_idx >= header->string_ids_size_) { - *error_msg = "String index not available for method flags verification"; - return false; - } - uint32_t string_off = - (reinterpret_cast(begin + header->string_ids_off_) + string_idx)-> - string_data_off_; - if (string_off >= header->file_size_) { - *error_msg = "String offset out of bounds for method flags verification"; - return false; - } - const uint8_t* str_data_ptr = begin + string_off; - uint32_t dummy; - if (!DecodeUnsignedLeb128Checked(&str_data_ptr, begin + header->file_size_, &dummy)) { - *error_msg = "String size out of bounds for method flags verification"; - return false; - } - *str = reinterpret_cast(str_data_ptr); - return true; -} - -// Gets constructor flags based on the |method_name|. Returns true if -// method_name is either or and sets -// |constructor_flags_by_name| appropriately. Otherwise set -// |constructor_flags_by_name| to zero and returns whether -// |method_name| is valid. -bool GetConstructorFlagsForMethodName(const char* method_name, - uint32_t* constructor_flags_by_name) { - if (method_name[0] != '<') { - *constructor_flags_by_name = 0; - return true; - } - if (strcmp(method_name + 1, "clinit>") == 0) { - *constructor_flags_by_name = kAccStatic | kAccConstructor; - return true; - } - if (strcmp(method_name + 1, "init>") == 0) { - *constructor_flags_by_name = kAccConstructor; - return true; - } - *constructor_flags_by_name = 0; - return false; -} - const char* DexFileVerifier::CheckLoadStringByTypeIdx(dex::TypeIndex type_idx, const char* error_string) { if (UNLIKELY(!CheckIndex(type_idx.index_, dex_file_->NumTypeIds(), error_string))) { @@ -682,10 +621,11 @@ bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, return false; } + const DexFile::MethodId& method_id = + *(reinterpret_cast(begin_ + header_->method_ids_off_) + idx); + // Check that it's the right class. - dex::TypeIndex my_class_index = - (reinterpret_cast(begin_ + header_->method_ids_off_) + idx)-> - class_idx_; + dex::TypeIndex my_class_index = method_id.class_idx_; if (class_type_index != my_class_index) { ErrorStringPrintf("Method's class index unexpected, %" PRIu16 " vs %" PRIu16, my_class_index.index_, @@ -710,16 +650,23 @@ bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, } std::string error_msg; - const char* method_name; - if (!FindMethodName(idx, begin_, header_, &method_name, &error_msg)) { - ErrorStringPrintf("%s", error_msg.c_str()); - return false; - } - uint32_t constructor_flags_by_name = 0; - if (!GetConstructorFlagsForMethodName(method_name, &constructor_flags_by_name)) { - ErrorStringPrintf("Bad method name: %s", method_name); - return false; + { + uint32_t string_idx = method_id.name_idx_.index_; + if (!CheckIndex(string_idx, header_->string_ids_size_, "method flags verification")) { + return false; + } + if (UNLIKELY(string_idx < angle_bracket_end_index_) && + string_idx >= angle_bracket_start_index_) { + if (string_idx == angle_clinit_angle_index_) { + constructor_flags_by_name = kAccStatic | kAccConstructor; + } else if (string_idx == angle_init_angle_index_) { + constructor_flags_by_name = kAccConstructor; + } else { + ErrorStringPrintf("Bad method name for method index %u", idx); + return false; + } + } } bool has_code = (code_offset != 0); @@ -1961,6 +1908,10 @@ bool DexFileVerifier::CheckIntraSection() { return false; } + if (type == DexFile::kDexTypeClassDataItem) { + FindStringRangesForMethodNames(); + } + // Check each item based on its type. switch (type) { case DexFile::kDexTypeHeaderItem: @@ -3181,6 +3132,55 @@ bool DexFileVerifier::CheckFieldAccessFlags(uint32_t idx, return true; } +void DexFileVerifier::FindStringRangesForMethodNames() { + // Use DexFile::StringId* as RandomAccessIterator. + const DexFile::StringId* first = reinterpret_cast( + begin_ + header_->string_ids_off_); + const DexFile::StringId* last = first + header_->string_ids_size_; + + auto get_string = [begin = begin_](const DexFile::StringId& id) { + const uint8_t* str_data_ptr = begin + id.string_data_off_; + DecodeUnsignedLeb128(&str_data_ptr); + return reinterpret_cast(str_data_ptr); + }; + auto compare = [&get_string](const DexFile::StringId& lhs, const char* rhs) { + return strcmp(get_string(lhs), rhs) < 0; + }; + + // '=' follows '<' + static_assert('<' + 1 == '=', "Unexpected character relation"); + const auto angle_end = std::lower_bound(first, last, "=", compare); + angle_bracket_end_index_ = angle_end - first; + + const auto angle_start = std::lower_bound(first, angle_end, "<", compare); + angle_bracket_start_index_ = angle_start - first; + if (angle_start == angle_end) { + // No strings starting with '<'. + angle_init_angle_index_ = std::numeric_limits::max(); + angle_clinit_angle_index_ = std::numeric_limits::max(); + return; + } + + { + constexpr const char* kClinit = ""; + const auto it = std::lower_bound(angle_start, angle_end, kClinit, compare); + if (it != angle_end && strcmp(get_string(*it), kClinit) == 0) { + angle_clinit_angle_index_ = it - first; + } else { + angle_clinit_angle_index_ = std::numeric_limits::max(); + } + } + { + constexpr const char* kInit = ""; + const auto it = std::lower_bound(angle_start, angle_end, kInit, compare); + if (it != angle_end && strcmp(get_string(*it), kInit) == 0) { + angle_init_angle_index_ = it - first; + } else { + angle_init_angle_index_ = std::numeric_limits::max(); + } + } +} + bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, uint32_t method_access_flags, uint32_t class_access_flags, diff --git a/libdexfile/dex/dex_file_verifier.h b/libdexfile/dex/dex_file_verifier.h index a4c23bb08a..eaffc6240a 100644 --- a/libdexfile/dex/dex_file_verifier.h +++ b/libdexfile/dex/dex_file_verifier.h @@ -17,6 +17,7 @@ #ifndef ART_LIBDEXFILE_DEX_DEX_FILE_VERIFIER_H_ #define ART_LIBDEXFILE_DEX_DEX_FILE_VERIFIER_H_ +#include #include #include "base/hash_map.h" @@ -52,7 +53,11 @@ class DexFileVerifier { verify_checksum_(verify_checksum), header_(&dex_file->GetHeader()), ptr_(nullptr), - previous_item_(nullptr) { + previous_item_(nullptr), + angle_bracket_start_index_(std::numeric_limits::max()), + angle_bracket_end_index_(std::numeric_limits::max()), + angle_init_angle_index_(std::numeric_limits::max()), + angle_clinit_angle_index_(std::numeric_limits::max()) { } bool Verify(); @@ -195,6 +200,8 @@ class DexFileVerifier { // Check validity of given method if it's a constructor or class initializer. bool CheckConstructorProperties(uint32_t method_index, uint32_t constructor_flags); + void FindStringRangesForMethodNames(); + const DexFile* const dex_file_; const uint8_t* const begin_; const size_t size_; @@ -237,6 +244,20 @@ class DexFileVerifier { // Set of type ids for which there are ClassDef elements in the dex file. std::unordered_set defined_classes_; + + // Cached string indices for "interesting" entries wrt/ method names. Will be populated by + // FindStringRangesForMethodNames (which is automatically called before verifying the + // classdataitem section). + // + // Strings starting with '<' are in the range + // [angle_bracket_start_index_,angle_bracket_end_index_). + // angle_init_angle_index_ and angle_clinit_angle_index_ denote the indices of "" and + // angle_clinit_angle_index_, respectively. If any value is not found, the corresponding + // index will be larger than any valid string index for this dex file. + size_t angle_bracket_start_index_; + size_t angle_bracket_end_index_; + size_t angle_init_angle_index_; + size_t angle_clinit_angle_index_; }; } // namespace art diff --git a/libdexfile/dex/dex_file_verifier_test.cc b/libdexfile/dex/dex_file_verifier_test.cc index 4c3cf776ee..f82081f8ee 100644 --- a/libdexfile/dex/dex_file_verifier_test.cc +++ b/libdexfile/dex/dex_file_verifier_test.cc @@ -173,7 +173,7 @@ TEST_F(DexFileVerifierTest, MethodId) { DexFile::MethodId* method_id = const_cast(&dex_file->GetMethodId(0)); method_id->name_idx_ = dex::StringIndex(0xFF); }, - "String index not available for method flags verification"); + "Bad index for method flags verification"); } // Method flags test class generated from the following smali code. The declared-synchronized -- GitLab From 9fecf30ba0577511276d8a4a3e15ff45ba2a2462 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 30 Apr 2018 14:13:22 -0700 Subject: [PATCH 316/749] Remove changes to 001-HelloWorld The test 001-HelloWorld was turned into a copy of test 170 a while ago. This returns test 001-HelloWorld to its original state as a pure hello-world program. Partial revert of commit 976b298a4e Test: ./test.py --host -j50 Change-Id: I20b021ca54655cc84f4fd27b2d145c3054bbb8f5 --- test/001-HelloWorld/src/Main.java | 33 ++----------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/test/001-HelloWorld/src/Main.java b/test/001-HelloWorld/src/Main.java index 401e8529cd..1ef6289559 100644 --- a/test/001-HelloWorld/src/Main.java +++ b/test/001-HelloWorld/src/Main.java @@ -14,37 +14,8 @@ * limitations under the License. */ -import java.util.concurrent.*; - -interface I { -} - -class A implements I { - static int x = (int)(10*Math.random()); // Suppress initialization. -} - public class Main { - public static void main(String[] args) throws Exception { - - final CountDownLatch first = new CountDownLatch(1); - final CountDownLatch second = new CountDownLatch(1); - - new Thread(new Runnable() { - public void run() { - try { - synchronized(I.class) { - first.countDown(); - second.await(); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - }).start(); - - first.await(); - new A(); - second.countDown(); - System.out.println("Hello, world!"); + public static void main(String[] args) { + System.out.println("Hello, world!"); } } -- GitLab From b52dbb7f99e9a51266c24b91a1dfd8bf469630f8 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Tue, 1 May 2018 14:03:45 +0100 Subject: [PATCH 317/749] Build, sync and use debuggerd and su in ART's Android root testing dir. Binaries /system/bin/debuggerd and /system/xbin/su are used by script tools/run-jdwp-tests.sh as part of the process dumping command when testing on device. Note that this dumping command won't work on ART Buildbot devices running Android N regardless, as the debuggerd protocol changed in an incompatible way in Android O. Test: tools/run-jdwp-tests.sh --mode=device Change-Id: I402918ca0879583a1beb2dc71dc1d81516607ed2 --- tools/buildbot-build.sh | 1 + tools/run-jdwp-tests.sh | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh index e447ab4cf4..31bddd5213 100755 --- a/tools/buildbot-build.sh +++ b/tools/buildbot-build.sh @@ -80,6 +80,7 @@ elif [[ $mode == "target" ]]; then fi make_command="make $j_arg $extra_args $showcommands build-art-target-tests $common_targets" make_command+=" libjavacrypto-target libnetd_client-target linker toybox toolbox sh" + make_command+=" debuggerd su" make_command+=" ${out_dir}/host/linux-x86/bin/adb libstdc++ " make_command+=" ${out_dir}/target/product/${TARGET_PRODUCT}/system/etc/public.libraries.txt" mode_suffix="-target" diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index 21ddcbc062..56d412bfa0 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -216,7 +216,11 @@ else if [[ "$mode" == "host" ]]; then dump_command="/bin/kill -3" else - dump_command="/system/xbin/su root /data/local/tmp/system/bin/debuggerd" + # Note that this dumping command won't work when `$android_root` + # is different from `/system` (e.g. on ART Buildbot devices) when + # the device is running Android N, as the debuggerd protocol + # changed in an incompatible way in Android O (see b/32466479). + dump_command="$android_root/xbin/su root $android_root/bin/debuggerd" fi if [[ $has_gdb = "yes" ]]; then if [[ $mode == "target" ]]; then -- GitLab From a1b1b1bd56c609cea90330dd39ba375f3ec081c7 Mon Sep 17 00:00:00 2001 From: Rico Wind Date: Tue, 1 May 2018 15:50:27 +0200 Subject: [PATCH 318/749] Add a few cases of non compatiple d8 tests Unblock rolling r8 Bug: b/65168732 Test: Test still work with dx Change-Id: I54c805e205c550215a6dc3f05ec5f83a753c054b --- test/530-checker-lse2/build | 20 ++++++++++++++++++++ test/569-checker-pattern-replacement/build | 20 ++++++++++++++++++++ test/583-checker-zero/build | 20 ++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 test/530-checker-lse2/build create mode 100644 test/569-checker-pattern-replacement/build create mode 100644 test/583-checker-zero/build diff --git a/test/530-checker-lse2/build b/test/530-checker-lse2/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/530-checker-lse2/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/569-checker-pattern-replacement/build b/test/569-checker-pattern-replacement/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/569-checker-pattern-replacement/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" diff --git a/test/583-checker-zero/build b/test/583-checker-zero/build new file mode 100644 index 0000000000..d85147f17b --- /dev/null +++ b/test/583-checker-zero/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# 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. + +# See b/65168732 +export USE_D8=false + +./default-build "$@" -- GitLab From f3a163cc87ef4600d78837389b72795014533780 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Tue, 1 May 2018 18:59:14 +0100 Subject: [PATCH 319/749] Increase logging in run-test 080-oom-throw to debug OOME-in-OOME issues. Ensure the stack trace dumped before setting exception "java.lang.OutOfMemoryError: OutOfMemoryError thrown while trying to throw OutOfMemoryError; no stack trace available" is displayed, by lowering the minimum log severity to WARNING for this test. Test: art/test/testrunner/testrunner.py --host -t 080-oom-throw Bug: 77567088 Change-Id: I3d3ba310e672c9c56310653528f1c1b309a4bf55 --- test/080-oom-throw/run | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/080-oom-throw/run b/test/080-oom-throw/run index eb473782a5..08db73bba0 100644 --- a/test/080-oom-throw/run +++ b/test/080-oom-throw/run @@ -14,4 +14,18 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Ensure the minimum log severity is at least 'WARNING' to display the +# stack trace shown before exception +# +# "java.lang.OutOfMemoryError: OutOfMemoryError thrown while trying +# to throw OutOfMemoryError; no stack trace available" +# +# is set, to try to understand a recurring crash in this test (b/77567088). +case "$ANDROID_LOG_TAGS" in + # Lower the minimum log severity to WARNING if it was initialy set + # to a higher level ('ERROR', 'FATAL' or 'SILENT' -- see + # https://developer.android.com/studio/command-line/logcat#filteringOutput). + (\*:[efs]) export ANDROID_LOG_TAGS='*:w';; +esac + exec ${RUN} $@ --runtime-option -Xmx16m -- GitLab From 3f08e9bb0dfbe9a51e1b378ae20a9338358349eb Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Tue, 1 May 2018 13:42:03 -0700 Subject: [PATCH 320/749] Remove some SIMD recognition code. Test: : test-art-host,target Change-Id: I7f00315c61ed99723236283bc39a4c7fb279df47 --- compiler/optimizing/loop_optimization.cc | 296 +------ compiler/optimizing/loop_optimization.h | 13 +- test/651-checker-simd-minmax/build | 20 - test/651-checker-simd-minmax/expected.txt | 7 - test/651-checker-simd-minmax/info.txt | 1 - .../src/ByteSimdMinMax.java | 273 ------ .../src/CharSimdMinMax.java | 132 --- .../src/DoubleSimdMinMax.java | 126 --- .../src/FloatSimdMinMax.java | 126 --- .../src/IntSimdMinMax.java | 142 ---- .../src/LongSimdMinMax.java | 121 --- test/651-checker-simd-minmax/src/Main.java | 27 - .../src/ShortSimdMinMax.java | 254 ------ test/661-checker-simd-reduc/src/Main.java | 168 ---- test/678-checker-simd-saturation/build | 20 - test/678-checker-simd-saturation/expected.txt | 1 - test/678-checker-simd-saturation/info.txt | 1 - .../678-checker-simd-saturation/src/Main.java | 784 ------------------ 18 files changed, 19 insertions(+), 2493 deletions(-) delete mode 100644 test/651-checker-simd-minmax/build delete mode 100644 test/651-checker-simd-minmax/expected.txt delete mode 100644 test/651-checker-simd-minmax/info.txt delete mode 100644 test/651-checker-simd-minmax/src/ByteSimdMinMax.java delete mode 100644 test/651-checker-simd-minmax/src/CharSimdMinMax.java delete mode 100644 test/651-checker-simd-minmax/src/DoubleSimdMinMax.java delete mode 100644 test/651-checker-simd-minmax/src/FloatSimdMinMax.java delete mode 100644 test/651-checker-simd-minmax/src/IntSimdMinMax.java delete mode 100644 test/651-checker-simd-minmax/src/LongSimdMinMax.java delete mode 100644 test/651-checker-simd-minmax/src/Main.java delete mode 100644 test/651-checker-simd-minmax/src/ShortSimdMinMax.java delete mode 100644 test/678-checker-simd-saturation/build delete mode 100644 test/678-checker-simd-saturation/expected.txt delete mode 100644 test/678-checker-simd-saturation/info.txt delete mode 100644 test/678-checker-simd-saturation/src/Main.java diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 7f1b319c12..0d85c2fbf5 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -153,18 +153,6 @@ static bool IsSignExtensionAndGet(HInstruction* instruction, return false; } } - // A MIN-MAX on narrower operands qualifies as well - // (returning the operator itself). - if (instruction->IsMin() || instruction->IsMax()) { - HBinaryOperation* min_max = instruction->AsBinaryOperation(); - DCHECK(min_max->GetType() == DataType::Type::kInt32 || - min_max->GetType() == DataType::Type::kInt64); - if (IsSignExtensionAndGet(min_max->InputAt(0), type, operand) && - IsSignExtensionAndGet(min_max->InputAt(1), type, operand)) { - *operand = min_max; - return true; - } - } return false; } @@ -228,18 +216,6 @@ static bool IsZeroExtensionAndGet(HInstruction* instruction, return false; } } - // A MIN-MAX on narrower operands qualifies as well - // (returning the operator itself). - if (instruction->IsMin() || instruction->IsMax()) { - HBinaryOperation* min_max = instruction->AsBinaryOperation(); - DCHECK(min_max->GetType() == DataType::Type::kInt32 || - min_max->GetType() == DataType::Type::kInt64); - if (IsZeroExtensionAndGet(min_max->InputAt(0), type, operand) && - IsZeroExtensionAndGet(min_max->InputAt(1), type, operand)) { - *operand = min_max; - return true; - } - } return false; } @@ -363,128 +339,11 @@ static bool IsSubConst2(HGraph* graph, 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; -} - -// Set value range for type (or fail). -static bool CanSetRange(DataType::Type type, - /*out*/ int64_t* uhi, - /*out*/ int64_t* slo, - /*out*/ int64_t* shi) { - if (DataType::Size(type) == 1) { - *uhi = std::numeric_limits::max(); - *slo = std::numeric_limits::min(); - *shi = std::numeric_limits::max(); - return true; - } else if (DataType::Size(type) == 2) { - *uhi = std::numeric_limits::max(); - *slo = std::numeric_limits::min(); - *shi = std::numeric_limits::max(); - return true; - } - return false; -} - -// Accept various saturated addition forms. -static bool IsSaturatedAdd(HInstruction* a, - HInstruction* b, - DataType::Type type, - int64_t lo, - int64_t hi, - bool is_unsigned) { - int64_t ulo = 0, uhi = 0, slo = 0, shi = 0; - if (!CanSetRange(type, &uhi, &slo, &shi)) { - return false; - } - // Tighten the range for signed single clipping on constant. - if (!is_unsigned) { - int64_t c = 0; - if (IsInt64AndGet(a, &c) || IsInt64AndGet(b, &c)) { - // For c in proper range and narrower operand r: - // MIN(r + c, 127) c > 0 - // or MAX(r + c, -128) c < 0 (and possibly redundant bound). - if (0 < c && c <= shi && hi == shi) { - if (lo <= (slo + c)) { - return true; - } - } else if (slo <= c && c < 0 && lo == slo) { - if (hi >= (shi + c)) { - return true; - } - } - } - } - // Detect for narrower operands r and s: - // MIN(r + s, 255) => SAT_ADD_unsigned - // MAX(MIN(r + s, 127), -128) => SAT_ADD_signed. - return is_unsigned ? (lo <= ulo && hi == uhi) : (lo == slo && hi == shi); -} - -// Accept various saturated subtraction forms. -static bool IsSaturatedSub(HInstruction* a, - DataType::Type type, - int64_t lo, - int64_t hi, - bool is_unsigned) { - int64_t ulo = 0, uhi = 0, slo = 0, shi = 0; - if (!CanSetRange(type, &uhi, &slo, &shi)) { - return false; - } - // Tighten the range for signed single clipping on constant. - if (!is_unsigned) { - int64_t c = 0; - if (IsInt64AndGet(a, /*out*/ &c)) { - // For c in proper range and narrower operand r: - // MIN(c - r, 127) c > 0 - // or MAX(c - r, -128) c < 0 (and possibly redundant bound). - if (0 < c && c <= shi && hi == shi) { - if (lo <= (c - shi)) { - return true; - } - } else if (slo <= c && c < 0 && lo == slo) { - if (hi >= (c - slo)) { - return true; - } - } - } - } - // Detect for narrower operands r and s: - // MAX(r - s, 0) => SAT_SUB_unsigned - // MIN(MAX(r - s, -128), 127) => SAT_ADD_signed. - return is_unsigned ? (lo == ulo && hi >= uhi) : (lo == slo && hi == shi); -} - // Detect reductions of the following forms, // x = x_phi + .. // x = x_phi - .. -// x = min(x_phi, ..) -// x = max(x_phi, ..) static bool HasReductionFormat(HInstruction* reduction, HInstruction* phi) { - if (reduction->IsAdd() || reduction->IsMin() || reduction->IsMax()) { + if (reduction->IsAdd()) { return (reduction->InputAt(0) == phi && reduction->InputAt(1) != phi) || (reduction->InputAt(0) != phi && reduction->InputAt(1) == phi); } else if (reduction->IsSub()) { @@ -497,10 +356,6 @@ static bool HasReductionFormat(HInstruction* reduction, HInstruction* phi) { static HVecReduce::ReductionKind GetReductionKind(HVecOperation* reduction) { if (reduction->IsVecAdd() || reduction->IsVecSub() || reduction->IsVecSADAccumulate()) { return HVecReduce::kSum; - } else if (reduction->IsVecMin()) { - return HVecReduce::kMin; - } else if (reduction->IsVecMax()) { - return HVecReduce::kMax; } LOG(FATAL) << "Unsupported SIMD reduction " << reduction->GetId(); UNREACHABLE(); @@ -1601,37 +1456,6 @@ 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); - HInstruction* r = opa; - HInstruction* s = opb; - bool is_unsigned = false; - if (HasVectorRestrictions(restrictions, kNoMinMax)) { - return false; - } else if (HasVectorRestrictions(restrictions, kNoHiBits) && - !IsNarrowerOperands(opa, opb, type, &r, &s, &is_unsigned)) { - return false; // reject, unless all operands are same-extension narrower - } - // Accept MIN/MAX(x, y) for vectorizable operands. - DCHECK(r != nullptr && s != nullptr); - if (generate_code && vector_mode_ != kVector) { // de-idiom - r = opa; - s = opb; - } - if (VectorizeUse(node, r, generate_code, type, restrictions) && - VectorizeUse(node, s, generate_code, type, restrictions)) { - if (generate_code) { - GenerateVecOp( - instruction, vector_map_->Get(r), vector_map_->Get(s), type, is_unsigned); - } - return true; - } } return false; } @@ -1687,7 +1511,7 @@ bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrict *restrictions |= kNoDiv; return TrySetVectorLength(4); case DataType::Type::kInt64: - *restrictions |= kNoDiv | kNoMul | kNoMinMax; + *restrictions |= kNoDiv | kNoMul; return TrySetVectorLength(2); case DataType::Type::kFloat32: *restrictions |= kNoReduction; @@ -1717,13 +1541,13 @@ bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrict *restrictions |= kNoDiv | kNoSAD; return TrySetVectorLength(4); case DataType::Type::kInt64: - *restrictions |= kNoMul | kNoDiv | kNoShr | kNoAbs | kNoMinMax | kNoSAD; + *restrictions |= kNoMul | kNoDiv | kNoShr | kNoAbs | kNoSAD; return TrySetVectorLength(2); case DataType::Type::kFloat32: - *restrictions |= kNoMinMax | kNoReduction; // minmax: -0.0 vs +0.0 + *restrictions |= kNoReduction; return TrySetVectorLength(4); case DataType::Type::kFloat64: - *restrictions |= kNoMinMax | kNoReduction; // minmax: -0.0 vs +0.0 + *restrictions |= kNoReduction; return TrySetVectorLength(2); default: break; @@ -1736,11 +1560,11 @@ bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrict case DataType::Type::kBool: case DataType::Type::kUint8: case DataType::Type::kInt8: - *restrictions |= kNoDiv | kNoSaturation; + *restrictions |= kNoDiv; return TrySetVectorLength(16); case DataType::Type::kUint16: case DataType::Type::kInt16: - *restrictions |= kNoDiv | kNoSaturation | kNoStringCharAt; + *restrictions |= kNoDiv | kNoStringCharAt; return TrySetVectorLength(8); case DataType::Type::kInt32: *restrictions |= kNoDiv; @@ -1749,10 +1573,10 @@ bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrict *restrictions |= kNoDiv; return TrySetVectorLength(2); case DataType::Type::kFloat32: - *restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN) + *restrictions |= kNoReduction; return TrySetVectorLength(4); case DataType::Type::kFloat64: - *restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN) + *restrictions |= kNoReduction; return TrySetVectorLength(2); default: break; @@ -1765,11 +1589,11 @@ bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrict case DataType::Type::kBool: case DataType::Type::kUint8: case DataType::Type::kInt8: - *restrictions |= kNoDiv | kNoSaturation; + *restrictions |= kNoDiv; return TrySetVectorLength(16); case DataType::Type::kUint16: case DataType::Type::kInt16: - *restrictions |= kNoDiv | kNoSaturation | kNoStringCharAt; + *restrictions |= kNoDiv | kNoStringCharAt; return TrySetVectorLength(8); case DataType::Type::kInt32: *restrictions |= kNoDiv; @@ -1778,10 +1602,10 @@ bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrict *restrictions |= kNoDiv; return TrySetVectorLength(2); case DataType::Type::kFloat32: - *restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN) + *restrictions |= kNoReduction; return TrySetVectorLength(4); case DataType::Type::kFloat64: - *restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN) + *restrictions |= kNoReduction; return TrySetVectorLength(2); default: break; @@ -2006,8 +1830,7 @@ HInstruction* HLoopOptimization::ReduceAndExtractIfNeeded(HInstruction* instruct void HLoopOptimization::GenerateVecOp(HInstruction* org, HInstruction* opa, HInstruction* opb, - DataType::Type type, - bool is_unsigned) { + DataType::Type type) { uint32_t dex_pc = org->GetDexPc(); HInstruction* vector = nullptr; DataType::Type org_type = org->GetType(); @@ -2072,24 +1895,6 @@ void HLoopOptimization::GenerateVecOp(HInstruction* org, GENERATE_VEC( new (global_allocator_) HVecUShr(global_allocator_, opa, opb, type, vector_length_, dex_pc), new (global_allocator_) HUShr(org_type, opa, opb, dex_pc)); - case HInstruction::kMin: - GENERATE_VEC( - new (global_allocator_) HVecMin(global_allocator_, - opa, - opb, - HVecOperation::ToProperType(type, is_unsigned), - vector_length_, - dex_pc), - new (global_allocator_) HMin(org_type, opa, opb, dex_pc)); - case HInstruction::kMax: - GENERATE_VEC( - new (global_allocator_) HVecMax(global_allocator_, - opa, - opb, - HVecOperation::ToProperType(type, is_unsigned), - vector_length_, - dex_pc), - new (global_allocator_) HMax(org_type, opa, opb, dex_pc)); case HInstruction::kAbs: DCHECK(opb == nullptr); GENERATE_VEC( @@ -2108,79 +1913,6 @@ 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 on narrower operands? We will try both - // formats since, e.g., x+c can be interpreted as x+c and x-(-c), depending - // on what clipping values are used, to get most benefits. - int64_t lo = std::numeric_limits::min(); - int64_t hi = std::numeric_limits::max(); - HInstruction* clippee = FindClippee(instruction, &lo, &hi); - HInstruction* a = nullptr; - HInstruction* b = nullptr; - HInstruction* r = nullptr; - HInstruction* s = nullptr; - bool is_unsigned = false; - bool is_add = true; - int64_t c = 0; - // First try for saturated addition. - if (IsAddConst2(graph_, clippee, /*out*/ &a, /*out*/ &b, /*out*/ &c) && c == 0 && - IsNarrowerOperands(a, b, type, &r, &s, &is_unsigned) && - IsSaturatedAdd(r, s, type, lo, hi, is_unsigned)) { - is_add = true; - } else { - // Then try again for saturated subtraction. - a = b = r = s = nullptr; - if (IsSubConst2(graph_, clippee, /*out*/ &a, /*out*/ &b) && - IsNarrowerOperands(a, b, type, &r, &s, &is_unsigned) && - IsSaturatedSub(r, type, lo, hi, is_unsigned)) { - is_add = false; - } else { - return false; - } - } - // Accept saturation idiom for vectorizable operands. - DCHECK(r != nullptr && s != nullptr); - 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(new (global_allocator_) HVecSaturationAdd( - global_allocator_, op1, op2, vtype, vector_length_, kNoDexPc)) - : reinterpret_cast(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 11e969875e..7807da15ed 100644 --- a/compiler/optimizing/loop_optimization.h +++ b/compiler/optimizing/loop_optimization.h @@ -78,12 +78,10 @@ class HLoopOptimization : public HOptimization { kNoSignedHAdd = 1 << 5, // no signed halving add kNoUnroundedHAdd = 1 << 6, // no unrounded halving add kNoAbs = 1 << 7, // no absolute value - kNoMinMax = 1 << 8, // no min/max - kNoStringCharAt = 1 << 9, // no StringCharAt - 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 + kNoStringCharAt = 1 << 8, // no StringCharAt + kNoReduction = 1 << 9, // no reduction + kNoSAD = 1 << 10, // no sum of absolute differences (SAD) + kNoWideSAD = 1 << 11, // no sum of absolute differences (SAD) with operand widening }; /* @@ -188,8 +186,7 @@ class HLoopOptimization : public HOptimization { void GenerateVecOp(HInstruction* org, HInstruction* opa, HInstruction* opb, - DataType::Type type, - bool is_unsigned = false); + DataType::Type type); // Vectorization idioms. bool VectorizeSaturationIdiom(LoopNode* node, diff --git a/test/651-checker-simd-minmax/build b/test/651-checker-simd-minmax/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/651-checker-simd-minmax/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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 -# -# 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/651-checker-simd-minmax/expected.txt b/test/651-checker-simd-minmax/expected.txt deleted file mode 100644 index f362c45b77..0000000000 --- a/test/651-checker-simd-minmax/expected.txt +++ /dev/null @@ -1,7 +0,0 @@ -ByteSimdMinMax passed -CharSimdMinMax passed -ShortSimdMinMax passed -IntSimdMinMax passed -LongSimdMinMax passed -DoubleSimdMinMax passed -FloatSimdMinMax passed diff --git a/test/651-checker-simd-minmax/info.txt b/test/651-checker-simd-minmax/info.txt deleted file mode 100644 index 73af1242c0..0000000000 --- a/test/651-checker-simd-minmax/info.txt +++ /dev/null @@ -1 +0,0 @@ -Functional tests on min/max SIMD vectorization. diff --git a/test/651-checker-simd-minmax/src/ByteSimdMinMax.java b/test/651-checker-simd-minmax/src/ByteSimdMinMax.java deleted file mode 100644 index fff15faeaa..0000000000 --- a/test/651-checker-simd-minmax/src/ByteSimdMinMax.java +++ /dev/null @@ -1,273 +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. - */ - -/** - * Tests for MIN/MAX vectorization. - */ -public class ByteSimdMinMax { - - /// CHECK-START: void ByteSimdMinMax.doitMin(byte[], byte[], byte[]) loop_optimization (before) - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMin(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMin(byte[] x, byte[] y, byte[] z) { - int min = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < min; i++) { - x[i] = (byte) Math.min(y[i], z[i]); - } - } - - /// CHECK-START: void ByteSimdMinMax.doitMinUnsigned(byte[], byte[], byte[]) instruction_simplifier (before) - /// CHECK-DAG: <> IntConstant 255 loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> And [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> And [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMinIntInt loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START: void ByteSimdMinMax.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (before) - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMinUnsigned(byte[] x, byte[] y, byte[] z) { - int min = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < min; i++) { - x[i] = (byte) Math.min(y[i] & 0xff, z[i] & 0xff); - } - } - - /// CHECK-START: void ByteSimdMinMax.doitMax(byte[], byte[], byte[]) loop_optimization (before) - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMax(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMax(byte[] x, byte[] y, byte[] z) { - int min = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < min; i++) { - x[i] = (byte) Math.max(y[i], z[i]); - } - } - - /// CHECK-START: void ByteSimdMinMax.doitMaxUnsigned(byte[], byte[], byte[]) instruction_simplifier (before) - /// CHECK-DAG: <> IntConstant 255 loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> And [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> And [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMaxIntInt loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START: void ByteSimdMinMax.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (before) - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Uint8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMaxUnsigned(byte[] x, byte[] y, byte[] z) { - int min = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < min; i++) { - x[i] = (byte) Math.max(y[i] & 0xff, z[i] & 0xff); - } - } - - /// CHECK-START: void ByteSimdMinMax.doitMin100(byte[], byte[]) loop_optimization (before) - /// CHECK-DAG: <> IntConstant 100 loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMin100(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 100 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMin100(byte[] x, byte[] y) { - int min = Math.min(x.length, y.length); - for (int i = 0; i < min; i++) { - x[i] = (byte) Math.min(y[i], 100); - } - } - - /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMinMax(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant -11 loop:none - /// CHECK-DAG: <> IntConstant 23 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMinMax(byte[] x, byte[] y) { - int n = Math.min(x.length, y.length); - for (int i = 0; i < n; i++) { - x[i] = (byte) Math.max(-11, Math.min(y[i], 23)); - } - } - - /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMinMaxUnsigned(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 11 loop:none - /// CHECK-DAG: <> IntConstant 23 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint8 loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Uint8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMinMaxUnsigned(byte[] x, byte[] y) { - int n = Math.min(x.length, y.length); - for (int i = 0; i < n; i++) { - x[i] = (byte) Math.max(11, Math.min(y[i] & 0xff, 23)); - } - } - - /// CHECK-START-{ARM,ARM64}: void ByteSimdMinMax.doitMinAlt(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMinAlt(byte[] x, byte[] y, byte[] z) { - int n = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < n; ++i) { - x[i] = y[i] < z[i] ? y[i] : z[i]; - } - } - - /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMaxAlt(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMaxAlt(byte[] x, byte[] y, byte[] z) { - int n = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < n; ++i) { - x[i] = y[i] > z[i] ? y[i] : z[i]; - } - } - - public static void main() { - // Initialize cross-values for all possible values. - int total = 256 * 256; - byte[] x = new byte[total]; - byte[] y = new byte[total]; - byte[] z = new byte[total]; - int k = 0; - for (int i = 0; i < 256; i++) { - for (int j = 0; j < 256; j++) { - x[k] = 0; - y[k] = (byte) i; - z[k] = (byte) j; - k++; - } - } - - // And test. - doitMin(x, y, z); - for (int i = 0; i < total; i++) { - byte expected = (byte) Math.min(y[i], z[i]); - expectEquals(expected, x[i]); - } - doitMinUnsigned(x, y, z); - for (int i = 0; i < total; i++) { - byte expected = (byte) Math.min(y[i] & 0xff, z[i] & 0xff); - expectEquals(expected, x[i]); - } - doitMax(x, y, z); - for (int i = 0; i < total; i++) { - byte expected = (byte) Math.max(y[i], z[i]); - expectEquals(expected, x[i]); - } - doitMaxUnsigned(x, y, z); - for (int i = 0; i < total; i++) { - byte expected = (byte) Math.max(y[i] & 0xff, z[i] & 0xff); - expectEquals(expected, x[i]); - } - doitMin100(x, y); - for (int i = 0; i < total; i++) { - byte expected = (byte) Math.min(y[i], 100); - expectEquals(expected, x[i]); - } - doitMinMax(x, y); - for (int i = 0; i < total; i++) { - int s = y[i]; - byte expected = (byte) (s < -11 ? -11 : (s > 23 ? 23 : s)); - expectEquals(expected, x[i]); - } - doitMinMaxUnsigned(x, y); - for (int i = 0; i < total; i++) { - int u = y[i] & 0xff; - byte expected = (byte) (u < 11 ? 11 : (u > 23 ? 23 : u)); - expectEquals(expected, x[i]); - } - doitMinAlt(x, y, z); - for (int i = 0; i < total; i++) { - byte expected = (byte) Math.min(y[i], z[i]); - expectEquals(expected, x[i]); - } - doitMaxAlt(x, y, z); - for (int i = 0; i < total; i++) { - byte expected = (byte) Math.max(y[i], z[i]); - expectEquals(expected, x[i]); - } - System.out.println("ByteSimdMinMax passed"); - } - - private static void expectEquals(byte expected, byte result) { - if (expected != result) { - throw new Error("Expected: " + expected + ", found: " + result); - } - } -} diff --git a/test/651-checker-simd-minmax/src/CharSimdMinMax.java b/test/651-checker-simd-minmax/src/CharSimdMinMax.java deleted file mode 100644 index 30169c4591..0000000000 --- a/test/651-checker-simd-minmax/src/CharSimdMinMax.java +++ /dev/null @@ -1,132 +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. - */ - -/** - * Tests for MIN/MAX vectorization. - */ -public class CharSimdMinMax { - - /// CHECK-START: void CharSimdMinMax.doitMin(char[], char[], char[]) loop_optimization (before) - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64,MIPS64}: void CharSimdMinMax.doitMin(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMin(char[] x, char[] y, char[] z) { - int min = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < min; i++) { - x[i] = (char) Math.min(y[i], z[i]); - } - } - - /// CHECK-START: void CharSimdMinMax.doitMax(char[], char[], char[]) loop_optimization (before) - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64,MIPS64}: void CharSimdMinMax.doitMax(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMax(char[] x, char[] y, char[] z) { - int min = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < min; i++) { - x[i] = (char) Math.max(y[i], z[i]); - } - } - - /// CHECK-START: void CharSimdMinMax.doitMin100(char[], char[]) loop_optimization (before) - /// CHECK-DAG: <> IntConstant 100 loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM64,MIPS64}: void CharSimdMinMax.doitMin100(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 100 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMin100(char[] x, char[] y) { - int min = Math.min(x.length, y.length); - for (int i = 0; i < min; i++) { - x[i] = (char) Math.min(y[i], 100); - } - } - - public static void main() { - char[] interesting = { - 0x0000, 0x0001, 0x007f, 0x0080, 0x0081, 0x00ff, - 0x0100, 0x0101, 0x017f, 0x0180, 0x0181, 0x01ff, - 0x7f00, 0x7f01, 0x7f7f, 0x7f80, 0x7f81, 0x7fff, - 0x8000, 0x8001, 0x807f, 0x8080, 0x8081, 0x80ff, - 0x8100, 0x8101, 0x817f, 0x8180, 0x8181, 0x81ff, - 0xff00, 0xff01, 0xff7f, 0xff80, 0xff81, 0xffff - }; - // Initialize cross-values for the interesting values. - int total = interesting.length * interesting.length; - char[] x = new char[total]; - char[] y = new char[total]; - char[] z = new char[total]; - int k = 0; - for (int i = 0; i < interesting.length; i++) { - for (int j = 0; j < interesting.length; j++) { - x[k] = 0; - y[k] = interesting[i]; - z[k] = interesting[j]; - k++; - } - } - - // And test. - doitMin(x, y, z); - for (int i = 0; i < total; i++) { - char expected = (char) Math.min(y[i], z[i]); - expectEquals(expected, x[i]); - } - doitMax(x, y, z); - for (int i = 0; i < total; i++) { - char expected = (char) Math.max(y[i], z[i]); - expectEquals(expected, x[i]); - } - doitMin100(x, y); - for (int i = 0; i < total; i++) { - char expected = (char) Math.min(y[i], 100); - expectEquals(expected, x[i]); - } - - System.out.println("CharSimdMinMax passed"); - } - - private static void expectEquals(char expected, char result) { - if (expected != result) { - throw new Error("Expected: " + expected + ", found: " + result); - } - } -} diff --git a/test/651-checker-simd-minmax/src/DoubleSimdMinMax.java b/test/651-checker-simd-minmax/src/DoubleSimdMinMax.java deleted file mode 100644 index da20594db8..0000000000 --- a/test/651-checker-simd-minmax/src/DoubleSimdMinMax.java +++ /dev/null @@ -1,126 +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. - */ - -/** - * Tests for MIN/MAX vectorization. - */ -public class DoubleSimdMinMax { - - /// CHECK-START: void DoubleSimdMinMax.doitMin(double[], double[], double[]) loop_optimization (before) - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - // TODO x86: 0.0 vs -0.0? - // TODO MIPS64: min(x, NaN)? - // - /// CHECK-START-ARM64: void DoubleSimdMinMax.doitMin(double[], double[], double[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMin(double[] x, double[] y, double[] z) { - int min = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < min; i++) { - x[i] = Math.min(y[i], z[i]); - } - } - - /// CHECK-START: void DoubleSimdMinMax.doitMax(double[], double[], double[]) loop_optimization (before) - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - // TODO x86: 0.0 vs -0.0? - // TODO MIPS64: max(x, NaN)? - // - /// CHECK-START-ARM64: void DoubleSimdMinMax.doitMax(double[], double[], double[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMax(double[] x, double[] y, double[] z) { - int min = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < min; i++) { - x[i] = Math.max(y[i], z[i]); - } - } - - public static void main() { - double[] interesting = { - -0.0f, - +0.0f, - -1.0f, - +1.0f, - -3.14f, - +3.14f, - -100.0f, - +100.0f, - -4444.44f, - +4444.44f, - Double.MIN_NORMAL, - Double.MIN_VALUE, - Double.MAX_VALUE, - Double.NEGATIVE_INFINITY, - Double.POSITIVE_INFINITY, - Double.NaN - }; - // Initialize cross-values for the interesting values. - int total = interesting.length * interesting.length; - double[] x = new double[total]; - double[] y = new double[total]; - double[] z = new double[total]; - int k = 0; - for (int i = 0; i < interesting.length; i++) { - for (int j = 0; j < interesting.length; j++) { - x[k] = 0; - y[k] = interesting[i]; - z[k] = interesting[j]; - k++; - } - } - - // And test. - doitMin(x, y, z); - for (int i = 0; i < total; i++) { - double expected = Math.min(y[i], z[i]); - expectEquals(expected, x[i]); - } - doitMax(x, y, z); - for (int i = 0; i < total; i++) { - double expected = Math.max(y[i], z[i]); - expectEquals(expected, x[i]); - } - - System.out.println("DoubleSimdMinMax passed"); - } - - private static void expectEquals(double expected, double result) { - // Tests the bits directly. This distinguishes correctly between +0.0 - // and -0.0 and returns a canonical representation for all NaN. - long expected_bits = Double.doubleToLongBits(expected); - long result_bits = Double.doubleToLongBits(result); - if (expected_bits != result_bits) { - throw new Error("Expected: " + expected + - "(0x" + Long.toHexString(expected_bits) + "), found: " + result + - "(0x" + Long.toHexString(result_bits) + ")"); - } - } -} diff --git a/test/651-checker-simd-minmax/src/FloatSimdMinMax.java b/test/651-checker-simd-minmax/src/FloatSimdMinMax.java deleted file mode 100644 index 645081248a..0000000000 --- a/test/651-checker-simd-minmax/src/FloatSimdMinMax.java +++ /dev/null @@ -1,126 +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. - */ - -/** - * Tests for MIN/MAX vectorization. - */ -public class FloatSimdMinMax { - - /// CHECK-START: void FloatSimdMinMax.doitMin(float[], float[], float[]) loop_optimization (before) - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - // TODO x86: 0.0 vs -0.0? - // TODO MIPS64: min(x, NaN)? - // - /// CHECK-START-ARM64: void FloatSimdMinMax.doitMin(float[], float[], float[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMin(float[] x, float[] y, float[] z) { - int min = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < min; i++) { - x[i] = Math.min(y[i], z[i]); - } - } - - /// CHECK-START: void FloatSimdMinMax.doitMax(float[], float[], float[]) loop_optimization (before) - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - // TODO x86: 0.0 vs -0.0? - // TODO MIPS64: max(x, NaN)? - // - /// CHECK-START-ARM64: void FloatSimdMinMax.doitMax(float[], float[], float[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMax(float[] x, float[] y, float[] z) { - int min = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < min; i++) { - x[i] = Math.max(y[i], z[i]); - } - } - - public static void main() { - float[] interesting = { - -0.0f, - +0.0f, - -1.0f, - +1.0f, - -3.14f, - +3.14f, - -100.0f, - +100.0f, - -4444.44f, - +4444.44f, - Float.MIN_NORMAL, - Float.MIN_VALUE, - Float.MAX_VALUE, - Float.NEGATIVE_INFINITY, - Float.POSITIVE_INFINITY, - Float.NaN - }; - // Initialize cross-values for the interesting values. - int total = interesting.length * interesting.length; - float[] x = new float[total]; - float[] y = new float[total]; - float[] z = new float[total]; - int k = 0; - for (int i = 0; i < interesting.length; i++) { - for (int j = 0; j < interesting.length; j++) { - x[k] = 0; - y[k] = interesting[i]; - z[k] = interesting[j]; - k++; - } - } - - // And test. - doitMin(x, y, z); - for (int i = 0; i < total; i++) { - float expected = Math.min(y[i], z[i]); - expectEquals(expected, x[i]); - } - doitMax(x, y, z); - for (int i = 0; i < total; i++) { - float expected = Math.max(y[i], z[i]); - expectEquals(expected, x[i]); - } - - System.out.println("FloatSimdMinMax passed"); - } - - private static void expectEquals(float expected, float result) { - // Tests the bits directly. This distinguishes correctly between +0.0 - // and -0.0 and returns a canonical representation for all NaN. - int expected_bits = Float.floatToIntBits(expected); - int result_bits = Float.floatToIntBits(result); - if (expected_bits != result_bits) { - throw new Error("Expected: " + expected + - "(0x" + Integer.toHexString(expected_bits) + "), found: " + result + - "(0x" + Integer.toHexString(result_bits) + ")"); - } - } -} diff --git a/test/651-checker-simd-minmax/src/IntSimdMinMax.java b/test/651-checker-simd-minmax/src/IntSimdMinMax.java deleted file mode 100644 index ad88843175..0000000000 --- a/test/651-checker-simd-minmax/src/IntSimdMinMax.java +++ /dev/null @@ -1,142 +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. - */ - -/** - * Tests for MIN/MAX vectorization. - */ -public class IntSimdMinMax { - - /// CHECK-START: void IntSimdMinMax.doitMin(int[], int[], int[]) loop_optimization (before) - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64,MIPS64}: void IntSimdMinMax.doitMin(int[], int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int32 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMin(int[] x, int[] y, int[] z) { - int min = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < min; i++) { - x[i] = Math.min(y[i], z[i]); - } - } - - /// CHECK-START: void IntSimdMinMax.doitMax(int[], int[], int[]) loop_optimization (before) - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64,MIPS64}: void IntSimdMinMax.doitMax(int[], int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Int32 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMax(int[] x, int[] y, int[] z) { - int min = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < min; i++) { - x[i] = Math.max(y[i], z[i]); - } - } - - /// CHECK-START-{ARM,ARM64}: int IntSimdMinMax.findMin(int[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: VecExtractScalar [<>] loop:none - private static int findMin(int[] a) { - int x = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i++) { - if (a[i] < x) - x = a[i]; - } - return x; - } - - /// CHECK-START-{ARM,ARM64}: int IntSimdMinMax.findMax(int[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: VecExtractScalar [<>] loop:none - private static int findMax(int[] a) { - int x = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i++) { - if (a[i] > x) - x = a[i]; - } - return x; - } - - public static void main() { - int[] interesting = { - 0x00000000, 0x00000001, 0x00007fff, 0x00008000, 0x00008001, 0x0000ffff, - 0x00010000, 0x00010001, 0x00017fff, 0x00018000, 0x00018001, 0x0001ffff, - 0x7fff0000, 0x7fff0001, 0x7fff7fff, 0x7fff8000, 0x7fff8001, 0x7fffffff, - 0x80000000, 0x80000001, 0x80007fff, 0x80008000, 0x80008001, 0x8000ffff, - 0x80010000, 0x80010001, 0x80017fff, 0x80018000, 0x80018001, 0x8001ffff, - 0xffff0000, 0xffff0001, 0xffff7fff, 0xffff8000, 0xffff8001, 0xffffffff - }; - // Initialize cross-values for the interesting values. - int total = interesting.length * interesting.length; - int[] x = new int[total]; - int[] y = new int[total]; - int[] z = new int[total]; - int k = 0; - for (int i = 0; i < interesting.length; i++) { - for (int j = 0; j < interesting.length; j++) { - x[k] = 0; - y[k] = interesting[i]; - z[k] = interesting[j]; - k++; - } - } - - // And test. - doitMin(x, y, z); - for (int i = 0; i < total; i++) { - int expected = Math.min(y[i], z[i]); - expectEquals(expected, x[i]); - } - doitMax(x, y, z); - for (int i = 0; i < total; i++) { - int expected = Math.max(y[i], z[i]); - expectEquals(expected, x[i]); - } - expectEquals(Integer.MIN_VALUE, findMin(x)); - expectEquals(Integer.MAX_VALUE, findMax(x)); - expectEquals(Integer.MIN_VALUE, findMin(y)); - expectEquals(Integer.MAX_VALUE, findMax(y)); - expectEquals(Integer.MIN_VALUE, findMin(z)); - expectEquals(Integer.MAX_VALUE, findMax(z)); - - System.out.println("IntSimdMinMax passed"); - } - - private static void expectEquals(int expected, int result) { - if (expected != result) { - throw new Error("Expected: " + expected + ", found: " + result); - } - } -} diff --git a/test/651-checker-simd-minmax/src/LongSimdMinMax.java b/test/651-checker-simd-minmax/src/LongSimdMinMax.java deleted file mode 100644 index bb0c6047ed..0000000000 --- a/test/651-checker-simd-minmax/src/LongSimdMinMax.java +++ /dev/null @@ -1,121 +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. - */ - -/** - * Tests for MIN/MAX vectorization. - */ -public class LongSimdMinMax { - - /// CHECK-START: void LongSimdMinMax.doitMin(long[], long[], long[]) loop_optimization (before) - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - // Not directly supported for longs. - // - /// CHECK-START-ARM64: void LongSimdMinMax.doitMin(long[], long[], long[]) loop_optimization (after) - /// CHECK-NOT: VecMin - // - /// CHECK-START-MIPS64: void LongSimdMinMax.doitMin(long[], long[], long[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - - private static void doitMin(long[] x, long[] y, long[] z) { - int min = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < min; i++) { - x[i] = Math.min(y[i], z[i]); - } - } - - /// CHECK-START: void LongSimdMinMax.doitMax(long[], long[], long[]) loop_optimization (before) - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - // Not directly supported for longs. - // - /// CHECK-START-ARM64: void LongSimdMinMax.doitMax(long[], long[], long[]) loop_optimization (after) - /// CHECK-NOT: VecMax - // - /// CHECK-START-MIPS64: void LongSimdMinMax.doitMax(long[], long[], long[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMax(long[] x, long[] y, long[] z) { - int min = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < min; i++) { - x[i] = Math.max(y[i], z[i]); - } - } - - public static void main() { - long[] interesting = { - 0x0000000000000000L, 0x0000000000000001L, 0x000000007fffffffL, - 0x0000000080000000L, 0x0000000080000001L, 0x00000000ffffffffL, - 0x0000000100000000L, 0x0000000100000001L, 0x000000017fffffffL, - 0x0000000180000000L, 0x0000000180000001L, 0x00000001ffffffffL, - 0x7fffffff00000000L, 0x7fffffff00000001L, 0x7fffffff7fffffffL, - 0x7fffffff80000000L, 0x7fffffff80000001L, 0x7fffffffffffffffL, - 0x8000000000000000L, 0x8000000000000001L, 0x800000007fffffffL, - 0x8000000080000000L, 0x8000000080000001L, 0x80000000ffffffffL, - 0x8000000100000000L, 0x8000000100000001L, 0x800000017fffffffL, - 0x8000000180000000L, 0x8000000180000001L, 0x80000001ffffffffL, - 0xffffffff00000000L, 0xffffffff00000001L, 0xffffffff7fffffffL, - 0xffffffff80000000L, 0xffffffff80000001L, 0xffffffffffffffffL - }; - // Initialize cross-values for the interesting values. - int total = interesting.length * interesting.length; - long[] x = new long[total]; - long[] y = new long[total]; - long[] z = new long[total]; - int k = 0; - for (int i = 0; i < interesting.length; i++) { - for (int j = 0; j < interesting.length; j++) { - x[k] = 0; - y[k] = interesting[i]; - z[k] = interesting[j]; - k++; - } - } - - // And test. - doitMin(x, y, z); - for (int i = 0; i < total; i++) { - long expected = Math.min(y[i], z[i]); - expectEquals(expected, x[i]); - } - doitMax(x, y, z); - for (int i = 0; i < total; i++) { - long expected = Math.max(y[i], z[i]); - expectEquals(expected, x[i]); - } - - System.out.println("LongSimdMinMax passed"); - } - - private static void expectEquals(long expected, long result) { - if (expected != result) { - throw new Error("Expected: " + expected + ", found: " + result); - } - } -} diff --git a/test/651-checker-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/Main.java deleted file mode 100644 index 9134dd1edd..0000000000 --- a/test/651-checker-simd-minmax/src/Main.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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 Main { - public static void main(String[] args) { - ByteSimdMinMax.main(); - CharSimdMinMax.main(); - ShortSimdMinMax.main(); - IntSimdMinMax.main(); - LongSimdMinMax.main(); - DoubleSimdMinMax.main(); - FloatSimdMinMax.main(); - } -} diff --git a/test/651-checker-simd-minmax/src/ShortSimdMinMax.java b/test/651-checker-simd-minmax/src/ShortSimdMinMax.java deleted file mode 100644 index 9075d8007c..0000000000 --- a/test/651-checker-simd-minmax/src/ShortSimdMinMax.java +++ /dev/null @@ -1,254 +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. - */ - -/** - * Tests for MIN/MAX vectorization. - */ -public class ShortSimdMinMax { - - /// CHECK-START: void ShortSimdMinMax.doitMin(short[], short[], short[]) loop_optimization (before) - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMin(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMin(short[] x, short[] y, short[] z) { - int min = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < min; i++) { - x[i] = (short) Math.min(y[i], z[i]); - } - } - - /// CHECK-START: void ShortSimdMinMax.doitMinUnsigned(short[], short[], short[]) instruction_simplifier (before) - /// CHECK-DAG: <> IntConstant 65535 loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> And [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> And [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMinIntInt loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START: void ShortSimdMinMax.doitMinUnsigned(short[], short[], short[]) loop_optimization (before) - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMinUnsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMinUnsigned(short[] x, short[] y, short[] z) { - int min = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < min; i++) { - x[i] = (short) Math.min(y[i] & 0xffff, z[i] & 0xffff); - } - } - - /// CHECK-START: void ShortSimdMinMax.doitMax(short[], short[], short[]) loop_optimization (before) - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMax(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMax(short[] x, short[] y, short[] z) { - int min = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < min; i++) { - x[i] = (short) Math.max(y[i], z[i]); - } - } - - /// CHECK-START: void ShortSimdMinMax.doitMaxUnsigned(short[], short[], short[]) instruction_simplifier (before) - /// CHECK-DAG: <> IntConstant 65535 loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> And [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> And [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMaxIntInt loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START: void ShortSimdMinMax.doitMaxUnsigned(short[], short[], short[]) loop_optimization (before) - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMaxUnsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMaxUnsigned(short[] x, short[] y, short[] z) { - int min = Math.min(x.length, Math.min(y.length, z.length)); - for (int i = 0; i < min; i++) { - x[i] = (short) Math.max(y[i] & 0xffff, z[i] & 0xffff); - } - } - - /// CHECK-START: void ShortSimdMinMax.doitMin100(short[], short[]) loop_optimization (before) - /// CHECK-DAG: <> IntConstant 100 loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMin100(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 100 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMin100(short[] x, short[] y) { - int min = Math.min(x.length, y.length); - for (int i = 0; i < min; i++) { - x[i] = (short) Math.min(y[i], 100); - } - } - - /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMinMax(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant -1111 loop:none - /// CHECK-DAG: <> IntConstant 2323 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMinMax(short[] x, short[] y) { - int n = Math.min(x.length, y.length); - for (int i = 0; i < n; i++) { - x[i] = (short) Math.max(-1111, Math.min(y[i], 2323)); - } - } - - /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMinMaxUnsigned(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 1111 loop:none - /// CHECK-DAG: <> IntConstant 2323 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - private static void doitMinMaxUnsigned(short[] x, short[] y) { - int n = Math.min(x.length, y.length); - for (int i = 0; i < n; i++) { - x[i] = (short) Math.max(1111, Math.min(y[i] & 0xffff, 2323)); - } - } - - public static void main() { - short[] interesting = { - (short) 0x0000, (short) 0x0001, (short) 0x007f, - (short) 0x0080, (short) 0x0081, (short) 0x00ff, - (short) 0x0100, (short) 0x0101, (short) 0x017f, - (short) 0x0180, (short) 0x0181, (short) 0x01ff, - (short) 0x7f00, (short) 0x7f01, (short) 0x7f7f, - (short) 0x7f80, (short) 0x7f81, (short) 0x7fff, - (short) 0x8000, (short) 0x8001, (short) 0x807f, - (short) 0x8080, (short) 0x8081, (short) 0x80ff, - (short) 0x8100, (short) 0x8101, (short) 0x817f, - (short) 0x8180, (short) 0x8181, (short) 0x81ff, - (short) 0xff00, (short) 0xff01, (short) 0xff7f, - (short) 0xff80, (short) 0xff81, (short) 0xffff - }; - // Initialize cross-values for the interesting values. - int total = interesting.length * interesting.length; - short[] x = new short[total]; - short[] y = new short[total]; - short[] z = new short[total]; - int k = 0; - for (int i = 0; i < interesting.length; i++) { - for (int j = 0; j < interesting.length; j++) { - x[k] = 0; - y[k] = interesting[i]; - z[k] = interesting[j]; - k++; - } - } - - // And test. - doitMin(x, y, z); - for (int i = 0; i < total; i++) { - short expected = (short) Math.min(y[i], z[i]); - expectEquals(expected, x[i]); - } - doitMinUnsigned(x, y, z); - for (int i = 0; i < total; i++) { - short expected = (short) Math.min(y[i] & 0xffff, z[i] & 0xffff); - expectEquals(expected, x[i]); - } - doitMax(x, y, z); - for (int i = 0; i < total; i++) { - short expected = (short) Math.max(y[i], z[i]); - expectEquals(expected, x[i]); - } - doitMaxUnsigned(x, y, z); - for (int i = 0; i < total; i++) { - short expected = (short) Math.max(y[i] & 0xffff, z[i] & 0xffff); - expectEquals(expected, x[i]); - } - doitMin100(x, y); - for (int i = 0; i < total; i++) { - short expected = (short) Math.min(y[i], 100); - expectEquals(expected, x[i]); - } - doitMinMax(x, y); - for (int i = 0; i < total; i++) { - int s = y[i]; - short expected = (short) (s < -1111 ? -1111 : (s > 2323 ? 2323 : s)); - expectEquals(expected, x[i]); - } - doitMinMaxUnsigned(x, y); - for (int i = 0; i < total; i++) { - int u = y[i] & 0xffff; - short expected = (short) (u < 1111 ? 1111 : (u > 2323 ? 2323 : u)); - expectEquals(expected, x[i]); - } - - System.out.println("ShortSimdMinMax passed"); - } - - private static void expectEquals(short expected, short result) { - if (expected != result) { - throw new Error("Expected: " + expected + ", found: " + result); - } - } -} diff --git a/test/661-checker-simd-reduc/src/Main.java b/test/661-checker-simd-reduc/src/Main.java index fcd50a6d15..eff2018078 100644 --- a/test/661-checker-simd-reduc/src/Main.java +++ b/test/661-checker-simd-reduc/src/Main.java @@ -347,126 +347,6 @@ public class Main { return sum; } - private static byte reductionMinByte(byte[] x) { - byte min = Byte.MAX_VALUE; - for (int i = 0; i < x.length; i++) { - min = (byte) Math.min(min, x[i]); - } - return min; - } - - private static short reductionMinShort(short[] x) { - short min = Short.MAX_VALUE; - for (int i = 0; i < x.length; i++) { - min = (short) Math.min(min, x[i]); - } - return min; - } - - private static char reductionMinChar(char[] x) { - char min = Character.MAX_VALUE; - for (int i = 0; i < x.length; i++) { - min = (char) Math.min(min, x[i]); - } - return min; - } - - /// CHECK-START: int Main.reductionMinInt(int[]) loop_optimization (before) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 1 loop:none - /// CHECK-DAG: <> IntConstant 2147483647 loop:none - /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Return [<>] loop:none - // - /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionMinInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant {{2|4}} loop:none - /// CHECK-DAG: <> VecReplicateScalar [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecMin [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - private static int reductionMinInt(int[] x) { - int min = Integer.MAX_VALUE; - for (int i = 0; i < x.length; i++) { - min = Math.min(min, x[i]); - } - return min; - } - - private static long reductionMinLong(long[] x) { - long min = Long.MAX_VALUE; - for (int i = 0; i < x.length; i++) { - min = Math.min(min, x[i]); - } - return min; - } - - private static byte reductionMaxByte(byte[] x) { - byte max = Byte.MIN_VALUE; - for (int i = 0; i < x.length; i++) { - max = (byte) Math.max(max, x[i]); - } - return max; - } - - private static short reductionMaxShort(short[] x) { - short max = Short.MIN_VALUE; - for (int i = 0; i < x.length; i++) { - max = (short) Math.max(max, x[i]); - } - return max; - } - - private static char reductionMaxChar(char[] x) { - char max = Character.MIN_VALUE; - for (int i = 0; i < x.length; i++) { - max = (char) Math.max(max, x[i]); - } - return max; - } - - /// CHECK-START: int Main.reductionMaxInt(int[]) loop_optimization (before) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 1 loop:none - /// CHECK-DAG: <> IntConstant -2147483648 loop:none - /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: Max [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Return [<>] loop:none - // - /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionMaxInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant {{2|4}} loop:none - /// CHECK-DAG: <> VecReplicateScalar [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecMax [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - private static int reductionMaxInt(int[] x) { - int max = Integer.MIN_VALUE; - for (int i = 0; i < x.length; i++) { - max = Math.max(max, x[i]); - } - return max; - } - - private static long reductionMaxLong(long[] x) { - long max = Long.MIN_VALUE; - for (int i = 0; i < x.length; i++) { - max = Math.max(max, x[i]); - } - return max; - } - // // A few special cases. // @@ -491,24 +371,6 @@ public class Main { return sum; } - private static int reductionMinInt10(int[] x) { - int min = Integer.MAX_VALUE; - // Amenable to complete unrolling. - for (int i = 10; i <= 10; i++) { - min = Math.min(min, x[i]); - } - return min; - } - - private static int reductionMaxInt10(int[] x) { - int max = Integer.MIN_VALUE; - // Amenable to complete unrolling. - for (int i = 10; i <= 10; i++) { - max = Math.max(max, x[i]); - } - return max; - } - // // Main driver. // @@ -587,40 +449,10 @@ public class Main { expectEquals(27466, reductionMinusChar(xc)); expectEquals(-365750, reductionMinusInt(xi)); expectEquals(-365750L, reductionMinusLong(xl)); - expectEquals(-128, reductionMinByte(xb)); - expectEquals(-17, reductionMinShort(xs)); - expectEquals(1, reductionMinChar(xc)); - expectEquals(-17, reductionMinInt(xi)); - expectEquals(-17L, reductionMinLong(xl)); - expectEquals(3, reductionMinByte(xpb)); - expectEquals(3, reductionMinShort(xps)); - expectEquals(3, reductionMinChar(xpc)); - expectEquals(3, reductionMinInt(xpi)); - expectEquals(3L, reductionMinLong(xpl)); - expectEquals(-103, reductionMinByte(xnb)); - expectEquals(-103, reductionMinShort(xns)); - expectEquals(-103, reductionMinInt(xni)); - expectEquals(-103L, reductionMinLong(xnl)); - expectEquals(127, reductionMaxByte(xb)); - expectEquals(1480, reductionMaxShort(xs)); - expectEquals(65534, reductionMaxChar(xc)); - expectEquals(1480, reductionMaxInt(xi)); - expectEquals(1480L, reductionMaxLong(xl)); - expectEquals(102, reductionMaxByte(xpb)); - expectEquals(102, reductionMaxShort(xps)); - expectEquals(102, reductionMaxChar(xpc)); - expectEquals(102, reductionMaxInt(xpi)); - expectEquals(102L, reductionMaxLong(xpl)); - expectEquals(-4, reductionMaxByte(xnb)); - expectEquals(-4, reductionMaxShort(xns)); - expectEquals(-4, reductionMaxInt(xni)); - expectEquals(-4L, reductionMaxLong(xnl)); // Test special cases. expectEquals(13, reductionInt10(xi)); expectEquals(-13, reductionMinusInt10(xi)); - expectEquals(13, reductionMinInt10(xi)); - expectEquals(13, reductionMaxInt10(xi)); System.out.println("passed"); } diff --git a/test/678-checker-simd-saturation/build b/test/678-checker-simd-saturation/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/678-checker-simd-saturation/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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 -# -# 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/678-checker-simd-saturation/expected.txt b/test/678-checker-simd-saturation/expected.txt deleted file mode 100644 index b0aad4deb5..0000000000 --- a/test/678-checker-simd-saturation/expected.txt +++ /dev/null @@ -1 +0,0 @@ -passed diff --git a/test/678-checker-simd-saturation/info.txt b/test/678-checker-simd-saturation/info.txt deleted file mode 100644 index ab7a80241d..0000000000 --- a/test/678-checker-simd-saturation/info.txt +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 7a22ca175d..0000000000 --- a/test/678-checker-simd-saturation/src/Main.java +++ /dev/null @@ -1,784 +0,0 @@ -/* - * 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 { - - static final int $inline$p15() { - return 15; - } - - static final int $inline$m15() { - return -15; - } - - // - // Direct min-max. - // - - /// CHECK-START: void Main.satAddUByte(byte[], byte[], byte[]) loop_optimization (before) - /// CHECK-DAG: <> IntConstant 255 loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64}: void Main.satAddUByte(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] 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: <> IntConstant -128 loop:none - /// CHECK-DAG: <> IntConstant 127 loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64}: void Main.satAddSByte(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] 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: <> IntConstant 65535 loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64}: void Main.satAddUShort(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] 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: <> IntConstant -32768 loop:none - /// CHECK-DAG: <> IntConstant 32767 loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64}: void Main.satAddSShort(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] 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: <> IntConstant 0 loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64}: void Main.satSubUByte(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] 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: <> IntConstant -128 loop:none - /// CHECK-DAG: <> IntConstant 127 loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64}: void Main.satSubSByte(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] 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: <> IntConstant 0 loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64}: void Main.satSubUShort(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] 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: <> IntConstant -32768 loop:none - /// CHECK-DAG: <> IntConstant 32767 loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64}: void Main.satSubSShort(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] 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); - } - } - - // - // Single clipping signed 8-bit saturation. - // - - /// CHECK-START-{ARM,ARM64}: void Main.satAddPConstSByte(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void satAddPConstSByte(byte[] a, byte[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - b[i] = (byte) Math.min(a[i] + 15, 127); - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.satAddNConstSByte(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void satAddNConstSByte(byte[] a, byte[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - b[i] = (byte) Math.max(a[i] - 15, -128); - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.satSubPConstSByte(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void satSubPConstSByte(byte[] a, byte[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - b[i] = (byte) Math.min(15 - a[i], 127); - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.satSubNConstSByte(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void satSubNConstSByte(byte[] a, byte[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - b[i] = (byte) Math.max(-15 - a[i], -128); - } - } - - // - // Single clipping signed 16-bit saturation. - // - - /// CHECK-START-{ARM,ARM64}: void Main.satAddPConstSShort(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void satAddPConstSShort(short[] a, short[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - b[i] = (short) Math.min(a[i] + 15, 32767); - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.satAddNConstSShort(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void satAddNConstSShort(short[] a, short[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - b[i] = (short) Math.max(a[i] - 15, -32768); - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.satSubPConstSShort(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void satSubPConstSShort(short[] a, short[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - b[i] = (short) Math.min(15 - a[i], 32767); - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.satSubNConstSShort(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void satSubNConstSShort(short[] a, short[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - b[i] = (short) Math.max(-15 - a[i], -32768); - } - } - - // - // Alternatives 8-bit clipping. - // - - /// CHECK-START-{ARM,ARM64}: void Main.usatAddConst(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void usatAddConst(byte[] a, byte[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - b[i] = (byte) Math.min((a[i] & 0xff) + $inline$p15(), 255); - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.usatAddConstAlt(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void usatAddConstAlt(byte[] a, byte[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - b[i] = (byte) Math.min((a[i] & 0xff) - $inline$m15(), 255); - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.usatSubConst(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void usatSubConst(byte[] a, byte[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - b[i] = (byte) Math.max((a[i] & 0xff) - $inline$p15(), 0); - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.usatSubConstAlt(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void usatSubConstAlt(byte[] a, byte[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - b[i] = (byte) Math.max((a[i] & 0xff) + $inline$m15(), 0); - } - } - - // - // Alternatives 16-bit clipping. - // - - /// CHECK-START: void Main.satAlt1(short[], short[], short[]) loop_optimization (before) - /// CHECK-DAG: <> IntConstant -32768 loop:none - /// CHECK-DAG: <> IntConstant 32767 loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64}: void Main.satAlt1(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] 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; - } - } - - /// CHECK-START: void Main.satAlt2(short[], short[], short[]) loop_optimization (before) - /// CHECK-DAG: <> IntConstant -32768 loop:none - /// CHECK-DAG: <> IntConstant 32767 loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Max [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> Min [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-{ARM,ARM64}: void Main.satAlt2(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - 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; - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.satAlt3(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - 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; - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.usatAlt1(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void usatAlt1(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 t = (0xffff & a[i]) + (0xffff & b[i]); - c[i] = (short) (t <= 65535 ? t : 65535); - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.usatAlt2(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void usatAlt2(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 t = (a[i] & 0xffff) + (b[i] & 0xffff); - c[i] = (short) (t < 65535 ? t : 65535); - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.usatAlt3(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void usatAlt3(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 x = (a[i] & 0xffff); - int y = (b[i] & 0xffff); - int t = y + x ; - if (t >= 65535) t = 65535; - c[i] = (short) t; - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.usatAlt4(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void usatAlt4(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 x = (a[i] & 0xffff); - int y = (b[i] & 0xffff); - int t = y + x ; - if (t > 65535) t = 65535; - c[i] = (short) t; - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.satRedundantClip(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationAdd [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void satRedundantClip(short[] a, short[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - // Max clipping redundant. - b[i] = (short) Math.max(Math.min(a[i] + 15, 32767), -32768 + 15); - } - } - - /// CHECK-START: void Main.satNonRedundantClip(short[], short[]) loop_optimization (after) - /// CHECK-NOT: VecSaturationAdd - public static void satNonRedundantClip(short[] a, short[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - // Max clipping not redundant (one off). - b[i] = (short) Math.max(Math.min(a[i] + 15, 32767), -32768 + 16); - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.usatSubConst(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void usatSubConst(short[] a, short[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - int t = a[i] & 0xffff; - int s = t - $inline$p15(); - b[i] = (short)(s > 0 ? s : 0); - } - } - - /// CHECK-START-{ARM,ARM64}: void Main.usatSubConstAlt(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecReplicateScalar loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSaturationSub [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - public static void usatSubConstAlt(short[] a, short[] b) { - int n = Math.min(a.length, b.length); - for (int i = 0; i < n; i++) { - int t = a[i] & 0xffff; - int s = t + $inline$m15(); - b[i] = (short)(s > 0 ? s : 0); - } - } - - // - // 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]); - } - // Single clipping. - satAddPConstSByte(b1, out); - for (int i = 0; i < m; i++) { - byte e = (byte) Math.min(b1[i] + 15, 127); - expectEquals(e, out[i]); - } - satAddNConstSByte(b1, out); - for (int i = 0; i < m; i++) { - byte e = (byte) Math.max(b1[i] - 15, -128); - expectEquals(e, out[i]); - } - satSubPConstSByte(b1, out); - for (int i = 0; i < m; i++) { - byte e = (byte) Math.min(15 - b1[i], 127); - expectEquals(e, out[i]); - } - satSubNConstSByte(b1, out); - for (int i = 0; i < m; i++) { - byte e = (byte) Math.max(-15 - b1[i], -128); - expectEquals(e, out[i]); - } - // Alternatives. - usatAddConst(b1, out); - for (int i = 0; i < m; i++) { - byte e = (byte) Math.min((b1[i] & 0xff) + 15, 255); - expectEquals(e, out[i]); - } - usatAddConstAlt(b1, out); - for (int i = 0; i < m; i++) { - byte e = (byte) Math.min((b1[i] & 0xff) + 15, 255); - expectEquals(e, out[i]); - } - usatSubConst(b1, out); - for (int i = 0; i < m; i++) { - byte e = (byte) Math.max((b1[i] & 0xff) - 15, 0); - expectEquals(e, out[i]); - } - usatSubConstAlt(b1, out); - for (int i = 0; i < m; i++) { - byte e = (byte) Math.max((b1[i] & 0xff) - 15, 0); - 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]); - } - // Single clipping. - satAddPConstSShort(s1, out); - for (int i = 0; i < m; i++) { - short e = (short) Math.min(s1[i] + 15, 32767); - expectEquals(e, out[i]); - } - satAddNConstSShort(s1, out); - for (int i = 0; i < m; i++) { - short e = (short) Math.max(s1[i] - 15, -32768); - expectEquals(e, out[i]); - } - satSubPConstSShort(s1, out); - for (int i = 0; i < m; i++) { - short e = (short) Math.min(15 - s1[i], 32767); - expectEquals(e, out[i]); - } - satSubNConstSShort(s1, out); - for (int i = 0; i < m; i++) { - short e = (short) Math.max(-15 - s1[i], -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]); - } - usatAlt1(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]); - } - usatAlt2(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]); - } - usatAlt3(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]); - } - usatAlt4(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]); - } - satRedundantClip(s1, out); - for (int i = 0; i < m; i++) { - short e = (short) Math.min(s1[i] + 15, 32767); - expectEquals(e, out[i]); - } - satNonRedundantClip(s1, out); - for (int i = 0; i < m; i++) { - short e = (short) Math.max(Math.min(s1[i] + 15, 32767), -32752); - expectEquals(e, out[i]); - } - usatSubConst(s1, out); - for (int i = 0; i < m; i++) { - short e = (short) Math.max((s1[i] & 0xffff) - 15, 0); - expectEquals(e, out[i]); - } - usatSubConstAlt(s1, out); - for (int i = 0; i < m; i++) { - short e = (short) Math.max((s1[i] & 0xffff) - 15, 0); - 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); - } - } -} -- GitLab From d5f8de8bc61160bb2cecdcc0f5aa54d151b6176a Mon Sep 17 00:00:00 2001 From: David Sehr Date: Fri, 27 Apr 2018 14:12:03 -0700 Subject: [PATCH 321/749] Add CommonArtTest Add a test framework that does not start up a Runtime object. Bug: 78651010 Test: make -j 40 test-art-host Change-Id: I6c8af384af5fe1289c6cf137635e94934ac3795d --- build/Android.gtest.mk | 1 + libartbase/Android.bp | 23 +- libartbase/base/common_art_test.cc | 420 ++++++++++++++++++ libartbase/base/common_art_test.h | 234 ++++++++++ libartbase/base/mem_map_test.cc | 5 +- libartbase/base/scoped_flock_test.cc | 4 +- libartbase/base/unix_file/fd_file_test.cc | 2 +- .../base/unix_file/random_access_file_test.h | 6 +- libartbase/base/zip_archive_test.cc | 4 +- libdexfile/dex/type_lookup_table_test.cc | 6 +- runtime/Android.bp | 1 + runtime/common_runtime_test.cc | 361 +-------------- runtime/common_runtime_test.h | 121 +---- runtime/dex/art_dex_file_loader_test.cc | 2 +- test/Android.bp | 1 + 15 files changed, 696 insertions(+), 495 deletions(-) create mode 100644 libartbase/base/common_art_test.cc create mode 100644 libartbase/base/common_art_test.h diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index a4a72f4ccc..b46f6771ea 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -370,6 +370,7 @@ ART_TEST_MODULES := \ art_imgdiag_tests \ art_libartbase_tests \ art_libdexfile_tests \ + art_libprofile_tests \ art_oatdump_tests \ art_patchoat_tests \ art_profman_tests \ diff --git a/libartbase/Android.bp b/libartbase/Android.bp index 8cf4d0376d..50abdd36af 100644 --- a/libartbase/Android.bp +++ b/libartbase/Android.bp @@ -116,9 +116,26 @@ art_cc_library { export_shared_lib_headers: ["libbase"], } -// For now many of these tests still use CommonRuntimeTest, almost universally because of -// ScratchFile and related. -// TODO: Remove CommonRuntimeTest dependency from these tests. +art_cc_library { + name: "libartbase-art-gtest", + defaults: ["libart-gtest-defaults"], + srcs: [ + "base/common_art_test.cc", + ], + shared_libs: [ + "libartbased", + "libdexfiled", + "libbase", + "libbacktrace", + ], + header_libs: [ + "libnativehelper_header_only", + ], + include_dirs: [ + "external/icu/icu4c/source/common", + ], +} + art_cc_test { name: "art_libartbase_tests", defaults: [ diff --git a/libartbase/base/common_art_test.cc b/libartbase/base/common_art_test.cc new file mode 100644 index 0000000000..0d798f37f7 --- /dev/null +++ b/libartbase/base/common_art_test.cc @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2012 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 "common_art_test.h" + +#include +#include +#include +#include +#include +#include "nativehelper/scoped_local_ref.h" + +#include "android-base/stringprintf.h" +#include + +#include "art_field-inl.h" +#include "base/file_utils.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/mem_map.h" +#include "base/mutex.h" +#include "base/os.h" +#include "base/runtime_debug.h" +#include "base/stl_util.h" +#include "base/unix_file/fd_file.h" +#include "dex/art_dex_file_loader.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_loader.h" +#include "dex/primitive.h" +#include "gtest/gtest.h" + +namespace art { + +using android::base::StringPrintf; + +ScratchFile::ScratchFile() { + // ANDROID_DATA needs to be set + CHECK_NE(static_cast(nullptr), getenv("ANDROID_DATA")) << + "Are you subclassing RuntimeTest?"; + filename_ = getenv("ANDROID_DATA"); + filename_ += "/TmpFile-XXXXXX"; + int fd = mkstemp(&filename_[0]); + CHECK_NE(-1, fd) << strerror(errno) << " for " << filename_; + file_.reset(new File(fd, GetFilename(), true)); +} + +ScratchFile::ScratchFile(const ScratchFile& other, const char* suffix) + : ScratchFile(other.GetFilename() + suffix) {} + +ScratchFile::ScratchFile(const std::string& filename) : filename_(filename) { + int fd = open(filename_.c_str(), O_RDWR | O_CREAT, 0666); + CHECK_NE(-1, fd); + file_.reset(new File(fd, GetFilename(), true)); +} + +ScratchFile::ScratchFile(File* file) { + CHECK(file != nullptr); + filename_ = file->GetPath(); + file_.reset(file); +} + +ScratchFile::ScratchFile(ScratchFile&& other) { + *this = std::move(other); +} + +ScratchFile& ScratchFile::operator=(ScratchFile&& other) { + if (GetFile() != other.GetFile()) { + std::swap(filename_, other.filename_); + std::swap(file_, other.file_); + } + return *this; +} + +ScratchFile::~ScratchFile() { + Unlink(); +} + +int ScratchFile::GetFd() const { + return file_->Fd(); +} + +void ScratchFile::Close() { + if (file_.get() != nullptr) { + if (file_->FlushCloseOrErase() != 0) { + PLOG(WARNING) << "Error closing scratch file."; + } + } +} + +void ScratchFile::Unlink() { + if (!OS::FileExists(filename_.c_str())) { + return; + } + Close(); + int unlink_result = unlink(filename_.c_str()); + CHECK_EQ(0, unlink_result); +} + +void CommonArtTestImpl::SetUpAndroidRoot() { + if (IsHost()) { + // $ANDROID_ROOT is set on the device, but not necessarily on the host. + // But it needs to be set so that icu4c can find its locale data. + const char* android_root_from_env = getenv("ANDROID_ROOT"); + if (android_root_from_env == nullptr) { + // Use ANDROID_HOST_OUT for ANDROID_ROOT if it is set. + const char* android_host_out = getenv("ANDROID_HOST_OUT"); + if (android_host_out != nullptr) { + setenv("ANDROID_ROOT", android_host_out, 1); + } else { + // Build it from ANDROID_BUILD_TOP or cwd + std::string root; + const char* android_build_top = getenv("ANDROID_BUILD_TOP"); + if (android_build_top != nullptr) { + root += android_build_top; + } else { + // Not set by build server, so default to current directory + char* cwd = getcwd(nullptr, 0); + setenv("ANDROID_BUILD_TOP", cwd, 1); + root += cwd; + free(cwd); + } +#if defined(__linux__) + root += "/out/host/linux-x86"; +#elif defined(__APPLE__) + root += "/out/host/darwin-x86"; +#else +#error unsupported OS +#endif + setenv("ANDROID_ROOT", root.c_str(), 1); + } + } + setenv("LD_LIBRARY_PATH", ":", 0); // Required by java.lang.System.. + + // Not set by build server, so default + if (getenv("ANDROID_HOST_OUT") == nullptr) { + setenv("ANDROID_HOST_OUT", getenv("ANDROID_ROOT"), 1); + } + } +} + +void CommonArtTestImpl::SetUpAndroidData(std::string& android_data) { + // On target, Cannot use /mnt/sdcard because it is mounted noexec, so use subdir of dalvik-cache + if (IsHost()) { + const char* tmpdir = getenv("TMPDIR"); + if (tmpdir != nullptr && tmpdir[0] != 0) { + android_data = tmpdir; + } else { + android_data = "/tmp"; + } + } else { + android_data = "/data/dalvik-cache"; + } + android_data += "/art-data-XXXXXX"; + if (mkdtemp(&android_data[0]) == nullptr) { + PLOG(FATAL) << "mkdtemp(\"" << &android_data[0] << "\") failed"; + } + setenv("ANDROID_DATA", android_data.c_str(), 1); +} + +void CommonArtTestImpl::SetUp() { + SetUpAndroidRoot(); + SetUpAndroidData(android_data_); + dalvik_cache_.append(android_data_.c_str()); + dalvik_cache_.append("/dalvik-cache"); + int mkdir_result = mkdir(dalvik_cache_.c_str(), 0700); + ASSERT_EQ(mkdir_result, 0); +} + +void CommonArtTestImpl::TearDownAndroidData(const std::string& android_data, bool fail_on_error) { + if (fail_on_error) { + ASSERT_EQ(rmdir(android_data.c_str()), 0); + } else { + rmdir(android_data.c_str()); + } +} + +// Helper - find directory with the following format: +// ${ANDROID_BUILD_TOP}/${subdir1}/${subdir2}-${version}/${subdir3}/bin/ +std::string CommonArtTestImpl::GetAndroidToolsDir(const std::string& subdir1, + const std::string& subdir2, + const std::string& subdir3) { + std::string root; + const char* android_build_top = getenv("ANDROID_BUILD_TOP"); + if (android_build_top != nullptr) { + root = android_build_top; + } else { + // Not set by build server, so default to current directory + char* cwd = getcwd(nullptr, 0); + setenv("ANDROID_BUILD_TOP", cwd, 1); + root = cwd; + free(cwd); + } + + std::string toolsdir = root + "/" + subdir1; + std::string founddir; + DIR* dir; + if ((dir = opendir(toolsdir.c_str())) != nullptr) { + float maxversion = 0; + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + std::string format = subdir2 + "-%f"; + float version; + if (std::sscanf(entry->d_name, format.c_str(), &version) == 1) { + if (version > maxversion) { + maxversion = version; + founddir = toolsdir + "/" + entry->d_name + "/" + subdir3 + "/bin/"; + } + } + } + closedir(dir); + } + + if (founddir.empty()) { + ADD_FAILURE() << "Cannot find Android tools directory."; + } + return founddir; +} + +std::string CommonArtTestImpl::GetAndroidHostToolsDir() { + return GetAndroidToolsDir("prebuilts/gcc/linux-x86/host", + "x86_64-linux-glibc2.15", + "x86_64-linux"); +} + +std::string CommonArtTestImpl::GetCoreArtLocation() { + return GetCoreFileLocation("art"); +} + +std::string CommonArtTestImpl::GetCoreOatLocation() { + return GetCoreFileLocation("oat"); +} + +std::unique_ptr CommonArtTestImpl::LoadExpectSingleDexFile(const char* location) { + std::vector> dex_files; + std::string error_msg; + MemMap::Init(); + static constexpr bool kVerifyChecksum = true; + const ArtDexFileLoader dex_file_loader; + if (!dex_file_loader.Open( + location, location, /* verify */ true, kVerifyChecksum, &error_msg, &dex_files)) { + LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n"; + UNREACHABLE(); + } else { + CHECK_EQ(1U, dex_files.size()) << "Expected only one dex file in " << location; + return std::move(dex_files[0]); + } +} + +void CommonArtTestImpl::ClearDirectory(const char* dirpath, bool recursive) { + ASSERT_TRUE(dirpath != nullptr); + DIR* dir = opendir(dirpath); + ASSERT_TRUE(dir != nullptr); + dirent* e; + struct stat s; + while ((e = readdir(dir)) != nullptr) { + if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0)) { + continue; + } + std::string filename(dirpath); + filename.push_back('/'); + filename.append(e->d_name); + int stat_result = lstat(filename.c_str(), &s); + ASSERT_EQ(0, stat_result) << "unable to stat " << filename; + if (S_ISDIR(s.st_mode)) { + if (recursive) { + ClearDirectory(filename.c_str()); + int rmdir_result = rmdir(filename.c_str()); + ASSERT_EQ(0, rmdir_result) << filename; + } + } else { + int unlink_result = unlink(filename.c_str()); + ASSERT_EQ(0, unlink_result) << filename; + } + } + closedir(dir); +} + +void CommonArtTestImpl::TearDown() { + const char* android_data = getenv("ANDROID_DATA"); + ASSERT_TRUE(android_data != nullptr); + ClearDirectory(dalvik_cache_.c_str()); + int rmdir_cache_result = rmdir(dalvik_cache_.c_str()); + ASSERT_EQ(0, rmdir_cache_result); + TearDownAndroidData(android_data_, true); + dalvik_cache_.clear(); +} + +static std::string GetDexFileName(const std::string& jar_prefix, bool host) { + std::string path; + if (host) { + const char* host_dir = getenv("ANDROID_HOST_OUT"); + CHECK(host_dir != nullptr); + path = host_dir; + } else { + path = GetAndroidRoot(); + } + + std::string suffix = host + ? "-hostdex" // The host version. + : "-testdex"; // The unstripped target version. + + return StringPrintf("%s/framework/%s%s.jar", path.c_str(), jar_prefix.c_str(), suffix.c_str()); +} + +std::vector CommonArtTestImpl::GetLibCoreDexFileNames() { + return std::vector({GetDexFileName("core-oj", IsHost()), + GetDexFileName("core-libart", IsHost())}); +} + +std::string CommonArtTestImpl::GetTestAndroidRoot() { + if (IsHost()) { + const char* host_dir = getenv("ANDROID_HOST_OUT"); + CHECK(host_dir != nullptr); + return host_dir; + } + return GetAndroidRoot(); +} + +// Check that for target builds we have ART_TARGET_NATIVETEST_DIR set. +#ifdef ART_TARGET +#ifndef ART_TARGET_NATIVETEST_DIR +#error "ART_TARGET_NATIVETEST_DIR not set." +#endif +// Wrap it as a string literal. +#define ART_TARGET_NATIVETEST_DIR_STRING STRINGIFY(ART_TARGET_NATIVETEST_DIR) "/" +#else +#define ART_TARGET_NATIVETEST_DIR_STRING "" +#endif + +std::string CommonArtTestImpl::GetTestDexFileName(const char* name) const { + CHECK(name != nullptr); + std::string filename; + if (IsHost()) { + filename += getenv("ANDROID_HOST_OUT"); + filename += "/framework/"; + } else { + filename += ART_TARGET_NATIVETEST_DIR_STRING; + } + filename += "art-gtest-"; + filename += name; + filename += ".jar"; + return filename; +} + +std::vector> CommonArtTestImpl::OpenTestDexFiles(const char* name) { + std::string filename = GetTestDexFileName(name); + static constexpr bool kVerifyChecksum = true; + std::string error_msg; + const ArtDexFileLoader dex_file_loader; + std::vector> dex_files; + bool success = dex_file_loader.Open(filename.c_str(), + filename.c_str(), + /* verify */ true, + kVerifyChecksum, + &error_msg, &dex_files); + CHECK(success) << "Failed to open '" << filename << "': " << error_msg; + for (auto& dex_file : dex_files) { + CHECK_EQ(PROT_READ, dex_file->GetPermissions()); + CHECK(dex_file->IsReadOnly()); + } + return dex_files; +} + +std::unique_ptr CommonArtTestImpl::OpenTestDexFile(const char* name) { + std::vector> vector = OpenTestDexFiles(name); + EXPECT_EQ(1U, vector.size()); + return std::move(vector[0]); +} + +std::string CommonArtTestImpl::GetCoreFileLocation(const char* suffix) { + CHECK(suffix != nullptr); + + std::string location; + if (IsHost()) { + const char* host_dir = getenv("ANDROID_HOST_OUT"); + CHECK(host_dir != nullptr); + location = StringPrintf("%s/framework/core.%s", host_dir, suffix); + } else { + location = StringPrintf("/data/art-test/core.%s", suffix); + } + + return location; +} + +std::string CommonArtTestImpl::CreateClassPath( + const std::vector>& dex_files) { + CHECK(!dex_files.empty()); + std::string classpath = dex_files[0]->GetLocation(); + for (size_t i = 1; i < dex_files.size(); i++) { + classpath += ":" + dex_files[i]->GetLocation(); + } + return classpath; +} + +std::string CommonArtTestImpl::CreateClassPathWithChecksums( + const std::vector>& dex_files) { + CHECK(!dex_files.empty()); + std::string classpath = dex_files[0]->GetLocation() + "*" + + std::to_string(dex_files[0]->GetLocationChecksum()); + for (size_t i = 1; i < dex_files.size(); i++) { + classpath += ":" + dex_files[i]->GetLocation() + "*" + + std::to_string(dex_files[i]->GetLocationChecksum()); + } + return classpath; +} + +} // namespace art diff --git a/libartbase/base/common_art_test.h b/libartbase/base/common_art_test.h new file mode 100644 index 0000000000..a4764c275d --- /dev/null +++ b/libartbase/base/common_art_test.h @@ -0,0 +1,234 @@ +/* + * 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. + */ + +#ifndef ART_LIBARTBASE_BASE_COMMON_ART_TEST_H_ +#define ART_LIBARTBASE_BASE_COMMON_ART_TEST_H_ + +#include + +#include + +#include + +#include "base/globals.h" +#include "base/mutex.h" +#include "base/os.h" +#include "base/unix_file/fd_file.h" +#include "dex/art_dex_file_loader.h" +#include "dex/compact_dex_level.h" + +namespace art { + +using LogSeverity = android::base::LogSeverity; +using ScopedLogSeverity = android::base::ScopedLogSeverity; + +// OBJ pointer helpers to avoid needing .Decode everywhere. +#define EXPECT_OBJ_PTR_EQ(a, b) EXPECT_EQ(MakeObjPtr(a).Ptr(), MakeObjPtr(b).Ptr()); +#define ASSERT_OBJ_PTR_EQ(a, b) ASSERT_EQ(MakeObjPtr(a).Ptr(), MakeObjPtr(b).Ptr()); +#define EXPECT_OBJ_PTR_NE(a, b) EXPECT_NE(MakeObjPtr(a).Ptr(), MakeObjPtr(b).Ptr()); +#define ASSERT_OBJ_PTR_NE(a, b) ASSERT_NE(MakeObjPtr(a).Ptr(), MakeObjPtr(b).Ptr()); + +class DexFile; + +class ScratchFile { + public: + ScratchFile(); + + explicit ScratchFile(const std::string& filename); + + ScratchFile(const ScratchFile& other, const char* suffix); + + ScratchFile(ScratchFile&& other); + + ScratchFile& operator=(ScratchFile&& other); + + explicit ScratchFile(File* file); + + ~ScratchFile(); + + const std::string& GetFilename() const { + return filename_; + } + + File* GetFile() const { + return file_.get(); + } + + int GetFd() const; + + void Close(); + void Unlink(); + + private: + std::string filename_; + std::unique_ptr file_; +}; + +class CommonArtTestImpl { + public: + CommonArtTestImpl() = default; + virtual ~CommonArtTestImpl() = default; + + static void SetUpAndroidRoot(); + + // Note: setting up ANDROID_DATA may create a temporary directory. If this is used in a + // non-derived class, be sure to also call the corresponding tear-down below. + static void SetUpAndroidData(std::string& android_data); + + static void TearDownAndroidData(const std::string& android_data, bool fail_on_error); + + // Gets the paths of the libcore dex files. + static std::vector GetLibCoreDexFileNames(); + + // Returns bin directory which contains host's prebuild tools. + static std::string GetAndroidHostToolsDir(); + + // Retuerns the filename for a test dex (i.e. XandY or ManyMethods). + std::string GetTestDexFileName(const char* name) const; + + template + bool MutateDexFile(File* output_dex, const std::string& input_jar, const Mutator& mutator) { + std::vector> dex_files; + std::string error_msg; + const ArtDexFileLoader dex_file_loader; + CHECK(dex_file_loader.Open(input_jar.c_str(), + input_jar.c_str(), + /*verify*/ true, + /*verify_checksum*/ true, + &error_msg, + &dex_files)) << error_msg; + EXPECT_EQ(dex_files.size(), 1u) << "Only one input dex is supported"; + const std::unique_ptr& dex = dex_files[0]; + CHECK(dex->EnableWrite()) << "Failed to enable write"; + DexFile* dex_file = const_cast(dex.get()); + mutator(dex_file); + const_cast(dex_file->GetHeader()).checksum_ = dex_file->CalculateChecksum(); + if (!output_dex->WriteFully(dex->Begin(), dex->Size())) { + return false; + } + if (output_dex->Flush() != 0) { + PLOG(FATAL) << "Could not flush the output file."; + } + return true; + } + + protected: + static bool IsHost() { + return !kIsTargetBuild; + } + + // Helper - find directory with the following format: + // ${ANDROID_BUILD_TOP}/${subdir1}/${subdir2}-${version}/${subdir3}/bin/ + static std::string GetAndroidToolsDir(const std::string& subdir1, + const std::string& subdir2, + const std::string& subdir3); + + // File location to core.art, e.g. $ANDROID_HOST_OUT/system/framework/core.art + static std::string GetCoreArtLocation(); + + // File location to core.oat, e.g. $ANDROID_HOST_OUT/system/framework/core.oat + static std::string GetCoreOatLocation(); + + std::unique_ptr LoadExpectSingleDexFile(const char* location); + + void ClearDirectory(const char* dirpath, bool recursive = true); + + std::string GetTestAndroidRoot(); + + std::vector> OpenTestDexFiles(const char* name); + + std::unique_ptr OpenTestDexFile(const char* name); + + + std::string android_data_; + std::string dalvik_cache_; + + virtual void SetUp(); + + virtual void TearDown(); + + // Creates the class path string for the given dex files (the list of dex file locations + // separated by ':'). + std::string CreateClassPath(const std::vector>& dex_files); + // Same as CreateClassPath but add the dex file checksum after each location. The separator + // is '*'. + std::string CreateClassPathWithChecksums( + const std::vector>& dex_files); + + static std::string GetCoreFileLocation(const char* suffix); + + std::vector> loaded_dex_files_; +}; + +template +class CommonArtTestBase : public TestType, public CommonArtTestImpl { + public: + CommonArtTestBase() {} + virtual ~CommonArtTestBase() {} + + protected: + virtual void SetUp() OVERRIDE { + CommonArtTestImpl::SetUp(); + } + + virtual void TearDown() OVERRIDE { + CommonArtTestImpl::TearDown(); + } +}; + +using CommonArtTest = CommonArtTestBase; + +template +using CommonArtTestWithParam = CommonArtTestBase>; + +#define TEST_DISABLED_FOR_TARGET() \ + if (kIsTargetBuild) { \ + printf("WARNING: TEST DISABLED FOR TARGET\n"); \ + return; \ + } + +#define TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS() \ + if (!kHostStaticBuildEnabled) { \ + printf("WARNING: TEST DISABLED FOR NON-STATIC HOST BUILDS\n"); \ + return; \ + } + +#define TEST_DISABLED_FOR_MEMORY_TOOL() \ + if (RUNNING_ON_MEMORY_TOOL > 0) { \ + printf("WARNING: TEST DISABLED FOR MEMORY TOOL\n"); \ + return; \ + } + +#define TEST_DISABLED_FOR_MEMORY_TOOL_VALGRIND() \ + if (RUNNING_ON_MEMORY_TOOL > 0 && kMemoryToolIsValgrind) { \ + printf("WARNING: TEST DISABLED FOR MEMORY TOOL VALGRIND\n"); \ + return; \ + } + +#define TEST_DISABLED_FOR_MEMORY_TOOL_ASAN() \ + if (RUNNING_ON_MEMORY_TOOL > 0 && !kMemoryToolIsValgrind) { \ + printf("WARNING: TEST DISABLED FOR MEMORY TOOL ASAN\n"); \ + return; \ + } + +#define TEST_DISABLED_FOR_HEAP_POISONING() \ + if (kPoisonHeapReferences) { \ + printf("WARNING: TEST DISABLED FOR HEAP POISONING\n"); \ + return; \ + } +} // namespace art + +#endif // ART_LIBARTBASE_BASE_COMMON_ART_TEST_H_ diff --git a/libartbase/base/mem_map_test.cc b/libartbase/base/mem_map_test.cc index 4408f5502a..d956126df1 100644 --- a/libartbase/base/mem_map_test.cc +++ b/libartbase/base/mem_map_test.cc @@ -21,13 +21,14 @@ #include #include -#include "common_runtime_test.h" +#include "base/common_art_test.h" +#include "common_runtime_test.h" // For TEST_DISABLED_FOR_MIPS #include "memory_tool.h" #include "unix_file/fd_file.h" namespace art { -class MemMapTest : public CommonRuntimeTest { +class MemMapTest : public CommonArtTest { public: static uint8_t* BaseBegin(MemMap* mem_map) { return reinterpret_cast(mem_map->base_begin_); diff --git a/libartbase/base/scoped_flock_test.cc b/libartbase/base/scoped_flock_test.cc index 1b6caaf747..f9ac1e0230 100644 --- a/libartbase/base/scoped_flock_test.cc +++ b/libartbase/base/scoped_flock_test.cc @@ -16,11 +16,11 @@ #include "scoped_flock.h" -#include "common_runtime_test.h" +#include "base/common_art_test.h" namespace art { -class ScopedFlockTest : public CommonRuntimeTest {}; +class ScopedFlockTest : public CommonArtTest {}; TEST_F(ScopedFlockTest, TestLocking) { ScratchFile scratch_file; diff --git a/libartbase/base/unix_file/fd_file_test.cc b/libartbase/base/unix_file/fd_file_test.cc index 7fc057b9e8..1f731a7a7b 100644 --- a/libartbase/base/unix_file/fd_file_test.cc +++ b/libartbase/base/unix_file/fd_file_test.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "common_runtime_test.h" // For ScratchFile +#include "base/common_art_test.h" // For ScratchFile #include "gtest/gtest.h" #include "fd_file.h" #include "random_access_file_test.h" diff --git a/libartbase/base/unix_file/random_access_file_test.h b/libartbase/base/unix_file/random_access_file_test.h index 1de5f7b72c..dbe6ca948f 100644 --- a/libartbase/base/unix_file/random_access_file_test.h +++ b/libartbase/base/unix_file/random_access_file_test.h @@ -21,7 +21,7 @@ #include #include -#include "common_runtime_test.h" +#include "base/common_art_test.h" namespace unix_file { @@ -35,11 +35,11 @@ class RandomAccessFileTest : public testing::Test { virtual RandomAccessFile* MakeTestFile() = 0; virtual void SetUp() { - art::CommonRuntimeTest::SetUpAndroidData(android_data_); + art::CommonArtTest::SetUpAndroidData(android_data_); } virtual void TearDown() { - art::CommonRuntimeTest::TearDownAndroidData(android_data_, true); + art::CommonArtTest::TearDownAndroidData(android_data_, true); } std::string GetTmpPath(const std::string& name) { diff --git a/libartbase/base/zip_archive_test.cc b/libartbase/base/zip_archive_test.cc index 63743f754c..b99a471e04 100644 --- a/libartbase/base/zip_archive_test.cc +++ b/libartbase/base/zip_archive_test.cc @@ -22,13 +22,13 @@ #include #include -#include "common_runtime_test.h" +#include "base/common_art_test.h" #include "os.h" #include "unix_file/fd_file.h" namespace art { -class ZipArchiveTest : public CommonRuntimeTest {}; +class ZipArchiveTest : public CommonArtTest {}; TEST_F(ZipArchiveTest, FindAndExtract) { std::string error_msg; diff --git a/libdexfile/dex/type_lookup_table_test.cc b/libdexfile/dex/type_lookup_table_test.cc index b6ab6da78c..6c3d291332 100644 --- a/libdexfile/dex/type_lookup_table_test.cc +++ b/libdexfile/dex/type_lookup_table_test.cc @@ -18,7 +18,7 @@ #include -#include "common_runtime_test.h" +#include "base/common_art_test.h" #include "dex/dex_file-inl.h" #include "dex/utf-inl.h" #include "scoped_thread_state_change-inl.h" @@ -26,10 +26,9 @@ namespace art { using DescriptorClassDefIdxPair = std::pair; -class TypeLookupTableTest : public CommonRuntimeTestWithParam {}; +class TypeLookupTableTest : public CommonArtTestWithParam {}; TEST_F(TypeLookupTableTest, CreateLookupTable) { - ScopedObjectAccess soa(Thread::Current()); std::unique_ptr dex_file(OpenTestDexFile("Lookup")); std::unique_ptr table(TypeLookupTable::Create(*dex_file)); ASSERT_NE(nullptr, table.get()); @@ -38,7 +37,6 @@ TEST_F(TypeLookupTableTest, CreateLookupTable) { } TEST_P(TypeLookupTableTest, Find) { - ScopedObjectAccess soa(Thread::Current()); std::unique_ptr dex_file(OpenTestDexFile("Lookup")); std::unique_ptr table(TypeLookupTable::Create(*dex_file)); ASSERT_NE(nullptr, table.get()); diff --git a/runtime/Android.bp b/runtime/Android.bp index f39562313d..28bee3d67e 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -511,6 +511,7 @@ art_cc_library { ], shared_libs: [ "libartd", + "libartbase-art-gtest", "libbase", "libbacktrace", ], diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index f6a5efc547..f5b15ec239 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -67,6 +67,7 @@ int main(int argc, char **argv) { art::Locks::Init(); art::InitLogging(argv, art::Runtime::Abort); + art::MemMap::Init(); LOG(INFO) << "Running main() from common_runtime_test.cc..."; testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); @@ -76,69 +77,6 @@ namespace art { using android::base::StringPrintf; -ScratchFile::ScratchFile() { - // ANDROID_DATA needs to be set - CHECK_NE(static_cast(nullptr), getenv("ANDROID_DATA")) << - "Are you subclassing RuntimeTest?"; - filename_ = getenv("ANDROID_DATA"); - filename_ += "/TmpFile-XXXXXX"; - int fd = mkstemp(&filename_[0]); - CHECK_NE(-1, fd) << strerror(errno) << " for " << filename_; - file_.reset(new File(fd, GetFilename(), true)); -} - -ScratchFile::ScratchFile(const ScratchFile& other, const char* suffix) - : ScratchFile(other.GetFilename() + suffix) {} - -ScratchFile::ScratchFile(const std::string& filename) : filename_(filename) { - int fd = open(filename_.c_str(), O_RDWR | O_CREAT, 0666); - CHECK_NE(-1, fd); - file_.reset(new File(fd, GetFilename(), true)); -} - -ScratchFile::ScratchFile(File* file) { - CHECK(file != nullptr); - filename_ = file->GetPath(); - file_.reset(file); -} - -ScratchFile::ScratchFile(ScratchFile&& other) { - *this = std::move(other); -} - -ScratchFile& ScratchFile::operator=(ScratchFile&& other) { - if (GetFile() != other.GetFile()) { - std::swap(filename_, other.filename_); - std::swap(file_, other.file_); - } - return *this; -} - -ScratchFile::~ScratchFile() { - Unlink(); -} - -int ScratchFile::GetFd() const { - return file_->Fd(); -} - -void ScratchFile::Close() { - if (file_.get() != nullptr) { - if (file_->FlushCloseOrErase() != 0) { - PLOG(WARNING) << "Error closing scratch file."; - } - } -} - -void ScratchFile::Unlink() { - if (!OS::FileExists(filename_.c_str())) { - return; - } - Close(); - int unlink_result = unlink(filename_.c_str()); - CHECK_EQ(0, unlink_result); -} - static bool unstarted_initialized_ = false; CommonRuntimeTestImpl::CommonRuntimeTestImpl() @@ -151,124 +89,6 @@ CommonRuntimeTestImpl::~CommonRuntimeTestImpl() { runtime_.reset(); } -void CommonRuntimeTestImpl::SetUpAndroidRoot() { - if (IsHost()) { - // $ANDROID_ROOT is set on the device, but not necessarily on the host. - // But it needs to be set so that icu4c can find its locale data. - const char* android_root_from_env = getenv("ANDROID_ROOT"); - if (android_root_from_env == nullptr) { - // Use ANDROID_HOST_OUT for ANDROID_ROOT if it is set. - const char* android_host_out = getenv("ANDROID_HOST_OUT"); - if (android_host_out != nullptr) { - setenv("ANDROID_ROOT", android_host_out, 1); - } else { - // Build it from ANDROID_BUILD_TOP or cwd - std::string root; - const char* android_build_top = getenv("ANDROID_BUILD_TOP"); - if (android_build_top != nullptr) { - root += android_build_top; - } else { - // Not set by build server, so default to current directory - char* cwd = getcwd(nullptr, 0); - setenv("ANDROID_BUILD_TOP", cwd, 1); - root += cwd; - free(cwd); - } -#if defined(__linux__) - root += "/out/host/linux-x86"; -#elif defined(__APPLE__) - root += "/out/host/darwin-x86"; -#else -#error unsupported OS -#endif - setenv("ANDROID_ROOT", root.c_str(), 1); - } - } - setenv("LD_LIBRARY_PATH", ":", 0); // Required by java.lang.System.. - - // Not set by build server, so default - if (getenv("ANDROID_HOST_OUT") == nullptr) { - setenv("ANDROID_HOST_OUT", getenv("ANDROID_ROOT"), 1); - } - } -} - -void CommonRuntimeTestImpl::SetUpAndroidData(std::string& android_data) { - // On target, Cannot use /mnt/sdcard because it is mounted noexec, so use subdir of dalvik-cache - if (IsHost()) { - const char* tmpdir = getenv("TMPDIR"); - if (tmpdir != nullptr && tmpdir[0] != 0) { - android_data = tmpdir; - } else { - android_data = "/tmp"; - } - } else { - android_data = "/data/dalvik-cache"; - } - android_data += "/art-data-XXXXXX"; - if (mkdtemp(&android_data[0]) == nullptr) { - PLOG(FATAL) << "mkdtemp(\"" << &android_data[0] << "\") failed"; - } - setenv("ANDROID_DATA", android_data.c_str(), 1); -} - -void CommonRuntimeTestImpl::TearDownAndroidData(const std::string& android_data, - bool fail_on_error) { - if (fail_on_error) { - ASSERT_EQ(rmdir(android_data.c_str()), 0); - } else { - rmdir(android_data.c_str()); - } -} - -// Helper - find directory with the following format: -// ${ANDROID_BUILD_TOP}/${subdir1}/${subdir2}-${version}/${subdir3}/bin/ -static std::string GetAndroidToolsDir(const std::string& subdir1, - const std::string& subdir2, - const std::string& subdir3) { - std::string root; - const char* android_build_top = getenv("ANDROID_BUILD_TOP"); - if (android_build_top != nullptr) { - root = android_build_top; - } else { - // Not set by build server, so default to current directory - char* cwd = getcwd(nullptr, 0); - setenv("ANDROID_BUILD_TOP", cwd, 1); - root = cwd; - free(cwd); - } - - std::string toolsdir = root + "/" + subdir1; - std::string founddir; - DIR* dir; - if ((dir = opendir(toolsdir.c_str())) != nullptr) { - float maxversion = 0; - struct dirent* entry; - while ((entry = readdir(dir)) != nullptr) { - std::string format = subdir2 + "-%f"; - float version; - if (std::sscanf(entry->d_name, format.c_str(), &version) == 1) { - if (version > maxversion) { - maxversion = version; - founddir = toolsdir + "/" + entry->d_name + "/" + subdir3 + "/bin/"; - } - } - } - closedir(dir); - } - - if (founddir.empty()) { - ADD_FAILURE() << "Cannot find Android tools directory."; - } - return founddir; -} - -std::string CommonRuntimeTestImpl::GetAndroidHostToolsDir() { - return GetAndroidToolsDir("prebuilts/gcc/linux-x86/host", - "x86_64-linux-glibc2.15", - "x86_64-linux"); -} - std::string CommonRuntimeTestImpl::GetAndroidTargetToolsDir(InstructionSet isa) { switch (isa) { case InstructionSet::kArm: @@ -297,38 +117,8 @@ std::string CommonRuntimeTestImpl::GetAndroidTargetToolsDir(InstructionSet isa) return ""; } -std::string CommonRuntimeTestImpl::GetCoreArtLocation() { - return GetCoreFileLocation("art"); -} - -std::string CommonRuntimeTestImpl::GetCoreOatLocation() { - return GetCoreFileLocation("oat"); -} - -std::unique_ptr CommonRuntimeTestImpl::LoadExpectSingleDexFile( - const char* location) { - std::vector> dex_files; - std::string error_msg; - MemMap::Init(); - static constexpr bool kVerifyChecksum = true; - const ArtDexFileLoader dex_file_loader; - if (!dex_file_loader.Open( - location, location, /* verify */ true, kVerifyChecksum, &error_msg, &dex_files)) { - LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n"; - UNREACHABLE(); - } else { - CHECK_EQ(1U, dex_files.size()) << "Expected only one dex file in " << location; - return std::move(dex_files[0]); - } -} - void CommonRuntimeTestImpl::SetUp() { - SetUpAndroidRoot(); - SetUpAndroidData(android_data_); - dalvik_cache_.append(android_data_.c_str()); - dalvik_cache_.append("/dalvik-cache"); - int mkdir_result = mkdir(dalvik_cache_.c_str(), 0700); - ASSERT_EQ(mkdir_result, 0); + CommonArtTestImpl::SetUp(); std::string min_heap_string(StringPrintf("-Xms%zdm", gc::Heap::kDefaultInitialSize / MB)); std::string max_heap_string(StringPrintf("-Xmx%zdm", gc::Heap::kDefaultMaximumSize / MB)); @@ -406,80 +196,13 @@ void CommonRuntimeTestImpl::FinalizeSetup() { runtime_->GetHeap()->SetMinIntervalHomogeneousSpaceCompactionByOom(0U); } -void CommonRuntimeTestImpl::ClearDirectory(const char* dirpath, bool recursive) { - ASSERT_TRUE(dirpath != nullptr); - DIR* dir = opendir(dirpath); - ASSERT_TRUE(dir != nullptr); - dirent* e; - struct stat s; - while ((e = readdir(dir)) != nullptr) { - if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0)) { - continue; - } - std::string filename(dirpath); - filename.push_back('/'); - filename.append(e->d_name); - int stat_result = lstat(filename.c_str(), &s); - ASSERT_EQ(0, stat_result) << "unable to stat " << filename; - if (S_ISDIR(s.st_mode)) { - if (recursive) { - ClearDirectory(filename.c_str()); - int rmdir_result = rmdir(filename.c_str()); - ASSERT_EQ(0, rmdir_result) << filename; - } - } else { - int unlink_result = unlink(filename.c_str()); - ASSERT_EQ(0, unlink_result) << filename; - } - } - closedir(dir); -} - void CommonRuntimeTestImpl::TearDown() { - const char* android_data = getenv("ANDROID_DATA"); - ASSERT_TRUE(android_data != nullptr); - ClearDirectory(dalvik_cache_.c_str()); - int rmdir_cache_result = rmdir(dalvik_cache_.c_str()); - ASSERT_EQ(0, rmdir_cache_result); - TearDownAndroidData(android_data_, true); - dalvik_cache_.clear(); - + CommonArtTestImpl::TearDown(); if (runtime_ != nullptr) { runtime_->GetHeap()->VerifyHeap(); // Check for heap corruption after the test } } -static std::string GetDexFileName(const std::string& jar_prefix, bool host) { - std::string path; - if (host) { - const char* host_dir = getenv("ANDROID_HOST_OUT"); - CHECK(host_dir != nullptr); - path = host_dir; - } else { - path = GetAndroidRoot(); - } - - std::string suffix = host - ? "-hostdex" // The host version. - : "-testdex"; // The unstripped target version. - - return StringPrintf("%s/framework/%s%s.jar", path.c_str(), jar_prefix.c_str(), suffix.c_str()); -} - -std::vector CommonRuntimeTestImpl::GetLibCoreDexFileNames() { - return std::vector({GetDexFileName("core-oj", IsHost()), - GetDexFileName("core-libart", IsHost())}); -} - -std::string CommonRuntimeTestImpl::GetTestAndroidRoot() { - if (IsHost()) { - const char* host_dir = getenv("ANDROID_HOST_OUT"); - CHECK(host_dir != nullptr); - return host_dir; - } - return GetAndroidRoot(); -} - // Check that for target builds we have ART_TARGET_NATIVETEST_DIR set. #ifdef ART_TARGET #ifndef ART_TARGET_NATIVETEST_DIR @@ -491,47 +214,6 @@ std::string CommonRuntimeTestImpl::GetTestAndroidRoot() { #define ART_TARGET_NATIVETEST_DIR_STRING "" #endif -std::string CommonRuntimeTestImpl::GetTestDexFileName(const char* name) const { - CHECK(name != nullptr); - std::string filename; - if (IsHost()) { - filename += getenv("ANDROID_HOST_OUT"); - filename += "/framework/"; - } else { - filename += ART_TARGET_NATIVETEST_DIR_STRING; - } - filename += "art-gtest-"; - filename += name; - filename += ".jar"; - return filename; -} - -std::vector> CommonRuntimeTestImpl::OpenTestDexFiles( - const char* name) { - std::string filename = GetTestDexFileName(name); - static constexpr bool kVerifyChecksum = true; - std::string error_msg; - const ArtDexFileLoader dex_file_loader; - std::vector> dex_files; - bool success = dex_file_loader.Open(filename.c_str(), - filename.c_str(), - /* verify */ true, - kVerifyChecksum, - &error_msg, &dex_files); - CHECK(success) << "Failed to open '" << filename << "': " << error_msg; - for (auto& dex_file : dex_files) { - CHECK_EQ(PROT_READ, dex_file->GetPermissions()); - CHECK(dex_file->IsReadOnly()); - } - return dex_files; -} - -std::unique_ptr CommonRuntimeTestImpl::OpenTestDexFile(const char* name) { - std::vector> vector = OpenTestDexFiles(name); - EXPECT_EQ(1U, vector.size()); - return std::move(vector[0]); -} - std::vector CommonRuntimeTestImpl::GetDexFiles(jobject jclass_loader) { ScopedObjectAccess soa(Thread::Current()); @@ -658,43 +340,6 @@ jobject CommonRuntimeTestImpl::LoadDexInDelegateLastClassLoader(const std::strin parent_loader); } -std::string CommonRuntimeTestImpl::GetCoreFileLocation(const char* suffix) { - CHECK(suffix != nullptr); - - std::string location; - if (IsHost()) { - const char* host_dir = getenv("ANDROID_HOST_OUT"); - CHECK(host_dir != nullptr); - location = StringPrintf("%s/framework/core.%s", host_dir, suffix); - } else { - location = StringPrintf("/data/art-test/core.%s", suffix); - } - - return location; -} - -std::string CommonRuntimeTestImpl::CreateClassPath( - const std::vector>& dex_files) { - CHECK(!dex_files.empty()); - std::string classpath = dex_files[0]->GetLocation(); - for (size_t i = 1; i < dex_files.size(); i++) { - classpath += ":" + dex_files[i]->GetLocation(); - } - return classpath; -} - -std::string CommonRuntimeTestImpl::CreateClassPathWithChecksums( - const std::vector>& dex_files) { - CHECK(!dex_files.empty()); - std::string classpath = dex_files[0]->GetLocation() + "*" + - std::to_string(dex_files[0]->GetLocationChecksum()); - for (size_t i = 1; i < dex_files.size(); i++) { - classpath += ":" + dex_files[i]->GetLocation() + "*" + - std::to_string(dex_files[i]->GetLocationChecksum()); - } - return classpath; -} - void CommonRuntimeTestImpl::FillHeap(Thread* self, ClassLinker* class_linker, VariableSizedHandleScope* handle_scope) { diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 186b1cd509..892abb4dbb 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -25,6 +25,7 @@ #include #include "arch/instruction_set.h" +#include "base/common_art_test.h" #include "base/globals.h" #include "base/mutex.h" #include "base/os.h" @@ -55,64 +56,13 @@ typedef std::vector> RuntimeOptions; class Thread; class VariableSizedHandleScope; -class ScratchFile { - public: - ScratchFile(); - - explicit ScratchFile(const std::string& filename); - - ScratchFile(const ScratchFile& other, const char* suffix); - - ScratchFile(ScratchFile&& other); - - ScratchFile& operator=(ScratchFile&& other); - - explicit ScratchFile(File* file); - - ~ScratchFile(); - - const std::string& GetFilename() const { - return filename_; - } - - File* GetFile() const { - return file_.get(); - } - - int GetFd() const; - - void Close(); - void Unlink(); - - private: - std::string filename_; - std::unique_ptr file_; -}; - -class CommonRuntimeTestImpl { +class CommonRuntimeTestImpl : public CommonArtTestImpl { public: CommonRuntimeTestImpl(); virtual ~CommonRuntimeTestImpl(); - static void SetUpAndroidRoot(); - - // Note: setting up ANDROID_DATA may create a temporary directory. If this is used in a - // non-derived class, be sure to also call the corresponding tear-down below. - static void SetUpAndroidData(std::string& android_data); - - static void TearDownAndroidData(const std::string& android_data, bool fail_on_error); - - // Gets the paths of the libcore dex files. - static std::vector GetLibCoreDexFileNames(); - // Returns bin directory which contains host's prebuild tools. - static std::string GetAndroidHostToolsDir(); - - // Returns bin directory which contains target's prebuild tools. static std::string GetAndroidTargetToolsDir(InstructionSet isa); - // Retuerns the filename for a test dex (i.e. XandY or ManyMethods). - std::string GetTestDexFileName(const char* name) const; - // A helper function to fill the heap. static void FillHeap(Thread* self, ClassLinker* class_linker, @@ -157,26 +107,6 @@ class CommonRuntimeTestImpl { // Called after the runtime is created. virtual void PostRuntimeCreate() {} - static bool IsHost() { - return !kIsTargetBuild; - } - - // File location to core.art, e.g. $ANDROID_HOST_OUT/system/framework/core.art - static std::string GetCoreArtLocation(); - - // File location to core.oat, e.g. $ANDROID_HOST_OUT/system/framework/core.oat - static std::string GetCoreOatLocation(); - - std::unique_ptr LoadExpectSingleDexFile(const char* location); - - void ClearDirectory(const char* dirpath, bool recursive = true); - - std::string GetTestAndroidRoot(); - - std::vector> OpenTestDexFiles(const char* name); - - std::unique_ptr OpenTestDexFile(const char* name); - // Loads the test dex file identified by the given dex_name into a PathClassLoader. // Returns the created class loader. jobject LoadDex(const char* dex_name) REQUIRES_SHARED(Locks::mutator_lock_); @@ -191,9 +121,6 @@ class CommonRuntimeTestImpl { jclass loader_class, jobject parent_loader); - std::string android_data_; - std::string dalvik_cache_; - std::unique_ptr runtime_; // The class_linker_, java_lang_dex_file_, and boot_class_path_ are all @@ -221,20 +148,6 @@ class CommonRuntimeTestImpl { // Called to finish up runtime creation and filling test fields. By default runs root // initializers, initialize well-known classes, and creates the heap thread pool. virtual void FinalizeSetup(); - - // Creates the class path string for the given dex files (the list of dex file locations - // separated by ':'). - std::string CreateClassPath( - const std::vector>& dex_files); - // Same as CreateClassPath but add the dex file checksum after each location. The separator - // is '*'. - std::string CreateClassPathWithChecksums( - const std::vector>& dex_files); - - private: - static std::string GetCoreFileLocation(const char* suffix); - - std::vector> loaded_dex_files_; }; template @@ -278,12 +191,6 @@ class CheckJniAbortCatcher { DISALLOW_COPY_AND_ASSIGN(CheckJniAbortCatcher); }; -#define TEST_DISABLED_FOR_TARGET() \ - if (kIsTargetBuild) { \ - printf("WARNING: TEST DISABLED FOR TARGET\n"); \ - return; \ - } - #define TEST_DISABLED_FOR_MIPS() \ if (kRuntimeISA == InstructionSet::kMips) { \ printf("WARNING: TEST DISABLED FOR MIPS\n"); \ @@ -308,30 +215,6 @@ class CheckJniAbortCatcher { return; \ } -#define TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS() \ - if (!kHostStaticBuildEnabled) { \ - printf("WARNING: TEST DISABLED FOR NON-STATIC HOST BUILDS\n"); \ - return; \ - } - -#define TEST_DISABLED_FOR_MEMORY_TOOL() \ - if (RUNNING_ON_MEMORY_TOOL > 0) { \ - printf("WARNING: TEST DISABLED FOR MEMORY TOOL\n"); \ - return; \ - } - -#define TEST_DISABLED_FOR_MEMORY_TOOL_VALGRIND() \ - if (RUNNING_ON_MEMORY_TOOL > 0 && kMemoryToolIsValgrind) { \ - printf("WARNING: TEST DISABLED FOR MEMORY TOOL VALGRIND\n"); \ - return; \ - } - -#define TEST_DISABLED_FOR_MEMORY_TOOL_ASAN() \ - if (RUNNING_ON_MEMORY_TOOL > 0 && !kMemoryToolIsValgrind) { \ - printf("WARNING: TEST DISABLED FOR MEMORY TOOL ASAN\n"); \ - return; \ - } - #define TEST_DISABLED_FOR_HEAP_POISONING() \ if (kPoisonHeapReferences) { \ printf("WARNING: TEST DISABLED FOR HEAP POISONING\n"); \ diff --git a/runtime/dex/art_dex_file_loader_test.cc b/runtime/dex/art_dex_file_loader_test.cc index afc2599ea0..aee397b65b 100644 --- a/runtime/dex/art_dex_file_loader_test.cc +++ b/runtime/dex/art_dex_file_loader_test.cc @@ -353,7 +353,7 @@ TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { /* verify_checksum */ false, &error_msg, &dex_files); - ASSERT_TRUE(success); + ASSERT_TRUE(success) << error_msg; ASSERT_GE(dex_files.size(), 1u); for (std::unique_ptr& dex_file : dex_files) { ASSERT_FALSE(dex_file->IsPlatformDexFile()); diff --git a/test/Android.bp b/test/Android.bp index 16d14cdf30..0c6b449877 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -148,6 +148,7 @@ art_cc_library { whole_static_libs: [ "libart-compiler-gtest", "libart-runtime-gtest", + "libartbase-art-gtest", "libgtest", ], shared_libs: [ -- GitLab From a655575c393f88219fa559d3d2e71ad905675621 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 2 May 2018 10:39:09 +0100 Subject: [PATCH 322/749] Don't run ICU tests on gcstress. Also fix logic around boolean checks (thanks bash). bug: 78228743 Test: run-libcore-tests Change-Id: I2fc2310e9328c0f4b24a281599d51a46336369a9 --- tools/run-libcore-tests.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh index 7f0383d55d..26b5c0a09b 100755 --- a/tools/run-libcore-tests.sh +++ b/tools/run-libcore-tests.sh @@ -77,7 +77,6 @@ working_packages=("libcore.dalvik.system" "libcore.javax.security" "libcore.javax.sql" "libcore.javax.xml" - "libcore.libcore.icu" "libcore.libcore.io" "libcore.libcore.net" "libcore.libcore.reflect" @@ -154,11 +153,14 @@ fi vogar_args="$vogar_args --vm-arg -Xusejit:$use_jit" # gcstress may lead to timeouts, so we need dedicated expectations files for it. -if [[ $gcstress ]]; then +if $gcstress; then expectations="$expectations --expectations art/tools/libcore_gcstress_failures.txt" - if [[ $debug ]]; then + if $debug; then expectations="$expectations --expectations art/tools/libcore_gcstress_debug_failures.txt" fi +else + # We only run this package when not under gcstress as it can cause timeouts. See b/78228743. + working_packages+=("libcore.libcore.icu") fi # Disable network-related libcore tests that are failing on the following -- GitLab From a92f51bc0cb959a9c54d998ef0e7a08d740df8db Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 1 May 2018 11:50:33 -0700 Subject: [PATCH 323/749] ART: Follow-up changes to DexFileVerifier Address comments. Bug: 78568168 Test: m test-art-host Change-Id: I9649f4342986995bf2aeb83e89c1ea74a9d9b74b --- libdexfile/dex/dex_file_verifier.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc index 2380f48d3a..ba65fc9b28 100644 --- a/libdexfile/dex/dex_file_verifier.cc +++ b/libdexfile/dex/dex_file_verifier.cc @@ -615,7 +615,7 @@ bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, uint32_t code_offset, ClassDataItemIterator* direct_it, bool expect_direct) { - DCHECK(expect_direct || direct_it != nullptr); + DCHECK_EQ(expect_direct, direct_it == nullptr); // Check for overflow. if (!CheckIndex(idx, header_->method_ids_size_, "class_data_item method_idx")) { return false; @@ -1252,9 +1252,10 @@ bool DexFileVerifier::CheckIntraCodeItem() { uint32_t* handler_offsets; constexpr size_t kAllocaMaxSize = 1024; if (handlers_size < kAllocaMaxSize/sizeof(uint32_t)) { + // Note: Clang does not specify alignment guarantees for alloca. So align by hand. handler_offsets = - AlignDown(reinterpret_cast(alloca((handlers_size + 1) * sizeof(uint32_t))), - alignof(uint32_t[])); + AlignUp(reinterpret_cast(alloca((handlers_size + 1) * sizeof(uint32_t))), + alignof(uint32_t[])); } else { handler_offsets_uptr.reset(new uint32_t[handlers_size]); handler_offsets = handler_offsets_uptr.get(); -- GitLab From 52f5a1fb829bd238fdd3c12da66b2b5de92c6ad1 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Wed, 2 May 2018 11:05:44 +0100 Subject: [PATCH 324/749] ART: No JIT for native MethodHandle/VarHandle methods MethodHandle invocations can record JIT samples. This can lead to transitioning the JNI stubs for MH.invoke and MH.invokeExact from AOT compiled to JIT compiled and then to generic when there is a JIT code cache collection. The transitions and differences in stack frame representations can cause a crash during stack walking. This only affects native invocations of handles which we do to raise UnsuppportedOperationExceptions when invoked reflectively. Not performance critical and not likely to be exercised in real code, but leads to flakes in gcstress testing of 956-methodhandles. Bug: 78151261 Test: art/test/run-test --host --64 --compact-dex-level fast --jit --no-relocate --pic-test 956 Test: art/test.py --host -r -t 716 Change-Id: Ie3fdbcfc4decb12814290bcce8d25c5e2fde87f1 --- runtime/jit/jit.cc | 26 +++- test/716-jli-jit-samples/build | 20 +++ test/716-jli-jit-samples/expected.txt | 3 + test/716-jli-jit-samples/info.txt | 2 + test/716-jli-jit-samples/src-art/Main.java | 142 +++++++++++++++++++++ test/common/runtime_state.cc | 22 ++-- 6 files changed, 204 insertions(+), 11 deletions(-) create mode 100755 test/716-jli-jit-samples/build create mode 100644 test/716-jli-jit-samples/expected.txt create mode 100644 test/716-jli-jit-samples/info.txt create mode 100644 test/716-jli-jit-samples/src-art/Main.java diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index d20f760c17..2c0fbadc1d 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -29,6 +29,8 @@ #include "interpreter/interpreter.h" #include "java_vm_ext.h" #include "jit_code_cache.h" +#include "mirror/method_handle_impl.h" +#include "mirror/var_handle.h" #include "oat_file_manager.h" #include "oat_quick_method_header.h" #include "profile/profile_compilation_info.h" @@ -638,15 +640,33 @@ class JitCompileTask FINAL : public Task { DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask); }; +static bool IgnoreSamplesForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { + if (method->IsClassInitializer() || !method->IsCompilable()) { + // We do not want to compile such methods. + return true; + } + if (method->IsNative()) { + ObjPtr klass = method->GetDeclaringClass(); + if (klass == mirror::MethodHandle::StaticClass() || klass == mirror::VarHandle::StaticClass()) { + // MethodHandle and VarHandle invocation methods are required to throw an + // UnsupportedOperationException if invoked reflectively. We achieve this by having native + // implementations that arise the exception. We need to disable JIT compilation of these JNI + // methods as it can lead to transitioning between JIT compiled JNI stubs and generic JNI + // stubs. Since these stubs have different stack representations we can then crash in stack + // walking (b/78151261). + return true; + } + } + return false; +} + void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_backedges) { if (thread_pool_ == nullptr) { // Should only see this when shutting down. DCHECK(Runtime::Current()->IsShuttingDown(self)); return; } - - if (method->IsClassInitializer() || !method->IsCompilable()) { - // We do not want to compile such methods. + if (IgnoreSamplesForMethod(method)) { return; } if (hot_method_threshold_ == 0) { diff --git a/test/716-jli-jit-samples/build b/test/716-jli-jit-samples/build new file mode 100755 index 0000000000..730a8a14bb --- /dev/null +++ b/test/716-jli-jit-samples/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# make us exit on a failure +set -e + +./default-build "$@" --experimental var-handles diff --git a/test/716-jli-jit-samples/expected.txt b/test/716-jli-jit-samples/expected.txt new file mode 100644 index 0000000000..dc533f3689 --- /dev/null +++ b/test/716-jli-jit-samples/expected.txt @@ -0,0 +1,3 @@ +JNI_OnLoad called +MethodHandle OK +VarHandle OK diff --git a/test/716-jli-jit-samples/info.txt b/test/716-jli-jit-samples/info.txt new file mode 100644 index 0000000000..81a76f64da --- /dev/null +++ b/test/716-jli-jit-samples/info.txt @@ -0,0 +1,2 @@ +Test MethodHandle and VarHandle invokes do not accumulate JIT samples +(regression test for b/78151261). diff --git a/test/716-jli-jit-samples/src-art/Main.java b/test/716-jli-jit-samples/src-art/Main.java new file mode 100644 index 0000000000..def6b9f669 --- /dev/null +++ b/test/716-jli-jit-samples/src-art/Main.java @@ -0,0 +1,142 @@ +/* + * 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.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.invoke.VarHandle; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class Main { + private static final int ITERATIONS = 100; + + private static final VarHandle widgetIdVarHandle; + + public static native int getHotnessCounter(Class cls, String methodName); + + public static class Widget { + public Widget(int id) { + this.id = id; + } + + int getId() { + return id; + } + + int id; + } + + static { + try { + widgetIdVarHandle = MethodHandles.lookup().findVarHandle(Widget.class, "id", int.class); + } catch (Exception e) { + throw new Error(e); + } + } + + private static void assertEquals(int i1, int i2) { + if (i1 == i2) { + return; + } + throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2); + } + + private static void assertEquals(Object o, Object p) { + if (o == p) { + return; + } + if (o != null && p != null && o.equals(p)) { + return; + } + throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p); + } + + private static void fail() { + System.out.println("fail"); + Thread.dumpStack(); + } + + private static void fail(String message) { + System.out.println("fail: " + message); + Thread.dumpStack(); + } + + private static void testMethodHandleCounters() throws Throwable { + for (int i = 0; i < ITERATIONS; ++i) { + // Regular MethodHandle invocations + MethodHandle mh = + MethodHandles.lookup() + .findConstructor( + Widget.class, MethodType.methodType(void.class, int.class)); + Widget w = (Widget) mh.invoke(3); + w = (Widget) mh.invokeExact(3); + assertEquals(0, getHotnessCounter(MethodHandle.class, "invoke")); + assertEquals(0, getHotnessCounter(MethodHandle.class, "invokeExact")); + + // Reflective MethodHandle invocations + String[] methodNames = {"invoke", "invokeExact"}; + for (String methodName : methodNames) { + Method invokeMethod = MethodHandle.class.getMethod(methodName, Object[].class); + MethodHandle instance = + MethodHandles.lookup() + .findVirtual( + Widget.class, "getId", MethodType.methodType(int.class)); + try { + invokeMethod.invoke(instance, new Object[] {new Object[] {}}); + fail(); + } catch (InvocationTargetException ite) { + assertEquals(ite.getCause().getClass(), UnsupportedOperationException.class); + } + } + assertEquals(0, getHotnessCounter(MethodHandle.class, "invoke")); + assertEquals(0, getHotnessCounter(MethodHandle.class, "invokeExact")); + } + + System.out.println("MethodHandle OK"); + } + + private static void testVarHandleCounters() throws Throwable { + Widget w = new Widget(0); + for (int i = 0; i < ITERATIONS; ++i) { + // Regular accessor invocations + widgetIdVarHandle.set(w, i); + assertEquals(i, widgetIdVarHandle.get(w)); + assertEquals(0, getHotnessCounter(VarHandle.class, "set")); + assertEquals(0, getHotnessCounter(VarHandle.class, "get")); + + // Reflective accessor invocations + for (String accessorName : new String[] {"get", "set"}) { + Method setMethod = VarHandle.class.getMethod(accessorName, Object[].class); + try { + setMethod.invoke(widgetIdVarHandle, new Object[] {new Object[0]}); + fail(); + } catch (InvocationTargetException ite) { + assertEquals(ite.getCause().getClass(), UnsupportedOperationException.class); + } + } + assertEquals(0, getHotnessCounter(VarHandle.class, "set")); + assertEquals(0, getHotnessCounter(VarHandle.class, "get")); + } + System.out.println("VarHandle OK"); + } + + public static void main(String[] args) throws Throwable { + System.loadLibrary(args[0]); + testMethodHandleCounters(); + testVarHandleCounters(); + } +} diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index a55cc79ef9..f89888bb99 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -258,17 +258,23 @@ extern "C" JNIEXPORT int JNICALL Java_Main_getHotnessCounter(JNIEnv* env, jclass, jclass cls, jstring method_name) { - ArtMethod* method = nullptr; - { - ScopedObjectAccess soa(Thread::Current()); + ScopedObjectAccess soa(Thread::Current()); + ScopedUtfChars chars(env, method_name); + CHECK(chars.c_str() != nullptr); + ArtMethod* method = + soa.Decode(cls)->FindDeclaredDirectMethodByName(chars.c_str(), + kRuntimePointerSize); + if (method != nullptr) { + return method->GetCounter(); + } - ScopedUtfChars chars(env, method_name); - CHECK(chars.c_str() != nullptr); - method = soa.Decode(cls)->FindDeclaredDirectMethodByName( - chars.c_str(), kRuntimePointerSize); + method = soa.Decode(cls)->FindDeclaredVirtualMethodByName(chars.c_str(), + kRuntimePointerSize); + if (method != nullptr) { + return method->GetCounter(); } - return method->GetCounter(); + return std::numeric_limits::min(); } extern "C" JNIEXPORT int JNICALL Java_Main_numberOfDeoptimizations(JNIEnv*, jclass) { -- GitLab From 86bb96fd68403e93520d1aa757fcbf1048a21d26 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Thu, 3 May 2018 14:07:26 +0100 Subject: [PATCH 325/749] ART: Skip RI test of 716-jli-jit-samples Test: art/test.py --host --jvm --64 -r -t 716 Bug: 78151261 Change-Id: I0a6ee0016cf34a7aeceb048d4c25ccc1f5275f0f --- test/knownfailures.json | 1 + 1 file changed, 1 insertion(+) diff --git a/test/knownfailures.json b/test/knownfailures.json index 369f927270..e109bf5a8f 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -883,6 +883,7 @@ "706-checker-scheduler", "707-checker-invalid-profile", "714-invoke-custom-lambda-metafactory", + "716-jli-jit-samples", "800-smali", "801-VoidCheckCast", "802-deoptimization", -- GitLab From a8360cd6b858906f20558552f7bf3b3876c72ec4 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Wed, 2 May 2018 16:07:51 -0700 Subject: [PATCH 326/749] Perform rudimentary check on graph size for no-change assertions. Rationale: This will find blatant violations of asserting a no-change pass change if the graph size changed nevertheless. Bug: 78171933 Test: test-art-host,target Change-Id: I07b38e71c75dd6f728246d096976c8333b363329 --- compiler/optimizing/graph_checker.cc | 24 ++++++++++++++++++++++ compiler/optimizing/graph_checker.h | 12 +++++------ compiler/optimizing/loop_optimization.cc | 1 + compiler/optimizing/optimizing_compiler.cc | 20 ++++++++++++++---- 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index fbcbe3608e..a689f35e0f 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -58,6 +58,30 @@ static bool IsExitTryBoundaryIntoExitBlock(HBasicBlock* block) { !boundary->IsEntry(); } + +size_t GraphChecker::Run(bool pass_change, size_t last_size) { + size_t current_size = GetGraph()->GetReversePostOrder().size(); + if (!pass_change) { + // Nothing changed for certain. Do a quick sanity check on that assertion + // for anything other than the first call (when last size was still 0). + if (last_size != 0) { + if (current_size != last_size) { + AddError(StringPrintf("Incorrect no-change assertion, " + "last graph size %zu vs current graph size %zu", + last_size, current_size)); + } + } + // TODO: if we would trust the "false" value of the flag completely, we + // could skip checking the graph at this point. + } + + // VisitReversePostOrder is used instead of VisitInsertionOrder, + // as the latter might visit dead blocks removed by the dominator + // computation. + VisitReversePostOrder(); + return current_size; +} + void GraphChecker::VisitBasicBlock(HBasicBlock* block) { current_block_ = block; diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h index dbedc40518..3a2bb7a00c 100644 --- a/compiler/optimizing/graph_checker.h +++ b/compiler/optimizing/graph_checker.h @@ -38,13 +38,11 @@ class GraphChecker : public HGraphDelegateVisitor { seen_ids_.ClearAllBits(); } - // Check the whole graph (in reverse post-order). - void Run() { - // VisitReversePostOrder is used instead of VisitInsertionOrder, - // as the latter might visit dead blocks removed by the dominator - // computation. - VisitReversePostOrder(); - } + // Check the whole graph. The pass_change parameter indicates whether changes + // may have occurred during the just executed pass. The default value is + // conservatively "true" (something may have changed). The last_size parameter + // and return value pass along the observed graph sizes. + size_t Run(bool pass_change = true, size_t last_size = 0); void VisitBasicBlock(HBasicBlock* block) OVERRIDE; diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 0d85c2fbf5..1ce3524bd6 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -589,6 +589,7 @@ bool HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) { // loop if the induction of any inner loop has changed. if (TraverseLoopsInnerToOuter(node->inner)) { induction_range_.ReVisit(node->loop_info); + changed = true; } // Repeat simplifications in the loop-body until no more changes occur. // Note that since each simplification consists of eliminating code (without diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index a6163a7fcf..6e2c99444c 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -107,6 +107,7 @@ class PassObserver : public ValueObject { CompilerDriver* compiler_driver, Mutex& dump_mutex) : graph_(graph), + last_seen_graph_size_(0), cached_method_name_(), timing_logger_enabled_(compiler_driver->GetCompilerOptions().GetDumpTimings()), timing_logger_(timing_logger_enabled_ ? GetMethodName() : "", true, true), @@ -174,7 +175,7 @@ class PassObserver : public ValueObject { visualizer_oss_.clear(); } - void EndPass(const char* pass_name) REQUIRES(!visualizer_dump_mutex_) { + void EndPass(const char* pass_name, bool pass_change) REQUIRES(!visualizer_dump_mutex_) { // Pause timer first, then dump graph. if (timing_logger_enabled_) { timing_logger_.EndTiming(); @@ -188,7 +189,7 @@ class PassObserver : public ValueObject { if (kIsDebugBuild) { if (!graph_in_bad_state_) { GraphChecker checker(graph_); - checker.Run(); + last_seen_graph_size_ = checker.Run(pass_change, last_seen_graph_size_); if (!checker.IsValid()) { LOG(FATAL) << "Error after " << pass_name << ": " << Dumpable(checker); } @@ -214,6 +215,7 @@ class PassObserver : public ValueObject { } HGraph* const graph_; + size_t last_seen_graph_size_; std::string cached_method_name_; @@ -241,16 +243,22 @@ class PassScope : public ValueObject { public: PassScope(const char *pass_name, PassObserver* pass_observer) : pass_name_(pass_name), + pass_change_(true), // assume change pass_observer_(pass_observer) { pass_observer_->StartPass(pass_name_); } + void SetPassNotChanged() { + pass_change_ = false; + } + ~PassScope() { - pass_observer_->EndPass(pass_name_); + pass_observer_->EndPass(pass_name_, pass_change_); } private: const char* const pass_name_; + bool pass_change_; PassObserver* const pass_observer_; }; @@ -324,7 +332,11 @@ class OptimizingCompiler FINAL : public Compiler { PassScope scope(optimizations[i]->GetPassName(), pass_observer); bool pass_change = optimizations[i]->Run(); pass_changes[static_cast(definitions[i].pass)] = pass_change; - change |= pass_change; + if (pass_change) { + change = true; + } else { + scope.SetPassNotChanged(); + } } else { // Skip the pass and record that nothing changed. pass_changes[static_cast(definitions[i].pass)] = false; -- GitLab From e2b4195e3a562eb60a38c2beeba8ba97c689aeed Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 25 Apr 2018 09:09:28 +0100 Subject: [PATCH 327/749] [veridex] Reflection detection improvements. - Handle invoke range instructions. - Implement parameter substitution. bug: 77513322 Test: m (cherry picked from commit 5de2ff26d448629082ffe3058a0a76fc38362daa) Change-Id: Ib7bb1b3ff9b28f0ecfc8b338ef4cf08e5c9b159f --- tools/veridex/flow_analysis.cc | 147 +++++++++++++++------ tools/veridex/flow_analysis.h | 113 ++++++++++++---- tools/veridex/precise_hidden_api_finder.cc | 80 +++++++---- tools/veridex/precise_hidden_api_finder.h | 13 +- 4 files changed, 262 insertions(+), 91 deletions(-) diff --git a/tools/veridex/flow_analysis.cc b/tools/veridex/flow_analysis.cc index 736abb7f17..e2833bf01d 100644 --- a/tools/veridex/flow_analysis.cc +++ b/tools/veridex/flow_analysis.cc @@ -243,43 +243,7 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { case Instruction::INVOKE_STATIC: case Instruction::INVOKE_SUPER: case Instruction::INVOKE_VIRTUAL: { - VeriMethod method = resolver_->GetMethod(instruction.VRegB_35c()); - uint32_t args[5]; - instruction.GetVarArgs(args); - if (method == VeriClass::forName_) { - RegisterValue value = GetRegister(args[0]); - last_result_ = RegisterValue( - value.GetSource(), value.GetDexFileReference(), VeriClass::class_); - } else if (IsGetField(method)) { - RegisterValue cls = GetRegister(args[0]); - RegisterValue name = GetRegister(args[1]); - field_uses_.push_back(std::make_pair(cls, name)); - last_result_ = GetReturnType(instruction.VRegB_35c()); - } else if (IsGetMethod(method)) { - RegisterValue cls = GetRegister(args[0]); - RegisterValue name = GetRegister(args[1]); - method_uses_.push_back(std::make_pair(cls, name)); - last_result_ = GetReturnType(instruction.VRegB_35c()); - } else if (method == VeriClass::getClass_) { - RegisterValue obj = GetRegister(args[0]); - const VeriClass* cls = obj.GetType(); - if (cls != nullptr && cls->GetClassDef() != nullptr) { - const DexFile::ClassDef* def = cls->GetClassDef(); - last_result_ = RegisterValue( - RegisterSource::kClass, - DexFileReference(&resolver_->GetDexFileOf(*cls), def->class_idx_.index_), - VeriClass::class_); - } else { - last_result_ = RegisterValue( - obj.GetSource(), obj.GetDexFileReference(), VeriClass::class_); - } - } else if (method == VeriClass::loadClass_) { - RegisterValue value = GetRegister(args[1]); - last_result_ = RegisterValue( - value.GetSource(), value.GetDexFileReference(), VeriClass::class_); - } else { - last_result_ = GetReturnType(instruction.VRegB_35c()); - } + last_result_ = AnalyzeInvoke(instruction, /* is_range */ false); break; } @@ -288,7 +252,7 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { case Instruction::INVOKE_STATIC_RANGE: case Instruction::INVOKE_SUPER_RANGE: case Instruction::INVOKE_VIRTUAL_RANGE: { - last_result_ = GetReturnType(instruction.VRegB_3rc()); + last_result_ = AnalyzeInvoke(instruction, /* is_range */ true); break; } @@ -520,6 +484,7 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { case Instruction::IPUT_BYTE: case Instruction::IPUT_CHAR: case Instruction::IPUT_SHORT: { + AnalyzeFieldSet(instruction); break; } @@ -541,6 +506,7 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { case Instruction::SPUT_BYTE: case Instruction::SPUT_CHAR: case Instruction::SPUT_SHORT: { + AnalyzeFieldSet(instruction); break; } @@ -613,7 +579,112 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { void VeriFlowAnalysis::Run() { FindBranches(); + uint32_t number_of_registers = code_item_accessor_.RegistersSize(); + uint32_t number_of_parameters = code_item_accessor_.InsSize(); + std::vector& initial_values = *dex_registers_[0].get(); + for (uint32_t i = 0; i < number_of_parameters; ++i) { + initial_values[number_of_registers - number_of_parameters + i] = RegisterValue( + RegisterSource::kParameter, + i, + DexFileReference(&resolver_->GetDexFile(), method_id_), + nullptr); + } AnalyzeCode(); } +static uint32_t GetParameterAt(const Instruction& instruction, + bool is_range, + uint32_t* args, + uint32_t index) { + return is_range ? instruction.VRegC() + index : args[index]; +} + +RegisterValue FlowAnalysisCollector::AnalyzeInvoke(const Instruction& instruction, bool is_range) { + uint32_t id = is_range ? instruction.VRegB_3rc() : instruction.VRegB_35c(); + VeriMethod method = resolver_->GetMethod(id); + uint32_t args[5]; + if (!is_range) { + instruction.GetVarArgs(args); + } + + if (method == VeriClass::forName_) { + // Class.forName. Fetch the first parameter. + RegisterValue value = GetRegister(GetParameterAt(instruction, is_range, args, 0)); + return RegisterValue( + value.GetSource(), value.GetDexFileReference(), VeriClass::class_); + } else if (IsGetField(method)) { + // Class.getField or Class.getDeclaredField. Fetch the first parameter for the class, and the + // second parameter for the field name. + RegisterValue cls = GetRegister(GetParameterAt(instruction, is_range, args, 0)); + RegisterValue name = GetRegister(GetParameterAt(instruction, is_range, args, 1)); + uses_.push_back(ReflectAccessInfo(cls, name, /* is_method */ false)); + return GetReturnType(id); + } else if (IsGetMethod(method)) { + // Class.getMethod or Class.getDeclaredMethod. Fetch the first parameter for the class, and the + // second parameter for the field name. + RegisterValue cls = GetRegister(GetParameterAt(instruction, is_range, args, 0)); + RegisterValue name = GetRegister(GetParameterAt(instruction, is_range, args, 1)); + uses_.push_back(ReflectAccessInfo(cls, name, /* is_method */ true)); + return GetReturnType(id); + } else if (method == VeriClass::getClass_) { + // Get the type of the first parameter. + RegisterValue obj = GetRegister(GetParameterAt(instruction, is_range, args, 0)); + const VeriClass* cls = obj.GetType(); + if (cls != nullptr && cls->GetClassDef() != nullptr) { + const DexFile::ClassDef* def = cls->GetClassDef(); + return RegisterValue( + RegisterSource::kClass, + DexFileReference(&resolver_->GetDexFileOf(*cls), def->class_idx_.index_), + VeriClass::class_); + } else { + return RegisterValue( + obj.GetSource(), obj.GetDexFileReference(), VeriClass::class_); + } + } else if (method == VeriClass::loadClass_) { + // ClassLoader.loadClass. Fetch the first parameter. + RegisterValue value = GetRegister(GetParameterAt(instruction, is_range, args, 1)); + return RegisterValue( + value.GetSource(), value.GetDexFileReference(), VeriClass::class_); + } else { + // Return a RegisterValue referencing the method whose type is the return type + // of the method. + return GetReturnType(id); + } +} + +void FlowAnalysisCollector::AnalyzeFieldSet(const Instruction& instruction ATTRIBUTE_UNUSED) { + // There are no fields that escape reflection uses. +} + +RegisterValue FlowAnalysisSubstitutor::AnalyzeInvoke(const Instruction& instruction, + bool is_range) { + uint32_t id = is_range ? instruction.VRegB_3rc() : instruction.VRegB_35c(); + MethodReference method(&resolver_->GetDexFile(), id); + // TODO: doesn't work for multidex + // TODO: doesn't work for overriding (but maybe should be done at a higher level); + if (accesses_.find(method) == accesses_.end()) { + return GetReturnType(id); + } + uint32_t args[5]; + if (!is_range) { + instruction.GetVarArgs(args); + } + for (const ReflectAccessInfo& info : accesses_.at(method)) { + if (info.cls.IsParameter() || info.name.IsParameter()) { + RegisterValue cls = info.cls.IsParameter() + ? GetRegister(GetParameterAt(instruction, is_range, args, info.cls.GetParameterIndex())) + : info.cls; + RegisterValue name = info.name.IsParameter() + ? GetRegister(GetParameterAt(instruction, is_range, args, info.name.GetParameterIndex())) + : info.name; + uses_.push_back(ReflectAccessInfo(cls, name, info.is_method)); + } + } + return GetReturnType(id); +} + +void FlowAnalysisSubstitutor::AnalyzeFieldSet(const Instruction& instruction ATTRIBUTE_UNUSED) { + // TODO: analyze field sets. +} + } // namespace art diff --git a/tools/veridex/flow_analysis.h b/tools/veridex/flow_analysis.h index 80ae5fc9df..62c9916a61 100644 --- a/tools/veridex/flow_analysis.h +++ b/tools/veridex/flow_analysis.h @@ -21,13 +21,11 @@ #include "dex/dex_file_reference.h" #include "dex/method_reference.h" #include "hidden_api.h" +#include "resolver.h" #include "veridex.h" namespace art { -class VeridexClass; -class VeridexResolver; - /** * The source where a dex register comes from. */ @@ -45,13 +43,29 @@ enum class RegisterSource { */ class RegisterValue { public: - RegisterValue() : source_(RegisterSource::kNone), reference_(nullptr, 0), type_(nullptr) {} + RegisterValue() : source_(RegisterSource::kNone), + parameter_index_(0), + reference_(nullptr, 0), + type_(nullptr) {} RegisterValue(RegisterSource source, DexFileReference reference, const VeriClass* type) - : source_(source), reference_(reference), type_(type) {} + : source_(source), parameter_index_(0), reference_(reference), type_(type) {} + + RegisterValue(RegisterSource source, + uint32_t parameter_index, + DexFileReference reference, + const VeriClass* type) + : source_(source), parameter_index_(parameter_index), reference_(reference), type_(type) {} RegisterSource GetSource() const { return source_; } DexFileReference GetDexFileReference() const { return reference_; } const VeriClass* GetType() const { return type_; } + uint32_t GetParameterIndex() const { + CHECK(IsParameter()); + return parameter_index_; + } + bool IsParameter() const { return source_ == RegisterSource::kParameter; } + bool IsClass() const { return source_ == RegisterSource::kClass; } + bool IsString() const { return source_ == RegisterSource::kString; } std::string ToString() const { switch (source_) { @@ -68,6 +82,8 @@ class RegisterValue { } case RegisterSource::kClass: return reference_.dex_file->StringByTypeIdx(dex::TypeIndex(reference_.index)); + case RegisterSource::kParameter: + return std::string("Parameter of ") + reference_.dex_file->PrettyMethod(reference_.index); default: return ""; } @@ -75,6 +91,7 @@ class RegisterValue { private: RegisterSource source_; + uint32_t parameter_index_; DexFileReference reference_; const VeriClass* type_; }; @@ -85,22 +102,18 @@ struct InstructionInfo { class VeriFlowAnalysis { public: - VeriFlowAnalysis(VeridexResolver* resolver, - const CodeItemDataAccessor& code_item_accessor) + VeriFlowAnalysis(VeridexResolver* resolver, const ClassDataItemIterator& it) : resolver_(resolver), - code_item_accessor_(code_item_accessor), - dex_registers_(code_item_accessor.InsnsSizeInCodeUnits()), - instruction_infos_(code_item_accessor.InsnsSizeInCodeUnits()) {} + method_id_(it.GetMemberIndex()), + code_item_accessor_(resolver->GetDexFile(), it.GetMethodCodeItem()), + dex_registers_(code_item_accessor_.InsnsSizeInCodeUnits()), + instruction_infos_(code_item_accessor_.InsnsSizeInCodeUnits()) {} void Run(); - const std::vector>& GetFieldUses() const { - return field_uses_; - } - - const std::vector>& GetMethodUses() const { - return method_uses_; - } + virtual RegisterValue AnalyzeInvoke(const Instruction& instruction, bool is_range) = 0; + virtual void AnalyzeFieldSet(const Instruction& instruction) = 0; + virtual ~VeriFlowAnalysis() {} private: // Find all branches in the code. @@ -124,14 +137,19 @@ class VeriFlowAnalysis { uint32_t dex_register, RegisterSource kind, VeriClass* cls, uint32_t source_id); void UpdateRegister(uint32_t dex_register, const RegisterValue& value); void UpdateRegister(uint32_t dex_register, const VeriClass* cls); - const RegisterValue& GetRegister(uint32_t dex_register); void ProcessDexInstruction(const Instruction& inst); void SetVisited(uint32_t dex_pc); - RegisterValue GetReturnType(uint32_t method_index); RegisterValue GetFieldType(uint32_t field_index); + protected: + const RegisterValue& GetRegister(uint32_t dex_register); + RegisterValue GetReturnType(uint32_t method_index); + VeridexResolver* resolver_; - const CodeItemDataAccessor& code_item_accessor_; + + private: + const uint32_t method_id_; + CodeItemDataAccessor code_item_accessor_; // Vector of register values for all branch targets. std::vector>> dex_registers_; @@ -144,12 +162,59 @@ class VeriFlowAnalysis { // The value of invoke instructions, to be fetched when visiting move-result. RegisterValue last_result_; +}; + +struct ReflectAccessInfo { + RegisterValue cls; + RegisterValue name; + bool is_method; + + ReflectAccessInfo(RegisterValue c, RegisterValue n, bool m) : cls(c), name(n), is_method(m) {} - // List of reflection field uses found. - std::vector> field_uses_; + bool IsConcrete() const { + // We capture RegisterSource::kString for the class, for example in Class.forName. + return (cls.IsClass() || cls.IsString()) && name.IsString(); + } +}; + +// Collects all reflection uses. +class FlowAnalysisCollector : public VeriFlowAnalysis { + public: + FlowAnalysisCollector(VeridexResolver* resolver, const ClassDataItemIterator& it) + : VeriFlowAnalysis(resolver, it) {} + + const std::vector& GetUses() const { + return uses_; + } + + RegisterValue AnalyzeInvoke(const Instruction& instruction, bool is_range) OVERRIDE; + void AnalyzeFieldSet(const Instruction& instruction) OVERRIDE; + + private: + // List of reflection uses found, concrete and abstract. + std::vector uses_; +}; + +// Substitutes reflection uses by new ones. +class FlowAnalysisSubstitutor : public VeriFlowAnalysis { + public: + FlowAnalysisSubstitutor(VeridexResolver* resolver, + const ClassDataItemIterator& it, + const std::map>& accesses) + : VeriFlowAnalysis(resolver, it), accesses_(accesses) {} - // List of reflection method uses found. - std::vector> method_uses_; + const std::vector& GetUses() const { + return uses_; + } + + RegisterValue AnalyzeInvoke(const Instruction& instruction, bool is_range) OVERRIDE; + void AnalyzeFieldSet(const Instruction& instruction) OVERRIDE; + + private: + // List of reflection uses found, concrete and abstract. + std::vector uses_; + // The abstract uses we are trying to subsititute. + const std::map>& accesses_; }; } // namespace art diff --git a/tools/veridex/precise_hidden_api_finder.cc b/tools/veridex/precise_hidden_api_finder.cc index 4ae5769014..89754c2cdf 100644 --- a/tools/veridex/precise_hidden_api_finder.cc +++ b/tools/veridex/precise_hidden_api_finder.cc @@ -29,7 +29,9 @@ namespace art { -void PreciseHiddenApiFinder::Run(const std::vector>& resolvers) { +void PreciseHiddenApiFinder::RunInternal( + const std::vector>& resolvers, + const std::function& action) { for (const std::unique_ptr& resolver : resolvers) { const DexFile& dex_file = resolver->GetDexFile(); size_t class_def_count = dex_file.NumClassDefs(); @@ -47,43 +49,67 @@ void PreciseHiddenApiFinder::Run(const std::vector& accesses, + MethodReference ref) { + for (const ReflectAccessInfo& info : accesses) { + if (info.IsConcrete()) { + concrete_uses_[ref].push_back(info); + } else { + abstract_uses_[ref].push_back(info); + } + } +} + +void PreciseHiddenApiFinder::Run(const std::vector>& resolvers) { + // Collect reflection uses. + RunInternal(resolvers, [this] (VeridexResolver* resolver, const ClassDataItemIterator& it) { + FlowAnalysisCollector collector(resolver, it); + collector.Run(); + AddUsesAt(collector.GetUses(), MethodReference(&resolver->GetDexFile(), it.GetMemberIndex())); + }); + + // For non-final reflection uses, do a limited fixed point calculation over the code to try + // substituting them with final reflection uses. + // We limit the number of times we iterate over the code as one run can be long. + static const int kMaximumIterations = 10; + uint32_t i = 0; + while (!abstract_uses_.empty() && (i++ < kMaximumIterations)) { + // Fetch and clear the worklist. + std::map> current_uses + = std::move(abstract_uses_); + RunInternal(resolvers, + [this, current_uses] (VeridexResolver* resolver, const ClassDataItemIterator& it) { + FlowAnalysisSubstitutor substitutor(resolver, it, current_uses); + substitutor.Run(); + AddUsesAt(substitutor.GetUses(), + MethodReference(&resolver->GetDexFile(), it.GetMemberIndex())); + }); + } +} + void PreciseHiddenApiFinder::Dump(std::ostream& os, HiddenApiStats* stats) { static const char* kPrefix = " "; - std::map> uses; - for (auto kinds : { field_uses_, method_uses_ }) { - for (auto it : kinds) { - MethodReference ref = it.first; - for (const std::pair& info : it.second) { - if ((info.first.GetSource() == RegisterSource::kClass || - info.first.GetSource() == RegisterSource::kString) && - info.second.GetSource() == RegisterSource::kString) { - std::string cls(info.first.ToString()); - std::string name(info.second.ToString()); - std::string full_name = cls + "->" + name; - HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name); - if (api_list != HiddenApiAccessFlags::kWhitelist) { - uses[full_name].push_back(ref); - } - } + std::map> named_uses; + for (auto it : concrete_uses_) { + MethodReference ref = it.first; + for (const ReflectAccessInfo& info : it.second) { + std::string cls(info.cls.ToString()); + std::string name(info.name.ToString()); + std::string full_name = cls + "->" + name; + HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name); + if (api_list != HiddenApiAccessFlags::kWhitelist) { + named_uses[full_name].push_back(ref); } } } - for (auto it : uses) { + for (auto it : named_uses) { ++stats->reflection_count; const std::string& full_name = it.first; HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name); diff --git a/tools/veridex/precise_hidden_api_finder.h b/tools/veridex/precise_hidden_api_finder.h index 22744a6f1f..1c4d0ae84e 100644 --- a/tools/veridex/precise_hidden_api_finder.h +++ b/tools/veridex/precise_hidden_api_finder.h @@ -45,9 +45,18 @@ class PreciseHiddenApiFinder { void Dump(std::ostream& os, HiddenApiStats* stats); private: + // Run over all methods of all dex files, and call `action` on each. + void RunInternal( + const std::vector>& resolvers, + const std::function& action); + + // Add uses found in method `ref`. + void AddUsesAt(const std::vector& accesses, MethodReference ref); + const HiddenApi& hidden_api_; - std::map>> field_uses_; - std::map>> method_uses_; + + std::map> concrete_uses_; + std::map> abstract_uses_; }; } // namespace art -- GitLab From 0aa1e7010610c7acb9f69da6dd5360570e218b9d Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Thu, 3 May 2018 13:52:20 -0700 Subject: [PATCH 328/749] Remove unnecessary references to libunwind. Bug: 79209464 Test: Builds. Change-Id: Ie8b0720da683ab8d40cba46690ed44c5c53efe88 --- Android.bp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Android.bp b/Android.bp index 32a96e13ba..59f7abf138 100644 --- a/Android.bp +++ b/Android.bp @@ -10,8 +10,6 @@ art_static_dependencies = [ "libz", "libbacktrace", "libcutils", - "libunwindbacktrace", - "libunwind", "libunwindstack", "libutils", "libbase", -- GitLab From d1fa440902e55b6b032a2c5c06a356558e882007 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 3 May 2018 15:43:13 +0100 Subject: [PATCH 329/749] ARM: Remove VIXL dependency from ArmManagedRegister. Also remove unnecesary DWARF includes. Motivation: Preparing to move JNI calling conventions to runtime/ to unify the GenericJNI frame creation with the JNI compiler. Test: Rely on TreeHugger. Change-Id: If8afc4a4fa41e41f0242962bb225b36633c1c153 --- .../utils/arm/jni_macro_assembler_arm_vixl.cc | 304 +++++++++--------- compiler/utils/arm/managed_register_arm.h | 40 +-- compiler/utils/arm64/managed_register_arm64.h | 1 - compiler/utils/mips/managed_register_mips.h | 1 - .../utils/mips64/managed_register_mips64.h | 1 - compiler/utils/x86/managed_register_x86.h | 1 - .../utils/x86_64/managed_register_x86_64.h | 1 - 7 files changed, 157 insertions(+), 192 deletions(-) diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc index 065c3de23c..2c428fac7e 100644 --- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc +++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc @@ -37,6 +37,29 @@ namespace arm { #define ___ asm_.GetVIXLAssembler()-> #endif +vixl::aarch32::Register AsVIXLRegister(ArmManagedRegister reg) { + CHECK(reg.IsCoreRegister()); + return vixl::aarch32::Register(reg.RegId()); +} + +static inline vixl::aarch32::SRegister AsVIXLSRegister(ArmManagedRegister reg) { + CHECK(reg.IsSRegister()); + return vixl::aarch32::SRegister(reg.RegId() - kNumberOfCoreRegIds); +} + +static inline vixl::aarch32::DRegister AsVIXLDRegister(ArmManagedRegister reg) { + CHECK(reg.IsDRegister()); + return vixl::aarch32::DRegister(reg.RegId() - kNumberOfCoreRegIds - kNumberOfSRegIds); +} + +static inline vixl::aarch32::Register AsVIXLRegisterPairLow(ArmManagedRegister reg) { + return vixl::aarch32::Register(reg.AsRegisterPairLow()); +} + +static inline vixl::aarch32::Register AsVIXLRegisterPairHigh(ArmManagedRegister reg) { + return vixl::aarch32::Register(reg.AsRegisterPairHigh()); +} + void ArmVIXLJNIMacroAssembler::FinalizeCode() { for (const std::unique_ptr< ArmVIXLJNIMacroAssembler::ArmException>& exception : exception_blocks_) { @@ -60,7 +83,7 @@ void ArmVIXLJNIMacroAssembler::BuildFrame(size_t frame_size, ArrayRef callee_save_regs, const ManagedRegisterEntrySpills& entry_spills) { CHECK_ALIGNED(frame_size, kStackAlignment); - CHECK(r0.Is(method_reg.AsArm().AsVIXLRegister())); + CHECK(r0.Is(AsVIXLRegister(method_reg.AsArm()))); // Push callee saves and link register. RegList core_spill_mask = 1 << LR; @@ -104,13 +127,13 @@ void ArmVIXLJNIMacroAssembler::BuildFrame(size_t frame_size, ManagedRegisterSpill spill = entry_spills.at(i); offset += spill.getSize(); } else if (reg.IsCoreRegister()) { - asm_.StoreToOffset(kStoreWord, reg.AsVIXLRegister(), sp, offset); + asm_.StoreToOffset(kStoreWord, AsVIXLRegister(reg), sp, offset); offset += 4; } else if (reg.IsSRegister()) { - asm_.StoreSToOffset(reg.AsVIXLSRegister(), sp, offset); + asm_.StoreSToOffset(AsVIXLSRegister(reg), sp, offset); offset += 4; } else if (reg.IsDRegister()) { - asm_.StoreDToOffset(reg.AsVIXLDRegister(), sp, offset); + asm_.StoreDToOffset(AsVIXLDRegister(reg), sp, offset); offset += 8; } } @@ -208,76 +231,71 @@ void ArmVIXLJNIMacroAssembler::Store(FrameOffset dest, ManagedRegister m_src, si } else if (src.IsCoreRegister()) { CHECK_EQ(4u, size); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(src.AsVIXLRegister()); - asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value()); + temps.Exclude(AsVIXLRegister(src)); + asm_.StoreToOffset(kStoreWord, AsVIXLRegister(src), sp, dest.Int32Value()); } else if (src.IsRegisterPair()) { CHECK_EQ(8u, size); - asm_.StoreToOffset(kStoreWord, src.AsVIXLRegisterPairLow(), sp, dest.Int32Value()); - asm_.StoreToOffset(kStoreWord, src.AsVIXLRegisterPairHigh(), sp, dest.Int32Value() + 4); + asm_.StoreToOffset(kStoreWord, AsVIXLRegisterPairLow(src), sp, dest.Int32Value()); + asm_.StoreToOffset(kStoreWord, AsVIXLRegisterPairHigh(src), sp, dest.Int32Value() + 4); } else if (src.IsSRegister()) { CHECK_EQ(4u, size); - asm_.StoreSToOffset(src.AsVIXLSRegister(), sp, dest.Int32Value()); + asm_.StoreSToOffset(AsVIXLSRegister(src), sp, dest.Int32Value()); } else { CHECK_EQ(8u, size); CHECK(src.IsDRegister()) << src; - asm_.StoreDToOffset(src.AsVIXLDRegister(), sp, dest.Int32Value()); + asm_.StoreDToOffset(AsVIXLDRegister(src), sp, dest.Int32Value()); } } void ArmVIXLJNIMacroAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) { - ArmManagedRegister src = msrc.AsArm(); - CHECK(src.IsCoreRegister()) << src; + vixl::aarch32::Register src = AsVIXLRegister(msrc.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(src.AsVIXLRegister()); - asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value()); + temps.Exclude(src); + asm_.StoreToOffset(kStoreWord, src, sp, dest.Int32Value()); } void ArmVIXLJNIMacroAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) { - ArmManagedRegister src = msrc.AsArm(); - CHECK(src.IsCoreRegister()) << src; + vixl::aarch32::Register src = AsVIXLRegister(msrc.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(src.AsVIXLRegister()); - asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value()); + temps.Exclude(src); + asm_.StoreToOffset(kStoreWord, src, sp, dest.Int32Value()); } void ArmVIXLJNIMacroAssembler::StoreSpanning(FrameOffset dest, ManagedRegister msrc, FrameOffset in_off, ManagedRegister mscratch) { - ArmManagedRegister src = msrc.AsArm(); - ArmManagedRegister scratch = mscratch.AsArm(); - asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value()); + vixl::aarch32::Register src = AsVIXLRegister(msrc.AsArm()); + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); + asm_.StoreToOffset(kStoreWord, src, sp, dest.Int32Value()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); - asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, in_off.Int32Value()); - asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, dest.Int32Value() + 4); + temps.Exclude(scratch); + asm_.LoadFromOffset(kLoadWord, scratch, sp, in_off.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value() + 4); } void ArmVIXLJNIMacroAssembler::CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) { - ArmManagedRegister scratch = mscratch.AsArm(); + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); - asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, src.Int32Value()); - asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, dest.Int32Value()); + temps.Exclude(scratch); + asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value()); } -void ArmVIXLJNIMacroAssembler::LoadRef(ManagedRegister dest, - ManagedRegister base, +void ArmVIXLJNIMacroAssembler::LoadRef(ManagedRegister mdest, + ManagedRegister mbase, MemberOffset offs, bool unpoison_reference) { - ArmManagedRegister dst = dest.AsArm(); - CHECK(dst.IsCoreRegister() && dst.IsCoreRegister()) << dst; + vixl::aarch32::Register dest = AsVIXLRegister(mdest.AsArm()); + vixl::aarch32::Register base = AsVIXLRegister(mbase.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(dst.AsVIXLRegister(), base.AsArm().AsVIXLRegister()); - asm_.LoadFromOffset(kLoadWord, - dst.AsVIXLRegister(), - base.AsArm().AsVIXLRegister(), - offs.Int32Value()); + temps.Exclude(dest, base); + asm_.LoadFromOffset(kLoadWord, dest, base, offs.Int32Value()); if (unpoison_reference) { - asm_.MaybeUnpoisonHeapReference(dst.AsVIXLRegister()); + asm_.MaybeUnpoisonHeapReference(dest); } } @@ -294,13 +312,12 @@ void ArmVIXLJNIMacroAssembler::LoadRawPtr(ManagedRegister dest ATTRIBUTE_UNUSED, void ArmVIXLJNIMacroAssembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm, - ManagedRegister scratch) { - ArmManagedRegister mscratch = scratch.AsArm(); - CHECK(mscratch.IsCoreRegister()) << mscratch; + ManagedRegister mscratch) { + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(mscratch.AsVIXLRegister()); - asm_.LoadImmediate(mscratch.AsVIXLRegister(), imm); - asm_.StoreToOffset(kStoreWord, mscratch.AsVIXLRegister(), sp, dest.Int32Value()); + temps.Exclude(scratch); + asm_.LoadImmediate(scratch, imm); + asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value()); } void ArmVIXLJNIMacroAssembler::Load(ManagedRegister m_dst, FrameOffset src, size_t size) { @@ -313,23 +330,21 @@ void ArmVIXLJNIMacroAssembler::LoadFromThread(ManagedRegister m_dst, return Load(m_dst.AsArm(), tr, src.Int32Value(), size); } -void ArmVIXLJNIMacroAssembler::LoadRawPtrFromThread(ManagedRegister m_dst, ThreadOffset32 offs) { - ArmManagedRegister dst = m_dst.AsArm(); - CHECK(dst.IsCoreRegister()) << dst; +void ArmVIXLJNIMacroAssembler::LoadRawPtrFromThread(ManagedRegister mdest, ThreadOffset32 offs) { + vixl::aarch32::Register dest = AsVIXLRegister(mdest.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(dst.AsVIXLRegister()); - asm_.LoadFromOffset(kLoadWord, dst.AsVIXLRegister(), tr, offs.Int32Value()); + temps.Exclude(dest); + asm_.LoadFromOffset(kLoadWord, dest, tr, offs.Int32Value()); } void ArmVIXLJNIMacroAssembler::CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset32 thr_offs, ManagedRegister mscratch) { - ArmManagedRegister scratch = mscratch.AsArm(); - CHECK(scratch.IsCoreRegister()) << scratch; + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); - asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), tr, thr_offs.Int32Value()); - asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, fr_offs.Int32Value()); + temps.Exclude(scratch); + asm_.LoadFromOffset(kLoadWord, scratch, tr, thr_offs.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, sp, fr_offs.Int32Value()); } void ArmVIXLJNIMacroAssembler::CopyRawPtrToThread(ThreadOffset32 thr_offs ATTRIBUTE_UNUSED, @@ -341,12 +356,11 @@ void ArmVIXLJNIMacroAssembler::CopyRawPtrToThread(ThreadOffset32 thr_offs ATTRIB void ArmVIXLJNIMacroAssembler::StoreStackOffsetToThread(ThreadOffset32 thr_offs, FrameOffset fr_offs, ManagedRegister mscratch) { - ArmManagedRegister scratch = mscratch.AsArm(); - CHECK(scratch.IsCoreRegister()) << scratch; + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); - asm_.AddConstant(scratch.AsVIXLRegister(), sp, fr_offs.Int32Value()); - asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), tr, thr_offs.Int32Value()); + temps.Exclude(scratch); + asm_.AddConstant(scratch, sp, fr_offs.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, tr, thr_offs.Int32Value()); } void ArmVIXLJNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) { @@ -363,43 +377,43 @@ void ArmVIXLJNIMacroAssembler::ZeroExtend(ManagedRegister mreg ATTRIBUTE_UNUSED, UNIMPLEMENTED(FATAL) << "no zero extension necessary for arm"; } -void ArmVIXLJNIMacroAssembler::Move(ManagedRegister m_dst, - ManagedRegister m_src, +void ArmVIXLJNIMacroAssembler::Move(ManagedRegister mdst, + ManagedRegister msrc, size_t size ATTRIBUTE_UNUSED) { - ArmManagedRegister dst = m_dst.AsArm(); - ArmManagedRegister src = m_src.AsArm(); + ArmManagedRegister dst = mdst.AsArm(); + ArmManagedRegister src = msrc.AsArm(); if (!dst.Equals(src)) { if (dst.IsCoreRegister()) { CHECK(src.IsCoreRegister()) << src; UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(dst.AsVIXLRegister()); - ___ Mov(dst.AsVIXLRegister(), src.AsVIXLRegister()); + temps.Exclude(AsVIXLRegister(dst)); + ___ Mov(AsVIXLRegister(dst), AsVIXLRegister(src)); } else if (dst.IsDRegister()) { if (src.IsDRegister()) { - ___ Vmov(F64, dst.AsVIXLDRegister(), src.AsVIXLDRegister()); + ___ Vmov(F64, AsVIXLDRegister(dst), AsVIXLDRegister(src)); } else { // VMOV Dn, Rlo, Rhi (Dn = {Rlo, Rhi}) CHECK(src.IsRegisterPair()) << src; - ___ Vmov(dst.AsVIXLDRegister(), src.AsVIXLRegisterPairLow(), src.AsVIXLRegisterPairHigh()); + ___ Vmov(AsVIXLDRegister(dst), AsVIXLRegisterPairLow(src), AsVIXLRegisterPairHigh(src)); } } else if (dst.IsSRegister()) { if (src.IsSRegister()) { - ___ Vmov(F32, dst.AsVIXLSRegister(), src.AsVIXLSRegister()); + ___ Vmov(F32, AsVIXLSRegister(dst), AsVIXLSRegister(src)); } else { // VMOV Sn, Rn (Sn = Rn) CHECK(src.IsCoreRegister()) << src; - ___ Vmov(dst.AsVIXLSRegister(), src.AsVIXLRegister()); + ___ Vmov(AsVIXLSRegister(dst), AsVIXLRegister(src)); } } else { CHECK(dst.IsRegisterPair()) << dst; CHECK(src.IsRegisterPair()) << src; // Ensure that the first move doesn't clobber the input of the second. if (src.AsRegisterPairHigh() != dst.AsRegisterPairLow()) { - ___ Mov(dst.AsVIXLRegisterPairLow(), src.AsVIXLRegisterPairLow()); - ___ Mov(dst.AsVIXLRegisterPairHigh(), src.AsVIXLRegisterPairHigh()); + ___ Mov(AsVIXLRegisterPairLow(dst), AsVIXLRegisterPairLow(src)); + ___ Mov(AsVIXLRegisterPairHigh(dst), AsVIXLRegisterPairHigh(src)); } else { - ___ Mov(dst.AsVIXLRegisterPairHigh(), src.AsVIXLRegisterPairHigh()); - ___ Mov(dst.AsVIXLRegisterPairLow(), src.AsVIXLRegisterPairLow()); + ___ Mov(AsVIXLRegisterPairHigh(dst), AsVIXLRegisterPairHigh(src)); + ___ Mov(AsVIXLRegisterPairLow(dst), AsVIXLRegisterPairLow(src)); } } } @@ -407,21 +421,20 @@ void ArmVIXLJNIMacroAssembler::Move(ManagedRegister m_dst, void ArmVIXLJNIMacroAssembler::Copy(FrameOffset dest, FrameOffset src, - ManagedRegister scratch, + ManagedRegister mscratch, size_t size) { - ArmManagedRegister temp = scratch.AsArm(); - CHECK(temp.IsCoreRegister()) << temp; + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); CHECK(size == 4 || size == 8) << size; UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(temp.AsVIXLRegister()); + temps.Exclude(scratch); if (size == 4) { - asm_.LoadFromOffset(kLoadWord, temp.AsVIXLRegister(), sp, src.Int32Value()); - asm_.StoreToOffset(kStoreWord, temp.AsVIXLRegister(), sp, dest.Int32Value()); + asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value()); } else if (size == 8) { - asm_.LoadFromOffset(kLoadWord, temp.AsVIXLRegister(), sp, src.Int32Value()); - asm_.StoreToOffset(kStoreWord, temp.AsVIXLRegister(), sp, dest.Int32Value()); - asm_.LoadFromOffset(kLoadWord, temp.AsVIXLRegister(), sp, src.Int32Value() + 4); - asm_.StoreToOffset(kStoreWord, temp.AsVIXLRegister(), sp, dest.Int32Value() + 4); + asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value()); + asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value() + 4); + asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value() + 4); } } @@ -471,48 +484,44 @@ void ArmVIXLJNIMacroAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg, FrameOffset handle_scope_offset, ManagedRegister min_reg, bool null_allowed) { - ArmManagedRegister out_reg = mout_reg.AsArm(); - ArmManagedRegister in_reg = min_reg.AsArm(); - CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg; - CHECK(out_reg.IsCoreRegister()) << out_reg; + vixl::aarch32::Register out_reg = AsVIXLRegister(mout_reg.AsArm()); + vixl::aarch32::Register in_reg = + min_reg.AsArm().IsNoRegister() ? vixl::aarch32::Register() : AsVIXLRegister(min_reg.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(out_reg.AsVIXLRegister()); + temps.Exclude(out_reg); if (null_allowed) { // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is // the address in the handle scope holding the reference. // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset) - if (in_reg.IsNoRegister()) { - asm_.LoadFromOffset(kLoadWord, - out_reg.AsVIXLRegister(), - sp, - handle_scope_offset.Int32Value()); + if (!in_reg.IsValid()) { + asm_.LoadFromOffset(kLoadWord, out_reg, sp, handle_scope_offset.Int32Value()); in_reg = out_reg; } - temps.Exclude(in_reg.AsVIXLRegister()); - ___ Cmp(in_reg.AsVIXLRegister(), 0); + temps.Exclude(in_reg); + ___ Cmp(in_reg, 0); if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value())) { - if (!out_reg.Equals(in_reg)) { + if (!out_reg.Is(in_reg)) { ExactAssemblyScope guard(asm_.GetVIXLAssembler(), 3 * vixl32::kMaxInstructionSizeInBytes, CodeBufferCheckScope::kMaximumSize); ___ it(eq, 0xc); - ___ mov(eq, out_reg.AsVIXLRegister(), 0); - asm_.AddConstantInIt(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne); + ___ mov(eq, out_reg, 0); + asm_.AddConstantInIt(out_reg, sp, handle_scope_offset.Int32Value(), ne); } else { ExactAssemblyScope guard(asm_.GetVIXLAssembler(), 2 * vixl32::kMaxInstructionSizeInBytes, CodeBufferCheckScope::kMaximumSize); ___ it(ne, 0x8); - asm_.AddConstantInIt(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne); + asm_.AddConstantInIt(out_reg, sp, handle_scope_offset.Int32Value(), ne); } } else { // TODO: Implement this (old arm assembler would have crashed here). UNIMPLEMENTED(FATAL); } } else { - asm_.AddConstant(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value()); + asm_.AddConstant(out_reg, sp, handle_scope_offset.Int32Value()); } } @@ -520,31 +529,30 @@ void ArmVIXLJNIMacroAssembler::CreateHandleScopeEntry(FrameOffset out_off, FrameOffset handle_scope_offset, ManagedRegister mscratch, bool null_allowed) { - ArmManagedRegister scratch = mscratch.AsArm(); - CHECK(scratch.IsCoreRegister()) << scratch; + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); + temps.Exclude(scratch); if (null_allowed) { - asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value()); + asm_.LoadFromOffset(kLoadWord, scratch, sp, handle_scope_offset.Int32Value()); // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is // the address in the handle scope holding the reference. // e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset) - ___ Cmp(scratch.AsVIXLRegister(), 0); + ___ Cmp(scratch, 0); if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value())) { ExactAssemblyScope guard(asm_.GetVIXLAssembler(), 2 * vixl32::kMaxInstructionSizeInBytes, CodeBufferCheckScope::kMaximumSize); ___ it(ne, 0x8); - asm_.AddConstantInIt(scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne); + asm_.AddConstantInIt(scratch, sp, handle_scope_offset.Int32Value(), ne); } else { // TODO: Implement this (old arm assembler would have crashed here). UNIMPLEMENTED(FATAL); } } else { - asm_.AddConstant(scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value()); + asm_.AddConstant(scratch, sp, handle_scope_offset.Int32Value()); } - asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, out_off.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, sp, out_off.Int32Value()); } void ArmVIXLJNIMacroAssembler::LoadReferenceFromHandleScope( @@ -566,32 +574,23 @@ void ArmVIXLJNIMacroAssembler::VerifyObject(FrameOffset src ATTRIBUTE_UNUSED, void ArmVIXLJNIMacroAssembler::Call(ManagedRegister mbase, Offset offset, ManagedRegister mscratch) { - ArmManagedRegister base = mbase.AsArm(); - ArmManagedRegister scratch = mscratch.AsArm(); - CHECK(base.IsCoreRegister()) << base; - CHECK(scratch.IsCoreRegister()) << scratch; + vixl::aarch32::Register base = AsVIXLRegister(mbase.AsArm()); + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); - asm_.LoadFromOffset(kLoadWord, - scratch.AsVIXLRegister(), - base.AsVIXLRegister(), - offset.Int32Value()); - ___ Blx(scratch.AsVIXLRegister()); + temps.Exclude(scratch); + asm_.LoadFromOffset(kLoadWord, scratch, base, offset.Int32Value()); + ___ Blx(scratch); // TODO: place reference map on call. } void ArmVIXLJNIMacroAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) { - ArmManagedRegister scratch = mscratch.AsArm(); - CHECK(scratch.IsCoreRegister()) << scratch; + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); + temps.Exclude(scratch); // Call *(*(SP + base) + offset) - asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, base.Int32Value()); - asm_.LoadFromOffset(kLoadWord, - scratch.AsVIXLRegister(), - scratch.AsVIXLRegister(), - offset.Int32Value()); - ___ Blx(scratch.AsVIXLRegister()); + asm_.LoadFromOffset(kLoadWord, scratch, sp, base.Int32Value()); + asm_.LoadFromOffset(kLoadWord, scratch, scratch, offset.Int32Value()); + ___ Blx(scratch); // TODO: place reference map on call } @@ -602,8 +601,8 @@ void ArmVIXLJNIMacroAssembler::CallFromThread(ThreadOffset32 offset ATTRIBUTE_UN void ArmVIXLJNIMacroAssembler::GetCurrentThread(ManagedRegister mtr) { UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(mtr.AsArm().AsVIXLRegister()); - ___ Mov(mtr.AsArm().AsVIXLRegister(), tr); + temps.Exclude(AsVIXLRegister(mtr.AsArm())); + ___ Mov(AsVIXLRegister(mtr.AsArm()), tr); } void ArmVIXLJNIMacroAssembler::GetCurrentThread(FrameOffset dest_offset, @@ -611,19 +610,19 @@ void ArmVIXLJNIMacroAssembler::GetCurrentThread(FrameOffset dest_offset, asm_.StoreToOffset(kStoreWord, tr, sp, dest_offset.Int32Value()); } -void ArmVIXLJNIMacroAssembler::ExceptionPoll(ManagedRegister m_scratch, size_t stack_adjust) { +void ArmVIXLJNIMacroAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) { CHECK_ALIGNED(stack_adjust, kStackAlignment); - ArmManagedRegister scratch = m_scratch.AsArm(); + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); + temps.Exclude(scratch); exception_blocks_.emplace_back( - new ArmVIXLJNIMacroAssembler::ArmException(scratch, stack_adjust)); + new ArmVIXLJNIMacroAssembler::ArmException(mscratch.AsArm(), stack_adjust)); asm_.LoadFromOffset(kLoadWord, - scratch.AsVIXLRegister(), + scratch, tr, Thread::ExceptionOffset().Int32Value()); - ___ Cmp(scratch.AsVIXLRegister(), 0); + ___ Cmp(scratch, 0); vixl32::Label* label = exception_blocks_.back()->Entry(); ___ BPreferNear(ne, label); // TODO: think about using CBNZ here. @@ -640,19 +639,18 @@ void ArmVIXLJNIMacroAssembler::Jump(JNIMacroLabel* label) { void ArmVIXLJNIMacroAssembler::Jump(JNIMacroLabel* label, JNIMacroUnaryCondition condition, - ManagedRegister test) { + ManagedRegister mtest) { CHECK(label != nullptr); + vixl::aarch32::Register test = AsVIXLRegister(mtest.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(test.AsArm().AsVIXLRegister()); + temps.Exclude(test); switch (condition) { case JNIMacroUnaryCondition::kZero: - ___ CompareAndBranchIfZero(test.AsArm().AsVIXLRegister(), - ArmVIXLJNIMacroLabel::Cast(label)->AsArm()); + ___ CompareAndBranchIfZero(test, ArmVIXLJNIMacroLabel::Cast(label)->AsArm()); break; case JNIMacroUnaryCondition::kNotZero: - ___ CompareAndBranchIfNonZero(test.AsArm().AsVIXLRegister(), - ArmVIXLJNIMacroLabel::Cast(label)->AsArm()); + ___ CompareAndBranchIfNonZero(test, ArmVIXLJNIMacroLabel::Cast(label)->AsArm()); break; default: LOG(FATAL) << "Not implemented unary condition: " << static_cast(condition); @@ -672,12 +670,13 @@ void ArmVIXLJNIMacroAssembler::EmitExceptionPoll( DecreaseFrameSize(exception->stack_adjust_); } + vixl::aarch32::Register scratch = AsVIXLRegister(exception->scratch_); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(exception->scratch_.AsVIXLRegister()); + temps.Exclude(scratch); // Pass exception object as argument. // Don't care about preserving r0 as this won't return. - ___ Mov(r0, exception->scratch_.AsVIXLRegister()); - temps.Include(exception->scratch_.AsVIXLRegister()); + ___ Mov(r0, scratch); + temps.Include(scratch); // TODO: check that exception->scratch_ is dead by this point. vixl32::Register temp = temps.Acquire(); ___ Ldr(temp, @@ -698,26 +697,27 @@ void ArmVIXLJNIMacroAssembler::Load(ArmManagedRegister if (dest.IsNoRegister()) { CHECK_EQ(0u, size) << dest; } else if (dest.IsCoreRegister()) { - CHECK(!dest.AsVIXLRegister().Is(sp)) << dest; + vixl::aarch32::Register dst = AsVIXLRegister(dest); + CHECK(!dst.Is(sp)) << dest; UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(dest.AsVIXLRegister()); + temps.Exclude(dst); if (size == 1u) { - ___ Ldrb(dest.AsVIXLRegister(), MemOperand(base, offset)); + ___ Ldrb(dst, MemOperand(base, offset)); } else { CHECK_EQ(4u, size) << dest; - ___ Ldr(dest.AsVIXLRegister(), MemOperand(base, offset)); + ___ Ldr(dst, MemOperand(base, offset)); } } else if (dest.IsRegisterPair()) { CHECK_EQ(8u, size) << dest; - ___ Ldr(dest.AsVIXLRegisterPairLow(), MemOperand(base, offset)); - ___ Ldr(dest.AsVIXLRegisterPairHigh(), MemOperand(base, offset + 4)); + ___ Ldr(AsVIXLRegisterPairLow(dest), MemOperand(base, offset)); + ___ Ldr(AsVIXLRegisterPairHigh(dest), MemOperand(base, offset + 4)); } else if (dest.IsSRegister()) { - ___ Vldr(dest.AsVIXLSRegister(), MemOperand(base, offset)); + ___ Vldr(AsVIXLSRegister(dest), MemOperand(base, offset)); } else { CHECK(dest.IsDRegister()) << dest; - ___ Vldr(dest.AsVIXLDRegister(), MemOperand(base, offset)); + ___ Vldr(AsVIXLDRegister(dest), MemOperand(base, offset)); } } diff --git a/compiler/utils/arm/managed_register_arm.h b/compiler/utils/arm/managed_register_arm.h index 26f23b2ed6..e42572dc32 100644 --- a/compiler/utils/arm/managed_register_arm.h +++ b/compiler/utils/arm/managed_register_arm.h @@ -20,15 +20,8 @@ #include #include "constants_arm.h" -#include "debug/dwarf/register.h" #include "utils/managed_register.h" -// TODO(VIXL): Make VIXL compile with -Wshadow. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshadow" -#include "aarch32/macro-assembler-aarch32.h" -#pragma GCC diagnostic pop - namespace art { namespace arm { @@ -97,31 +90,16 @@ class ArmManagedRegister : public ManagedRegister { return static_cast(id_); } - vixl::aarch32::Register AsVIXLRegister() const { - CHECK(IsCoreRegister()); - return vixl::aarch32::Register(id_); - } - constexpr SRegister AsSRegister() const { CHECK(IsSRegister()); return static_cast(id_ - kNumberOfCoreRegIds); } - vixl::aarch32::SRegister AsVIXLSRegister() const { - CHECK(IsSRegister()); - return vixl::aarch32::SRegister(id_ - kNumberOfCoreRegIds); - } - constexpr DRegister AsDRegister() const { CHECK(IsDRegister()); return static_cast(id_ - kNumberOfCoreRegIds - kNumberOfSRegIds); } - vixl::aarch32::DRegister AsVIXLDRegister() const { - CHECK(IsDRegister()); - return vixl::aarch32::DRegister(id_ - kNumberOfCoreRegIds - kNumberOfSRegIds); - } - constexpr SRegister AsOverlappingDRegisterLow() const { CHECK(IsOverlappingDRegister()); DRegister d_reg = AsDRegister(); @@ -150,20 +128,12 @@ class ArmManagedRegister : public ManagedRegister { return FromRegId(AllocIdLow()).AsCoreRegister(); } - vixl::aarch32::Register AsVIXLRegisterPairLow() const { - return vixl::aarch32::Register(AsRegisterPairLow()); - } - constexpr Register AsRegisterPairHigh() const { CHECK(IsRegisterPair()); // Appropriate mapping of register ids allows to use AllocIdHigh(). return FromRegId(AllocIdHigh()).AsCoreRegister(); } - vixl::aarch32::Register AsVIXLRegisterPairHigh() const { - return vixl::aarch32::Register(AsRegisterPairHigh()); - } - constexpr bool IsCoreRegister() const { CHECK(IsValidManagedRegister()); return (0 <= id_) && (id_ < kNumberOfCoreRegIds); @@ -255,16 +225,16 @@ class ArmManagedRegister : public ManagedRegister { return FromDRegister(static_cast(r)); } - private: - constexpr bool IsValidManagedRegister() const { - return (0 <= id_) && (id_ < kNumberOfRegIds); - } - int RegId() const { CHECK(!IsNoRegister()); return id_; } + private: + constexpr bool IsValidManagedRegister() const { + return (0 <= id_) && (id_ < kNumberOfRegIds); + } + int AllocId() const { CHECK(IsValidManagedRegister() && !IsOverlappingDRegister() && !IsRegisterPair()); diff --git a/compiler/utils/arm64/managed_register_arm64.h b/compiler/utils/arm64/managed_register_arm64.h index 9ce7ec9a97..0513890aa8 100644 --- a/compiler/utils/arm64/managed_register_arm64.h +++ b/compiler/utils/arm64/managed_register_arm64.h @@ -20,7 +20,6 @@ #include #include "arch/arm64/registers_arm64.h" -#include "debug/dwarf/register.h" #include "utils/managed_register.h" namespace art { diff --git a/compiler/utils/mips/managed_register_mips.h b/compiler/utils/mips/managed_register_mips.h index 66204e70e3..18d5821e61 100644 --- a/compiler/utils/mips/managed_register_mips.h +++ b/compiler/utils/mips/managed_register_mips.h @@ -18,7 +18,6 @@ #define ART_COMPILER_UTILS_MIPS_MANAGED_REGISTER_MIPS_H_ #include "constants_mips.h" -#include "debug/dwarf/register.h" #include "utils/managed_register.h" namespace art { diff --git a/compiler/utils/mips64/managed_register_mips64.h b/compiler/utils/mips64/managed_register_mips64.h index 3980199b1e..94166d32b7 100644 --- a/compiler/utils/mips64/managed_register_mips64.h +++ b/compiler/utils/mips64/managed_register_mips64.h @@ -18,7 +18,6 @@ #define ART_COMPILER_UTILS_MIPS64_MANAGED_REGISTER_MIPS64_H_ #include "constants_mips64.h" -#include "debug/dwarf/register.h" #include "utils/managed_register.h" namespace art { diff --git a/compiler/utils/x86/managed_register_x86.h b/compiler/utils/x86/managed_register_x86.h index c0c2b650e9..8810bfa2f1 100644 --- a/compiler/utils/x86/managed_register_x86.h +++ b/compiler/utils/x86/managed_register_x86.h @@ -18,7 +18,6 @@ #define ART_COMPILER_UTILS_X86_MANAGED_REGISTER_X86_H_ #include "constants_x86.h" -#include "debug/dwarf/register.h" #include "utils/managed_register.h" namespace art { diff --git a/compiler/utils/x86_64/managed_register_x86_64.h b/compiler/utils/x86_64/managed_register_x86_64.h index 32af672670..6760882965 100644 --- a/compiler/utils/x86_64/managed_register_x86_64.h +++ b/compiler/utils/x86_64/managed_register_x86_64.h @@ -18,7 +18,6 @@ #define ART_COMPILER_UTILS_X86_64_MANAGED_REGISTER_X86_64_H_ #include "constants_x86_64.h" -#include "debug/dwarf/register.h" #include "utils/managed_register.h" namespace art { -- GitLab From 7d2975388c111f342c10cdcef3f3ea604bf6865c Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Fri, 4 May 2018 09:31:45 +0100 Subject: [PATCH 330/749] ART: Disable 716-jli-jit-samples for jvmti stress tests Bug: 37272822 Test: art/test.py --host --jvmti-stress -r -t 716 Test: art/test.py --host --redefine-stress -r -t 716 Change-Id: I77c3962b01701549c93fb33028dfcf87890373e1 --- test/knownfailures.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/knownfailures.json b/test/knownfailures.json index e109bf5a8f..f3137587f6 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -412,10 +412,11 @@ ".*methodhandle.*", ".*method-handle.*", ".*varhandle.*", - ".*var-handle.*" + ".*var-handle.*", + "716-jli-jit-samples" ], "description": [ - "Tests that use invoke-polymorphic/invoke-custom which is not yet supported by", + "Tests for bytecodes introduced after DEX version 037 that are unsupported by", "dexter/slicer." ], "bug": "b/37272822", -- GitLab From 9a6ca9f645c76e9081d8de39e6d98377e208a650 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 4 May 2018 13:06:55 +0100 Subject: [PATCH 331/749] MIPS: Skip output verification for assembler tests. These tests were taking too much time, skipping the output verification brings it down from ~60s to ~10s per test. Test: m test-art-host-gtest Bug: 73903608 Change-Id: Ifd55c8013dea92de631e7c033111959a794759f2 --- compiler/utils/mips/assembler_mips32r5_test.cc | 10 ++++++++++ compiler/utils/mips/assembler_mips32r6_test.cc | 10 ++++++++++ compiler/utils/mips/assembler_mips_test.cc | 10 ++++++++++ compiler/utils/mips64/assembler_mips64_test.cc | 10 ++++++++++ 4 files changed, 40 insertions(+) diff --git a/compiler/utils/mips/assembler_mips32r5_test.cc b/compiler/utils/mips/assembler_mips32r5_test.cc index 9a69ffd3dd..0f858926df 100644 --- a/compiler/utils/mips/assembler_mips32r5_test.cc +++ b/compiler/utils/mips/assembler_mips32r5_test.cc @@ -45,6 +45,16 @@ class AssemblerMIPS32r5Test : public AssemblerTest Base; + // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<> + // and reimplement it without the verification against `assembly_string`. b/73903608 + void DriverStr(const std::string& assembly_string ATTRIBUTE_UNUSED, + const std::string& test_name ATTRIBUTE_UNUSED) { + GetAssembler()->FinalizeCode(); + std::vector data(GetAssembler()->CodeSize()); + MemoryRegion code(data.data(), data.size()); + GetAssembler()->FinalizeInstructions(code); + } + AssemblerMIPS32r5Test() : instruction_set_features_(MipsInstructionSetFeatures::FromVariant("mips32r5", nullptr)) { } diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc index 691c33f3e7..3d876ca613 100644 --- a/compiler/utils/mips/assembler_mips32r6_test.cc +++ b/compiler/utils/mips/assembler_mips32r6_test.cc @@ -45,6 +45,16 @@ class AssemblerMIPS32r6Test : public AssemblerTest Base; + // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<> + // and reimplement it without the verification against `assembly_string`. b/73903608 + void DriverStr(const std::string& assembly_string ATTRIBUTE_UNUSED, + const std::string& test_name ATTRIBUTE_UNUSED) { + GetAssembler()->FinalizeCode(); + std::vector data(GetAssembler()->CodeSize()); + MemoryRegion code(data.data(), data.size()); + GetAssembler()->FinalizeInstructions(code); + } + AssemblerMIPS32r6Test() : instruction_set_features_(MipsInstructionSetFeatures::FromVariant("mips32r6", nullptr)) { } diff --git a/compiler/utils/mips/assembler_mips_test.cc b/compiler/utils/mips/assembler_mips_test.cc index b027d3a549..f94d074299 100644 --- a/compiler/utils/mips/assembler_mips_test.cc +++ b/compiler/utils/mips/assembler_mips_test.cc @@ -43,6 +43,16 @@ class AssemblerMIPSTest : public AssemblerTest Base; + // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<> + // and reimplement it without the verification against `assembly_string`. b/73903608 + void DriverStr(const std::string& assembly_string ATTRIBUTE_UNUSED, + const std::string& test_name ATTRIBUTE_UNUSED) { + GetAssembler()->FinalizeCode(); + std::vector data(GetAssembler()->CodeSize()); + MemoryRegion code(data.data(), data.size()); + GetAssembler()->FinalizeInstructions(code); + } + protected: // Get the typically used name for this architecture, e.g., aarch64, x86-64, ... std::string GetArchitectureString() OVERRIDE { diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc index fb5f12be93..a53ff7cc2b 100644 --- a/compiler/utils/mips64/assembler_mips64_test.cc +++ b/compiler/utils/mips64/assembler_mips64_test.cc @@ -48,6 +48,16 @@ class AssemblerMIPS64Test : public AssemblerTest Base; + // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<> + // and reimplement it without the verification against `assembly_string`. b/73903608 + void DriverStr(const std::string& assembly_string ATTRIBUTE_UNUSED, + const std::string& test_name ATTRIBUTE_UNUSED) { + GetAssembler()->FinalizeCode(); + std::vector data(GetAssembler()->CodeSize()); + MemoryRegion code(data.data(), data.size()); + GetAssembler()->FinalizeInstructions(code); + } + AssemblerMIPS64Test() : instruction_set_features_(Mips64InstructionSetFeatures::FromVariant("default", nullptr)) {} -- GitLab From b09abb2f8a988ccc95d91dc8624577188c771bc0 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 3 May 2018 11:51:22 -0700 Subject: [PATCH 332/749] Revert "ART: Support per PID stack trace files." Remove leftovers. This reverts commit 84695aef89a3c42ea81c23f0590ae2ceca09ce6f. Bug: 32064548 Bug: 77288304 Test: m test-art-host Change-Id: I9b6f7f7daddd75ff47c7b17a91738858f0023605 --- test/etc/run-test-jar | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index be1296b990..81e77be547 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -80,11 +80,6 @@ DEX2OAT_TIMEOUT="300" # 5 mins # The *hard* timeout where we really start trying to kill the dex2oat. DEX2OAT_RT_TIMEOUT="360" # 6 mins -# if "y", set -Xstacktracedir and inform the test of its location. When -# this is set, stack trace dumps (from signal 3) will be written to a file -# under this directory instead of stdout. -SET_STACK_TRACE_DUMP_DIR="n" - # if "y", run 'sync' before dalvikvm to make sure all files from # build step (e.g. dex2oat) were finished writing. SYNC_BEFORE_RUN="n" @@ -364,9 +359,6 @@ while true; do elif [ "x$1" = "x--random-profile" ]; then RANDOM_PROFILE="y" shift - elif [ "x$1" = "x--set-stack-trace-dump-dir" ]; then - SET_STACK_TRACE_DUMP_DIR="y" - shift elif expr "x$1" : "x--" >/dev/null 2>&1; then echo "unknown $0 option: $1" 1>&2 exit 1 @@ -375,22 +367,12 @@ while true; do fi done -mkdir_locations="" - if [ "$USE_JVM" = "n" ]; then FLAGS="${FLAGS} ${ANDROID_FLAGS}" for feature in ${EXPERIMENTAL}; do FLAGS="${FLAGS} -Xexperimental:${feature} -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:${feature}" COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xexperimental:${feature}" done - - if [ "$SET_STACK_TRACE_DUMP_DIR" = "y" ]; then - # Note that DEX_LOCATION is used as a proxy for tmpdir throughout this - # file (it will be under the test specific folder). - mkdir_locations="${mkdir_locations} $DEX_LOCATION/stack_traces" - FLAGS="${FLAGS} -Xstacktracedir:$DEX_LOCATION/stack_traces" - ARGS="${ARGS} --stack-trace-dir $DEX_LOCATION/stack_traces" - fi fi if [ "x$1" = "x" ] ; then @@ -684,7 +666,7 @@ profman_cmdline="true" dex2oat_cmdline="true" vdex_cmdline="true" dm_cmdline="true" -mkdir_locations="${mkdir_locations} ${DEX_LOCATION}/dalvik-cache/$ISA" +mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA" strip_cmdline="true" sync_cmdline="true" -- GitLab From 53af040f8c1a817dcb1e727a3e83baab3449569a Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 3 May 2018 10:40:37 -0700 Subject: [PATCH 333/749] ART: Remove tombstoned parameters These are no longer supported. Always try to use tombstoned when on a device. Bug: 77288304 Test: m test-art-host Test: Device boots Test: manual ANR dumps work Change-Id: Iffd3287432becfc7982cdcb9a0cfe44f0c5b5143 --- runtime/parsed_options.cc | 7 ----- runtime/runtime.cc | 9 +----- runtime/runtime.h | 8 ------ runtime/runtime_options.def | 2 -- runtime/signal_catcher.cc | 56 ++++++------------------------------- runtime/signal_catcher.h | 19 +------------ 6 files changed, 11 insertions(+), 90 deletions(-) diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 5518eb2c49..3aa481af8c 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -252,12 +252,6 @@ std::unique_ptr ParsedOptions::MakeParser(bool ignore_unrecognize .Define("-Xstackdumplockprofthreshold:_") .WithType() .IntoKey(M::StackDumpLockProfThreshold) - .Define("-Xusetombstonedtraces") - .WithValue(true) - .IntoKey(M::UseTombstonedTraces) - .Define("-Xstacktracefile:_") - .WithType() - .IntoKey(M::StackTraceFile) .Define("-Xmethod-trace") .IntoKey(M::MethodTrace) .Define("-Xmethod-trace-file:_") @@ -699,7 +693,6 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, "The following Dalvik options are supported:\n"); UsageMessage(stream, " -Xzygote\n"); UsageMessage(stream, " -Xjnitrace:substring (eg NativeClass or nativeMethod)\n"); - UsageMessage(stream, " -Xstacktracefile:\n"); UsageMessage(stream, " -Xgc:[no]preverify\n"); UsageMessage(stream, " -Xgc:[no]postverify\n"); UsageMessage(stream, " -XX:HeapGrowthLimit=N\n"); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index d12a976be8..7823014781 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -232,7 +232,6 @@ Runtime::Runtime() intern_table_(nullptr), class_linker_(nullptr), signal_catcher_(nullptr), - use_tombstoned_traces_(false), java_vm_(nullptr), fault_message_lock_("Fault message lock"), fault_message_(""), @@ -904,7 +903,7 @@ void Runtime::InitNonZygoteOrPostFork( void Runtime::StartSignalCatcher() { if (!is_zygote_) { - signal_catcher_ = new SignalCatcher(stack_trace_file_, use_tombstoned_traces_); + signal_catcher_ = new SignalCatcher(); } } @@ -1152,12 +1151,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { abort_ = runtime_options.GetOrDefault(Opt::HookAbort); default_stack_size_ = runtime_options.GetOrDefault(Opt::StackSize); - use_tombstoned_traces_ = runtime_options.GetOrDefault(Opt::UseTombstonedTraces); -#if !defined(ART_TARGET_ANDROID) - CHECK(!use_tombstoned_traces_) - << "-Xusetombstonedtraces is only supported in an Android environment"; -#endif - stack_trace_file_ = runtime_options.ReleaseOrDefault(Opt::StackTraceFile); compiler_executable_ = runtime_options.ReleaseOrDefault(Opt::Compiler); compiler_options_ = runtime_options.ReleaseOrDefault(Opt::CompilerOptions); diff --git a/runtime/runtime.h b/runtime/runtime.h index 1b7663cbdf..56d95e0b63 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -871,14 +871,6 @@ class Runtime { SignalCatcher* signal_catcher_; - // If true, the runtime will connect to tombstoned via a socket to - // request an open file descriptor to write its traces to. - bool use_tombstoned_traces_; - - // Location to which traces must be written on SIGQUIT. Only used if - // tombstoned_traces_ == false. - std::string stack_trace_file_; - std::unique_ptr java_vm_; std::unique_ptr jit_; diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 4121ad69ed..427385d914 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -103,8 +103,6 @@ RUNTIME_OPTIONS_KEY (Unit, ForceNativeBridge) RUNTIME_OPTIONS_KEY (LogVerbosity, Verbose) RUNTIME_OPTIONS_KEY (unsigned int, LockProfThreshold) RUNTIME_OPTIONS_KEY (unsigned int, StackDumpLockProfThreshold) -RUNTIME_OPTIONS_KEY (bool, UseTombstonedTraces, false) -RUNTIME_OPTIONS_KEY (std::string, StackTraceFile) RUNTIME_OPTIONS_KEY (Unit, MethodTrace) RUNTIME_OPTIONS_KEY (std::string, MethodTraceFile, "/data/misc/trace/method-trace-file.bin") RUNTIME_OPTIONS_KEY (unsigned int, MethodTraceFileSize, 10 * MB) diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc index d590ad5cc6..f4a27b8397 100644 --- a/runtime/signal_catcher.cc +++ b/runtime/signal_catcher.cc @@ -73,19 +73,10 @@ static void DumpCmdLine(std::ostream& os) { #endif } -SignalCatcher::SignalCatcher(const std::string& stack_trace_file, - bool use_tombstoned_stack_trace_fd) - : stack_trace_file_(stack_trace_file), - use_tombstoned_stack_trace_fd_(use_tombstoned_stack_trace_fd), - lock_("SignalCatcher lock"), +SignalCatcher::SignalCatcher() + : lock_("SignalCatcher lock"), cond_("SignalCatcher::cond_", lock_), thread_(nullptr) { -#if !defined(ART_TARGET_ANDROID) - // We're not running on Android, so we can't communicate with tombstoned - // to ask for an open file. - CHECK(!use_tombstoned_stack_trace_fd_); -#endif - SetHaltFlag(false); // Create a raw pthread; its start routine will attach to the runtime. @@ -116,37 +107,11 @@ bool SignalCatcher::ShouldHalt() { return halt_; } -bool SignalCatcher::OpenStackTraceFile(android::base::unique_fd* tombstone_fd, - android::base::unique_fd* output_fd) { - if (use_tombstoned_stack_trace_fd_) { -#if defined(ART_TARGET_ANDROID) - return tombstoned_connect(getpid(), tombstone_fd, output_fd, kDebuggerdJavaBacktrace); -#else - UNUSED(tombstone_fd); - UNUSED(output_fd); -#endif - } - - // The runtime is not configured to dump traces to a file, will LOG(INFO) - // instead. - if (stack_trace_file_.empty()) { - return false; - } - - int fd = open(stack_trace_file_.c_str(), O_APPEND | O_CREAT | O_WRONLY, 0666); - if (fd == -1) { - PLOG(ERROR) << "Unable to open stack trace file '" << stack_trace_file_ << "'"; - return false; - } - - output_fd->reset(fd); - return true; -} - void SignalCatcher::Output(const std::string& s) { +#if defined(ART_TARGET_ANDROID) android::base::unique_fd tombstone_fd; android::base::unique_fd output_fd; - if (!OpenStackTraceFile(&tombstone_fd, &output_fd)) { + if (!tombstoned_connect(getpid(), &tombstone_fd, &output_fd, kDebuggerdJavaBacktrace)) { LOG(INFO) << s; return; } @@ -161,19 +126,16 @@ void SignalCatcher::Output(const std::string& s) { file->Erase(); } - const std::string output_path_msg = (use_tombstoned_stack_trace_fd_) ? - "[tombstoned]" : stack_trace_file_; - if (success) { - LOG(INFO) << "Wrote stack traces to '" << output_path_msg << "'"; + LOG(INFO) << "Wrote stack traces to tombstoned"; } else { - PLOG(ERROR) << "Failed to write stack traces to '" << output_path_msg << "'"; + PLOG(ERROR) << "Failed to write stack traces to tombstoned"; } - -#if defined(ART_TARGET_ANDROID) - if (use_tombstoned_stack_trace_fd_ && !tombstoned_notify_completion(tombstone_fd)) { + if (!tombstoned_notify_completion(tombstone_fd)) { PLOG(WARNING) << "Unable to notify tombstoned of dump completion"; } +#else + LOG(INFO) << s; #endif } diff --git a/runtime/signal_catcher.h b/runtime/signal_catcher.h index 8a2a7289de..46eae7e50e 100644 --- a/runtime/signal_catcher.h +++ b/runtime/signal_catcher.h @@ -33,17 +33,7 @@ class Thread; */ class SignalCatcher { public: - // If |use_tombstoned_stack_trace_fd| is |true|, traces will be - // written to a file descriptor provided by tombstoned. The process - // will communicate with tombstoned via a unix domain socket. This - // mode of stack trace dumping is only supported in an Android - // environment. - // - // If false, all traces will be dumped to |stack_trace_file| if it's - // non-empty. If |stack_trace_file| is empty, all traces will be written - // to the log buffer. - SignalCatcher(const std::string& stack_trace_file, - const bool use_tombstoned_stack_trace_fd); + SignalCatcher(); ~SignalCatcher(); void HandleSigQuit() REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_, @@ -54,19 +44,12 @@ class SignalCatcher { // NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock. static void* Run(void* arg) NO_THREAD_SAFETY_ANALYSIS; - // NOTE: We're using android::base::unique_fd here for easier - // interoperability with tombstoned client APIs. - bool OpenStackTraceFile(android::base::unique_fd* tombstone_fd, - android::base::unique_fd* output_fd); void HandleSigUsr1(); void Output(const std::string& s); void SetHaltFlag(bool new_value) REQUIRES(!lock_); bool ShouldHalt() REQUIRES(!lock_); int WaitForSignal(Thread* self, SignalSet& signals) REQUIRES(!lock_); - std::string stack_trace_file_; - const bool use_tombstoned_stack_trace_fd_; - mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; ConditionVariable cond_ GUARDED_BY(lock_); bool halt_ GUARDED_BY(lock_); -- GitLab From f9e11ac336888238c9573eb081324c101bf39f49 Mon Sep 17 00:00:00 2001 From: Sen Jiang Date: Fri, 4 May 2018 13:08:48 -0700 Subject: [PATCH 334/749] Match LZMA SDK 18.05 API. The signature of ISeqInStream::Read and ISeqOutStream::Write changed. CXzProps::lzma2Props is not a pointer now. Test: mma Change-Id: Ia6936bd2ff1ea9a09e2d81496867078bf9116d6a --- compiler/debug/elf_gnu_debugdata_writer.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h index a88c5cb213..fd132f4ac4 100644 --- a/compiler/debug/elf_gnu_debugdata_writer.h +++ b/compiler/debug/elf_gnu_debugdata_writer.h @@ -41,23 +41,23 @@ static void XzCompress(const std::vector* src, std::vector* ds Lzma2EncProps_Normalize(&lzma2Props); CXzProps props; XzProps_Init(&props); - props.lzma2Props = &lzma2Props; + props.lzma2Props = lzma2Props; // Implement the required interface for communication (written in C so no virtual methods). struct XzCallbacks : public ISeqInStream, public ISeqOutStream, public ICompressProgress { - static SRes ReadImpl(void* p, void* buf, size_t* size) { - auto* ctx = static_cast(reinterpret_cast(p)); + static SRes ReadImpl(const ISeqInStream* p, void* buf, size_t* size) { + auto* ctx = static_cast(const_cast(p)); *size = std::min(*size, ctx->src_->size() - ctx->src_pos_); memcpy(buf, ctx->src_->data() + ctx->src_pos_, *size); ctx->src_pos_ += *size; return SZ_OK; } - static size_t WriteImpl(void* p, const void* buf, size_t size) { - auto* ctx = static_cast(reinterpret_cast(p)); + static size_t WriteImpl(const ISeqOutStream* p, const void* buf, size_t size) { + auto* ctx = static_cast(p); const uint8_t* buffer = reinterpret_cast(buf); ctx->dst_->insert(ctx->dst_->end(), buffer, buffer + size); return size; } - static SRes ProgressImpl(void* , UInt64, UInt64) { + static SRes ProgressImpl(const ICompressProgress* , UInt64, UInt64) { return SZ_OK; } size_t src_pos_; @@ -113,4 +113,3 @@ static std::vector MakeMiniDebugInfoInternal( } // namespace art #endif // ART_COMPILER_DEBUG_ELF_GNU_DEBUGDATA_WRITER_H_ - -- GitLab From 59962d73232c03832bc806a67670f96cb73b42eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Gjesse?= Date: Mon, 7 May 2018 13:58:22 +0200 Subject: [PATCH 335/749] Update expectations for 913-heaps for new D8 version Updates to D8 causes the dex registers allocated to be different. Test: test/testrunner/testrunner.py -b --host -t 913 Change-Id: Id1c49adea4da0cf80eb999b14800af11bc8ca31d --- test/913-heaps/expected_d8.diff | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/913-heaps/expected_d8.diff b/test/913-heaps/expected_d8.diff index 3ea3c0d2b0..1ad0cbdd3b 100644 --- a/test/913-heaps/expected_d8.diff +++ b/test/913-heaps/expected_d8.diff @@ -10,8 +10,8 @@ 51c50,51 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1] --- -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1] -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1] 102,103c102 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1] < root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1] @@ -24,8 +24,8 @@ 117c116,117 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1] --- -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1] -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1] 162c162 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1] --- @@ -37,8 +37,8 @@ 179c179,180 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1] --- -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1] -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1] 201,202c202 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1] < root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1] @@ -51,8 +51,8 @@ 248c248,249 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1] --- -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1] -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1] 292d292 < root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1] 347c347 @@ -66,5 +66,5 @@ 368c368,369 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1] --- -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1] -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1] -- GitLab From 2697d581d133a2698f1021ee41732501d622491e Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 7 May 2018 13:23:11 -0700 Subject: [PATCH 336/749] ART: Relayout Region Relayout the Region fields. Reduces required memory for a Region object by 20% (80B to 64B). With the default run-test config, this reduces the Region array size from 164.4KB to 131.8KB. Bug: 79365543 Test: m test-art-host Change-Id: Ic044629decd3fabe4e8b964f10fee155ca65abf9 --- runtime/gc/space/region_space.h | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h index 811d6db876..a12917140b 100644 --- a/runtime/gc/space/region_space.h +++ b/runtime/gc/space/region_space.h @@ -299,10 +299,17 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { public: Region() : idx_(static_cast(-1)), - begin_(nullptr), top_(nullptr), end_(nullptr), - state_(RegionState::kRegionStateAllocated), type_(RegionType::kRegionTypeToSpace), - objects_allocated_(0), alloc_time_(0), live_bytes_(static_cast(-1)), - is_newly_allocated_(false), is_a_tlab_(false), thread_(nullptr) {} + live_bytes_(static_cast(-1)), + begin_(nullptr), + thread_(nullptr), + top_(nullptr), + end_(nullptr), + objects_allocated_(0), + alloc_time_(0), + is_newly_allocated_(false), + is_a_tlab_(false), + state_(RegionState::kRegionStateAllocated), + type_(RegionType::kRegionTypeToSpace) {} void Init(size_t idx, uint8_t* begin, uint8_t* end) { idx_ = idx; @@ -496,22 +503,22 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { private: size_t idx_; // The region's index in the region space. + size_t live_bytes_; // The live bytes. Used to compute the live percent. uint8_t* begin_; // The begin address of the region. + Thread* thread_; // The owning thread if it's a tlab. // Note that `top_` can be higher than `end_` in the case of a // large region, where an allocated object spans multiple regions // (large region + one or more large tail regions). Atomic top_; // The current position of the allocation. uint8_t* end_; // The end address of the region. - RegionState state_; // The region state (see RegionState). - RegionType type_; // The region type (see RegionType). Atomic objects_allocated_; // The number of objects allocated. uint32_t alloc_time_; // The allocation time of the region. // Note that newly allocated and evacuated regions use -1 as // special value for `live_bytes_`. - size_t live_bytes_; // The live bytes. Used to compute the live percent. bool is_newly_allocated_; // True if it's allocated after the last collection. bool is_a_tlab_; // True if it's a tlab. - Thread* thread_; // The owning thread if it's a tlab. + RegionState state_; // The region state (see RegionState). + RegionType type_; // The region type (see RegionType). friend class RegionSpace; }; -- GitLab From e40f65f36ec441b4d1a0a769c72d6ca70e099115 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 7 May 2018 17:02:22 -0700 Subject: [PATCH 337/749] ART: Relayout ProfilingInfo Order the fields by size to save 8B (32B to 24B) for 64-bit libart. Bug: 79365543 Test: m test-art-host Change-Id: Ia452524caf31b0fd42c44e666fc95d07fef444b3 --- runtime/jit/profiling_info.cc | 10 +++++----- runtime/jit/profiling_info.h | 22 +++++++++++----------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc index 9126bea7d0..2cb569c61a 100644 --- a/runtime/jit/profiling_info.cc +++ b/runtime/jit/profiling_info.cc @@ -26,12 +26,12 @@ namespace art { ProfilingInfo::ProfilingInfo(ArtMethod* method, const std::vector& entries) - : number_of_inline_caches_(entries.size()), - method_(method), - is_method_being_compiled_(false), - is_osr_method_being_compiled_(false), + : method_(method), + saved_entry_point_(nullptr), + number_of_inline_caches_(entries.size()), current_inline_uses_(0), - saved_entry_point_(nullptr) { + is_method_being_compiled_(false), + is_osr_method_being_compiled_(false) { memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache)); for (size_t i = 0; i < number_of_inline_caches_; ++i) { cache_[i].dex_pc_ = entries[i]; diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h index 788fa1f92b..a3dae8330a 100644 --- a/runtime/jit/profiling_info.h +++ b/runtime/jit/profiling_info.h @@ -132,27 +132,27 @@ class ProfilingInfo { private: ProfilingInfo(ArtMethod* method, const std::vector& entries); - // Number of instructions we are profiling in the ArtMethod. - const uint32_t number_of_inline_caches_; - // Method this profiling info is for. // Not 'const' as JVMTI introduces obsolete methods that we implement by creating new ArtMethods. // See JitCodeCache::MoveObsoleteMethod. ArtMethod* method_; - // Whether the ArtMethod is currently being compiled. This flag - // is implicitly guarded by the JIT code cache lock. - // TODO: Make the JIT code cache lock global. - bool is_method_being_compiled_; - bool is_osr_method_being_compiled_; + // Entry point of the corresponding ArtMethod, while the JIT code cache + // is poking for the liveness of compiled code. + const void* saved_entry_point_; + + // Number of instructions we are profiling in the ArtMethod. + const uint32_t number_of_inline_caches_; // When the compiler inlines the method associated to this ProfilingInfo, // it updates this counter so that the GC does not try to clear the inline caches. uint16_t current_inline_uses_; - // Entry point of the corresponding ArtMethod, while the JIT code cache - // is poking for the liveness of compiled code. - const void* saved_entry_point_; + // Whether the ArtMethod is currently being compiled. This flag + // is implicitly guarded by the JIT code cache lock. + // TODO: Make the JIT code cache lock global. + bool is_method_being_compiled_; + bool is_osr_method_being_compiled_; // Dynamically allocated array of size `number_of_inline_caches_`. InlineCache cache_[0]; -- GitLab From b3d1eeed7426570f61a0b0d4be1a2987200311f6 Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Thu, 3 May 2018 22:28:03 -0700 Subject: [PATCH 338/749] Add new profile saver options: save without jit & profile AOT code --Xps-save-without-jit-notifications The hotness for system server code is increased in AOT-ed code. The flow does not call into JIT and as such notifications are not triggered. Instead of relying on the JIT system, make use of a simple back off strategy to save the profile. --Xps-profile-aot-code Starts the profile saver even if the oat file is speed compiled. Test: m test-art-host boot a device with system server profiling enabled. Bug: 73313191 Change-Id: I66ec422a76bc9244349da7a5d18a3df5bcc9ccb7 --- cmdline/cmdline_types.h | 10 ++++++++++ runtime/jit/jit.h | 7 +++++++ runtime/jit/profile_saver.cc | 24 ++++++++++++++++++++--- runtime/jit/profile_saver_options.h | 30 +++++++++++++++++++++++++---- 4 files changed, 64 insertions(+), 7 deletions(-) diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index 5a25a6ceb6..48da7551a7 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -643,6 +643,16 @@ struct CmdlineType : CmdlineTypeParser return Result::SuccessNoValue(); } + if (option == "profile-aot-code") { + existing.profile_aot_code_ = true; + return Result::SuccessNoValue(); + } + + if (option == "save-without-jit-notifications") { + existing.wait_for_jit_notifications_to_save_ = false; + return Result::SuccessNoValue(); + } + // The rest of these options are always the wildcard from '-Xps-*' std::string suffix = RemovePrefix(option); diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index 6d27cfe5db..4b8b8919d1 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -252,6 +252,13 @@ class JitOptions { void SetSaveProfilingInfo(bool save_profiling_info) { profile_saver_options_.SetEnabled(save_profiling_info); } + void SetWaitForJitNotificationsToSaveProfile(bool value) { + profile_saver_options_.SetWaitForJitNotificationsToSave(value); + } + void SetProfileAOTCode(bool value) { + profile_saver_options_.SetProfileAOTCode(value); + } + void SetJitAtFirstUse() { use_jit_compilation_ = true; compile_threshold_ = 0; diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 618fde8f00..efa8a4d2d5 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -131,6 +131,11 @@ void ProfileSaver::Run() { } FetchAndCacheResolvedClassesAndMethods(/*startup*/ true); + + // When we save without waiting for JIT notifications we use a simple + // exponential back off policy bounded by max_wait_without_jit. + uint32_t max_wait_without_jit = options_.GetMinSavePeriodMs() * 16; + uint64_t cur_wait_without_jit = options_.GetMinSavePeriodMs(); // Loop for the profiled methods. while (!ShuttingDown(self)) { uint64_t sleep_start = NanoTime(); @@ -138,7 +143,14 @@ void ProfileSaver::Run() { uint64_t sleep_time = 0; { MutexLock mu(self, wait_lock_); - period_condition_.Wait(self); + if (options_.GetWaitForJitNotificationsToSave()) { + period_condition_.Wait(self); + } else { + period_condition_.TimedWait(self, cur_wait_without_jit, 0); + if (cur_wait_without_jit < max_wait_without_jit) { + cur_wait_without_jit *= 2; + } + } sleep_time = NanoTime() - sleep_start; } // Check if the thread was woken up for shutdown. @@ -597,7 +609,13 @@ void* ProfileSaver::RunProfileSaverThread(void* arg) { return nullptr; } -static bool ShouldProfileLocation(const std::string& location) { +static bool ShouldProfileLocation(const std::string& location, bool profile_aot_code) { + if (profile_aot_code) { + // If we have to profile all the code, irrespective of its compilation state, return true + // right away. + return true; + } + OatFileManager& oat_manager = Runtime::Current()->GetOatFileManager(); const OatFile* oat_file = oat_manager.FindOpenedOatFileFromDexLocation(location); if (oat_file == nullptr) { @@ -629,7 +647,7 @@ void ProfileSaver::Start(const ProfileSaverOptions& options, std::vector code_paths_to_profile; for (const std::string& location : code_paths) { - if (ShouldProfileLocation(location)) { + if (ShouldProfileLocation(location, options.GetProfileAOTCode())) { code_paths_to_profile.push_back(location); } } diff --git a/runtime/jit/profile_saver_options.h b/runtime/jit/profile_saver_options.h index d1e14e2766..18f7899af1 100644 --- a/runtime/jit/profile_saver_options.h +++ b/runtime/jit/profile_saver_options.h @@ -41,7 +41,9 @@ struct ProfileSaverOptions { min_notification_before_wake_(kMinNotificationBeforeWake), max_notification_before_wake_(kMaxNotificationBeforeWake), profile_path_(""), - profile_boot_class_path_(false) {} + profile_boot_class_path_(false), + profile_aot_code_(false), + wait_for_jit_notifications_to_save_(true) {} ProfileSaverOptions( bool enabled, @@ -53,7 +55,9 @@ struct ProfileSaverOptions { uint32_t min_notification_before_wake, uint32_t max_notification_before_wake, const std::string& profile_path, - bool profile_boot_class_path) + bool profile_boot_class_path, + bool profile_aot_code = false, + bool wait_for_jit_notifications_to_save = true) : enabled_(enabled), min_save_period_ms_(min_save_period_ms), save_resolved_classes_delay_ms_(save_resolved_classes_delay_ms), @@ -63,7 +67,9 @@ struct ProfileSaverOptions { min_notification_before_wake_(min_notification_before_wake), max_notification_before_wake_(max_notification_before_wake), profile_path_(profile_path), - profile_boot_class_path_(profile_boot_class_path) {} + profile_boot_class_path_(profile_boot_class_path), + profile_aot_code_(profile_aot_code), + wait_for_jit_notifications_to_save_(wait_for_jit_notifications_to_save) {} bool IsEnabled() const { return enabled_; @@ -103,6 +109,18 @@ struct ProfileSaverOptions { bool GetProfileBootClassPath() const { return profile_boot_class_path_; } + bool GetProfileAOTCode() const { + return profile_aot_code_; + } + void SetProfileAOTCode(bool value) { + profile_aot_code_ = value; + } + bool GetWaitForJitNotificationsToSave() const { + return wait_for_jit_notifications_to_save_; + } + void SetWaitForJitNotificationsToSave(bool value) { + wait_for_jit_notifications_to_save_ = value; + } friend std::ostream & operator<<(std::ostream &os, const ProfileSaverOptions& pso) { os << "enabled_" << pso.enabled_ @@ -113,7 +131,9 @@ struct ProfileSaverOptions { << ", min_classes_to_save_" << pso.min_classes_to_save_ << ", min_notification_before_wake_" << pso.min_notification_before_wake_ << ", max_notification_before_wake_" << pso.max_notification_before_wake_ - << ", profile_boot_class_path_" << pso.profile_boot_class_path_; + << ", profile_boot_class_path_" << pso.profile_boot_class_path_ + << ", profile_aot_code_" << pso.profile_aot_code_ + << ", wait_for_jit_notifications_to_save_" << pso.wait_for_jit_notifications_to_save_; return os; } @@ -129,6 +149,8 @@ struct ProfileSaverOptions { uint32_t max_notification_before_wake_; std::string profile_path_; bool profile_boot_class_path_; + bool profile_aot_code_; + bool wait_for_jit_notifications_to_save_; }; } // namespace art -- GitLab From a8503d9696f37ff66b23016f3374ecbe59774dc6 Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Thu, 5 Apr 2018 16:10:25 +0100 Subject: [PATCH 339/749] Replace kAllLists policy with kJustWarn. It seems pretty unlikely that we'd ever want to disallow access to the light greylist in P, since doing do would break do many apps. We don't need this policy here as an opt-in for apps now, since the StrictMode work will achieve the same thing. Instead, make a kJustWarn policy which allows access to all APIs, but leaves the detection and logging logic in place. This gives us the option of disabling enforcement, but still gathering logs to find out which apps use which APIs. Add some tests for GetActionFromAccessFlags since the logic is getting more complex. Bug: 77517571 Test: make test-art-host-gtest-hidden_api_test Test: boot device Merged-In: I2e6824d7ef53532d0836a2b6b1930cbbed196d0c Change-Id: I2e6824d7ef53532d0836a2b6b1930cbbed196d0c (cherry picked from commit 68693699d62bc7a2192f51be191ae81defcf4388) --- runtime/hidden_api.cc | 3 +-- runtime/hidden_api.h | 7 ++++++- runtime/hidden_api_test.cc | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index 0e72f275d2..2d01fddd6e 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -46,12 +46,11 @@ static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags: // GetMemberAction-related static_asserts. static_assert( - EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) && EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) && EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist), "Mismatch between EnforcementPolicy and ApiList enums"); static_assert( - EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList && + EnforcementPolicy::kJustWarn < EnforcementPolicy::kDarkGreyAndBlackList && EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly, "EnforcementPolicy values ordering not correct"); diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index ffdeacbfff..3ac08778c4 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -33,7 +33,7 @@ namespace hiddenapi { // frameworks/base/core/java/android/content/pm/ApplicationInfo.java enum class EnforcementPolicy { kNoChecks = 0, - kAllLists = 1, // ban anything but whitelist + kJustWarn = 1, // keep checks enabled, but allow everything (enables logging) kDarkGreyAndBlackList = 2, // ban dark grey & blacklist kBlacklistOnly = 3, // ban blacklist violations only kMax = kBlacklistOnly, @@ -69,6 +69,11 @@ inline Action GetActionFromAccessFlags(uint32_t access_flags) { if (api_list == HiddenApiAccessFlags::kWhitelist) { return kAllow; } + // if policy is "just warn", always warn. We returned above for whitelist APIs. + if (policy == EnforcementPolicy::kJustWarn) { + return kAllowButWarn; + } + DCHECK(policy >= EnforcementPolicy::kDarkGreyAndBlackList); // The logic below relies on equality of values in the enums EnforcementPolicy and // HiddenApiAccessFlags::ApiList, and their ordering. Assertions are in hidden_api.cc. if (static_cast(policy) > static_cast(api_list)) { diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc index 5a31dd4972..65d6363bfd 100644 --- a/runtime/hidden_api_test.cc +++ b/runtime/hidden_api_test.cc @@ -22,6 +22,7 @@ namespace art { using hiddenapi::detail::MemberSignature; +using hiddenapi::GetActionFromAccessFlags; class HiddenApiTest : public CommonRuntimeTest { protected: @@ -84,6 +85,39 @@ class HiddenApiTest : public CommonRuntimeTest { ArtMethod* class3_method1_i_; }; +TEST_F(HiddenApiTest, CheckGetActionFromRuntimeFlags) { + uint32_t whitelist = HiddenApiAccessFlags::EncodeForRuntime(0, HiddenApiAccessFlags::kWhitelist); + uint32_t lightgreylist = + HiddenApiAccessFlags::EncodeForRuntime(0, HiddenApiAccessFlags::kLightGreylist); + uint32_t darkgreylist = + HiddenApiAccessFlags::EncodeForRuntime(0, HiddenApiAccessFlags::kDarkGreylist); + uint32_t blacklist = HiddenApiAccessFlags::EncodeForRuntime(0, HiddenApiAccessFlags::kBlacklist); + + runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks); + ASSERT_EQ(GetActionFromAccessFlags(whitelist), hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(lightgreylist), hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(darkgreylist), hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(blacklist), hiddenapi::kAllow); + + runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kJustWarn); + ASSERT_EQ(GetActionFromAccessFlags(whitelist), hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(lightgreylist), hiddenapi::kAllowButWarn); + ASSERT_EQ(GetActionFromAccessFlags(darkgreylist), hiddenapi::kAllowButWarn); + ASSERT_EQ(GetActionFromAccessFlags(blacklist), hiddenapi::kAllowButWarn); + + runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList); + ASSERT_EQ(GetActionFromAccessFlags(whitelist), hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(lightgreylist), hiddenapi::kAllowButWarn); + ASSERT_EQ(GetActionFromAccessFlags(darkgreylist), hiddenapi::kDeny); + ASSERT_EQ(GetActionFromAccessFlags(blacklist), hiddenapi::kDeny); + + runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly); + ASSERT_EQ(GetActionFromAccessFlags(whitelist), hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(lightgreylist), hiddenapi::kAllowButWarn); + ASSERT_EQ(GetActionFromAccessFlags(darkgreylist), hiddenapi::kAllowButWarnAndToast); + ASSERT_EQ(GetActionFromAccessFlags(blacklist), hiddenapi::kDeny); +} + TEST_F(HiddenApiTest, CheckMembersRead) { ASSERT_NE(nullptr, class1_field1_); ASSERT_NE(nullptr, class1_field12_); -- GitLab From 4525e0b62cc808a49b4cd2c826bb52a768779e72 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Thu, 5 Apr 2018 16:57:32 +0100 Subject: [PATCH 340/749] Fix verifier/linker IncompatibleClassChangeError with hidden API The verifier and class linker will attempt to find a method with the wrong type if it could not be found with the original type, i.e an interface method on a regular class and vice versa. This logic did not previously take hidden API restrictions into account and would result in bogus error messages to the user or debug crashes. Bug: 64382372 Bug: 77464273 Test: art/test.py -r -t 674-hiddenapi Merged-In: If8327a70dd73b90249da3d9e505f0c6f89838f8e Change-Id: If8327a70dd73b90249da3d9e505f0c6f89838f8e (cherry picked from commit 54a99cfcf3d3463404fdf4152523dcc69b8648d7) --- runtime/class_linker.cc | 42 ++++++++++++++--- runtime/class_linker.h | 9 ++++ runtime/hidden_api.cc | 35 ++++++++------ runtime/hidden_api.h | 1 + runtime/verifier/method_verifier.cc | 12 +---- test/674-hiddenapi/src-ex/ChildClass.java | 1 + test/674-hiddenapi/src-ex/Linking.java | 52 +++++++++++++++++++++ test/674-hiddenapi/src/DummyClass.java | 26 +++++++++++ test/674-hiddenapi/src/ParentInterface.java | 2 +- 9 files changed, 149 insertions(+), 31 deletions(-) create mode 100644 test/674-hiddenapi/src/DummyClass.java diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 57c0d9dab2..44445aea9b 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -7884,6 +7884,40 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr klass, return resolved; } +// Returns true if `method` is either null or hidden. +// Does not print any warnings if it is hidden. +static bool CheckNoSuchMethod(ArtMethod* method, + ObjPtr dex_cache, + ObjPtr class_loader) + REQUIRES_SHARED(Locks::mutator_lock_) { + return method == nullptr || + hiddenapi::GetMemberAction(method, + class_loader, + dex_cache, + hiddenapi::kNone) // do not print warnings + == hiddenapi::kDeny; +} + +ArtMethod* ClassLinker::FindIncompatibleMethod(ObjPtr klass, + ObjPtr dex_cache, + ObjPtr class_loader, + uint32_t method_idx) { + if (klass->IsInterface()) { + ArtMethod* method = klass->FindClassMethod(dex_cache, method_idx, image_pointer_size_); + return CheckNoSuchMethod(method, dex_cache, class_loader) ? nullptr : method; + } else { + // If there was an interface method with the same signature, we would have + // found it in the "copied" methods. Only DCHECK that the interface method + // really does not exist. + if (kIsDebugBuild) { + ArtMethod* method = + klass->FindInterfaceMethod(dex_cache, method_idx, image_pointer_size_); + DCHECK(CheckNoSuchMethod(method, dex_cache, class_loader)); + } + return nullptr; + } +} + template ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, Handle dex_cache, @@ -7959,13 +7993,7 @@ ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, // If we had a method, or if we can find one with another lookup type, // it's an incompatible-class-change error. if (resolved == nullptr) { - if (klass->IsInterface()) { - resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, pointer_size); - } else { - // If there was an interface method with the same signature, - // we would have found it also in the "copied" methods. - DCHECK(klass->FindInterfaceMethod(dex_cache.Get(), method_idx, pointer_size) == nullptr); - } + resolved = FindIncompatibleMethod(klass, dex_cache.Get(), class_loader.Get(), method_idx); } if (resolved != nullptr) { ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index fa70f65bca..e935d1dfb8 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -329,6 +329,15 @@ class ClassLinker { uint32_t method_idx) REQUIRES_SHARED(Locks::mutator_lock_); + // Find a method using the wrong lookup mechanism. If `klass` is an interface, + // search for a class method. If it is a class, search for an interface method. + // This is useful when throwing IncompatibleClassChangeError. + ArtMethod* FindIncompatibleMethod(ObjPtr klass, + ObjPtr dex_cache, + ObjPtr class_loader, + uint32_t method_idx) + REQUIRES_SHARED(Locks::mutator_lock_); + // Resolve a method with a given ID from the DexFile associated with the given DexCache // and ClassLoader, storing the result in DexCache. The ClassLinker and ClassLoader are // used as in ResolveType. What is unique is the method type argument which is used to diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index 2d01fddd6e..fa47e3a17a 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -27,6 +27,9 @@ namespace hiddenapi { static inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { switch (value) { + case kNone: + LOG(FATAL) << "Internal access to hidden API should not be logged"; + UNREACHABLE(); case kReflection: os << "reflection"; break; @@ -115,6 +118,8 @@ void MemberSignature::WarnAboutAccess(AccessMethod access_method, template Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) { + DCHECK_NE(action, kAllow); + // Get the signature, we need it later. MemberSignature member_signature(member); @@ -134,10 +139,12 @@ Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) } } - // Print a log message with information about this class member access. - // We do this regardless of whether we block the access or not. - member_signature.WarnAboutAccess(access_method, - HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags())); + if (access_method != kNone) { + // Print a log message with information about this class member access. + // We do this regardless of whether we block the access or not. + member_signature.WarnAboutAccess(access_method, + HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags())); + } if (action == kDeny) { // Block access @@ -147,16 +154,18 @@ Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) // Allow access to this member but print a warning. DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast); - // Depending on a runtime flag, we might move the member into whitelist and - // skip the warning the next time the member is accessed. - if (runtime->ShouldDedupeHiddenApiWarnings()) { - member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( - member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist)); - } + if (access_method != kNone) { + // Depending on a runtime flag, we might move the member into whitelist and + // skip the warning the next time the member is accessed. + if (runtime->ShouldDedupeHiddenApiWarnings()) { + member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( + member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist)); + } - // If this action requires a UI warning, set the appropriate flag. - if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) { - runtime->SetPendingHiddenApiWarning(true); + // If this action requires a UI warning, set the appropriate flag. + if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) { + runtime->SetPendingHiddenApiWarning(true); + } } return action; diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index 3ac08778c4..d2c71a7d35 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -53,6 +53,7 @@ enum Action { }; enum AccessMethod { + kNone, // internal test that does not correspond to an actual access by app kReflection, kJNI, kLinking, diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 72292c33f8..bc29aaca6d 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3801,16 +3801,8 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess( must_fail = true; // Try to find the method also with the other type for better error reporting below // but do not store such bogus lookup result in the DexCache or VerifierDeps. - if (klass->IsInterface()) { - // NB This is normally not really allowed but we want to get any static or private object - // methods for error message purposes. This will never be returned. - // TODO We might want to change the verifier to not require this. - res_method = klass->FindClassMethod(dex_cache_.Get(), dex_method_idx, pointer_size); - } else { - // If there was an interface method with the same signature, - // we would have found it also in the "copied" methods. - DCHECK(klass->FindInterfaceMethod(dex_cache_.Get(), dex_method_idx, pointer_size) == nullptr); - } + res_method = class_linker->FindIncompatibleMethod( + klass, dex_cache_.Get(), class_loader_.Get(), dex_method_idx); } if (res_method == nullptr) { diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java index ea66f1651e..e3d3e698e6 100644 --- a/test/674-hiddenapi/src-ex/ChildClass.java +++ b/test/674-hiddenapi/src-ex/ChildClass.java @@ -121,6 +121,7 @@ public class ChildClass { checkLinking("LinkFieldGet" + suffix, /*takesParameter*/ false, expected); checkLinking("LinkFieldSet" + suffix, /*takesParameter*/ true, expected); checkLinking("LinkMethod" + suffix, /*takesParameter*/ false, expected); + checkLinking("LinkMethodInterface" + suffix, /*takesParameter*/ false, expected); } // Check whether Class.newInstance succeeds. diff --git a/test/674-hiddenapi/src-ex/Linking.java b/test/674-hiddenapi/src-ex/Linking.java index a89b92b2b9..0fa0b1912f 100644 --- a/test/674-hiddenapi/src-ex/Linking.java +++ b/test/674-hiddenapi/src-ex/Linking.java @@ -174,6 +174,32 @@ class LinkMethodBlacklist { } } +// INVOKE INSTANCE INTERFACE METHOD + +class LinkMethodInterfaceWhitelist { + public static int access() { + return DummyClass.getInterfaceInstance().methodPublicWhitelist(); + } +} + +class LinkMethodInterfaceLightGreylist { + public static int access() { + return DummyClass.getInterfaceInstance().methodPublicLightGreylist(); + } +} + +class LinkMethodInterfaceDarkGreylist { + public static int access() { + return DummyClass.getInterfaceInstance().methodPublicDarkGreylist(); + } +} + +class LinkMethodInterfaceBlacklist { + public static int access() { + return DummyClass.getInterfaceInstance().methodPublicBlacklist(); + } +} + // INVOKE STATIC METHOD class LinkMethodStaticWhitelist { @@ -199,3 +225,29 @@ class LinkMethodStaticBlacklist { return ParentClass.methodPublicStaticBlacklist(); } } + +// INVOKE INTERFACE STATIC METHOD + +class LinkMethodInterfaceStaticWhitelist { + public static int access() { + return ParentInterface.methodPublicStaticWhitelist(); + } +} + +class LinkMethodInterfaceStaticLightGreylist { + public static int access() { + return ParentInterface.methodPublicStaticLightGreylist(); + } +} + +class LinkMethodInterfaceStaticDarkGreylist { + public static int access() { + return ParentInterface.methodPublicStaticDarkGreylist(); + } +} + +class LinkMethodInterfaceStaticBlacklist { + public static int access() { + return ParentInterface.methodPublicStaticBlacklist(); + } +} diff --git a/test/674-hiddenapi/src/DummyClass.java b/test/674-hiddenapi/src/DummyClass.java new file mode 100644 index 0000000000..51281a2666 --- /dev/null +++ b/test/674-hiddenapi/src/DummyClass.java @@ -0,0 +1,26 @@ +/* + * 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. + */ + +public class DummyClass implements ParentInterface { + public int methodPublicWhitelist() { return 1; } + public int methodPublicLightGreylist() { return 2; } + public int methodPublicDarkGreylist() { return 3; } + public int methodPublicBlacklist() { return 4; } + + public static ParentInterface getInterfaceInstance() { + return new DummyClass(); + } +} diff --git a/test/674-hiddenapi/src/ParentInterface.java b/test/674-hiddenapi/src/ParentInterface.java index e36fe0e6b2..f79ac9d661 100644 --- a/test/674-hiddenapi/src/ParentInterface.java +++ b/test/674-hiddenapi/src/ParentInterface.java @@ -23,9 +23,9 @@ public interface ParentInterface { // INSTANCE METHOD int methodPublicWhitelist(); - int methodPublicBlacklist(); int methodPublicLightGreylist(); int methodPublicDarkGreylist(); + int methodPublicBlacklist(); // STATIC METHOD static int methodPublicStaticWhitelist() { return 21; } -- GitLab From 9a81945ca49de3c5b3969cc85278ecbadf238c84 Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Thu, 5 Apr 2018 13:58:55 +0100 Subject: [PATCH 341/749] Treat hidden API exemptions as whitelist. This was a request from android-api-council, effectively to treat exempted APIs as equivalent to public APIs. The reasoning for this is that if the support library uses such APIs, we don't want to confuse developers with a warning in the log. To avoid examing the exemptions list on all light greylist API accesses, also change the light greylist warn behavior to only print a warning in the log if the app is debuggable. This means: - less log spam from light greylist usages - debuggable apps still get info about light greylist usage Bug: 64382372 Test: m Test: Boot device Test: $ adb shell settings put global hidden_api_blacklist_exemptions \ Test: "\"Landroid/app/Activity;->mWindow:,Landroid/app/Activity;->mCalled:\"" Test: Verified with test app & adb logcat Merged-In: Ibada61b591517f7e72c7101aea04ff0ad4beb0ee Change-Id: Ibada61b591517f7e72c7101aea04ff0ad4beb0ee (cherry picked from commit c8ce5f520d2ba84ff8f393f78ba953ae6d467ca8) --- runtime/hidden_api.cc | 32 ++++++++++++++++++-------------- runtime/runtime.h | 3 ++- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index fa47e3a17a..02b4f5349d 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -125,25 +125,29 @@ Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) Runtime* runtime = Runtime::Current(); - if (action == kDeny) { - // If we were about to deny, check for an exemption first. - // Exempted APIs are treated as light grey list. + // Check for an exemption first. Exempted APIs are treated as white list. + // We only do this if we're about to deny, or if the app is debuggable. This is because: + // - we only print a warning for light greylist violations for debuggable apps + // - for non-debuggable apps, there is no distinction between light grey & whitelisted APIs. + // - we want to avoid the overhead of checking for exemptions for light greylisted APIs whenever + // possible. + if (action == kDeny || runtime->IsJavaDebuggable()) { if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) { - action = kAllowButWarn; + action = kAllow; // Avoid re-examining the exemption list next time. - // Note this results in the warning below showing "light greylist", which - // seems like what one would expect. Exemptions effectively add new members to - // the light greylist. + // Note this results in no warning for the member, which seems like what one would expect. + // Exemptions effectively adds new members to the whitelist. member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( - member->GetAccessFlags(), HiddenApiAccessFlags::kLightGreylist)); + member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist)); + return kAllow; } - } - if (access_method != kNone) { - // Print a log message with information about this class member access. - // We do this regardless of whether we block the access or not. - member_signature.WarnAboutAccess(access_method, - HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags())); + if (access_method != kNone) { + // Print a log message with information about this class member access. + // We do this if we're about to block access, or the app is debuggable. + member_signature.WarnAboutAccess(access_method, + HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags())); + } } if (action == kDeny) { diff --git a/runtime/runtime.h b/runtime/runtime.h index 56d95e0b63..67813a7411 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -1006,7 +1006,8 @@ class Runtime { // Whether access checks on hidden API should be performed. hiddenapi::EnforcementPolicy hidden_api_policy_; - // List of signature prefixes of methods that have been removed from the blacklist + // List of signature prefixes of methods that have been removed from the blacklist, and treated + // as if whitelisted. std::vector hidden_api_exemptions_; // Whether the application has used an API which is not restricted but we -- GitLab From 9a13d423c71517bc9cb3cf40e97dd0f6f5d5c72c Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Mon, 9 Apr 2018 12:24:55 +0100 Subject: [PATCH 342/749] Consider whitelist when listing class members. Previously, only the enforcement policy was considered when getting declared fields or members, meaning whitelisted APIs would still not be discoverable. Fix this by calling hiddenapi::GetMemberAction from within IsDiscoverable. Bug: 77787686 Bug: 64382372 Test: cts/tests/signature/runSignatureTests.sh (with ag/3863796) Test: art/test.py --host -t 674-hiddenapi Merged-In: I234d274f47f377e3e105c81aae2d49072287992a Change-Id: I234d274f47f377e3e105c81aae2d49072287992a (cherry picked from commit 64ee8aeaeb70aa2d5d1c3ff57a682a5001869653) --- runtime/hidden_api.cc | 6 ++- runtime/native/java_lang_Class.cc | 31 ++++++------- test/674-hiddenapi/src-art/Main.java | 53 ++++++++++++++++------- test/674-hiddenapi/src-ex/ChildClass.java | 16 ++++--- 4 files changed, 69 insertions(+), 37 deletions(-) diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index 02b4f5349d..98feb4d8ed 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -137,8 +137,10 @@ Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) // Avoid re-examining the exemption list next time. // Note this results in no warning for the member, which seems like what one would expect. // Exemptions effectively adds new members to the whitelist. - member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( - member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist)); + if (runtime->ShouldDedupeHiddenApiWarnings()) { + member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( + member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist)); + } return kAllow; } diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index bfd7f69cef..2c1c963ed6 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -128,19 +128,20 @@ ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) // the criteria. Some reflection calls only return public members // (public_only == true), some members should be hidden from non-boot class path // callers (enforce_hidden_api == true). +template ALWAYS_INLINE static bool IsDiscoverable(bool public_only, bool enforce_hidden_api, - uint32_t access_flags) { - if (public_only && ((access_flags & kAccPublic) == 0)) { - return false; - } - - if (enforce_hidden_api && - hiddenapi::GetActionFromAccessFlags(access_flags) == hiddenapi::kDeny) { + T* member) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (public_only && ((member->GetAccessFlags() & kAccPublic) == 0)) { return false; } - return true; + return hiddenapi::GetMemberAction(member, + nullptr, + [enforce_hidden_api] (Thread*) { return !enforce_hidden_api; }, + hiddenapi::kNone) + != hiddenapi::kDeny; } ALWAYS_INLINE static inline ObjPtr DecodeClass( @@ -269,12 +270,12 @@ static mirror::ObjectArray* GetDeclaredFields( bool enforce_hidden_api = ShouldEnforceHiddenApi(self); // Lets go subtract all the non discoverable fields. for (ArtField& field : ifields) { - if (!IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) { + if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) { --array_size; } } for (ArtField& field : sfields) { - if (!IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) { + if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) { --array_size; } } @@ -285,7 +286,7 @@ static mirror::ObjectArray* GetDeclaredFields( return nullptr; } for (ArtField& field : ifields) { - if (IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) { + if (IsDiscoverable(public_only, enforce_hidden_api, &field)) { auto* reflect_field = mirror::Field::CreateFromArtField(self, &field, force_resolve); @@ -300,7 +301,7 @@ static mirror::ObjectArray* GetDeclaredFields( } } for (ArtField& field : sfields) { - if (IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) { + if (IsDiscoverable(public_only, enforce_hidden_api, &field)) { auto* reflect_field = mirror::Field::CreateFromArtField(self, &field, force_resolve); @@ -521,7 +522,7 @@ static ALWAYS_INLINE inline bool MethodMatchesConstructor( DCHECK(m != nullptr); return m->IsConstructor() && !m->IsStatic() && - IsDiscoverable(public_only, enforce_hidden_api, m->GetAccessFlags()); + IsDiscoverable(public_only, enforce_hidden_api, m); } static jobjectArray Class_getDeclaredConstructorsInternal( @@ -591,7 +592,7 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT uint32_t modifiers = m.GetAccessFlags(); // Add non-constructor declared methods. if ((modifiers & kAccConstructor) == 0 && - IsDiscoverable(public_only, enforce_hidden_api, modifiers)) { + IsDiscoverable(public_only, enforce_hidden_api, &m)) { ++num_methods; } } @@ -605,7 +606,7 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT for (ArtMethod& m : klass->GetDeclaredMethods(kRuntimePointerSize)) { uint32_t modifiers = m.GetAccessFlags(); if ((modifiers & kAccConstructor) == 0 && - IsDiscoverable(public_only, enforce_hidden_api, modifiers)) { + IsDiscoverable(public_only, enforce_hidden_api, &m)) { DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); DCHECK(!Runtime::Current()->IsActiveTransaction()); auto* method = diff --git a/test/674-hiddenapi/src-art/Main.java b/test/674-hiddenapi/src-art/Main.java index a808e946a9..782748c345 100644 --- a/test/674-hiddenapi/src-art/Main.java +++ b/test/674-hiddenapi/src-art/Main.java @@ -16,6 +16,7 @@ import dalvik.system.InMemoryDexClassLoader; import dalvik.system.PathClassLoader; +import dalvik.system.VMRuntime; import java.io.File; import java.io.InputStream; import java.lang.reflect.Constructor; @@ -34,26 +35,41 @@ public class Main { // Enable hidden API checks in case they are disabled by default. init(); + // TODO there are sequential depencies between these test cases, and bugs + // in the production code may lead to subsequent tests to erroneously pass, + // or test the wrong thing. We rely on not deduping hidden API warnings + // here for the same reasons), meaning the code under test and production + // code are running in different configurations. Each test should be run in + // a fresh process to ensure that they are working correcting and not + // accidentally interfering with eachother. + // Run test with both parent and child dex files loaded with class loaders. // The expectation is that hidden members in parent should be visible to // the child. - doTest(false, false); + doTest(false, false, false); doUnloading(); // Now append parent dex file to boot class path and run again. This time - // the child dex file should not be able to access private APIs of the parent. + // the child dex file should not be able to access private APIs of the + // parent. appendToBootClassLoader(DEX_PARENT_BOOT); - doTest(true, false); + doTest(true, false, false); + doUnloading(); + + // Now run the same test again, but with the blacklist exmemptions list set + // to "L" which matches everything. + doTest(true, false, true); doUnloading(); // And finally append to child to boot class path as well. With both in the // boot class path, access should be granted. appendToBootClassLoader(DEX_CHILD); - doTest(true, true); + doTest(true, true, false); doUnloading(); } - private static void doTest(boolean parentInBoot, boolean childInBoot) throws Exception { + private static void doTest(boolean parentInBoot, boolean childInBoot, boolean whitelistAllApis) + throws Exception { // Load parent dex if it is not in boot class path. ClassLoader parentLoader = null; if (parentInBoot) { @@ -78,12 +94,18 @@ public class Main { // be loaded once, but for some reason even classes from a class loader // cannot register their native methods against symbols in a shared library // loaded by their parent class loader. - String nativeLibCopy = createNativeLibCopy(parentInBoot, childInBoot); + String nativeLibCopy = createNativeLibCopy(parentInBoot, childInBoot, whitelistAllApis); + + if (whitelistAllApis) { + VMRuntime.getRuntime().setHiddenApiExemptions(new String[]{"L"}); + } // Invoke ChildClass.runTest Class.forName("ChildClass", true, childLoader) - .getDeclaredMethod("runTest", String.class, Boolean.TYPE, Boolean.TYPE) - .invoke(null, nativeLibCopy, parentInBoot, childInBoot); + .getDeclaredMethod("runTest", String.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE) + .invoke(null, nativeLibCopy, parentInBoot, childInBoot, whitelistAllApis); + + VMRuntime.getRuntime().setHiddenApiExemptions(new String[0]); } // Routine which tries to figure out the absolute path of our native library. @@ -122,20 +144,21 @@ public class Main { return buffer; } - // Copy native library to a new file with a unique name so it does not conflict - // with other loaded instance of the same binary file. - private static String createNativeLibCopy(boolean parentInBoot, boolean childInBoot) - throws Exception { + // Copy native library to a new file with a unique name so it does not + // conflict with other loaded instance of the same binary file. + private static String createNativeLibCopy( + boolean parentInBoot, boolean childInBoot, boolean whitelistAllApis) throws Exception { String tempFileName = System.mapLibraryName( - "hiddenapitest_" + (parentInBoot ? "1" : "0") + (childInBoot ? "1" : "0")); + "hiddenapitest_" + (parentInBoot ? "1" : "0") + (childInBoot ? "1" : "0") + + (whitelistAllApis ? "1" : "0")); File tempFile = new File(System.getenv("DEX_LOCATION"), tempFileName); Files.copy(new File(nativeLibFileName).toPath(), tempFile.toPath()); return tempFile.getAbsolutePath(); } private static void doUnloading() { - // Do multiple GCs to prevent rare flakiness if some other thread is keeping the - // classloader live. + // 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(); } diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java index e3d3e698e6..da0c36f536 100644 --- a/test/674-hiddenapi/src-ex/ChildClass.java +++ b/test/674-hiddenapi/src-ex/ChildClass.java @@ -70,7 +70,7 @@ public class ChildClass { private static final boolean booleanValues[] = new boolean[] { false, true }; public static void runTest(String libFileName, boolean expectedParentInBoot, - boolean expectedChildInBoot) throws Exception { + boolean expectedChildInBoot, boolean everythingWhitelisted) throws Exception { System.load(libFileName); // Check expectations about loading into boot class path. @@ -84,13 +84,15 @@ public class ChildClass { throw new RuntimeException("Expected ChildClass " + (expectedChildInBoot ? "" : "not ") + "in boot class path"); } + ChildClass.everythingWhitelisted = everythingWhitelisted; boolean isSameBoot = (isParentInBoot == isChildInBoot); // Run meaningful combinations of access flags. for (Hiddenness hiddenness : Hiddenness.values()) { final Behaviour expected; - if (isSameBoot || hiddenness == Hiddenness.Whitelist) { + if (isSameBoot || hiddenness == Hiddenness.Whitelist || + (hiddenness == Hiddenness.Blacklist && everythingWhitelisted)) { expected = Behaviour.Granted; } else if (hiddenness == Hiddenness.Blacklist) { expected = Behaviour.Denied; @@ -510,14 +512,16 @@ public class ChildClass { String fn, boolean canAccess) { throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() + "." + name + " to " + (canAccess ? "" : "not ") + "be discoverable with " + fn + ". " + - "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot); + "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " + + "everythingWhitelisted = " + everythingWhitelisted); } private static void throwAccessException(Class klass, String name, boolean isField, String fn) { throw new RuntimeException("Expected to be able to access " + (isField ? "field " : "method ") + klass.getName() + "." + name + " using " + fn + ". " + - "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot); + "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " + + "everythingWhitelisted = " + everythingWhitelisted); } private static void throwWarningException(Class klass, String name, boolean isField, @@ -525,7 +529,8 @@ public class ChildClass { throw new RuntimeException("Expected access to " + (isField ? "field " : "method ") + klass.getName() + "." + name + " using " + fn + " to " + (setsWarning ? "" : "not ") + "set the warning flag. " + - "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot); + "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " + + "everythingWhitelisted = " + everythingWhitelisted); } private static void throwModifiersException(Class klass, String name, boolean isField) { @@ -535,6 +540,7 @@ public class ChildClass { private static boolean isParentInBoot; private static boolean isChildInBoot; + private static boolean everythingWhitelisted; private static native boolean hasPendingWarning(); private static native void clearWarning(); -- GitLab From 1fd97f29d646770c5e8ddee2e8fe468ffcc9ea2c Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Tue, 3 Apr 2018 15:32:32 +0100 Subject: [PATCH 343/749] Log an event on hidden API accesses. The new event consists of: - The type of access (reflection, JNI, etc.) - The action taken (warn or deny) - The type of member accessed (field or method) - Name of the class which defined the member accessed - The name of the member - The type signature of the member (type of field, or method signature) The fully qualified member name is also not included to avoid the overhead of building the string. It can be build from the information included. Similarly, the package name, version, etc. are not included as they can be inferred from the context when analyzing the event log. The event is sampled, according to a sampling rate that can be set by a configuration option, to reduce log spam. Test: $ m Test: $ adb shell settings put global hidden_api_access_log_sampling_rate 65536 Test: $ adb lolcat -b events | grep art_hidden_api_access Sample output: 16796 16796 I art_hidden_api_access: [1,0,Ldalvik/system/VMRuntime;,getRuntime,()Ldalvik/system/VMRuntime;] 16796 16796 I art_hidden_api_access: [1,2,Ldalvik/system/VMRuntime;,setHiddenApiExemptions,([Ljava/lang/String;)V] 16796 16796 I art_hidden_api_access: [1,3,Landroid/app/Activity;,mDoReportFullyDrawn,Z] (Timestamps have been elided) Bug: 64382372 Bug: 77517571 Merged-In: I012b2c9fbffbd00ed3219918e7a736a4f7435ec8 Change-Id: I012b2c9fbffbd00ed3219918e7a736a4f7435ec8 (cherry picked from commit 73ddda4403c8088a730b8d456de46bb8e0307ed8) --- runtime/hidden_api.cc | 81 ++++++++++++++++----- runtime/hidden_api.h | 34 +++++++-- runtime/native/dalvik_system_VMRuntime.cc | 5 ++ runtime/native/dalvik_system_ZygoteHooks.cc | 7 ++ runtime/runtime.cc | 1 + runtime/runtime.h | 12 +++ 6 files changed, 114 insertions(+), 26 deletions(-) diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index 98feb4d8ed..c5d09cfa77 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -14,6 +14,8 @@ * limitations under the License. */ +#include + #include "hidden_api.h" #include @@ -59,31 +61,37 @@ static_assert( namespace detail { +// This is the ID of the event log event. It is duplicated from +// system/core/logcat/event.logtags +constexpr int EVENT_LOG_TAG_art_hidden_api_access = 20004; + MemberSignature::MemberSignature(ArtField* field) { - member_type_ = "field"; - signature_parts_ = { - field->GetDeclaringClass()->GetDescriptor(&tmp_), - "->", - field->GetName(), - ":", - field->GetTypeDescriptor() - }; + class_name_ = field->GetDeclaringClass()->GetDescriptor(&tmp_); + member_name_ = field->GetName(); + type_signature_ = field->GetTypeDescriptor(); + type_ = kField; } MemberSignature::MemberSignature(ArtMethod* method) { - member_type_ = "method"; - signature_parts_ = { - method->GetDeclaringClass()->GetDescriptor(&tmp_), - "->", - method->GetName(), - method->GetSignature().ToString() - }; + class_name_ = method->GetDeclaringClass()->GetDescriptor(&tmp_); + member_name_ = method->GetName(); + type_signature_ = method->GetSignature().ToString(); + type_ = kMethod; +} + +inline std::vector MemberSignature::GetSignatureParts() const { + if (type_ == kField) { + return { class_name_.c_str(), "->", member_name_.c_str(), ":", type_signature_.c_str() }; + } else { + DCHECK_EQ(type_, kMethod); + return { class_name_.c_str(), "->", member_name_.c_str(), type_signature_.c_str() }; + } } bool MemberSignature::DoesPrefixMatch(const std::string& prefix) const { size_t pos = 0; - for (const std::string& part : signature_parts_) { - size_t count = std::min(prefix.length() - pos, part.length()); + for (const char* part : GetSignatureParts()) { + size_t count = std::min(prefix.length() - pos, strlen(part)); if (prefix.compare(pos, count, part, 0, count) == 0) { pos += count; } else { @@ -105,15 +113,38 @@ bool MemberSignature::IsExempted(const std::vector& exemptions) { } void MemberSignature::Dump(std::ostream& os) const { - for (std::string part : signature_parts_) { + for (const char* part : GetSignatureParts()) { os << part; } } void MemberSignature::WarnAboutAccess(AccessMethod access_method, HiddenApiAccessFlags::ApiList list) { - LOG(WARNING) << "Accessing hidden " << member_type_ << " " << Dumpable(*this) - << " (" << list << ", " << access_method << ")"; + LOG(WARNING) << "Accessing hidden " << (type_ == kField ? "field " : "method ") + << Dumpable(*this) << " (" << list << ", " << access_method << ")"; +} + +void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action action_taken) { + if (access_method == kLinking) { + // Linking warnings come from static analysis/compilation of the bytecode + // and can contain false positives (i.e. code that is never run). We choose + // not to log these in the event log. + return; + } + uint32_t flags = 0; + if (action_taken == kDeny) { + flags |= kAccessDenied; + } + if (type_ == kField) { + flags |= kMemberIsField; + } + android_log_event_list ctx(EVENT_LOG_TAG_art_hidden_api_access); + ctx << access_method; + ctx << flags; + ctx << class_name_; + ctx << member_name_; + ctx << type_signature_; + ctx << LOG_ID_EVENTS; } template @@ -152,6 +183,16 @@ Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) } } + if (kIsTargetBuild) { + uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate(); + // Assert that RAND_MAX is big enough, to ensure sampling below works as expected. + static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small"); + if (eventLogSampleRate != 0 && + (static_cast(std::rand()) & 0xffff) < eventLogSampleRate) { + member_signature.LogAccessToEventLog(access_method, action); + } + } + if (action == kDeny) { // Block access return action; diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index d2c71a7d35..4325496ca3 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -52,11 +52,22 @@ enum Action { kDeny }; +// Do not change the values of items in this enum, as they are written to the +// event log for offline analysis. Any changes will interfere with that analysis. enum AccessMethod { - kNone, // internal test that does not correspond to an actual access by app - kReflection, - kJNI, - kLinking, + kNone = 0, // internal test that does not correspond to an actual access by app + kReflection = 1, + kJNI = 2, + kLinking = 3, +}; + +// Do not change the values of items in this enum, as they are written to the +// event log for offline analysis. Any changes will interfere with that analysis. +enum AccessContextFlags { + // Accessed member is a field if this bit is set, else a method + kMemberIsField = 1 << 0, + // Indicates if access was denied to the member, instead of just printing a warning. + kAccessDenied = 1 << 1, }; inline Action GetActionFromAccessFlags(uint32_t access_flags) { @@ -93,9 +104,18 @@ namespace detail { // is used as a helper when matching prefixes, and when logging the signature. class MemberSignature { private: - std::string member_type_; - std::vector signature_parts_; + enum MemberType { + kField, + kMethod, + }; + + std::string class_name_; + std::string member_name_; + std::string type_signature_; std::string tmp_; + MemberType type_; + + inline std::vector GetSignatureParts() const; public: explicit MemberSignature(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_); @@ -111,6 +131,8 @@ class MemberSignature { bool IsExempted(const std::vector& exemptions); void WarnAboutAccess(AccessMethod access_method, HiddenApiAccessFlags::ApiList list); + + void LogAccessToEventLog(AccessMethod access_method, Action action_taken); }; template diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index a5ade6f30f..264f657973 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -93,6 +93,10 @@ static void VMRuntime_setHiddenApiExemptions(JNIEnv* env, Runtime::Current()->SetHiddenApiExemptions(exemptions_vec); } +static void VMRuntime_setHiddenApiAccessLogSamplingRate(JNIEnv*, jclass, jint rate) { + Runtime::Current()->SetHiddenApiEventLogSampleRate(rate); +} + static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaElementClass, jint length) { ScopedFastNativeObjectAccess soa(env); @@ -688,6 +692,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, disableJitCompilation, "()V"), NATIVE_METHOD(VMRuntime, hasUsedHiddenApi, "()Z"), NATIVE_METHOD(VMRuntime, setHiddenApiExemptions, "([Ljava/lang/String;)V"), + NATIVE_METHOD(VMRuntime, setHiddenApiAccessLogSamplingRate, "(I)V"), NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"), FAST_NATIVE_METHOD(VMRuntime, isDebuggerActive, "()Z"), FAST_NATIVE_METHOD(VMRuntime, isNativeDebuggable, "()Z"), diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index cf0a72a477..84c7926a11 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -363,6 +363,13 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, << "Child zygote processes should be forked with EnforcementPolicy::kDisable"; Runtime::Current()->SetHiddenApiEnforcementPolicy(api_enforcement_policy); Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe_hidden_api_warnings); + if (api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks && + Runtime::Current()->GetHiddenApiEventLogSampleRate() != 0) { + // Hidden API checks are enabled, and we are sampling access for the event log. Initialize the + // random seed, to ensure the sampling is actually random. We do this post-fork, as doing it + // pre-fork would result in the same sequence for every forked process. + std::srand(static_cast(NanoTime())); + } // Clear the hidden API warning flag, in case it was set. Runtime::Current()->SetPendingHiddenApiWarning(false); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 7823014781..e2e315cbba 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -273,6 +273,7 @@ Runtime::Runtime() pending_hidden_api_warning_(false), dedupe_hidden_api_warnings_(true), always_set_hidden_api_warning_flag_(false), + hidden_api_access_event_log_rate_(0), dump_native_stack_on_sig_quit_(true), pruned_dalvik_cache_(false), // Initially assume we perceive jank in case the process state is never updated. diff --git a/runtime/runtime.h b/runtime/runtime.h index 67813a7411..87f5b5134d 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -569,6 +569,14 @@ class Runtime { return always_set_hidden_api_warning_flag_; } + void SetHiddenApiEventLogSampleRate(uint32_t rate) { + hidden_api_access_event_log_rate_ = rate; + } + + uint32_t GetHiddenApiEventLogSampleRate() const { + return hidden_api_access_event_log_rate_; + } + bool IsDexFileFallbackEnabled() const { return allow_dex_file_fallback_; } @@ -1023,6 +1031,10 @@ class Runtime { // when there is a warning. This is only used for testing. bool always_set_hidden_api_warning_flag_; + // How often to log hidden API access to the event log. An integer between 0 (never) + // and 0x10000 (always). + uint32_t hidden_api_access_event_log_rate_; + // Whether threads should dump their native stack on SIGQUIT. bool dump_native_stack_on_sig_quit_; -- GitLab From 85b5a57020dd1a61ae5148ff7c854d4323720afd Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Wed, 11 Apr 2018 16:08:21 +0100 Subject: [PATCH 344/749] Log for light grey API accesses. For the public Beta 1 build, it seems likely that 3P devs will want to check the logcat for hidden API accesses by their own apps. So enable this logging for light grey API accesses. The option can easily be diabled again later by setting the new flag to false (which should be done before public release). Bug: 64382372 Test: m Test: Verify logcat output Merged-In: Ie610ebcf712a0abc3f1c200510048e2c93b712f7 Change-Id: Ie610ebcf712a0abc3f1c200510048e2c93b712f7 (cherry picked from commit 27199e603fb9a80f35976eab63e5f400808598bc) --- runtime/hidden_api.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index c5d09cfa77..c2fd4ee329 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -27,6 +27,11 @@ namespace art { namespace hiddenapi { +// Set to true if we should always print a warning in logcat for all hidden API accesses, not just +// dark grey and black. This can be set to true for developer preview / beta builds, but should be +// false for public release builds. +static constexpr bool kLogAllAccesses = true; + static inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { switch (value) { case kNone: @@ -162,7 +167,7 @@ Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) // - for non-debuggable apps, there is no distinction between light grey & whitelisted APIs. // - we want to avoid the overhead of checking for exemptions for light greylisted APIs whenever // possible. - if (action == kDeny || runtime->IsJavaDebuggable()) { + if (kLogAllAccesses || action == kDeny || runtime->IsJavaDebuggable()) { if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) { action = kAllow; // Avoid re-examining the exemption list next time. -- GitLab From 523cd63ce4b705d07e3ebfe04807c24860f20346 Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Thu, 12 Apr 2018 14:30:11 +0100 Subject: [PATCH 345/749] Fix test after ag/3881475. A side effect of warning for light greylist accesses is that we also check the API exemptions list for then, changing the return value for such APIs from AllowButWarn to Allow. Bug: 64382372 Test: art/test.py --host -t 674-hiddenapi Merged-In: I45f50e5e2ab37b1c8adb09f198e268390dd84e55 Change-Id: I45f50e5e2ab37b1c8adb09f198e268390dd84e55 (cherry picked from commit 724e9c8cdb75cd606fe583fd2502783046d62796) --- test/674-hiddenapi/src-ex/ChildClass.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java index da0c36f536..0349e8fe46 100644 --- a/test/674-hiddenapi/src-ex/ChildClass.java +++ b/test/674-hiddenapi/src-ex/ChildClass.java @@ -91,8 +91,7 @@ public class ChildClass { // Run meaningful combinations of access flags. for (Hiddenness hiddenness : Hiddenness.values()) { final Behaviour expected; - if (isSameBoot || hiddenness == Hiddenness.Whitelist || - (hiddenness == Hiddenness.Blacklist && everythingWhitelisted)) { + if (isSameBoot || hiddenness == Hiddenness.Whitelist || everythingWhitelisted) { expected = Behaviour.Granted; } else if (hiddenness == Hiddenness.Blacklist) { expected = Behaviour.Denied; -- GitLab From 0a054d3e8d00ba4f9841883bf3f8452b8578cf6a Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Thu, 12 Apr 2018 15:43:11 +0100 Subject: [PATCH 346/749] Add note about tests impacted by kLogAllAccesses flag. Test: m Bug: 64382372 Merged-In: Ief4d0596eeff7ed711556b2a23165871d6e06ac3 Change-Id: Ief4d0596eeff7ed711556b2a23165871d6e06ac3 (cherry picked from commit 6d6012ec03d20dd0a450715c41e15d710550f0ac) --- runtime/hidden_api.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index c2fd4ee329..e519682092 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -30,6 +30,9 @@ namespace hiddenapi { // Set to true if we should always print a warning in logcat for all hidden API accesses, not just // dark grey and black. This can be set to true for developer preview / beta builds, but should be // false for public release builds. +// Note that when flipping this flag, you must also update the expectations of test 674-hiddenapi +// as it affects whether or not we warn for light grey APIs that have been added to the exemptions +// list. static constexpr bool kLogAllAccesses = true; static inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { -- GitLab From c9c87f63f7809568fbab751c8f07107e6151cced Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Thu, 19 Apr 2018 14:40:40 +0100 Subject: [PATCH 347/749] Add VMRuntime.setDedupeHiddenApiWarnings(). This exposes the equivalently named functionality in arts Runtime class. Test: m Bug: 78268765 Merged-In: If5b2fff70a079d588ae3bba706ab7f572afac0b2 Change-Id: If5b2fff70a079d588ae3bba706ab7f572afac0b2 (cherry picked from commit 80900a3c6746a64b17121734af776f49f0570723) --- runtime/native/dalvik_system_VMRuntime.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 264f657973..e88ff09430 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -682,6 +682,12 @@ static void VMRuntime_setSystemDaemonThreadPriority(JNIEnv* env ATTRIBUTE_UNUSED #endif } +static void VMRuntime_setDedupeHiddenApiWarnings(JNIEnv* env ATTRIBUTE_UNUSED, + jclass klass ATTRIBUTE_UNUSED, + jboolean dedupe) { + Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe); +} + static JNINativeMethod gMethods[] = { FAST_NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"), NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"), @@ -723,6 +729,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, getCurrentInstructionSet, "()Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, didPruneDalvikCache, "()Z"), NATIVE_METHOD(VMRuntime, setSystemDaemonThreadPriority, "()V"), + NATIVE_METHOD(VMRuntime, setDedupeHiddenApiWarnings, "(Z)V"), }; void register_dalvik_system_VMRuntime(JNIEnv* env) { -- GitLab From 1724520ec2788838413b20672f73afa5a00b0d4c Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Thu, 12 Apr 2018 13:56:37 +0100 Subject: [PATCH 348/749] Use tron for logging hidden API accesses. Example output: sysui_multi_action: [757,1391,1392,1,1394,Ldalvik/system/VMRuntime;->getRuntime()Ldalvik/system/VMRuntime;] sysui_multi_action: [757,1391,1392,1,1393,1,1394,Ldalvik/system/VMRuntime;->setHiddenApiExemptions([Ljava/lang/String;)V] Test: m Test: $ adb shell settings put global \ Test: hidden_api_access_log_sampling_rate 65536 Test: $ adb lolcat -b events | grep sysui_multi_action | grep 1390 Bug: 77517571 Merged-In: I23548f902d2ff56ad00534421bf8afa902edaa75 Change-Id: I23548f902d2ff56ad00534421bf8afa902edaa75 (cherry picked from commit 2d4d62f2c4cb6f4a659333263a32f7d94c08fc11) --- Android.bp | 1 + runtime/Android.bp | 1 + runtime/hidden_api.cc | 50 ++++++++++++++++++++++++++++--------------- runtime/hidden_api.h | 10 ++++----- 4 files changed, 39 insertions(+), 23 deletions(-) diff --git a/Android.bp b/Android.bp index 59f7abf138..34a646915f 100644 --- a/Android.bp +++ b/Android.bp @@ -15,6 +15,7 @@ art_static_dependencies = [ "libbase", "liblz4", "liblzma", + "libmetricslogger_static", ] subdirs = [ diff --git a/runtime/Android.bp b/runtime/Android.bp index 28bee3d67e..1ef5bf080f 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -397,6 +397,7 @@ cc_defaults { "libbacktrace", "liblz4", "liblog", + "libmetricslogger", // For atrace, properties, ashmem, set_sched_policy and socket_peer_is_trusted. "libcutils", // For common macros. diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index e519682092..612a930fab 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include +#include #include "hidden_api.h" @@ -24,6 +24,12 @@ #include "thread-current-inl.h" #include "well_known_classes.h" +using android::metricslogger::ComplexEventLogger; +using android::metricslogger::ACTION_HIDDEN_API_ACCESSED; +using android::metricslogger::FIELD_HIDDEN_API_ACCESS_METHOD; +using android::metricslogger::FIELD_HIDDEN_API_ACCESS_DENIED; +using android::metricslogger::FIELD_HIDDEN_API_SIGNATURE; + namespace art { namespace hiddenapi { @@ -69,10 +75,6 @@ static_assert( namespace detail { -// This is the ID of the event log event. It is duplicated from -// system/core/logcat/event.logtags -constexpr int EVENT_LOG_TAG_art_hidden_api_access = 20004; - MemberSignature::MemberSignature(ArtField* field) { class_name_ = field->GetDeclaringClass()->GetDescriptor(&tmp_); member_name_ = field->GetName(); @@ -131,6 +133,25 @@ void MemberSignature::WarnAboutAccess(AccessMethod access_method, LOG(WARNING) << "Accessing hidden " << (type_ == kField ? "field " : "method ") << Dumpable(*this) << " (" << list << ", " << access_method << ")"; } +// Convert an AccessMethod enum to a value for logging from the proto enum. +// This method may look odd (the enum values are current the same), but it +// prevents coupling the internal enum to the proto enum (which should never +// be changed) so that we are free to change the internal one if necessary in +// future. +inline static int32_t GetEnumValueForLog(AccessMethod access_method) { + switch (access_method) { + case kNone: + return android::metricslogger::ACCESS_METHOD_NONE; + case kReflection: + return android::metricslogger::ACCESS_METHOD_REFLECTION; + case kJNI: + return android::metricslogger::ACCESS_METHOD_JNI; + case kLinking: + return android::metricslogger::ACCESS_METHOD_LINKING; + default: + DCHECK(false); + } +} void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action action_taken) { if (access_method == kLinking) { @@ -139,20 +160,15 @@ void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action act // not to log these in the event log. return; } - uint32_t flags = 0; + ComplexEventLogger log_maker(ACTION_HIDDEN_API_ACCESSED); + log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_METHOD, GetEnumValueForLog(access_method)); if (action_taken == kDeny) { - flags |= kAccessDenied; - } - if (type_ == kField) { - flags |= kMemberIsField; + log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_DENIED, 1); } - android_log_event_list ctx(EVENT_LOG_TAG_art_hidden_api_access); - ctx << access_method; - ctx << flags; - ctx << class_name_; - ctx << member_name_; - ctx << type_signature_; - ctx << LOG_ID_EVENTS; + std::ostringstream signature_str; + Dump(signature_str); + log_maker.AddTaggedData(FIELD_HIDDEN_API_SIGNATURE, signature_str.str()); + log_maker.Record(); } template diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index 4325496ca3..e117c08c25 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -52,13 +52,11 @@ enum Action { kDeny }; -// Do not change the values of items in this enum, as they are written to the -// event log for offline analysis. Any changes will interfere with that analysis. enum AccessMethod { - kNone = 0, // internal test that does not correspond to an actual access by app - kReflection = 1, - kJNI = 2, - kLinking = 3, + kNone, // internal test that does not correspond to an actual access by app + kReflection, + kJNI, + kLinking, }; // Do not change the values of items in this enum, as they are written to the -- GitLab From 2e6f69c704202d41f0ab5ab0aa65583a26184e51 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Thu, 19 Apr 2018 12:41:04 +0100 Subject: [PATCH 349/749] Set hidden API flags of intrinsics Intrinsics overwrite the hidden API access flags of the respective Java method, which is why we need to hardcode their API list membership. VarHandle intrinsics are blacklisted because they could be used to bypass hidden API checks and given they are new in P, should not be used by anybody except tests (that do not enforce API checks). The remaining intrinsics which happen to be @hide are put on light greylist. We used to put them on whitelist implicitly, hence never saw warnings about them. To be safe, we put them on light grey. Note that these are set even for the core image that currently does not have hidden API flags. That is fine because (a) VarHandles can still be tested, and (b) light greylist membership may print warnings but will not change the semantics. Bug: 64382372 Bug: 77733081 Test: make Merged-In: Ia9a7765260acb533560676e7dfcd51065cfb247d Change-Id: Ia9a7765260acb533560676e7dfcd51065cfb247d (cherry picked from commit 49dded0c15dbf3d7c3920ae4744c93c2d6081202) --- runtime/art_method-inl.h | 92 ++++++++++++++++++++++++++++++---------- runtime/art_method.h | 7 +-- 2 files changed, 71 insertions(+), 28 deletions(-) diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 1565644380..4d54ac1d9f 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -384,19 +384,67 @@ inline bool ArtMethod::HasSingleImplementation() { return (GetAccessFlags() & kAccSingleImplementation) != 0; } -inline bool ArtMethod::IsHiddenIntrinsic(uint32_t ordinal) { - switch (static_cast(ordinal)) { - case Intrinsics::kReferenceGetReferent: - case Intrinsics::kSystemArrayCopyChar: - case Intrinsics::kStringGetCharsNoCheck: - case Intrinsics::kVarHandleFullFence: - case Intrinsics::kVarHandleAcquireFence: - case Intrinsics::kVarHandleReleaseFence: - case Intrinsics::kVarHandleLoadLoadFence: - case Intrinsics::kVarHandleStoreStoreFence: - return true; - default: - return false; +inline HiddenApiAccessFlags::ApiList ArtMethod::GetHiddenApiAccessFlags() { + if (UNLIKELY(IsIntrinsic())) { + switch (static_cast(GetIntrinsic())) { + case Intrinsics::kSystemArrayCopyChar: + case Intrinsics::kStringGetCharsNoCheck: + case Intrinsics::kReferenceGetReferent: + // These intrinsics are on the light greylist and will fail a DCHECK in + // SetIntrinsic() if their flags change on the respective dex methods. + // Note that the DCHECK currently won't fail if the dex methods are + // whitelisted, e.g. in the core image (b/77733081). As a result, we + // might print warnings but we won't change the semantics. + return HiddenApiAccessFlags::kLightGreylist; + case Intrinsics::kVarHandleFullFence: + case Intrinsics::kVarHandleAcquireFence: + case Intrinsics::kVarHandleReleaseFence: + case Intrinsics::kVarHandleLoadLoadFence: + case Intrinsics::kVarHandleStoreStoreFence: + case Intrinsics::kVarHandleCompareAndExchange: + case Intrinsics::kVarHandleCompareAndExchangeAcquire: + case Intrinsics::kVarHandleCompareAndExchangeRelease: + case Intrinsics::kVarHandleCompareAndSet: + case Intrinsics::kVarHandleGet: + case Intrinsics::kVarHandleGetAcquire: + case Intrinsics::kVarHandleGetAndAdd: + case Intrinsics::kVarHandleGetAndAddAcquire: + case Intrinsics::kVarHandleGetAndAddRelease: + case Intrinsics::kVarHandleGetAndBitwiseAnd: + case Intrinsics::kVarHandleGetAndBitwiseAndAcquire: + case Intrinsics::kVarHandleGetAndBitwiseAndRelease: + case Intrinsics::kVarHandleGetAndBitwiseOr: + case Intrinsics::kVarHandleGetAndBitwiseOrAcquire: + case Intrinsics::kVarHandleGetAndBitwiseOrRelease: + case Intrinsics::kVarHandleGetAndBitwiseXor: + case Intrinsics::kVarHandleGetAndBitwiseXorAcquire: + case Intrinsics::kVarHandleGetAndBitwiseXorRelease: + case Intrinsics::kVarHandleGetAndSet: + case Intrinsics::kVarHandleGetAndSetAcquire: + case Intrinsics::kVarHandleGetAndSetRelease: + case Intrinsics::kVarHandleGetOpaque: + case Intrinsics::kVarHandleGetVolatile: + case Intrinsics::kVarHandleSet: + case Intrinsics::kVarHandleSetOpaque: + case Intrinsics::kVarHandleSetRelease: + case Intrinsics::kVarHandleSetVolatile: + case Intrinsics::kVarHandleWeakCompareAndSet: + case Intrinsics::kVarHandleWeakCompareAndSetAcquire: + case Intrinsics::kVarHandleWeakCompareAndSetPlain: + case Intrinsics::kVarHandleWeakCompareAndSetRelease: + // These intrinsics are on the blacklist and will fail a DCHECK in + // SetIntrinsic() if their flags change on the respective dex methods. + // Note that the DCHECK currently won't fail if the dex methods are + // whitelisted, e.g. in the core image (b/77733081). Given that they are + // exclusively VarHandle intrinsics, they should not be used outside + // tests that do not enable hidden API checks. + return HiddenApiAccessFlags::kBlacklist; + default: + // Remaining intrinsics are public API. We DCHECK that in SetIntrinsic(). + return HiddenApiAccessFlags::kWhitelist; + } + } else { + return HiddenApiAccessFlags::DecodeFromRuntime(GetAccessFlags()); } } @@ -422,7 +470,7 @@ inline void ArtMethod::SetIntrinsic(uint32_t intrinsic) { bool is_default_conflict = IsDefaultConflicting(); bool is_compilable = IsCompilable(); bool must_count_locks = MustCountLocks(); - HiddenApiAccessFlags::ApiList hidden_api_list = GetHiddenApiAccessFlags(); + HiddenApiAccessFlags::ApiList hidden_api_flags = GetHiddenApiAccessFlags(); SetAccessFlags(new_value); DCHECK_EQ(java_flags, (GetAccessFlags() & kAccJavaFlagsMask)); DCHECK_EQ(is_constructor, IsConstructor()); @@ -436,14 +484,14 @@ inline void ArtMethod::SetIntrinsic(uint32_t intrinsic) { DCHECK_EQ(is_default_conflict, IsDefaultConflicting()); DCHECK_EQ(is_compilable, IsCompilable()); DCHECK_EQ(must_count_locks, MustCountLocks()); - if (kIsDebugBuild) { - if (IsHiddenIntrinsic(intrinsic)) { - // Special case some of our intrinsics because the access flags clash - // with the intrinsics ordinal. - DCHECK_EQ(HiddenApiAccessFlags::kWhitelist, GetHiddenApiAccessFlags()); - } else { - DCHECK_EQ(hidden_api_list, GetHiddenApiAccessFlags()); - } + // Only DCHECK that we have preserved the hidden API access flags if the + // original method was not on the whitelist. This is because the core image + // does not have the access flags set (b/77733081). It is fine to hard-code + // these because (a) warnings on greylist do not change semantics, and + // (b) only VarHandle intrinsics are blacklisted at the moment and they + // should not be used outside tests with disabled API checks. + if (hidden_api_flags != HiddenApiAccessFlags::kWhitelist) { + DCHECK_EQ(hidden_api_flags, GetHiddenApiAccessFlags()); } } else { SetAccessFlags(new_value); diff --git a/runtime/art_method.h b/runtime/art_method.h index 64d293200f..ae13d86934 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -341,9 +341,7 @@ class ArtMethod FINAL { AddAccessFlags(kAccMustCountLocks); } - HiddenApiAccessFlags::ApiList GetHiddenApiAccessFlags() { - return HiddenApiAccessFlags::DecodeFromRuntime(GetAccessFlags()); - } + HiddenApiAccessFlags::ApiList GetHiddenApiAccessFlags(); // Returns true if this method could be overridden by a default method. bool IsOverridableByDefaultMethod() REQUIRES_SHARED(Locks::mutator_lock_); @@ -873,9 +871,6 @@ class ArtMethod FINAL { } while (!access_flags_.compare_exchange_weak(old_access_flags, new_access_flags)); } - // Returns true if the given intrinsic is considered hidden. - bool IsHiddenIntrinsic(uint32_t ordinal); - DISALLOW_COPY_AND_ASSIGN(ArtMethod); // Need to use CopyFrom to deal with 32 vs 64 bits. }; -- GitLab From 166546c3579b7a9deb413f8e44ad94b8ed41335b Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Mon, 23 Apr 2018 13:50:38 +0100 Subject: [PATCH 350/749] Fix hidden API flags decoding for intrinsics Hidden API decision logic would try to decode the access flags of intrinsics directly, bypassing the override in ArtMethod. This patch get hidden_api.h to use the same code path. This also fixes CtsHiddenApiDiscoveryTestCases where the access flags of blacklisted APIs are tested. VarHandle intrinsics would not pass. Bug: 64382372 Bug: 72430785 Bug: 78230396 Test: cts-tradefed run cts --module CtsHiddenApiDiscoveryTestCases Merged-In: I080313dd91bbee2d7d98b00c02e224974b344c01 Change-Id: I080313dd91bbee2d7d98b00c02e224974b344c01 (cherry picked from commit 14c212a44ac9a3ad12025ebf30836129669fa949) --- libdexfile/dex/hidden_api_access_flags.h | 10 ++--- runtime/art_field.h | 5 +++ runtime/art_method-inl.h | 3 +- runtime/art_method.h | 2 +- runtime/hidden_api.h | 20 +++++++--- runtime/hidden_api_test.cc | 51 +++++++++++++----------- 6 files changed, 54 insertions(+), 37 deletions(-) diff --git a/libdexfile/dex/hidden_api_access_flags.h b/libdexfile/dex/hidden_api_access_flags.h index b62d044c6a..1aaeabd783 100644 --- a/libdexfile/dex/hidden_api_access_flags.h +++ b/libdexfile/dex/hidden_api_access_flags.h @@ -86,12 +86,10 @@ class HiddenApiAccessFlags { } static ALWAYS_INLINE ApiList DecodeFromRuntime(uint32_t runtime_access_flags) { - if ((runtime_access_flags & kAccIntrinsic) != 0) { - return kWhitelist; - } else { - uint32_t int_value = (runtime_access_flags & kAccHiddenApiBits) >> kAccFlagsShift; - return static_cast(int_value); - } + // This is used in the fast path, only DCHECK here. + DCHECK_EQ(runtime_access_flags & kAccIntrinsic, 0u); + uint32_t int_value = (runtime_access_flags & kAccHiddenApiBits) >> kAccFlagsShift; + return static_cast(int_value); } static ALWAYS_INLINE uint32_t EncodeForRuntime(uint32_t runtime_access_flags, ApiList value) { diff --git a/runtime/art_field.h b/runtime/art_field.h index 29d71af358..f39af3900c 100644 --- a/runtime/art_field.h +++ b/runtime/art_field.h @@ -20,6 +20,7 @@ #include #include "dex/dex_file_types.h" +#include "dex/hidden_api_access_flags.h" #include "dex/modifiers.h" #include "dex/primitive.h" #include "gc_root.h" @@ -179,6 +180,10 @@ class ArtField FINAL { return (GetAccessFlags() & kAccVolatile) != 0; } + HiddenApiAccessFlags::ApiList GetHiddenApiAccessFlags() REQUIRES_SHARED(Locks::mutator_lock_) { + return HiddenApiAccessFlags::DecodeFromRuntime(GetAccessFlags()); + } + // Returns an instance field with this offset in the given class or null if not found. // If kExactOffset is true then we only find the matching offset, not the field containing the // offset. diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 4d54ac1d9f..c1fac364bb 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -384,7 +384,8 @@ inline bool ArtMethod::HasSingleImplementation() { return (GetAccessFlags() & kAccSingleImplementation) != 0; } -inline HiddenApiAccessFlags::ApiList ArtMethod::GetHiddenApiAccessFlags() { +inline HiddenApiAccessFlags::ApiList ArtMethod::GetHiddenApiAccessFlags() + REQUIRES_SHARED(Locks::mutator_lock_) { if (UNLIKELY(IsIntrinsic())) { switch (static_cast(GetIntrinsic())) { case Intrinsics::kSystemArrayCopyChar: diff --git a/runtime/art_method.h b/runtime/art_method.h index ae13d86934..acaa4a68a1 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -341,7 +341,7 @@ class ArtMethod FINAL { AddAccessFlags(kAccMustCountLocks); } - HiddenApiAccessFlags::ApiList GetHiddenApiAccessFlags(); + HiddenApiAccessFlags::ApiList GetHiddenApiAccessFlags() REQUIRES_SHARED(Locks::mutator_lock_); // Returns true if this method could be overridden by a default method. bool IsOverridableByDefaultMethod() REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index e117c08c25..5762bfefc1 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -68,17 +68,17 @@ enum AccessContextFlags { kAccessDenied = 1 << 1, }; -inline Action GetActionFromAccessFlags(uint32_t access_flags) { +inline Action GetActionFromAccessFlags(HiddenApiAccessFlags::ApiList api_list) { + if (api_list == HiddenApiAccessFlags::kWhitelist) { + return kAllow; + } + EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); if (policy == EnforcementPolicy::kNoChecks) { // Exit early. Nothing to enforce. return kAllow; } - HiddenApiAccessFlags::ApiList api_list = HiddenApiAccessFlags::DecodeFromRuntime(access_flags); - if (api_list == HiddenApiAccessFlags::kWhitelist) { - return kAllow; - } // if policy is "just warn", always warn. We returned above for whitelist APIs. if (policy == EnforcementPolicy::kJustWarn) { return kAllowButWarn; @@ -169,7 +169,15 @@ inline Action GetMemberAction(T* member, REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); - Action action = GetActionFromAccessFlags(member->GetAccessFlags()); + // Decode hidden API access flags. + // NB Multiple threads might try to access (and overwrite) these simultaneously, + // causing a race. We only do that if access has not been denied, so the race + // cannot change Java semantics. We should, however, decode the access flags + // once and use it throughout this function, otherwise we may get inconsistent + // results, e.g. print whitelist warnings (b/78327881). + HiddenApiAccessFlags::ApiList api_list = member->GetHiddenApiAccessFlags(); + + Action action = GetActionFromAccessFlags(member->GetHiddenApiAccessFlags()); if (action == kAllow) { // Nothing to do. return action; diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc index 65d6363bfd..1985c6bb19 100644 --- a/runtime/hidden_api_test.cc +++ b/runtime/hidden_api_test.cc @@ -86,36 +86,41 @@ class HiddenApiTest : public CommonRuntimeTest { }; TEST_F(HiddenApiTest, CheckGetActionFromRuntimeFlags) { - uint32_t whitelist = HiddenApiAccessFlags::EncodeForRuntime(0, HiddenApiAccessFlags::kWhitelist); - uint32_t lightgreylist = - HiddenApiAccessFlags::EncodeForRuntime(0, HiddenApiAccessFlags::kLightGreylist); - uint32_t darkgreylist = - HiddenApiAccessFlags::EncodeForRuntime(0, HiddenApiAccessFlags::kDarkGreylist); - uint32_t blacklist = HiddenApiAccessFlags::EncodeForRuntime(0, HiddenApiAccessFlags::kBlacklist); - runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks); - ASSERT_EQ(GetActionFromAccessFlags(whitelist), hiddenapi::kAllow); - ASSERT_EQ(GetActionFromAccessFlags(lightgreylist), hiddenapi::kAllow); - ASSERT_EQ(GetActionFromAccessFlags(darkgreylist), hiddenapi::kAllow); - ASSERT_EQ(GetActionFromAccessFlags(blacklist), hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kWhitelist), hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kLightGreylist), hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kDarkGreylist), hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kBlacklist), hiddenapi::kAllow); runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kJustWarn); - ASSERT_EQ(GetActionFromAccessFlags(whitelist), hiddenapi::kAllow); - ASSERT_EQ(GetActionFromAccessFlags(lightgreylist), hiddenapi::kAllowButWarn); - ASSERT_EQ(GetActionFromAccessFlags(darkgreylist), hiddenapi::kAllowButWarn); - ASSERT_EQ(GetActionFromAccessFlags(blacklist), hiddenapi::kAllowButWarn); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kWhitelist), + hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kLightGreylist), + hiddenapi::kAllowButWarn); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kDarkGreylist), + hiddenapi::kAllowButWarn); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kBlacklist), + hiddenapi::kAllowButWarn); runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList); - ASSERT_EQ(GetActionFromAccessFlags(whitelist), hiddenapi::kAllow); - ASSERT_EQ(GetActionFromAccessFlags(lightgreylist), hiddenapi::kAllowButWarn); - ASSERT_EQ(GetActionFromAccessFlags(darkgreylist), hiddenapi::kDeny); - ASSERT_EQ(GetActionFromAccessFlags(blacklist), hiddenapi::kDeny); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kWhitelist), + hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kLightGreylist), + hiddenapi::kAllowButWarn); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kDarkGreylist), + hiddenapi::kDeny); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kBlacklist), + hiddenapi::kDeny); runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly); - ASSERT_EQ(GetActionFromAccessFlags(whitelist), hiddenapi::kAllow); - ASSERT_EQ(GetActionFromAccessFlags(lightgreylist), hiddenapi::kAllowButWarn); - ASSERT_EQ(GetActionFromAccessFlags(darkgreylist), hiddenapi::kAllowButWarnAndToast); - ASSERT_EQ(GetActionFromAccessFlags(blacklist), hiddenapi::kDeny); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kWhitelist), + hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kLightGreylist), + hiddenapi::kAllowButWarn); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kDarkGreylist), + hiddenapi::kAllowButWarnAndToast); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kBlacklist), + hiddenapi::kDeny); } TEST_F(HiddenApiTest, CheckMembersRead) { -- GitLab From 39512f5ac08fb8b567336d7c3874065fce062ce7 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Mon, 23 Apr 2018 13:51:16 +0100 Subject: [PATCH 351/749] Fix race in hidden_api.h The hidden API decision logic can dedupe warnings by changing the access flags of a previously warned about method to whitelist, thus exiting early on the next attempt to access it. This logic had a race in it, because it would decode the flags, make a decision and then decode them again when printing a warning. If another thread changed the flags in between, the warning would say "whitelist". Change the code so that the hidden API flags are decoded only once. This may still result in multiple warnings about the same API, but at least the messages will be consistent. Bug: 78327881 Bug: 64382372 Test: none Merged-In: I956dd56536bdfce492845e6a90fdcbe29c2676b5 Change-Id: I956dd56536bdfce492845e6a90fdcbe29c2676b5 (cherry picked from commit b8c6619f16348403c3933d9e425b4b8c80af5389) --- runtime/hidden_api.cc | 10 +++++++--- runtime/hidden_api.h | 7 +++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index 612a930fab..bc6a8f28de 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -172,7 +172,10 @@ void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action act } template -Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) { +Action GetMemberActionImpl(T* member, + HiddenApiAccessFlags::ApiList api_list, + Action action, + AccessMethod access_method) { DCHECK_NE(action, kAllow); // Get the signature, we need it later. @@ -202,8 +205,7 @@ Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) if (access_method != kNone) { // Print a log message with information about this class member access. // We do this if we're about to block access, or the app is debuggable. - member_signature.WarnAboutAccess(access_method, - HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags())); + member_signature.WarnAboutAccess(access_method, api_list); } } @@ -244,9 +246,11 @@ Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) // Need to instantiate this. template Action GetMemberActionImpl(ArtField* member, + HiddenApiAccessFlags::ApiList api_list, Action action, AccessMethod access_method); template Action GetMemberActionImpl(ArtMethod* member, + HiddenApiAccessFlags::ApiList api_list, Action action, AccessMethod access_method); } // namespace detail diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index 5762bfefc1..65c6406393 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -134,7 +134,10 @@ class MemberSignature { }; template -Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) +Action GetMemberActionImpl(T* member, + HiddenApiAccessFlags::ApiList api_list, + Action action, + AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_); // Returns true if the caller is either loaded by the boot strap class loader or comes from @@ -191,7 +194,7 @@ inline Action GetMemberAction(T* member, } // Member is hidden and caller is not in the platform. - return detail::GetMemberActionImpl(member, action, access_method); + return detail::GetMemberActionImpl(member, api_list, action, access_method); } inline bool IsCallerInPlatformDex(ObjPtr caller) -- GitLab From bb0454a067389a7a1370d2150bf59efd5aac20a1 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Thu, 26 Apr 2018 16:52:11 +0100 Subject: [PATCH 352/749] Do not overwrite hidden access flags for intrinsics Deduplicating warnings works by overwriting the runtime access flags of a method/field to move it from a greylist to the whitelist. This triggers a CHECK when attempted on an intrinsic as their ordinal bits clash with the hidden API access flags. Do not attempt to deduplicate for those. Bug: 78574586 Test: (cd cts/tests/signature ; ./runSignatureTests.sh) Merged-In: I39e555a0f4cd5f662eea348baf4ef72a5827306d Change-Id: I39e555a0f4cd5f662eea348baf4ef72a5827306d (cherry picked from commit 8a6b2f3353d81d8ceb6826bd7b1dd1987c1a6fee) --- runtime/hidden_api.cc | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index bc6a8f28de..e8918e75b0 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -171,6 +171,23 @@ void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action act log_maker.Record(); } +static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtField*) { + return true; +} + +static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtMethod* method) { + return !method->IsIntrinsic(); +} + +template +static ALWAYS_INLINE void MaybeWhitelistMember(Runtime* runtime, T* member) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (CanUpdateMemberAccessFlags(member) && runtime->ShouldDedupeHiddenApiWarnings()) { + member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( + member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist)); + } +} + template Action GetMemberActionImpl(T* member, HiddenApiAccessFlags::ApiList api_list, @@ -195,10 +212,7 @@ Action GetMemberActionImpl(T* member, // Avoid re-examining the exemption list next time. // Note this results in no warning for the member, which seems like what one would expect. // Exemptions effectively adds new members to the whitelist. - if (runtime->ShouldDedupeHiddenApiWarnings()) { - member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( - member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist)); - } + MaybeWhitelistMember(runtime, member); return kAllow; } @@ -230,10 +244,7 @@ Action GetMemberActionImpl(T* member, if (access_method != kNone) { // Depending on a runtime flag, we might move the member into whitelist and // skip the warning the next time the member is accessed. - if (runtime->ShouldDedupeHiddenApiWarnings()) { - member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( - member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist)); - } + MaybeWhitelistMember(runtime, member); // If this action requires a UI warning, set the appropriate flag. if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) { -- GitLab From 1f9d3c3a11cd106246d814ac02b6dafd881f40ad Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Wed, 2 May 2018 16:53:06 +0100 Subject: [PATCH 353/749] Fix hiddenapi::MemberSignature for proxies Proxy classes are classes generated at runtime which implement a given interface. Because they do not inherit the associated dex file form the interface(s), names/signatures of methods cannot be requested directly, but rather through the original interface method. Calling getName() on a proxy mirror::Class also triggers a DCHECK. This patch will refer to the interface method when printing the signature instead of the proxy method. This fixes the warning printed for the proxy method, printing even the class name of the interface instead of the name of the proxy class. This is meant to provide useful info to the devloper. Proxies do not define fields except for the synthetic 'interfaces' and 'throws' fields. Their signatures remain unchanged. This patch also continues to check the access flags of the proxy method for performance reasons. De-proxying the method would introduce two new memory accesses into the fast path. That means deduplication of warnings remains independent between the original and proxy methods. Bug: 78327881 Test: make test-art-host-gtest-hidden_api_test Merged-In: I8f334e5e2b62ca38691c94524edaf198eb73574b Change-Id: I8f334e5e2b62ca38691c94524edaf198eb73574b (cherry picked from commit 73a64f6a2a475c2fe018c7ab1151e3f44d316533) --- runtime/hidden_api.cc | 4 + runtime/hidden_api_test.cc | 56 +++++++++++- runtime/proxy_test.cc | 99 +++----------------- runtime/proxy_test.h | 114 ++++++++++++++++++++++++ test/HiddenApiSignatures/Interface.java | 21 +++++ 5 files changed, 205 insertions(+), 89 deletions(-) create mode 100644 runtime/proxy_test.h create mode 100644 test/HiddenApiSignatures/Interface.java diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index e8918e75b0..9445ae0c8e 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -83,6 +83,10 @@ MemberSignature::MemberSignature(ArtField* field) { } MemberSignature::MemberSignature(ArtMethod* method) { + // If this is a proxy method, print the signature of the interface method. + method = method->GetInterfaceMethodIfProxy( + Runtime::Current()->GetClassLinker()->GetImagePointerSize()); + class_name_ = method->GetDeclaringClass()->GetDescriptor(&tmp_); member_name_ = method->GetName(); type_signature_ = method->GetSignature().ToString(); diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc index 1985c6bb19..68d4eecbb9 100644 --- a/runtime/hidden_api_test.cc +++ b/runtime/hidden_api_test.cc @@ -18,6 +18,7 @@ #include "common_runtime_test.h" #include "jni_internal.h" +#include "proxy_test.h" namespace art { @@ -31,7 +32,7 @@ class HiddenApiTest : public CommonRuntimeTest { CommonRuntimeTest::SetUp(); self_ = Thread::Current(); self_->TransitionFromSuspendedToRunnable(); - LoadDex("HiddenApiSignatures"); + jclass_loader_ = LoadDex("HiddenApiSignatures"); bool started = runtime_->Start(); CHECK(started); @@ -69,6 +70,7 @@ class HiddenApiTest : public CommonRuntimeTest { protected: Thread* self_; + jobject jclass_loader_; ArtField* class1_field1_; ArtField* class1_field12_; ArtMethod* class1_init_; @@ -311,4 +313,56 @@ TEST_F(HiddenApiTest, CheckFieldTrailingCharsNoMatch) { ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); } +TEST_F(HiddenApiTest, CheckMemberSignatureForProxyClass) { + ScopedObjectAccess soa(self_); + StackHandleScope<4> hs(soa.Self()); + Handle class_loader( + hs.NewHandle(soa.Decode(jclass_loader_))); + + // Find interface we will create a proxy for. + Handle h_iface(hs.NewHandle( + class_linker_->FindClass(soa.Self(), "Lmypackage/packagea/Interface;", class_loader))); + ASSERT_TRUE(h_iface != nullptr); + + // Create the proxy class. + std::vector interfaces; + interfaces.push_back(h_iface.Get()); + Handle proxyClass = hs.NewHandle(proxy_test::GenerateProxyClass( + soa, jclass_loader_, runtime_->GetClassLinker(), "$Proxy1234", interfaces)); + ASSERT_TRUE(proxyClass != nullptr); + ASSERT_TRUE(proxyClass->IsProxyClass()); + ASSERT_TRUE(proxyClass->IsInitialized()); + + // Find the "method" virtual method. + ArtMethod* method = nullptr; + for (auto& m : proxyClass->GetDeclaredVirtualMethods(kRuntimePointerSize)) { + if (strcmp("method", m.GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetName()) == 0) { + method = &m; + break; + } + } + ASSERT_TRUE(method != nullptr); + + // Find the "interfaces" static field. This is generated for all proxies. + ArtField* field = nullptr; + for (size_t i = 0; i < proxyClass->NumStaticFields(); ++i) { + ArtField* f = proxyClass->GetStaticField(i); + if (strcmp("interfaces", f->GetName()) == 0) { + field = f; + break; + } + } + ASSERT_TRUE(field != nullptr); + + // Test the signature. We expect the signature from the interface class. + std::ostringstream ss_method; + MemberSignature(method).Dump(ss_method); + ASSERT_EQ("Lmypackage/packagea/Interface;->method()V", ss_method.str()); + + // Test the signature. We expect the signature of the proxy class. + std::ostringstream ss_field; + MemberSignature(field).Dump(ss_field); + ASSERT_EQ("L$Proxy1234;->interfaces:[Ljava/lang/Class;", ss_field.str()); +} + } // namespace art diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc index 3ad3a4b6a9..4e0bf890db 100644 --- a/runtime/proxy_test.cc +++ b/runtime/proxy_test.cc @@ -18,97 +18,16 @@ #include #include "art_field-inl.h" -#include "art_method-inl.h" #include "base/enums.h" -#include "class_linker-inl.h" #include "common_compiler_test.h" -#include "mirror/class-inl.h" #include "mirror/field-inl.h" -#include "mirror/method.h" +#include "proxy_test.h" #include "scoped_thread_state_change-inl.h" namespace art { +namespace proxy_test { -class ProxyTest : public CommonCompilerTest { - public: - // Generate a proxy class with the given name and interfaces. This is a simplification from what - // libcore does to fit to our test needs. We do not check for duplicated interfaces or methods and - // we do not declare exceptions. - mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, jobject jclass_loader, - const char* className, - const std::vector& interfaces) - REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::Class* javaLangObject = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); - CHECK(javaLangObject != nullptr); - - jclass javaLangClass = soa.AddLocalReference(mirror::Class::GetJavaLangClass()); - - // Builds the interfaces array. - jobjectArray proxyClassInterfaces = soa.Env()->NewObjectArray(interfaces.size(), javaLangClass, - nullptr); - soa.Self()->AssertNoPendingException(); - for (size_t i = 0; i < interfaces.size(); ++i) { - soa.Env()->SetObjectArrayElement(proxyClassInterfaces, i, - soa.AddLocalReference(interfaces[i])); - } - - // Builds the method array. - jsize methods_count = 3; // Object.equals, Object.hashCode and Object.toString. - for (mirror::Class* interface : interfaces) { - methods_count += interface->NumVirtualMethods(); - } - jobjectArray proxyClassMethods = soa.Env()->NewObjectArray( - methods_count, soa.AddLocalReference(mirror::Method::StaticClass()), nullptr); - soa.Self()->AssertNoPendingException(); - - jsize array_index = 0; - // Fill the method array - DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); - ArtMethod* method = javaLangObject->FindClassMethod( - "equals", "(Ljava/lang/Object;)Z", kRuntimePointerSize); - CHECK(method != nullptr); - CHECK(!method->IsDirect()); - CHECK(method->GetDeclaringClass() == javaLangObject); - DCHECK(!Runtime::Current()->IsActiveTransaction()); - soa.Env()->SetObjectArrayElement( - proxyClassMethods, array_index++, soa.AddLocalReference( - mirror::Method::CreateFromArtMethod(soa.Self(), method))); - method = javaLangObject->FindClassMethod("hashCode", "()I", kRuntimePointerSize); - CHECK(method != nullptr); - CHECK(!method->IsDirect()); - CHECK(method->GetDeclaringClass() == javaLangObject); - soa.Env()->SetObjectArrayElement( - proxyClassMethods, array_index++, soa.AddLocalReference( - mirror::Method::CreateFromArtMethod(soa.Self(), method))); - method = javaLangObject->FindClassMethod( - "toString", "()Ljava/lang/String;", kRuntimePointerSize); - CHECK(method != nullptr); - CHECK(!method->IsDirect()); - CHECK(method->GetDeclaringClass() == javaLangObject); - soa.Env()->SetObjectArrayElement( - proxyClassMethods, array_index++, soa.AddLocalReference( - mirror::Method::CreateFromArtMethod(soa.Self(), method))); - // Now adds all interfaces virtual methods. - for (mirror::Class* interface : interfaces) { - for (auto& m : interface->GetDeclaredVirtualMethods(kRuntimePointerSize)) { - soa.Env()->SetObjectArrayElement( - proxyClassMethods, array_index++, soa.AddLocalReference( - mirror::Method::CreateFromArtMethod(soa.Self(), &m))); - } - } - CHECK_EQ(array_index, methods_count); - - // Builds an empty exception array. - jobjectArray proxyClassThrows = soa.Env()->NewObjectArray(0, javaLangClass, nullptr); - soa.Self()->AssertNoPendingException(); - - mirror::Class* proxyClass = class_linker_->CreateProxyClass( - soa, soa.Env()->NewStringUTF(className), proxyClassInterfaces, jclass_loader, - proxyClassMethods, proxyClassThrows); - soa.Self()->AssertNoPendingException(); - return proxyClass; - } -}; +class ProxyTest : public CommonRuntimeTest {}; // Creates a proxy class and check ClassHelper works correctly. TEST_F(ProxyTest, ProxyClassHelper) { @@ -129,7 +48,7 @@ TEST_F(ProxyTest, ProxyClassHelper) { interfaces.push_back(I.Get()); interfaces.push_back(J.Get()); Handle proxy_class(hs.NewHandle( - GenerateProxyClass(soa, jclass_loader, "$Proxy1234", interfaces))); + GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy1234", interfaces))); interfaces.clear(); // Don't least possibly stale objects in the array as good practice. ASSERT_TRUE(proxy_class != nullptr); ASSERT_TRUE(proxy_class->IsProxyClass()); @@ -164,7 +83,8 @@ TEST_F(ProxyTest, ProxyFieldHelper) { std::vector interfaces; interfaces.push_back(I.Get()); interfaces.push_back(J.Get()); - proxyClass = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy1234", interfaces)); + proxyClass = hs.NewHandle( + GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy1234", interfaces)); } ASSERT_TRUE(proxyClass != nullptr); @@ -212,8 +132,10 @@ TEST_F(ProxyTest, CheckArtMirrorFieldsOfProxyStaticFields) { Handle proxyClass1; { std::vector interfaces; - proxyClass0 = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy0", interfaces)); - proxyClass1 = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy1", interfaces)); + proxyClass0 = hs.NewHandle( + GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy0", interfaces)); + proxyClass1 = hs.NewHandle( + GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy1", interfaces)); } ASSERT_TRUE(proxyClass0 != nullptr); @@ -255,4 +177,5 @@ TEST_F(ProxyTest, CheckArtMirrorFieldsOfProxyStaticFields) { EXPECT_EQ(field11->GetArtField(), &static_fields1->At(1)); } +} // namespace proxy_test } // namespace art diff --git a/runtime/proxy_test.h b/runtime/proxy_test.h new file mode 100644 index 0000000000..b559823257 --- /dev/null +++ b/runtime/proxy_test.h @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#ifndef ART_RUNTIME_PROXY_TEST_H_ +#define ART_RUNTIME_PROXY_TEST_H_ + +#include +#include + +#include "art_method-inl.h" +#include "class_linker-inl.h" +#include "mirror/class-inl.h" +#include "mirror/method.h" + +namespace art { +namespace proxy_test { + +// Generate a proxy class with the given name and interfaces. This is a simplification from what +// libcore does to fit to our test needs. We do not check for duplicated interfaces or methods and +// we do not declare exceptions. +mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, + jobject jclass_loader, + ClassLinker* class_linker, + const char* className, + const std::vector& interfaces) + REQUIRES_SHARED(Locks::mutator_lock_) { + mirror::Class* javaLangObject = class_linker->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); + CHECK(javaLangObject != nullptr); + + jclass javaLangClass = soa.AddLocalReference(mirror::Class::GetJavaLangClass()); + + // Builds the interfaces array. + jobjectArray proxyClassInterfaces = soa.Env()->NewObjectArray(interfaces.size(), javaLangClass, + nullptr); + soa.Self()->AssertNoPendingException(); + for (size_t i = 0; i < interfaces.size(); ++i) { + soa.Env()->SetObjectArrayElement(proxyClassInterfaces, i, + soa.AddLocalReference(interfaces[i])); + } + + // Builds the method array. + jsize methods_count = 3; // Object.equals, Object.hashCode and Object.toString. + for (mirror::Class* interface : interfaces) { + methods_count += interface->NumVirtualMethods(); + } + jobjectArray proxyClassMethods = soa.Env()->NewObjectArray( + methods_count, soa.AddLocalReference(mirror::Method::StaticClass()), nullptr); + soa.Self()->AssertNoPendingException(); + + jsize array_index = 0; + // Fill the method array + DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); + ArtMethod* method = javaLangObject->FindClassMethod( + "equals", "(Ljava/lang/Object;)Z", kRuntimePointerSize); + CHECK(method != nullptr); + CHECK(!method->IsDirect()); + CHECK(method->GetDeclaringClass() == javaLangObject); + DCHECK(!Runtime::Current()->IsActiveTransaction()); + soa.Env()->SetObjectArrayElement( + proxyClassMethods, array_index++, soa.AddLocalReference( + mirror::Method::CreateFromArtMethod(soa.Self(), method))); + method = javaLangObject->FindClassMethod("hashCode", "()I", kRuntimePointerSize); + CHECK(method != nullptr); + CHECK(!method->IsDirect()); + CHECK(method->GetDeclaringClass() == javaLangObject); + soa.Env()->SetObjectArrayElement( + proxyClassMethods, array_index++, soa.AddLocalReference( + mirror::Method::CreateFromArtMethod(soa.Self(), method))); + method = javaLangObject->FindClassMethod( + "toString", "()Ljava/lang/String;", kRuntimePointerSize); + CHECK(method != nullptr); + CHECK(!method->IsDirect()); + CHECK(method->GetDeclaringClass() == javaLangObject); + soa.Env()->SetObjectArrayElement( + proxyClassMethods, array_index++, soa.AddLocalReference( + mirror::Method::CreateFromArtMethod(soa.Self(), method))); + // Now adds all interfaces virtual methods. + for (mirror::Class* interface : interfaces) { + for (auto& m : interface->GetDeclaredVirtualMethods(kRuntimePointerSize)) { + soa.Env()->SetObjectArrayElement( + proxyClassMethods, array_index++, soa.AddLocalReference( + mirror::Method::CreateFromArtMethod(soa.Self(), &m))); + } + } + CHECK_EQ(array_index, methods_count); + + // Builds an empty exception array. + jobjectArray proxyClassThrows = soa.Env()->NewObjectArray(0, javaLangClass, nullptr); + soa.Self()->AssertNoPendingException(); + + mirror::Class* proxyClass = class_linker->CreateProxyClass( + soa, soa.Env()->NewStringUTF(className), proxyClassInterfaces, jclass_loader, + proxyClassMethods, proxyClassThrows); + soa.Self()->AssertNoPendingException(); + return proxyClass; +} + +} // namespace proxy_test +} // namespace art + +#endif // ART_RUNTIME_PROXY_TEST_H_ diff --git a/test/HiddenApiSignatures/Interface.java b/test/HiddenApiSignatures/Interface.java new file mode 100644 index 0000000000..f141d09bf6 --- /dev/null +++ b/test/HiddenApiSignatures/Interface.java @@ -0,0 +1,21 @@ +/* + * 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 mypackage.packagea; + +public interface Interface { + public void method(); +} -- GitLab From bd78567cef305e35481734b7fc24f68ded031439 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 3 May 2018 17:09:09 +0100 Subject: [PATCH 354/749] Store HIR type in HInstruction::packed_field_. This is similar to https://android-review.googlesource.com/609566 though the performance impact has not been measured. However, avoiding a virtual call reduces pressure on the branch predictor and provides better optimization opportunities for the C++ compiler. As there is now no difference between HTemplateInstruction<> and HExpression<> (the type is stored in HInstruction), we remove the former and use HExpression<> for all instructions that have a fixed number of inputs. Test: Rely on TreeHugger. Change-Id: Ib3fd111048b0ac38ee65386a7e5af70c5ccc98de --- compiler/optimizing/nodes.h | 248 +++++++++++++---------------- compiler/optimizing/nodes_mips.h | 4 +- compiler/optimizing/nodes_vector.h | 26 ++- compiler/optimizing/nodes_x86.h | 4 +- 4 files changed, 124 insertions(+), 158 deletions(-) diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 79d733060b..295f570b34 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1965,6 +1965,9 @@ class HInstruction : public ArenaObject { #undef DECLARE_KIND HInstruction(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc) + : HInstruction(kind, DataType::Type::kVoid, side_effects, dex_pc) {} + + HInstruction(InstructionKind kind, DataType::Type type, SideEffects side_effects, uint32_t dex_pc) : previous_(nullptr), next_(nullptr), block_(nullptr), @@ -1979,6 +1982,7 @@ class HInstruction : public ArenaObject { side_effects_(side_effects), reference_type_handle_(ReferenceTypeInfo::CreateInvalid().GetTypeHandle()) { SetPackedField(kind); + SetPackedField(type); SetPackedFlag(ReferenceTypeInfo::CreateInvalid().IsExact()); } @@ -2036,7 +2040,9 @@ class HInstruction : public ArenaObject { virtual void Accept(HGraphVisitor* visitor) = 0; virtual const char* DebugName() const = 0; - virtual DataType::Type GetType() const { return DataType::Type::kVoid; } + DataType::Type GetType() const { + return TypeField::Decode(GetPackedFields()); + } virtual bool NeedsEnvironment() const { return false; } @@ -2342,13 +2348,18 @@ class HInstruction : public ArenaObject { static constexpr size_t kFieldInstructionKind = kFlagReferenceTypeIsExact + 1; static constexpr size_t kFieldInstructionKindSize = MinimumBitsToStore(static_cast(InstructionKind::kLastInstructionKind - 1)); - static constexpr size_t kNumberOfGenericPackedBits = + static constexpr size_t kFieldType = kFieldInstructionKind + kFieldInstructionKindSize; + static constexpr size_t kFieldTypeSize = + MinimumBitsToStore(static_cast(DataType::Type::kLast)); + static constexpr size_t kNumberOfGenericPackedBits = kFieldType + kFieldTypeSize; static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte; static_assert(kNumberOfGenericPackedBits <= kMaxNumberOfPackedBits, "Too many generic packed fields"); + using TypeField = BitField; + const HUserRecord InputRecordAt(size_t i) const { return GetInputRecords()[i]; } @@ -2595,6 +2606,15 @@ class HVariableInputSizeInstruction : public HInstruction { ArenaAllocKind kind) : HInstruction(inst_kind, side_effects, dex_pc), inputs_(number_of_inputs, allocator->Adapter(kind)) {} + HVariableInputSizeInstruction(InstructionKind inst_kind, + DataType::Type type, + SideEffects side_effects, + uint32_t dex_pc, + ArenaAllocator* allocator, + size_t number_of_inputs, + ArenaAllocKind kind) + : HInstruction(inst_kind, type, side_effects, dex_pc), + inputs_(number_of_inputs, allocator->Adapter(kind)) {} DEFAULT_COPY_CONSTRUCTOR(VariableInputSizeInstruction); @@ -2602,11 +2622,16 @@ class HVariableInputSizeInstruction : public HInstruction { }; template -class HTemplateInstruction: public HInstruction { +class HExpression : public HInstruction { public: - HTemplateInstruction(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc) + HExpression(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc) : HInstruction(kind, side_effects, dex_pc), inputs_() {} - virtual ~HTemplateInstruction() {} + HExpression(InstructionKind kind, + DataType::Type type, + SideEffects side_effects, + uint32_t dex_pc) + : HInstruction(kind, type, side_effects, dex_pc), inputs_() {} + virtual ~HExpression() {} using HInstruction::GetInputRecords; // Keep the const version visible. ArrayRef> GetInputRecords() OVERRIDE FINAL { @@ -2614,7 +2639,7 @@ class HTemplateInstruction: public HInstruction { } protected: - DEFAULT_COPY_CONSTRUCTOR(TemplateInstruction); + DEFAULT_COPY_CONSTRUCTOR(Expression); private: std::array, N> inputs_; @@ -2622,14 +2647,13 @@ class HTemplateInstruction: public HInstruction { friend class SsaBuilder; }; -// HTemplateInstruction specialization for N=0. +// HExpression specialization for N=0. template<> -class HTemplateInstruction<0>: public HInstruction { +class HExpression<0> : public HInstruction { public: - explicit HTemplateInstruction<0>(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc) - : HInstruction(kind, side_effects, dex_pc) {} + using HInstruction::HInstruction; - virtual ~HTemplateInstruction() {} + virtual ~HExpression() {} using HInstruction::GetInputRecords; // Keep the const version visible. ArrayRef> GetInputRecords() OVERRIDE FINAL { @@ -2637,46 +2661,18 @@ class HTemplateInstruction<0>: public HInstruction { } protected: - DEFAULT_COPY_CONSTRUCTOR(TemplateInstruction<0>); + DEFAULT_COPY_CONSTRUCTOR(Expression<0>); private: friend class SsaBuilder; }; -template -class HExpression : public HTemplateInstruction { - public: - using HInstruction::InstructionKind; - HExpression(InstructionKind kind, - DataType::Type type, - SideEffects side_effects, - uint32_t dex_pc) - : HTemplateInstruction(kind, side_effects, dex_pc) { - this->template SetPackedField(type); - } - virtual ~HExpression() {} - - DataType::Type GetType() const OVERRIDE { - return TypeField::Decode(this->GetPackedFields()); - } - - protected: - static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits; - static constexpr size_t kFieldTypeSize = - MinimumBitsToStore(static_cast(DataType::Type::kLast)); - static constexpr size_t kNumberOfExpressionPackedBits = kFieldType + kFieldTypeSize; - static_assert(kNumberOfExpressionPackedBits <= HInstruction::kMaxNumberOfPackedBits, - "Too many packed fields."); - using TypeField = BitField; - DEFAULT_COPY_CONSTRUCTOR(Expression); -}; - // Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow // instruction that branches to the exit block. -class HReturnVoid FINAL : public HTemplateInstruction<0> { +class HReturnVoid FINAL : public HExpression<0> { public: explicit HReturnVoid(uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kReturnVoid, SideEffects::None(), dex_pc) { + : HExpression(kReturnVoid, SideEffects::None(), dex_pc) { } bool IsControlFlow() const OVERRIDE { return true; } @@ -2689,10 +2685,10 @@ class HReturnVoid FINAL : public HTemplateInstruction<0> { // Represents dex's RETURN opcodes. A HReturn is a control flow // instruction that branches to the exit block. -class HReturn FINAL : public HTemplateInstruction<1> { +class HReturn FINAL : public HExpression<1> { public: explicit HReturn(HInstruction* value, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kReturn, SideEffects::None(), dex_pc) { + : HExpression(kReturn, SideEffects::None(), dex_pc) { SetRawInputAt(0, value); } @@ -2713,13 +2709,13 @@ class HPhi FINAL : public HVariableInputSizeInstruction { uint32_t dex_pc = kNoDexPc) : HVariableInputSizeInstruction( kPhi, + ToPhiType(type), SideEffects::None(), dex_pc, allocator, number_of_inputs, kArenaAllocPhiInputs), reg_number_(reg_number) { - SetPackedField(ToPhiType(type)); DCHECK_NE(GetType(), DataType::Type::kVoid); // Phis are constructed live and marked dead if conflicting or unused. // Individual steps of SsaBuilder should assume that if a phi has been @@ -2737,7 +2733,6 @@ class HPhi FINAL : public HVariableInputSizeInstruction { bool IsCatchPhi() const { return GetBlock()->IsCatchBlock(); } - DataType::Type GetType() const OVERRIDE { return GetPackedField(); } void SetType(DataType::Type new_type) { // Make sure that only valid type changes occur. The following are allowed: // (1) int -> float/ref (primitive type propagation), @@ -2796,14 +2791,10 @@ class HPhi FINAL : public HVariableInputSizeInstruction { DEFAULT_COPY_CONSTRUCTOR(Phi); private: - static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits; - static constexpr size_t kFieldTypeSize = - MinimumBitsToStore(static_cast(DataType::Type::kLast)); - static constexpr size_t kFlagIsLive = kFieldType + kFieldTypeSize; + static constexpr size_t kFlagIsLive = HInstruction::kNumberOfGenericPackedBits; static constexpr size_t kFlagCanBeNull = kFlagIsLive + 1; static constexpr size_t kNumberOfPhiPackedBits = kFlagCanBeNull + 1; static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - using TypeField = BitField; const uint32_t reg_number_; }; @@ -2811,10 +2802,10 @@ class HPhi FINAL : public HVariableInputSizeInstruction { // The exit instruction is the only instruction of the exit block. // Instructions aborting the method (HThrow and HReturn) must branch to the // exit block. -class HExit FINAL : public HTemplateInstruction<0> { +class HExit FINAL : public HExpression<0> { public: explicit HExit(uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kExit, SideEffects::None(), dex_pc) { + : HExpression(kExit, SideEffects::None(), dex_pc) { } bool IsControlFlow() const OVERRIDE { return true; } @@ -2826,10 +2817,10 @@ class HExit FINAL : public HTemplateInstruction<0> { }; // Jumps from one block to another. -class HGoto FINAL : public HTemplateInstruction<0> { +class HGoto FINAL : public HExpression<0> { public: explicit HGoto(uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kGoto, SideEffects::None(), dex_pc) { + : HExpression(kGoto, SideEffects::None(), dex_pc) { } bool IsClonable() const OVERRIDE { return true; } @@ -3096,10 +3087,10 @@ class HDoubleConstant FINAL : public HConstant { // Conditional branch. A block ending with an HIf instruction must have // two successors. -class HIf FINAL : public HTemplateInstruction<1> { +class HIf FINAL : public HExpression<1> { public: explicit HIf(HInstruction* input, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kIf, SideEffects::None(), dex_pc) { + : HExpression(kIf, SideEffects::None(), dex_pc) { SetRawInputAt(0, input); } @@ -3126,7 +3117,7 @@ class HIf FINAL : public HTemplateInstruction<1> { // non-exceptional control flow. // Normal-flow successor is stored at index zero, exception handlers under // higher indices in no particular order. -class HTryBoundary FINAL : public HTemplateInstruction<0> { +class HTryBoundary FINAL : public HExpression<0> { public: enum class BoundaryKind { kEntry, @@ -3135,7 +3126,7 @@ class HTryBoundary FINAL : public HTemplateInstruction<0> { }; explicit HTryBoundary(BoundaryKind kind, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kTryBoundary, SideEffects::None(), dex_pc) { + : HExpression(kTryBoundary, SideEffects::None(), dex_pc) { SetPackedField(kind); } @@ -3219,6 +3210,7 @@ class HDeoptimize FINAL : public HVariableInputSizeInstruction { uint32_t dex_pc) : HVariableInputSizeInstruction( kDeoptimize, + guard->GetType(), SideEffects::CanTriggerGC(), dex_pc, allocator, @@ -3242,10 +3234,6 @@ class HDeoptimize FINAL : public HVariableInputSizeInstruction { DeoptimizationKind GetDeoptimizationKind() const { return GetPackedField(); } - DataType::Type GetType() const OVERRIDE { - return GuardsAnInput() ? GuardedInput()->GetType() : DataType::Type::kVoid; - } - bool GuardsAnInput() const { return InputCount() == 2; } @@ -3288,6 +3276,7 @@ class HShouldDeoptimizeFlag FINAL : public HVariableInputSizeInstruction { // with regard to other passes. HShouldDeoptimizeFlag(ArenaAllocator* allocator, uint32_t dex_pc) : HVariableInputSizeInstruction(kShouldDeoptimizeFlag, + DataType::Type::kInt32, SideEffects::None(), dex_pc, allocator, @@ -3295,8 +3284,6 @@ class HShouldDeoptimizeFlag FINAL : public HVariableInputSizeInstruction { kArenaAllocCHA) { } - DataType::Type GetType() const OVERRIDE { return DataType::Type::kInt32; } - // We do all CHA guard elimination/motion in a single pass, after which there is no // further guard elimination/motion since a guard might have been used for justification // of the elimination of another guard. Therefore, we pretend this guard cannot be moved @@ -3360,7 +3347,7 @@ class HClassTableGet FINAL : public HExpression<1> { DEFAULT_COPY_CONSTRUCTOR(ClassTableGet); private: - static constexpr size_t kFieldTableKind = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldTableKind = kNumberOfGenericPackedBits; static constexpr size_t kFieldTableKindSize = MinimumBitsToStore(static_cast(TableKind::kLast)); static constexpr size_t kNumberOfClassTableGetPackedBits = kFieldTableKind + kFieldTableKindSize; @@ -3375,13 +3362,13 @@ class HClassTableGet FINAL : public HExpression<1> { // PackedSwitch (jump table). A block ending with a PackedSwitch instruction will // have one successor for each entry in the switch table, and the final successor // will be the block containing the next Dex opcode. -class HPackedSwitch FINAL : public HTemplateInstruction<1> { +class HPackedSwitch FINAL : public HExpression<1> { public: HPackedSwitch(int32_t start_value, uint32_t num_entries, HInstruction* input, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kPackedSwitch, SideEffects::None(), dex_pc), + : HExpression(kPackedSwitch, SideEffects::None(), dex_pc), start_value_(start_value), num_entries_(num_entries) { SetRawInputAt(0, input); @@ -3611,7 +3598,7 @@ class HCondition : public HBinaryOperation { protected: // Needed if we merge a HCompare into a HCondition. - static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldComparisonBias = kNumberOfGenericPackedBits; static constexpr size_t kFieldComparisonBiasSize = MinimumBitsToStore(static_cast(ComparisonBias::kLast)); static constexpr size_t kNumberOfConditionPackedBits = @@ -4131,7 +4118,7 @@ class HCompare FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Compare); protected: - static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldComparisonBias = kNumberOfGenericPackedBits; static constexpr size_t kFieldComparisonBiasSize = MinimumBitsToStore(static_cast(ComparisonBias::kLast)); static constexpr size_t kNumberOfComparePackedBits = @@ -4210,7 +4197,7 @@ class HNewInstance FINAL : public HExpression<1> { DEFAULT_COPY_CONSTRUCTOR(NewInstance); private: - static constexpr size_t kFlagFinalizable = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagFinalizable = kNumberOfGenericPackedBits; static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1; static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); @@ -4251,8 +4238,6 @@ class HInvoke : public HVariableInputSizeInstruction { // inputs at the end of their list of inputs. uint32_t GetNumberOfArguments() const { return number_of_arguments_; } - DataType::Type GetType() const OVERRIDE { return GetPackedField(); } - uint32_t GetDexMethodIndex() const { return dex_method_index_; } InvokeType GetInvokeType() const { @@ -4305,16 +4290,11 @@ class HInvoke : public HVariableInputSizeInstruction { static constexpr size_t kFieldInvokeType = kNumberOfGenericPackedBits; static constexpr size_t kFieldInvokeTypeSize = MinimumBitsToStore(static_cast(kMaxInvokeType)); - static constexpr size_t kFieldReturnType = - kFieldInvokeType + kFieldInvokeTypeSize; - static constexpr size_t kFieldReturnTypeSize = - MinimumBitsToStore(static_cast(DataType::Type::kLast)); - static constexpr size_t kFlagCanThrow = kFieldReturnType + kFieldReturnTypeSize; + static constexpr size_t kFlagCanThrow = kFieldInvokeType + kFieldInvokeTypeSize; static constexpr size_t kFlagAlwaysThrows = kFlagCanThrow + 1; static constexpr size_t kNumberOfInvokePackedBits = kFlagAlwaysThrows + 1; static_assert(kNumberOfInvokePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using InvokeTypeField = BitField; - using ReturnTypeField = BitField; HInvoke(InstructionKind kind, ArenaAllocator* allocator, @@ -4327,6 +4307,7 @@ class HInvoke : public HVariableInputSizeInstruction { InvokeType invoke_type) : HVariableInputSizeInstruction( kind, + return_type, SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays. dex_pc, allocator, @@ -4337,7 +4318,6 @@ class HInvoke : public HVariableInputSizeInstruction { dex_method_index_(dex_method_index), intrinsic_(Intrinsics::kNone), intrinsic_optimizations_(0) { - SetPackedField(return_type); SetPackedField(invoke_type); SetPackedFlag(true); } @@ -4550,7 +4530,7 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { } bool CanBeNull() const OVERRIDE { - return GetPackedField() == DataType::Type::kReference && !IsStringInit(); + return GetType() == DataType::Type::kReference && !IsStringInit(); } // Get the index of the special input, if any. @@ -5146,8 +5126,6 @@ class HDivZeroCheck FINAL : public HExpression<1> { SetRawInputAt(0, value); } - DataType::Type GetType() const OVERRIDE { return InputAt(0)->GetType(); } - bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { @@ -5500,7 +5478,7 @@ class HParameterValue FINAL : public HExpression<0> { private: // Whether or not the parameter value corresponds to 'this' argument. - static constexpr size_t kFlagIsThis = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagIsThis = kNumberOfGenericPackedBits; static constexpr size_t kFlagCanBeNull = kFlagIsThis + 1; static constexpr size_t kNumberOfParameterValuePackedBits = kFlagCanBeNull + 1; static_assert(kNumberOfParameterValuePackedBits <= kMaxNumberOfPackedBits, @@ -5742,7 +5720,7 @@ class HInstanceFieldGet FINAL : public HExpression<1> { const FieldInfo field_info_; }; -class HInstanceFieldSet FINAL : public HTemplateInstruction<2> { +class HInstanceFieldSet FINAL : public HExpression<2> { public: HInstanceFieldSet(HInstruction* object, HInstruction* value, @@ -5754,9 +5732,9 @@ class HInstanceFieldSet FINAL : public HTemplateInstruction<2> { uint16_t declaring_class_def_index, const DexFile& dex_file, uint32_t dex_pc) - : HTemplateInstruction(kInstanceFieldSet, - SideEffects::FieldWriteOfType(field_type, is_volatile), - dex_pc), + : HExpression(kInstanceFieldSet, + SideEffects::FieldWriteOfType(field_type, is_volatile), + dex_pc), field_info_(field, field_offset, field_type, @@ -5882,13 +5860,13 @@ class HArrayGet FINAL : public HExpression<2> { // a particular HArrayGet is actually a String.charAt() by looking at the type // of the input but that requires holding the mutator lock, so we prefer to use // a flag, so that code generators don't need to do the locking. - static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagIsStringCharAt = kNumberOfGenericPackedBits; static constexpr size_t kNumberOfArrayGetPackedBits = kFlagIsStringCharAt + 1; static_assert(kNumberOfArrayGetPackedBits <= HInstruction::kMaxNumberOfPackedBits, "Too many packed fields."); }; -class HArraySet FINAL : public HTemplateInstruction<3> { +class HArraySet FINAL : public HExpression<3> { public: HArraySet(HInstruction* array, HInstruction* index, @@ -5910,7 +5888,7 @@ class HArraySet FINAL : public HTemplateInstruction<3> { DataType::Type expected_component_type, SideEffects side_effects, uint32_t dex_pc) - : HTemplateInstruction(kArraySet, side_effects, dex_pc) { + : HExpression(kArraySet, side_effects, dex_pc) { SetPackedField(expected_component_type); SetPackedFlag(value->GetType() == DataType::Type::kReference); SetPackedFlag(true); @@ -6039,7 +6017,7 @@ class HArrayLength FINAL : public HExpression<1> { // determine whether a particular HArrayLength is actually a String.length() by // looking at the type of the input but that requires holding the mutator lock, so // we prefer to use a flag, so that code generators don't need to do the locking. - static constexpr size_t kFlagIsStringLength = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagIsStringLength = kNumberOfGenericPackedBits; static constexpr size_t kNumberOfArrayLengthPackedBits = kFlagIsStringLength + 1; static_assert(kNumberOfArrayLengthPackedBits <= HInstruction::kMaxNumberOfPackedBits, "Too many packed fields."); @@ -6080,13 +6058,13 @@ class HBoundsCheck FINAL : public HExpression<2> { DEFAULT_COPY_CONSTRUCTOR(BoundsCheck); private: - static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagIsStringCharAt = kNumberOfGenericPackedBits; }; -class HSuspendCheck FINAL : public HTemplateInstruction<0> { +class HSuspendCheck FINAL : public HExpression<0> { public: explicit HSuspendCheck(uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kSuspendCheck, SideEffects::CanTriggerGC(), dex_pc), + : HExpression(kSuspendCheck, SideEffects::CanTriggerGC(), dex_pc), slow_path_(nullptr) { } @@ -6112,10 +6090,10 @@ class HSuspendCheck FINAL : public HTemplateInstruction<0> { // Pseudo-instruction which provides the native debugger with mapping information. // It ensures that we can generate line number and local variables at this point. -class HNativeDebugInfo : public HTemplateInstruction<0> { +class HNativeDebugInfo : public HExpression<0> { public: explicit HNativeDebugInfo(uint32_t dex_pc) - : HTemplateInstruction<0>(kNativeDebugInfo, SideEffects::None(), dex_pc) { + : HExpression<0>(kNativeDebugInfo, SideEffects::None(), dex_pc) { } bool NeedsEnvironment() const OVERRIDE { @@ -6174,7 +6152,10 @@ class HLoadClass FINAL : public HInstruction { bool is_referrers_class, uint32_t dex_pc, bool needs_access_check) - : HInstruction(kLoadClass, SideEffectsForArchRuntimeCalls(), dex_pc), + : HInstruction(kLoadClass, + DataType::Type::kReference, + SideEffectsForArchRuntimeCalls(), + dex_pc), special_input_(HUserRecord(current_method)), type_index_(type_index), dex_file_(dex_file), @@ -6285,10 +6266,6 @@ class HLoadClass FINAL : public HInstruction { &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u); } - DataType::Type GetType() const OVERRIDE { - return DataType::Type::kReference; - } - Handle GetClass() const { return klass_; } @@ -6399,7 +6376,10 @@ class HLoadString FINAL : public HInstruction { dex::StringIndex string_index, const DexFile& dex_file, uint32_t dex_pc) - : HInstruction(kLoadString, SideEffectsForArchRuntimeCalls(), dex_pc), + : HInstruction(kLoadString, + DataType::Type::kReference, + SideEffectsForArchRuntimeCalls(), + dex_pc), special_input_(HUserRecord(current_method)), string_index_(string_index), dex_file_(dex_file) { @@ -6474,10 +6454,6 @@ class HLoadString FINAL : public HInstruction { &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u); } - DataType::Type GetType() const OVERRIDE { - return DataType::Type::kReference; - } - DECLARE_INSTRUCTION(LoadString); protected: @@ -6633,7 +6609,7 @@ class HStaticFieldGet FINAL : public HExpression<1> { const FieldInfo field_info_; }; -class HStaticFieldSet FINAL : public HTemplateInstruction<2> { +class HStaticFieldSet FINAL : public HExpression<2> { public: HStaticFieldSet(HInstruction* cls, HInstruction* value, @@ -6645,9 +6621,9 @@ class HStaticFieldSet FINAL : public HTemplateInstruction<2> { uint16_t declaring_class_def_index, const DexFile& dex_file, uint32_t dex_pc) - : HTemplateInstruction(kStaticFieldSet, - SideEffects::FieldWriteOfType(field_type, is_volatile), - dex_pc), + : HExpression(kStaticFieldSet, + SideEffects::FieldWriteOfType(field_type, is_volatile), + dex_pc), field_info_(field, field_offset, field_type, @@ -6714,16 +6690,14 @@ class HUnresolvedInstanceFieldGet FINAL : public HExpression<1> { const uint32_t field_index_; }; -class HUnresolvedInstanceFieldSet FINAL : public HTemplateInstruction<2> { +class HUnresolvedInstanceFieldSet FINAL : public HExpression<2> { public: HUnresolvedInstanceFieldSet(HInstruction* obj, HInstruction* value, DataType::Type field_type, uint32_t field_index, uint32_t dex_pc) - : HTemplateInstruction(kUnresolvedInstanceFieldSet, - SideEffects::AllExceptGCDependency(), - dex_pc), + : HExpression(kUnresolvedInstanceFieldSet, SideEffects::AllExceptGCDependency(), dex_pc), field_index_(field_index) { SetPackedField(field_type); DCHECK_EQ(DataType::Kind(field_type), DataType::Kind(value->GetType())); @@ -6784,15 +6758,13 @@ class HUnresolvedStaticFieldGet FINAL : public HExpression<0> { const uint32_t field_index_; }; -class HUnresolvedStaticFieldSet FINAL : public HTemplateInstruction<1> { +class HUnresolvedStaticFieldSet FINAL : public HExpression<1> { public: HUnresolvedStaticFieldSet(HInstruction* value, DataType::Type field_type, uint32_t field_index, uint32_t dex_pc) - : HTemplateInstruction(kUnresolvedStaticFieldSet, - SideEffects::AllExceptGCDependency(), - dex_pc), + : HExpression(kUnresolvedStaticFieldSet, SideEffects::AllExceptGCDependency(), dex_pc), field_index_(field_index) { SetPackedField(field_type); DCHECK_EQ(DataType::Kind(field_type), DataType::Kind(value->GetType())); @@ -6841,10 +6813,10 @@ class HLoadException FINAL : public HExpression<0> { // Implicit part of move-exception which clears thread-local exception storage. // Must not be removed because the runtime expects the TLS to get cleared. -class HClearException FINAL : public HTemplateInstruction<0> { +class HClearException FINAL : public HExpression<0> { public: explicit HClearException(uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kClearException, SideEffects::AllWrites(), dex_pc) { + : HExpression(kClearException, SideEffects::AllWrites(), dex_pc) { } DECLARE_INSTRUCTION(ClearException); @@ -6853,10 +6825,10 @@ class HClearException FINAL : public HTemplateInstruction<0> { DEFAULT_COPY_CONSTRUCTOR(ClearException); }; -class HThrow FINAL : public HTemplateInstruction<1> { +class HThrow FINAL : public HExpression<1> { public: HThrow(HInstruction* exception, uint32_t dex_pc) - : HTemplateInstruction(kThrow, SideEffects::CanTriggerGC(), dex_pc) { + : HExpression(kThrow, SideEffects::CanTriggerGC(), dex_pc) { SetRawInputAt(0, exception); } @@ -6897,6 +6869,7 @@ std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs); class HTypeCheckInstruction : public HVariableInputSizeInstruction { public: HTypeCheckInstruction(InstructionKind kind, + DataType::Type type, HInstruction* object, HInstruction* target_class_or_null, TypeCheckKind check_kind, @@ -6908,6 +6881,7 @@ class HTypeCheckInstruction : public HVariableInputSizeInstruction { SideEffects side_effects) : HVariableInputSizeInstruction( kind, + type, side_effects, dex_pc, allocator, @@ -7010,6 +6984,7 @@ class HInstanceOf FINAL : public HTypeCheckInstruction { HIntConstant* bitstring_path_to_root, HIntConstant* bitstring_mask) : HTypeCheckInstruction(kInstanceOf, + DataType::Type::kBool, object, target_class_or_null, check_kind, @@ -7020,8 +6995,6 @@ class HInstanceOf FINAL : public HTypeCheckInstruction { bitstring_mask, SideEffectsForArchRuntimeCalls(check_kind)) {} - DataType::Type GetType() const OVERRIDE { return DataType::Type::kBool; } - bool NeedsEnvironment() const OVERRIDE { return CanCallRuntime(GetTypeCheckKind()); } @@ -7074,7 +7047,7 @@ class HBoundType FINAL : public HExpression<1> { private: // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this // is false then CanBeNull() cannot be true). - static constexpr size_t kFlagUpperCanBeNull = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagUpperCanBeNull = kNumberOfGenericPackedBits; static constexpr size_t kFlagCanBeNull = kFlagUpperCanBeNull + 1; static constexpr size_t kNumberOfBoundTypePackedBits = kFlagCanBeNull + 1; static_assert(kNumberOfBoundTypePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); @@ -7099,6 +7072,7 @@ class HCheckCast FINAL : public HTypeCheckInstruction { HIntConstant* bitstring_path_to_root, HIntConstant* bitstring_mask) : HTypeCheckInstruction(kCheckCast, + DataType::Type::kVoid, object, target_class_or_null, check_kind, @@ -7148,13 +7122,12 @@ enum MemBarrierKind { }; std::ostream& operator<<(std::ostream& os, const MemBarrierKind& kind); -class HMemoryBarrier FINAL : public HTemplateInstruction<0> { +class HMemoryBarrier FINAL : public HExpression<0> { public: explicit HMemoryBarrier(MemBarrierKind barrier_kind, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction( - kMemoryBarrier, - SideEffects::AllWritesAndReads(), // Assume write/read on all fields/arrays. - dex_pc) { + : HExpression(kMemoryBarrier, + SideEffects::AllWritesAndReads(), // Assume write/read on all fields/arrays. + dex_pc) { SetPackedField(barrier_kind); } @@ -7331,7 +7304,7 @@ class HConstructorFence FINAL : public HVariableInputSizeInstruction { DEFAULT_COPY_CONSTRUCTOR(ConstructorFence); }; -class HMonitorOperation FINAL : public HTemplateInstruction<1> { +class HMonitorOperation FINAL : public HExpression<1> { public: enum class OperationKind { kEnter, @@ -7340,10 +7313,9 @@ class HMonitorOperation FINAL : public HTemplateInstruction<1> { }; HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc) - : HTemplateInstruction( - kMonitorOperation, - SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays. - dex_pc) { + : HExpression(kMonitorOperation, + SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays. + dex_pc) { SetPackedField(kind); SetRawInputAt(0, object); } @@ -7493,10 +7465,10 @@ std::ostream& operator<<(std::ostream& os, const MoveOperands& rhs); static constexpr size_t kDefaultNumberOfMoves = 4; -class HParallelMove FINAL : public HTemplateInstruction<0> { +class HParallelMove FINAL : public HExpression<0> { public: explicit HParallelMove(ArenaAllocator* allocator, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kParallelMove, SideEffects::None(), dex_pc), + : HExpression(kParallelMove, SideEffects::None(), dex_pc), moves_(allocator->Adapter(kArenaAllocMoveOperands)) { moves_.reserve(kDefaultNumberOfMoves); } diff --git a/compiler/optimizing/nodes_mips.h b/compiler/optimizing/nodes_mips.h index d0e0fef946..05b27a7810 100644 --- a/compiler/optimizing/nodes_mips.h +++ b/compiler/optimizing/nodes_mips.h @@ -39,14 +39,14 @@ class HMipsComputeBaseMethodAddress : public HExpression<0> { }; // Mips version of HPackedSwitch that holds a pointer to the base method address. -class HMipsPackedSwitch FINAL : public HTemplateInstruction<2> { +class HMipsPackedSwitch FINAL : public HExpression<2> { public: HMipsPackedSwitch(int32_t start_value, int32_t num_entries, HInstruction* input, HMipsComputeBaseMethodAddress* method_base, uint32_t dex_pc) - : HTemplateInstruction(kMipsPackedSwitch, SideEffects::None(), dex_pc), + : HExpression(kMipsPackedSwitch, SideEffects::None(), dex_pc), start_value_(start_value), num_entries_(num_entries) { SetRawInputAt(0, input); diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h index 1a484e1944..c5e9a8d036 100644 --- a/compiler/optimizing/nodes_vector.h +++ b/compiler/optimizing/nodes_vector.h @@ -79,13 +79,14 @@ class HVecOperation : public HVariableInputSizeInstruction { size_t vector_length, uint32_t dex_pc) : HVariableInputSizeInstruction(kind, + kSIMDType, side_effects, dex_pc, allocator, number_of_inputs, kArenaAllocVectorNode), vector_length_(vector_length) { - SetPackedField(packed_type); + SetPackedField(packed_type); DCHECK_LT(1u, vector_length); } @@ -99,14 +100,9 @@ class HVecOperation : public HVariableInputSizeInstruction { return vector_length_ * DataType::Size(GetPackedType()); } - // Returns the type of the vector operation. - DataType::Type GetType() const OVERRIDE { - return kSIMDType; - } - // Returns the true component type packed in a vector. DataType::Type GetPackedType() const { - return GetPackedField(); + return GetPackedField(); } // Assumes vector nodes cannot be moved by default. Each concrete implementation @@ -185,12 +181,12 @@ class HVecOperation : public HVariableInputSizeInstruction { protected: // Additional packed bits. - static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits; - static constexpr size_t kFieldTypeSize = + static constexpr size_t kFieldPackedType = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldPackedTypeSize = MinimumBitsToStore(static_cast(DataType::Type::kLast)); - static constexpr size_t kNumberOfVectorOpPackedBits = kFieldType + kFieldTypeSize; + static constexpr size_t kNumberOfVectorOpPackedBits = kFieldPackedType + kFieldPackedTypeSize; static_assert(kNumberOfVectorOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - using TypeField = BitField; + using PackedTypeField = BitField; DEFAULT_COPY_CONSTRUCTOR(VecOperation); @@ -358,11 +354,9 @@ class HVecExtractScalar FINAL : public HVecUnaryOperation { DCHECK(HasConsistentPackedTypes(input, packed_type)); DCHECK_LT(index, vector_length); DCHECK_EQ(index, 0u); - } - - // Yields a single component in the vector. - DataType::Type GetType() const OVERRIDE { - return GetPackedType(); + // Yields a single component in the vector. + // Overrides the kSIMDType set by the VecOperation constructor. + SetPackedField(packed_type); } // An extract needs to stay in place, since SIMD registers are not diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h index 4c32be7d15..d1e7f68edb 100644 --- a/compiler/optimizing/nodes_x86.h +++ b/compiler/optimizing/nodes_x86.h @@ -89,14 +89,14 @@ class HX86FPNeg FINAL : public HExpression<2> { }; // X86 version of HPackedSwitch that holds a pointer to the base method address. -class HX86PackedSwitch FINAL : public HTemplateInstruction<2> { +class HX86PackedSwitch FINAL : public HExpression<2> { public: HX86PackedSwitch(int32_t start_value, int32_t num_entries, HInstruction* input, HX86ComputeBaseMethodAddress* method_base, uint32_t dex_pc) - : HTemplateInstruction(kX86PackedSwitch, SideEffects::None(), dex_pc), + : HExpression(kX86PackedSwitch, SideEffects::None(), dex_pc), start_value_(start_value), num_entries_(num_entries) { SetRawInputAt(0, input); -- GitLab From 0dcccd815684b4c7540f9c6d39de161e44e5f51c Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 4 May 2018 13:32:25 +0100 Subject: [PATCH 355/749] ART: Remove InstructionTypeEquals(). Avoid the virtual call and simply compare the instruction kinds. Test: Rely on TreeHugger. Change-Id: I7310de976614c5ec63d61a447a40047de5bc654d --- compiler/optimizing/instruction_simplifier.cc | 4 ++-- compiler/optimizing/nodes.cc | 5 ++--- compiler/optimizing/nodes.h | 8 -------- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 0fe16725f3..ca84d421a7 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -2666,10 +2666,10 @@ bool InstructionSimplifierVisitor::TryHandleAssociativeAndCommutativeOperation( HConstant* const2; HBinaryOperation* y; - if (instruction->InstructionTypeEquals(left) && right->IsConstant()) { + if (instruction->GetKind() == left->GetKind() && right->IsConstant()) { const2 = right->AsConstant(); y = left->AsBinaryOperation(); - } else if (left->IsConstant() && instruction->InstructionTypeEquals(right)) { + } else if (left->IsConstant() && instruction->GetKind() == right->GetKind()) { const2 = left->AsConstant(); y = right->AsBinaryOperation(); } else { diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index f784f8f7f3..6029347ca8 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1680,10 +1680,9 @@ bool HCondition::IsBeforeWhenDisregardMoves(HInstruction* instruction) const { } bool HInstruction::Equals(const HInstruction* other) const { - if (!InstructionTypeEquals(other)) return false; - DCHECK_EQ(GetKind(), other->GetKind()); - if (!InstructionDataEquals(other)) return false; + if (GetKind() != other->GetKind()) return false; if (GetType() != other->GetType()) return false; + if (!InstructionDataEquals(other)) return false; HConstInputsRef inputs = GetInputs(); HConstInputsRef other_inputs = other->GetInputs(); if (inputs.size() != other_inputs.size()) return false; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 295f570b34..3433e91796 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1525,9 +1525,6 @@ FOR_EACH_INSTRUCTION(FORWARD_DECLARATION) H##type& operator=(const H##type&) = delete; \ public: \ const char* DebugName() const OVERRIDE { return #type; } \ - bool InstructionTypeEquals(const HInstruction* other) const OVERRIDE { \ - return other->Is##type(); \ - } \ HInstruction* Clone(ArenaAllocator* arena) const OVERRIDE { \ DCHECK(IsClonable()); \ return new (arena) H##type(*this->As##type()); \ @@ -2272,11 +2269,6 @@ class HInstruction : public ArenaObject { // meanings? split and rename? virtual bool CanBeMoved() const { return false; } - // Returns whether the two instructions are of the same kind. - virtual bool InstructionTypeEquals(const HInstruction* other ATTRIBUTE_UNUSED) const { - return false; - } - // Returns whether any data encoded in the two instructions is equal. // This method does not look at the inputs. Both instructions must be // of the same type, otherwise the method has undefined behavior. -- GitLab From e394622583774d7e3c32bea0c7ffc4777ee47e56 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 4 May 2018 14:18:47 +0100 Subject: [PATCH 356/749] ART: Do not define abstract HIR kinds. They were never used and the two cases in HSchedulerARM64's IsSchedulable() were unreachable. Test: Rely on TreeHugger. Change-Id: I506bc2a89a6fbf556f32e25ecfe61eaad236a40a --- compiler/optimizing/nodes.cc | 2 +- compiler/optimizing/nodes.h | 2 +- compiler/optimizing/scheduler_arm64.h | 7 +++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 6029347ca8..5f2833e9a6 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1697,7 +1697,7 @@ bool HInstruction::Equals(const HInstruction* other) const { std::ostream& operator<<(std::ostream& os, const HInstruction::InstructionKind& rhs) { #define DECLARE_CASE(type, super) case HInstruction::k##type: os << #type; break; switch (rhs) { - FOR_EACH_INSTRUCTION(DECLARE_CASE) + FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_CASE) default: os << "Unknown instruction kind " << static_cast(rhs); break; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 3433e91796..1d913c0d25 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1956,7 +1956,7 @@ class HInstruction : public ArenaObject { public: #define DECLARE_KIND(type, super) k##type, enum InstructionKind { - FOR_EACH_INSTRUCTION(DECLARE_KIND) + FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_KIND) kLastInstructionKind }; #undef DECLARE_KIND diff --git a/compiler/optimizing/scheduler_arm64.h b/compiler/optimizing/scheduler_arm64.h index f71cb5b784..4f394d5e16 100644 --- a/compiler/optimizing/scheduler_arm64.h +++ b/compiler/optimizing/scheduler_arm64.h @@ -68,12 +68,10 @@ class SchedulingLatencyVisitorARM64 : public SchedulingLatencyVisitor { M(ArrayGet , unused) \ M(ArrayLength , unused) \ M(ArraySet , unused) \ - M(BinaryOperation , unused) \ M(BoundsCheck , unused) \ M(Div , unused) \ M(InstanceFieldGet , unused) \ M(InstanceOf , unused) \ - M(Invoke , unused) \ M(LoadString , unused) \ M(Mul , unused) \ M(NewArray , unused) \ @@ -108,6 +106,10 @@ class SchedulingLatencyVisitorARM64 : public SchedulingLatencyVisitor { M(VecLoad , unused) \ M(VecStore , unused) +#define FOR_EACH_SCHEDULED_ABSTRACT_INSTRUCTION(M) \ + M(BinaryOperation , unused) \ + M(Invoke , unused) + #define FOR_EACH_SCHEDULED_SHARED_INSTRUCTION(M) \ M(BitwiseNegatedRight, unused) \ M(MultiplyAccumulate, unused) \ @@ -119,6 +121,7 @@ class SchedulingLatencyVisitorARM64 : public SchedulingLatencyVisitor { void Visit##type(H##type* instruction) OVERRIDE; FOR_EACH_SCHEDULED_COMMON_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_SCHEDULED_ABSTRACT_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) FOR_EACH_SCHEDULED_SHARED_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION) -- GitLab From 76cfe61b0c20f7c9d9ba9b60b188f483d81c5f68 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Mon, 30 Oct 2017 13:14:28 +0000 Subject: [PATCH 357/749] Support ART on-device testing in a chroot environment. This change updates the ART test rules and scripts to allow installation and ART testing in a chroot directory on device. All existing ART testing is supported: - ART gtests (with and without Valgrind). - ART run-tests. - libcore tests (with companion CL in external/vogar). - JDWP tests (with companion CL in external/vogar). Test: Run ART tests (gtest, run-tests, libcore tests, JDWP tests) in chroot Bug: 34729697 Bug: 68125496 Change-Id: I398e9bafee61eccd98d827ab8d9b8f6395aaa853 --- Android.mk | 27 ++++++++++++------ build/Android.gtest.mk | 52 +++++++++++++++++++++-------------- test/etc/run-test-jar | 44 ++++++++++++++++++----------- test/run-test | 48 ++++++++++++++++++++++++++------ test/testrunner/env.py | 2 ++ test/testrunner/testrunner.py | 3 ++ 6 files changed, 123 insertions(+), 53 deletions(-) diff --git a/Android.mk b/Android.mk index 64b9400f66..d6472be895 100644 --- a/Android.mk +++ b/Android.mk @@ -110,22 +110,33 @@ TEST_ART_ADB_ROOT_AND_REMOUNT := \ # Sync test files to the target, depends upon all things that must be pushed to the target. .PHONY: test-art-target-sync -# Check if we need to sync. In case ART_TEST_ANDROID_ROOT is not empty, -# the code below uses 'adb push' instead of 'adb sync', which does not -# check if the files on the device have changed. +# Check if we need to sync. In case ART_TEST_CHROOT or ART_TEST_ANDROID_ROOT +# is not empty, the code below uses 'adb push' instead of 'adb sync', +# which does not check if the files on the device have changed. +# TODO: Remove support for ART_TEST_ANDROID_ROOT when it is no longer needed. ifneq ($(ART_TEST_NO_SYNC),true) +# Sync system and data partitions. ifeq ($(ART_TEST_ANDROID_ROOT),) +ifeq ($(ART_TEST_CHROOT),) test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS) $(TEST_ART_ADB_ROOT_AND_REMOUNT) adb sync system && adb sync data else test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS) $(TEST_ART_ADB_ROOT_AND_REMOUNT) - adb wait-for-device push $(PRODUCT_OUT)/system $(ART_TEST_ANDROID_ROOT) -# Push the contents of the `data` dir into `/data` on the device. If -# `/data` already exists on the device, it is not overwritten, but its -# contents are updated. - adb push $(PRODUCT_OUT)/data / + adb wait-for-device + adb push $(PRODUCT_OUT)/system $(ART_TEST_CHROOT)/ + adb push $(PRODUCT_OUT)/data $(ART_TEST_CHROOT)/ +endif +else +test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS) + $(TEST_ART_ADB_ROOT_AND_REMOUNT) + adb wait-for-device + adb push $(PRODUCT_OUT)/system $(ART_TEST_CHROOT)$(ART_TEST_ANDROID_ROOT) +# Push the contents of the `data` dir into `$(ART_TEST_CHROOT)/data` on the device (note +# that $(ART_TEST_CHROOT) can be empty). If `$(ART_TEST_CHROOT)/data` already exists on +# the device, it is not overwritten, but its content is updated. + adb push $(PRODUCT_OUT)/data $(ART_TEST_CHROOT)/ endif endif diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index b46f6771ea..be040a98da 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -472,17 +472,29 @@ define define-art-gtest-rule-target $$(gtest_rule) valgrind-$$(gtest_rule): PRIVATE_TARGET_EXE := $$(gtest_target_exe) +ifeq ($(ART_TEST_CHROOT),) +# Non-chroot configuration. +maybe_art_test_chroot := +maybe_chroot_command := +else +# Chroot configuration. +maybe_art_test_chroot := $(ART_TEST_CHROOT)/ +maybe_chroot_command := chroot $(ART_TEST_CHROOT) +endif + +# File witnessing the success of the gtest, the presence of which means the gtest's success. +gtest_witness := $(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID + .PHONY: $$(gtest_rule) $$(gtest_rule): test-art-target-sync - $(hide) adb shell touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID - $(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID - $(hide) adb shell chmod 755 $$(PRIVATE_TARGET_EXE) + $(hide) adb shell touch $(gtest_witness) + $(hide) adb shell rm $(gtest_witness) + $(hide) adb shell chmod 755 $(maybe_art_test_chroot)$$(PRIVATE_TARGET_EXE) $(hide) $$(call ART_TEST_SKIP,$$@) && \ - (adb shell "env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ + (adb shell "$(maybe_chroot_command) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) $$(PRIVATE_TARGET_EXE) \ - && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID" \ - && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID /tmp/ \ - && $$(call ART_TEST_PASSED,$$@)) \ + && touch $(gtest_witness)" \ + && (adb pull $(gtest_witness) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ || $$(call ART_TEST_FAILED,$$@)) $(hide) rm -f /tmp/$$@-$$$$PPID @@ -492,19 +504,18 @@ $$(gtest_rule): test-art-target-sync .PHONY: valgrind-$$(gtest_rule) valgrind-$$(gtest_rule): $(ART_VALGRIND_TARGET_DEPENDENCIES) test-art-target-sync - $(hide) adb shell touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID - $(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID - $(hide) adb shell chmod 755 $$(PRIVATE_TARGET_EXE) + $(hide) adb shell touch $(gtest_witness) + $(hide) adb shell rm $(gtest_witness) + $(hide) adb shell chmod 755 $(maybe_art_test_chroot)$$(PRIVATE_TARGET_EXE) $(hide) $$(call ART_TEST_SKIP,$$@) && \ - (adb shell "env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ + (adb shell "$(maybe_chroot_command) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \ $$$$ANDROID_ROOT/bin/valgrind \ --leak-check=full --error-exitcode=1 --workaround-gcc296-bugs=yes \ --suppressions=$(ART_TARGET_TEST_DIR)/valgrind-target-suppressions.txt \ --num-callers=50 --show-mismatched-frees=no $$(PRIVATE_TARGET_EXE) \ - && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID" \ - && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID /tmp/ \ - && $$(call ART_TEST_PASSED,$$@)) \ + && touch $(gtest_witness)" \ + && (adb pull $(gtest_witness) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ || $$(call ART_TEST_FAILED,$$@)) $(hide) rm -f /tmp/$$@-$$$$PPID @@ -514,10 +525,12 @@ valgrind-$$(gtest_rule): $(ART_VALGRIND_TARGET_DEPENDENCIES) test-art-target-syn ART_TEST_TARGET_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule) # Clear locally defined variables. - valgrind_gtest_rule := - gtest_rule := - gtest_exe := + gtest_witness := + maybe_chroot_command := + maybe_art_test_chroot := gtest_target_exe := + gtest_exe := + gtest_rule := endef # define-art-gtest-rule-target ART_VALGRIND_DEPENDENCIES := \ @@ -595,10 +608,9 @@ valgrind-$$(gtest_rule): $$(gtest_exe) $$(gtest_deps) $(ART_VALGRIND_DEPENDENCIE ART_TEST_HOST_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule) # Clear locally defined variables. - valgrind_gtest_rule := - gtest_rule := - gtest_exe := gtest_deps := + gtest_exe := + gtest_rule := endef # define-art-gtest-rule-host # Define the rules to build and run host and target gtests. diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 81e77be547..fad801192a 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -13,6 +13,7 @@ ARCHITECTURES_32="(arm|x86|mips|none)" ARCHITECTURES_64="(arm64|x86_64|mips64|none)" ARCHITECTURES_PATTERN="${ARCHITECTURES_32}" BOOT_IMAGE="" +CHROOT= COMPILE_FLAGS="" DALVIKVM="dalvikvm32" DEBUGGER="n" @@ -299,6 +300,10 @@ while true; do elif [ "x$1" = "x--no-optimize" ]; then OPTIMIZE="n" shift + elif [ "x$1" = "x--chroot" ]; then + shift + CHROOT="$1" + shift elif [ "x$1" = "x--android-root" ]; then shift ANDROID_ROOT="$1" @@ -367,6 +372,9 @@ while true; do fi done +# The DEX_LOCATION with the chroot prefix, if any. +CHROOT_DEX_LOCATION="$CHROOT$DEX_LOCATION" + if [ "$USE_JVM" = "n" ]; then FLAGS="${FLAGS} ${ANDROID_FLAGS}" for feature in ${EXPERIMENTAL}; do @@ -817,28 +825,28 @@ if [ "$HOST" = "n" ]; then adb root > /dev/null adb wait-for-device if [ "$QUIET" = "n" ]; then - adb shell rm -rf $DEX_LOCATION - adb shell mkdir -p $DEX_LOCATION - adb push $TEST_NAME.jar $DEX_LOCATION - adb push $TEST_NAME-ex.jar $DEX_LOCATION + adb shell rm -rf $CHROOT_DEX_LOCATION + adb shell mkdir -p $CHROOT_DEX_LOCATION + adb push $TEST_NAME.jar $CHROOT_DEX_LOCATION + adb push $TEST_NAME-ex.jar $CHROOT_DEX_LOCATION if [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then - adb push profile $DEX_LOCATION + adb push profile $CHROOT_DEX_LOCATION fi # Copy resource folder if [ -d res ]; then - adb push res $DEX_LOCATION + adb push res $CHROOT_DEX_LOCATION fi else - adb shell rm -r $DEX_LOCATION >/dev/null 2>&1 - adb shell mkdir -p $DEX_LOCATION >/dev/null 2>&1 - adb push $TEST_NAME.jar $DEX_LOCATION >/dev/null 2>&1 - adb push $TEST_NAME-ex.jar $DEX_LOCATION >/dev/null 2>&1 + adb shell rm -rf $CHROOT_DEX_LOCATION >/dev/null 2>&1 + adb shell mkdir -p $CHROOT_DEX_LOCATION >/dev/null 2>&1 + adb push $TEST_NAME.jar $CHROOT_DEX_LOCATION >/dev/null 2>&1 + adb push $TEST_NAME-ex.jar $CHROOT_DEX_LOCATION >/dev/null 2>&1 if [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then - adb push profile $DEX_LOCATION >/dev/null 2>&1 + adb push profile $CHROOT_DEX_LOCATION >/dev/null 2>&1 fi # Copy resource folder if [ -d res ]; then - adb push res $DEX_LOCATION >/dev/null 2>&1 + adb push res $CHROOT_DEX_LOCATION >/dev/null 2>&1 fi fi @@ -847,7 +855,7 @@ if [ "$HOST" = "n" ]; then # Current default installation is dalvikvm 64bits and dex2oat 32bits, # so we can only use LD_LIBRARY_PATH when testing on a local # installation. - LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBRARY_DIRECTORY:$LD_LIBRARY_PATH + LD_LIBRARY_PATH="$ANDROID_ROOT/$LIBRARY_DIRECTORY:$LD_LIBRARY_PATH" fi # System libraries needed by libarttestd.so @@ -889,14 +897,18 @@ if [ "$HOST" = "n" ]; then fi if [ "$QUIET" = "n" ]; then - adb push $cmdfile $DEX_LOCATION/cmdline.sh + adb push $cmdfile $CHROOT_DEX_LOCATION/cmdline.sh else - adb push $cmdfile $DEX_LOCATION/cmdline.sh > /dev/null 2>&1 + adb push $cmdfile $CHROOT_DEX_LOCATION/cmdline.sh >/dev/null 2>&1 fi exit_status=0 if [ "$DRY_RUN" != "y" ]; then - adb shell sh $DEX_LOCATION/cmdline.sh + if [ -n "$CHROOT" ]; then + adb shell chroot "$CHROOT" sh $DEX_LOCATION/cmdline.sh + else + adb shell sh $DEX_LOCATION/cmdline.sh + fi exit_status=$? fi diff --git a/test/run-test b/test/run-test index 5f85b0875b..be0a88d1f9 100755 --- a/test/run-test +++ b/test/run-test @@ -121,6 +121,8 @@ if [ -z "$HIDDENAPI" ]; then export HIDDENAPI="${ANDROID_HOST_OUT}/bin/hiddenapi" fi +chroot= + info="info.txt" build="build" run="run" @@ -380,6 +382,16 @@ while true; do break fi shift + elif [ "x$1" = "x--chroot" ]; then + shift + if [ "x$1" = "x" ]; then + echo "$0 missing argument to --chroot" 1>&2 + usage="yes" + break + fi + chroot="$1" + run_args="${run_args} --chroot $1" + shift elif [ "x$1" = "x--android-root" ]; then shift if [ "x$1" = "x" ]; then @@ -449,6 +461,9 @@ while true; do fi done +# The DEX_LOCATION with the chroot prefix, if any. +chroot_dex_location="$chroot$DEX_LOCATION" + run_args="${run_args} ${image_args}" # Allocate file descriptor real_stderr and redirect it to the shell's error # output (fd 2). @@ -476,7 +491,7 @@ function err_echo() { # tmp_dir may be relative, resolve. # # Cannot use realpath, as it does not exist on Mac. -# Cannot us a simple "cd", as the path might not be created yet. +# Cannot use a simple "cd", as the path might not be created yet. # Cannot use readlink -m, as it does not exist on Mac. # Fallback to nuclear option: noncanonical_tmp_dir=$tmp_dir @@ -550,7 +565,13 @@ if [ "$target_mode" = "no" ]; then if [ "$runtime" = "jvm" ]; then if [ "$prebuild_mode" = "yes" ]; then err_echo "--prebuild with --jvm is unsupported" - exit 1; + exit 1 + fi + else + # ART/Dalvik host mode. + if [ -n "$chroot" ]; then + err_echo "--chroot with --host is unsupported" + exit 1 fi fi fi @@ -628,6 +649,12 @@ if [ "$bisection_search" = "yes" -a "$have_patchoat" = "no" ]; then usage="yes" fi +# TODO: Chroot-based bisection search is not supported yet (see below); implement it. +if [ "$bisection_search" = "yes" -a -n "$chroot" ]; then + err_echo "--chroot with --bisection-search is unsupported" + exit 1 +fi + if [ "$usage" = "no" ]; then if [ "x$1" = "x" -o "x$1" = "x-" ]; then test_dir=`basename "$oldwd"` @@ -732,6 +759,7 @@ if [ "$usage" = "yes" ]; then echo " Run with jvmti method redefinition stress testing" echo " --always-clean Delete the test files even if the test fails." echo " --never-clean Keep the test files even if the test succeeds." + echo " --chroot [newroot] Run with root directory set to newroot." echo " --android-root [path] The path on target for the android root. (/system by default)." echo " --dex2oat-swap Use a dex2oat swap file." echo " --instruction-set-features [string]" @@ -866,7 +894,7 @@ if [ "$dev_mode" = "yes" ]; then if [ "$run_exit" = "0" ]; then if [ "$run_checker" = "yes" ]; then if [ "$target_mode" = "yes" ]; then - adb pull $cfg_output_dir/$cfg_output &> /dev/null + adb pull "$chroot/$cfg_output_dir/$cfg_output" &> /dev/null fi "$checker" $checker_args "$cfg_output" "$tmp_dir" 2>&1 checker_exit="$?" @@ -888,7 +916,7 @@ elif [ "$update_mode" = "yes" ]; then "./${run}" $run_args "$@" >"$output" 2>&1 if [ "$run_checker" = "yes" ]; then if [ "$target_mode" = "yes" ]; then - adb pull $cfg_output_dir/$cfg_output &> /dev/null + adb pull "$chroot/$cfg_output_dir/$cfg_output" &> /dev/null fi "$checker" -q $checker_args "$cfg_output" "$tmp_dir" >> "$output" 2>&1 fi @@ -926,7 +954,7 @@ else good_run="no" elif [ "$run_checker" = "yes" ]; then if [ "$target_mode" = "yes" ]; then - adb pull $cfg_output_dir/$cfg_output &> /dev/null + adb pull "$chroot/$cfg_output_dir/$cfg_output" &> /dev/null fi "$checker" -q $checker_args "$cfg_output" "$tmp_dir" >> "$output" 2>&1 checker_exit="$?" @@ -986,6 +1014,7 @@ fi ) 2>&${real_stderr} 1>&2 # Attempt bisection only if the test failed. +# TODO: Implement support for chroot-based bisection search. if [ "$bisection_search" = "yes" -a "$good" != "yes" ]; then # Bisecting works by skipping different optimization passes which breaks checker assertions. if [ "$run_checker" == "yes" ]; then @@ -997,17 +1026,18 @@ if [ "$bisection_search" = "yes" -a "$good" != "yes" ]; then maybe_device_mode="" raw_cmd="" if [ "$target_mode" = "yes" ]; then - # Produce cmdline.sh in $DEX_LOCATION. "$@" is passed as a runtime option + # Produce cmdline.sh in $chroot_dex_location. "$@" is passed as a runtime option # so that cmdline.sh forwards its arguments to dalvikvm. invoke-with is set # to exec in order to preserve pid when calling dalvikvm. This is required # for bisection search to correctly retrieve logs from device. "./${run}" $run_args --runtime-option '"$@"' --invoke-with exec --dry-run "$@" &> /dev/null - adb shell chmod u+x "$DEX_LOCATION/cmdline.sh" + adb shell chmod u+x "$chroot_dex_location/cmdline.sh" maybe_device_mode="--device" raw_cmd="$DEX_LOCATION/cmdline.sh" else raw_cmd="$cwd/${run} --external-log-tags $run_args $@" fi + # TODO: Pass a `--chroot` option to the bisection_search.py script and use it there. $ANDROID_BUILD_TOP/art/tools/bisection_search/bisection_search.py \ $maybe_device_mode \ --raw-cmd="$raw_cmd" \ @@ -1023,7 +1053,7 @@ if [ "$always_clean" = "yes" -o "$good" = "yes" ] && [ "$never_clean" = "no" ]; cd "$oldwd" rm -rf "$tmp_dir" if [ "$target_mode" = "yes" -a "$build_exit" = "0" ]; then - adb shell rm -rf $DEX_LOCATION + adb shell rm -rf $chroot_dex_location fi if [ "$good" = "yes" ]; then exit 0 @@ -1040,7 +1070,7 @@ fi else echo "${TEST_NAME} files left in ${tmp_dir} on host" if [ "$target_mode" == "yes" ]; then - echo "and in ${DEX_LOCATION} on target" + echo "and in ${chroot_dex_location} on target" fi fi diff --git a/test/testrunner/env.py b/test/testrunner/env.py index 7564f5a6b4..0c1c308218 100644 --- a/test/testrunner/env.py +++ b/test/testrunner/env.py @@ -91,6 +91,8 @@ HOST_2ND_ARCH_PREFIX = _get_build_var('HOST_2ND_ARCH_PREFIX') HOST_2ND_ARCH_PREFIX_DEX2OAT_HOST_INSTRUCTION_SET_FEATURES = _env.get( HOST_2ND_ARCH_PREFIX + 'DEX2OAT_HOST_INSTRUCTION_SET_FEATURES') +ART_TEST_CHROOT = _env.get('ART_TEST_CHROOT') + ART_TEST_ANDROID_ROOT = _env.get('ART_TEST_ANDROID_ROOT') ART_TEST_WITH_STRACE = _getEnvBoolean('ART_TEST_DEBUG_GC', False) diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 88b509d3b7..254ffc9db1 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -320,6 +320,9 @@ def run_tests(tests): if env.ART_TEST_BISECTION: options_all += ' --bisection-search' + if env.ART_TEST_CHROOT: + options_all += ' --chroot ' + env.ART_TEST_CHROOT + if env.ART_TEST_ANDROID_ROOT: options_all += ' --android-root ' + env.ART_TEST_ANDROID_ROOT -- GitLab From e4f1c51df96c5a690b263ce9020d3fe97b33b84e Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Mon, 30 Oct 2017 13:28:28 +0000 Subject: [PATCH 358/749] Support a chroot-based environment in ART Buildbot's target harness. When ART_TEST_CHROOT is defined, instead of having the ART Buildbot install ART into a local path (set with ART_TEST_ANDROID_ROOT) and hijack the linker (using CUSTOM_TARGET_LINKER), prepare and use a chroot environment (set with ART_TEST_CHROOT). In this scenario, ART and its dependencies can be built and used normally (in particular, when building from the master-art Android manifest) and still be executed as a standalone runtime outside of the device's /system path, for testing purposes. Test: Have the ART Buildbot build and run ART on device with chroot. Bug: 34729697 Bug: 68125496 Change-Id: I08f1acd0d2813584f703fedb84e69df954cbdbda --- tools/buildbot-build.sh | 4 ++ tools/cleanup-buildbot-device.sh | 64 ++++++++++++++++++++++++++++++++ tools/run-jdwp-tests.sh | 17 +++++++++ tools/run-libcore-tests.sh | 29 ++++++++++++++- tools/setup-buildbot-device.sh | 58 ++++++++++++++++++++++++++++- 5 files changed, 168 insertions(+), 4 deletions(-) create mode 100755 tools/cleanup-buildbot-device.sh diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh index 31bddd5213..10eb9360af 100755 --- a/tools/buildbot-build.sh +++ b/tools/buildbot-build.sh @@ -83,6 +83,10 @@ elif [[ $mode == "target" ]]; then make_command+=" debuggerd su" make_command+=" ${out_dir}/host/linux-x86/bin/adb libstdc++ " make_command+=" ${out_dir}/target/product/${TARGET_PRODUCT}/system/etc/public.libraries.txt" + if [[ -n "$ART_TEST_CHROOT" ]]; then + # These targets are needed for the chroot environment. + make_command+=" crash_dump event-log-tags" + fi mode_suffix="-target" fi diff --git a/tools/cleanup-buildbot-device.sh b/tools/cleanup-buildbot-device.sh new file mode 100755 index 0000000000..53072ae7c5 --- /dev/null +++ b/tools/cleanup-buildbot-device.sh @@ -0,0 +1,64 @@ +#!/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. + +green='\033[0;32m' +nc='\033[0m' + +# Setup as root, as device cleanup requires it. +adb root +adb wait-for-device + +if [[ -n "$ART_TEST_CHROOT" ]]; then + # Check that ART_TEST_CHROOT is correctly defined. + if [[ "x$ART_TEST_CHROOT" != x/* ]]; then + echo "$ART_TEST_CHROOT is not an absolute path" + exit 1 + fi + + echo -e "${green}Clean up /system in chroot${nc}" + # Remove all files under /system except the potential property_contexts file. + # + # The current ART Buildbot set-up runs the "setup device" step + # (performed by script tools/setup-buildbot-device.sh) before the + # "device cleanup" step (implemented by this script). As + # property_contexts file aliases are created during the former step, + # we need this exception to prevent the property_contexts file under + # /system in the chroot from being removed by the latter step. + # + # TODO: Reorder ART Buildbot steps so that "device cleanup" happens + # before "setup device" and remove this special case. + # + # TODO: Also consider adding a "tear down device" step on the ART + # Buildbot (at the very end of a build) undoing (some of) the work + # done in the "device setup" step. + adb shell find "$ART_TEST_CHROOT/system" \ + ! -path "$ART_TEST_CHROOT/system/etc/selinux/plat_property_contexts" \ + ! -type d \ + -exec rm -f \{\} + + + echo -e "${green}Clean up some subdirs in /data in chroot${nc}" + adb shell rm -rf \ + "$ART_TEST_CHROOT/data/local/tmp/*" \ + "$ART_TEST_CHROOT/data/art-test" \ + "$ART_TEST_CHROOT/data/nativetest" \ + "$ART_TEST_CHROOT/data/nativetest64" \ + "$ART_TEST_CHROOT/data/run-test" \ + "$ART_TEST_CHROOT/data/dalvik-cache/*" \ + "$ART_TEST_CHROOT/data/misc/trace/*" +else + adb shell rm -rf \ + /data/local/tmp /data/art-test /data/nativetest /data/nativetest64 '/data/misc/trace/*' +fi diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index 56d412bfa0..d376cad9da 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -68,6 +68,8 @@ test="org.apache.harmony.jpda.tests.share.AllTests" mode="target" # Use JIT compiling by default. use_jit=true +# Don't use chroot by default. +use_chroot=false variant_cmdline_parameter="--variant=X32" dump_command="/bin/true" # Timeout of JDWP test in ms. @@ -110,6 +112,15 @@ while true; do # We don't care about jit with the RI use_jit=false shift + elif [[ "$1" == "--chroot" ]]; then + use_chroot=true + # Adjust settings for chroot environment. + art="/system/bin/art" + art_debugee="sh /system/bin/art" + vm_command="--vm-command=$art" + device_dir="--device-dir=/tmp" + # Shift the "--chroot" flag and its argument. + shift 2 elif [[ $1 == --test-timeout-ms ]]; then # Remove the --test-timeout-ms from the arguments. args=${args/$1} @@ -191,6 +202,12 @@ while true; do fi done +if $use_chroot && [[ $mode == "host" ]]; then + # Chroot-based testing is not supported on host. + echo "Cannot use --chroot with --mode=host" + exit 1 +fi + if [[ $has_gdb = "yes" ]]; then if [[ $explicit_debug = "no" ]]; then debug="yes" diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh index 26b5c0a09b..3537c1b861 100755 --- a/tools/run-libcore-tests.sh +++ b/tools/run-libcore-tests.sh @@ -104,10 +104,14 @@ vogar_args=$@ gcstress=false debug=false +# Don't use device mode by default. +device_mode=false +# Don't use chroot by default. +use_chroot=false + while true; do if [[ "$1" == "--mode=device" ]]; then - vogar_args="$vogar_args --device-dir=/data/local/tmp" - vogar_args="$vogar_args --vm-command=$android_root/bin/art" + device_mode=true vogar_args="$vogar_args --vm-arg -Ximage:/data/art-test/core.art" shift elif [[ "$1" == "--mode=host" ]]; then @@ -131,6 +135,10 @@ while true; do elif [[ "$1" == "-Xgc:gcstress" ]]; then gcstress=true shift + elif [[ "$1" == "--chroot" ]]; then + use_chroot=true + # Shift the "--chroot" flag and its argument. + shift 2 elif [[ "$1" == "" ]]; then break else @@ -138,6 +146,23 @@ while true; do fi done +if $device_mode; then + if $use_chroot; then + vogar_args="$vogar_args --device-dir=/tmp" + vogar_args="$vogar_args --vm-command=/system/bin/art" + else + vogar_args="$vogar_args --device-dir=/data/local/tmp" + vogar_args="$vogar_args --vm-command=$android_root/bin/art" + fi +else + # Host mode. + if $use_chroot; then + # Chroot-based testing is not supported on host. + echo "Cannot use --chroot with --mode=host" + exit 1 + fi +fi + # Increase the timeout, as vogar cannot set individual test # timeout when being asked to run packages, and some tests go above # the default timeout. diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh index 5ce7f5244e..f71d973925 100755 --- a/tools/setup-buildbot-device.sh +++ b/tools/setup-buildbot-device.sh @@ -17,8 +17,7 @@ green='\033[0;32m' nc='\033[0m' -# Setup as root, as the next buildbot step (device cleanup) requires it. -# This is also required to set the date, if needed. +# Setup as root, as some actions performed here (e.g. setting the date) requires it. adb root adb wait-for-device @@ -100,3 +99,58 @@ else processes=$(adb shell "ps" | grep dalvikvm | awk '{print $2}') for i in $processes; do adb shell kill -9 $i; done fi + +if [[ -n "$ART_TEST_CHROOT" ]]; then + # Prepare the chroot dir. + echo -e "${green}Prepare the chroot dir in $ART_TEST_CHROOT${nc}" + + # Check that ART_TEST_CHROOT is correctly defined. + [[ "x$ART_TEST_CHROOT" = x/* ]] || { echo "$ART_TEST_CHROOT is not an absolute path"; exit 1; } + + # Create chroot. + adb shell mkdir -p "$ART_TEST_CHROOT" + + # Provide property_contexts file(s) in chroot. + # This is required to have Android system properties work from the chroot. + # Notes: + # - In Android N, only '/property_contexts' is expected. + # - In Android O, property_context files are expected under /system and /vendor. + # (See bionic/libc/bionic/system_properties.cpp for more information.) + property_context_files="/property_contexts \ + /system/etc/selinux/plat_property_contexts \ + /vendor/etc/selinux/nonplat_property_context \ + /plat_property_contexts \ + /nonplat_property_contexts" + for f in $property_context_files; do + adb shell test -f "$f" \ + "&&" mkdir -p "$ART_TEST_CHROOT$(dirname $f)" \ + "&&" cp -f "$f" "$ART_TEST_CHROOT$f" + done + + # Create directories required for ART testing in chroot. + adb shell mkdir -p "$ART_TEST_CHROOT/tmp" + adb shell mkdir -p "$ART_TEST_CHROOT/data/dalvik-cache" + adb shell mkdir -p "$ART_TEST_CHROOT/data/local/tmp" + + # Populate /etc in chroot with required files. + adb shell mkdir -p "$ART_TEST_CHROOT/system/etc" + adb shell "cd $ART_TEST_CHROOT && ln -s system/etc etc" + + # Provide /proc in chroot. + adb shell mkdir -p "$ART_TEST_CHROOT/proc" + adb shell mount | grep -q "^proc on $ART_TEST_CHROOT/proc type proc " \ + || adb shell mount -t proc proc "$ART_TEST_CHROOT/proc" + + # Provide /sys in chroot. + adb shell mkdir -p "$ART_TEST_CHROOT/sys" + adb shell mount | grep -q "^sysfs on $ART_TEST_CHROOT/sys type sysfs " \ + || adb shell mount -t sysfs sysfs "$ART_TEST_CHROOT/sys" + # Provide /sys/kernel/debug in chroot. + adb shell mount | grep -q "^debugfs on $ART_TEST_CHROOT/sys/kernel/debug type debugfs " \ + || adb shell mount -t debugfs debugfs "$ART_TEST_CHROOT/sys/kernel/debug" + + # Provide /dev in chroot. + adb shell mkdir -p "$ART_TEST_CHROOT/dev" + adb shell mount | grep -q "^tmpfs on $ART_TEST_CHROOT/dev type tmpfs " \ + || adb shell mount -o bind /dev "$ART_TEST_CHROOT/dev" +fi -- GitLab From 2da52b084b940a5330aa985b687c852495bc38ff Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 8 May 2018 16:31:34 +0100 Subject: [PATCH 359/749] ART: Add --dump-pass-timings compiler option. This replaces the old --dump-passes option removed in https://android-review.googlesource.com/549200 . Using --dump-timing for timing optimization passes makes the high level timings useless, so return to using two different options for these. Test: Manually run dex2oat with --dump-timings, --dump-pass-timings and both. Change-Id: Iddc4cfee35652fb493656e7d6081a898c2894f72 --- compiler/driver/compiler_options.cc | 1 + compiler/driver/compiler_options.h | 5 +++++ compiler/driver/compiler_options_map-inl.h | 7 +++++++ compiler/driver/compiler_options_map.def | 1 + compiler/optimizing/optimizing_compiler.cc | 2 +- dex2oat/dex2oat.cc | 3 +++ 6 files changed, 18 insertions(+), 1 deletion(-) diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc index 2d82d79c4a..933be4f004 100644 --- a/compiler/driver/compiler_options.cc +++ b/compiler/driver/compiler_options.cc @@ -51,6 +51,7 @@ CompilerOptions::CompilerOptions() implicit_suspend_checks_(false), compile_pic_(false), dump_timings_(false), + dump_pass_timings_(false), dump_stats_(false), verbose_methods_(), abort_on_hard_verifier_failure_(false), diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index cdd9d4de00..cee989b315 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -270,6 +270,10 @@ class CompilerOptions FINAL { return dump_timings_; } + bool GetDumpPassTimings() const { + return dump_pass_timings_; + } + bool GetDumpStats() const { return dump_stats_; } @@ -316,6 +320,7 @@ class CompilerOptions FINAL { bool implicit_suspend_checks_; bool compile_pic_; bool dump_timings_; + bool dump_pass_timings_; bool dump_stats_; // Vector of methods to have verbose output enabled for. diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h index 3b18db09fc..32fc887b8e 100644 --- a/compiler/driver/compiler_options_map-inl.h +++ b/compiler/driver/compiler_options_map-inl.h @@ -85,6 +85,10 @@ inline bool ReadCompilerOptions(Base& map, CompilerOptions* options, std::string options->dump_timings_ = true; } + if (map.Exists(Base::DumpPassTimings)) { + options->dump_pass_timings_ = true; + } + if (map.Exists(Base::DumpStats)) { options->dump_stats_ = true; } @@ -146,6 +150,9 @@ inline void AddCompilerOptionsArgumentParserOptions(Builder& b) { .Define({"--dump-timings"}) .IntoKey(Map::DumpTimings) + .Define({"--dump-pass-timings"}) + .IntoKey(Map::DumpPassTimings) + .Define({"--dump-stats"}) .IntoKey(Map::DumpStats) diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def index acddae7299..529d43fc72 100644 --- a/compiler/driver/compiler_options_map.def +++ b/compiler/driver/compiler_options_map.def @@ -60,6 +60,7 @@ COMPILER_OPTIONS_KEY (ParseStringList<','>, VerboseMethods) COMPILER_OPTIONS_KEY (bool, DeduplicateCode, true) COMPILER_OPTIONS_KEY (Unit, CountHotnessInCompiledCode) COMPILER_OPTIONS_KEY (Unit, DumpTimings) +COMPILER_OPTIONS_KEY (Unit, DumpPassTimings) COMPILER_OPTIONS_KEY (Unit, DumpStats) #undef COMPILER_OPTIONS_KEY diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 6e2c99444c..c4977decd9 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -109,7 +109,7 @@ class PassObserver : public ValueObject { : graph_(graph), last_seen_graph_size_(0), cached_method_name_(), - timing_logger_enabled_(compiler_driver->GetCompilerOptions().GetDumpTimings()), + timing_logger_enabled_(compiler_driver->GetCompilerOptions().GetDumpPassTimings()), timing_logger_(timing_logger_enabled_ ? GetMethodName() : "", true, true), disasm_info_(graph->GetAllocator()), visualizer_oss_(), diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index df38ee3a34..3252354417 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -350,6 +350,9 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(""); UsageError(" --dump-timings: display a breakdown of where time was spent"); UsageError(""); + UsageError(" --dump-pass-timings: display a breakdown of time spent in optimization"); + UsageError(" passes for each compiled method."); + UsageError(""); UsageError(" -g"); UsageError(" --generate-debug-info: Generate debug information for native debugging,"); UsageError(" such as stack unwinding information, ELF symbols and DWARF sections."); -- GitLab From a90dd515c4451ad3d6e7ecdf0f0527fcc1e32d91 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 4 May 2018 15:04:45 +0100 Subject: [PATCH 360/749] Reimplement HInstruction::Is##type(). Use a table-based implementation for abstract kinds. This implementation can be fully inlined and even eliminated if the range of possible kinds is known. There is a small (<0.5%) improvement of dex2oat compile time for a big app (sum of "Compile Dex File Quick" dumped for --dump-timings with the timing of compiler passes hacked away) on aosp_taimen-userdebug, little cores, -j4. The sizes of libart-compiler.so have changed: - lib/libart-compiler.so: 2178608 -> 2169136 (-9472) .rodata: 0xd36c -> 0xd55c (+496) .text: 0x175914 -> 0x175f44 (+1584) .data.rel.ro: 0xa858 -> 0x84e8 (-9072) - lib64/libart-compiler.so: 3091432 -> 3064248 (-27184) .rodata: 0x15aa4 -> 0x15c94 (+496) .text: 0x203304 -> 0x202294 (-4208) .data.rel.ro: 0x151d8 -> 0x10a68 (-18288) Note that .data.rel.ro is always dirty memory, .rodata and .text is always clean. Test: Rely on TreeHugger Change-Id: I95e6040ecd23cad83f024970c3bf785d32169deb --- compiler/optimizing/nodes.h | 50 ++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 1d913c0d25..ae1e6065cd 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1534,10 +1534,7 @@ FOR_EACH_INSTRUCTION(FORWARD_DECLARATION) #define DECLARE_ABSTRACT_INSTRUCTION(type) \ private: \ H##type& operator=(const H##type&) = delete; \ - public: \ - bool Is##type() const { return As##type() != nullptr; } \ - const H##type* As##type() const { return this; } \ - H##type* As##type() { return this; } + public: #define DEFAULT_COPY_CONSTRUCTOR(type) \ explicit H##type(const H##type& other) = default; @@ -2231,19 +2228,17 @@ class HInstruction : public ArenaObject { void MoveBeforeFirstUserAndOutOfLoops(); #define INSTRUCTION_TYPE_CHECK(type, super) \ - bool Is##type() const; \ - const H##type* As##type() const; \ - H##type* As##type(); + bool Is##type() const; - FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK) + FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CHECK) #undef INSTRUCTION_TYPE_CHECK -#define INSTRUCTION_TYPE_CHECK(type, super) \ - bool Is##type() const { return (As##type() != nullptr); } \ - virtual const H##type* As##type() const { return nullptr; } \ - virtual H##type* As##type() { return nullptr; } - FOR_EACH_ABSTRACT_INSTRUCTION(INSTRUCTION_TYPE_CHECK) -#undef INSTRUCTION_TYPE_CHECK +#define INSTRUCTION_TYPE_CAST(type, super) \ + const H##type* As##type() const; \ + H##type* As##type(); + + FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CAST) +#undef INSTRUCTION_TYPE_CAST // Return a clone of the instruction if it is clonable (shallow copy by default, custom copy // if a custom copy-constructor is provided for a particular type). If IsClonable() is false for @@ -7753,7 +7748,27 @@ inline bool IsZeroBitPattern(HInstruction* instruction) { } #define INSTRUCTION_TYPE_CHECK(type, super) \ - inline bool HInstruction::Is##type() const { return GetKind() == k##type; } \ + inline bool HInstruction::Is##type() const { return GetKind() == k##type; } + FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK) +#undef INSTRUCTION_TYPE_CHECK + +#define INSTRUCTION_TYPE_CHECK_RESULT(type, super) \ + std::is_base_of::value, +#define INSTRUCTION_TYPE_CHECK(type, super) \ + inline bool HInstruction::Is##type() const { \ + DCHECK_LT(GetKind(), kLastInstructionKind); \ + using BaseType = H##type; \ + static constexpr bool results[] = { \ + FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK_RESULT) \ + }; \ + return results[static_cast(GetKind())]; \ + } + + FOR_EACH_ABSTRACT_INSTRUCTION(INSTRUCTION_TYPE_CHECK) +#undef INSTRUCTION_TYPE_CHECK +#undef INSTRUCTION_TYPE_CHECK_CASE + +#define INSTRUCTION_TYPE_CAST(type, super) \ inline const H##type* HInstruction::As##type() const { \ return Is##type() ? down_cast(this) : nullptr; \ } \ @@ -7761,8 +7776,9 @@ inline bool IsZeroBitPattern(HInstruction* instruction) { return Is##type() ? static_cast(this) : nullptr; \ } - FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK) -#undef INSTRUCTION_TYPE_CHECK + FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CAST) +#undef INSTRUCTION_TYPE_CAST + // Create space in `blocks` for adding `number_of_new_blocks` entries // starting at location `at`. Blocks after `at` are moved accordingly. -- GitLab From 5a2993cd9a158a8fb36d1fdd65d796b3e6e6db2c Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 8 May 2018 18:11:25 +0100 Subject: [PATCH 361/749] ART: Templatize DexFileVerifier::CheckIntraIdSection(). And use macros in DexFileVerifier::CheckIntraSection() to avoid code duplication. This is a follow-up to https://android-review.googlesource.com/672438 but the improvements in instruction count measured on host with "perf stat dexdump2 -c " are absolutely insignificant (0.0003%). Test: Rely on TreeHugger. Change-Id: I95fd3b2f7f2d767e5446dd71552b34ea55f4bd47 --- libdexfile/dex/dex_file_verifier.cc | 190 ++++++++-------------------- libdexfile/dex/dex_file_verifier.h | 4 +- 2 files changed, 52 insertions(+), 142 deletions(-) diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc index ba65fc9b28..a32f64e49f 100644 --- a/libdexfile/dex/dex_file_verifier.cc +++ b/libdexfile/dex/dex_file_verifier.cc @@ -1761,63 +1761,13 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_c return true; } -bool DexFileVerifier::CheckIntraSectionIterateByType(size_t offset, - uint32_t count, - DexFile::MapItemType type) { - switch (type) { - case DexFile::kDexTypeHeaderItem: - return CheckIntraSectionIterate(offset, count); - case DexFile::kDexTypeStringIdItem: - return CheckIntraSectionIterate(offset, count); - case DexFile::kDexTypeTypeIdItem: - return CheckIntraSectionIterate(offset, count); - case DexFile::kDexTypeProtoIdItem: - return CheckIntraSectionIterate(offset, count); - case DexFile::kDexTypeFieldIdItem: - return CheckIntraSectionIterate(offset, count); - case DexFile::kDexTypeMethodIdItem: - return CheckIntraSectionIterate(offset, count); - case DexFile::kDexTypeClassDefItem: - return CheckIntraSectionIterate(offset, count); - case DexFile::kDexTypeCallSiteIdItem: - return CheckIntraSectionIterate(offset, count); - case DexFile::kDexTypeMethodHandleItem: - return CheckIntraSectionIterate(offset, count); - case DexFile::kDexTypeMapList: - return CheckIntraSectionIterate(offset, count); - case DexFile::kDexTypeTypeList: - return CheckIntraSectionIterate(offset, count); - case DexFile::kDexTypeAnnotationSetRefList: - return CheckIntraSectionIterate(offset, count); - case DexFile::kDexTypeAnnotationSetItem: - return CheckIntraSectionIterate(offset, count); - case DexFile::kDexTypeClassDataItem: - return CheckIntraSectionIterate(offset, count); - case DexFile::kDexTypeCodeItem: - return CheckIntraSectionIterate(offset, count); - case DexFile::kDexTypeStringDataItem: - return CheckIntraSectionIterate(offset, count); - case DexFile::kDexTypeDebugInfoItem: - return CheckIntraSectionIterate(offset, count); - case DexFile::kDexTypeAnnotationItem: - return CheckIntraSectionIterate(offset, count); - case DexFile::kDexTypeEncodedArrayItem: - return CheckIntraSectionIterate(offset, count); - case DexFile::kDexTypeAnnotationsDirectoryItem: - return CheckIntraSectionIterate(offset, count); - } - LOG(FATAL) << "Unreachable"; - UNREACHABLE(); -} - -bool DexFileVerifier::CheckIntraIdSection(size_t offset, - uint32_t count, - DexFile::MapItemType type) { +template +bool DexFileVerifier::CheckIntraIdSection(size_t offset, uint32_t count) { uint32_t expected_offset; uint32_t expected_size; // Get the expected offset and size from the header. - switch (type) { + switch (kType) { case DexFile::kDexTypeStringIdItem: expected_offset = header_->string_ids_off_; expected_size = header_->string_ids_size_; @@ -1843,7 +1793,7 @@ bool DexFileVerifier::CheckIntraIdSection(size_t offset, expected_size = header_->class_defs_size_; break; default: - ErrorStringPrintf("Bad type for id section: %x", type); + ErrorStringPrintf("Bad type for id section: %x", kType); return false; } @@ -1857,7 +1807,7 @@ bool DexFileVerifier::CheckIntraIdSection(size_t offset, return false; } - return CheckIntraSectionIterateByType(offset, count, type); + return CheckIntraSectionIterate(offset, count); } template @@ -1888,7 +1838,8 @@ bool DexFileVerifier::CheckIntraDataSection(size_t offset, uint32_t count) { } bool DexFileVerifier::CheckIntraSection() { - const DexFile::MapList* map = reinterpret_cast(begin_ + header_->map_off_); + const DexFile::MapList* map = + reinterpret_cast(begin_ + header_->map_off_); const DexFile::MapItem* item = map->list_; size_t offset = 0; uint32_t count = map->size_; @@ -1927,17 +1878,22 @@ bool DexFileVerifier::CheckIntraSection() { ptr_ = begin_ + header_->header_size_; offset = header_->header_size_; break; - case DexFile::kDexTypeStringIdItem: - case DexFile::kDexTypeTypeIdItem: - case DexFile::kDexTypeProtoIdItem: - case DexFile::kDexTypeFieldIdItem: - case DexFile::kDexTypeMethodIdItem: - case DexFile::kDexTypeClassDefItem: - if (!CheckIntraIdSection(section_offset, section_count, type)) { - return false; - } - offset = ptr_ - begin_; + +#define CHECK_INTRA_ID_SECTION_CASE(type) \ + case type: \ + if (!CheckIntraIdSection(section_offset, section_count)) { \ + return false; \ + } \ + offset = ptr_ - begin_; \ break; + CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeStringIdItem) + CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeTypeIdItem) + CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeProtoIdItem) + CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeFieldIdItem) + CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeMethodIdItem) + CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeClassDefItem) +#undef CHECK_INTRA_ID_SECTION_CASE + case DexFile::kDexTypeMapList: if (UNLIKELY(section_count != 1)) { ErrorStringPrintf("Multiple map list items"); @@ -1951,80 +1907,34 @@ bool DexFileVerifier::CheckIntraSection() { ptr_ += sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem)); offset = section_offset + sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem)); break; - case DexFile::kDexTypeMethodHandleItem: - CheckIntraSectionIterate(section_offset, section_count); - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeCallSiteIdItem: - CheckIntraSectionIterate(section_offset, section_count); - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeTypeList: - if (!CheckIntraDataSection(section_offset, section_count)) { - return false; - } - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeAnnotationSetRefList: - if (!CheckIntraDataSection(section_offset, - section_count)) { - return false; - } - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeAnnotationSetItem: - if (!CheckIntraDataSection(section_offset, - section_count)) { - return false; - } - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeClassDataItem: - if (!CheckIntraDataSection(section_offset, section_count)) { - return false; - } - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeCodeItem: - if (!CheckIntraDataSection(section_offset, section_count)) { - return false; - } - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeStringDataItem: - if (!CheckIntraDataSection(section_offset, - section_count)) { - return false; - } - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeDebugInfoItem: - if (!CheckIntraDataSection(section_offset, section_count)) { - return false; - } - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeAnnotationItem: - if (!CheckIntraDataSection(section_offset, - section_count)) { - return false; - } - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeEncodedArrayItem: - if (!CheckIntraDataSection(section_offset, - section_count)) { - return false; - } - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeAnnotationsDirectoryItem: - if (!CheckIntraDataSection(section_offset, - section_count)) { - return false; - } - offset = ptr_ - begin_; - break; + +#define CHECK_INTRA_SECTION_ITERATE_CASE(type) \ + case type: \ + CheckIntraSectionIterate(section_offset, section_count); \ + offset = ptr_ - begin_; \ + break; + CHECK_INTRA_SECTION_ITERATE_CASE(DexFile::kDexTypeMethodHandleItem) + CHECK_INTRA_SECTION_ITERATE_CASE(DexFile::kDexTypeCallSiteIdItem) +#undef CHECK_INTRA_SECTION_ITERATE_CASE + +#define CHECK_INTRA_DATA_SECTION_CASE(type) \ + case type: \ + if (!CheckIntraDataSection(section_offset, section_count)) { \ + return false; \ + } \ + offset = ptr_ - begin_; \ + break; + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeTypeList) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeAnnotationSetRefList) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeAnnotationSetItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeClassDataItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeCodeItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeStringDataItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeDebugInfoItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeAnnotationItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeEncodedArrayItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeAnnotationsDirectoryItem) +#undef CHECK_INTRA_DATA_SECTION_CASE } if (offset == current_offset) { diff --git a/libdexfile/dex/dex_file_verifier.h b/libdexfile/dex/dex_file_verifier.h index eaffc6240a..04d8d71fa8 100644 --- a/libdexfile/dex/dex_file_verifier.h +++ b/libdexfile/dex/dex_file_verifier.h @@ -126,8 +126,8 @@ class DexFileVerifier { template bool CheckIntraSectionIterate(size_t offset, uint32_t count); - bool CheckIntraSectionIterateByType(size_t offset, uint32_t count, DexFile::MapItemType type); - bool CheckIntraIdSection(size_t offset, uint32_t count, DexFile::MapItemType type); + template + bool CheckIntraIdSection(size_t offset, uint32_t count); template bool CheckIntraDataSection(size_t offset, uint32_t count); bool CheckIntraSection(); -- GitLab From 238c963a08f9d2c1a39805c2daa9c32482bd9e36 Mon Sep 17 00:00:00 2001 From: Chih-Hung Hsieh Date: Tue, 8 May 2018 10:22:52 -0700 Subject: [PATCH 362/749] Do not use clang lld for oatdump tests Bug: 79417743 Test: HOST_PREFER_32_BIT=true m -j32 test-art-host-gtest-oatdump_app_test Change-Id: If217972d4983ef8c3425afb70cc59a227e576ee3 --- dex2oat/Android.bp | 2 ++ oatdump/Android.bp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index 4fafca9e1b..18548baf7f 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -294,6 +294,8 @@ art_cc_binary { use_clang_lld: true, }, }, + // b/79417743, oatdump 32-bit tests failed with clang lld + use_clang_lld: false, ldflags: [ // We need this because GC stress mode makes use of // _Unwind_GetIP and _Unwind_Backtrace and the symbols are also diff --git a/oatdump/Android.bp b/oatdump/Android.bp index be12c8e406..535acdff62 100644 --- a/oatdump/Android.bp +++ b/oatdump/Android.bp @@ -24,6 +24,8 @@ cc_defaults { shared_libs: ["libcutils"], }, }, + // b/79417743, oatdump 32-bit tests failed with clang lld + use_clang_lld: false, header_libs: [ "art_cmdlineparser_headers", ], -- GitLab From a128c5cb01ddef00c6ab1b029e413e77264e88f5 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Tue, 8 May 2018 12:43:15 -0700 Subject: [PATCH 363/749] Add support for cortex-a55/cortex-a75. Bug: 78133793 Test: Builds and ran unit tests. Change-Id: I3e15e402dcc367ecea426895ec8e666887832e8d --- .../arch/arm/instruction_set_features_arm.cc | 2 ++ .../arm64/instruction_set_features_arm64.cc | 2 ++ .../instruction_set_features_arm64_test.cc | 20 +++++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc index 801254fd30..608999b3bf 100644 --- a/runtime/arch/arm/instruction_set_features_arm.cc +++ b/runtime/arch/arm/instruction_set_features_arm.cc @@ -46,9 +46,11 @@ ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromVariant( "cortex-a53", "cortex-a53.a57", "cortex-a53.a72", + "cortex-a55", "cortex-a57", "cortex-a72", "cortex-a73", + "cortex-a75", "exynos-m1", "denver", "kryo" diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc index 42c9a846d0..d0f61c946c 100644 --- a/runtime/arch/arm64/instruction_set_features_arm64.cc +++ b/runtime/arch/arm64/instruction_set_features_arm64.cc @@ -52,6 +52,8 @@ Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromVariant( // Check to see if this is an expected variant. static const char* arm64_known_variants[] = { "cortex-a35", + "cortex-a55", + "cortex-a75", "exynos-m1", "exynos-m2", "exynos-m3", diff --git a/runtime/arch/arm64/instruction_set_features_arm64_test.cc b/runtime/arch/arm64/instruction_set_features_arm64_test.cc index 7fd39b6b1b..b946f4f637 100644 --- a/runtime/arch/arm64/instruction_set_features_arm64_test.cc +++ b/runtime/arch/arm64/instruction_set_features_arm64_test.cc @@ -64,6 +64,26 @@ TEST(Arm64InstructionSetFeaturesTest, Arm64Features) { EXPECT_FALSE(kryo_features->Equals(cortex_a57_features.get())); EXPECT_STREQ("-a53", kryo_features->GetFeatureString().c_str()); EXPECT_EQ(kryo_features->AsBitmap(), 0U); + + std::unique_ptr cortex_a55_features( + InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a55", &error_msg)); + ASSERT_TRUE(cortex_a55_features.get() != nullptr) << error_msg; + EXPECT_EQ(cortex_a55_features->GetInstructionSet(), InstructionSet::kArm64); + EXPECT_TRUE(cortex_a55_features->Equals(cortex_a55_features.get())); + EXPECT_TRUE(cortex_a55_features->Equals(cortex_a35_features.get())); + EXPECT_FALSE(cortex_a55_features->Equals(cortex_a57_features.get())); + EXPECT_STREQ("-a53", cortex_a55_features->GetFeatureString().c_str()); + EXPECT_EQ(cortex_a55_features->AsBitmap(), 0U); + + std::unique_ptr cortex_a75_features( + InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a75", &error_msg)); + ASSERT_TRUE(cortex_a75_features.get() != nullptr) << error_msg; + EXPECT_EQ(cortex_a75_features->GetInstructionSet(), InstructionSet::kArm64); + EXPECT_TRUE(cortex_a75_features->Equals(cortex_a75_features.get())); + EXPECT_TRUE(cortex_a75_features->Equals(cortex_a35_features.get())); + EXPECT_FALSE(cortex_a75_features->Equals(cortex_a57_features.get())); + EXPECT_STREQ("-a53", cortex_a75_features->GetFeatureString().c_str()); + EXPECT_EQ(cortex_a75_features->AsBitmap(), 0U); } } // namespace art -- GitLab From 5db8b7b821d0b77cb3b041de7861b7d635050cc2 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 8 May 2018 16:10:59 -0700 Subject: [PATCH 364/749] ART: Relayout BaseMutex and Mutex Reduce size of BaseMutex (and thus all derived mutex classes) by changing the field layout. Reduction from 24B to 16B on 64-bit systems. Bug: 79365543 Test: m test-art-host Change-Id: Id7b50aa9af1ae5d249e62573ab67257c12922bfa --- runtime/base/mutex.cc | 6 +++--- runtime/base/mutex.h | 23 +++++++++++++---------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 73b464119e..da286d7e41 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -195,8 +195,8 @@ class ScopedContentionRecorder FINAL : public ValueObject { }; BaseMutex::BaseMutex(const char* name, LockLevel level) - : level_(level), - name_(name), + : name_(name), + level_(level), should_respond_to_empty_checkpoint_request_(false) { if (kLogLockContentions) { ScopedAllMutexesLock mu(this); @@ -386,7 +386,7 @@ void BaseMutex::DumpContention(std::ostream& os) const { Mutex::Mutex(const char* name, LockLevel level, bool recursive) - : BaseMutex(name, level), exclusive_owner_(0), recursive_(recursive), recursion_count_(0) { + : BaseMutex(name, level), exclusive_owner_(0), recursion_count_(0), recursive_(recursive) { #if ART_USE_FUTEXES DCHECK_EQ(0, state_.load(std::memory_order_relaxed)); DCHECK_EQ(0, num_contenders_.load(std::memory_order_relaxed)); diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 1cf4ddded4..602d183bbb 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -57,7 +57,7 @@ class Mutex; // partial ordering and thereby cause deadlock situations to fail checks. // // [1] http://www.drdobbs.com/parallel/use-lock-hierarchies-to-avoid-deadlock/204801163 -enum LockLevel { +enum LockLevel : uint8_t { kLoggingLock = 0, kSwapMutexesLock, kUnexpectedSignalLock, @@ -142,20 +142,20 @@ enum LockLevel { }; std::ostream& operator<<(std::ostream& os, const LockLevel& rhs); -const bool kDebugLocking = kIsDebugBuild; +constexpr bool kDebugLocking = kIsDebugBuild; // Record Log contention information, dumpable via SIGQUIT. #ifdef ART_USE_FUTEXES // To enable lock contention logging, set this to true. -const bool kLogLockContentions = false; +constexpr bool kLogLockContentions = false; #else // Keep this false as lock contention logging is supported only with // futex. -const bool kLogLockContentions = false; +constexpr bool kLogLockContentions = false; #endif -const size_t kContentionLogSize = 4; -const size_t kContentionLogDataSize = kLogLockContentions ? 1 : 0; -const size_t kAllMutexDataSize = kLogLockContentions ? 1 : 0; +constexpr size_t kContentionLogSize = 4; +constexpr size_t kContentionLogDataSize = kLogLockContentions ? 1 : 0; +constexpr size_t kAllMutexDataSize = kLogLockContentions ? 1 : 0; // Base class for all Mutex implementations class BaseMutex { @@ -196,9 +196,7 @@ class BaseMutex { void RecordContention(uint64_t blocked_tid, uint64_t owner_tid, uint64_t nano_time_blocked); void DumpContention(std::ostream& os) const; - const LockLevel level_; // Support for lock hierarchy. const char* const name_; - bool should_respond_to_empty_checkpoint_request_; // A log entry that records contention but makes no guarantee that either tid will be held live. struct ContentionLogEntry { @@ -221,6 +219,9 @@ class BaseMutex { }; ContentionLogData contention_log_data_[kContentionLogDataSize]; + const LockLevel level_; // Support for lock hierarchy. + bool should_respond_to_empty_checkpoint_request_; + public: bool HasEverContended() const { if (kLogLockContentions) { @@ -307,8 +308,10 @@ class LOCKABLE Mutex : public BaseMutex { pthread_mutex_t mutex_; Atomic exclusive_owner_; // Guarded by mutex_. Asynchronous reads are OK. #endif - const bool recursive_; // Can the lock be recursively held? + unsigned int recursion_count_; + const bool recursive_; // Can the lock be recursively held? + friend class ConditionVariable; DISALLOW_COPY_AND_ASSIGN(Mutex); }; -- GitLab From a3ad0cdd711857f04f477e2cdc5b56a2c74a3018 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 4 May 2018 10:06:38 +0100 Subject: [PATCH 365/749] ART: Move JNI files to runtime/jni/ . Test: Rely on TreeHugger. Change-Id: I9cae11191ef1567ae9453be498882a7767285140 --- adbconnection/adbconnection.cc | 4 ++-- benchmark/jobject-benchmark/jobject_benchmark.cc | 2 +- compiler/driver/compiler_driver.cc | 2 +- compiler/jni/jni_compiler_test.cc | 4 ++-- compiler/jni/quick/jni_compiler.cc | 2 +- compiler/optimizing/prepare_for_register_allocation.cc | 2 +- compiler/trampolines/trampoline_compiler.cc | 2 +- dex2oat/dex2oat.cc | 2 +- dex2oat/linker/image_writer.cc | 2 +- openjdkjvm/OpenjdkJvm.cc | 4 ++-- openjdkjvmti/OpenjdkJvmTi.cc | 2 +- openjdkjvmti/art_jvmti.h | 4 ++-- openjdkjvmti/deopt_manager.cc | 2 +- openjdkjvmti/events-inl.h | 2 +- openjdkjvmti/events.cc | 4 ++-- openjdkjvmti/jvmti_weak_table-inl.h | 2 +- openjdkjvmti/ti_breakpoint.cc | 2 +- openjdkjvmti/ti_class.cc | 4 ++-- openjdkjvmti/ti_class_loader-inl.h | 2 +- openjdkjvmti/ti_class_loader.cc | 2 +- openjdkjvmti/ti_class_loader.h | 2 +- openjdkjvmti/ti_field.cc | 2 +- openjdkjvmti/ti_heap.cc | 4 ++-- openjdkjvmti/ti_jni.cc | 4 ++-- openjdkjvmti/ti_method.cc | 2 +- openjdkjvmti/ti_redefine.cc | 2 +- openjdkjvmti/ti_redefine.h | 2 +- openjdkjvmti/ti_search.cc | 2 +- openjdkjvmti/ti_stack.cc | 4 ++-- openjdkjvmti/ti_thread.cc | 2 +- openjdkjvmti/ti_threadgroup.cc | 2 +- openjdkjvmti/transform.cc | 2 +- runtime/Android.bp | 10 +++++----- runtime/arch/stub_test.cc | 2 +- runtime/art_method.cc | 2 +- runtime/check_jni.cc | 4 ++-- runtime/class_linker.cc | 4 ++-- runtime/class_loader_context.cc | 2 +- runtime/class_loader_utils.h | 2 +- runtime/common_runtime_test.cc | 4 ++-- runtime/debugger.cc | 2 +- runtime/dex/dex_file_annotations.cc | 2 +- runtime/entrypoints/entrypoint_utils-inl.h | 2 +- runtime/entrypoints/entrypoint_utils.cc | 2 +- runtime/entrypoints/jni/jni_entrypoints.cc | 2 +- runtime/gc/collector/semi_space.cc | 2 +- runtime/gc/heap.cc | 2 +- runtime/gc/reference_processor.cc | 2 +- runtime/hidden_api_test.cc | 2 +- runtime/indirect_reference_table.cc | 4 ++-- runtime/instrumentation_test.cc | 2 +- runtime/jdwp/object_registry.cc | 2 +- runtime/jit/jit.cc | 2 +- runtime/{ => jni}/java_vm_ext.cc | 0 runtime/{ => jni}/java_vm_ext.h | 6 +++--- runtime/{ => jni}/java_vm_ext_test.cc | 2 +- runtime/{ => jni}/jni_env_ext-inl.h | 6 +++--- runtime/{ => jni}/jni_env_ext.cc | 0 runtime/{ => jni}/jni_env_ext.h | 6 +++--- runtime/{ => jni}/jni_internal.cc | 0 runtime/{ => jni}/jni_internal.h | 6 +++--- runtime/{ => jni}/jni_internal_test.cc | 0 runtime/mirror/method_handles_lookup.cc | 2 +- runtime/mirror/var_handle.cc | 2 +- runtime/native/dalvik_system_DexFile.cc | 2 +- runtime/native/dalvik_system_VMDebug.cc | 4 ++-- runtime/native/dalvik_system_VMRuntime.cc | 4 ++-- runtime/native/dalvik_system_VMStack.cc | 2 +- runtime/native/dalvik_system_ZygoteHooks.cc | 4 ++-- runtime/native/java_lang_Class.cc | 2 +- runtime/native/java_lang_Object.cc | 2 +- runtime/native/java_lang_String.cc | 2 +- runtime/native/java_lang_StringFactory.cc | 2 +- runtime/native/java_lang_System.cc | 2 +- runtime/native/java_lang_Thread.cc | 2 +- runtime/native/java_lang_Throwable.cc | 2 +- runtime/native/java_lang_VMClassLoader.cc | 2 +- runtime/native/java_lang_invoke_MethodHandleImpl.cc | 2 +- runtime/native/java_lang_ref_FinalizerReference.cc | 2 +- runtime/native/java_lang_ref_Reference.cc | 2 +- runtime/native/java_lang_reflect_Array.cc | 2 +- runtime/native/java_lang_reflect_Constructor.cc | 2 +- runtime/native/java_lang_reflect_Executable.cc | 2 +- runtime/native/java_lang_reflect_Field.cc | 2 +- runtime/native/java_lang_reflect_Method.cc | 2 +- runtime/native/java_lang_reflect_Parameter.cc | 2 +- runtime/native/java_lang_reflect_Proxy.cc | 2 +- .../native/java_util_concurrent_atomic_AtomicLong.cc | 2 +- runtime/native/libcore_util_CharsetUtils.cc | 2 +- .../native/org_apache_harmony_dalvik_ddmc_DdmServer.cc | 2 +- .../org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc | 2 +- runtime/native/sun_misc_Unsafe.cc | 2 +- runtime/native_bridge_art_interface.cc | 2 +- runtime/non_debuggable_classes.cc | 2 +- runtime/oat_file_manager.cc | 2 +- runtime/reflection.cc | 4 ++-- runtime/reflection_test.cc | 4 ++-- runtime/runtime.cc | 4 ++-- runtime/scoped_thread_state_change-inl.h | 2 +- runtime/scoped_thread_state_change.cc | 2 +- runtime/thread-inl.h | 2 +- runtime/thread.cc | 4 ++-- runtime/thread_list.cc | 2 +- runtime/ti/agent.cc | 2 +- runtime/well_known_classes.cc | 2 +- test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc | 4 ++-- test/1947-breakpoint-redefine-deopt/check_deopt.cc | 2 +- test/708-jit-cache-churn/jit.cc | 2 +- test/900-hello-plugin/load_unload.cc | 2 +- test/common/stack_inspect.cc | 2 +- 110 files changed, 139 insertions(+), 139 deletions(-) rename runtime/{ => jni}/java_vm_ext.cc (100%) rename runtime/{ => jni}/java_vm_ext.h (98%) rename runtime/{ => jni}/java_vm_ext_test.cc (99%) rename runtime/{ => jni}/jni_env_ext-inl.h (92%) rename runtime/{ => jni}/jni_env_ext.cc (100%) rename runtime/{ => jni}/jni_env_ext.h (98%) rename runtime/{ => jni}/jni_internal.cc (100%) rename runtime/{ => jni}/jni_internal.h (92%) rename runtime/{ => jni}/jni_internal_test.cc (100%) diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc index 4c2d4d72d8..8cd0d8bc9f 100644 --- a/adbconnection/adbconnection.cc +++ b/adbconnection/adbconnection.cc @@ -23,8 +23,8 @@ #include "base/logging.h" #include "base/macros.h" #include "base/mutex.h" -#include "java_vm_ext.h" -#include "jni_env_ext.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_env_ext.h" #include "mirror/throwable.h" #include "nativehelper/ScopedLocalRef.h" #include "runtime-inl.h" diff --git a/benchmark/jobject-benchmark/jobject_benchmark.cc b/benchmark/jobject-benchmark/jobject_benchmark.cc index 7e0a5362c9..2f38b78eaf 100644 --- a/benchmark/jobject-benchmark/jobject_benchmark.cc +++ b/benchmark/jobject-benchmark/jobject_benchmark.cc @@ -16,7 +16,7 @@ #include "jni.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "mirror/class-inl.h" #include "scoped_thread_state_change-inl.h" diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index fbbb4e960f..39ed825001 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -57,7 +57,7 @@ #include "gc/space/space.h" #include "handle_scope-inl.h" #include "intrinsics_enum.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "linker/linker_patch.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index 730a1a63e8..c643af787d 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -28,8 +28,8 @@ #include "dex/dex_file.h" #include "gtest/gtest.h" #include "indirect_reference_table.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index 8cb1998f7f..0902bf2bce 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -37,7 +37,7 @@ #include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "jni_env_ext.h" +#include "jni/jni_env_ext.h" #include "thread.h" #include "utils/arm/managed_register_arm.h" #include "utils/arm64/managed_register_arm64.h" diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index 59733397bf..831bccc90a 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -17,7 +17,7 @@ #include "prepare_for_register_allocation.h" #include "dex/dex_file_types.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "optimizing_compiler_stats.h" #include "well_known_classes.h" diff --git a/compiler/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc index 57360e74a3..26aa434c0d 100644 --- a/compiler/trampolines/trampoline_compiler.cc +++ b/compiler/trampolines/trampoline_compiler.cc @@ -18,7 +18,7 @@ #include "base/arena_allocator.h" #include "base/malloc_arena_pool.h" -#include "jni_env_ext.h" +#include "jni/jni_env_ext.h" #ifdef ART_ENABLE_CODEGEN_arm #include "utils/arm/assembler_arm_vixl.h" diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 3252354417..63518be15f 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -78,7 +78,7 @@ #include "gc/space/space-inl.h" #include "gc/verification.h" #include "interpreter/unstarted_runtime.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "linker/buffered_output_stream.h" #include "linker/elf_writer.h" #include "linker/elf_writer_quick.h" diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index bd1e6df93d..9e5cd8035c 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -52,7 +52,7 @@ #include "image.h" #include "imt_conflict_table.h" #include "subtype_check.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "linear_alloc.h" #include "lock_word.h" #include "mirror/array-inl.h" diff --git a/openjdkjvm/OpenjdkJvm.cc b/openjdkjvm/OpenjdkJvm.cc index 975d1948fe..be1ab7812a 100644 --- a/openjdkjvm/OpenjdkJvm.cc +++ b/openjdkjvm/OpenjdkJvm.cc @@ -48,8 +48,8 @@ #include "common_throws.h" #include "gc/heap.h" #include "handle_scope-inl.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "mirror/string-inl.h" #include "monitor.h" diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc index ef5151990c..59f61e2ee4 100644 --- a/openjdkjvmti/OpenjdkJvmTi.cc +++ b/openjdkjvmti/OpenjdkJvmTi.cc @@ -43,7 +43,7 @@ #include "base/logging.h" // For gLogVerbosity. #include "base/mutex.h" #include "events-inl.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "obj_ptr-inl.h" #include "object_tagging.h" #include "runtime.h" diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h index 73cc601e3e..82f3866c65 100644 --- a/openjdkjvmti/art_jvmti.h +++ b/openjdkjvmti/art_jvmti.h @@ -47,8 +47,8 @@ #include "base/strlcpy.h" #include "base/mutex.h" #include "events.h" -#include "java_vm_ext.h" -#include "jni_env_ext.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_env_ext.h" #include "jvmti.h" #include "ti_breakpoint.h" diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc index d4e5df1d67..2f24d7ea3d 100644 --- a/openjdkjvmti/deopt_manager.cc +++ b/openjdkjvmti/deopt_manager.cc @@ -41,7 +41,7 @@ #include "dex/modifiers.h" #include "events-inl.h" #include "jit/jit.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object_array-inl.h" #include "nativehelper/scoped_local_ref.h" diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h index 74ffb84579..6a8ba48109 100644 --- a/openjdkjvmti/events-inl.h +++ b/openjdkjvmti/events-inl.h @@ -23,7 +23,7 @@ #include "base/mutex-inl.h" #include "events.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "nativehelper/scoped_local_ref.h" #include "scoped_thread_state_change-inl.h" #include "ti_breakpoint.h" diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index de678711fc..5cb4299293 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -44,8 +44,8 @@ #include "gc/scoped_gc_critical_section.h" #include "handle_scope-inl.h" #include "instrumentation.h" -#include "jni_env_ext-inl.h" -#include "jni_internal.h" +#include "jni/jni_env_ext-inl.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/object-inl.h" #include "monitor.h" diff --git a/openjdkjvmti/jvmti_weak_table-inl.h b/openjdkjvmti/jvmti_weak_table-inl.h index 699004298e..d9b8a84e55 100644 --- a/openjdkjvmti/jvmti_weak_table-inl.h +++ b/openjdkjvmti/jvmti_weak_table-inl.h @@ -41,7 +41,7 @@ #include "art_jvmti.h" #include "gc/allocation_listener.h" #include "instrumentation.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "jvmti_allocator.h" #include "mirror/class.h" #include "mirror/object.h" diff --git a/openjdkjvmti/ti_breakpoint.cc b/openjdkjvmti/ti_breakpoint.cc index 136b1d3e1a..813aa8eb83 100644 --- a/openjdkjvmti/ti_breakpoint.cc +++ b/openjdkjvmti/ti_breakpoint.cc @@ -41,7 +41,7 @@ #include "dex/dex_file_annotations.h" #include "dex/modifiers.h" #include "events-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object_array-inl.h" #include "nativehelper/scoped_local_ref.h" diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc index 261fe3e810..c9d71b4857 100644 --- a/openjdkjvmti/ti_class.cc +++ b/openjdkjvmti/ti_class.cc @@ -54,8 +54,8 @@ #include "gc/heap.h" #include "gc_root.h" #include "handle.h" -#include "jni_env_ext-inl.h" -#include "jni_internal.h" +#include "jni/jni_env_ext-inl.h" +#include "jni/jni_internal.h" #include "mirror/array-inl.h" #include "mirror/class-inl.h" #include "mirror/class_ext.h" diff --git a/openjdkjvmti/ti_class_loader-inl.h b/openjdkjvmti/ti_class_loader-inl.h index 95278f4b2d..9b04841eb3 100644 --- a/openjdkjvmti/ti_class_loader-inl.h +++ b/openjdkjvmti/ti_class_loader-inl.h @@ -36,7 +36,7 @@ #include "art_field-inl.h" #include "handle.h" #include "handle_scope.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object.h" #include "mirror/object_array-inl.h" #include "well_known_classes.h" diff --git a/openjdkjvmti/ti_class_loader.cc b/openjdkjvmti/ti_class_loader.cc index 3df5de909d..9a32849ed0 100644 --- a/openjdkjvmti/ti_class_loader.cc +++ b/openjdkjvmti/ti_class_loader.cc @@ -46,7 +46,7 @@ #include "instrumentation.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "jvmti_allocator.h" #include "mirror/class.h" #include "mirror/class_ext.h" diff --git a/openjdkjvmti/ti_class_loader.h b/openjdkjvmti/ti_class_loader.h index 142e2e1588..a3857e595a 100644 --- a/openjdkjvmti/ti_class_loader.h +++ b/openjdkjvmti/ti_class_loader.h @@ -45,7 +45,7 @@ #include "dex/dex_file.h" #include "dex/utf.h" #include "gc_root-inl.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "jvmti.h" #include "linear_alloc.h" #include "mirror/array-inl.h" diff --git a/openjdkjvmti/ti_field.cc b/openjdkjvmti/ti_field.cc index c016966d21..328e2a1e40 100644 --- a/openjdkjvmti/ti_field.cc +++ b/openjdkjvmti/ti_field.cc @@ -36,7 +36,7 @@ #include "base/enums.h" #include "dex/dex_file_annotations.h" #include "dex/modifiers.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object_array-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" diff --git a/openjdkjvmti/ti_heap.cc b/openjdkjvmti/ti_heap.cc index d0a7cf0657..d23370bc5c 100644 --- a/openjdkjvmti/ti_heap.cc +++ b/openjdkjvmti/ti_heap.cc @@ -26,8 +26,8 @@ #include "gc/heap.h" #include "gc_root-inl.h" #include "java_frame_root_info.h" -#include "jni_env_ext.h" -#include "jni_internal.h" +#include "jni/jni_env_ext.h" +#include "jni/jni_internal.h" #include "jvmti_weak_table-inl.h" #include "mirror/class.h" #include "mirror/object-inl.h" diff --git a/openjdkjvmti/ti_jni.cc b/openjdkjvmti/ti_jni.cc index dd2dda118a..b655d6a8e1 100644 --- a/openjdkjvmti/ti_jni.cc +++ b/openjdkjvmti/ti_jni.cc @@ -35,8 +35,8 @@ #include "art_jvmti.h" #include "base/mutex.h" -#include "java_vm_ext.h" -#include "jni_env_ext.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_env_ext.h" #include "runtime.h" #include "thread-current-inl.h" diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index b83310dc85..c0c312c490 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -44,7 +44,7 @@ #include "events-inl.h" #include "gc_root-inl.h" #include "jit/jit.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index a23baa5095..8a726bca14 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -58,7 +58,7 @@ #include "jdwp/object_registry.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "jvmti_allocator.h" #include "mirror/class-inl.h" #include "mirror/class_ext.h" diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h index e14b7ae1c8..227eacd180 100644 --- a/openjdkjvmti/ti_redefine.h +++ b/openjdkjvmti/ti_redefine.h @@ -45,7 +45,7 @@ #include "dex/dex_file.h" #include "dex/utf.h" #include "gc_root-inl.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "jvmti.h" #include "linear_alloc.h" #include "mirror/array-inl.h" diff --git a/openjdkjvmti/ti_search.cc b/openjdkjvmti/ti_search.cc index cbb7b53bff..bcbab14cdd 100644 --- a/openjdkjvmti/ti_search.cc +++ b/openjdkjvmti/ti_search.cc @@ -41,7 +41,7 @@ #include "dex/art_dex_file_loader.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object.h" #include "mirror/string.h" diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc index 4526be4cbe..eee8108b01 100644 --- a/openjdkjvmti/ti_stack.cc +++ b/openjdkjvmti/ti_stack.cc @@ -50,8 +50,8 @@ #include "dex/dex_file_types.h" #include "gc_root.h" #include "handle_scope-inl.h" -#include "jni_env_ext.h" -#include "jni_internal.h" +#include "jni/jni_env_ext.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/dex_cache.h" #include "nativehelper/scoped_local_ref.h" diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc index 414139c7b4..cabf9e8b09 100644 --- a/openjdkjvmti/ti_thread.cc +++ b/openjdkjvmti/ti_thread.cc @@ -43,7 +43,7 @@ #include "gc/gc_cause.h" #include "gc/scoped_gc_critical_section.h" #include "gc_root-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/object-inl.h" #include "mirror/string.h" diff --git a/openjdkjvmti/ti_threadgroup.cc b/openjdkjvmti/ti_threadgroup.cc index c0597ad0cc..e17e61fe62 100644 --- a/openjdkjvmti/ti_threadgroup.cc +++ b/openjdkjvmti/ti_threadgroup.cc @@ -37,7 +37,7 @@ #include "base/macros.h" #include "base/mutex.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/object-inl.h" #include "mirror/string.h" diff --git a/openjdkjvmti/transform.cc b/openjdkjvmti/transform.cc index 29a9b10ca2..8797553b07 100644 --- a/openjdkjvmti/transform.cc +++ b/openjdkjvmti/transform.cc @@ -48,7 +48,7 @@ #include "events-inl.h" #include "fault_handler.h" #include "gc_root-inl.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "jvalue.h" #include "jvmti.h" #include "linear_alloc.h" diff --git a/runtime/Android.bp b/runtime/Android.bp index 1ef5bf080f..05e923949e 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -100,7 +100,6 @@ cc_defaults { "interpreter/shadow_frame.cc", "interpreter/unstarted_runtime.cc", "java_frame_root_info.cc", - "java_vm_ext.cc", "jdwp/jdwp_event.cc", "jdwp/jdwp_expand_buf.cc", "jdwp/jdwp_handler.cc", @@ -108,13 +107,14 @@ cc_defaults { "jdwp/jdwp_request.cc", "jdwp/jdwp_socket.cc", "jdwp/object_registry.cc", - "jni_env_ext.cc", "jit/debugger_interface.cc", "jit/jit.cc", "jit/jit_code_cache.cc", "jit/profiling_info.cc", "jit/profile_saver.cc", - "jni_internal.cc", + "jni/java_vm_ext.cc", + "jni/jni_env_ext.cc", + "jni/jni_internal.cc", "jobject_comparator.cc", "linear_alloc.cc", "managed_stack.cc", @@ -581,7 +581,7 @@ art_cc_test { "interpreter/safe_math_test.cc", "interpreter/unstarted_runtime_test.cc", "jdwp/jdwp_options_test.cc", - "java_vm_ext_test.cc", + "jni/java_vm_ext_test.cc", "method_handles_test.cc", "mirror/dex_cache_test.cc", "mirror/method_type_test.cc", @@ -621,7 +621,7 @@ art_cc_test { "art_gtest_defaults", ], srcs: [ - "jni_internal_test.cc", + "jni/jni_internal_test.cc", "proxy_test.cc", "reflection_test.cc", ], diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index 4be4b12611..78516e3aeb 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -24,7 +24,7 @@ #include "common_runtime_test.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "imt_conflict_table.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "linear_alloc.h" #include "mirror/class-inl.h" #include "mirror/string-inl.h" diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 41b01c251b..87fcb20698 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -35,7 +35,7 @@ #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "jit/profiling_info.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_ext.h" #include "mirror/executable.h" diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index 9a43790575..f8b977eea7 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -34,8 +34,8 @@ #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "gc/space/space.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/field.h" #include "mirror/method.h" diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 44445aea9b..4141a37366 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -79,11 +79,11 @@ #include "imtable-inl.h" #include "intern_table.h" #include "interpreter/interpreter.h" -#include "java_vm_ext.h" #include "jit/debugger_interface.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "linear_alloc.h" #include "mirror/call_site.h" #include "mirror/class-inl.h" diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index 4afc44cb91..98174142f1 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -25,7 +25,7 @@ #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "oat_file_assistant.h" #include "obj_ptr-inl.h" #include "runtime.h" diff --git a/runtime/class_loader_utils.h b/runtime/class_loader_utils.h index 1439f11636..af42878e97 100644 --- a/runtime/class_loader_utils.h +++ b/runtime/class_loader_utils.h @@ -20,7 +20,7 @@ #include "art_field-inl.h" #include "base/mutex.h" #include "handle_scope.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "native/dalvik_system_DexFile.h" #include "scoped_thread_state_change-inl.h" diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index f5b15ec239..75b091d98f 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -48,8 +48,8 @@ #include "gtest/gtest.h" #include "handle_scope-inl.h" #include "interpreter/unstarted_runtime.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "native/dalvik_system_DexFile.h" diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 28659cb11d..88628bbc50 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -51,7 +51,7 @@ #include "handle_scope-inl.h" #include "jdwp/jdwp_priv.h" #include "jdwp/object_registry.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "jvalue-inl.h" #include "mirror/class-inl.h" #include "mirror/class.h" diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc index 6f3354b724..c399b1c7fe 100644 --- a/runtime/dex/dex_file_annotations.cc +++ b/runtime/dex/dex_file_annotations.cc @@ -24,7 +24,7 @@ #include "art_method-inl.h" #include "class_linker-inl.h" #include "dex/dex_file-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "jvalue-inl.h" #include "mirror/field.h" #include "mirror/method.h" diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 137eb4fe1e..d4e7492f00 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -31,7 +31,7 @@ #include "imt_conflict_table.h" #include "imtable-inl.h" #include "indirect_reference_table.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/array.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index ffa138d5b1..246c703e93 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -26,7 +26,7 @@ #include "entrypoints/quick/callee_save_frame.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/card_table-inl.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "mirror/class-inl.h" #include "mirror/method.h" #include "mirror/object-inl.h" diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc index 780e221129..a4083a4f81 100644 --- a/runtime/entrypoints/jni/jni_entrypoints.cc +++ b/runtime/entrypoints/jni/jni_entrypoints.cc @@ -18,7 +18,7 @@ #include "art_method-inl.h" #include "entrypoints/entrypoint_utils.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "mirror/object-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread.h" diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index 1e136bca2e..681ac2ef28 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -39,7 +39,7 @@ #include "gc/space/space-inl.h" #include "indirect_reference_table.h" #include "intern_table.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mark_sweep-inl.h" #include "mirror/object-inl.h" #include "mirror/object-refvisitor-inl.h" diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index e85824de70..b004566ed1 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -71,9 +71,9 @@ #include "heap-visit-objects-inl.h" #include "image.h" #include "intern_table.h" -#include "java_vm_ext.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" +#include "jni/java_vm_ext.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object-refvisitor-inl.h" diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc index 356f3ecaa8..5be7b325d0 100644 --- a/runtime/gc/reference_processor.cc +++ b/runtime/gc/reference_processor.cc @@ -19,7 +19,7 @@ #include "base/time_utils.h" #include "base/utils.h" #include "collector/garbage_collector.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/reference-inl.h" diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc index 68d4eecbb9..ab0c2901ff 100644 --- a/runtime/hidden_api_test.cc +++ b/runtime/hidden_api_test.cc @@ -17,7 +17,7 @@ #include "hidden_api.h" #include "common_runtime_test.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "proxy_test.h" namespace art { diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc index 6143ba6fd4..950a54d61e 100644 --- a/runtime/indirect_reference_table.cc +++ b/runtime/indirect_reference_table.cc @@ -19,8 +19,8 @@ #include "base/mutator_locked_dumpable.h" #include "base/systrace.h" #include "base/utils.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "nth_caller_visitor.h" #include "reference_table.h" #include "runtime.h" diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc index 836bbe711f..3171eeb861 100644 --- a/runtime/instrumentation_test.cc +++ b/runtime/instrumentation_test.cc @@ -24,7 +24,7 @@ #include "dex/dex_file.h" #include "gc/scoped_gc_critical_section.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "jvalue.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc index 510f5f00a6..df1eb2b561 100644 --- a/runtime/jdwp/object_registry.cc +++ b/runtime/jdwp/object_registry.cc @@ -17,7 +17,7 @@ #include "object_registry.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/throwable.h" #include "obj_ptr-inl.h" diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 2c0fbadc1d..d4fe97784d 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -27,8 +27,8 @@ #include "debugger.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "interpreter/interpreter.h" -#include "java_vm_ext.h" #include "jit_code_cache.h" +#include "jni/java_vm_ext.h" #include "mirror/method_handle_impl.h" #include "mirror/var_handle.h" #include "oat_file_manager.h" diff --git a/runtime/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc similarity index 100% rename from runtime/java_vm_ext.cc rename to runtime/jni/java_vm_ext.cc diff --git a/runtime/java_vm_ext.h b/runtime/jni/java_vm_ext.h similarity index 98% rename from runtime/java_vm_ext.h rename to runtime/jni/java_vm_ext.h index ac20afecd4..408d3542ed 100644 --- a/runtime/java_vm_ext.h +++ b/runtime/jni/java_vm_ext.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_JAVA_VM_EXT_H_ -#define ART_RUNTIME_JAVA_VM_EXT_H_ +#ifndef ART_RUNTIME_JNI_JAVA_VM_EXT_H_ +#define ART_RUNTIME_JNI_JAVA_VM_EXT_H_ #include "jni.h" @@ -262,4 +262,4 @@ class JavaVMExt : public JavaVM { } // namespace art -#endif // ART_RUNTIME_JAVA_VM_EXT_H_ +#endif // ART_RUNTIME_JNI_JAVA_VM_EXT_H_ diff --git a/runtime/java_vm_ext_test.cc b/runtime/jni/java_vm_ext_test.cc similarity index 99% rename from runtime/java_vm_ext_test.cc rename to runtime/jni/java_vm_ext_test.cc index a15ec56274..74e4a30905 100644 --- a/runtime/java_vm_ext_test.cc +++ b/runtime/jni/java_vm_ext_test.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "jni_internal.h" +#include "jni/jni_internal.h" #include diff --git a/runtime/jni_env_ext-inl.h b/runtime/jni/jni_env_ext-inl.h similarity index 92% rename from runtime/jni_env_ext-inl.h rename to runtime/jni/jni_env_ext-inl.h index 14f708b18d..7609a9e01a 100644 --- a/runtime/jni_env_ext-inl.h +++ b/runtime/jni/jni_env_ext-inl.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_JNI_ENV_EXT_INL_H_ -#define ART_RUNTIME_JNI_ENV_EXT_INL_H_ +#ifndef ART_RUNTIME_JNI_JNI_ENV_EXT_INL_H_ +#define ART_RUNTIME_JNI_JNI_ENV_EXT_INL_H_ #include "jni_env_ext.h" @@ -51,4 +51,4 @@ inline T JNIEnvExt::AddLocalReference(ObjPtr obj) { } // namespace art -#endif // ART_RUNTIME_JNI_ENV_EXT_INL_H_ +#endif // ART_RUNTIME_JNI_JNI_ENV_EXT_INL_H_ diff --git a/runtime/jni_env_ext.cc b/runtime/jni/jni_env_ext.cc similarity index 100% rename from runtime/jni_env_ext.cc rename to runtime/jni/jni_env_ext.cc diff --git a/runtime/jni_env_ext.h b/runtime/jni/jni_env_ext.h similarity index 98% rename from runtime/jni_env_ext.h rename to runtime/jni/jni_env_ext.h index 291ac48e86..3a007adcf0 100644 --- a/runtime/jni_env_ext.h +++ b/runtime/jni/jni_env_ext.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_JNI_ENV_EXT_H_ -#define ART_RUNTIME_JNI_ENV_EXT_H_ +#ifndef ART_RUNTIME_JNI_JNI_ENV_EXT_H_ +#define ART_RUNTIME_JNI_JNI_ENV_EXT_H_ #include @@ -229,4 +229,4 @@ class ScopedJniEnvLocalRefState { } // namespace art -#endif // ART_RUNTIME_JNI_ENV_EXT_H_ +#endif // ART_RUNTIME_JNI_JNI_ENV_EXT_H_ diff --git a/runtime/jni_internal.cc b/runtime/jni/jni_internal.cc similarity index 100% rename from runtime/jni_internal.cc rename to runtime/jni/jni_internal.cc diff --git a/runtime/jni_internal.h b/runtime/jni/jni_internal.h similarity index 92% rename from runtime/jni_internal.h rename to runtime/jni/jni_internal.h index 2c90b3ba78..d0426617eb 100644 --- a/runtime/jni_internal.h +++ b/runtime/jni/jni_internal.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_JNI_INTERNAL_H_ -#define ART_RUNTIME_JNI_INTERNAL_H_ +#ifndef ART_RUNTIME_JNI_JNI_INTERNAL_H_ +#define ART_RUNTIME_JNI_JNI_INTERNAL_H_ #include #include @@ -59,4 +59,4 @@ static inline ArtMethod* DecodeArtMethod(jmethodID method_id) { std::ostream& operator<<(std::ostream& os, const jobjectRefType& rhs); -#endif // ART_RUNTIME_JNI_INTERNAL_H_ +#endif // ART_RUNTIME_JNI_JNI_INTERNAL_H_ diff --git a/runtime/jni_internal_test.cc b/runtime/jni/jni_internal_test.cc similarity index 100% rename from runtime/jni_internal_test.cc rename to runtime/jni/jni_internal_test.cc diff --git a/runtime/mirror/method_handles_lookup.cc b/runtime/mirror/method_handles_lookup.cc index 039bbf2932..aeecf75c1f 100644 --- a/runtime/mirror/method_handles_lookup.cc +++ b/runtime/mirror/method_handles_lookup.cc @@ -20,7 +20,7 @@ #include "dex/modifiers.h" #include "gc_root-inl.h" #include "handle_scope.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/method_handle_impl.h" #include "object-inl.h" #include "well_known_classes.h" diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc index a79c0a26d0..b309f596fd 100644 --- a/runtime/mirror/var_handle.cc +++ b/runtime/mirror/var_handle.cc @@ -22,7 +22,7 @@ #include "class_linker.h" #include "gc_root-inl.h" #include "intrinsics_enum.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "jvalue-inl.h" #include "method_handles.h" #include "method_type.h" diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 8320d9c7ba..5c2ca24da9 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -35,7 +35,7 @@ #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "jit/debugger_interface.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" #include "mirror/string.h" diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 3692a308d8..6aaafc2864 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -35,8 +35,8 @@ #include "gc/space/zygote_space.h" #include "handle_scope-inl.h" #include "hprof/hprof.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/object_array-inl.h" #include "native_util.h" diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index e88ff09430..7f7b524227 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -41,8 +41,8 @@ extern "C" void android_set_application_target_sdk_version(uint32_t version); #include "gc/space/image_space.h" #include "gc/task_processor.h" #include "intern_table.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/dex_cache-inl.h" #include "mirror/object-inl.h" diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc index ed0eb97da1..39192274ad 100644 --- a/runtime/native/dalvik_system_VMStack.cc +++ b/runtime/native/dalvik_system_VMStack.cc @@ -22,7 +22,7 @@ #include "art_method-inl.h" #include "gc/task_processor.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index 84c7926a11..e105bab872 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -28,9 +28,9 @@ #include "base/runtime_debug.h" #include "debugger.h" #include "hidden_api.h" -#include "java_vm_ext.h" #include "jit/jit.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "native_util.h" #include "nativehelper/jni_macros.h" #include "nativehelper/scoped_utf_chars.h" diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 2c1c963ed6..2625c0a316 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -28,7 +28,7 @@ #include "dex/dex_file_annotations.h" #include "dex/utf.h" #include "hidden_api.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/field-inl.h" diff --git a/runtime/native/java_lang_Object.cc b/runtime/native/java_lang_Object.cc index d52bf0490b..208ccf6a82 100644 --- a/runtime/native/java_lang_Object.cc +++ b/runtime/native/java_lang_Object.cc @@ -18,7 +18,7 @@ #include "nativehelper/jni_macros.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object-inl.h" #include "native_util.h" #include "scoped_fast_native_object_access-inl.h" diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc index b5aea7ca7c..8976058b53 100644 --- a/runtime/native/java_lang_String.cc +++ b/runtime/native/java_lang_String.cc @@ -19,7 +19,7 @@ #include "nativehelper/jni_macros.h" #include "common_throws.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/array.h" #include "mirror/object-inl.h" #include "mirror/string-inl.h" diff --git a/runtime/native/java_lang_StringFactory.cc b/runtime/native/java_lang_StringFactory.cc index 136a02f8f6..07e875efcb 100644 --- a/runtime/native/java_lang_StringFactory.cc +++ b/runtime/native/java_lang_StringFactory.cc @@ -17,7 +17,7 @@ #include "java_lang_StringFactory.h" #include "common_throws.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object-inl.h" #include "mirror/string.h" #include "native_util.h" diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc index 390f026588..2c4184c285 100644 --- a/runtime/native/java_lang_System.cc +++ b/runtime/native/java_lang_System.cc @@ -20,7 +20,7 @@ #include "common_throws.h" #include "gc/accounting/card_table-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/array.h" #include "mirror/class-inl.h" #include "mirror/class.h" diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc index 9a52f7002b..9edb0c21dd 100644 --- a/runtime/native/java_lang_Thread.cc +++ b/runtime/native/java_lang_Thread.cc @@ -17,7 +17,7 @@ #include "java_lang_Thread.h" #include "common_throws.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object.h" #include "monitor.h" #include "native_util.h" diff --git a/runtime/native/java_lang_Throwable.cc b/runtime/native/java_lang_Throwable.cc index 03b7f9dfba..b5ef7d807b 100644 --- a/runtime/native/java_lang_Throwable.cc +++ b/runtime/native/java_lang_Throwable.cc @@ -18,7 +18,7 @@ #include "nativehelper/jni_macros.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "native_util.h" #include "scoped_fast_native_object_access-inl.h" #include "thread.h" diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc index 44585fc453..0630737d29 100644 --- a/runtime/native/java_lang_VMClassLoader.cc +++ b/runtime/native/java_lang_VMClassLoader.cc @@ -20,7 +20,7 @@ #include "class_linker.h" #include "dex/descriptors_names.h" #include "dex/dex_file_loader.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" #include "native_util.h" diff --git a/runtime/native/java_lang_invoke_MethodHandleImpl.cc b/runtime/native/java_lang_invoke_MethodHandleImpl.cc index 2e3b4d41ef..1f2bf09f0e 100644 --- a/runtime/native/java_lang_invoke_MethodHandleImpl.cc +++ b/runtime/native/java_lang_invoke_MethodHandleImpl.cc @@ -20,7 +20,7 @@ #include "art_method.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/field.h" #include "mirror/method.h" #include "mirror/method_handle_impl.h" diff --git a/runtime/native/java_lang_ref_FinalizerReference.cc b/runtime/native/java_lang_ref_FinalizerReference.cc index 72af5f7ea7..c89188c99c 100644 --- a/runtime/native/java_lang_ref_FinalizerReference.cc +++ b/runtime/native/java_lang_ref_FinalizerReference.cc @@ -20,7 +20,7 @@ #include "gc/heap.h" #include "gc/reference_processor.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object-inl.h" #include "mirror/reference-inl.h" #include "native_util.h" diff --git a/runtime/native/java_lang_ref_Reference.cc b/runtime/native/java_lang_ref_Reference.cc index 524a18ca20..fc018d15c4 100644 --- a/runtime/native/java_lang_ref_Reference.cc +++ b/runtime/native/java_lang_ref_Reference.cc @@ -20,7 +20,7 @@ #include "gc/heap.h" #include "gc/reference_processor.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object-inl.h" #include "mirror/reference-inl.h" #include "native_util.h" diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc index d28f74158e..8bcda10f2a 100644 --- a/runtime/native/java_lang_reflect_Array.cc +++ b/runtime/native/java_lang_reflect_Array.cc @@ -22,7 +22,7 @@ #include "common_throws.h" #include "dex/dex_file-inl.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "native_util.h" diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc index 86124388bc..a5d6c9704d 100644 --- a/runtime/native/java_lang_reflect_Constructor.cc +++ b/runtime/native/java_lang_reflect_Constructor.cc @@ -23,7 +23,7 @@ #include "class_linker-inl.h" #include "class_linker.h" #include "dex/dex_file_annotations.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/method.h" #include "mirror/object-inl.h" diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc index b129c66759..9a2d3020c0 100644 --- a/runtime/native/java_lang_reflect_Executable.cc +++ b/runtime/native/java_lang_reflect_Executable.cc @@ -22,7 +22,7 @@ #include "art_method-inl.h" #include "dex/dex_file_annotations.h" #include "handle.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/method.h" #include "mirror/object-inl.h" diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 13275d92e4..e0afbee845 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -26,7 +26,7 @@ #include "common_throws.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_annotations.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/field-inl.h" #include "native_util.h" diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc index 4355c06acd..2503b3cb44 100644 --- a/runtime/native/java_lang_reflect_Method.cc +++ b/runtime/native/java_lang_reflect_Method.cc @@ -23,7 +23,7 @@ #include "class_linker-inl.h" #include "class_linker.h" #include "dex/dex_file_annotations.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" diff --git a/runtime/native/java_lang_reflect_Parameter.cc b/runtime/native/java_lang_reflect_Parameter.cc index b80b20cd8d..263a56796f 100644 --- a/runtime/native/java_lang_reflect_Parameter.cc +++ b/runtime/native/java_lang_reflect_Parameter.cc @@ -24,7 +24,7 @@ #include "common_throws.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_annotations.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "native_util.h" #include "scoped_fast_native_object_access-inl.h" diff --git a/runtime/native/java_lang_reflect_Proxy.cc b/runtime/native/java_lang_reflect_Proxy.cc index 691ed28b0b..f723ed223d 100644 --- a/runtime/native/java_lang_reflect_Proxy.cc +++ b/runtime/native/java_lang_reflect_Proxy.cc @@ -19,7 +19,7 @@ #include "nativehelper/jni_macros.h" #include "class_linker.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "mirror/object_array.h" #include "mirror/string.h" diff --git a/runtime/native/java_util_concurrent_atomic_AtomicLong.cc b/runtime/native/java_util_concurrent_atomic_AtomicLong.cc index c0032975ce..fa288edcb8 100644 --- a/runtime/native/java_util_concurrent_atomic_AtomicLong.cc +++ b/runtime/native/java_util_concurrent_atomic_AtomicLong.cc @@ -21,7 +21,7 @@ #include "arch/instruction_set.h" #include "base/atomic.h" #include "base/quasi_atomic.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "native_util.h" namespace art { diff --git a/runtime/native/libcore_util_CharsetUtils.cc b/runtime/native/libcore_util_CharsetUtils.cc index f3aba2575b..24298049ee 100644 --- a/runtime/native/libcore_util_CharsetUtils.cc +++ b/runtime/native/libcore_util_CharsetUtils.cc @@ -18,7 +18,7 @@ #include -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/string-inl.h" #include "mirror/string.h" #include "native_util.h" diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc index 8f8fd71727..419aed8578 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc @@ -20,7 +20,7 @@ #include "base/array_ref.h" #include "debugger.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "native_util.h" #include "nativehelper/jni_macros.h" #include "nativehelper/scoped_primitive_array.h" diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc index fbee7b31a3..028675d448 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc @@ -22,7 +22,7 @@ #include "base/mutex.h" #include "debugger.h" #include "gc/heap.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "native_util.h" #include "nativehelper/jni_macros.h" #include "nativehelper/scoped_local_ref.h" diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc index fb00ae3967..d41a19556e 100644 --- a/runtime/native/sun_misc_Unsafe.cc +++ b/runtime/native/sun_misc_Unsafe.cc @@ -27,7 +27,7 @@ #include "base/quasi_atomic.h" #include "common_throws.h" #include "gc/accounting/card_table-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/array.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc index 7d72805dc6..def48e8be9 100644 --- a/runtime/native_bridge_art_interface.cc +++ b/runtime/native_bridge_art_interface.cc @@ -25,7 +25,7 @@ #include "base/logging.h" // For VLOG. #include "base/macros.h" #include "dex/dex_file-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "scoped_thread_state_change-inl.h" #include "sigchain.h" diff --git a/runtime/non_debuggable_classes.cc b/runtime/non_debuggable_classes.cc index 8484e2cde7..f42a2d6755 100644 --- a/runtime/non_debuggable_classes.cc +++ b/runtime/non_debuggable_classes.cc @@ -16,7 +16,7 @@ #include "non_debuggable_classes.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "nativehelper/scoped_local_ref.h" #include "obj_ptr-inl.h" diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index f6fb9ded87..16e6cf375c 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -38,7 +38,7 @@ #include "gc/scoped_gc_critical_section.h" #include "gc/space/image_space.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" #include "oat_file.h" diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 068bc285e5..dfa4b3daab 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -23,8 +23,8 @@ #include "common_throws.h" #include "dex/dex_file-inl.h" #include "indirect_reference_table-inl.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/executable.h" #include "mirror/object_array-inl.h" diff --git a/runtime/reflection_test.cc b/runtime/reflection_test.cc index 7b36c7368e..d2d720f722 100644 --- a/runtime/reflection_test.cc +++ b/runtime/reflection_test.cc @@ -23,8 +23,8 @@ #include "base/enums.h" #include "common_compiler_test.h" #include "dex/descriptors_names.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "nativehelper/scoped_local_ref.h" #include "scoped_thread_state_change-inl.h" diff --git a/runtime/runtime.cc b/runtime/runtime.cc index e2e315cbba..f86b7a054e 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -93,11 +93,11 @@ #include "instrumentation.h" #include "intern_table.h" #include "interpreter/interpreter.h" -#include "java_vm_ext.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "jit/profile_saver.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "linear_alloc.h" #include "memory_representation.h" #include "mirror/array.h" diff --git a/runtime/scoped_thread_state_change-inl.h b/runtime/scoped_thread_state_change-inl.h index f95593209b..3089c24c59 100644 --- a/runtime/scoped_thread_state_change-inl.h +++ b/runtime/scoped_thread_state_change-inl.h @@ -22,7 +22,7 @@ #include #include "base/casts.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "obj_ptr-inl.h" #include "runtime.h" #include "thread-inl.h" diff --git a/runtime/scoped_thread_state_change.cc b/runtime/scoped_thread_state_change.cc index 6a86cc6411..edbce05325 100644 --- a/runtime/scoped_thread_state_change.cc +++ b/runtime/scoped_thread_state_change.cc @@ -19,7 +19,7 @@ #include #include "base/casts.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "obj_ptr-inl.h" #include "runtime-inl.h" diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index e34f32e0bf..91c27af407 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -23,7 +23,7 @@ #include "base/casts.h" #include "base/mutex-inl.h" #include "base/time_utils.h" -#include "jni_env_ext.h" +#include "jni/jni_env_ext.h" #include "managed_stack-inl.h" #include "obj_ptr.h" #include "thread-current-inl.h" diff --git a/runtime/thread.cc b/runtime/thread.cc index f6ac64f7bd..eada24d257 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -65,8 +65,8 @@ #include "interpreter/interpreter.h" #include "interpreter/shadow_frame.h" #include "java_frame_root_info.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/object_array-inl.h" diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 44af867d60..b2be549996 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -40,7 +40,7 @@ #include "gc/heap.h" #include "gc/reference_processor.h" #include "gc_root.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "lock_word.h" #include "monitor.h" #include "native_stack_dump.h" diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc index 15c514e593..608f0ee13c 100644 --- a/runtime/ti/agent.cc +++ b/runtime/ti/agent.cc @@ -21,7 +21,7 @@ #include "nativeloader/native_loader.h" #include "base/strlcpy.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "runtime.h" #include "thread-current-inl.h" #include "scoped_thread_state_change-inl.h" diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index f5d112c30b..b79334ac7f 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -25,7 +25,7 @@ #include "entrypoints/quick/quick_entrypoints_enum.h" #include "hidden_api.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/throwable.h" #include "nativehelper/scoped_local_ref.h" diff --git a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc index f01b82553d..d9ade931d2 100644 --- a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc +++ b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc @@ -19,8 +19,8 @@ #include "base/casts.h" #include "base/macros.h" -#include "java_vm_ext.h" -#include "jni_env_ext.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_env_ext.h" #include "thread-current-inl.h" namespace art { diff --git a/test/1947-breakpoint-redefine-deopt/check_deopt.cc b/test/1947-breakpoint-redefine-deopt/check_deopt.cc index b40b201c9c..667d8be684 100644 --- a/test/1947-breakpoint-redefine-deopt/check_deopt.cc +++ b/test/1947-breakpoint-redefine-deopt/check_deopt.cc @@ -16,7 +16,7 @@ #include "jni.h" #include "art_method-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "instrumentation.h" #include "scoped_thread_state_change-inl.h" diff --git a/test/708-jit-cache-churn/jit.cc b/test/708-jit-cache-churn/jit.cc index 1284a8703d..1b80eb3c0c 100644 --- a/test/708-jit-cache-churn/jit.cc +++ b/test/708-jit-cache-churn/jit.cc @@ -19,7 +19,7 @@ #include "art_method.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" diff --git a/test/900-hello-plugin/load_unload.cc b/test/900-hello-plugin/load_unload.cc index cab0abf58b..7121d108a4 100644 --- a/test/900-hello-plugin/load_unload.cc +++ b/test/900-hello-plugin/load_unload.cc @@ -21,7 +21,7 @@ #include #include "art_method-inl.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "runtime.h" namespace art { diff --git a/test/common/stack_inspect.cc b/test/common/stack_inspect.cc index fd6273769b..192274e5ae 100644 --- a/test/common/stack_inspect.cc +++ b/test/common/stack_inspect.cc @@ -20,7 +20,7 @@ #include "base/mutex.h" #include "dex/dex_file-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "nth_caller_visitor.h" #include "oat_file.h" -- GitLab From 96e754c9389dfe47bc4e8d174b78e9e806533bf8 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 4 May 2018 10:52:40 +0100 Subject: [PATCH 366/749] ART: Remove unused JobjectComparator. Unused since https://android-review.googlesource.com/144043 . Test: Rely on TreeHugger. Change-Id: I0cf004e66ba2698bb3dc45804d71f9d5ee3647fd --- runtime/Android.bp | 1 - runtime/jobject_comparator.cc | 56 ----------------------------------- runtime/jobject_comparator.h | 30 ------------------- 3 files changed, 87 deletions(-) delete mode 100644 runtime/jobject_comparator.cc delete mode 100644 runtime/jobject_comparator.h diff --git a/runtime/Android.bp b/runtime/Android.bp index 05e923949e..116453b1bf 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -115,7 +115,6 @@ cc_defaults { "jni/java_vm_ext.cc", "jni/jni_env_ext.cc", "jni/jni_internal.cc", - "jobject_comparator.cc", "linear_alloc.cc", "managed_stack.cc", "method_handles.cc", diff --git a/runtime/jobject_comparator.cc b/runtime/jobject_comparator.cc deleted file mode 100644 index 4c45e3839b..0000000000 --- a/runtime/jobject_comparator.cc +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2012 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 "jobject_comparator.h" - -#include "mirror/array-inl.h" -#include "mirror/class-inl.h" -#include "mirror/object-inl.h" -#include "scoped_thread_state_change-inl.h" - -namespace art { - -bool JobjectComparator::operator()(jobject jobj1, jobject jobj2) const { - // Ensure null references and cleared jweaks appear at the end. - if (jobj1 == nullptr) { - return true; - } else if (jobj2 == nullptr) { - return false; - } - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); - Handle obj1(hs.NewHandle(soa.Decode(jobj1))); - Handle obj2(hs.NewHandle(soa.Decode(jobj2))); - if (obj1 == nullptr) { - return true; - } else if (obj2 == nullptr) { - return false; - } - // Sort by class... - if (obj1->GetClass() != obj2->GetClass()) { - return obj1->GetClass()->IdentityHashCode() < obj2->GetClass()->IdentityHashCode(); - } - // ...then by size... - const size_t count1 = obj1->SizeOf(); - const size_t count2 = obj2->SizeOf(); - if (count1 != count2) { - return count1 < count2; - } - // ...and finally by identity hash code. - return obj1->IdentityHashCode() < obj2->IdentityHashCode(); -} - -} // namespace art diff --git a/runtime/jobject_comparator.h b/runtime/jobject_comparator.h deleted file mode 100644 index 698d6678d6..0000000000 --- a/runtime/jobject_comparator.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2012 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_JOBJECT_COMPARATOR_H_ -#define ART_RUNTIME_JOBJECT_COMPARATOR_H_ - -#include - -namespace art { - -struct JobjectComparator { - bool operator()(jobject jobj1, jobject jobj2) const; -}; - -} // namespace art - -#endif // ART_RUNTIME_JOBJECT_COMPARATOR_H_ -- GitLab From 6d5b7e38fd5aba9eaad3a630b4859d214fa8f77c Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 9 May 2018 16:52:48 +0100 Subject: [PATCH 367/749] ART: Clean up after HInstruction::Is##type() rewrite. Address late comments on https://android-review.googlesource.com/679021 . Test: Rely on TreeHugger. Change-Id: Id82976651aced36b8ee2808b6a9effbfb3224d42 --- compiler/optimizing/nodes.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index ae1e6065cd..9dcd741388 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -7747,11 +7747,13 @@ inline bool IsZeroBitPattern(HInstruction* instruction) { return instruction->IsConstant() && instruction->AsConstant()->IsZeroBitPattern(); } +// Implement HInstruction::Is##type() for concrete instructions. #define INSTRUCTION_TYPE_CHECK(type, super) \ inline bool HInstruction::Is##type() const { return GetKind() == k##type; } FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK) #undef INSTRUCTION_TYPE_CHECK +// Implement HInstruction::Is##type() for abstract instructions. #define INSTRUCTION_TYPE_CHECK_RESULT(type, super) \ std::is_base_of::value, #define INSTRUCTION_TYPE_CHECK(type, super) \ @@ -7766,7 +7768,7 @@ inline bool IsZeroBitPattern(HInstruction* instruction) { FOR_EACH_ABSTRACT_INSTRUCTION(INSTRUCTION_TYPE_CHECK) #undef INSTRUCTION_TYPE_CHECK -#undef INSTRUCTION_TYPE_CHECK_CASE +#undef INSTRUCTION_TYPE_CHECK_RESULT #define INSTRUCTION_TYPE_CAST(type, super) \ inline const H##type* HInstruction::As##type() const { \ -- GitLab From d5aeadeac0debd90d0a931c1d41bee6a2d32b209 Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Tue, 8 May 2018 18:23:24 -0700 Subject: [PATCH 368/749] Clear the profile if we fail to add new methods or classes We may fail to add new data in the profile if it contains outdated data (e.g. if the dex files we profiled were updated in the meantime). If this happens, clear the profile to ensure we don't keep around useless data. Test: m test-art-host, 595-profiling-saving Bug: 77839992 Bug: 79200824 Change-Id: I238d56ba4eeec96a3464e42f164d0e798f555ec4 --- .../profile/profile_compilation_info.cc | 12 ++++++-- libprofile/profile/profile_compilation_info.h | 3 ++ .../profile/profile_compilation_info_test.cc | 29 +++++++++++++++++++ runtime/jit/profile_saver.cc | 18 ++++++++++-- 4 files changed, 57 insertions(+), 5 deletions(-) diff --git a/libprofile/profile/profile_compilation_info.cc b/libprofile/profile/profile_compilation_info.cc index 0e0c3c5116..748e24e27c 100644 --- a/libprofile/profile/profile_compilation_info.cc +++ b/libprofile/profile/profile_compilation_info.cc @@ -96,9 +96,7 @@ ProfileCompilationInfo::ProfileCompilationInfo() ProfileCompilationInfo::~ProfileCompilationInfo() { VLOG(profiler) << Dumpable(allocator_.GetMemStats()); - for (DexFileData* data : info_) { - delete data; - } + ClearData(); } void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx, @@ -2106,4 +2104,12 @@ bool ProfileCompilationInfo::ProfileFilterFnAcceptAll( return true; } +void ProfileCompilationInfo::ClearData() { + for (DexFileData* data : info_) { + delete data; + } + info_.clear(); + profile_key_map_.clear(); +} + } // namespace art diff --git a/libprofile/profile/profile_compilation_info.h b/libprofile/profile/profile_compilation_info.h index 32c796c363..e28c5f17b6 100644 --- a/libprofile/profile/profile_compilation_info.h +++ b/libprofile/profile/profile_compilation_info.h @@ -445,6 +445,9 @@ class ProfileCompilationInfo { // Checks if the profile is empty. bool IsEmpty() const; + // Clears all the data from the profile. + void ClearData(); + private: enum ProfileLoadStatus { kProfileLoadWouldOverwiteData, diff --git a/libprofile/profile/profile_compilation_info_test.cc b/libprofile/profile/profile_compilation_info_test.cc index b0f96492df..b3262a7a14 100644 --- a/libprofile/profile/profile_compilation_info_test.cc +++ b/libprofile/profile/profile_compilation_info_test.cc @@ -1339,4 +1339,33 @@ TEST_F(ProfileCompilationInfoTest, FilteredLoadingWithClasses) { ASSERT_TRUE(loaded_info.Equals(expected_info)); } + +TEST_F(ProfileCompilationInfoTest, ClearData) { + ProfileCompilationInfo info; + for (uint16_t i = 0; i < 10; i++) { + ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &info)); + } + ASSERT_FALSE(IsEmpty(info)); + info.ClearData(); + ASSERT_TRUE(IsEmpty(info)); +} + +TEST_F(ProfileCompilationInfoTest, ClearDataAndSave) { + ProfileCompilationInfo info; + for (uint16_t i = 0; i < 10; i++) { + ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &info)); + } + info.ClearData(); + + ScratchFile profile; + ASSERT_TRUE(info.Save(GetFd(profile))); + ASSERT_EQ(0, profile.GetFile()->Flush()); + + // Check that we get back what we saved. + ProfileCompilationInfo loaded_info; + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + ASSERT_TRUE(loaded_info.Load(GetFd(profile))); + ASSERT_TRUE(loaded_info.Equals(info)); +} + } // namespace art diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 618fde8f00..d6230f8241 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -516,10 +516,24 @@ bool ProfileSaver::ProcessProfilingInfo(bool force_save, /*out*/uint16_t* number uint64_t last_save_number_of_methods = info.GetNumberOfMethods(); uint64_t last_save_number_of_classes = info.GetNumberOfResolvedClasses(); - info.AddMethods(profile_methods, ProfileCompilationInfo::MethodHotness::kFlagPostStartup); + // Try to add the method data. Note this may fail is the profile loaded from disk contains + // outdated data (e.g. the previous profiled dex files might have been updated). + // If this happens we clear the profile data and for the save to ensure the file is cleared. + if (!info.AddMethods(profile_methods, + ProfileCompilationInfo::MethodHotness::kFlagPostStartup)) { + LOG(WARNING) << "Could not add methods to the existing profiler. " + << "Clearing the profile data."; + info.ClearData(); + force_save = true; + } + auto profile_cache_it = profile_cache_.find(filename); if (profile_cache_it != profile_cache_.end()) { - info.MergeWith(*(profile_cache_it->second)); + if (!info.MergeWith(*(profile_cache_it->second))) { + LOG(WARNING) << "Could not merge the profile. Clearing the profile data."; + info.ClearData(); + force_save = true; + } } int64_t delta_number_of_methods = -- GitLab From 016fcbe2d24a99b688ae2b5c03ce13d270a97886 Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Thu, 3 May 2018 19:47:35 -0700 Subject: [PATCH 369/749] Support system server ART-profiling We don't have to map the code cache as executable if we only want to save profiles. This enables system server profiling without disabling SElinux to bypass the jit code cache exec-mapping. Test: m test-art-host boot a device with system server profiling enabled. Bug: 73313191 Change-Id: I7f25a905e0b23456183e39e58ad8f4b829ddf0c5 --- runtime/jit/jit.cc | 2 + runtime/jit/jit_code_cache.cc | 67 +++++++++++++++------ runtime/jit/jit_code_cache.h | 10 ++- runtime/native/dalvik_system_ZygoteHooks.cc | 10 ++- runtime/runtime.cc | 17 +++++- runtime/runtime.h | 6 +- 6 files changed, 88 insertions(+), 24 deletions(-) diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index d4fe97784d..0684b461ae 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -185,10 +185,12 @@ Jit* Jit::Create(JitOptions* options, std::string* error_msg) { if (jit_compiler_handle_ == nullptr && !LoadCompiler(error_msg)) { return nullptr; } + bool code_cache_only_for_profile_data = !options->UseJitCompilation(); jit->code_cache_.reset(JitCodeCache::Create( options->GetCodeCacheInitialCapacity(), options->GetCodeCacheMaxCapacity(), jit->generate_debug_info_, + code_cache_only_for_profile_data, error_msg)); if (jit->GetCodeCache() == nullptr) { return nullptr; diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 1c8c26cf5d..249a8b04fb 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -50,7 +50,6 @@ namespace art { namespace jit { -static constexpr int kProtAll = PROT_READ | PROT_WRITE | PROT_EXEC; static constexpr int kProtData = PROT_READ | PROT_WRITE; static constexpr int kProtCode = PROT_READ | PROT_EXEC; @@ -161,6 +160,7 @@ class JitCodeCache::JniStubData { JitCodeCache* JitCodeCache::Create(size_t initial_capacity, size_t max_capacity, bool generate_debug_info, + bool used_only_for_profile_data, std::string* error_msg) { ScopedTrace trace(__PRETTY_FUNCTION__); CHECK_GE(max_capacity, initial_capacity); @@ -184,6 +184,15 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, return nullptr; } + // Decide how we should map the code and data sections. + // If we use the code cache just for profiling we do not need to map the code section as + // executable. + // NOTE 1: this is yet another workaround to bypass strict SElinux policies in order to be able + // to profile system server. + // NOTE 2: We could just not create the code section at all but we will need to + // special case too many cases. + int memmap_flags_prot_code = used_only_for_profile_data ? (kProtCode & ~PROT_EXEC) : kProtCode; + std::string error_str; // Map name specific for android_os_Debug.cpp accounting. // Map in low 4gb to simplify accessing root tables for x86_64. @@ -216,8 +225,11 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, DCHECK_EQ(code_size + data_size, max_capacity); uint8_t* divider = data_map->Begin() + data_size; - MemMap* code_map = - data_map->RemapAtEnd(divider, "jit-code-cache", kProtAll, &error_str, use_ashmem); + MemMap* code_map = data_map->RemapAtEnd( + divider, + "jit-code-cache", + memmap_flags_prot_code | PROT_WRITE, + &error_str, use_ashmem); if (code_map == nullptr) { std::ostringstream oss; oss << "Failed to create read write execute cache: " << error_str << " size=" << max_capacity; @@ -229,7 +241,13 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, code_size = initial_capacity - data_size; DCHECK_EQ(code_size + data_size, initial_capacity); return new JitCodeCache( - code_map, data_map.release(), code_size, data_size, max_capacity, garbage_collect_code); + code_map, + data_map.release(), + code_size, + data_size, + max_capacity, + garbage_collect_code, + memmap_flags_prot_code); } JitCodeCache::JitCodeCache(MemMap* code_map, @@ -237,7 +255,8 @@ JitCodeCache::JitCodeCache(MemMap* code_map, size_t initial_code_capacity, size_t initial_data_capacity, size_t max_capacity, - bool garbage_collect_code) + bool garbage_collect_code, + int memmap_flags_prot_code) : lock_("Jit code cache", kJitCodeCacheLock), lock_cond_("Jit code cache condition variable", lock_), collection_in_progress_(false), @@ -258,7 +277,8 @@ JitCodeCache::JitCodeCache(MemMap* code_map, histogram_code_memory_use_("Memory used for compiled code", 16), histogram_profiling_info_memory_use_("Memory used for profiling info", 16), is_weak_access_enabled_(true), - inline_cache_cond_("Jit inline cache condition variable", lock_) { + inline_cache_cond_("Jit inline cache condition variable", lock_), + memmap_flags_prot_code_(memmap_flags_prot_code) { DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity); code_mspace_ = create_mspace_with_base(code_map_->Begin(), code_end_, false /*locked*/); @@ -274,7 +294,7 @@ JitCodeCache::JitCodeCache(MemMap* code_map, "mprotect jit code cache", code_map_->Begin(), code_map_->Size(), - kProtCode); + memmap_flags_prot_code_); CheckedCall(mprotect, "mprotect jit data cache", data_map_->Begin(), @@ -327,19 +347,30 @@ const void* JitCodeCache::GetJniStubCode(ArtMethod* method) { class ScopedCodeCacheWrite : ScopedTrace { public: - explicit ScopedCodeCacheWrite(MemMap* code_map) + explicit ScopedCodeCacheWrite(const JitCodeCache* const code_cache) : ScopedTrace("ScopedCodeCacheWrite"), - code_map_(code_map) { + code_cache_(code_cache) { ScopedTrace trace("mprotect all"); - CheckedCall(mprotect, "make code writable", code_map_->Begin(), code_map_->Size(), kProtAll); + CheckedCall( + mprotect, + "make code writable", + code_cache_->code_map_->Begin(), + code_cache_->code_map_->Size(), + code_cache_->memmap_flags_prot_code_ | PROT_WRITE); } + ~ScopedCodeCacheWrite() { ScopedTrace trace("mprotect code"); - CheckedCall(mprotect, "make code protected", code_map_->Begin(), code_map_->Size(), kProtCode); + CheckedCall( + mprotect, + "make code protected", + code_cache_->code_map_->Begin(), + code_cache_->code_map_->Size(), + code_cache_->memmap_flags_prot_code_); } private: - MemMap* const code_map_; + const JitCodeCache* const code_cache_; DISALLOW_COPY_AND_ASSIGN(ScopedCodeCacheWrite); }; @@ -557,7 +588,7 @@ void JitCodeCache::FreeAllMethodHeaders( // so it's possible for the same method_header to start representing // different compile code. MutexLock mu(Thread::Current(), lock_); - ScopedCodeCacheWrite scc(code_map_.get()); + ScopedCodeCacheWrite scc(this); for (const OatQuickMethodHeader* method_header : method_headers) { FreeCode(method_header->GetCode()); } @@ -576,7 +607,7 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { // with the classlinker_classes_lock_ held, and suspending ourselves could // lead to a deadlock. { - ScopedCodeCacheWrite scc(code_map_.get()); + ScopedCodeCacheWrite scc(this); for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) { it->second.RemoveMethodsIn(alloc); if (it->second.GetMethods().empty()) { @@ -715,7 +746,7 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, MutexLock mu(self, lock_); WaitForPotentialCollectionToComplete(self); { - ScopedCodeCacheWrite scc(code_map_.get()); + ScopedCodeCacheWrite scc(this); memory = AllocateCode(total_size); if (memory == nullptr) { return nullptr; @@ -878,7 +909,7 @@ bool JitCodeCache::RemoveMethodLocked(ArtMethod* method, bool release_memory) { } bool in_cache = false; - ScopedCodeCacheWrite ccw(code_map_.get()); + ScopedCodeCacheWrite ccw(this); if (UNLIKELY(method->IsNative())) { auto it = jni_stubs_map_.find(JniStubKey(method)); if (it != jni_stubs_map_.end() && it->second.RemoveMethod(method)) { @@ -1105,7 +1136,7 @@ void JitCodeCache::SetFootprintLimit(size_t new_footprint) { DCHECK_EQ(per_space_footprint * 2, new_footprint); mspace_set_footprint_limit(data_mspace_, per_space_footprint); { - ScopedCodeCacheWrite scc(code_map_.get()); + ScopedCodeCacheWrite scc(this); mspace_set_footprint_limit(code_mspace_, per_space_footprint); } } @@ -1273,7 +1304,7 @@ void JitCodeCache::RemoveUnmarkedCode(Thread* self) { std::unordered_set method_headers; { MutexLock mu(self, lock_); - ScopedCodeCacheWrite scc(code_map_.get()); + ScopedCodeCacheWrite scc(this); // Iterate over all compiled code and remove entries that are not marked. for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) { JniStubData* data = &it->second; diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index f1c99fb85a..b10f57eff2 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -68,6 +68,7 @@ template class ObjectArray; namespace jit { class JitInstrumentationCache; +class ScopedCodeCacheWrite; // Alignment in bits that will suit all architectures. static constexpr int kJitCodeAlignment = 16; @@ -88,6 +89,7 @@ class JitCodeCache { static JitCodeCache* Create(size_t initial_capacity, size_t max_capacity, bool generate_debug_info, + bool used_only_for_profile_data, std::string* error_msg); ~JitCodeCache(); @@ -270,7 +272,8 @@ class JitCodeCache { size_t initial_code_capacity, size_t initial_data_capacity, size_t max_capacity, - bool garbage_collect_code); + bool garbage_collect_code, + int memmap_flags_prot_code); // Internal version of 'CommitCode' that will not retry if the // allocation fails. Return null if the allocation fails. @@ -442,7 +445,12 @@ class JitCodeCache { // Condition to wait on for accessing inline caches. ConditionVariable inline_cache_cond_ GUARDED_BY(lock_); + // Mapping flags for the code section. + const int memmap_flags_prot_code_; + friend class art::JitJniStubTestHelper; + friend class ScopedCodeCacheWrite; + DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache); }; diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index e105bab872..38c65f5deb 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -177,6 +177,7 @@ enum { DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 11, HIDDEN_API_ENFORCEMENT_POLICY_MASK = (1 << 12) | (1 << 13), + PROFILE_SYSTEM_SERVER = 1 << 14, // bits to shift (flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) by to get a value // corresponding to hiddenapi::EnforcementPolicy @@ -308,6 +309,9 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, (runtime_flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT); runtime_flags &= ~HIDDEN_API_ENFORCEMENT_POLICY_MASK; + bool profile_system_server = (runtime_flags & PROFILE_SYSTEM_SERVER) == PROFILE_SYSTEM_SERVER; + runtime_flags &= ~PROFILE_SYSTEM_SERVER; + if (runtime_flags != 0) { LOG(ERROR) << StringPrintf("Unknown bits set in runtime_flags: %#x", runtime_flags); } @@ -392,7 +396,11 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, env, is_system_server, action, isa_string.c_str()); } else { Runtime::Current()->InitNonZygoteOrPostFork( - env, is_system_server, Runtime::NativeBridgeAction::kUnload, nullptr); + env, + is_system_server, + Runtime::NativeBridgeAction::kUnload, + /*isa*/ nullptr, + profile_system_server); } } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index f86b7a054e..b8775b874f 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -861,7 +861,11 @@ void Runtime::EndThreadBirth() REQUIRES(Locks::runtime_shutdown_lock_) { } void Runtime::InitNonZygoteOrPostFork( - JNIEnv* env, bool is_system_server, NativeBridgeAction action, const char* isa) { + JNIEnv* env, + bool is_system_server, + NativeBridgeAction action, + const char* isa, + bool profile_system_server) { is_zygote_ = false; if (is_native_bridge_loaded_) { @@ -884,8 +888,15 @@ void Runtime::InitNonZygoteOrPostFork( heap_->ResetGcPerformanceInfo(); // We may want to collect profiling samples for system server, but we never want to JIT there. - if ((!is_system_server || !jit_options_->UseJitCompilation()) && - !safe_mode_ && + if (is_system_server) { + jit_options_->SetUseJitCompilation(false); + jit_options_->SetSaveProfilingInfo(profile_system_server); + if (profile_system_server) { + jit_options_->SetWaitForJitNotificationsToSaveProfile(false); + VLOG(profiler) << "Enabling system server profiles"; + } + } + if (!safe_mode_ && (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) && jit_ == nullptr) { // Note that when running ART standalone (not zygote, nor zygote fork), diff --git a/runtime/runtime.h b/runtime/runtime.h index 87f5b5134d..953acbb948 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -451,7 +451,11 @@ class Runtime { void PreZygoteFork(); void InitNonZygoteOrPostFork( - JNIEnv* env, bool is_system_server, NativeBridgeAction action, const char* isa); + JNIEnv* env, + bool is_system_server, + NativeBridgeAction action, + const char* isa, + bool profile_system_server = false); const instrumentation::Instrumentation* GetInstrumentation() const { return &instrumentation_; -- GitLab From 35a4f486cb0a2a600a4ea346b4615ad0ea189dc2 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 9 May 2018 14:49:54 +0100 Subject: [PATCH 370/749] Add support for loading trusted dex files. Which can use hidden APIs. bug: 64382372 Test: m Change-Id: Ifd40b49270c11ad51281b2127532952fb5206a9d --- runtime/native/dalvik_system_DexFile.cc | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 8320d9c7ba..0c67e88604 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -816,6 +816,28 @@ static jlong DexFile_getStaticSizeOfDexFile(JNIEnv* env, jclass, jobject cookie) return static_cast(file_size); } +static void DexFile_setTrusted(JNIEnv* env, jclass, jobject j_cookie) { + Runtime* runtime = Runtime::Current(); + ScopedObjectAccess soa(env); + + // Currently only allow this for debuggable apps. + if (!runtime->IsJavaDebuggable()) { + ThrowSecurityException("Can't exempt class, process is not debuggable."); + return; + } + + std::vector dex_files; + const OatFile* oat_file; + if (!ConvertJavaArrayToDexFiles(env, j_cookie, dex_files, oat_file)) { + Thread::Current()->AssertPendingException(); + return; + } + + for (const DexFile* dex_file : dex_files) { + const_cast(dex_file)->SetIsPlatformDexFile(); + } +} + static JNINativeMethod gMethods[] = { NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"), NATIVE_METHOD(DexFile, @@ -854,7 +876,8 @@ static JNINativeMethod gMethods[] = { "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"), NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J"), NATIVE_METHOD(DexFile, getDexFileOptimizationStatus, - "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;") + "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"), + NATIVE_METHOD(DexFile, setTrusted, "(Ljava/lang/Object;)V") }; void register_dalvik_system_DexFile(JNIEnv* env) { -- GitLab From 59a49a6fe102b26aacda906b6a2c466d84819c73 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Wed, 9 May 2018 10:58:27 +0100 Subject: [PATCH 371/749] Implement VMDebug API to mark a class "platform" Add a native implementation of VMDebug.allowHiddenApiReflectionFrom which takes a Class object and sets a new access flag kAccSkipAccessChecks on the corresponding mirror::Class instance. The function will throw a SecurityException if the process has not been forked as Java debuggable. hiddenapi::IsCallerInPlatformDex is extended to take an optional argument of the mirror::Class of the caller. If it is set and the class has kAccSkipAccessChecks, the caller is allowed to acccess non-SDK APIs. Note that the mirror::Class of the caller is only provided for reflection and JNI. The access flag is ignored for other means of access. Bug: 64382372 Test: N/A Change-Id: I2bf0ca7dcb45c17fe91eb2d421c947b892bd6fec --- libdexfile/dex/modifiers.h | 1 + runtime/hidden_api.h | 50 ++++++++++++++++--------- runtime/jni/jni_internal.cc | 6 +-- runtime/mirror/class.h | 9 +++++ runtime/native/dalvik_system_VMDebug.cc | 20 ++++++++++ runtime/native/java_lang_Class.cc | 8 ++-- 6 files changed, 69 insertions(+), 25 deletions(-) diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h index 2425a588df..be82fff65c 100644 --- a/libdexfile/dex/modifiers.h +++ b/libdexfile/dex/modifiers.h @@ -58,6 +58,7 @@ static constexpr uint32_t kAccObsoleteMethod = 0x00040000; // method (ru static constexpr uint32_t kAccSkipAccessChecks = 0x00080000; // method (runtime, not native) // Used by a class to denote that the verifier has attempted to check it at least once. static constexpr uint32_t kAccVerificationAttempted = 0x00080000; // class (runtime) +static constexpr uint32_t kAccSkipHiddenApiChecks = 0x00100000; // class (runtime) // This is set by the class linker during LinkInterfaceMethods. It is used by a method to represent // that it was copied from its declaring class into another class. All methods marked kAccMiranda // and kAccDefaultConflict will have this bit set. Any kAccDefault method contained in the methods_ diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index 65c6406393..8e21fd3b8f 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -143,31 +143,45 @@ Action GetMemberActionImpl(T* member, // Returns true if the caller is either loaded by the boot strap class loader or comes from // a dex file located in ${ANDROID_ROOT}/framework/. ALWAYS_INLINE -inline bool IsCallerInPlatformDex(ObjPtr caller_class_loader, - ObjPtr caller_dex_cache) +inline bool IsCallerTrusted(ObjPtr caller, + ObjPtr caller_class_loader, + ObjPtr caller_dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) { if (caller_class_loader.IsNull()) { + // Boot class loader. return true; - } else if (caller_dex_cache.IsNull()) { - return false; - } else { + } + + if (!caller_dex_cache.IsNull()) { const DexFile* caller_dex_file = caller_dex_cache->GetDexFile(); - return caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile(); + if (caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile()) { + // Caller is in a platform dex file. + return true; + } + } + + if (!caller.IsNull() && + caller->ShouldSkipHiddenApiChecks() && + Runtime::Current()->IsJavaDebuggable()) { + // We are in debuggable mode and this caller has been marked trusted. + return true; } + + return false; } } // namespace detail // Returns true if access to `member` should be denied to the caller of the -// reflective query. The decision is based on whether the caller is in the -// platform or not. Because different users of this function determine this -// in a different way, `fn_caller_in_platform(self)` is called and should -// return true if the caller is located in the platform. +// reflective query. The decision is based on whether the caller is trusted or +// not. Because different users of this function determine this in a different +// way, `fn_caller_is_trusted(self)` is called and should return true if the +// caller is allowed to access the platform. // This function might print warnings into the log if the member is hidden. template inline Action GetMemberAction(T* member, Thread* self, - std::function fn_caller_in_platform, + std::function fn_caller_is_trusted, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); @@ -188,8 +202,8 @@ inline Action GetMemberAction(T* member, // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access. // This can be *very* expensive. Save it for last. - if (fn_caller_in_platform(self)) { - // Caller in the platform. Exit. + if (fn_caller_is_trusted(self)) { + // Caller is trusted. Exit. return kAllow; } @@ -197,10 +211,9 @@ inline Action GetMemberAction(T* member, return detail::GetMemberActionImpl(member, api_list, action, access_method); } -inline bool IsCallerInPlatformDex(ObjPtr caller) - REQUIRES_SHARED(Locks::mutator_lock_) { +inline bool IsCallerTrusted(ObjPtr caller) REQUIRES_SHARED(Locks::mutator_lock_) { return !caller.IsNull() && - detail::IsCallerInPlatformDex(caller->GetClassLoader(), caller->GetDexCache()); + detail::IsCallerTrusted(caller, caller->GetClassLoader(), caller->GetDexCache()); } // Returns true if access to `member` should be denied to a caller loaded with @@ -212,10 +225,11 @@ inline Action GetMemberAction(T* member, ObjPtr caller_dex_cache, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { - bool caller_in_platform = detail::IsCallerInPlatformDex(caller_class_loader, caller_dex_cache); + bool is_caller_trusted = + detail::IsCallerTrusted(/* caller */ nullptr, caller_class_loader, caller_dex_cache); return GetMemberAction(member, /* thread */ nullptr, - [caller_in_platform] (Thread*) { return caller_in_platform; }, + [is_caller_trusted] (Thread*) { return is_caller_trusted; }, access_method); } diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc index 9dbcded867..cd66a60376 100644 --- a/runtime/jni/jni_internal.cc +++ b/runtime/jni/jni_internal.cc @@ -80,15 +80,15 @@ namespace art { // things not rendering correctly. E.g. b/16858794 static constexpr bool kWarnJniAbort = false; -static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::IsCallerInPlatformDex(GetCallingClass(self, /* num_frames */ 1)); +static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { + return hiddenapi::IsCallerTrusted(GetCallingClass(self, /* num_frames */ 1)); } template ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { hiddenapi::Action action = hiddenapi::GetMemberAction( - member, self, IsCallerInPlatformDex, hiddenapi::kJNI); + member, self, IsCallerTrusted, hiddenapi::kJNI); if (action != hiddenapi::kAllow) { hiddenapi::NotifyHiddenApiListener(member); } diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 51d1376a3c..98e25eb320 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -210,6 +210,15 @@ class MANAGED Class FINAL : public Object { return (GetAccessFlags() & kAccClassIsFinalizable) != 0; } + ALWAYS_INLINE bool ShouldSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) { + return (GetAccessFlags() & kAccSkipHiddenApiChecks) != 0; + } + + ALWAYS_INLINE void SetSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t flags = GetAccessFlags(); + SetAccessFlags(flags | kAccSkipHiddenApiChecks); + } + ALWAYS_INLINE void SetRecursivelyInitialized() REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK_EQ(GetLockOwnerThreadId(), Thread::Current()->GetThreadId()); uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_)); diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 6aaafc2864..f1e267becc 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -588,6 +588,25 @@ static void VMDebug_nativeAttachAgent(JNIEnv* env, jclass, jstring agent, jobjec Runtime::Current()->AttachAgent(env, filename, classloader); } +static void VMDebug_allowHiddenApiReflectionFrom(JNIEnv* env, jclass, jclass j_caller) { + Runtime* runtime = Runtime::Current(); + ScopedObjectAccess soa(env); + + if (!runtime->IsJavaDebuggable()) { + ThrowSecurityException("Can't exempt class, process is not debuggable."); + return; + } + + StackHandleScope<1> hs(soa.Self()); + Handle h_caller(hs.NewHandle(soa.Decode(j_caller))); + if (h_caller.IsNull()) { + ThrowNullPointerException("argument is null"); + return; + } + + h_caller->SetSkipHiddenApiChecks(); +} + static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"), NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"), @@ -623,6 +642,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"), NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"), NATIVE_METHOD(VMDebug, nativeAttachAgent, "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"), + NATIVE_METHOD(VMDebug, allowHiddenApiReflectionFrom, "(Ljava/lang/Class;)V"), }; void register_dalvik_system_VMDebug(JNIEnv* env) { diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 2625c0a316..68024cd1c2 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -52,7 +52,7 @@ namespace art { // Returns true if the first caller outside of the Class class or java.lang.invoke package // is in a platform DEX file. -static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { +static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { // Walk the stack and find the first frame not from java.lang.Class and not from java.lang.invoke. // This is very expensive. Save this till the last. struct FirstExternalCallerVisitor : public StackVisitor { @@ -99,7 +99,7 @@ static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_l FirstExternalCallerVisitor visitor(self); visitor.WalkStack(); return visitor.caller != nullptr && - hiddenapi::IsCallerInPlatformDex(visitor.caller->GetDeclaringClass()); + hiddenapi::IsCallerTrusted(visitor.caller->GetDeclaringClass()); } // Returns true if the first non-ClassClass caller up the stack is not allowed to @@ -107,7 +107,7 @@ static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_l ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); - return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInPlatformDex(self); + return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerTrusted(self); } // Returns true if the first non-ClassClass caller up the stack should not be @@ -116,7 +116,7 @@ template ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { hiddenapi::Action action = hiddenapi::GetMemberAction( - member, self, IsCallerInPlatformDex, hiddenapi::kReflection); + member, self, IsCallerTrusted, hiddenapi::kReflection); if (action != hiddenapi::kAllow) { hiddenapi::NotifyHiddenApiListener(member); } -- GitLab From 18259d7fb7164a5e029df4f883b3a79ccc2403e8 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Thu, 12 Apr 2018 11:18:23 +0100 Subject: [PATCH 372/749] ART: Compiler support for const-method-type Implemented as a runtime call. Bug: 66890674 Test: art/test.py --target -r -t 979 Test: art/test.py --target --64 -r -t 979 Test: art/test.py --host -r -t 979 Change-Id: I4b3d3969d455d0198cfe122eea8abd54e0ea20ee --- compiler/optimizing/code_generator.cc | 20 +++++ compiler/optimizing/code_generator.h | 5 ++ compiler/optimizing/code_generator_arm64.cc | 10 +++ .../optimizing/code_generator_arm_vixl.cc | 10 +++ compiler/optimizing/code_generator_mips.cc | 10 +++ compiler/optimizing/code_generator_mips64.cc | 10 +++ compiler/optimizing/code_generator_x86.cc | 10 +++ compiler/optimizing/code_generator_x86_64.cc | 10 +++ compiler/optimizing/graph_visualizer.cc | 7 ++ compiler/optimizing/instruction_builder.cc | 14 +++ compiler/optimizing/instruction_builder.h | 4 + compiler/optimizing/nodes.h | 46 ++++++++++ .../optimizing/reference_type_propagation.cc | 12 +++ .../optimizing/reference_type_propagation.h | 2 + .../assembler_thumb_test_expected.cc.inc | 2 +- dex2oat/linker/oat_writer_test.cc | 2 +- runtime/arch/arm/quick_entrypoints_arm.S | 6 +- runtime/arch/arm64/quick_entrypoints_arm64.S | 6 +- runtime/arch/mips/entrypoints_init_mips.cc | 2 + runtime/arch/mips/quick_entrypoints_mips.S | 13 ++- .../arch/mips64/quick_entrypoints_mips64.S | 13 ++- runtime/arch/x86/quick_entrypoints_x86.S | 8 +- .../arch/x86_64/quick_entrypoints_x86_64.S | 8 +- runtime/asm_support.h | 2 +- runtime/entrypoints/entrypoint_utils.cc | 15 ++++ runtime/entrypoints/entrypoint_utils.h | 5 ++ .../entrypoints/quick/quick_default_externs.h | 1 + .../quick/quick_default_init_entrypoints.h | 1 + .../quick/quick_dexcache_entrypoints.cc | 10 +++ .../quick/quick_entrypoints_list.h | 1 + runtime/entrypoints_order_test.cc | 3 +- runtime/oat.h | 4 +- runtime/verifier/method_verifier.cc | 2 - test/979-const-method-handle/expected.txt | 2 + test/979-const-method-handle/src/Main.java | 90 ++++++++++++++----- 35 files changed, 325 insertions(+), 41 deletions(-) diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 231017f55e..1e44311cb1 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -736,6 +736,26 @@ void CodeGenerator::GenerateLoadClassRuntimeCall(HLoadClass* cls) { } } +void CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary( + HLoadMethodType* method_type, + Location runtime_proto_index_location, + Location runtime_return_location) { + DCHECK_EQ(method_type->InputCount(), 1u); + LocationSummary* locations = + new (method_type->GetBlock()->GetGraph()->GetAllocator()) LocationSummary( + method_type, LocationSummary::kCallOnMainOnly); + locations->SetInAt(0, Location::NoLocation()); + locations->AddTemp(runtime_proto_index_location); + locations->SetOut(runtime_return_location); +} + +void CodeGenerator::GenerateLoadMethodTypeRuntimeCall(HLoadMethodType* method_type) { + LocationSummary* locations = method_type->GetLocations(); + MoveConstant(locations->GetTemp(0), method_type->GetProtoIndex()); + CheckEntrypointTypes(); + InvokeRuntime(kQuickResolveMethodType, method_type, method_type->GetDexPc()); +} + static uint32_t GetBootImageOffsetImpl(const void* object, ImageHeader::ImageSections section) { Runtime* runtime = Runtime::Current(); DCHECK(runtime->IsAotCompiler()); diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index f0c4ee01cc..7e84a448cc 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -564,6 +564,11 @@ class CodeGenerator : public DeletableArenaObject { Location runtime_return_location); void GenerateLoadClassRuntimeCall(HLoadClass* cls); + static void CreateLoadMethodTypeRuntimeCallLocationSummary(HLoadMethodType* method_type, + Location runtime_type_index_location, + Location runtime_return_location); + void GenerateLoadMethodTypeRuntimeCall(HLoadMethodType* method_type); + uint32_t GetBootImageOffset(HLoadClass* load_class); uint32_t GetBootImageOffset(HLoadString* load_string); uint32_t GetBootImageOffset(HInvokeStaticOrDirect* invoke); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index d4cfab82de..0601d2d79a 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -5144,6 +5144,16 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA } } +void LocationsBuilderARM64::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConvention calling_convention; + Location location = LocationFrom(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorARM64::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + static MemOperand GetExceptionTlsAddress() { return MemOperand(tr, Thread::ExceptionOffset().Int32Value()); } diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 58ce9aa9f0..33304c619d 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -7527,6 +7527,16 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ } } +void LocationsBuilderARMVIXL::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConventionARMVIXL calling_convention; + Location location = LocationFrom(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorARMVIXL::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath); diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 25e2eddbfa..3a3fcffe15 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -8226,6 +8226,16 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF } } +void LocationsBuilderMIPS::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConvention calling_convention; + Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, loc, loc); +} + +void InstructionCodeGeneratorMIPS::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + static int32_t GetExceptionTlsOffset() { return Thread::ExceptionOffset().Int32Value(); } diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 5b07b55cbb..d6fc9a15b8 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -6262,6 +6262,16 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S } } +void LocationsBuilderMIPS64::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConvention calling_convention; + Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, loc, loc); +} + +void InstructionCodeGeneratorMIPS64::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + static int32_t GetExceptionTlsOffset() { return Thread::ExceptionOffset().Int32Value(); } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 82d1fda878..d18a750faf 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -6539,6 +6539,16 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFE } } +void LocationsBuilderX86::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConvention calling_convention; + Location location = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorX86::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + void LocationsBuilderX86::VisitClinitCheck(HClinitCheck* check) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 322b0cfc4c..450c8574b0 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -5908,6 +5908,16 @@ void LocationsBuilderX86_64::VisitClinitCheck(HClinitCheck* check) { } } +void LocationsBuilderX86_64::VisitLoadMethodType(HLoadMethodType* load) { + // Custom calling convention: RAX serves as both input and output. + Location location = Location::RegisterLocation(RAX); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorX86_64::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + void InstructionCodeGeneratorX86_64::VisitClinitCheck(HClinitCheck* check) { // We assume the class to not be null. SlowPathCode* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathX86_64( diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 54d4644580..87ce1f0c73 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -386,6 +386,13 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { << load_class->NeedsAccessCheck() << std::noboolalpha; } + void VisitLoadMethodType(HLoadMethodType* load_method_type) OVERRIDE { + StartAttributeStream("load_kind") << "RuntimeCall"; + const DexFile& dex_file = load_method_type->GetDexFile(); + const DexFile::ProtoId& proto_id = dex_file.GetProtoId(load_method_type->GetProtoIndex()); + StartAttributeStream("method_type") << dex_file.GetProtoSignature(proto_id); + } + void VisitLoadString(HLoadString* load_string) OVERRIDE { StartAttributeStream("load_kind") << load_string->GetLoadKind(); } diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 9647dd5d41..61730a8128 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1896,6 +1896,13 @@ bool HInstructionBuilder::LoadClassNeedsAccessCheck(Handle klass) } } +void HInstructionBuilder::BuildLoadMethodType(uint16_t proto_idx, uint32_t dex_pc) { + const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); + HLoadMethodType* load_method_type = + new (allocator_) HLoadMethodType(graph_->GetCurrentMethod(), proto_idx, dex_file, dex_pc); + AppendInstruction(load_method_type); +} + void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, uint8_t destination, uint8_t reference, @@ -2927,6 +2934,13 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, break; } + case Instruction::CONST_METHOD_TYPE: { + uint16_t proto_idx = instruction.VRegB_21c(); + BuildLoadMethodType(proto_idx, dex_pc); + UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); + break; + } + case Instruction::MOVE_EXCEPTION: { AppendInstruction(new (allocator_) HLoadException(dex_pc)); UpdateLocal(instruction.VRegA_11x(), current_block_->GetLastInstruction()); diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index f78829232d..3fde54cffa 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -45,6 +45,7 @@ class VariableSizedHandleScope; namespace mirror { class Class; +class MethodType; } // namespace mirror class HInstructionBuilder : public ValueObject { @@ -239,6 +240,9 @@ class HInstructionBuilder : public ValueObject { bool LoadClassNeedsAccessCheck(Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); + // Builds a `HLoadMethodType` loading the given `proto_index`. + void BuildLoadMethodType(uint16_t proto_idx, uint32_t dex_pc); + // Returns the outer-most compiling method's class. ObjPtr GetOutermostCompilingClass() const; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 9dcd741388..54882ff92b 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -41,6 +41,7 @@ #include "intrinsics_enum.h" #include "locations.h" #include "mirror/class.h" +#include "mirror/method_type.h" #include "offsets.h" #include "utils/intrusive_forward_list.h" @@ -1382,6 +1383,7 @@ class HLoopInformationOutwardIterator : public ValueObject { M(LessThanOrEqual, Condition) \ M(LoadClass, Instruction) \ M(LoadException, Instruction) \ + M(LoadMethodType, Instruction) \ M(LoadString, Instruction) \ M(LongConstant, Constant) \ M(Max, Instruction) \ @@ -6498,6 +6500,50 @@ inline void HLoadString::AddSpecialInput(HInstruction* special_input) { special_input->AddUseAt(this, 0); } +class HLoadMethodType FINAL : public HInstruction { + public: + HLoadMethodType(HCurrentMethod* current_method, + uint16_t proto_idx, + const DexFile& dex_file, + uint32_t dex_pc) + : HInstruction(kLoadMethodType, + DataType::Type::kReference, + SideEffectsForArchRuntimeCalls(), + dex_pc), + special_input_(HUserRecord(current_method)), + proto_idx_(proto_idx), + dex_file_(dex_file) { + } + + using HInstruction::GetInputRecords; // Keep the const version visible. + ArrayRef> GetInputRecords() OVERRIDE FINAL { + return ArrayRef>( + &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u); + } + + bool IsClonable() const OVERRIDE { return true; } + + uint16_t GetProtoIndex() const { return proto_idx_; } + + const DexFile& GetDexFile() const { return dex_file_; } + + static SideEffects SideEffectsForArchRuntimeCalls() { + return SideEffects::CanTriggerGC(); + } + + DECLARE_INSTRUCTION(LoadMethodType); + + protected: + DEFAULT_COPY_CONSTRUCTOR(LoadMethodType); + + private: + // The special input is the HCurrentMethod for kRuntimeCall. + HUserRecord special_input_; + + uint16_t proto_idx_; + const DexFile& dex_file_; +}; + /** * Performs an initialization check on its Class object input. */ diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index c47c69af67..b15a0ea52f 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -59,6 +59,12 @@ ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetClassCla return GetRootHandle(handles_, ClassLinker::kJavaLangClass, &class_class_handle_); } +ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetMethodTypeClassHandle() { + return GetRootHandle(handles_, + ClassLinker::kJavaLangInvokeMethodType, + &method_type_class_handle_); +} + ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetStringClassHandle() { return GetRootHandle(handles_, ClassLinker::kJavaLangString, &string_class_handle_); } @@ -89,6 +95,7 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { void VisitLoadClass(HLoadClass* load_class) OVERRIDE; void VisitInstanceOf(HInstanceOf* load_class) OVERRIDE; void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE; + void VisitLoadMethodType(HLoadMethodType* instr) OVERRIDE; void VisitLoadString(HLoadString* instr) OVERRIDE; void VisitLoadException(HLoadException* instr) OVERRIDE; void VisitNewArray(HNewArray* instr) OVERRIDE; @@ -668,6 +675,11 @@ void ReferenceTypePropagation::RTPVisitor::VisitClinitCheck(HClinitCheck* instr) instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo()); } +void ReferenceTypePropagation::RTPVisitor::VisitLoadMethodType(HLoadMethodType* instr) { + instr->SetReferenceTypeInfo( + ReferenceTypeInfo::Create(handle_cache_->GetMethodTypeClassHandle(), /* is_exact */ true)); +} + void ReferenceTypePropagation::RTPVisitor::VisitLoadString(HLoadString* instr) { instr->SetReferenceTypeInfo( ReferenceTypeInfo::Create(handle_cache_->GetStringClassHandle(), /* is_exact */ true)); diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h index 400852f4dc..da2193d223 100644 --- a/compiler/optimizing/reference_type_propagation.h +++ b/compiler/optimizing/reference_type_propagation.h @@ -75,6 +75,7 @@ class ReferenceTypePropagation : public HOptimization { ReferenceTypeInfo::TypeHandle GetObjectClassHandle(); ReferenceTypeInfo::TypeHandle GetClassClassHandle(); + ReferenceTypeInfo::TypeHandle GetMethodTypeClassHandle(); ReferenceTypeInfo::TypeHandle GetStringClassHandle(); ReferenceTypeInfo::TypeHandle GetThrowableClassHandle(); @@ -83,6 +84,7 @@ class ReferenceTypePropagation : public HOptimization { ReferenceTypeInfo::TypeHandle object_class_handle_; ReferenceTypeInfo::TypeHandle class_class_handle_; + ReferenceTypeInfo::TypeHandle method_type_class_handle_; ReferenceTypeInfo::TypeHandle string_class_handle_; ReferenceTypeInfo::TypeHandle throwable_class_handle_; }; diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc index 674dc9a78b..ff3e1ba9f0 100644 --- a/compiler/utils/assembler_thumb_test_expected.cc.inc +++ b/compiler/utils/assembler_thumb_test_expected.cc.inc @@ -153,7 +153,7 @@ const char* const VixlJniHelpersResults[] = { " 21c: f8d9 8034 ldr.w r8, [r9, #52] ; 0x34\n", " 220: 4770 bx lr\n", " 222: 4660 mov r0, ip\n", - " 224: f8d9 c2c4 ldr.w ip, [r9, #708] ; 0x2c4\n", + " 224: f8d9 c2c8 ldr.w ip, [r9, #712] ; 0x2c8\n", " 228: 47e0 blx ip\n", nullptr }; diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 1699153e7e..7bd9f0fe8d 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -497,7 +497,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(76U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(24U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(162 * static_cast(GetInstructionSetPointerSize(kRuntimeISA)), + EXPECT_EQ(163 * static_cast(GetInstructionSetPointerSize(kRuntimeISA)), sizeof(QuickEntryPoints)); } diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index a930cc494e..1a1f4edad8 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -1015,7 +1015,10 @@ ENTRY \name END \name .endm -// Macro for string and type resolution and initialization. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. + */ .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET .extern \entrypoint ENTRY \name @@ -1040,6 +1043,7 @@ END \name ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode // Note: Functions `art{Get,Set}{Static,Instance}FromCompiledCode` are diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 9ff5ebede3..9919e98d6b 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1533,7 +1533,10 @@ ENTRY \name END \name .endm -// Macro for string and type resolution and initialization. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. + */ .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET .extern \entrypoint ENTRY \name @@ -1577,6 +1580,7 @@ TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode // Note: Functions `art{Get,Set}{Static,Instance}FromCompiledCode` are diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 9418caf98c..58b9d48951 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -203,6 +203,8 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { static_assert(!IsDirectEntrypoint(kQuickInitializeType), "Non-direct C stub marked direct."); qpoints->pResolveString = art_quick_resolve_string; static_assert(!IsDirectEntrypoint(kQuickResolveString), "Non-direct C stub marked direct."); + qpoints->pResolveMethodType = art_quick_resolve_method_type; + static_assert(!IsDirectEntrypoint(kQuickResolveMethodType), "Non-direct C stub marked direct."); // Field qpoints->pSet8Instance = art_quick_set8_instance; diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index d8fe480719..104a4ca6d6 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -2027,8 +2027,11 @@ GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_tlab, artAllocArrayFr GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64 -// Macro for string and type resolution and initialization. -// $a0 is both input and output. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. $a0 is both input and + * output. + */ .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET .extern \entrypoint ENTRY_NO_GP \name @@ -2052,6 +2055,12 @@ END \name ONE_ARG_SAVE_EVERYTHING_DOWNCALL \name, \entrypoint, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET .endm + /* + * Entry from managed code to resolve a method type. On entry, A0 holds the method type index. + * On success the MethodType is returned, otherwise an exception is raised. + */ +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode + /* * Entry from managed code to resolve a string, this stub will allocate a String and deliver an * exception on error. On success the String is returned. A0 holds the string index. The fast diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 8d2a7bd6c1..1e94e07c23 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1930,8 +1930,11 @@ GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_tlab, artAllocArrayFr GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64 -// Macro for string and type resolution and initialization. -// $a0 is both input and output. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. $a0 is both input and + * output. + */ .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET .extern \entrypoint ENTRY_NO_GP \name @@ -1952,6 +1955,12 @@ END \name ONE_ARG_SAVE_EVERYTHING_DOWNCALL \name, \entrypoint, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET .endm + /* + * Entry from managed code to resolve a method type. On entry, A0 holds the method type index. + * On success the MethodType is returned, otherwise an exception is raised. + */ +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode + /* * Entry from managed code to resolve a string, this stub will allocate a String and deliver an * exception on error. On success the String is returned. A0 holds the string index. The fast diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index df43aef94b..0ae691468d 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -923,7 +923,10 @@ MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) END_FUNCTION VAR(c_name) END_MACRO -// Macro for string and type resolution and initialization. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. + */ MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET) DEFINE_FUNCTION VAR(c_name) SETUP_SAVE_EVERYTHING_FRAME ebx, ebx, \runtime_method_offset // save ref containing registers for GC @@ -932,7 +935,7 @@ MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset CFI_ADJUST_CFA_OFFSET(8) pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) - PUSH eax // pass arg1 + PUSH eax // pass the index of the constant as arg1 call CALLVAR(cxx_name) // cxx_name(arg1, Thread*) addl MACRO_LITERAL(16), %esp // pop arguments CFI_ADJUST_CFA_OFFSET(-16) @@ -1278,6 +1281,7 @@ GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFr ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 4f941e1c48..8fef8020ef 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -951,12 +951,15 @@ MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) END_FUNCTION VAR(c_name) END_MACRO -// Macro for string and type resolution and initialization. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. + */ MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET) DEFINE_FUNCTION VAR(c_name) SETUP_SAVE_EVERYTHING_FRAME \runtime_method_offset // save everything for GC // Outgoing argument set up - movl %eax, %edi // pass string index + movl %eax, %edi // pass the index of the constant as arg0 movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current() call CALLVAR(cxx_name) // cxx_name(arg0, Thread*) testl %eax, %eax // If result is null, deliver the OOME. @@ -1298,6 +1301,7 @@ END_FUNCTION art_quick_alloc_object_initialized_region_tlab ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO diff --git a/runtime/asm_support.h b/runtime/asm_support.h index 2f7d6ab98f..705e1ff6cf 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -73,7 +73,7 @@ ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, // Offset of field Thread::tlsPtr_.mterp_current_ibase. #define THREAD_CURRENT_IBASE_OFFSET \ - (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 162) * __SIZEOF_POINTER__) + (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 163) * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET, art::Thread::MterpCurrentIBaseOffset().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_default_ibase. diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 246c703e93..df184bc733 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -260,4 +260,19 @@ ArtMethod* GetCalleeSaveOuterMethod(Thread* self, CalleeSaveType type) { return DoGetCalleeSaveMethodOuterCallerAndPc(sp, type).first; } +ObjPtr ResolveMethodTypeFromCode(ArtMethod* referrer, + uint32_t proto_idx) { + Thread::PoisonObjectPointersIfDebug(); + ObjPtr method_type = + referrer->GetDexCache()->GetResolvedMethodType(proto_idx); + if (UNLIKELY(method_type == nullptr)) { + StackHandleScope<2> hs(Thread::Current()); + Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); + Handle class_loader(hs.NewHandle(referrer->GetClassLoader())); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + method_type = class_linker->ResolveMethodType(hs.Self(), proto_idx, dex_cache, class_loader); + } + return method_type; +} + } // namespace art diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index eb32153b16..203ff3d031 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -34,6 +34,7 @@ namespace art { namespace mirror { class Array; class Class; +class MethodType; class Object; class String; } // namespace mirror @@ -151,6 +152,10 @@ inline ObjPtr ResolveVerifyAndClinit(dex::TypeIndex type_idx, REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); +ObjPtr ResolveMethodTypeFromCode(ArtMethod* referrer, uint32_t proto_idx) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Roles::uninterruptible_); + inline ObjPtr ResolveStringFromCode(ArtMethod* referrer, dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_) diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h index 2d0932a0c4..d934a532ee 100644 --- a/runtime/entrypoints/quick/quick_default_externs.h +++ b/runtime/entrypoints/quick/quick_default_externs.h @@ -37,6 +37,7 @@ extern "C" void art_quick_check_instance_of(art::mirror::Object*, art::mirror::C extern "C" void* art_quick_initialize_static_storage(uint32_t); extern "C" void* art_quick_initialize_type(uint32_t); extern "C" void* art_quick_initialize_type_and_verify_access(uint32_t); +extern "C" void* art_quick_resolve_method_type(uint32_t); extern "C" void* art_quick_resolve_string(uint32_t); // Field entrypoints. diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h index 8c90800463..a4572f65a7 100644 --- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h +++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h @@ -37,6 +37,7 @@ static void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qp qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage; qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access; qpoints->pInitializeType = art_quick_initialize_type; + qpoints->pResolveMethodType = art_quick_resolve_method_type; qpoints->pResolveString = art_quick_resolve_string; // Field diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index cfb427f1ac..09cbfffe4b 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -183,6 +183,16 @@ extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type return result.Ptr(); } +extern "C" mirror::MethodType* artResolveMethodTypeFromCode(uint32_t proto_idx, Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); + auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, + CalleeSaveType::kSaveEverything); + ArtMethod* caller = caller_and_outer.caller; + ObjPtr result = ResolveMethodTypeFromCode(caller, proto_idx); + return result.Ptr(); +} + extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h index 48a56f2fbf..39dcd39208 100644 --- a/runtime/entrypoints/quick/quick_entrypoints_list.h +++ b/runtime/entrypoints/quick/quick_entrypoints_list.h @@ -38,6 +38,7 @@ V(InitializeStaticStorage, void*, uint32_t) \ V(InitializeTypeAndVerifyAccess, void*, uint32_t) \ V(InitializeType, void*, uint32_t) \ + V(ResolveMethodType, void*, uint32_t) \ V(ResolveString, void*, uint32_t) \ \ V(Set8Instance, int, uint32_t, void*, int8_t) \ diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index 1fdf439d3f..b0689f6cd3 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -183,7 +183,8 @@ class EntrypointsOrderTest : public CommonRuntimeTest { sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeTypeAndVerifyAccess, pInitializeType, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeType, pResolveString, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeType, pResolveMethodType, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveMethodType, pResolveString, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveString, pSet8Instance, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet8Instance, pSet8Static, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet8Static, pSet16Instance, sizeof(void*)); diff --git a/runtime/oat.h b/runtime/oat.h index 0318606f87..9a58ded7e8 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: Use rMR as temp in Baker RB introspection marking. - static constexpr uint8_t kOatVersion[] = { '1', '4', '1', '\0' }; + // Last oat version changed reason: compiler support const-method-type + static constexpr uint8_t kOatVersion[] = { '1', '4', '2', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index bc29aaca6d..5cd60915b5 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -2293,8 +2293,6 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::CONST_METHOD_TYPE: work_line_->SetRegisterType( this, inst->VRegA_21c(), reg_types_.JavaLangInvokeMethodType()); - // TODO: add compiler support for const-method-{handle,type} (b/66890674) - Fail(VERIFY_ERROR_FORCE_INTERPRETER); break; case Instruction::MONITOR_ENTER: work_line_->PushMonitor(this, inst->VRegA_11x(), work_insn_idx_); diff --git a/test/979-const-method-handle/expected.txt b/test/979-const-method-handle/expected.txt index bc943e368e..2d169b9508 100644 --- a/test/979-const-method-handle/expected.txt +++ b/test/979-const-method-handle/expected.txt @@ -1,4 +1,6 @@ (int,Integer,System)String +repeatConstMethodType0((int,Integer,System)String) +repeatConstMethodType1((LocalClass)void) Hello World! And Hello Zog Hello World! And Hello Zorba name is HoverFly diff --git a/test/979-const-method-handle/src/Main.java b/test/979-const-method-handle/src/Main.java index 663814f232..79be913a67 100644 --- a/test/979-const-method-handle/src/Main.java +++ b/test/979-const-method-handle/src/Main.java @@ -26,59 +26,100 @@ class Main { throw new Error("Unreachable"); } + private static class LocalClass { + public LocalClass() {} + + private int field; + } + @ConstantMethodType( - returnType = String.class, - parameterTypes = {int.class, Integer.class, System.class} - ) + returnType = String.class, + parameterTypes = {int.class, Integer.class, System.class}) private static MethodType methodType0() { unreachable(); return null; } + @ConstantMethodType( + returnType = void.class, + parameterTypes = {LocalClass.class}) + private static MethodType methodType1() { + unreachable(); + return null; + } + + private static void repeatConstMethodType0(MethodType expected) { + System.out.print("repeatConstMethodType0("); + System.out.print(expected); + System.out.println(")"); + for (int i = 0; i < 12000; ++i) { + MethodType actual = methodType0(); + if (!actual.equals(expected)) { + System.out.print("Expected: "); + System.out.println(expected); + System.out.print("Actual: "); + System.out.println(actual); + unreachable(); + } + } + } + + private static void repeatConstMethodType1(MethodType expected) { + System.out.print("repeatConstMethodType1("); + System.out.print(expected); + System.out.println(")"); + for (int i = 0; i < 12000; ++i) { + MethodType actual = methodType1(); + if (!actual.equals(expected)) { + System.out.print("Expected: "); + System.out.println(expected); + System.out.print("Actual: "); + System.out.println(actual); + unreachable(); + } + } + } + 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" - ) + 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;" - ) + 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" - ) + 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" - ) + kind = ConstantMethodHandle.STATIC_PUT, + owner = "java/lang/Math", + fieldOrMethodName = "E", + descriptor = "D") private static MethodHandle putMathE() { unreachable(); return null; @@ -86,6 +127,9 @@ class Main { public static void main(String[] args) throws Throwable { System.out.println(methodType0()); + repeatConstMethodType0( + MethodType.methodType(String.class, int.class, Integer.class, System.class)); + repeatConstMethodType1(MethodType.methodType(void.class, LocalClass.class)); printHelloHandle().invokeExact("Zog"); printHelloHandle().invokeExact("Zorba"); setNameHandle().invokeExact("HoverFly"); -- GitLab From 2b80ed488c497393270c98f7a767d8495166db8e Mon Sep 17 00:00:00 2001 From: David Sehr Date: Tue, 8 May 2018 08:58:15 -0700 Subject: [PATCH 373/749] Make dexlayout and profman build without libart Use libprofile and libartbase to remove the dependencies on libart for dexlayout and profman. dexdiag remains connected to libart because of vdex file APIs. Bug: 78652467 Test: make -j 40 test-art-host-gtest Change-Id: Ie4d58e7e75aa725a6d453a9d4c7fefd868aa7b2d --- dexlayout/Android.bp | 9 ++--- dexlayout/dexlayout.cc | 32 +++++++++++++++--- dexlayout/dexlayout_main.cc | 8 +++-- libprofile/Android.bp | 1 - profman/Android.bp | 4 +-- profman/profman.cc | 67 +++++++++++++++++++++++-------------- 6 files changed, 82 insertions(+), 39 deletions(-) diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp index b009774582..5285c08cc2 100644 --- a/dexlayout/Android.bp +++ b/dexlayout/Android.bp @@ -27,6 +27,7 @@ art_cc_defaults { ], export_include_dirs: ["."], shared_libs: [ + "libartbase", "libbase", ], static_libs: ["libz"], @@ -39,7 +40,6 @@ art_cc_library { "dex2oat-pgo-defaults", ], shared_libs: [ - "libart", "libdexfile", "libprofile", ], @@ -60,7 +60,6 @@ art_cc_library { "art_debug_defaults", ], shared_libs: [ - "libartd", "libdexfiled", "libprofiled", ], @@ -80,8 +79,9 @@ art_cc_binary { name: "dexlayout", defaults: ["dexlayout-defaults"], shared_libs: [ + "libartbase", + "libdexfile", "libprofile", - "libart", "libart-dexlayout", ], } @@ -93,8 +93,9 @@ art_cc_binary { "dexlayout-defaults", ], shared_libs: [ + "libartbased", + "libdexfiled", "libprofiled", - "libartd", "libartd-dexlayout", ], } diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index 62dd1a9554..03dfee319e 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -36,8 +36,8 @@ #include "base/logging.h" // For VLOG_IS_ON. #include "base/mem_map.h" #include "base/os.h" +#include "base/unix_file/fd_file.h" #include "base/utils.h" -#include "dex/art_dex_file_loader.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_layout.h" @@ -1930,7 +1930,7 @@ bool DexLayout::ProcessDexFile(const char* file_name, std::string location = "memory mapped file for " + std::string(file_name); // Dex file verifier cannot handle compact dex. bool verify = options_.compact_dex_level_ == CompactDexLevel::kCompactDexLevelNone; - const ArtDexFileLoader dex_file_loader; + const DexFileLoader dex_file_loader; DexContainer::Section* const main_section = (*dex_container)->GetMainSection(); DexContainer::Section* const data_section = (*dex_container)->GetDataSection(); DCHECK_EQ(file_size, main_section->Size()) @@ -1980,10 +1980,32 @@ int DexLayout::ProcessFile(const char* file_name) { // all of which are Zip archives with "classes.dex" inside. const bool verify_checksum = !options_.ignore_bad_checksum_; std::string error_msg; - const ArtDexFileLoader dex_file_loader; + std::unique_ptr input_file(OS::OpenFileForReading(file_name)); + if (input_file == nullptr) { + LOG(ERROR) << "Could not open file " << file_name << " for reading"; + return -1; + } + std::unique_ptr mmap(MemMap::MapFile(input_file->GetLength(), + PROT_READ, + MAP_PRIVATE, + input_file->Fd(), + /*start*/0, + /*low_4gb*/false, + file_name, + &error_msg)); + if (mmap == nullptr) { + LOG(ERROR) << "MemMap failed for '" << file_name << "' " << error_msg; + return -1; + } + const DexFileLoader dex_file_loader; std::vector> dex_files; - if (!dex_file_loader.Open( - file_name, file_name, /* verify */ true, verify_checksum, &error_msg, &dex_files)) { + if (!dex_file_loader.OpenAll(mmap->Begin(), + mmap->Size(), + file_name, + /*verify*/true, + verify_checksum, + &error_msg, + &dex_files)) { // Display returned error message to user. Note that this error behavior // differs from the error messages shown by the original Dalvik dexdump. LOG(ERROR) << error_msg; diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc index 185c1420ab..3f92d501f6 100644 --- a/dexlayout/dexlayout_main.cc +++ b/dexlayout/dexlayout_main.cc @@ -34,7 +34,6 @@ #include "base/logging.h" // For InitLogging. #include "base/mem_map.h" #include "profile/profile_compilation_info.h" -#include "runtime.h" namespace art { @@ -66,12 +65,17 @@ static void Usage(void) { LOG(ERROR) << " -x : compact dex generation level, either 'none' or 'fast'"; } +NO_RETURN static void Abort(const char* msg) { + LOG(ERROR) << "Aborted: " << msg; + exit(1); +} + /* * Main driver of the dexlayout utility. */ int DexlayoutDriver(int argc, char** argv) { // Art specific set up. - InitLogging(argv, Runtime::Abort); + InitLogging(argv, Abort); MemMap::Init(); Options options; diff --git a/libprofile/Android.bp b/libprofile/Android.bp index bcb90cb680..5afe73b353 100644 --- a/libprofile/Android.bp +++ b/libprofile/Android.bp @@ -40,7 +40,6 @@ cc_defaults { ], }, }, - //generated_sources: ["art_libartbase_operator_srcs"], cflags: ["-DBUILDING_LIBART=1"], shared_libs: [ "libartbase", diff --git a/profman/Android.bp b/profman/Android.bp index 3c8c72c34a..c9c92e6685 100644 --- a/profman/Android.bp +++ b/profman/Android.bp @@ -39,7 +39,7 @@ art_cc_binary { name: "profman", defaults: ["profman-defaults"], shared_libs: [ - "libart", + "libartbase", "libprofile", "libdexfile", ], @@ -52,7 +52,7 @@ art_cc_binary { "profman-defaults", ], shared_libs: [ - "libartd", + "libartbased", "libprofiled", "libdexfiled", ], diff --git a/profman/profman.cc b/profman/profman.cc index cd88d03929..c16fadd828 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -40,7 +41,6 @@ #include "base/utils.h" #include "base/zip_archive.h" #include "boot_image_profile.h" -#include "dex/art_dex_file_loader.h" #include "dex/bytecode_utils.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_file.h" @@ -49,7 +49,6 @@ #include "dex/type_reference.h" #include "profile/profile_compilation_info.h" #include "profile_assistant.h" -#include "runtime.h" namespace art { @@ -177,6 +176,11 @@ static constexpr char kMethodFlagStringHot = 'H'; static constexpr char kMethodFlagStringStartup = 'S'; static constexpr char kMethodFlagStringPostStartup = 'P'; +NO_RETURN static void Abort(const char* msg) { + LOG(ERROR) << "Aborted: " << msg; + exit(1); +} + // TODO(calin): This class has grown too much from its initial design. Split the functionality // into smaller, more contained pieces. class ProfMan FINAL { @@ -202,8 +206,8 @@ class ProfMan FINAL { original_argc = argc; original_argv = argv; - Locks::Init(); - InitLogging(argv, Runtime::Abort); + MemMap::Init(); + InitLogging(argv, Abort); // Skip over the command name. argv++; @@ -413,36 +417,49 @@ class ProfMan FINAL { } static constexpr bool kVerifyChecksum = true; for (size_t i = 0; i < dex_locations_.size(); ++i) { - std::string error_msg; - const ArtDexFileLoader dex_file_loader; - std::vector> dex_files_for_location; + std::unique_ptr apk_file; // We do not need to verify the apk for processing profiles. if (use_apk_fd_list) { - if (dex_file_loader.OpenZip(apks_fd_[i], - dex_locations_[i], - /* verify */ false, - kVerifyChecksum, - &error_msg, - &dex_files_for_location)) { - } else { - LOG(ERROR) << "OpenZip failed for '" << dex_locations_[i] << "' " << error_msg; - return false; - } + apk_file.reset(new File(apks_fd_[i], false/*checkUsage*/)); } else { - if (dex_file_loader.Open(apk_files_[i].c_str(), - dex_locations_[i], - /* verify */ false, - kVerifyChecksum, - &error_msg, - &dex_files_for_location)) { - } else { - LOG(ERROR) << "Open failed for '" << dex_locations_[i] << "' " << error_msg; + apk_file.reset(new File(apk_files_[i], O_RDONLY, false/*checkUsage*/)); + if (apk_file == nullptr) { + LOG(ERROR) << "Open failed for '" << dex_locations_[i] << "' "; return false; } } + std::string error_msg; + std::unique_ptr mmap(MemMap::MapFile(apk_file->GetLength(), + PROT_READ, + MAP_PRIVATE, + apk_file->Fd(), + /*start*/0, + /*low_4gb*/false, + dex_locations_[i].c_str(), + &error_msg)); + if (mmap == nullptr) { + LOG(ERROR) << "MemMap failed for '" << dex_locations_[i] << "' " << error_msg; + return false; + } + const DexFileLoader dex_file_loader; + std::vector> dex_files_for_location; + if (!dex_file_loader.OpenAll(mmap->Begin(), + mmap->Size(), + dex_locations_[i], + /* verify */ false, + kVerifyChecksum, + &error_msg, + &dex_files_for_location)) { + LOG(ERROR) << "OpenAll failed for '" << dex_locations_[i] << "' " << error_msg; + return false; + } for (std::unique_ptr& dex_file : dex_files_for_location) { process_fn(std::move(dex_file)); } + // Leak apk_file and mmap for now. + // TODO: close fds, etc. + apk_file.release(); + mmap.release(); } return true; } -- GitLab From e11945792a98282c5383bf514d466e6346adb3a2 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Thu, 10 May 2018 17:57:20 +0100 Subject: [PATCH 374/749] Fix the target gtest witness filename expansion in Makefile rules. Also fix the path to the valgrind binary in target valgrind gtest rules. Test: Run ART gtests with chroot Test: Run ART gtests without chroot Bug: 34729697 Change-Id: I164185ea63eee1b19f039436f99933c040f4c66e --- build/Android.gtest.mk | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index be040a98da..3daaf0156e 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -483,18 +483,21 @@ maybe_chroot_command := chroot $(ART_TEST_CHROOT) endif # File witnessing the success of the gtest, the presence of which means the gtest's success. -gtest_witness := $(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID +gtest_witness := \ + $(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$(gtest_rule)-$$$$PPID + +$$(gtest_rule): GTEST_WITNESS := $$(gtest_witness) .PHONY: $$(gtest_rule) $$(gtest_rule): test-art-target-sync - $(hide) adb shell touch $(gtest_witness) - $(hide) adb shell rm $(gtest_witness) + $(hide) adb shell touch $$(GTEST_WITNESS) + $(hide) adb shell rm $$(GTEST_WITNESS) $(hide) adb shell chmod 755 $(maybe_art_test_chroot)$$(PRIVATE_TARGET_EXE) $(hide) $$(call ART_TEST_SKIP,$$@) && \ (adb shell "$(maybe_chroot_command) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) $$(PRIVATE_TARGET_EXE) \ - && touch $(gtest_witness)" \ - && (adb pull $(gtest_witness) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ + && touch $$(GTEST_WITNESS)" \ + && (adb pull $$(GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ || $$(call ART_TEST_FAILED,$$@)) $(hide) rm -f /tmp/$$@-$$$$PPID @@ -502,20 +505,27 @@ $$(gtest_rule): test-art-target-sync ART_TEST_TARGET_GTEST_RULES += $$(gtest_rule) ART_TEST_TARGET_GTEST_$(1)_RULES += $$(gtest_rule) +# File witnessing the success of the Valgrind gtest, the presence of which means the gtest's +# success. +valgrind_gtest_witness := \ + $(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/valgrind-$$(gtest_rule)-$$$$PPID + +valgrind-$$(gtest_rule): VALGRIND_GTEST_WITNESS := $$(valgrind_gtest_witness) + .PHONY: valgrind-$$(gtest_rule) valgrind-$$(gtest_rule): $(ART_VALGRIND_TARGET_DEPENDENCIES) test-art-target-sync - $(hide) adb shell touch $(gtest_witness) - $(hide) adb shell rm $(gtest_witness) + $(hide) adb shell touch $$(VALGRIND_GTEST_WITNESS) + $(hide) adb shell rm $$(VALGRIND_GTEST_WITNESS) $(hide) adb shell chmod 755 $(maybe_art_test_chroot)$$(PRIVATE_TARGET_EXE) $(hide) $$(call ART_TEST_SKIP,$$@) && \ (adb shell "$(maybe_chroot_command) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \ - $$$$ANDROID_ROOT/bin/valgrind \ + $(ART_GTEST_TARGET_ANDROID_ROOT)/bin/valgrind \ --leak-check=full --error-exitcode=1 --workaround-gcc296-bugs=yes \ --suppressions=$(ART_TARGET_TEST_DIR)/valgrind-target-suppressions.txt \ --num-callers=50 --show-mismatched-frees=no $$(PRIVATE_TARGET_EXE) \ - && touch $(gtest_witness)" \ - && (adb pull $(gtest_witness) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ + && touch $$(VALGRIND_GTEST_WITNESS)" \ + && (adb pull $$(VALGRIND_GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ || $$(call ART_TEST_FAILED,$$@)) $(hide) rm -f /tmp/$$@-$$$$PPID @@ -525,6 +535,7 @@ valgrind-$$(gtest_rule): $(ART_VALGRIND_TARGET_DEPENDENCIES) test-art-target-syn ART_TEST_TARGET_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule) # Clear locally defined variables. + valgrind_gtest_witness := gtest_witness := maybe_chroot_command := maybe_art_test_chroot := -- GitLab From 0d0f3164160e50ddb78022f662c5438fc167f50d Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 10 May 2018 12:55:40 +0100 Subject: [PATCH 375/749] Handle multidex in LocationIsOnSystemFramework. bug: 79111915 Test: art_dex_file_loader_test.cc Change-Id: I507ad98c62b4c589196685d74abdeaf748502a61 --- libdexfile/dex/dex_file_loader.cc | 2 + libdexfile/dex/dex_file_loader.h | 2 +- runtime/base/file_utils.cc | 15 ++---- runtime/dex/art_dex_file_loader.cc | 5 +- runtime/dex/art_dex_file_loader_test.cc | 61 +++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 12 deletions(-) diff --git a/libdexfile/dex/dex_file_loader.cc b/libdexfile/dex/dex_file_loader.cc index 1e0f5ac6ae..457addf114 100644 --- a/libdexfile/dex/dex_file_loader.cc +++ b/libdexfile/dex/dex_file_loader.cc @@ -191,6 +191,8 @@ std::string DexFileLoader::GetDexCanonicalLocation(const char* dex_location) { std::string base_location = GetBaseLocation(dex_location); const char* suffix = dex_location + base_location.size(); DCHECK(suffix[0] == 0 || suffix[0] == kMultiDexSeparator); + // Warning: Bionic implementation of realpath() allocates > 12KB on the stack. + // Do not run this code on a small stack, e.g. in signal handler. UniqueCPtr path(realpath(base_location.c_str(), nullptr)); if (path != nullptr && path.get() != base_location) { return std::string(path.get()) + suffix; diff --git a/libdexfile/dex/dex_file_loader.h b/libdexfile/dex/dex_file_loader.h index 28cdfc13ce..01532203eb 100644 --- a/libdexfile/dex/dex_file_loader.h +++ b/libdexfile/dex/dex_file_loader.h @@ -71,7 +71,7 @@ class DexFileLoader { // of the dex file. In the second case (oat) it will include the file name // and possibly some multidex annotation to uniquely identify it. // canonical_dex_location: - // the dex_location where it's file name part has been made canonical. + // the dex_location where its file name part has been made canonical. static std::string GetDexCanonicalLocation(const char* dex_location); // For normal dex files, location and base location coincide. If a dex file is part of a multidex diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc index 7921985b15..537216c198 100644 --- a/runtime/base/file_utils.cc +++ b/runtime/base/file_utils.cc @@ -261,12 +261,12 @@ std::string ReplaceFileExtension(const std::string& filename, const std::string& } } -bool LocationIsOnSystem(const char* location) { - UniqueCPtr path(realpath(location, nullptr)); - return path != nullptr && android::base::StartsWith(path.get(), GetAndroidRoot().c_str()); +bool LocationIsOnSystem(const char* path) { + UniqueCPtr full_path(realpath(path, nullptr)); + return path != nullptr && android::base::StartsWith(full_path.get(), GetAndroidRoot().c_str()); } -bool LocationIsOnSystemFramework(const char* location) { +bool LocationIsOnSystemFramework(const char* full_path) { std::string error_msg; std::string root_path = GetAndroidRootSafe(&error_msg); if (root_path.empty()) { @@ -275,12 +275,7 @@ bool LocationIsOnSystemFramework(const char* location) { return false; } std::string framework_path = root_path + "/framework/"; - - // Warning: Bionic implementation of realpath() allocates > 12KB on the stack. - // Do not run this code on a small stack, e.g. in signal handler. - UniqueCPtr path(realpath(location, nullptr)); - return path != nullptr && - android::base::StartsWith(path.get(), framework_path.c_str()); + return android::base::StartsWith(full_path, framework_path); } } // namespace art diff --git a/runtime/dex/art_dex_file_loader.cc b/runtime/dex/art_dex_file_loader.cc index 415e451098..392ce1e7f5 100644 --- a/runtime/dex/art_dex_file_loader.cc +++ b/runtime/dex/art_dex_file_loader.cc @@ -534,7 +534,10 @@ std::unique_ptr ArtDexFileLoader::OpenCommon(const uint8_t* base, // Check if this dex file is located in the framework directory. // If it is, set a flag on the dex file. This is used by hidden API // policy decision logic. - if (dex_file != nullptr && LocationIsOnSystemFramework(location.c_str())) { + // Location can contain multidex suffix, so fetch its canonical version. Note + // that this will call `realpath`. + std::string path = DexFileLoader::GetDexCanonicalLocation(location.c_str()); + if (dex_file != nullptr && LocationIsOnSystemFramework(path.c_str())) { dex_file->SetIsPlatformDexFile(); } diff --git a/runtime/dex/art_dex_file_loader_test.cc b/runtime/dex/art_dex_file_loader_test.cc index aee397b65b..274a6df702 100644 --- a/runtime/dex/art_dex_file_loader_test.cc +++ b/runtime/dex/art_dex_file_loader_test.cc @@ -49,20 +49,31 @@ class ArtDexFileLoaderTest : public CommonRuntimeTest { CommonRuntimeTest::SetUp(); std::string dex_location = GetTestDexFileName("Main"); + std::string multidex_location = GetTestDexFileName("MultiDex"); data_location_path_ = android_data_ + "/foo.jar"; system_location_path_ = GetAndroidRoot() + "/foo.jar"; system_framework_location_path_ = GetAndroidRoot() + "/framework/foo.jar"; + data_multi_location_path_ = android_data_ + "/multifoo.jar"; + system_multi_location_path_ = GetAndroidRoot() + "/multifoo.jar"; + system_framework_multi_location_path_ = GetAndroidRoot() + "/framework/multifoo.jar"; Copy(dex_location, data_location_path_); Copy(dex_location, system_location_path_); Copy(dex_location, system_framework_location_path_); + + Copy(multidex_location, data_multi_location_path_); + Copy(multidex_location, system_multi_location_path_); + Copy(multidex_location, system_framework_multi_location_path_); } virtual void TearDown() { remove(data_location_path_.c_str()); remove(system_location_path_.c_str()); remove(system_framework_location_path_.c_str()); + remove(data_multi_location_path_.c_str()); + remove(system_multi_location_path_.c_str()); + remove(system_framework_multi_location_path_.c_str()); CommonRuntimeTest::TearDown(); } @@ -70,6 +81,9 @@ class ArtDexFileLoaderTest : public CommonRuntimeTest { std::string data_location_path_; std::string system_location_path_; std::string system_framework_location_path_; + std::string data_multi_location_path_; + std::string system_multi_location_path_; + std::string system_framework_multi_location_path_; }; // TODO: Port OpenTestDexFile(s) need to be ported to use non-ART utilities, and @@ -390,6 +404,53 @@ TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { for (std::unique_ptr& dex_file : dex_files) { ASSERT_TRUE(dex_file->IsPlatformDexFile()); } + + dex_files.clear(); + + // Load multidex file from a non-system directory and check that it is not flagged as framework. + success = loader.Open(data_multi_location_path_.c_str(), + data_multi_location_path_, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success) << error_msg; + ASSERT_GT(dex_files.size(), 1u); + for (std::unique_ptr& dex_file : dex_files) { + ASSERT_FALSE(dex_file->IsPlatformDexFile()); + } + + dex_files.clear(); + + // Load multidex file from a system, non-framework directory and check that it is not flagged + // as framework. + success = loader.Open(system_multi_location_path_.c_str(), + system_multi_location_path_, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success); + ASSERT_GT(dex_files.size(), 1u); + for (std::unique_ptr& dex_file : dex_files) { + ASSERT_FALSE(dex_file->IsPlatformDexFile()); + } + + dex_files.clear(); + + // Load multidex file from a system/framework directory and check that it is flagged as a + // framework dex. + success = loader.Open(system_framework_multi_location_path_.c_str(), + system_framework_multi_location_path_, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success); + ASSERT_GT(dex_files.size(), 1u); + for (std::unique_ptr& dex_file : dex_files) { + ASSERT_TRUE(dex_file->IsPlatformDexFile()); + } } } // namespace art -- GitLab From d8860b42e47d48fcc47db9d0daf5a1b9432180a1 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 2 May 2018 14:58:12 +0100 Subject: [PATCH 376/749] Do not load app image for class collisions Even for special shared libraries that are compiled with '--class-loader-context=&', we must reject the app image if there are duplicate classes. In the case where "&" is not specified, avoid the collision check. This is safe since the class loader context check was actually run. Test: 172-app-image-twice Bug: 77342775 Bug: 79200502 Change-Id: Idc2d59166680948d4d34d0f224491f77ecad2974 --- dex2oat/dex2oat.cc | 4 +- runtime/Android.bp | 1 + runtime/class_loader_context.cc | 31 +++--- runtime/class_loader_context.h | 22 +++-- runtime/class_loader_context_test.cc | 44 ++++++--- runtime/oat_file_assistant.cc | 4 +- runtime/oat_file_manager.cc | 96 ++++++++++++++----- runtime/oat_file_manager.h | 26 ++++- test/172-app-image-twice/check | 18 ++++ test/172-app-image-twice/debug_print_class.cc | 33 +++++++ test/172-app-image-twice/expected.txt | 1 + test/172-app-image-twice/info.txt | 1 + test/172-app-image-twice/profile | 1 + test/172-app-image-twice/run | 20 ++++ test/172-app-image-twice/src/Main.java | 48 ++++++++++ test/172-app-image-twice/src/TestClass.java | 18 ++++ test/Android.bp | 1 + test/knownfailures.json | 1 + 18 files changed, 306 insertions(+), 64 deletions(-) create mode 100755 test/172-app-image-twice/check create mode 100644 test/172-app-image-twice/debug_print_class.cc create mode 100644 test/172-app-image-twice/expected.txt create mode 100644 test/172-app-image-twice/info.txt create mode 100644 test/172-app-image-twice/profile create mode 100644 test/172-app-image-twice/run create mode 100644 test/172-app-image-twice/src/Main.java create mode 100644 test/172-app-image-twice/src/TestClass.java diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 63518be15f..6b65aca943 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1257,10 +1257,10 @@ class Dex2Oat FINAL { if (stored_class_loader_context_ == nullptr) { Usage("Option --stored-class-loader-context has an incorrect format: %s", stored_context_arg.c_str()); - } else if (!class_loader_context_->VerifyClassLoaderContextMatch( + } else if (class_loader_context_->VerifyClassLoaderContextMatch( stored_context_arg, /*verify_names*/ false, - /*verify_checksums*/ false)) { + /*verify_checksums*/ false) != ClassLoaderContext::VerificationResult::kVerifies) { Usage( "Option --stored-class-loader-context '%s' mismatches --class-loader-context '%s'", stored_context_arg.c_str(), diff --git a/runtime/Android.bp b/runtime/Android.bp index 116453b1bf..64e6796ba0 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -422,6 +422,7 @@ gensrcs { srcs: [ "arch/instruction_set.h", "base/mutex.h", + "class_loader_context.h", "class_status.h", "debugger.h", "gc_root.h", diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index 98174142f1..2bd541118b 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -672,9 +672,10 @@ static bool IsAbsoluteLocation(const std::string& location) { return !location.empty() && location[0] == '/'; } -bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec, - bool verify_names, - bool verify_checksums) const { +ClassLoaderContext::VerificationResult ClassLoaderContext::VerifyClassLoaderContextMatch( + const std::string& context_spec, + bool verify_names, + bool verify_checksums) const { if (verify_names || verify_checksums) { DCHECK(dex_files_open_attempted_); DCHECK(dex_files_open_result_); @@ -683,15 +684,21 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex ClassLoaderContext expected_context; if (!expected_context.Parse(context_spec, verify_checksums)) { LOG(WARNING) << "Invalid class loader context: " << context_spec; - return false; + return VerificationResult::kMismatch; } // Special shared library contexts always match. They essentially instruct the runtime // to ignore the class path check because the oat file is known to be loaded in different // contexts. OatFileManager will further verify if the oat file can be loaded based on the // collision check. - if (special_shared_library_ || expected_context.special_shared_library_) { - return true; + if (expected_context.special_shared_library_) { + // Special case where we are the only entry in the class path. + if (class_loader_chain_.size() == 1 && class_loader_chain_[0].classpath.size() == 0) { + return VerificationResult::kVerifies; + } + return VerificationResult::kForcedToSkipChecks; + } else if (special_shared_library_) { + return VerificationResult::kForcedToSkipChecks; } if (expected_context.class_loader_chain_.size() != class_loader_chain_.size()) { @@ -699,7 +706,7 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << expected_context.class_loader_chain_.size() << ", actual=" << class_loader_chain_.size() << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } for (size_t i = 0; i < class_loader_chain_.size(); i++) { @@ -710,14 +717,14 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << ". expected=" << GetClassLoaderTypeName(expected_info.type) << ", found=" << GetClassLoaderTypeName(info.type) << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } if (info.classpath.size() != expected_info.classpath.size()) { LOG(WARNING) << "ClassLoaderContext classpath size mismatch for position " << i << ". expected=" << expected_info.classpath.size() << ", found=" << info.classpath.size() << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } if (verify_checksums) { @@ -772,7 +779,7 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << ". expected=" << expected_info.classpath[k] << ", found=" << info.classpath[k] << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } // Compare the checksums. @@ -781,11 +788,11 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << ". expected=" << expected_info.checksums[k] << ", found=" << info.checksums[k] << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } } } - return true; + return VerificationResult::kVerifies; } jclass ClassLoaderContext::GetClassLoaderClass(ClassLoaderType type) { diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h index 1c83007f41..a4268aa09a 100644 --- a/runtime/class_loader_context.h +++ b/runtime/class_loader_context.h @@ -22,8 +22,10 @@ #include "arch/instruction_set.h" #include "base/dchecked_vector.h" +#include "dex/dex_file.h" #include "handle_scope.h" #include "mirror/class_loader.h" +#include "oat_file.h" #include "scoped_thread_state_change.h" namespace art { @@ -34,6 +36,18 @@ class OatFile; // Utility class which holds the class loader context used during compilation/verification. class ClassLoaderContext { public: + enum class VerificationResult { + kVerifies, + kForcedToSkipChecks, + kMismatch, + }; + + enum ClassLoaderType { + kInvalidClassLoader = 0, + kPathClassLoader = 1, + kDelegateLastClassLoader = 2 + }; + ~ClassLoaderContext(); // Opens requested class path files and appends them to ClassLoaderInfo::opened_dex_files. @@ -109,7 +123,7 @@ class ClassLoaderContext { // This should be called after OpenDexFiles(). // Names are only verified if verify_names is true. // Checksums are only verified if verify_checksums is true. - bool VerifyClassLoaderContextMatch(const std::string& context_spec, + VerificationResult VerifyClassLoaderContextMatch(const std::string& context_spec, bool verify_names = true, bool verify_checksums = true) const; @@ -141,12 +155,6 @@ class ClassLoaderContext { static std::unique_ptr Default(); private: - enum ClassLoaderType { - kInvalidClassLoader = 0, - kPathClassLoader = 1, - kDelegateLastClassLoader = 2 - }; - struct ClassLoaderInfo { // The type of this class loader. ClassLoaderType type; diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc index 4689ae4c3f..5e3f48c100 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -608,6 +608,17 @@ TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) { VerifyClassLoaderPCLFromTestDex(context.get(), 3, "ForClassLoaderA"); } + +TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextFirstElement) { + std::string context_spec = "PCL[]"; + std::unique_ptr context = ParseContextWithChecksums(context_spec); + ASSERT_TRUE(context != nullptr); + PretendContextOpenedDexFiles(context.get()); + // Ensure that the special shared library marks as verified for the first thing in the class path. + ASSERT_EQ(context->VerifyClassLoaderContextMatch(OatFile::kSpecialSharedLibrary), + ClassLoaderContext::VerificationResult::kVerifies); +} + TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) { std::string context_spec = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890]"; std::unique_ptr context = ParseContextWithChecksums(context_spec); @@ -619,28 +630,36 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) { VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex"); VerifyClassLoaderDLC(context.get(), 1, "c.dex"); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_spec)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec), + ClassLoaderContext::VerificationResult::kVerifies); std::string wrong_class_loader_type = "PCL[a.dex*123:b.dex*456];PCL[c.dex*890]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_type)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_type), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_class_loader_order = "DLC[c.dex*890];PCL[a.dex*123:b.dex*456]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_order)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_order), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_classpath_order = "PCL[b.dex*456:a.dex*123];DLC[c.dex*890]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_classpath_order)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_classpath_order), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_checksum = "PCL[a.dex*999:b.dex*456];DLC[c.dex*890]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_checksum)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_checksum), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_extra_class_loader = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890];PCL[d.dex*321]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_extra_classpath = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890:d.dex*321]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_classpath)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_classpath), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_spec = "PCL[a.dex*999:b.dex*456];DLC["; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_spec)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_spec), + ClassLoaderContext::VerificationResult::kMismatch); } TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { @@ -652,7 +671,8 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { std::unique_ptr context = CreateContextForClassLoader(class_loader_d); std::string context_with_no_base_dir = context->EncodeContextForOatFile(""); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_no_base_dir)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_no_base_dir), + ClassLoaderContext::VerificationResult::kVerifies); std::string dex_location = GetTestDexFileName("ForClassLoaderA"); size_t pos = dex_location.rfind('/'); @@ -661,7 +681,8 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { std::string context_with_base_dir = context->EncodeContextForOatFile(parent); ASSERT_NE(context_with_base_dir, context_with_no_base_dir); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_base_dir)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_base_dir), + ClassLoaderContext::VerificationResult::kVerifies); } TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultidex) { @@ -669,7 +690,8 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultide std::unique_ptr context = CreateContextForClassLoader(class_loader); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile(""))); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")), + ClassLoaderContext::VerificationResult::kVerifies); } } // namespace art diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 9c8b6512a7..241102ea83 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -1217,7 +1217,9 @@ bool OatFileAssistant::OatFileInfo::ClassLoaderContextIsOkay(ClassLoaderContext* return false; } - bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext()); + + bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext()) == + ClassLoaderContext::VerificationResult::kVerifies; if (!result) { VLOG(oat) << "ClassLoaderContext check failed. Context was " << file->GetClassLoaderContext() diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 16e6cf375c..59a1045ba2 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -276,9 +276,19 @@ static void AddNext(/*inout*/DexFileAndClassPair& original, } } -static bool CollisionCheck(std::vector& dex_files_loaded, - std::vector& dex_files_unloaded, - std::string* error_msg /*out*/) { +static bool CheckClassCollision(const OatFile* oat_file, + const ClassLoaderContext* context, + std::string* error_msg /*out*/) { + std::vector dex_files_loaded = context->FlattenOpenedDexFiles(); + + // Vector that holds the newly opened dex files live, this is done to prevent leaks. + std::vector> opened_dex_files; + + ScopedTrace st("Collision check"); + // Add dex files from the oat file to check. + std::vector dex_files_unloaded; + AddDexFilesFromOat(oat_file, &dex_files_unloaded, &opened_dex_files); + // Generate type index information for each dex file. std::vector loaded_types; for (const DexFile* dex_file : dex_files_loaded) { @@ -355,9 +365,10 @@ static bool CollisionCheck(std::vector& dex_files_loaded, // against the following top element. If the descriptor is the same, it is now checked whether // the two elements agree on whether their dex file was from an already-loaded oat-file or the // new oat file. Any disagreement indicates a collision. -bool OatFileManager::HasCollisions(const OatFile* oat_file, - const ClassLoaderContext* context, - std::string* error_msg /*out*/) const { +OatFileManager::CheckCollisionResult OatFileManager::CheckCollision( + const OatFile* oat_file, + const ClassLoaderContext* context, + /*out*/ std::string* error_msg) const { DCHECK(oat_file != nullptr); DCHECK(error_msg != nullptr); @@ -367,28 +378,59 @@ bool OatFileManager::HasCollisions(const OatFile* oat_file, // Note that this has correctness implications as we cannot guarantee that the class resolution // used during compilation is OK (b/37777332). if (context == nullptr) { - LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader"; - return false; + LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader"; + return CheckCollisionResult::kSkippedUnsupportedClassLoader; } - // If the pat file loading context matches the context used during compilation then we accept + // If the oat file loading context matches the context used during compilation then we accept // the oat file without addition checks - if (context->VerifyClassLoaderContextMatch(oat_file->GetClassLoaderContext())) { - return false; + ClassLoaderContext::VerificationResult result = context->VerifyClassLoaderContextMatch( + oat_file->GetClassLoaderContext(), + /*verify_names*/ true, + /*verify_checksums*/ true); + switch (result) { + case ClassLoaderContext::VerificationResult::kForcedToSkipChecks: + return CheckCollisionResult::kSkippedClassLoaderContextSharedLibrary; + case ClassLoaderContext::VerificationResult::kMismatch: + // Mismatched context, do the actual collision check. + break; + case ClassLoaderContext::VerificationResult::kVerifies: + return CheckCollisionResult::kNoCollisions; } // The class loader context does not match. Perform a full duplicate classes check. + return CheckClassCollision(oat_file, context, error_msg) + ? CheckCollisionResult::kPerformedHasCollisions : CheckCollisionResult::kNoCollisions; +} - std::vector dex_files_loaded = context->FlattenOpenedDexFiles(); - - // Vector that holds the newly opened dex files live, this is done to prevent leaks. - std::vector> opened_dex_files; +bool OatFileManager::AcceptOatFile(CheckCollisionResult result) const { + // Take the file only if it has no collisions, or we must take it because of preopting. + // Also accept oat files for shared libraries and unsupported class loaders. + return result != CheckCollisionResult::kPerformedHasCollisions; +} - ScopedTrace st("Collision check"); - // Add dex files from the oat file to check. - std::vector dex_files_unloaded; - AddDexFilesFromOat(oat_file, &dex_files_unloaded, &opened_dex_files); - return CollisionCheck(dex_files_loaded, dex_files_unloaded, error_msg); +bool OatFileManager::ShouldLoadAppImage(CheckCollisionResult check_collision_result, + const OatFile* source_oat_file, + ClassLoaderContext* context, + std::string* error_msg) { + Runtime* const runtime = Runtime::Current(); + if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) { + // If we verified the class loader context (skipping due to the special marker doesn't + // count), then also avoid the collision check. + bool load_image = check_collision_result == CheckCollisionResult::kNoCollisions; + // If we skipped the collision check, we need to reverify to be sure its OK to load the + // image. + if (!load_image && + check_collision_result == + CheckCollisionResult::kSkippedClassLoaderContextSharedLibrary) { + // We can load the app image only if there are no collisions. If we know the + // class loader but didn't do the full collision check in HasCollisions(), + // do it now. b/77342775 + load_image = !CheckClassCollision(source_oat_file, context, error_msg); + } + return load_image; + } + return false; } std::vector> OatFileManager::OpenDexFilesFromOat( @@ -473,16 +515,17 @@ std::vector> OatFileManager::OpenDexFilesFromOat( << reinterpret_cast(oat_file.get()) << " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")"; + CheckCollisionResult check_collision_result = CheckCollisionResult::kPerformedHasCollisions; if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) { // Prevent oat files from being loaded if no class_loader or dex_elements are provided. // This can happen when the deprecated DexFile.(String) is called directly, and it // could load oat files without checking the classpath, which would be incorrect. // Take the file only if it has no collisions, or we must take it because of preopting. - bool accept_oat_file = - !HasCollisions(oat_file.get(), context.get(), /*out*/ &error_msg); + check_collision_result = CheckCollision(oat_file.get(), context.get(), /*out*/ &error_msg); + bool accept_oat_file = AcceptOatFile(check_collision_result); if (!accept_oat_file) { // Failed the collision check. Print warning. - if (Runtime::Current()->IsDexFileFallbackEnabled()) { + if (runtime->IsDexFileFallbackEnabled()) { if (!oat_file_assistant.HasOriginalDexFiles()) { // We need to fallback but don't have original dex files. We have to // fallback to opening the existing oat file. This is potentially @@ -529,10 +572,11 @@ std::vector> OatFileManager::OpenDexFilesFromOat( // We need to throw away the image space if we are debuggable but the oat-file source of the // image is not otherwise we might get classes with inlined methods or other such things. std::unique_ptr image_space; - if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) { + if (ShouldLoadAppImage(check_collision_result, + source_oat_file, + context.get(), + &error_msg)) { image_space = oat_file_assistant.OpenImageSpace(source_oat_file); - } else { - image_space = nullptr; } if (image_space != nullptr) { ScopedObjectAccess soa(self); diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h index 038474e31f..80456e9b75 100644 --- a/runtime/oat_file_manager.h +++ b/runtime/oat_file_manager.h @@ -108,23 +108,39 @@ class OatFileManager { void SetOnlyUseSystemOatFiles(); private: + enum class CheckCollisionResult { + kSkippedUnsupportedClassLoader, + kSkippedClassLoaderContextSharedLibrary, + kNoCollisions, + kPerformedHasCollisions, + }; + // Check that the class loader context of the given oat file matches the given context. // This will perform a check that all class loaders in the chain have the same type and // classpath. // If the context is null (which means the initial class loader was null or unsupported) - // this returns false. + // this returns kSkippedUnsupportedClassLoader. // If the context does not validate the method will check for duplicate class definitions of // the given oat file against the oat files (either from the class loaders if possible or all // non-boot oat files otherwise). - // Return true if there are any class definition collisions in the oat_file. - bool HasCollisions(const OatFile* oat_file, - const ClassLoaderContext* context, - /*out*/ std::string* error_msg) const + // Return kPerformedHasCollisions if there are any class definition collisions in the oat_file. + CheckCollisionResult CheckCollision(const OatFile* oat_file, + const ClassLoaderContext* context, + /*out*/ std::string* error_msg) const REQUIRES(!Locks::oat_file_manager_lock_); const OatFile* FindOpenedOatFileFromOatLocationLocked(const std::string& oat_location) const REQUIRES(Locks::oat_file_manager_lock_); + // Return true if we should accept the oat file. + bool AcceptOatFile(CheckCollisionResult result) const; + + // Return true if we should attempt to load the app image. + bool ShouldLoadAppImage(CheckCollisionResult check_collision_result, + const OatFile* source_oat_file, + ClassLoaderContext* context, + std::string* error_msg); + std::set> oat_files_ GUARDED_BY(Locks::oat_file_manager_lock_); bool have_non_pic_oat_file_; diff --git a/test/172-app-image-twice/check b/test/172-app-image-twice/check new file mode 100755 index 0000000000..26a97a48ae --- /dev/null +++ b/test/172-app-image-twice/check @@ -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. + +# Remove all lines not containing "passed". +grep "^passed" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null diff --git a/test/172-app-image-twice/debug_print_class.cc b/test/172-app-image-twice/debug_print_class.cc new file mode 100644 index 0000000000..6c3de20f2d --- /dev/null +++ b/test/172-app-image-twice/debug_print_class.cc @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#include "debug_print.h" +#include "dex/dex_file.h" +#include "mirror/class-inl.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-current-inl.h" + +namespace art { + +extern "C" JNIEXPORT void JNICALL Java_Main_debugPrintClass(JNIEnv*, jclass, jclass cls) { + ScopedObjectAccess soa(Thread::Current()); + ObjPtr klass = soa.Decode(cls); + LOG(ERROR) << "klass: " << klass.Ptr() << " dex_file: " << klass->GetDexFile().GetLocation() + << "/" << static_cast(&klass->GetDexFile()) + << " " << DescribeSpace(klass); +} + +} // namespace art diff --git a/test/172-app-image-twice/expected.txt b/test/172-app-image-twice/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/172-app-image-twice/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/172-app-image-twice/info.txt b/test/172-app-image-twice/info.txt new file mode 100644 index 0000000000..028046e872 --- /dev/null +++ b/test/172-app-image-twice/info.txt @@ -0,0 +1 @@ +Regression test for loading the same app image twice. diff --git a/test/172-app-image-twice/profile b/test/172-app-image-twice/profile new file mode 100644 index 0000000000..70cb2efbb5 --- /dev/null +++ b/test/172-app-image-twice/profile @@ -0,0 +1 @@ +LTestClass; diff --git a/test/172-app-image-twice/run b/test/172-app-image-twice/run new file mode 100644 index 0000000000..d1ad043552 --- /dev/null +++ b/test/172-app-image-twice/run @@ -0,0 +1,20 @@ +#!/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. + +# Build an app image with TestClass (specified by profile) and class loader +# context that skips the duplicate class checks. +exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \ + -Xcompiler-option --class-loader-context=\& diff --git a/test/172-app-image-twice/src/Main.java b/test/172-app-image-twice/src/Main.java new file mode 100644 index 0000000000..a1c151a6bc --- /dev/null +++ b/test/172-app-image-twice/src/Main.java @@ -0,0 +1,48 @@ +/* + * 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.Method; + +public class Main { + private static String TEST_NAME = "172-app-image-twice"; + + public static void main(String args[]) throws Exception { + System.loadLibrary(args[0]); + + Class tc1 = Class.forName("TestClass"); + + String dexPath = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + ".jar"; + Class bdcl = Class.forName("dalvik.system.BaseDexClassLoader"); + Method addDexPathMethod = bdcl.getDeclaredMethod("addDexPath", String.class); + addDexPathMethod.invoke(Main.class.getClassLoader(), dexPath); + + Class tc2 = Class.forName("TestClass"); + + // Add extra logging to simulate libcore logging, this logging should not be compared + // against. + System.out.println("Extra logging"); + + if (tc1 != tc2) { + System.out.println("Class mismatch!"); + debugPrintClass(tc1); + debugPrintClass(tc2); + } else { + System.out.println("passed"); + } + } + + public static native void debugPrintClass(Class cls); +} diff --git a/test/172-app-image-twice/src/TestClass.java b/test/172-app-image-twice/src/TestClass.java new file mode 100644 index 0000000000..5381718f6e --- /dev/null +++ b/test/172-app-image-twice/src/TestClass.java @@ -0,0 +1,18 @@ +/* + * 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 TestClass { +} diff --git a/test/Android.bp b/test/Android.bp index 0c6b449877..76189f62a9 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -423,6 +423,7 @@ cc_defaults { "154-gc-loop/heap_interface.cc", "167-visit-locks/visit_locks.cc", "169-threadgroup-jni/jni_daemon_thread.cc", + "172-app-image-twice/debug_print_class.cc", "1945-proxy-method-arguments/get_args.cc", "203-multi-checkpoint/multi_checkpoint.cc", "305-other-fault-handler/fault_handler.cc", diff --git a/test/knownfailures.json b/test/knownfailures.json index f3137587f6..f473a99a27 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -734,6 +734,7 @@ "164-resolution-trampoline-dex-cache", "167-visit-locks", "168-vmstack-annotated", + "172-app-image-twice", "201-built-in-except-detail-messages", "203-multi-checkpoint", "304-method-tracing", -- GitLab From 366f0443c4529976d5ac73c7d7273397d8c7c59d Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Tue, 8 May 2018 12:43:15 -0700 Subject: [PATCH 377/749] Add support for cortex-a55/cortex-a75. Bug: 78133793 Bug: 78242072 Test: Builds and ran unit tests. Change-Id: I3e15e402dcc367ecea426895ec8e666887832e8d Merged-In: I3e15e402dcc367ecea426895ec8e666887832e8d (cherry picked from commit a128c5cb01ddef00c6ab1b029e413e77264e88f5) --- .../arch/arm/instruction_set_features_arm.cc | 2 ++ .../arm64/instruction_set_features_arm64.cc | 2 ++ .../instruction_set_features_arm64_test.cc | 20 +++++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc index 801254fd30..608999b3bf 100644 --- a/runtime/arch/arm/instruction_set_features_arm.cc +++ b/runtime/arch/arm/instruction_set_features_arm.cc @@ -46,9 +46,11 @@ ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromVariant( "cortex-a53", "cortex-a53.a57", "cortex-a53.a72", + "cortex-a55", "cortex-a57", "cortex-a72", "cortex-a73", + "cortex-a75", "exynos-m1", "denver", "kryo" diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc index 42c9a846d0..d0f61c946c 100644 --- a/runtime/arch/arm64/instruction_set_features_arm64.cc +++ b/runtime/arch/arm64/instruction_set_features_arm64.cc @@ -52,6 +52,8 @@ Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromVariant( // Check to see if this is an expected variant. static const char* arm64_known_variants[] = { "cortex-a35", + "cortex-a55", + "cortex-a75", "exynos-m1", "exynos-m2", "exynos-m3", diff --git a/runtime/arch/arm64/instruction_set_features_arm64_test.cc b/runtime/arch/arm64/instruction_set_features_arm64_test.cc index 7fd39b6b1b..b946f4f637 100644 --- a/runtime/arch/arm64/instruction_set_features_arm64_test.cc +++ b/runtime/arch/arm64/instruction_set_features_arm64_test.cc @@ -64,6 +64,26 @@ TEST(Arm64InstructionSetFeaturesTest, Arm64Features) { EXPECT_FALSE(kryo_features->Equals(cortex_a57_features.get())); EXPECT_STREQ("-a53", kryo_features->GetFeatureString().c_str()); EXPECT_EQ(kryo_features->AsBitmap(), 0U); + + std::unique_ptr cortex_a55_features( + InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a55", &error_msg)); + ASSERT_TRUE(cortex_a55_features.get() != nullptr) << error_msg; + EXPECT_EQ(cortex_a55_features->GetInstructionSet(), InstructionSet::kArm64); + EXPECT_TRUE(cortex_a55_features->Equals(cortex_a55_features.get())); + EXPECT_TRUE(cortex_a55_features->Equals(cortex_a35_features.get())); + EXPECT_FALSE(cortex_a55_features->Equals(cortex_a57_features.get())); + EXPECT_STREQ("-a53", cortex_a55_features->GetFeatureString().c_str()); + EXPECT_EQ(cortex_a55_features->AsBitmap(), 0U); + + std::unique_ptr cortex_a75_features( + InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a75", &error_msg)); + ASSERT_TRUE(cortex_a75_features.get() != nullptr) << error_msg; + EXPECT_EQ(cortex_a75_features->GetInstructionSet(), InstructionSet::kArm64); + EXPECT_TRUE(cortex_a75_features->Equals(cortex_a75_features.get())); + EXPECT_TRUE(cortex_a75_features->Equals(cortex_a35_features.get())); + EXPECT_FALSE(cortex_a75_features->Equals(cortex_a57_features.get())); + EXPECT_STREQ("-a53", cortex_a75_features->GetFeatureString().c_str()); + EXPECT_EQ(cortex_a75_features->AsBitmap(), 0U); } } // namespace art -- GitLab From 818cb80ff7503f4ce46f05386209d0070b6b70b0 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 11 May 2018 05:30:16 +0000 Subject: [PATCH 378/749] Revert "Make dexlayout and profman build without libart" This reverts commit 2b80ed488c497393270c98f7a767d8495166db8e. Bug: 78652467 Reason for revert: ASAN tests failing Change-Id: Id4bba2711d8b69c0a64e3e8eb335a18facab9fdd --- dexlayout/Android.bp | 9 +++-- dexlayout/dexlayout.cc | 32 +++--------------- dexlayout/dexlayout_main.cc | 8 ++--- libprofile/Android.bp | 1 + profman/Android.bp | 4 +-- profman/profman.cc | 67 ++++++++++++++----------------------- 6 files changed, 39 insertions(+), 82 deletions(-) diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp index 5285c08cc2..b009774582 100644 --- a/dexlayout/Android.bp +++ b/dexlayout/Android.bp @@ -27,7 +27,6 @@ art_cc_defaults { ], export_include_dirs: ["."], shared_libs: [ - "libartbase", "libbase", ], static_libs: ["libz"], @@ -40,6 +39,7 @@ art_cc_library { "dex2oat-pgo-defaults", ], shared_libs: [ + "libart", "libdexfile", "libprofile", ], @@ -60,6 +60,7 @@ art_cc_library { "art_debug_defaults", ], shared_libs: [ + "libartd", "libdexfiled", "libprofiled", ], @@ -79,9 +80,8 @@ art_cc_binary { name: "dexlayout", defaults: ["dexlayout-defaults"], shared_libs: [ - "libartbase", - "libdexfile", "libprofile", + "libart", "libart-dexlayout", ], } @@ -93,9 +93,8 @@ art_cc_binary { "dexlayout-defaults", ], shared_libs: [ - "libartbased", - "libdexfiled", "libprofiled", + "libartd", "libartd-dexlayout", ], } diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index 03dfee319e..62dd1a9554 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -36,8 +36,8 @@ #include "base/logging.h" // For VLOG_IS_ON. #include "base/mem_map.h" #include "base/os.h" -#include "base/unix_file/fd_file.h" #include "base/utils.h" +#include "dex/art_dex_file_loader.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_layout.h" @@ -1930,7 +1930,7 @@ bool DexLayout::ProcessDexFile(const char* file_name, std::string location = "memory mapped file for " + std::string(file_name); // Dex file verifier cannot handle compact dex. bool verify = options_.compact_dex_level_ == CompactDexLevel::kCompactDexLevelNone; - const DexFileLoader dex_file_loader; + const ArtDexFileLoader dex_file_loader; DexContainer::Section* const main_section = (*dex_container)->GetMainSection(); DexContainer::Section* const data_section = (*dex_container)->GetDataSection(); DCHECK_EQ(file_size, main_section->Size()) @@ -1980,32 +1980,10 @@ int DexLayout::ProcessFile(const char* file_name) { // all of which are Zip archives with "classes.dex" inside. const bool verify_checksum = !options_.ignore_bad_checksum_; std::string error_msg; - std::unique_ptr input_file(OS::OpenFileForReading(file_name)); - if (input_file == nullptr) { - LOG(ERROR) << "Could not open file " << file_name << " for reading"; - return -1; - } - std::unique_ptr mmap(MemMap::MapFile(input_file->GetLength(), - PROT_READ, - MAP_PRIVATE, - input_file->Fd(), - /*start*/0, - /*low_4gb*/false, - file_name, - &error_msg)); - if (mmap == nullptr) { - LOG(ERROR) << "MemMap failed for '" << file_name << "' " << error_msg; - return -1; - } - const DexFileLoader dex_file_loader; + const ArtDexFileLoader dex_file_loader; std::vector> dex_files; - if (!dex_file_loader.OpenAll(mmap->Begin(), - mmap->Size(), - file_name, - /*verify*/true, - verify_checksum, - &error_msg, - &dex_files)) { + if (!dex_file_loader.Open( + file_name, file_name, /* verify */ true, verify_checksum, &error_msg, &dex_files)) { // Display returned error message to user. Note that this error behavior // differs from the error messages shown by the original Dalvik dexdump. LOG(ERROR) << error_msg; diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc index 3f92d501f6..185c1420ab 100644 --- a/dexlayout/dexlayout_main.cc +++ b/dexlayout/dexlayout_main.cc @@ -34,6 +34,7 @@ #include "base/logging.h" // For InitLogging. #include "base/mem_map.h" #include "profile/profile_compilation_info.h" +#include "runtime.h" namespace art { @@ -65,17 +66,12 @@ static void Usage(void) { LOG(ERROR) << " -x : compact dex generation level, either 'none' or 'fast'"; } -NO_RETURN static void Abort(const char* msg) { - LOG(ERROR) << "Aborted: " << msg; - exit(1); -} - /* * Main driver of the dexlayout utility. */ int DexlayoutDriver(int argc, char** argv) { // Art specific set up. - InitLogging(argv, Abort); + InitLogging(argv, Runtime::Abort); MemMap::Init(); Options options; diff --git a/libprofile/Android.bp b/libprofile/Android.bp index 5afe73b353..bcb90cb680 100644 --- a/libprofile/Android.bp +++ b/libprofile/Android.bp @@ -40,6 +40,7 @@ cc_defaults { ], }, }, + //generated_sources: ["art_libartbase_operator_srcs"], cflags: ["-DBUILDING_LIBART=1"], shared_libs: [ "libartbase", diff --git a/profman/Android.bp b/profman/Android.bp index c9c92e6685..3c8c72c34a 100644 --- a/profman/Android.bp +++ b/profman/Android.bp @@ -39,7 +39,7 @@ art_cc_binary { name: "profman", defaults: ["profman-defaults"], shared_libs: [ - "libartbase", + "libart", "libprofile", "libdexfile", ], @@ -52,7 +52,7 @@ art_cc_binary { "profman-defaults", ], shared_libs: [ - "libartbased", + "libartd", "libprofiled", "libdexfiled", ], diff --git a/profman/profman.cc b/profman/profman.cc index c16fadd828..cd88d03929 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -18,7 +18,6 @@ #include #include #include -#include #include #include @@ -41,6 +40,7 @@ #include "base/utils.h" #include "base/zip_archive.h" #include "boot_image_profile.h" +#include "dex/art_dex_file_loader.h" #include "dex/bytecode_utils.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_file.h" @@ -49,6 +49,7 @@ #include "dex/type_reference.h" #include "profile/profile_compilation_info.h" #include "profile_assistant.h" +#include "runtime.h" namespace art { @@ -176,11 +177,6 @@ static constexpr char kMethodFlagStringHot = 'H'; static constexpr char kMethodFlagStringStartup = 'S'; static constexpr char kMethodFlagStringPostStartup = 'P'; -NO_RETURN static void Abort(const char* msg) { - LOG(ERROR) << "Aborted: " << msg; - exit(1); -} - // TODO(calin): This class has grown too much from its initial design. Split the functionality // into smaller, more contained pieces. class ProfMan FINAL { @@ -206,8 +202,8 @@ class ProfMan FINAL { original_argc = argc; original_argv = argv; - MemMap::Init(); - InitLogging(argv, Abort); + Locks::Init(); + InitLogging(argv, Runtime::Abort); // Skip over the command name. argv++; @@ -417,49 +413,36 @@ class ProfMan FINAL { } static constexpr bool kVerifyChecksum = true; for (size_t i = 0; i < dex_locations_.size(); ++i) { - std::unique_ptr apk_file; + std::string error_msg; + const ArtDexFileLoader dex_file_loader; + std::vector> dex_files_for_location; // We do not need to verify the apk for processing profiles. if (use_apk_fd_list) { - apk_file.reset(new File(apks_fd_[i], false/*checkUsage*/)); + if (dex_file_loader.OpenZip(apks_fd_[i], + dex_locations_[i], + /* verify */ false, + kVerifyChecksum, + &error_msg, + &dex_files_for_location)) { + } else { + LOG(ERROR) << "OpenZip failed for '" << dex_locations_[i] << "' " << error_msg; + return false; + } } else { - apk_file.reset(new File(apk_files_[i], O_RDONLY, false/*checkUsage*/)); - if (apk_file == nullptr) { - LOG(ERROR) << "Open failed for '" << dex_locations_[i] << "' "; + if (dex_file_loader.Open(apk_files_[i].c_str(), + dex_locations_[i], + /* verify */ false, + kVerifyChecksum, + &error_msg, + &dex_files_for_location)) { + } else { + LOG(ERROR) << "Open failed for '" << dex_locations_[i] << "' " << error_msg; return false; } } - std::string error_msg; - std::unique_ptr mmap(MemMap::MapFile(apk_file->GetLength(), - PROT_READ, - MAP_PRIVATE, - apk_file->Fd(), - /*start*/0, - /*low_4gb*/false, - dex_locations_[i].c_str(), - &error_msg)); - if (mmap == nullptr) { - LOG(ERROR) << "MemMap failed for '" << dex_locations_[i] << "' " << error_msg; - return false; - } - const DexFileLoader dex_file_loader; - std::vector> dex_files_for_location; - if (!dex_file_loader.OpenAll(mmap->Begin(), - mmap->Size(), - dex_locations_[i], - /* verify */ false, - kVerifyChecksum, - &error_msg, - &dex_files_for_location)) { - LOG(ERROR) << "OpenAll failed for '" << dex_locations_[i] << "' " << error_msg; - return false; - } for (std::unique_ptr& dex_file : dex_files_for_location) { process_fn(std::move(dex_file)); } - // Leak apk_file and mmap for now. - // TODO: close fds, etc. - apk_file.release(); - mmap.release(); } return true; } -- GitLab From 9d12a5e44b09a13449c0a631bfdbc85c97681eac Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 11 May 2018 09:58:28 +0100 Subject: [PATCH 379/749] Adjust run test for target runs. Test: 172-app-image-twice Bug: 77342775 Bug: 79200502 Change-Id: I8a1c5ef775e40f0596f2f183aa32ea307b8ae93b --- test/172-app-image-twice/run | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/172-app-image-twice/run b/test/172-app-image-twice/run index d1ad043552..aa2819075f 100644 --- a/test/172-app-image-twice/run +++ b/test/172-app-image-twice/run @@ -16,5 +16,13 @@ # Build an app image with TestClass (specified by profile) and class loader # context that skips the duplicate class checks. -exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \ - -Xcompiler-option --class-loader-context=\& + +# Target and host use a different shell, and we need to special case the +# passing of the class loader context marker. +if [[ "$@" = *" --host "* ]]; then + ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \ + -Xcompiler-option --class-loader-context=\& +else + ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \ + -Xcompiler-option '--class-loader-context=\&' +fi -- GitLab From 607624f043af1de59f0069cfe6a68a00f950510e Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Fri, 11 May 2018 10:10:46 +0100 Subject: [PATCH 380/749] ART: Rename JitCodeCache::FreeCode(const void*) Rename FreeCode(const void*) to FreeCodeAndData(const void*). Leaves remaining FreeCode(uint8_t*) as responsible for freeing code. Bug: b/66095511 Test: art/test.py --host --64 --jit -r Change-Id: I87eb21a2f0c82c92f5bac3add8f9fc25c294dfc5 --- runtime/jit/jit_code_cache.cc | 8 ++++---- runtime/jit/jit_code_cache.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 249a8b04fb..d8aa00c45e 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -563,7 +563,7 @@ void JitCodeCache::SweepRootTables(IsMarkedVisitor* visitor) { } } -void JitCodeCache::FreeCode(const void* code_ptr) { +void JitCodeCache::FreeCodeAndData(const void* code_ptr) { uintptr_t allocation = FromCodeToAllocation(code_ptr); // Notify native debugger that we are about to remove the code. // It does nothing if we are not using native debugger. @@ -590,7 +590,7 @@ void JitCodeCache::FreeAllMethodHeaders( MutexLock mu(Thread::Current(), lock_); ScopedCodeCacheWrite scc(this); for (const OatQuickMethodHeader* method_header : method_headers) { - FreeCode(method_header->GetCode()); + FreeCodeAndData(method_header->GetCode()); } } @@ -916,7 +916,7 @@ bool JitCodeCache::RemoveMethodLocked(ArtMethod* method, bool release_memory) { in_cache = true; if (it->second.GetMethods().empty()) { if (release_memory) { - FreeCode(it->second.GetCode()); + FreeCodeAndData(it->second.GetCode()); } jni_stubs_map_.erase(it); } else { @@ -928,7 +928,7 @@ bool JitCodeCache::RemoveMethodLocked(ArtMethod* method, bool release_memory) { if (it->second == method) { in_cache = true; if (release_memory) { - FreeCode(it->first); + FreeCodeAndData(it->first); } it = method_code_map_.erase(it); } else { diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index b10f57eff2..958e8e8aa2 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -317,8 +317,8 @@ class JitCodeCache { REQUIRES(lock_) REQUIRES(Locks::mutator_lock_); - // Free in the mspace allocations for `code_ptr`. - void FreeCode(const void* code_ptr) REQUIRES(lock_); + // Free code and data allocations for `code_ptr`. + void FreeCodeAndData(const void* code_ptr) REQUIRES(lock_); // Number of bytes allocated in the code cache. size_t CodeCacheSizeLocked() REQUIRES(lock_); @@ -357,10 +357,10 @@ class JitCodeCache { REQUIRES(lock_) REQUIRES_SHARED(Locks::mutator_lock_); - void FreeCode(uint8_t* code) REQUIRES(lock_); uint8_t* AllocateCode(size_t code_size) REQUIRES(lock_); - void FreeData(uint8_t* data) REQUIRES(lock_); + void FreeCode(uint8_t* code) REQUIRES(lock_); uint8_t* AllocateData(size_t data_size) REQUIRES(lock_); + void FreeData(uint8_t* data) REQUIRES(lock_); bool IsWeakAccessEnabled(Thread* self) const; void WaitUntilInlineCacheAccessible(Thread* self) -- GitLab From dbaa5c7ba8935cf87ceb40a4054f9842929e9a51 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Thu, 10 May 2018 08:22:46 +0100 Subject: [PATCH 381/749] ART: Compiler support for const-method-handle Implemented as a runtime call. Bug: 66890674 Test: art/test.py --target -r -t 979 Test: art/test.py --target --64 -r -t 979 Test: art/test.py --host -r -t 979 Change-Id: I67f461c819a7d528d7455afda8b4a59e9aed381c --- compiler/optimizing/code_generator.cc | 20 +++++++ compiler/optimizing/code_generator.h | 5 ++ compiler/optimizing/code_generator_arm64.cc | 10 ++++ .../optimizing/code_generator_arm_vixl.cc | 10 ++++ compiler/optimizing/code_generator_mips.cc | 10 ++++ compiler/optimizing/code_generator_mips64.cc | 10 ++++ compiler/optimizing/code_generator_x86.cc | 10 ++++ compiler/optimizing/code_generator_x86_64.cc | 10 ++++ compiler/optimizing/graph_visualizer.cc | 5 ++ compiler/optimizing/instruction_builder.cc | 14 +++++ compiler/optimizing/instruction_builder.h | 5 +- compiler/optimizing/nodes.h | 47 +++++++++++++++- .../optimizing/reference_type_propagation.cc | 13 +++++ .../optimizing/reference_type_propagation.h | 2 + .../assembler_thumb_test_expected.cc.inc | 2 +- dex2oat/linker/oat_writer_test.cc | 2 +- runtime/arch/arm/quick_entrypoints_arm.S | 1 + runtime/arch/arm64/quick_entrypoints_arm64.S | 1 + runtime/arch/mips/entrypoints_init_mips.cc | 2 + runtime/arch/mips/quick_entrypoints_mips.S | 6 ++ .../arch/mips64/quick_entrypoints_mips64.S | 6 ++ runtime/arch/x86/quick_entrypoints_x86.S | 1 + .../arch/x86_64/quick_entrypoints_x86_64.S | 1 + runtime/asm_support.h | 2 +- runtime/entrypoints/entrypoint_utils.cc | 7 +++ runtime/entrypoints/entrypoint_utils.h | 6 ++ .../entrypoints/quick/quick_default_externs.h | 1 + .../quick/quick_default_init_entrypoints.h | 1 + .../quick/quick_dexcache_entrypoints.cc | 11 ++++ .../quick/quick_entrypoints_list.h | 1 + runtime/entrypoints_order_test.cc | 3 +- runtime/oat.h | 4 +- runtime/verifier/method_verifier.cc | 2 - test/979-const-method-handle/expected.txt | 1 + test/979-const-method-handle/src/Main.java | 56 +++++++++++++------ 35 files changed, 262 insertions(+), 26 deletions(-) diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 1e44311cb1..f57333741c 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -736,6 +736,26 @@ void CodeGenerator::GenerateLoadClassRuntimeCall(HLoadClass* cls) { } } +void CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary( + HLoadMethodHandle* method_handle, + Location runtime_proto_index_location, + Location runtime_return_location) { + DCHECK_EQ(method_handle->InputCount(), 1u); + LocationSummary* locations = + new (method_handle->GetBlock()->GetGraph()->GetAllocator()) LocationSummary( + method_handle, LocationSummary::kCallOnMainOnly); + locations->SetInAt(0, Location::NoLocation()); + locations->AddTemp(runtime_proto_index_location); + locations->SetOut(runtime_return_location); +} + +void CodeGenerator::GenerateLoadMethodHandleRuntimeCall(HLoadMethodHandle* method_handle) { + LocationSummary* locations = method_handle->GetLocations(); + MoveConstant(locations->GetTemp(0), method_handle->GetMethodHandleIndex()); + CheckEntrypointTypes(); + InvokeRuntime(kQuickResolveMethodHandle, method_handle, method_handle->GetDexPc()); +} + void CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary( HLoadMethodType* method_type, Location runtime_proto_index_location, diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 7e84a448cc..bcb25997f4 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -564,6 +564,11 @@ class CodeGenerator : public DeletableArenaObject { Location runtime_return_location); void GenerateLoadClassRuntimeCall(HLoadClass* cls); + static void CreateLoadMethodHandleRuntimeCallLocationSummary(HLoadMethodHandle* method_handle, + Location runtime_handle_index_location, + Location runtime_return_location); + void GenerateLoadMethodHandleRuntimeCall(HLoadMethodHandle* method_handle); + static void CreateLoadMethodTypeRuntimeCallLocationSummary(HLoadMethodType* method_type, Location runtime_type_index_location, Location runtime_return_location); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 0601d2d79a..6f173e19f5 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -5144,6 +5144,16 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA } } +void LocationsBuilderARM64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConvention calling_convention; + Location location = LocationFrom(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorARM64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + void LocationsBuilderARM64::VisitLoadMethodType(HLoadMethodType* load) { InvokeRuntimeCallingConvention calling_convention; Location location = LocationFrom(calling_convention.GetRegisterAt(0)); diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 33304c619d..859e1597c6 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -7527,6 +7527,16 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ } } +void LocationsBuilderARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConventionARMVIXL calling_convention; + Location location = LocationFrom(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + void LocationsBuilderARMVIXL::VisitLoadMethodType(HLoadMethodType* load) { InvokeRuntimeCallingConventionARMVIXL calling_convention; Location location = LocationFrom(calling_convention.GetRegisterAt(0)); diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 3a3fcffe15..7f3441fdf4 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -8226,6 +8226,16 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF } } +void LocationsBuilderMIPS::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConvention calling_convention; + Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, loc, loc); +} + +void InstructionCodeGeneratorMIPS::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + void LocationsBuilderMIPS::VisitLoadMethodType(HLoadMethodType* load) { InvokeRuntimeCallingConvention calling_convention; Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index d6fc9a15b8..ee32b96daf 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -6262,6 +6262,16 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S } } +void LocationsBuilderMIPS64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConvention calling_convention; + Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, loc, loc); +} + +void InstructionCodeGeneratorMIPS64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + void LocationsBuilderMIPS64::VisitLoadMethodType(HLoadMethodType* load) { InvokeRuntimeCallingConvention calling_convention; Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index d18a750faf..9e315381b1 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -6539,6 +6539,16 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFE } } +void LocationsBuilderX86::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConvention calling_convention; + Location location = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorX86::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + void LocationsBuilderX86::VisitLoadMethodType(HLoadMethodType* load) { InvokeRuntimeCallingConvention calling_convention; Location location = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 450c8574b0..f7397046d7 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -5908,6 +5908,16 @@ void LocationsBuilderX86_64::VisitClinitCheck(HClinitCheck* check) { } } +void LocationsBuilderX86_64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + // Custom calling convention: RAX serves as both input and output. + Location location = Location::RegisterLocation(RAX); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorX86_64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + void LocationsBuilderX86_64::VisitLoadMethodType(HLoadMethodType* load) { // Custom calling convention: RAX serves as both input and output. Location location = Location::RegisterLocation(RAX); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 87ce1f0c73..d65ad40565 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -386,6 +386,11 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { << load_class->NeedsAccessCheck() << std::noboolalpha; } + void VisitLoadMethodHandle(HLoadMethodHandle* load_method_handle) OVERRIDE { + StartAttributeStream("load_kind") << "RuntimeCall"; + StartAttributeStream("method_handle_index") << load_method_handle->GetMethodHandleIndex(); + } + void VisitLoadMethodType(HLoadMethodType* load_method_type) OVERRIDE { StartAttributeStream("load_kind") << "RuntimeCall"; const DexFile& dex_file = load_method_type->GetDexFile(); diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 61730a8128..35a39456a2 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1896,6 +1896,13 @@ bool HInstructionBuilder::LoadClassNeedsAccessCheck(Handle klass) } } +void HInstructionBuilder::BuildLoadMethodHandle(uint16_t proto_idx, uint32_t dex_pc) { + const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); + HLoadMethodHandle* load_method_handle = + new (allocator_) HLoadMethodHandle(graph_->GetCurrentMethod(), proto_idx, dex_file, dex_pc); + AppendInstruction(load_method_handle); +} + void HInstructionBuilder::BuildLoadMethodType(uint16_t proto_idx, uint32_t dex_pc) { const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); HLoadMethodType* load_method_type = @@ -2934,6 +2941,13 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, break; } + case Instruction::CONST_METHOD_HANDLE: { + uint16_t method_handle_idx = instruction.VRegB_21c(); + BuildLoadMethodHandle(method_handle_idx, dex_pc); + UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); + break; + } + case Instruction::CONST_METHOD_TYPE: { uint16_t proto_idx = instruction.VRegB_21c(); BuildLoadMethodType(proto_idx, dex_pc); diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index 3fde54cffa..95ffa6b054 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -240,7 +240,10 @@ class HInstructionBuilder : public ValueObject { bool LoadClassNeedsAccessCheck(Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); - // Builds a `HLoadMethodType` loading the given `proto_index`. + // Builds a `HLoadMethodHandle` loading the given `method_handle_idx`. + void BuildLoadMethodHandle(uint16_t method_handle_idx, uint32_t dex_pc); + + // Builds a `HLoadMethodType` loading the given `proto_idx`. void BuildLoadMethodType(uint16_t proto_idx, uint32_t dex_pc); // Returns the outer-most compiling method's class. diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 54882ff92b..a7c2d0b125 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1383,6 +1383,7 @@ class HLoopInformationOutwardIterator : public ValueObject { M(LessThanOrEqual, Condition) \ M(LoadClass, Instruction) \ M(LoadException, Instruction) \ + M(LoadMethodHandle, Instruction) \ M(LoadMethodType, Instruction) \ M(LoadString, Instruction) \ M(LongConstant, Constant) \ @@ -6500,6 +6501,50 @@ inline void HLoadString::AddSpecialInput(HInstruction* special_input) { special_input->AddUseAt(this, 0); } +class HLoadMethodHandle FINAL : public HInstruction { + public: + HLoadMethodHandle(HCurrentMethod* current_method, + uint16_t method_handle_idx, + const DexFile& dex_file, + uint32_t dex_pc) + : HInstruction(kLoadMethodHandle, + DataType::Type::kReference, + SideEffectsForArchRuntimeCalls(), + dex_pc), + special_input_(HUserRecord(current_method)), + method_handle_idx_(method_handle_idx), + dex_file_(dex_file) { + } + + using HInstruction::GetInputRecords; // Keep the const version visible. + ArrayRef> GetInputRecords() OVERRIDE FINAL { + return ArrayRef>( + &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u); + } + + bool IsClonable() const OVERRIDE { return true; } + + uint16_t GetMethodHandleIndex() const { return method_handle_idx_; } + + const DexFile& GetDexFile() const { return dex_file_; } + + static SideEffects SideEffectsForArchRuntimeCalls() { + return SideEffects::CanTriggerGC(); + } + + DECLARE_INSTRUCTION(LoadMethodHandle); + + protected: + DEFAULT_COPY_CONSTRUCTOR(LoadMethodHandle); + + private: + // The special input is the HCurrentMethod for kRuntimeCall. + HUserRecord special_input_; + + const uint16_t method_handle_idx_; + const DexFile& dex_file_; +}; + class HLoadMethodType FINAL : public HInstruction { public: HLoadMethodType(HCurrentMethod* current_method, @@ -6540,7 +6585,7 @@ class HLoadMethodType FINAL : public HInstruction { // The special input is the HCurrentMethod for kRuntimeCall. HUserRecord special_input_; - uint16_t proto_idx_; + const uint16_t proto_idx_; const DexFile& dex_file_; }; diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index b15a0ea52f..ecfa790b91 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -59,6 +59,12 @@ ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetClassCla return GetRootHandle(handles_, ClassLinker::kJavaLangClass, &class_class_handle_); } +ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetMethodHandleClassHandle() { + return GetRootHandle(handles_, + ClassLinker::kJavaLangInvokeMethodHandleImpl, + &method_handle_class_handle_); +} + ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetMethodTypeClassHandle() { return GetRootHandle(handles_, ClassLinker::kJavaLangInvokeMethodType, @@ -95,6 +101,7 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { void VisitLoadClass(HLoadClass* load_class) OVERRIDE; void VisitInstanceOf(HInstanceOf* load_class) OVERRIDE; void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE; + void VisitLoadMethodHandle(HLoadMethodHandle* instr) OVERRIDE; void VisitLoadMethodType(HLoadMethodType* instr) OVERRIDE; void VisitLoadString(HLoadString* instr) OVERRIDE; void VisitLoadException(HLoadException* instr) OVERRIDE; @@ -675,6 +682,12 @@ void ReferenceTypePropagation::RTPVisitor::VisitClinitCheck(HClinitCheck* instr) instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo()); } +void ReferenceTypePropagation::RTPVisitor::VisitLoadMethodHandle(HLoadMethodHandle* instr) { + instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create( + handle_cache_->GetMethodHandleClassHandle(), + /* is_exact */ true)); +} + void ReferenceTypePropagation::RTPVisitor::VisitLoadMethodType(HLoadMethodType* instr) { instr->SetReferenceTypeInfo( ReferenceTypeInfo::Create(handle_cache_->GetMethodTypeClassHandle(), /* is_exact */ true)); diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h index da2193d223..d36d592708 100644 --- a/compiler/optimizing/reference_type_propagation.h +++ b/compiler/optimizing/reference_type_propagation.h @@ -75,6 +75,7 @@ class ReferenceTypePropagation : public HOptimization { ReferenceTypeInfo::TypeHandle GetObjectClassHandle(); ReferenceTypeInfo::TypeHandle GetClassClassHandle(); + ReferenceTypeInfo::TypeHandle GetMethodHandleClassHandle(); ReferenceTypeInfo::TypeHandle GetMethodTypeClassHandle(); ReferenceTypeInfo::TypeHandle GetStringClassHandle(); ReferenceTypeInfo::TypeHandle GetThrowableClassHandle(); @@ -84,6 +85,7 @@ class ReferenceTypePropagation : public HOptimization { ReferenceTypeInfo::TypeHandle object_class_handle_; ReferenceTypeInfo::TypeHandle class_class_handle_; + ReferenceTypeInfo::TypeHandle method_handle_class_handle_; ReferenceTypeInfo::TypeHandle method_type_class_handle_; ReferenceTypeInfo::TypeHandle string_class_handle_; ReferenceTypeInfo::TypeHandle throwable_class_handle_; diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc index ff3e1ba9f0..19c405e517 100644 --- a/compiler/utils/assembler_thumb_test_expected.cc.inc +++ b/compiler/utils/assembler_thumb_test_expected.cc.inc @@ -153,7 +153,7 @@ const char* const VixlJniHelpersResults[] = { " 21c: f8d9 8034 ldr.w r8, [r9, #52] ; 0x34\n", " 220: 4770 bx lr\n", " 222: 4660 mov r0, ip\n", - " 224: f8d9 c2c8 ldr.w ip, [r9, #712] ; 0x2c8\n", + " 224: f8d9 c2cc ldr.w ip, [r9, #716] ; 0x2cc\n", " 228: 47e0 blx ip\n", nullptr }; diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 7bd9f0fe8d..df0641cc7f 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -497,7 +497,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(76U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(24U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(163 * static_cast(GetInstructionSetPointerSize(kRuntimeISA)), + EXPECT_EQ(164 * static_cast(GetInstructionSetPointerSize(kRuntimeISA)), sizeof(QuickEntryPoints)); } diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 1a1f4edad8..cd00125de5 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -1043,6 +1043,7 @@ END \name ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 9919e98d6b..ac5b2b8b88 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1580,6 +1580,7 @@ TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 58b9d48951..5d6e410101 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -203,6 +203,8 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { static_assert(!IsDirectEntrypoint(kQuickInitializeType), "Non-direct C stub marked direct."); qpoints->pResolveString = art_quick_resolve_string; static_assert(!IsDirectEntrypoint(kQuickResolveString), "Non-direct C stub marked direct."); + qpoints->pResolveMethodHandle = art_quick_resolve_method_handle; + static_assert(!IsDirectEntrypoint(kQuickResolveMethodHandle), "Non-direct C stub marked direct."); qpoints->pResolveMethodType = art_quick_resolve_method_type; static_assert(!IsDirectEntrypoint(kQuickResolveMethodType), "Non-direct C stub marked direct."); diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 104a4ca6d6..c367ea60c2 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -2055,6 +2055,12 @@ END \name ONE_ARG_SAVE_EVERYTHING_DOWNCALL \name, \entrypoint, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET .endm + /* + * Entry from managed code to resolve a method handle. On entry, A0 holds the method handle + * index. On success the MethodHandle is returned, otherwise an exception is raised. + */ +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode + /* * Entry from managed code to resolve a method type. On entry, A0 holds the method type index. * On success the MethodType is returned, otherwise an exception is raised. diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 1e94e07c23..1f4f174e26 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1955,6 +1955,12 @@ END \name ONE_ARG_SAVE_EVERYTHING_DOWNCALL \name, \entrypoint, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET .endm + /* + * Entry from managed code to resolve a method handle. On entry, A0 holds the method handle + * index. On success the MethodHandle is returned, otherwise an exception is raised. + */ +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode + /* * Entry from managed code to resolve a method type. On entry, A0 holds the method type index. * On success the MethodType is returned, otherwise an exception is raised. diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 0ae691468d..8ab4ce160f 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1281,6 +1281,7 @@ GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFr ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 8fef8020ef..eb945ed366 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -1301,6 +1301,7 @@ END_FUNCTION art_quick_alloc_object_initialized_region_tlab ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode diff --git a/runtime/asm_support.h b/runtime/asm_support.h index 705e1ff6cf..70ff40d32c 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -73,7 +73,7 @@ ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, // Offset of field Thread::tlsPtr_.mterp_current_ibase. #define THREAD_CURRENT_IBASE_OFFSET \ - (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 163) * __SIZEOF_POINTER__) + (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 164) * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET, art::Thread::MterpCurrentIBaseOffset().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_default_ibase. diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index df184bc733..a58946ae66 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -260,6 +260,13 @@ ArtMethod* GetCalleeSaveOuterMethod(Thread* self, CalleeSaveType type) { return DoGetCalleeSaveMethodOuterCallerAndPc(sp, type).first; } +ObjPtr ResolveMethodHandleFromCode(ArtMethod* referrer, + uint32_t method_handle_idx) { + Thread::PoisonObjectPointersIfDebug(); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + return class_linker->ResolveMethodHandle(Thread::Current(), method_handle_idx, referrer); +} + ObjPtr ResolveMethodTypeFromCode(ArtMethod* referrer, uint32_t proto_idx) { Thread::PoisonObjectPointersIfDebug(); diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index 203ff3d031..0a3b5dfc93 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -34,6 +34,7 @@ namespace art { namespace mirror { class Array; class Class; +class MethodHandle; class MethodType; class Object; class String; @@ -152,6 +153,11 @@ inline ObjPtr ResolveVerifyAndClinit(dex::TypeIndex type_idx, REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); +ObjPtr ResolveMethodHandleFromCode(ArtMethod* referrer, + uint32_t method_handle_idx) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Roles::uninterruptible_); + ObjPtr ResolveMethodTypeFromCode(ArtMethod* referrer, uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h index d934a532ee..1804d9e64d 100644 --- a/runtime/entrypoints/quick/quick_default_externs.h +++ b/runtime/entrypoints/quick/quick_default_externs.h @@ -37,6 +37,7 @@ extern "C" void art_quick_check_instance_of(art::mirror::Object*, art::mirror::C extern "C" void* art_quick_initialize_static_storage(uint32_t); extern "C" void* art_quick_initialize_type(uint32_t); extern "C" void* art_quick_initialize_type_and_verify_access(uint32_t); +extern "C" void* art_quick_resolve_method_handle(uint32_t); extern "C" void* art_quick_resolve_method_type(uint32_t); extern "C" void* art_quick_resolve_string(uint32_t); diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h index a4572f65a7..3f66045576 100644 --- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h +++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h @@ -37,6 +37,7 @@ static void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qp qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage; qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access; qpoints->pInitializeType = art_quick_initialize_type; + qpoints->pResolveMethodHandle = art_quick_resolve_method_handle; qpoints->pResolveMethodType = art_quick_resolve_method_type; qpoints->pResolveString = art_quick_resolve_string; diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index 09cbfffe4b..cf9ddd8aa8 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -183,6 +183,17 @@ extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type return result.Ptr(); } +extern "C" mirror::MethodHandle* artResolveMethodHandleFromCode(uint32_t method_handle_idx, + Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); + auto caller_and_outer = + GetCalleeSaveMethodCallerAndOuterMethod(self, CalleeSaveType::kSaveEverything); + ArtMethod* caller = caller_and_outer.caller; + ObjPtr result = ResolveMethodHandleFromCode(caller, method_handle_idx); + return result.Ptr(); +} + extern "C" mirror::MethodType* artResolveMethodTypeFromCode(uint32_t proto_idx, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h index 39dcd39208..3a8faca11d 100644 --- a/runtime/entrypoints/quick/quick_entrypoints_list.h +++ b/runtime/entrypoints/quick/quick_entrypoints_list.h @@ -38,6 +38,7 @@ V(InitializeStaticStorage, void*, uint32_t) \ V(InitializeTypeAndVerifyAccess, void*, uint32_t) \ V(InitializeType, void*, uint32_t) \ + V(ResolveMethodHandle, void*, uint32_t) \ V(ResolveMethodType, void*, uint32_t) \ V(ResolveString, void*, uint32_t) \ \ diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index b0689f6cd3..1337cd5fb2 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -183,7 +183,8 @@ class EntrypointsOrderTest : public CommonRuntimeTest { sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeTypeAndVerifyAccess, pInitializeType, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeType, pResolveMethodType, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeType, pResolveMethodHandle, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveMethodHandle, pResolveMethodType, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveMethodType, pResolveString, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveString, pSet8Instance, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet8Instance, pSet8Static, sizeof(void*)); diff --git a/runtime/oat.h b/runtime/oat.h index 9a58ded7e8..6c683f1541 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: compiler support const-method-type - static constexpr uint8_t kOatVersion[] = { '1', '4', '2', '\0' }; + // Last oat version changed reason: compiler support const-method-handle + static constexpr uint8_t kOatVersion[] = { '1', '4', '3', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 5cd60915b5..3518d2facd 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -2287,8 +2287,6 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::CONST_METHOD_HANDLE: work_line_->SetRegisterType( this, inst->VRegA_21c(), reg_types_.JavaLangInvokeMethodHandle()); - // TODO: add compiler support for const-method-{handle,type} (b/66890674) - Fail(VERIFY_ERROR_FORCE_INTERPRETER); break; case Instruction::CONST_METHOD_TYPE: work_line_->SetRegisterType( diff --git a/test/979-const-method-handle/expected.txt b/test/979-const-method-handle/expected.txt index 2d169b9508..bbaaedb0af 100644 --- a/test/979-const-method-handle/expected.txt +++ b/test/979-const-method-handle/expected.txt @@ -5,4 +5,5 @@ Hello World! And Hello Zog Hello World! And Hello Zorba name is HoverFly 2.718281828459045 +repeatConstMethodHandle() Attempting to set Math.E raised IAE diff --git a/test/979-const-method-handle/src/Main.java b/test/979-const-method-handle/src/Main.java index 79be913a67..427ca7a306 100644 --- a/test/979-const-method-handle/src/Main.java +++ b/test/979-const-method-handle/src/Main.java @@ -20,12 +20,25 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; class Main { + /** + * Number of iterations run to attempt to trigger JIT compilation. These tests run on ART and + * the RI so they iterate rather than using the ART only native method ensureJitCompiled(). + */ + private static final int ITERATIONS_FOR_JIT = 12000; + + /** A static field updated by method handle getters and setters. */ private static String name = "default"; private static void unreachable() { throw new Error("Unreachable"); } + private static void assertEquals(Object expected, Object actual) { + if (!expected.equals(actual)) { + throw new AssertionError("Assertion failure: " + expected + " != " + actual); + } + } + private static class LocalClass { public LocalClass() {} @@ -52,15 +65,9 @@ class Main { System.out.print("repeatConstMethodType0("); System.out.print(expected); System.out.println(")"); - for (int i = 0; i < 12000; ++i) { + for (int i = 0; i < ITERATIONS_FOR_JIT; ++i) { MethodType actual = methodType0(); - if (!actual.equals(expected)) { - System.out.print("Expected: "); - System.out.println(expected); - System.out.print("Actual: "); - System.out.println(actual); - unreachable(); - } + assertEquals(expected, actual); } } @@ -68,15 +75,9 @@ class Main { System.out.print("repeatConstMethodType1("); System.out.print(expected); System.out.println(")"); - for (int i = 0; i < 12000; ++i) { + for (int i = 0; i < ITERATIONS_FOR_JIT; ++i) { MethodType actual = methodType1(); - if (!actual.equals(expected)) { - System.out.print("Expected: "); - System.out.println(expected); - System.out.print("Actual: "); - System.out.println(actual); - unreachable(); - } + assertEquals(expected, actual); } } @@ -105,6 +106,16 @@ class Main { return null; } + @ConstantMethodHandle( + kind = ConstantMethodHandle.STATIC_GET, + owner = "Main", + fieldOrMethodName = "name", + descriptor = "Ljava/lang/String;") + private static MethodHandle getNameHandle() { + unreachable(); + return null; + } + @ConstantMethodHandle( kind = ConstantMethodHandle.STATIC_GET, owner = "java/lang/Math", @@ -125,6 +136,18 @@ class Main { return null; } + private static void repeatConstMethodHandle() throws Throwable { + System.out.println("repeatConstMethodHandle()"); + String[] values = {"A", "B", "C"}; + for (int i = 0; i < ITERATIONS_FOR_JIT; ++i) { + String value = values[i % values.length]; + setNameHandle().invoke(value); + String actual = (String) getNameHandle().invokeExact(); + assertEquals(value, actual); + assertEquals(value, name); + } + } + public static void main(String[] args) throws Throwable { System.out.println(methodType0()); repeatConstMethodType0( @@ -136,6 +159,7 @@ class Main { System.out.print("name is "); System.out.println(name); System.out.println(getMathE().invoke()); + repeatConstMethodHandle(); try { putMathE().invokeExact(Math.PI); unreachable(); -- GitLab From 66166d566ef09982c23d95d185bf8276dee96676 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 9 May 2018 18:19:29 +0100 Subject: [PATCH 382/749] [veridex] Add a --target-sdk-version to avoid false positives. 1) Recognize Build.VERSION.SDK_INT in flow analysis 2) Recognize simple if patterns. bug: 79404565 bug: 77513322 Test: m (cherry picked from commit 2a843c81e61128d2c1723c064786f8b7193c62f5) Change-Id: I0327f2468ce620ed4dc218bda5a3fc16f285cb7b --- tools/veridex/flow_analysis.cc | 97 ++++++++++++++++++++++++++++++---- tools/veridex/flow_analysis.h | 23 +++++--- tools/veridex/veridex.cc | 12 +++++ tools/veridex/veridex.h | 4 ++ 4 files changed, 119 insertions(+), 17 deletions(-) diff --git a/tools/veridex/flow_analysis.cc b/tools/veridex/flow_analysis.cc index e2833bf01d..154c60f6ac 100644 --- a/tools/veridex/flow_analysis.cc +++ b/tools/veridex/flow_analysis.cc @@ -112,7 +112,12 @@ void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, const VeriClass* cl RegisterValue(RegisterSource::kNone, DexFileReference(nullptr, 0), cls); } -const RegisterValue& VeriFlowAnalysis::GetRegister(uint32_t dex_register) { +void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, int32_t value, const VeriClass* cls) { + current_registers_[dex_register] = + RegisterValue(RegisterSource::kConstant, value, DexFileReference(nullptr, 0), cls); +} + +const RegisterValue& VeriFlowAnalysis::GetRegister(uint32_t dex_register) const { return current_registers_[dex_register]; } @@ -131,6 +136,49 @@ RegisterValue VeriFlowAnalysis::GetFieldType(uint32_t field_index) { return RegisterValue(RegisterSource::kField, DexFileReference(&dex_file, field_index), cls); } +int VeriFlowAnalysis::GetBranchFlags(const Instruction& instruction) const { + switch (instruction.Opcode()) { + #define IF_XX(cond, op) \ + case Instruction::IF_##cond: { \ + RegisterValue lhs = GetRegister(instruction.VRegA()); \ + RegisterValue rhs = GetRegister(instruction.VRegB()); \ + if (lhs.IsConstant() && rhs.IsConstant()) { \ + if (lhs.GetConstant() op rhs.GetConstant()) { \ + return Instruction::kBranch; \ + } else { \ + return Instruction::kContinue; \ + } \ + } \ + break; \ + } \ + case Instruction::IF_##cond##Z: { \ + RegisterValue val = GetRegister(instruction.VRegA()); \ + if (val.IsConstant()) { \ + if (val.GetConstant() op 0) { \ + return Instruction::kBranch; \ + } else { \ + return Instruction::kContinue; \ + } \ + } \ + break; \ + } + + IF_XX(EQ, ==); + IF_XX(NE, !=); + IF_XX(LT, <); + IF_XX(LE, <=); + IF_XX(GT, >); + IF_XX(GE, >=); + + #undef IF_XX + + default: + break; + } + + return Instruction::FlagsOf(instruction.Opcode()); +} + void VeriFlowAnalysis::AnalyzeCode() { std::vector work_list; work_list.push_back(0); @@ -149,16 +197,17 @@ void VeriFlowAnalysis::AnalyzeCode() { ProcessDexInstruction(inst); SetVisited(dex_pc); - int opcode_flags = Instruction::FlagsOf(inst.Opcode()); - if ((opcode_flags & Instruction::kContinue) != 0) { - if ((opcode_flags & Instruction::kBranch) != 0) { + int branch_flags = GetBranchFlags(inst); + + if ((branch_flags & Instruction::kContinue) != 0) { + if ((branch_flags & Instruction::kBranch) != 0) { uint32_t branch_dex_pc = dex_pc + inst.GetTargetOffset(); if (MergeRegisterValues(branch_dex_pc)) { work_list.push_back(branch_dex_pc); } } dex_pc += inst.SizeInCodeUnits(); - } else if ((opcode_flags & Instruction::kBranch) != 0) { + } else if ((branch_flags & Instruction::kBranch) != 0) { dex_pc += inst.GetTargetOffset(); DCHECK(IsBranchTarget(dex_pc)); } else { @@ -178,12 +227,30 @@ void VeriFlowAnalysis::AnalyzeCode() { void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { switch (instruction.Opcode()) { - case Instruction::CONST_4: - case Instruction::CONST_16: - case Instruction::CONST: + case Instruction::CONST_4: { + int32_t register_index = instruction.VRegA(); + int32_t value = instruction.VRegB_11n(); + UpdateRegister(register_index, value, VeriClass::integer_); + break; + } + case Instruction::CONST_16: { + int32_t register_index = instruction.VRegA(); + int32_t value = instruction.VRegB_21s(); + UpdateRegister(register_index, value, VeriClass::integer_); + break; + } + + case Instruction::CONST: { + int32_t register_index = instruction.VRegA(); + int32_t value = instruction.VRegB_31i(); + UpdateRegister(register_index, value, VeriClass::integer_); + break; + } + case Instruction::CONST_HIGH16: { int32_t register_index = instruction.VRegA(); - UpdateRegister(register_index, VeriClass::integer_); + int32_t value = instruction.VRegB_21h(); + UpdateRegister(register_index, value, VeriClass::integer_); break; } @@ -268,6 +335,8 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { case Instruction::RETURN: { break; } + + // If operations will be handled when looking at the control flow. #define IF_XX(cond) \ case Instruction::IF_##cond: break; \ case Instruction::IF_##cond##Z: break @@ -279,6 +348,8 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { IF_XX(GT); IF_XX(GE); + #undef IF_XX + case Instruction::GOTO: case Instruction::GOTO_16: case Instruction::GOTO_32: { @@ -495,7 +566,13 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { case Instruction::SGET_BYTE: case Instruction::SGET_CHAR: case Instruction::SGET_SHORT: { - UpdateRegister(instruction.VRegA_22c(), GetFieldType(instruction.VRegC_22c())); + uint32_t dest_reg = instruction.VRegA_21c(); + uint16_t field_index = instruction.VRegB_21c(); + if (VeriClass::sdkInt_ != nullptr && resolver_->GetField(field_index) == VeriClass::sdkInt_) { + UpdateRegister(dest_reg, gTargetSdkVersion, VeriClass::integer_); + } else { + UpdateRegister(dest_reg, GetFieldType(instruction.VRegC_22c())); + } break; } diff --git a/tools/veridex/flow_analysis.h b/tools/veridex/flow_analysis.h index 62c9916a61..fc093600c3 100644 --- a/tools/veridex/flow_analysis.h +++ b/tools/veridex/flow_analysis.h @@ -35,6 +35,7 @@ enum class RegisterSource { kMethod, kClass, kString, + kConstant, kNone }; @@ -44,28 +45,33 @@ enum class RegisterSource { class RegisterValue { public: RegisterValue() : source_(RegisterSource::kNone), - parameter_index_(0), + value_(0), reference_(nullptr, 0), type_(nullptr) {} RegisterValue(RegisterSource source, DexFileReference reference, const VeriClass* type) - : source_(source), parameter_index_(0), reference_(reference), type_(type) {} + : source_(source), value_(0), reference_(reference), type_(type) {} RegisterValue(RegisterSource source, - uint32_t parameter_index, + uint32_t value, DexFileReference reference, const VeriClass* type) - : source_(source), parameter_index_(parameter_index), reference_(reference), type_(type) {} + : source_(source), value_(value), reference_(reference), type_(type) {} RegisterSource GetSource() const { return source_; } DexFileReference GetDexFileReference() const { return reference_; } const VeriClass* GetType() const { return type_; } uint32_t GetParameterIndex() const { CHECK(IsParameter()); - return parameter_index_; + return value_; + } + uint32_t GetConstant() const { + CHECK(IsConstant()); + return value_; } bool IsParameter() const { return source_ == RegisterSource::kParameter; } bool IsClass() const { return source_ == RegisterSource::kClass; } bool IsString() const { return source_ == RegisterSource::kString; } + bool IsConstant() const { return source_ == RegisterSource::kConstant; } std::string ToString() const { switch (source_) { @@ -91,7 +97,7 @@ class RegisterValue { private: RegisterSource source_; - uint32_t parameter_index_; + uint32_t value_; DexFileReference reference_; const VeriClass* type_; }; @@ -137,12 +143,15 @@ class VeriFlowAnalysis { uint32_t dex_register, RegisterSource kind, VeriClass* cls, uint32_t source_id); void UpdateRegister(uint32_t dex_register, const RegisterValue& value); void UpdateRegister(uint32_t dex_register, const VeriClass* cls); + void UpdateRegister(uint32_t dex_register, int32_t value, const VeriClass* cls); void ProcessDexInstruction(const Instruction& inst); void SetVisited(uint32_t dex_pc); RegisterValue GetFieldType(uint32_t field_index); + int GetBranchFlags(const Instruction& instruction) const; + protected: - const RegisterValue& GetRegister(uint32_t dex_register); + const RegisterValue& GetRegister(uint32_t dex_register) const; RegisterValue GetReturnType(uint32_t method_index); VeridexResolver* resolver_; diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc index dc7ea94032..bcd4815a38 100644 --- a/tools/veridex/veridex.cc +++ b/tools/veridex/veridex.cc @@ -25,6 +25,7 @@ #include "precise_hidden_api_finder.h" #include "resolver.h" +#include #include namespace art { @@ -62,6 +63,7 @@ VeriMethod VeriClass::getMethod_ = nullptr; VeriMethod VeriClass::getDeclaredMethod_ = nullptr; VeriMethod VeriClass::getClass_ = nullptr; VeriMethod VeriClass::loadClass_ = nullptr; +VeriField VeriClass::sdkInt_ = nullptr; struct VeridexOptions { const char* dex_file = nullptr; @@ -70,6 +72,7 @@ struct VeridexOptions { const char* light_greylist = nullptr; const char* dark_greylist = nullptr; bool precise = true; + int target_sdk_version = 28; /* P */ }; static const char* Substr(const char* str, int index) { @@ -91,6 +94,7 @@ static void ParseArgs(VeridexOptions* options, int argc, char** argv) { static const char* kDarkGreylistOption = "--dark-greylist="; static const char* kLightGreylistOption = "--light-greylist="; static const char* kImprecise = "--imprecise"; + static const char* kTargetSdkVersion = "--target-sdk-version="; for (int i = 0; i < argc; ++i) { if (StartsWith(argv[i], kDexFileOption)) { @@ -105,6 +109,8 @@ static void ParseArgs(VeridexOptions* options, int argc, char** argv) { options->light_greylist = Substr(argv[i], strlen(kLightGreylistOption)); } else if (strcmp(argv[i], kImprecise) == 0) { options->precise = false; + } else if (StartsWith(argv[i], kTargetSdkVersion)) { + options->target_sdk_version = atoi(Substr(argv[i], strlen(kTargetSdkVersion))); } } } @@ -124,6 +130,7 @@ class Veridex { static int Run(int argc, char** argv) { VeridexOptions options; ParseArgs(&options, argc, argv); + gTargetSdkVersion = options.target_sdk_version; std::vector boot_content; std::vector app_content; @@ -200,6 +207,11 @@ class Veridex { VeriClass::loadClass_ = boot_resolvers[0]->LookupDeclaredMethodIn( *VeriClass::class_loader_, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + VeriClass* version = type_map["Landroid/os/Build$VERSION;"]; + if (version != nullptr) { + VeriClass::sdkInt_ = boot_resolvers[0]->LookupFieldIn(*version, "SDK_INT", "I"); + } + std::vector> app_resolvers; Resolve(app_dex_files, resolver_map, type_map, &app_resolvers); diff --git a/tools/veridex/veridex.h b/tools/veridex/veridex.h index 9c0a158174..31ddbf439e 100644 --- a/tools/veridex/veridex.h +++ b/tools/veridex/veridex.h @@ -24,6 +24,8 @@ namespace art { +static int gTargetSdkVersion = 1000; // Will be initialized after parsing options. + /** * Abstraction for fields defined in dex files. Currently, that's a pointer into their * `encoded_field` description. @@ -86,6 +88,8 @@ class VeriClass { static VeriMethod getClass_; static VeriMethod loadClass_; + static VeriField sdkInt_; + private: Primitive::Type kind_; uint8_t dimensions_; -- GitLab From 5ae7cdfe5b8da645d1fec61c76176e6a37e78fb9 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Wed, 9 May 2018 18:42:12 +0100 Subject: [PATCH 383/749] Move kVRegSize to globals.h to reduce include dependencies. Test: m test-art-host-gtest Change-Id: I26146535f2684ddab3554023f7df571d93a39f88 --- compiler/optimizing/code_generator.h | 1 - libartbase/base/globals.h | 3 +++ runtime/stack_map.h | 3 --- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index f0c4ee01cc..833b39ec6a 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -35,7 +35,6 @@ #include "optimizing_compiler_stats.h" #include "read_barrier_option.h" #include "stack.h" -#include "stack_map.h" #include "utils/label.h" namespace art { diff --git a/libartbase/base/globals.h b/libartbase/base/globals.h index 69d1a64a3b..39e0c509cd 100644 --- a/libartbase/base/globals.h +++ b/libartbase/base/globals.h @@ -38,6 +38,9 @@ static constexpr size_t kStackAlignment = 16; // compile-time constant so the compiler can generate better code. static constexpr int kPageSize = 4096; +// Size of Dex virtual registers. +static constexpr size_t kVRegSize = 4; + // Returns whether the given memory offset can be used for generating // an implicit null check. static inline bool CanDoImplicitNullCheckOn(uintptr_t offset) { diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 38397643b6..274e2b20b4 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -37,9 +37,6 @@ class VariableIndentationOutputStream; // (signed) values. static constexpr ssize_t kFrameSlotSize = 4; -// Size of Dex virtual registers. -static constexpr size_t kVRegSize = 4; - class ArtMethod; class CodeInfo; class StackMapEncoding; -- GitLab From 2c7e13b120926d3c3c18d649cd9849ea31b81477 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 11 May 2018 19:40:17 +0000 Subject: [PATCH 384/749] Revert "Do not load app image for class collisions" This reverts commit d8860b42e47d48fcc47db9d0daf5a1b9432180a1. Bug: 77342775 Bug: 79200502 Bug: 79575750 Reason for revert: Some regressions in boot time. Test: test-art-host Change-Id: Id5e5844b5156d048a54011708378c7cdb0650f68 --- dex2oat/dex2oat.cc | 4 +- runtime/Android.bp | 1 - runtime/class_loader_context.cc | 31 +++--- runtime/class_loader_context.h | 22 ++--- runtime/class_loader_context_test.cc | 44 +++------ runtime/oat_file_assistant.cc | 4 +- runtime/oat_file_manager.cc | 96 +++++-------------- runtime/oat_file_manager.h | 26 +---- test/172-app-image-twice/check | 18 ---- test/172-app-image-twice/debug_print_class.cc | 33 ------- test/172-app-image-twice/expected.txt | 1 - test/172-app-image-twice/info.txt | 1 - test/172-app-image-twice/profile | 1 - test/172-app-image-twice/run | 28 ------ test/172-app-image-twice/src/Main.java | 48 ---------- test/172-app-image-twice/src/TestClass.java | 18 ---- test/Android.bp | 1 - test/knownfailures.json | 1 - 18 files changed, 64 insertions(+), 314 deletions(-) delete mode 100755 test/172-app-image-twice/check delete mode 100644 test/172-app-image-twice/debug_print_class.cc delete mode 100644 test/172-app-image-twice/expected.txt delete mode 100644 test/172-app-image-twice/info.txt delete mode 100644 test/172-app-image-twice/profile delete mode 100644 test/172-app-image-twice/run delete mode 100644 test/172-app-image-twice/src/Main.java delete mode 100644 test/172-app-image-twice/src/TestClass.java diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 6b65aca943..63518be15f 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1257,10 +1257,10 @@ class Dex2Oat FINAL { if (stored_class_loader_context_ == nullptr) { Usage("Option --stored-class-loader-context has an incorrect format: %s", stored_context_arg.c_str()); - } else if (class_loader_context_->VerifyClassLoaderContextMatch( + } else if (!class_loader_context_->VerifyClassLoaderContextMatch( stored_context_arg, /*verify_names*/ false, - /*verify_checksums*/ false) != ClassLoaderContext::VerificationResult::kVerifies) { + /*verify_checksums*/ false)) { Usage( "Option --stored-class-loader-context '%s' mismatches --class-loader-context '%s'", stored_context_arg.c_str(), diff --git a/runtime/Android.bp b/runtime/Android.bp index 64e6796ba0..116453b1bf 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -422,7 +422,6 @@ gensrcs { srcs: [ "arch/instruction_set.h", "base/mutex.h", - "class_loader_context.h", "class_status.h", "debugger.h", "gc_root.h", diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index 2bd541118b..98174142f1 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -672,10 +672,9 @@ static bool IsAbsoluteLocation(const std::string& location) { return !location.empty() && location[0] == '/'; } -ClassLoaderContext::VerificationResult ClassLoaderContext::VerifyClassLoaderContextMatch( - const std::string& context_spec, - bool verify_names, - bool verify_checksums) const { +bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec, + bool verify_names, + bool verify_checksums) const { if (verify_names || verify_checksums) { DCHECK(dex_files_open_attempted_); DCHECK(dex_files_open_result_); @@ -684,21 +683,15 @@ ClassLoaderContext::VerificationResult ClassLoaderContext::VerifyClassLoaderCont ClassLoaderContext expected_context; if (!expected_context.Parse(context_spec, verify_checksums)) { LOG(WARNING) << "Invalid class loader context: " << context_spec; - return VerificationResult::kMismatch; + return false; } // Special shared library contexts always match. They essentially instruct the runtime // to ignore the class path check because the oat file is known to be loaded in different // contexts. OatFileManager will further verify if the oat file can be loaded based on the // collision check. - if (expected_context.special_shared_library_) { - // Special case where we are the only entry in the class path. - if (class_loader_chain_.size() == 1 && class_loader_chain_[0].classpath.size() == 0) { - return VerificationResult::kVerifies; - } - return VerificationResult::kForcedToSkipChecks; - } else if (special_shared_library_) { - return VerificationResult::kForcedToSkipChecks; + if (special_shared_library_ || expected_context.special_shared_library_) { + return true; } if (expected_context.class_loader_chain_.size() != class_loader_chain_.size()) { @@ -706,7 +699,7 @@ ClassLoaderContext::VerificationResult ClassLoaderContext::VerifyClassLoaderCont << expected_context.class_loader_chain_.size() << ", actual=" << class_loader_chain_.size() << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return VerificationResult::kMismatch; + return false; } for (size_t i = 0; i < class_loader_chain_.size(); i++) { @@ -717,14 +710,14 @@ ClassLoaderContext::VerificationResult ClassLoaderContext::VerifyClassLoaderCont << ". expected=" << GetClassLoaderTypeName(expected_info.type) << ", found=" << GetClassLoaderTypeName(info.type) << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return VerificationResult::kMismatch; + return false; } if (info.classpath.size() != expected_info.classpath.size()) { LOG(WARNING) << "ClassLoaderContext classpath size mismatch for position " << i << ". expected=" << expected_info.classpath.size() << ", found=" << info.classpath.size() << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return VerificationResult::kMismatch; + return false; } if (verify_checksums) { @@ -779,7 +772,7 @@ ClassLoaderContext::VerificationResult ClassLoaderContext::VerifyClassLoaderCont << ". expected=" << expected_info.classpath[k] << ", found=" << info.classpath[k] << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return VerificationResult::kMismatch; + return false; } // Compare the checksums. @@ -788,11 +781,11 @@ ClassLoaderContext::VerificationResult ClassLoaderContext::VerifyClassLoaderCont << ". expected=" << expected_info.checksums[k] << ", found=" << info.checksums[k] << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return VerificationResult::kMismatch; + return false; } } } - return VerificationResult::kVerifies; + return true; } jclass ClassLoaderContext::GetClassLoaderClass(ClassLoaderType type) { diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h index a4268aa09a..1c83007f41 100644 --- a/runtime/class_loader_context.h +++ b/runtime/class_loader_context.h @@ -22,10 +22,8 @@ #include "arch/instruction_set.h" #include "base/dchecked_vector.h" -#include "dex/dex_file.h" #include "handle_scope.h" #include "mirror/class_loader.h" -#include "oat_file.h" #include "scoped_thread_state_change.h" namespace art { @@ -36,18 +34,6 @@ class OatFile; // Utility class which holds the class loader context used during compilation/verification. class ClassLoaderContext { public: - enum class VerificationResult { - kVerifies, - kForcedToSkipChecks, - kMismatch, - }; - - enum ClassLoaderType { - kInvalidClassLoader = 0, - kPathClassLoader = 1, - kDelegateLastClassLoader = 2 - }; - ~ClassLoaderContext(); // Opens requested class path files and appends them to ClassLoaderInfo::opened_dex_files. @@ -123,7 +109,7 @@ class ClassLoaderContext { // This should be called after OpenDexFiles(). // Names are only verified if verify_names is true. // Checksums are only verified if verify_checksums is true. - VerificationResult VerifyClassLoaderContextMatch(const std::string& context_spec, + bool VerifyClassLoaderContextMatch(const std::string& context_spec, bool verify_names = true, bool verify_checksums = true) const; @@ -155,6 +141,12 @@ class ClassLoaderContext { static std::unique_ptr Default(); private: + enum ClassLoaderType { + kInvalidClassLoader = 0, + kPathClassLoader = 1, + kDelegateLastClassLoader = 2 + }; + struct ClassLoaderInfo { // The type of this class loader. ClassLoaderType type; diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc index 5e3f48c100..4689ae4c3f 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -608,17 +608,6 @@ TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) { VerifyClassLoaderPCLFromTestDex(context.get(), 3, "ForClassLoaderA"); } - -TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextFirstElement) { - std::string context_spec = "PCL[]"; - std::unique_ptr context = ParseContextWithChecksums(context_spec); - ASSERT_TRUE(context != nullptr); - PretendContextOpenedDexFiles(context.get()); - // Ensure that the special shared library marks as verified for the first thing in the class path. - ASSERT_EQ(context->VerifyClassLoaderContextMatch(OatFile::kSpecialSharedLibrary), - ClassLoaderContext::VerificationResult::kVerifies); -} - TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) { std::string context_spec = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890]"; std::unique_ptr context = ParseContextWithChecksums(context_spec); @@ -630,36 +619,28 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) { VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex"); VerifyClassLoaderDLC(context.get(), 1, "c.dex"); - ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec), - ClassLoaderContext::VerificationResult::kVerifies); + ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_spec)); std::string wrong_class_loader_type = "PCL[a.dex*123:b.dex*456];PCL[c.dex*890]"; - ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_type), - ClassLoaderContext::VerificationResult::kMismatch); + ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_type)); std::string wrong_class_loader_order = "DLC[c.dex*890];PCL[a.dex*123:b.dex*456]"; - ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_order), - ClassLoaderContext::VerificationResult::kMismatch); + ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_order)); std::string wrong_classpath_order = "PCL[b.dex*456:a.dex*123];DLC[c.dex*890]"; - ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_classpath_order), - ClassLoaderContext::VerificationResult::kMismatch); + ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_classpath_order)); std::string wrong_checksum = "PCL[a.dex*999:b.dex*456];DLC[c.dex*890]"; - ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_checksum), - ClassLoaderContext::VerificationResult::kMismatch); + ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_checksum)); std::string wrong_extra_class_loader = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890];PCL[d.dex*321]"; - ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader), - ClassLoaderContext::VerificationResult::kMismatch); + ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader)); std::string wrong_extra_classpath = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890:d.dex*321]"; - ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_classpath), - ClassLoaderContext::VerificationResult::kMismatch); + ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_classpath)); std::string wrong_spec = "PCL[a.dex*999:b.dex*456];DLC["; - ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_spec), - ClassLoaderContext::VerificationResult::kMismatch); + ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_spec)); } TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { @@ -671,8 +652,7 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { std::unique_ptr context = CreateContextForClassLoader(class_loader_d); std::string context_with_no_base_dir = context->EncodeContextForOatFile(""); - ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_no_base_dir), - ClassLoaderContext::VerificationResult::kVerifies); + ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_no_base_dir)); std::string dex_location = GetTestDexFileName("ForClassLoaderA"); size_t pos = dex_location.rfind('/'); @@ -681,8 +661,7 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { std::string context_with_base_dir = context->EncodeContextForOatFile(parent); ASSERT_NE(context_with_base_dir, context_with_no_base_dir); - ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_base_dir), - ClassLoaderContext::VerificationResult::kVerifies); + ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_base_dir)); } TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultidex) { @@ -690,8 +669,7 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultide std::unique_ptr context = CreateContextForClassLoader(class_loader); - ASSERT_EQ(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")), - ClassLoaderContext::VerificationResult::kVerifies); + ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile(""))); } } // namespace art diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 241102ea83..9c8b6512a7 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -1217,9 +1217,7 @@ bool OatFileAssistant::OatFileInfo::ClassLoaderContextIsOkay(ClassLoaderContext* return false; } - - bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext()) == - ClassLoaderContext::VerificationResult::kVerifies; + bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext()); if (!result) { VLOG(oat) << "ClassLoaderContext check failed. Context was " << file->GetClassLoaderContext() diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 59a1045ba2..16e6cf375c 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -276,19 +276,9 @@ static void AddNext(/*inout*/DexFileAndClassPair& original, } } -static bool CheckClassCollision(const OatFile* oat_file, - const ClassLoaderContext* context, - std::string* error_msg /*out*/) { - std::vector dex_files_loaded = context->FlattenOpenedDexFiles(); - - // Vector that holds the newly opened dex files live, this is done to prevent leaks. - std::vector> opened_dex_files; - - ScopedTrace st("Collision check"); - // Add dex files from the oat file to check. - std::vector dex_files_unloaded; - AddDexFilesFromOat(oat_file, &dex_files_unloaded, &opened_dex_files); - +static bool CollisionCheck(std::vector& dex_files_loaded, + std::vector& dex_files_unloaded, + std::string* error_msg /*out*/) { // Generate type index information for each dex file. std::vector loaded_types; for (const DexFile* dex_file : dex_files_loaded) { @@ -365,10 +355,9 @@ static bool CheckClassCollision(const OatFile* oat_file, // against the following top element. If the descriptor is the same, it is now checked whether // the two elements agree on whether their dex file was from an already-loaded oat-file or the // new oat file. Any disagreement indicates a collision. -OatFileManager::CheckCollisionResult OatFileManager::CheckCollision( - const OatFile* oat_file, - const ClassLoaderContext* context, - /*out*/ std::string* error_msg) const { +bool OatFileManager::HasCollisions(const OatFile* oat_file, + const ClassLoaderContext* context, + std::string* error_msg /*out*/) const { DCHECK(oat_file != nullptr); DCHECK(error_msg != nullptr); @@ -378,59 +367,28 @@ OatFileManager::CheckCollisionResult OatFileManager::CheckCollision( // Note that this has correctness implications as we cannot guarantee that the class resolution // used during compilation is OK (b/37777332). if (context == nullptr) { - LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader"; - return CheckCollisionResult::kSkippedUnsupportedClassLoader; + LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader"; + return false; } - // If the oat file loading context matches the context used during compilation then we accept + // If the pat file loading context matches the context used during compilation then we accept // the oat file without addition checks - ClassLoaderContext::VerificationResult result = context->VerifyClassLoaderContextMatch( - oat_file->GetClassLoaderContext(), - /*verify_names*/ true, - /*verify_checksums*/ true); - switch (result) { - case ClassLoaderContext::VerificationResult::kForcedToSkipChecks: - return CheckCollisionResult::kSkippedClassLoaderContextSharedLibrary; - case ClassLoaderContext::VerificationResult::kMismatch: - // Mismatched context, do the actual collision check. - break; - case ClassLoaderContext::VerificationResult::kVerifies: - return CheckCollisionResult::kNoCollisions; + if (context->VerifyClassLoaderContextMatch(oat_file->GetClassLoaderContext())) { + return false; } // The class loader context does not match. Perform a full duplicate classes check. - return CheckClassCollision(oat_file, context, error_msg) - ? CheckCollisionResult::kPerformedHasCollisions : CheckCollisionResult::kNoCollisions; -} -bool OatFileManager::AcceptOatFile(CheckCollisionResult result) const { - // Take the file only if it has no collisions, or we must take it because of preopting. - // Also accept oat files for shared libraries and unsupported class loaders. - return result != CheckCollisionResult::kPerformedHasCollisions; -} + std::vector dex_files_loaded = context->FlattenOpenedDexFiles(); -bool OatFileManager::ShouldLoadAppImage(CheckCollisionResult check_collision_result, - const OatFile* source_oat_file, - ClassLoaderContext* context, - std::string* error_msg) { - Runtime* const runtime = Runtime::Current(); - if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) { - // If we verified the class loader context (skipping due to the special marker doesn't - // count), then also avoid the collision check. - bool load_image = check_collision_result == CheckCollisionResult::kNoCollisions; - // If we skipped the collision check, we need to reverify to be sure its OK to load the - // image. - if (!load_image && - check_collision_result == - CheckCollisionResult::kSkippedClassLoaderContextSharedLibrary) { - // We can load the app image only if there are no collisions. If we know the - // class loader but didn't do the full collision check in HasCollisions(), - // do it now. b/77342775 - load_image = !CheckClassCollision(source_oat_file, context, error_msg); - } - return load_image; - } - return false; + // Vector that holds the newly opened dex files live, this is done to prevent leaks. + std::vector> opened_dex_files; + + ScopedTrace st("Collision check"); + // Add dex files from the oat file to check. + std::vector dex_files_unloaded; + AddDexFilesFromOat(oat_file, &dex_files_unloaded, &opened_dex_files); + return CollisionCheck(dex_files_loaded, dex_files_unloaded, error_msg); } std::vector> OatFileManager::OpenDexFilesFromOat( @@ -515,17 +473,16 @@ std::vector> OatFileManager::OpenDexFilesFromOat( << reinterpret_cast(oat_file.get()) << " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")"; - CheckCollisionResult check_collision_result = CheckCollisionResult::kPerformedHasCollisions; if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) { // Prevent oat files from being loaded if no class_loader or dex_elements are provided. // This can happen when the deprecated DexFile.(String) is called directly, and it // could load oat files without checking the classpath, which would be incorrect. // Take the file only if it has no collisions, or we must take it because of preopting. - check_collision_result = CheckCollision(oat_file.get(), context.get(), /*out*/ &error_msg); - bool accept_oat_file = AcceptOatFile(check_collision_result); + bool accept_oat_file = + !HasCollisions(oat_file.get(), context.get(), /*out*/ &error_msg); if (!accept_oat_file) { // Failed the collision check. Print warning. - if (runtime->IsDexFileFallbackEnabled()) { + if (Runtime::Current()->IsDexFileFallbackEnabled()) { if (!oat_file_assistant.HasOriginalDexFiles()) { // We need to fallback but don't have original dex files. We have to // fallback to opening the existing oat file. This is potentially @@ -572,11 +529,10 @@ std::vector> OatFileManager::OpenDexFilesFromOat( // We need to throw away the image space if we are debuggable but the oat-file source of the // image is not otherwise we might get classes with inlined methods or other such things. std::unique_ptr image_space; - if (ShouldLoadAppImage(check_collision_result, - source_oat_file, - context.get(), - &error_msg)) { + if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) { image_space = oat_file_assistant.OpenImageSpace(source_oat_file); + } else { + image_space = nullptr; } if (image_space != nullptr) { ScopedObjectAccess soa(self); diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h index 80456e9b75..038474e31f 100644 --- a/runtime/oat_file_manager.h +++ b/runtime/oat_file_manager.h @@ -108,39 +108,23 @@ class OatFileManager { void SetOnlyUseSystemOatFiles(); private: - enum class CheckCollisionResult { - kSkippedUnsupportedClassLoader, - kSkippedClassLoaderContextSharedLibrary, - kNoCollisions, - kPerformedHasCollisions, - }; - // Check that the class loader context of the given oat file matches the given context. // This will perform a check that all class loaders in the chain have the same type and // classpath. // If the context is null (which means the initial class loader was null or unsupported) - // this returns kSkippedUnsupportedClassLoader. + // this returns false. // If the context does not validate the method will check for duplicate class definitions of // the given oat file against the oat files (either from the class loaders if possible or all // non-boot oat files otherwise). - // Return kPerformedHasCollisions if there are any class definition collisions in the oat_file. - CheckCollisionResult CheckCollision(const OatFile* oat_file, - const ClassLoaderContext* context, - /*out*/ std::string* error_msg) const + // Return true if there are any class definition collisions in the oat_file. + bool HasCollisions(const OatFile* oat_file, + const ClassLoaderContext* context, + /*out*/ std::string* error_msg) const REQUIRES(!Locks::oat_file_manager_lock_); const OatFile* FindOpenedOatFileFromOatLocationLocked(const std::string& oat_location) const REQUIRES(Locks::oat_file_manager_lock_); - // Return true if we should accept the oat file. - bool AcceptOatFile(CheckCollisionResult result) const; - - // Return true if we should attempt to load the app image. - bool ShouldLoadAppImage(CheckCollisionResult check_collision_result, - const OatFile* source_oat_file, - ClassLoaderContext* context, - std::string* error_msg); - std::set> oat_files_ GUARDED_BY(Locks::oat_file_manager_lock_); bool have_non_pic_oat_file_; diff --git a/test/172-app-image-twice/check b/test/172-app-image-twice/check deleted file mode 100755 index 26a97a48ae..0000000000 --- a/test/172-app-image-twice/check +++ /dev/null @@ -1,18 +0,0 @@ -#!/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. - -# Remove all lines not containing "passed". -grep "^passed" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null diff --git a/test/172-app-image-twice/debug_print_class.cc b/test/172-app-image-twice/debug_print_class.cc deleted file mode 100644 index 6c3de20f2d..0000000000 --- a/test/172-app-image-twice/debug_print_class.cc +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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. - */ - -#include "debug_print.h" -#include "dex/dex_file.h" -#include "mirror/class-inl.h" -#include "scoped_thread_state_change-inl.h" -#include "thread-current-inl.h" - -namespace art { - -extern "C" JNIEXPORT void JNICALL Java_Main_debugPrintClass(JNIEnv*, jclass, jclass cls) { - ScopedObjectAccess soa(Thread::Current()); - ObjPtr klass = soa.Decode(cls); - LOG(ERROR) << "klass: " << klass.Ptr() << " dex_file: " << klass->GetDexFile().GetLocation() - << "/" << static_cast(&klass->GetDexFile()) - << " " << DescribeSpace(klass); -} - -} // namespace art diff --git a/test/172-app-image-twice/expected.txt b/test/172-app-image-twice/expected.txt deleted file mode 100644 index b0aad4deb5..0000000000 --- a/test/172-app-image-twice/expected.txt +++ /dev/null @@ -1 +0,0 @@ -passed diff --git a/test/172-app-image-twice/info.txt b/test/172-app-image-twice/info.txt deleted file mode 100644 index 028046e872..0000000000 --- a/test/172-app-image-twice/info.txt +++ /dev/null @@ -1 +0,0 @@ -Regression test for loading the same app image twice. diff --git a/test/172-app-image-twice/profile b/test/172-app-image-twice/profile deleted file mode 100644 index 70cb2efbb5..0000000000 --- a/test/172-app-image-twice/profile +++ /dev/null @@ -1 +0,0 @@ -LTestClass; diff --git a/test/172-app-image-twice/run b/test/172-app-image-twice/run deleted file mode 100644 index aa2819075f..0000000000 --- a/test/172-app-image-twice/run +++ /dev/null @@ -1,28 +0,0 @@ -#!/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. - -# Build an app image with TestClass (specified by profile) and class loader -# context that skips the duplicate class checks. - -# Target and host use a different shell, and we need to special case the -# passing of the class loader context marker. -if [[ "$@" = *" --host "* ]]; then - ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \ - -Xcompiler-option --class-loader-context=\& -else - ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \ - -Xcompiler-option '--class-loader-context=\&' -fi diff --git a/test/172-app-image-twice/src/Main.java b/test/172-app-image-twice/src/Main.java deleted file mode 100644 index a1c151a6bc..0000000000 --- a/test/172-app-image-twice/src/Main.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.Method; - -public class Main { - private static String TEST_NAME = "172-app-image-twice"; - - public static void main(String args[]) throws Exception { - System.loadLibrary(args[0]); - - Class tc1 = Class.forName("TestClass"); - - String dexPath = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + ".jar"; - Class bdcl = Class.forName("dalvik.system.BaseDexClassLoader"); - Method addDexPathMethod = bdcl.getDeclaredMethod("addDexPath", String.class); - addDexPathMethod.invoke(Main.class.getClassLoader(), dexPath); - - Class tc2 = Class.forName("TestClass"); - - // Add extra logging to simulate libcore logging, this logging should not be compared - // against. - System.out.println("Extra logging"); - - if (tc1 != tc2) { - System.out.println("Class mismatch!"); - debugPrintClass(tc1); - debugPrintClass(tc2); - } else { - System.out.println("passed"); - } - } - - public static native void debugPrintClass(Class cls); -} diff --git a/test/172-app-image-twice/src/TestClass.java b/test/172-app-image-twice/src/TestClass.java deleted file mode 100644 index 5381718f6e..0000000000 --- a/test/172-app-image-twice/src/TestClass.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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 TestClass { -} diff --git a/test/Android.bp b/test/Android.bp index 76189f62a9..0c6b449877 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -423,7 +423,6 @@ cc_defaults { "154-gc-loop/heap_interface.cc", "167-visit-locks/visit_locks.cc", "169-threadgroup-jni/jni_daemon_thread.cc", - "172-app-image-twice/debug_print_class.cc", "1945-proxy-method-arguments/get_args.cc", "203-multi-checkpoint/multi_checkpoint.cc", "305-other-fault-handler/fault_handler.cc", diff --git a/test/knownfailures.json b/test/knownfailures.json index f473a99a27..f3137587f6 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -734,7 +734,6 @@ "164-resolution-trampoline-dex-cache", "167-visit-locks", "168-vmstack-annotated", - "172-app-image-twice", "201-built-in-except-detail-messages", "203-multi-checkpoint", "304-method-tracing", -- GitLab From adc9086aac1f9442f5ec80cec5734909f0b0f262 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 11 May 2018 13:03:06 -0700 Subject: [PATCH 385/749] Revert "Revert "Do not load app image for class collisions"" Fixed bug in oat file assistant to allow the special shared library marker. Bug: 77342775 Bug: 79200502 Bug: 79575750 Test: test-art-host This reverts commit 2c7e13b120926d3c3c18d649cd9849ea31b81477. Change-Id: I647f55a07e4aef8bef56fb1ad7ff23056174b135 --- dex2oat/dex2oat.cc | 4 +- runtime/Android.bp | 1 + runtime/class_loader_context.cc | 31 +++--- runtime/class_loader_context.h | 22 +++-- runtime/class_loader_context_test.cc | 44 ++++++--- runtime/oat_file_assistant.cc | 4 +- runtime/oat_file_manager.cc | 96 ++++++++++++++----- runtime/oat_file_manager.h | 26 ++++- test/172-app-image-twice/check | 18 ++++ test/172-app-image-twice/debug_print_class.cc | 33 +++++++ test/172-app-image-twice/expected.txt | 1 + test/172-app-image-twice/info.txt | 1 + test/172-app-image-twice/profile | 1 + test/172-app-image-twice/run | 28 ++++++ test/172-app-image-twice/src/Main.java | 48 ++++++++++ test/172-app-image-twice/src/TestClass.java | 18 ++++ test/Android.bp | 1 + test/knownfailures.json | 1 + 18 files changed, 314 insertions(+), 64 deletions(-) create mode 100755 test/172-app-image-twice/check create mode 100644 test/172-app-image-twice/debug_print_class.cc create mode 100644 test/172-app-image-twice/expected.txt create mode 100644 test/172-app-image-twice/info.txt create mode 100644 test/172-app-image-twice/profile create mode 100644 test/172-app-image-twice/run create mode 100644 test/172-app-image-twice/src/Main.java create mode 100644 test/172-app-image-twice/src/TestClass.java diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 63518be15f..6b65aca943 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1257,10 +1257,10 @@ class Dex2Oat FINAL { if (stored_class_loader_context_ == nullptr) { Usage("Option --stored-class-loader-context has an incorrect format: %s", stored_context_arg.c_str()); - } else if (!class_loader_context_->VerifyClassLoaderContextMatch( + } else if (class_loader_context_->VerifyClassLoaderContextMatch( stored_context_arg, /*verify_names*/ false, - /*verify_checksums*/ false)) { + /*verify_checksums*/ false) != ClassLoaderContext::VerificationResult::kVerifies) { Usage( "Option --stored-class-loader-context '%s' mismatches --class-loader-context '%s'", stored_context_arg.c_str(), diff --git a/runtime/Android.bp b/runtime/Android.bp index 116453b1bf..64e6796ba0 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -422,6 +422,7 @@ gensrcs { srcs: [ "arch/instruction_set.h", "base/mutex.h", + "class_loader_context.h", "class_status.h", "debugger.h", "gc_root.h", diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index 98174142f1..2bd541118b 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -672,9 +672,10 @@ static bool IsAbsoluteLocation(const std::string& location) { return !location.empty() && location[0] == '/'; } -bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec, - bool verify_names, - bool verify_checksums) const { +ClassLoaderContext::VerificationResult ClassLoaderContext::VerifyClassLoaderContextMatch( + const std::string& context_spec, + bool verify_names, + bool verify_checksums) const { if (verify_names || verify_checksums) { DCHECK(dex_files_open_attempted_); DCHECK(dex_files_open_result_); @@ -683,15 +684,21 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex ClassLoaderContext expected_context; if (!expected_context.Parse(context_spec, verify_checksums)) { LOG(WARNING) << "Invalid class loader context: " << context_spec; - return false; + return VerificationResult::kMismatch; } // Special shared library contexts always match. They essentially instruct the runtime // to ignore the class path check because the oat file is known to be loaded in different // contexts. OatFileManager will further verify if the oat file can be loaded based on the // collision check. - if (special_shared_library_ || expected_context.special_shared_library_) { - return true; + if (expected_context.special_shared_library_) { + // Special case where we are the only entry in the class path. + if (class_loader_chain_.size() == 1 && class_loader_chain_[0].classpath.size() == 0) { + return VerificationResult::kVerifies; + } + return VerificationResult::kForcedToSkipChecks; + } else if (special_shared_library_) { + return VerificationResult::kForcedToSkipChecks; } if (expected_context.class_loader_chain_.size() != class_loader_chain_.size()) { @@ -699,7 +706,7 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << expected_context.class_loader_chain_.size() << ", actual=" << class_loader_chain_.size() << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } for (size_t i = 0; i < class_loader_chain_.size(); i++) { @@ -710,14 +717,14 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << ". expected=" << GetClassLoaderTypeName(expected_info.type) << ", found=" << GetClassLoaderTypeName(info.type) << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } if (info.classpath.size() != expected_info.classpath.size()) { LOG(WARNING) << "ClassLoaderContext classpath size mismatch for position " << i << ". expected=" << expected_info.classpath.size() << ", found=" << info.classpath.size() << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } if (verify_checksums) { @@ -772,7 +779,7 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << ". expected=" << expected_info.classpath[k] << ", found=" << info.classpath[k] << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } // Compare the checksums. @@ -781,11 +788,11 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << ". expected=" << expected_info.checksums[k] << ", found=" << info.checksums[k] << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } } } - return true; + return VerificationResult::kVerifies; } jclass ClassLoaderContext::GetClassLoaderClass(ClassLoaderType type) { diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h index 1c83007f41..a4268aa09a 100644 --- a/runtime/class_loader_context.h +++ b/runtime/class_loader_context.h @@ -22,8 +22,10 @@ #include "arch/instruction_set.h" #include "base/dchecked_vector.h" +#include "dex/dex_file.h" #include "handle_scope.h" #include "mirror/class_loader.h" +#include "oat_file.h" #include "scoped_thread_state_change.h" namespace art { @@ -34,6 +36,18 @@ class OatFile; // Utility class which holds the class loader context used during compilation/verification. class ClassLoaderContext { public: + enum class VerificationResult { + kVerifies, + kForcedToSkipChecks, + kMismatch, + }; + + enum ClassLoaderType { + kInvalidClassLoader = 0, + kPathClassLoader = 1, + kDelegateLastClassLoader = 2 + }; + ~ClassLoaderContext(); // Opens requested class path files and appends them to ClassLoaderInfo::opened_dex_files. @@ -109,7 +123,7 @@ class ClassLoaderContext { // This should be called after OpenDexFiles(). // Names are only verified if verify_names is true. // Checksums are only verified if verify_checksums is true. - bool VerifyClassLoaderContextMatch(const std::string& context_spec, + VerificationResult VerifyClassLoaderContextMatch(const std::string& context_spec, bool verify_names = true, bool verify_checksums = true) const; @@ -141,12 +155,6 @@ class ClassLoaderContext { static std::unique_ptr Default(); private: - enum ClassLoaderType { - kInvalidClassLoader = 0, - kPathClassLoader = 1, - kDelegateLastClassLoader = 2 - }; - struct ClassLoaderInfo { // The type of this class loader. ClassLoaderType type; diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc index 4689ae4c3f..5e3f48c100 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -608,6 +608,17 @@ TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) { VerifyClassLoaderPCLFromTestDex(context.get(), 3, "ForClassLoaderA"); } + +TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextFirstElement) { + std::string context_spec = "PCL[]"; + std::unique_ptr context = ParseContextWithChecksums(context_spec); + ASSERT_TRUE(context != nullptr); + PretendContextOpenedDexFiles(context.get()); + // Ensure that the special shared library marks as verified for the first thing in the class path. + ASSERT_EQ(context->VerifyClassLoaderContextMatch(OatFile::kSpecialSharedLibrary), + ClassLoaderContext::VerificationResult::kVerifies); +} + TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) { std::string context_spec = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890]"; std::unique_ptr context = ParseContextWithChecksums(context_spec); @@ -619,28 +630,36 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) { VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex"); VerifyClassLoaderDLC(context.get(), 1, "c.dex"); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_spec)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec), + ClassLoaderContext::VerificationResult::kVerifies); std::string wrong_class_loader_type = "PCL[a.dex*123:b.dex*456];PCL[c.dex*890]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_type)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_type), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_class_loader_order = "DLC[c.dex*890];PCL[a.dex*123:b.dex*456]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_order)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_order), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_classpath_order = "PCL[b.dex*456:a.dex*123];DLC[c.dex*890]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_classpath_order)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_classpath_order), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_checksum = "PCL[a.dex*999:b.dex*456];DLC[c.dex*890]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_checksum)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_checksum), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_extra_class_loader = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890];PCL[d.dex*321]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_extra_classpath = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890:d.dex*321]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_classpath)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_classpath), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_spec = "PCL[a.dex*999:b.dex*456];DLC["; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_spec)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_spec), + ClassLoaderContext::VerificationResult::kMismatch); } TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { @@ -652,7 +671,8 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { std::unique_ptr context = CreateContextForClassLoader(class_loader_d); std::string context_with_no_base_dir = context->EncodeContextForOatFile(""); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_no_base_dir)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_no_base_dir), + ClassLoaderContext::VerificationResult::kVerifies); std::string dex_location = GetTestDexFileName("ForClassLoaderA"); size_t pos = dex_location.rfind('/'); @@ -661,7 +681,8 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { std::string context_with_base_dir = context->EncodeContextForOatFile(parent); ASSERT_NE(context_with_base_dir, context_with_no_base_dir); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_base_dir)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_base_dir), + ClassLoaderContext::VerificationResult::kVerifies); } TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultidex) { @@ -669,7 +690,8 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultide std::unique_ptr context = CreateContextForClassLoader(class_loader); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile(""))); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")), + ClassLoaderContext::VerificationResult::kVerifies); } } // namespace art diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 9c8b6512a7..7d69927ffb 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -1217,7 +1217,9 @@ bool OatFileAssistant::OatFileInfo::ClassLoaderContextIsOkay(ClassLoaderContext* return false; } - bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext()); + + const bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext()) != + ClassLoaderContext::VerificationResult::kMismatch; if (!result) { VLOG(oat) << "ClassLoaderContext check failed. Context was " << file->GetClassLoaderContext() diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 16e6cf375c..59a1045ba2 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -276,9 +276,19 @@ static void AddNext(/*inout*/DexFileAndClassPair& original, } } -static bool CollisionCheck(std::vector& dex_files_loaded, - std::vector& dex_files_unloaded, - std::string* error_msg /*out*/) { +static bool CheckClassCollision(const OatFile* oat_file, + const ClassLoaderContext* context, + std::string* error_msg /*out*/) { + std::vector dex_files_loaded = context->FlattenOpenedDexFiles(); + + // Vector that holds the newly opened dex files live, this is done to prevent leaks. + std::vector> opened_dex_files; + + ScopedTrace st("Collision check"); + // Add dex files from the oat file to check. + std::vector dex_files_unloaded; + AddDexFilesFromOat(oat_file, &dex_files_unloaded, &opened_dex_files); + // Generate type index information for each dex file. std::vector loaded_types; for (const DexFile* dex_file : dex_files_loaded) { @@ -355,9 +365,10 @@ static bool CollisionCheck(std::vector& dex_files_loaded, // against the following top element. If the descriptor is the same, it is now checked whether // the two elements agree on whether their dex file was from an already-loaded oat-file or the // new oat file. Any disagreement indicates a collision. -bool OatFileManager::HasCollisions(const OatFile* oat_file, - const ClassLoaderContext* context, - std::string* error_msg /*out*/) const { +OatFileManager::CheckCollisionResult OatFileManager::CheckCollision( + const OatFile* oat_file, + const ClassLoaderContext* context, + /*out*/ std::string* error_msg) const { DCHECK(oat_file != nullptr); DCHECK(error_msg != nullptr); @@ -367,28 +378,59 @@ bool OatFileManager::HasCollisions(const OatFile* oat_file, // Note that this has correctness implications as we cannot guarantee that the class resolution // used during compilation is OK (b/37777332). if (context == nullptr) { - LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader"; - return false; + LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader"; + return CheckCollisionResult::kSkippedUnsupportedClassLoader; } - // If the pat file loading context matches the context used during compilation then we accept + // If the oat file loading context matches the context used during compilation then we accept // the oat file without addition checks - if (context->VerifyClassLoaderContextMatch(oat_file->GetClassLoaderContext())) { - return false; + ClassLoaderContext::VerificationResult result = context->VerifyClassLoaderContextMatch( + oat_file->GetClassLoaderContext(), + /*verify_names*/ true, + /*verify_checksums*/ true); + switch (result) { + case ClassLoaderContext::VerificationResult::kForcedToSkipChecks: + return CheckCollisionResult::kSkippedClassLoaderContextSharedLibrary; + case ClassLoaderContext::VerificationResult::kMismatch: + // Mismatched context, do the actual collision check. + break; + case ClassLoaderContext::VerificationResult::kVerifies: + return CheckCollisionResult::kNoCollisions; } // The class loader context does not match. Perform a full duplicate classes check. + return CheckClassCollision(oat_file, context, error_msg) + ? CheckCollisionResult::kPerformedHasCollisions : CheckCollisionResult::kNoCollisions; +} - std::vector dex_files_loaded = context->FlattenOpenedDexFiles(); - - // Vector that holds the newly opened dex files live, this is done to prevent leaks. - std::vector> opened_dex_files; +bool OatFileManager::AcceptOatFile(CheckCollisionResult result) const { + // Take the file only if it has no collisions, or we must take it because of preopting. + // Also accept oat files for shared libraries and unsupported class loaders. + return result != CheckCollisionResult::kPerformedHasCollisions; +} - ScopedTrace st("Collision check"); - // Add dex files from the oat file to check. - std::vector dex_files_unloaded; - AddDexFilesFromOat(oat_file, &dex_files_unloaded, &opened_dex_files); - return CollisionCheck(dex_files_loaded, dex_files_unloaded, error_msg); +bool OatFileManager::ShouldLoadAppImage(CheckCollisionResult check_collision_result, + const OatFile* source_oat_file, + ClassLoaderContext* context, + std::string* error_msg) { + Runtime* const runtime = Runtime::Current(); + if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) { + // If we verified the class loader context (skipping due to the special marker doesn't + // count), then also avoid the collision check. + bool load_image = check_collision_result == CheckCollisionResult::kNoCollisions; + // If we skipped the collision check, we need to reverify to be sure its OK to load the + // image. + if (!load_image && + check_collision_result == + CheckCollisionResult::kSkippedClassLoaderContextSharedLibrary) { + // We can load the app image only if there are no collisions. If we know the + // class loader but didn't do the full collision check in HasCollisions(), + // do it now. b/77342775 + load_image = !CheckClassCollision(source_oat_file, context, error_msg); + } + return load_image; + } + return false; } std::vector> OatFileManager::OpenDexFilesFromOat( @@ -473,16 +515,17 @@ std::vector> OatFileManager::OpenDexFilesFromOat( << reinterpret_cast(oat_file.get()) << " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")"; + CheckCollisionResult check_collision_result = CheckCollisionResult::kPerformedHasCollisions; if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) { // Prevent oat files from being loaded if no class_loader or dex_elements are provided. // This can happen when the deprecated DexFile.(String) is called directly, and it // could load oat files without checking the classpath, which would be incorrect. // Take the file only if it has no collisions, or we must take it because of preopting. - bool accept_oat_file = - !HasCollisions(oat_file.get(), context.get(), /*out*/ &error_msg); + check_collision_result = CheckCollision(oat_file.get(), context.get(), /*out*/ &error_msg); + bool accept_oat_file = AcceptOatFile(check_collision_result); if (!accept_oat_file) { // Failed the collision check. Print warning. - if (Runtime::Current()->IsDexFileFallbackEnabled()) { + if (runtime->IsDexFileFallbackEnabled()) { if (!oat_file_assistant.HasOriginalDexFiles()) { // We need to fallback but don't have original dex files. We have to // fallback to opening the existing oat file. This is potentially @@ -529,10 +572,11 @@ std::vector> OatFileManager::OpenDexFilesFromOat( // We need to throw away the image space if we are debuggable but the oat-file source of the // image is not otherwise we might get classes with inlined methods or other such things. std::unique_ptr image_space; - if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) { + if (ShouldLoadAppImage(check_collision_result, + source_oat_file, + context.get(), + &error_msg)) { image_space = oat_file_assistant.OpenImageSpace(source_oat_file); - } else { - image_space = nullptr; } if (image_space != nullptr) { ScopedObjectAccess soa(self); diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h index 038474e31f..80456e9b75 100644 --- a/runtime/oat_file_manager.h +++ b/runtime/oat_file_manager.h @@ -108,23 +108,39 @@ class OatFileManager { void SetOnlyUseSystemOatFiles(); private: + enum class CheckCollisionResult { + kSkippedUnsupportedClassLoader, + kSkippedClassLoaderContextSharedLibrary, + kNoCollisions, + kPerformedHasCollisions, + }; + // Check that the class loader context of the given oat file matches the given context. // This will perform a check that all class loaders in the chain have the same type and // classpath. // If the context is null (which means the initial class loader was null or unsupported) - // this returns false. + // this returns kSkippedUnsupportedClassLoader. // If the context does not validate the method will check for duplicate class definitions of // the given oat file against the oat files (either from the class loaders if possible or all // non-boot oat files otherwise). - // Return true if there are any class definition collisions in the oat_file. - bool HasCollisions(const OatFile* oat_file, - const ClassLoaderContext* context, - /*out*/ std::string* error_msg) const + // Return kPerformedHasCollisions if there are any class definition collisions in the oat_file. + CheckCollisionResult CheckCollision(const OatFile* oat_file, + const ClassLoaderContext* context, + /*out*/ std::string* error_msg) const REQUIRES(!Locks::oat_file_manager_lock_); const OatFile* FindOpenedOatFileFromOatLocationLocked(const std::string& oat_location) const REQUIRES(Locks::oat_file_manager_lock_); + // Return true if we should accept the oat file. + bool AcceptOatFile(CheckCollisionResult result) const; + + // Return true if we should attempt to load the app image. + bool ShouldLoadAppImage(CheckCollisionResult check_collision_result, + const OatFile* source_oat_file, + ClassLoaderContext* context, + std::string* error_msg); + std::set> oat_files_ GUARDED_BY(Locks::oat_file_manager_lock_); bool have_non_pic_oat_file_; diff --git a/test/172-app-image-twice/check b/test/172-app-image-twice/check new file mode 100755 index 0000000000..26a97a48ae --- /dev/null +++ b/test/172-app-image-twice/check @@ -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. + +# Remove all lines not containing "passed". +grep "^passed" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null diff --git a/test/172-app-image-twice/debug_print_class.cc b/test/172-app-image-twice/debug_print_class.cc new file mode 100644 index 0000000000..6c3de20f2d --- /dev/null +++ b/test/172-app-image-twice/debug_print_class.cc @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#include "debug_print.h" +#include "dex/dex_file.h" +#include "mirror/class-inl.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-current-inl.h" + +namespace art { + +extern "C" JNIEXPORT void JNICALL Java_Main_debugPrintClass(JNIEnv*, jclass, jclass cls) { + ScopedObjectAccess soa(Thread::Current()); + ObjPtr klass = soa.Decode(cls); + LOG(ERROR) << "klass: " << klass.Ptr() << " dex_file: " << klass->GetDexFile().GetLocation() + << "/" << static_cast(&klass->GetDexFile()) + << " " << DescribeSpace(klass); +} + +} // namespace art diff --git a/test/172-app-image-twice/expected.txt b/test/172-app-image-twice/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/172-app-image-twice/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/172-app-image-twice/info.txt b/test/172-app-image-twice/info.txt new file mode 100644 index 0000000000..028046e872 --- /dev/null +++ b/test/172-app-image-twice/info.txt @@ -0,0 +1 @@ +Regression test for loading the same app image twice. diff --git a/test/172-app-image-twice/profile b/test/172-app-image-twice/profile new file mode 100644 index 0000000000..70cb2efbb5 --- /dev/null +++ b/test/172-app-image-twice/profile @@ -0,0 +1 @@ +LTestClass; diff --git a/test/172-app-image-twice/run b/test/172-app-image-twice/run new file mode 100644 index 0000000000..aa2819075f --- /dev/null +++ b/test/172-app-image-twice/run @@ -0,0 +1,28 @@ +#!/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. + +# Build an app image with TestClass (specified by profile) and class loader +# context that skips the duplicate class checks. + +# Target and host use a different shell, and we need to special case the +# passing of the class loader context marker. +if [[ "$@" = *" --host "* ]]; then + ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \ + -Xcompiler-option --class-loader-context=\& +else + ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \ + -Xcompiler-option '--class-loader-context=\&' +fi diff --git a/test/172-app-image-twice/src/Main.java b/test/172-app-image-twice/src/Main.java new file mode 100644 index 0000000000..a1c151a6bc --- /dev/null +++ b/test/172-app-image-twice/src/Main.java @@ -0,0 +1,48 @@ +/* + * 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.Method; + +public class Main { + private static String TEST_NAME = "172-app-image-twice"; + + public static void main(String args[]) throws Exception { + System.loadLibrary(args[0]); + + Class tc1 = Class.forName("TestClass"); + + String dexPath = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + ".jar"; + Class bdcl = Class.forName("dalvik.system.BaseDexClassLoader"); + Method addDexPathMethod = bdcl.getDeclaredMethod("addDexPath", String.class); + addDexPathMethod.invoke(Main.class.getClassLoader(), dexPath); + + Class tc2 = Class.forName("TestClass"); + + // Add extra logging to simulate libcore logging, this logging should not be compared + // against. + System.out.println("Extra logging"); + + if (tc1 != tc2) { + System.out.println("Class mismatch!"); + debugPrintClass(tc1); + debugPrintClass(tc2); + } else { + System.out.println("passed"); + } + } + + public static native void debugPrintClass(Class cls); +} diff --git a/test/172-app-image-twice/src/TestClass.java b/test/172-app-image-twice/src/TestClass.java new file mode 100644 index 0000000000..5381718f6e --- /dev/null +++ b/test/172-app-image-twice/src/TestClass.java @@ -0,0 +1,18 @@ +/* + * 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 TestClass { +} diff --git a/test/Android.bp b/test/Android.bp index 0c6b449877..76189f62a9 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -423,6 +423,7 @@ cc_defaults { "154-gc-loop/heap_interface.cc", "167-visit-locks/visit_locks.cc", "169-threadgroup-jni/jni_daemon_thread.cc", + "172-app-image-twice/debug_print_class.cc", "1945-proxy-method-arguments/get_args.cc", "203-multi-checkpoint/multi_checkpoint.cc", "305-other-fault-handler/fault_handler.cc", diff --git a/test/knownfailures.json b/test/knownfailures.json index f3137587f6..f473a99a27 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -734,6 +734,7 @@ "164-resolution-trampoline-dex-cache", "167-visit-locks", "168-vmstack-annotated", + "172-app-image-twice", "201-built-in-except-detail-messages", "203-multi-checkpoint", "304-method-tracing", -- GitLab From 2300b2d45aba525a163688e5e8faa7448dbfcaef Mon Sep 17 00:00:00 2001 From: David Sehr Date: Thu, 10 May 2018 14:20:10 -0700 Subject: [PATCH 386/749] Remove MemMap dependency on libbacktrace Remove the dependency on BacktraceMap/libbacktrace for MemMap. The removed code was entirely debug, but we may want to add back some of it to facilitate test debugging. This removes the dependency from libartbase on libbacktrace and makes it possible to use libartbase from libdexflie without causing a cyclic dependency. This facilitates moving ArtDexFileLoader and some other code into libartbase, and gets rid of some debt accumulated during all these refactorings. Bug: 78652467 Test: make -j 50 checkbuild Change-Id: Ic575a764817ba660b851f7d5b5c60e43f8356fea --- libartbase/Android.bp | 1 - libartbase/base/mem_map.cc | 76 +++----------------------------------- libdexfile/Android.bp | 1 + runtime/oat_file.cc | 8 ++-- 4 files changed, 11 insertions(+), 75 deletions(-) diff --git a/libartbase/Android.bp b/libartbase/Android.bp index 50abdd36af..065f3eb07a 100644 --- a/libartbase/Android.bp +++ b/libartbase/Android.bp @@ -62,7 +62,6 @@ cc_defaults { generated_sources: ["art_libartbase_operator_srcs"], cflags: ["-DBUILDING_LIBART=1"], shared_libs: [ - "libbacktrace", "liblog", // For ashmem. "libcutils", diff --git a/libartbase/base/mem_map.cc b/libartbase/base/mem_map.cc index 9a1392ceee..c455fed829 100644 --- a/libartbase/base/mem_map.cc +++ b/libartbase/base/mem_map.cc @@ -29,7 +29,6 @@ #include "android-base/stringprintf.h" #include "android-base/unique_fd.h" -#include "backtrace/BacktraceMap.h" #include "cutils/ashmem.h" #include "allocator.h" @@ -57,21 +56,6 @@ using Maps = AllocationTrackingMultiMap; // All the non-empty MemMaps. Use a multimap as we do a reserve-and-divide (eg ElfMap::Load()). static Maps* gMaps GUARDED_BY(MemMap::GetMemMapsLock()) = nullptr; -static std::ostream& operator<<( - std::ostream& os, - std::pair iters) { - for (BacktraceMap::iterator it = iters.first; it != iters.second; ++it) { - const backtrace_map_t* entry = *it; - os << StringPrintf("0x%08x-0x%08x %c%c%c %s\n", - static_cast(entry->start), - static_cast(entry->end), - (entry->flags & PROT_READ) ? 'r' : '-', - (entry->flags & PROT_WRITE) ? 'w' : '-', - (entry->flags & PROT_EXEC) ? 'x' : '-', entry->name.c_str()); - } - return os; -} - std::ostream& operator<<(std::ostream& os, const Maps& mem_maps) { os << "MemMap:" << std::endl; for (auto it = mem_maps.begin(); it != mem_maps.end(); ++it) { @@ -149,8 +133,6 @@ bool MemMap::ContainedWithinExistingMap(uint8_t* ptr, size_t size, std::string* uintptr_t begin = reinterpret_cast(ptr); uintptr_t end = begin + size; - // There is a suspicion that BacktraceMap::Create is occasionally missing maps. TODO: Investigate - // further. { std::lock_guard mu(*mem_maps_lock_); for (auto& pair : *gMaps) { @@ -162,22 +144,6 @@ bool MemMap::ContainedWithinExistingMap(uint8_t* ptr, size_t size, std::string* } } - std::unique_ptr map(BacktraceMap::Create(getpid(), true)); - if (map == nullptr) { - if (error_msg != nullptr) { - *error_msg = StringPrintf("Failed to build process map"); - } - return false; - } - - ScopedBacktraceMapIteratorLock lock(map.get()); - for (BacktraceMap::iterator it = map->begin(); it != map->end(); ++it) { - const backtrace_map_t* entry = *it; - if ((begin >= entry->start && begin < entry->end) // start of new within old - && (end > entry->start && end <= entry->end)) { // end of new within old - return true; - } - } if (error_msg != nullptr) { PrintFileToLog("/proc/self/maps", LogSeverity::ERROR); *error_msg = StringPrintf("Requested region 0x%08" PRIxPTR "-0x%08" PRIxPTR " does not overlap " @@ -186,36 +152,6 @@ bool MemMap::ContainedWithinExistingMap(uint8_t* ptr, size_t size, std::string* return false; } -// Return true if the address range does not conflict with any /proc/self/maps entry. -static bool CheckNonOverlapping(uintptr_t begin, - uintptr_t end, - std::string* error_msg) { - std::unique_ptr map(BacktraceMap::Create(getpid(), true)); - if (map.get() == nullptr) { - *error_msg = StringPrintf("Failed to build process map"); - return false; - } - ScopedBacktraceMapIteratorLock lock(map.get()); - for (BacktraceMap::iterator it = map->begin(); it != map->end(); ++it) { - const backtrace_map_t* entry = *it; - if ((begin >= entry->start && begin < entry->end) // start of new within old - || (end > entry->start && end < entry->end) // end of new within old - || (begin <= entry->start && end > entry->end)) { // start/end of new includes all of old - std::ostringstream map_info; - map_info << std::make_pair(it, map->end()); - *error_msg = StringPrintf("Requested region 0x%08" PRIxPTR "-0x%08" PRIxPTR " overlaps with " - "existing map 0x%08" PRIxPTR "-0x%08" PRIxPTR " (%s)\n%s", - begin, end, - static_cast(entry->start), - static_cast(entry->end), - entry->name.c_str(), - map_info.str().c_str()); - return false; - } - } - return true; -} - // CheckMapRequest to validate a non-MAP_FAILED mmap result based on // the expected value, calling munmap if validation fails, giving the // reason in error_msg. @@ -236,7 +172,6 @@ static bool CheckMapRequest(uint8_t* expected_ptr, void* actual_ptr, size_t byte uintptr_t actual = reinterpret_cast(actual_ptr); uintptr_t expected = reinterpret_cast(expected_ptr); - uintptr_t limit = expected + byte_count; if (expected_ptr == actual_ptr) { return true; @@ -256,15 +191,16 @@ static bool CheckMapRequest(uint8_t* expected_ptr, void* actual_ptr, size_t byte // true, even if there is no overlap // - There might have been an overlap at the point of mmap, but the // overlapping region has since been unmapped. - std::string error_detail; - CheckNonOverlapping(expected, limit, &error_detail); + + // Tell the client the mappings that were in place at the time. + if (kIsDebugBuild) { + PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); + } + std::ostringstream os; os << StringPrintf("Failed to mmap at expected address, mapped at " "0x%08" PRIxPTR " instead of 0x%08" PRIxPTR, actual, expected); - if (!error_detail.empty()) { - os << " : " << error_detail; - } *error_msg = os.str(); } return false; diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp index b2c041c81f..9d49f371f8 100644 --- a/libdexfile/Android.bp +++ b/libdexfile/Android.bp @@ -55,6 +55,7 @@ cc_defaults { }, generated_sources: ["dexfile_operator_srcs"], shared_libs: [ + "libartbase", // Important note: relying on libartbase's header_lib is perfectly acceptable. // However, relying on the libartbase shared library introduces further, possibly cyclic, // dependencies for clients outside of ART. diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 371678d4d9..ffbc26c647 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -211,12 +211,12 @@ OatFileBase* OatFileBase::OpenOatFile(int zip_fd, return nullptr; } + ret->PreSetup(elf_filename); + if (!ret->LoadVdex(vdex_filename, writable, low_4gb, error_msg)) { return nullptr; } - ret->PreSetup(elf_filename); - if (!ret->Setup(zip_fd, abs_dex_location, error_msg)) { return nullptr; } @@ -252,12 +252,12 @@ OatFileBase* OatFileBase::OpenOatFile(int zip_fd, return nullptr; } + ret->PreSetup(oat_location); + if (!ret->LoadVdex(vdex_fd, vdex_location, writable, low_4gb, error_msg)) { return nullptr; } - ret->PreSetup(oat_location); - if (!ret->Setup(zip_fd, abs_dex_location, error_msg)) { return nullptr; } -- GitLab From c3e1895e7443c61b77f5c51cd2d18819cade57c1 Mon Sep 17 00:00:00 2001 From: David Sehr Date: Fri, 11 May 2018 16:59:31 -0700 Subject: [PATCH 387/749] Prepare to move ArtDexFileLoader to libdexfile Move file_utils and friends to libartbase so that ArtDexFileLoader can be moved to libdexfile. This will clean up duplication and complexity with zip file handling. Bug: 78652467 Test: make -j 40 test-art-host-gtest Change-Id: Ia5eac1f93caf3fa918b4b48803cbfd842035e29e --- libartbase/Android.bp | 5 ++++ .../arch/instruction_set.cc | 25 ------------------- .../arch/instruction_set.h | 8 +++--- .../arch/instruction_set_test.cc | 0 {runtime => libartbase}/base/file_utils.cc | 5 ++-- {runtime => libartbase}/base/file_utils.h | 7 +++--- .../base/file_utils_test.cc | 0 patchoat/Android.bp | 1 + runtime/Android.bp | 5 ---- runtime/arch/code_offset.h | 2 +- runtime/arch/instruction_set_features.h | 2 +- runtime/elf_file.cc | 23 +++++++++++++++++ 12 files changed, 41 insertions(+), 42 deletions(-) rename {runtime => libartbase}/arch/instruction_set.cc (88%) rename {runtime => libartbase}/arch/instruction_set.h (97%) rename {runtime => libartbase}/arch/instruction_set_test.cc (100%) rename {runtime => libartbase}/base/file_utils.cc (98%) rename {runtime => libartbase}/base/file_utils.h (96%) rename {runtime => libartbase}/base/file_utils_test.cc (100%) diff --git a/libartbase/Android.bp b/libartbase/Android.bp index 50abdd36af..6cd17317d6 100644 --- a/libartbase/Android.bp +++ b/libartbase/Android.bp @@ -19,11 +19,13 @@ cc_defaults { defaults: ["art_defaults"], host_supported: true, srcs: [ + "arch/instruction_set.cc", "base/allocator.cc", "base/arena_allocator.cc", "base/arena_bit_vector.cc", "base/bit_vector.cc", "base/file_magic.cc", + "base/file_utils.cc", "base/hex_dump.cc", "base/logging.cc", "base/malloc_arena_pool.cc", @@ -81,6 +83,7 @@ gensrcs { cmd: "$(location generate_operator_out) art/libartbase $(in) > $(out)", tools: ["generate_operator_out"], srcs: [ + "arch/instruction_set.h", "base/allocator.h", "base/callee_save_type.h", "base/unix_file/fd_file.h", @@ -142,12 +145,14 @@ art_cc_test { "art_gtest_defaults", ], srcs: [ + "arch/instruction_set_test.cc", "base/arena_allocator_test.cc", "base/bit_field_test.cc", "base/bit_string_test.cc", "base/bit_struct_test.cc", "base/bit_utils_test.cc", "base/bit_vector_test.cc", + "base/file_utils_test.cc", "base/hash_set_test.cc", "base/hex_dump_test.cc", "base/histogram_test.cc", diff --git a/runtime/arch/instruction_set.cc b/libartbase/arch/instruction_set.cc similarity index 88% rename from runtime/arch/instruction_set.cc rename to libartbase/arch/instruction_set.cc index b848eb27fc..a187663062 100644 --- a/runtime/arch/instruction_set.cc +++ b/libartbase/arch/instruction_set.cc @@ -16,8 +16,6 @@ #include "instruction_set.h" -// Explicitly include our own elf.h to avoid Linux and other dependencies. -#include "../elf.h" #include "android-base/logging.h" #include "base/bit_utils.h" #include "base/globals.h" @@ -83,29 +81,6 @@ InstructionSet GetInstructionSetFromString(const char* isa_str) { return InstructionSet::kNone; } -InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags) { - switch (e_machine) { - case EM_ARM: - return InstructionSet::kArm; - case EM_AARCH64: - return InstructionSet::kArm64; - case EM_386: - return InstructionSet::kX86; - case EM_X86_64: - return InstructionSet::kX86_64; - case EM_MIPS: { - if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R2 || - (e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { - return InstructionSet::kMips; - } else if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_64R6) { - return InstructionSet::kMips64; - } - break; - } - } - return InstructionSet::kNone; -} - size_t GetInstructionSetAlignment(InstructionSet isa) { switch (isa) { case InstructionSet::kArm: diff --git a/runtime/arch/instruction_set.h b/libartbase/arch/instruction_set.h similarity index 97% rename from runtime/arch/instruction_set.h rename to libartbase/arch/instruction_set.h index 6434005dda..06bd53a6a9 100644 --- a/runtime/arch/instruction_set.h +++ b/libartbase/arch/instruction_set.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_ARCH_INSTRUCTION_SET_H_ -#define ART_RUNTIME_ARCH_INSTRUCTION_SET_H_ +#ifndef ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_ +#define ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_ #include #include @@ -89,8 +89,6 @@ const char* GetInstructionSetString(InstructionSet isa); // Note: Returns kNone when the string cannot be parsed to a known value. InstructionSet GetInstructionSetFromString(const char* instruction_set); -InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags); - // Fatal logging out of line to keep the header clean of logging.h. NO_RETURN void InstructionSetAbort(InstructionSet isa); @@ -299,4 +297,4 @@ static inline TwoWordReturn GetTwoWordSuccessValue(uintptr_t hi, uintptr_t lo) { } // namespace art -#endif // ART_RUNTIME_ARCH_INSTRUCTION_SET_H_ +#endif // ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_ diff --git a/runtime/arch/instruction_set_test.cc b/libartbase/arch/instruction_set_test.cc similarity index 100% rename from runtime/arch/instruction_set_test.cc rename to libartbase/arch/instruction_set_test.cc diff --git a/runtime/base/file_utils.cc b/libartbase/base/file_utils.cc similarity index 98% rename from runtime/base/file_utils.cc rename to libartbase/base/file_utils.cc index 537216c198..9450e1e8c1 100644 --- a/runtime/base/file_utils.cc +++ b/libartbase/base/file_utils.cc @@ -47,7 +47,6 @@ #include "base/os.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" -#include "dex/dex_file_loader.h" #if defined(__APPLE__) #include @@ -64,6 +63,8 @@ namespace art { using android::base::StringAppendF; using android::base::StringPrintf; +static constexpr const char* kClassesDex = "classes.dex"; + bool ReadFileToString(const std::string& file_name, std::string* result) { File file(file_name, O_RDONLY, false); if (!file.IsOpened()) { @@ -224,7 +225,7 @@ bool GetDalvikCacheFilename(const char* location, const char* cache_location, !android::base::EndsWith(location, ".art") && !android::base::EndsWith(location, ".oat")) { cache_file += "/"; - cache_file += DexFileLoader::kClassesDex; + cache_file += kClassesDex; } std::replace(cache_file.begin(), cache_file.end(), '/', '@'); *filename = StringPrintf("%s/%s", cache_location, cache_file.c_str()); diff --git a/runtime/base/file_utils.h b/libartbase/base/file_utils.h similarity index 96% rename from runtime/base/file_utils.h rename to libartbase/base/file_utils.h index d4f6c576c0..063393bd3b 100644 --- a/runtime/base/file_utils.h +++ b/libartbase/base/file_utils.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_FILE_UTILS_H_ -#define ART_RUNTIME_BASE_FILE_UTILS_H_ +#ifndef ART_LIBARTBASE_BASE_FILE_UTILS_H_ +#define ART_LIBARTBASE_BASE_FILE_UTILS_H_ #include @@ -46,6 +46,7 @@ std::string GetDefaultBootImageLocation(std::string* error_msg); // Returns the dalvik-cache location, with subdir appended. Returns the empty string if the cache // could not be found. std::string GetDalvikCache(const char* subdir); + // Return true if we found the dalvik cache and stored it in the dalvik_cache argument. // have_android_data will be set to true if we have an ANDROID_DATA that exists, // dalvik_cache_exists will be true if there is a dalvik-cache directory that is present. @@ -79,4 +80,4 @@ bool LocationIsOnSystemFramework(const char* location); } // namespace art -#endif // ART_RUNTIME_BASE_FILE_UTILS_H_ +#endif // ART_LIBARTBASE_BASE_FILE_UTILS_H_ diff --git a/runtime/base/file_utils_test.cc b/libartbase/base/file_utils_test.cc similarity index 100% rename from runtime/base/file_utils_test.cc rename to libartbase/base/file_utils_test.cc diff --git a/patchoat/Android.bp b/patchoat/Android.bp index 0e8e517cd4..1e2f328ffd 100644 --- a/patchoat/Android.bp +++ b/patchoat/Android.bp @@ -25,6 +25,7 @@ cc_defaults { }, }, shared_libs: [ + "libartbase", "libbase", "libcrypto", // For computing the digest of image file ], diff --git a/runtime/Android.bp b/runtime/Android.bp index 64e6796ba0..472f8ce02f 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -32,7 +32,6 @@ cc_defaults { "art_field.cc", "art_method.cc", "barrier.cc", - "base/file_utils.cc", "base/mem_map_arena_pool.cc", "base/mutex.cc", "base/quasi_atomic.cc", @@ -207,7 +206,6 @@ cc_defaults { "well_known_classes.cc", "arch/context.cc", - "arch/instruction_set.cc", "arch/instruction_set_features.cc", "arch/memcmp16.cc", "arch/arm/instruction_set_features_arm.cc", @@ -420,7 +418,6 @@ gensrcs { cmd: "$(location generate_operator_out) art/runtime $(in) > $(out)", tools: ["generate_operator_out"], srcs: [ - "arch/instruction_set.h", "base/mutex.h", "class_loader_context.h", "class_status.h", @@ -531,7 +528,6 @@ art_cc_test { ], srcs: [ "arch/arch_test.cc", - "arch/instruction_set_test.cc", "arch/instruction_set_features_test.cc", "arch/memcmp16_test.cc", "arch/stub_test.cc", @@ -542,7 +538,6 @@ art_cc_test { "arch/x86/instruction_set_features_x86_test.cc", "arch/x86_64/instruction_set_features_x86_64_test.cc", "barrier_test.cc", - "base/file_utils_test.cc", "base/mutex_test.cc", "base/timing_logger_test.cc", "cha_test.cc", diff --git a/runtime/arch/code_offset.h b/runtime/arch/code_offset.h index 8e8dde4c4c..f0c6d22ef2 100644 --- a/runtime/arch/code_offset.h +++ b/runtime/arch/code_offset.h @@ -21,9 +21,9 @@ #include +#include "arch/instruction_set.h" #include "base/bit_utils.h" #include "base/macros.h" -#include "instruction_set.h" namespace art { diff --git a/runtime/arch/instruction_set_features.h b/runtime/arch/instruction_set_features.h index 5f1a507f7a..c31c927668 100644 --- a/runtime/arch/instruction_set_features.h +++ b/runtime/arch/instruction_set_features.h @@ -21,8 +21,8 @@ #include #include +#include "arch/instruction_set.h" #include "base/macros.h" -#include "instruction_set.h" namespace art { diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index 719b4af293..026b5da748 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -1073,6 +1073,29 @@ bool ElfFileImpl::GetLoadedSize(size_t* size, std::string* error_msg) return true; } +static InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags) { + switch (e_machine) { + case EM_ARM: + return InstructionSet::kArm; + case EM_AARCH64: + return InstructionSet::kArm64; + case EM_386: + return InstructionSet::kX86; + case EM_X86_64: + return InstructionSet::kX86_64; + case EM_MIPS: { + if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R2 || + (e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { + return InstructionSet::kMips; + } else if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_64R6) { + return InstructionSet::kMips64; + } + break; + } + } + return InstructionSet::kNone; +} + template bool ElfFileImpl::Load(File* file, bool executable, -- GitLab From a9d5ccdf887ef5c9f61481f036773bdfadd39fd7 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Mon, 14 May 2018 11:55:51 +0000 Subject: [PATCH 388/749] Revert "Move kVRegSize to globals.h to reduce include dependencies." This reverts commit 5ae7cdfe5b8da645d1fec61c76176e6a37e78fb9. Reason for revert: Unknown crash in linker Change-Id: Ib5646376e2e589a7a6c4d66e72caa1f208a15905 --- compiler/optimizing/code_generator.h | 1 + libartbase/base/globals.h | 3 --- runtime/stack_map.h | 3 +++ 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 833b39ec6a..f0c4ee01cc 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -35,6 +35,7 @@ #include "optimizing_compiler_stats.h" #include "read_barrier_option.h" #include "stack.h" +#include "stack_map.h" #include "utils/label.h" namespace art { diff --git a/libartbase/base/globals.h b/libartbase/base/globals.h index 39e0c509cd..69d1a64a3b 100644 --- a/libartbase/base/globals.h +++ b/libartbase/base/globals.h @@ -38,9 +38,6 @@ static constexpr size_t kStackAlignment = 16; // compile-time constant so the compiler can generate better code. static constexpr int kPageSize = 4096; -// Size of Dex virtual registers. -static constexpr size_t kVRegSize = 4; - // Returns whether the given memory offset can be used for generating // an implicit null check. static inline bool CanDoImplicitNullCheckOn(uintptr_t offset) { diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 274e2b20b4..38397643b6 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -37,6 +37,9 @@ class VariableIndentationOutputStream; // (signed) values. static constexpr ssize_t kFrameSlotSize = 4; +// Size of Dex virtual registers. +static constexpr size_t kVRegSize = 4; + class ArtMethod; class CodeInfo; class StackMapEncoding; -- GitLab From 1ab0fa89aed1100a3e6b631cb188db1d759b1efc Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Fri, 4 May 2018 11:28:03 +0100 Subject: [PATCH 389/749] Refactor ClassLinker & ImageWriter to use a common resolution routine ImageWriter prepopulates DexCaches of an app image, but does resolution directly on mirror::Class rather than going through the class linker where hidden API checks happen. This patch removes the duplicate code. Similarly, ClassLinker contains multiple methods for resolving methods and fields. Consolidate these into three common routines: - FindResolvedMethod - FindResolvedField - FindResolvedFieldJLS The CL also passes the correct class loader to ImageWriter::PruneAndPreloadDexCache because it would trip a DCHECK in class linker. Bug: 78548674 Test: art/test.py Change-Id: I12e383290945d2f44b209c32e8a7617533d86063 --- dex2oat/linker/image_writer.cc | 34 ++++------ runtime/class_linker.cc | 109 +++++++++++++++++++-------------- runtime/class_linker.h | 21 +++++++ 3 files changed, 96 insertions(+), 68 deletions(-) diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 9e5cd8035c..492f9ce5c1 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -1067,18 +1067,12 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr dex_cache, } if (method == nullptr || i < stored_index) { if (last_class != nullptr) { - const char* name = dex_file.StringDataByIdx(method_id.name_idx_); - Signature signature = dex_file.GetMethodSignature(method_id); - if (last_class->IsInterface()) { - method = last_class->FindInterfaceMethod(name, signature, target_ptr_size_); - } else { - method = last_class->FindClassMethod(name, signature, target_ptr_size_); - } - if (method != nullptr) { - // If the referenced class is in the image, the defining class must also be there. - DCHECK(KeepClass(method->GetDeclaringClass())); - dex_cache->SetResolvedMethod(i, method, target_ptr_size_); - } + // Try to resolve the method with the class linker, which will insert + // it into the dex cache if successful. + method = class_linker->FindResolvedMethod(last_class, dex_cache, class_loader, i); + // If the referenced class is in the image, the defining class must also be there. + DCHECK(method == nullptr || KeepClass(method->GetDeclaringClass())); + DCHECK(method == nullptr || dex_cache->GetResolvedMethod(i, target_ptr_size_) == method); } } else { DCHECK_EQ(i, stored_index); @@ -1112,14 +1106,10 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr dex_cache, } if (field == nullptr || i < stored_index) { if (last_class != nullptr) { - const char* name = dex_file.StringDataByIdx(field_id.name_idx_); - const char* type = dex_file.StringByTypeIdx(field_id.type_idx_); - field = mirror::Class::FindField(Thread::Current(), last_class, name, type); - if (field != nullptr) { - // If the referenced class is in the image, the defining class must also be there. - DCHECK(KeepClass(field->GetDeclaringClass())); - dex_cache->SetResolvedField(i, field, target_ptr_size_); - } + field = class_linker->FindResolvedFieldJLS(last_class, dex_cache, class_loader, i); + // If the referenced class is in the image, the defining class must also be there. + DCHECK(field == nullptr || KeepClass(field->GetDeclaringClass())); + DCHECK(field == nullptr || dex_cache->GetResolvedField(i, target_ptr_size_) == field); } } else { DCHECK_EQ(i, stored_index); @@ -1208,7 +1198,9 @@ void ImageWriter::PruneNonImageClasses() { } } for (ObjPtr dex_cache : dex_caches) { - PruneAndPreloadDexCache(dex_cache, class_loader); + // Pass the class loader associated with the DexCache. This can either be + // the app's `class_loader` or `nullptr` if boot class loader. + PruneAndPreloadDexCache(dex_cache, IsInBootImage(dex_cache.Ptr()) ? nullptr : class_loader); } // Drop the array class cache in the ClassLinker, as these are roots holding those classes live. diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 4141a37366..9c8b4383dc 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -4850,6 +4850,9 @@ bool ClassLinker::InitializeClass(Thread* self, Handle klass, const uint32_t field_idx = field->GetDexFieldIndex(); ArtField* resolved_field = dex_cache->GetResolvedField(field_idx, image_pointer_size_); if (resolved_field == nullptr) { + // Populating cache of a dex file which defines `klass` should always be allowed. + DCHECK_EQ(hiddenapi::GetMemberAction( + field, class_loader.Get(), dex_cache.Get(), hiddenapi::kNone), hiddenapi::kAllow); dex_cache->SetResolvedField(field_idx, field, image_pointer_size_); } else { DCHECK_EQ(field, resolved_field); @@ -8053,26 +8056,8 @@ ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx, return nullptr; } DCHECK(klass->IsResolved()); - Thread* self = is_static ? Thread::Current() : nullptr; - - // First try to find a field declared directly by `klass` by the field index. - ArtField* resolved_field = is_static - ? mirror::Class::FindStaticField(self, klass, dex_cache, field_idx) - : klass->FindInstanceField(dex_cache, field_idx); - - if (resolved_field == nullptr) { - // If not found in `klass` by field index, search the class hierarchy using the name and type. - const char* name = dex_file.GetFieldName(field_id); - const char* type = dex_file.GetFieldTypeDescriptor(field_id); - resolved_field = is_static - ? mirror::Class::FindStaticField(self, klass, name, type) - : klass->FindInstanceField(name, type); - } - if (resolved_field != nullptr) { - dex_cache->SetResolvedField(field_idx, resolved_field, image_pointer_size_); - } - return resolved_field; + return FindResolvedField(klass, dex_cache, class_loader, field_idx, is_static); } ArtField* ClassLinker::ResolveField(uint32_t field_idx, @@ -8087,39 +8072,18 @@ ArtField* ClassLinker::ResolveField(uint32_t field_idx, } const DexFile& dex_file = *dex_cache->GetDexFile(); const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); - Thread* const self = Thread::Current(); ObjPtr klass = ResolveType(field_id.class_idx_, dex_cache, class_loader); if (klass == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; } - if (is_static) { - resolved = mirror::Class::FindStaticField(self, klass, dex_cache.Get(), field_idx); - } else { - resolved = klass->FindInstanceField(dex_cache.Get(), field_idx); - } - + resolved = FindResolvedField(klass, dex_cache.Get(), class_loader.Get(), field_idx, is_static); if (resolved == nullptr) { const char* name = dex_file.GetFieldName(field_id); const char* type = dex_file.GetFieldTypeDescriptor(field_id); - if (is_static) { - resolved = mirror::Class::FindStaticField(self, klass, name, type); - } else { - resolved = klass->FindInstanceField(name, type); - } - } - - if (resolved == nullptr || - hiddenapi::GetMemberAction( - resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) { - 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; } @@ -8134,26 +8098,77 @@ ArtField* ClassLinker::ResolveFieldJLS(uint32_t field_idx, } const DexFile& dex_file = *dex_cache->GetDexFile(); const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); - Thread* self = Thread::Current(); ObjPtr klass = ResolveType(field_id.class_idx_, dex_cache, class_loader); if (klass == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; } - StringPiece name(dex_file.GetFieldName(field_id)); - StringPiece type(dex_file.GetFieldTypeDescriptor(field_id)); + resolved = FindResolvedFieldJLS(klass, dex_cache.Get(), class_loader.Get(), field_idx); + if (resolved == nullptr) { + const char* name = dex_file.GetFieldName(field_id); + const char* type = dex_file.GetFieldTypeDescriptor(field_id); + ThrowNoSuchFieldError("", klass, type, name); + } + return resolved; +} + +ArtField* ClassLinker::FindResolvedField(ObjPtr klass, + ObjPtr dex_cache, + ObjPtr class_loader, + uint32_t field_idx, + bool is_static) { + ArtField* resolved = nullptr; + Thread* self = is_static ? Thread::Current() : nullptr; + const DexFile& dex_file = *dex_cache->GetDexFile(); + + resolved = is_static ? mirror::Class::FindStaticField(self, klass, dex_cache, field_idx) + : klass->FindInstanceField(dex_cache, field_idx); + + if (resolved == nullptr) { + const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); + const char* name = dex_file.GetFieldName(field_id); + const char* type = dex_file.GetFieldTypeDescriptor(field_id); + resolved = is_static ? mirror::Class::FindStaticField(self, klass, name, type) + : klass->FindInstanceField(name, type); + } + + if (resolved != nullptr && + hiddenapi::GetMemberAction( + resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) { + resolved = nullptr; + } + + if (resolved != nullptr) { + dex_cache->SetResolvedField(field_idx, resolved, image_pointer_size_); + } + + return resolved; +} + +ArtField* ClassLinker::FindResolvedFieldJLS(ObjPtr klass, + ObjPtr dex_cache, + ObjPtr class_loader, + uint32_t field_idx) { + ArtField* resolved = nullptr; + Thread* self = Thread::Current(); + const DexFile& dex_file = *dex_cache->GetDexFile(); + const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); + + const char* name = dex_file.GetFieldName(field_id); + const char* type = dex_file.GetFieldTypeDescriptor(field_id); resolved = mirror::Class::FindField(self, klass, name, type); + if (resolved != nullptr && hiddenapi::GetMemberAction( - resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) { + resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) { resolved = nullptr; } + if (resolved != nullptr) { dex_cache->SetResolvedField(field_idx, resolved, image_pointer_size_); - } else { - ThrowNoSuchFieldError("", klass, type, name); } + return resolved; } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index e935d1dfb8..60cff9ee37 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -392,6 +392,27 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); + // Find a field with a given ID from the DexFile associated with the given DexCache + // and ClassLoader, storing the result in DexCache. The declaring class is assumed + // to have been already resolved into `klass`. The `is_static` argument is used to + // determine if we are resolving a static or non-static field. + ArtField* FindResolvedField(ObjPtr klass, + ObjPtr dex_cache, + ObjPtr class_loader, + uint32_t field_idx, + bool is_static) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Find a field with a given ID from the DexFile associated with the given DexCache + // and ClassLoader, storing the result in DexCache. The declaring class is assumed + // to have been already resolved into `klass`. No is_static argument is provided + // so that Java field resolution semantics are followed. + ArtField* FindResolvedFieldJLS(ObjPtr klass, + ObjPtr dex_cache, + ObjPtr class_loader, + uint32_t field_idx) + REQUIRES_SHARED(Locks::mutator_lock_); + // Resolve a method type with a given ID from the DexFile associated with a given DexCache // and ClassLoader, storing the result in the DexCache. ObjPtr ResolveMethodType(Thread* self, -- GitLab From 32bde99142c5e59b8cad572d9a7b5a81ee12cd00 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Mon, 14 May 2018 15:24:34 +0100 Subject: [PATCH 390/749] Propagate hiddenapi cmdline flags in OatFileAssistant::Dex2Oat ART can invoke dex2oat on an out-of-date or non-existent oat file. This code path will ignore any runtime hidden API enforcement policy settings. Change the code to set the correct flag and add a gtest for it. Bug: 79680013 Test: make test-art-host-gtest-oat_file_assistant_test Change-Id: Id6aa5e45d11626facb590621d43e2c52b9269b12 --- runtime/hidden_api.h | 16 +++++++++++ runtime/oat_file_assistant.cc | 6 +++++ runtime/oat_file_assistant_test.cc | 43 ++++++++++++++++++++++++++++++ runtime/well_known_classes.cc | 21 ++------------- 4 files changed, 67 insertions(+), 19 deletions(-) diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index 8e21fd3b8f..580224e439 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -95,6 +95,22 @@ inline Action GetActionFromAccessFlags(HiddenApiAccessFlags::ApiList api_list) { } } +class ScopedHiddenApiEnforcementPolicySetting { + public: + explicit ScopedHiddenApiEnforcementPolicySetting(EnforcementPolicy new_policy) + : initial_policy_(Runtime::Current()->GetHiddenApiEnforcementPolicy()) { + Runtime::Current()->SetHiddenApiEnforcementPolicy(new_policy); + } + + ~ScopedHiddenApiEnforcementPolicySetting() { + Runtime::Current()->SetHiddenApiEnforcementPolicy(initial_policy_); + } + + private: + const EnforcementPolicy initial_policy_; + DISALLOW_COPY_AND_ASSIGN(ScopedHiddenApiEnforcementPolicySetting); +}; + // Implementation details. DO NOT ACCESS DIRECTLY. namespace detail { diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 7d69927ffb..4880d83308 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -36,6 +36,7 @@ #include "exec_utils.h" #include "gc/heap.h" #include "gc/space/image_space.h" +#include "hidden_api.h" #include "image.h" #include "oat.h" #include "runtime.h" @@ -823,6 +824,11 @@ bool OatFileAssistant::Dex2Oat(const std::vector& args, argv.push_back("--compiler-filter=verify-none"); } + if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kNoChecks) { + argv.push_back("--runtime-arg"); + argv.push_back("-Xhidden-api-checks"); + } + if (runtime->MustRelocateIfPossible()) { argv.push_back("--runtime-arg"); argv.push_back("-Xrelocate"); diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 99bc0b2c6e..0b3c61d474 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -33,6 +33,7 @@ #include "class_loader_context.h" #include "common_runtime_test.h" #include "dexopt_test.h" +#include "hidden_api.h" #include "oat_file.h" #include "oat_file_manager.h" #include "scoped_thread_state_change-inl.h" @@ -43,6 +44,8 @@ namespace art { static const std::string kSpecialSharedLibrary = "&"; // NOLINT [runtime/string] [4] static ClassLoaderContext* kSpecialSharedLibraryContext = nullptr; +static constexpr char kDex2oatCmdLineHiddenApiArg[] = " --runtime-arg -Xhidden-api-checks"; + class OatFileAssistantTest : public DexoptTest { public: void VerifyOptimizationStatus(const std::string& file, @@ -1413,6 +1416,46 @@ TEST_F(OatFileAssistantTest, MakeUpToDateWithContext) { oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey)); } +TEST_F(OatFileAssistantTest, MakeUpToDateWithHiddenApiDisabled) { + hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption( + hiddenapi::EnforcementPolicy::kNoChecks); + + std::string dex_location = GetScratchDir() + "/TestDexHiddenApiDisabled.jar"; + Copy(GetDexSrc1(), dex_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + std::string error_msg; + int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg); + EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + + std::unique_ptr oat_file = oat_file_assistant.GetBestOatFile(); + EXPECT_NE(nullptr, oat_file.get()); + + const char* cmd_line = oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey); + EXPECT_NE(nullptr, cmd_line); + EXPECT_EQ(nullptr, strstr(cmd_line, kDex2oatCmdLineHiddenApiArg)); +} + +TEST_F(OatFileAssistantTest, MakeUpToDateWithHiddenApiEnabled) { + hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption( + hiddenapi::EnforcementPolicy::kBlacklistOnly); + + std::string dex_location = GetScratchDir() + "/TestDexHiddenApiEnabled.jar"; + Copy(GetDexSrc1(), dex_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + std::string error_msg; + int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg); + EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + + std::unique_ptr oat_file = oat_file_assistant.GetBestOatFile(); + EXPECT_NE(nullptr, oat_file.get()); + + const char* cmd_line = oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey); + EXPECT_NE(nullptr, cmd_line); + EXPECT_NE(nullptr, strstr(cmd_line, kDex2oatCmdLineHiddenApiArg)); +} + TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) { std::string dex_location = GetScratchDir() + "/TestDex.jar"; std::string context_location = GetScratchDir() + "/ContextDex.jar"; diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index b79334ac7f..4843061be6 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -282,26 +282,9 @@ uint32_t WellKnownClasses::StringInitToEntryPoint(ArtMethod* string_init) { } #undef STRING_INIT_LIST -class ScopedHiddenApiExemption { - public: - explicit ScopedHiddenApiExemption(Runtime* runtime) - : runtime_(runtime), - initial_policy_(runtime_->GetHiddenApiEnforcementPolicy()) { - runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks); - } - - ~ScopedHiddenApiExemption() { - runtime_->SetHiddenApiEnforcementPolicy(initial_policy_); - } - - private: - Runtime* runtime_; - const hiddenapi::EnforcementPolicy initial_policy_; - DISALLOW_COPY_AND_ASSIGN(ScopedHiddenApiExemption); -}; - void WellKnownClasses::Init(JNIEnv* env) { - ScopedHiddenApiExemption hiddenapi_exemption(Runtime::Current()); + hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption( + hiddenapi::EnforcementPolicy::kNoChecks); dalvik_annotation_optimization_CriticalNative = CacheClass(env, "dalvik/annotation/optimization/CriticalNative"); -- GitLab From 46226f02fdfe50e1d49ffd77d3e99598ff79d9b9 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Mon, 14 May 2018 15:34:32 +0100 Subject: [PATCH 391/749] Set default hidden API policy to dark+black When invoking dex2oat, only a boolean flag is used to enabled/disable hidden API checks, instead of a 2-bit value passed down to zygote. When the flag is true, use dark+black lists as the enforcement policy. This is conservative, as compiling with both lists enforced may lead to more classes being re-verified at runtime, but correctness is preserved. Bug: 64382372 Test: N/A Change-Id: If15723ad40b99c5475f10561b5b3a669a8fb01c7 --- runtime/runtime.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/runtime.cc b/runtime/runtime.cc index b8775b874f..4142cb0c95 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1198,7 +1198,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // As is, we're encoding some logic here about which specific policy to use, which would be better // controlled by the framework. hidden_api_policy_ = do_hidden_api_checks - ? hiddenapi::EnforcementPolicy::kBlacklistOnly + ? hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList : hiddenapi::EnforcementPolicy::kNoChecks; no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain); -- GitLab From e9c3bbb6a76c49c30562f886c2e480762408a7ed Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Thu, 3 May 2018 11:30:01 +0100 Subject: [PATCH 392/749] Do not log "None" type API accesses. They do not imply any actual access, so logging them just pollutes the logs. Bug: 77517571 Test: m Test: $ adb lolcat -b events | grep sysui_multi_action (cherry picked from commit f59ca619ebd1d546b86a50503d02fc4b3a1e7868) Change-Id: I837030862e24a86f65a004763e32d680de4d2feb --- runtime/hidden_api.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index 9445ae0c8e..9354d721b7 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -158,10 +158,11 @@ inline static int32_t GetEnumValueForLog(AccessMethod access_method) { } void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action action_taken) { - if (access_method == kLinking) { + if (access_method == kLinking || access_method == kNone) { // Linking warnings come from static analysis/compilation of the bytecode // and can contain false positives (i.e. code that is never run). We choose // not to log these in the event log. + // None does not correspond to actual access, so should also be ignored. return; } ComplexEventLogger log_maker(ACTION_HIDDEN_API_ACCESSED); -- GitLab From 06d10a78506fae7e033795cda7b1d9d0e1f1fff5 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Mon, 14 May 2018 08:53:38 +0100 Subject: [PATCH 393/749] ART: Add dex::ProtoIndex Test: m -j32 test-art-host Change-Id: Ic2d2a7a43be1b8590b97cdf3729200b043ffc6a3 --- compiler/optimizing/code_generator.cc | 2 +- compiler/optimizing/instruction_builder.cc | 18 ++-- compiler/optimizing/instruction_builder.h | 8 +- compiler/optimizing/nodes.h | 8 +- dexdump/dexdump.cc | 11 ++- dexlayout/dex_ir.cc | 4 +- libdexfile/dex/dex_file-inl.h | 2 +- libdexfile/dex/dex_file.cc | 10 ++- libdexfile/dex/dex_file.h | 14 +-- libdexfile/dex/dex_file_types.h | 86 +++++++++---------- libdexfile/dex/dex_file_verifier.cc | 7 +- libdexfile/dex/dex_file_verifier.h | 2 +- libdexfile/dex/dex_file_verifier_test.cc | 11 +-- libdexfile/dex/dex_instruction.cc | 20 +++-- runtime/class_linker.cc | 4 +- runtime/class_linker.h | 4 +- runtime/dex/art_dex_file_loader_test.cc | 4 +- runtime/entrypoints/entrypoint_utils.cc | 2 +- runtime/entrypoints/entrypoint_utils.h | 2 +- .../quick/quick_dexcache_entrypoints.cc | 2 +- .../quick/quick_trampoline_entrypoints.cc | 2 +- runtime/interpreter/interpreter_common.cc | 12 +-- runtime/interpreter/interpreter_common.h | 2 +- .../interpreter/interpreter_switch_impl.cc | 2 +- runtime/interpreter/mterp/mterp.cc | 3 +- runtime/mirror/dex_cache-inl.h | 14 +-- runtime/mirror/dex_cache.h | 6 +- runtime/mirror/dex_cache_test.cc | 4 +- runtime/verifier/method_verifier.cc | 8 +- 29 files changed, 144 insertions(+), 130 deletions(-) diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index f57333741c..fb556f435a 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -771,7 +771,7 @@ void CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary( void CodeGenerator::GenerateLoadMethodTypeRuntimeCall(HLoadMethodType* method_type) { LocationSummary* locations = method_type->GetLocations(); - MoveConstant(locations->GetTemp(0), method_type->GetProtoIndex()); + MoveConstant(locations->GetTemp(0), method_type->GetProtoIndex().index_); CheckEntrypointTypes(); InvokeRuntime(kQuickResolveMethodType, method_type, method_type->GetDexPc()); } diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 35a39456a2..0e20a65a2d 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1055,7 +1055,7 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, bool HInstructionBuilder::BuildInvokePolymorphic(const Instruction& instruction ATTRIBUTE_UNUSED, uint32_t dex_pc, uint32_t method_idx, - uint32_t proto_idx, + dex::ProtoIndex proto_idx, uint32_t number_of_vreg_arguments, bool is_range, uint32_t* args, @@ -1896,17 +1896,17 @@ bool HInstructionBuilder::LoadClassNeedsAccessCheck(Handle klass) } } -void HInstructionBuilder::BuildLoadMethodHandle(uint16_t proto_idx, uint32_t dex_pc) { +void HInstructionBuilder::BuildLoadMethodHandle(uint16_t method_handle_index, uint32_t dex_pc) { const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); - HLoadMethodHandle* load_method_handle = - new (allocator_) HLoadMethodHandle(graph_->GetCurrentMethod(), proto_idx, dex_file, dex_pc); + HLoadMethodHandle* load_method_handle = new (allocator_) HLoadMethodHandle( + graph_->GetCurrentMethod(), method_handle_index, dex_file, dex_pc); AppendInstruction(load_method_handle); } -void HInstructionBuilder::BuildLoadMethodType(uint16_t proto_idx, uint32_t dex_pc) { +void HInstructionBuilder::BuildLoadMethodType(dex::ProtoIndex proto_index, uint32_t dex_pc) { const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); HLoadMethodType* load_method_type = - new (allocator_) HLoadMethodType(graph_->GetCurrentMethod(), proto_idx, dex_file, dex_pc); + new (allocator_) HLoadMethodType(graph_->GetCurrentMethod(), proto_index, dex_file, dex_pc); AppendInstruction(load_method_type); } @@ -2189,7 +2189,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::INVOKE_POLYMORPHIC: { uint16_t method_idx = instruction.VRegB_45cc(); - uint16_t proto_idx = instruction.VRegH_45cc(); + dex::ProtoIndex proto_idx(instruction.VRegH_45cc()); uint32_t number_of_vreg_arguments = instruction.VRegA_45cc(); uint32_t args[5]; instruction.GetVarArgs(args); @@ -2205,7 +2205,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::INVOKE_POLYMORPHIC_RANGE: { uint16_t method_idx = instruction.VRegB_4rcc(); - uint16_t proto_idx = instruction.VRegH_4rcc(); + dex::ProtoIndex proto_idx(instruction.VRegH_4rcc()); uint32_t number_of_vreg_arguments = instruction.VRegA_4rcc(); uint32_t register_index = instruction.VRegC_4rcc(); return BuildInvokePolymorphic(instruction, @@ -2949,7 +2949,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, } case Instruction::CONST_METHOD_TYPE: { - uint16_t proto_idx = instruction.VRegB_21c(); + dex::ProtoIndex proto_idx(instruction.VRegB_21c()); BuildLoadMethodType(proto_idx, dex_pc); UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); break; diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index 95ffa6b054..9d886a8ef2 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -178,7 +178,7 @@ class HInstructionBuilder : public ValueObject { bool BuildInvokePolymorphic(const Instruction& instruction, uint32_t dex_pc, uint32_t method_idx, - uint32_t proto_idx, + dex::ProtoIndex proto_idx, uint32_t number_of_vreg_arguments, bool is_range, uint32_t* args, @@ -240,11 +240,11 @@ class HInstructionBuilder : public ValueObject { bool LoadClassNeedsAccessCheck(Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); - // Builds a `HLoadMethodHandle` loading the given `method_handle_idx`. + // Builds a `HLoadMethodHandle` loading the given `method_handle_index`. void BuildLoadMethodHandle(uint16_t method_handle_idx, uint32_t dex_pc); - // Builds a `HLoadMethodType` loading the given `proto_idx`. - void BuildLoadMethodType(uint16_t proto_idx, uint32_t dex_pc); + // Builds a `HLoadMethodType` loading the given `proto_index`. + void BuildLoadMethodType(dex::ProtoIndex proto_index, uint32_t dex_pc); // Returns the outer-most compiling method's class. ObjPtr GetOutermostCompilingClass() const; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index a7c2d0b125..e786502dee 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -6548,7 +6548,7 @@ class HLoadMethodHandle FINAL : public HInstruction { class HLoadMethodType FINAL : public HInstruction { public: HLoadMethodType(HCurrentMethod* current_method, - uint16_t proto_idx, + dex::ProtoIndex proto_index, const DexFile& dex_file, uint32_t dex_pc) : HInstruction(kLoadMethodType, @@ -6556,7 +6556,7 @@ class HLoadMethodType FINAL : public HInstruction { SideEffectsForArchRuntimeCalls(), dex_pc), special_input_(HUserRecord(current_method)), - proto_idx_(proto_idx), + proto_index_(proto_index), dex_file_(dex_file) { } @@ -6568,7 +6568,7 @@ class HLoadMethodType FINAL : public HInstruction { bool IsClonable() const OVERRIDE { return true; } - uint16_t GetProtoIndex() const { return proto_idx_; } + dex::ProtoIndex GetProtoIndex() const { return proto_index_; } const DexFile& GetDexFile() const { return dex_file_; } @@ -6585,7 +6585,7 @@ class HLoadMethodType FINAL : public HInstruction { // The special input is the HCurrentMethod for kRuntimeCall. HUserRecord special_input_; - const uint16_t proto_idx_; + const dex::ProtoIndex proto_index_; const DexFile& dex_file_; }; diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 9536381ed0..e72d49e05f 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -786,11 +786,10 @@ static void dumpLocalsCb(void* /*context*/, const DexFile::LocalInfo& entry) { static std::unique_ptr indexString(const DexFile* pDexFile, const Instruction* pDecInsn, size_t bufSize) { - static const u4 kInvalidIndex = std::numeric_limits::max(); std::unique_ptr buf(new char[bufSize]); // Determine index and width of the string. u4 index = 0; - u4 secondary_index = kInvalidIndex; + u2 secondary_index = 0; u4 width = 4; switch (Instruction::FormatOf(pDecInsn->Opcode())) { // SOME NOT SUPPORTED: @@ -898,7 +897,7 @@ static std::unique_ptr indexString(const DexFile* pDexFile, signature.ToString().c_str()); } if (secondary_index < pDexFile->GetHeader().proto_ids_size_) { - const DexFile::ProtoId& protoId = pDexFile->GetProtoId(secondary_index); + const DexFile::ProtoId& protoId = pDexFile->GetProtoId(dex::ProtoIndex(secondary_index)); const Signature signature = pDexFile->GetProtoSignature(protoId); proto = signature.ToString(); } @@ -916,7 +915,7 @@ static std::unique_ptr indexString(const DexFile* pDexFile, break; case Instruction::kIndexProtoRef: if (index < pDexFile->GetHeader().proto_ids_size_) { - const DexFile::ProtoId& protoId = pDexFile->GetProtoId(index); + const DexFile::ProtoId& protoId = pDexFile->GetProtoId(dex::ProtoIndex(index)); const Signature signature = pDexFile->GetProtoSignature(protoId); const std::string& proto = signature.ToString(); outSize = snprintf(buf.get(), bufSize, "%s // proto@%0*x", proto.c_str(), width, index); @@ -1705,7 +1704,7 @@ static void dumpCallSite(const DexFile* pDexFile, u4 idx) { dex::StringIndex method_name_idx = static_cast(it.GetJavaValue().i); const char* method_name = pDexFile->StringDataByIdx(method_name_idx); it.Next(); - uint32_t method_type_idx = static_cast(it.GetJavaValue().i); + dex::ProtoIndex method_type_idx = static_cast(it.GetJavaValue().i); const DexFile::ProtoId& method_type_id = pDexFile->GetProtoId(method_type_idx); std::string method_type = pDexFile->GetProtoSignature(method_type_id).ToString(); it.Next(); @@ -1763,7 +1762,7 @@ static void dumpCallSite(const DexFile* pDexFile, u4 idx) { break; case EncodedArrayValueIterator::ValueType::kMethodType: { type = "MethodType"; - uint32_t proto_idx = static_cast(it.GetJavaValue().i); + dex::ProtoIndex proto_idx = static_cast(it.GetJavaValue().i); const DexFile::ProtoId& proto_id = pDexFile->GetProtoId(proto_idx); value = pDexFile->GetProtoSignature(proto_id).ToString(); break; diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc index 1525d537b7..b7d9db6da5 100644 --- a/dexlayout/dex_ir.cc +++ b/dexlayout/dex_ir.cc @@ -332,7 +332,7 @@ void Collections::CreateTypeId(const DexFile& dex_file, uint32_t i) { } void Collections::CreateProtoId(const DexFile& dex_file, uint32_t i) { - const DexFile::ProtoId& disk_proto_id = dex_file.GetProtoId(i); + const DexFile::ProtoId& disk_proto_id = dex_file.GetProtoId(dex::ProtoIndex(i)); const DexFile::TypeList* type_list = dex_file.GetProtoParameters(disk_proto_id); TypeList* parameter_type_list = CreateTypeList(type_list, disk_proto_id.parameters_off_); @@ -353,7 +353,7 @@ void Collections::CreateFieldId(const DexFile& dex_file, uint32_t i) { void Collections::CreateMethodId(const DexFile& dex_file, uint32_t i) { const DexFile::MethodId& disk_method_id = dex_file.GetMethodId(i); MethodId* method_id = new MethodId(GetTypeId(disk_method_id.class_idx_.index_), - GetProtoId(disk_method_id.proto_idx_), + GetProtoId(disk_method_id.proto_idx_.index_), GetStringId(disk_method_id.name_idx_.index_)); AddIndexedItem(method_ids_, method_id, MethodIdsOffset() + i * MethodId::ItemSize(), i); } diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h index d1b32007c3..e78e8d7a44 100644 --- a/libdexfile/dex/dex_file-inl.h +++ b/libdexfile/dex/dex_file-inl.h @@ -127,7 +127,7 @@ inline const char* DexFile::GetReturnTypeDescriptor(const ProtoId& proto_id) con return StringByTypeIdx(proto_id.return_type_idx_); } -inline const char* DexFile::GetShorty(uint32_t proto_idx) const { +inline const char* DexFile::GetShorty(dex::ProtoIndex proto_idx) const { const ProtoId& proto_id = GetProtoId(proto_idx); return StringDataByIdx(proto_id.shorty_idx_); } diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc index 8cfee6655e..9de260c862 100644 --- a/libdexfile/dex/dex_file.cc +++ b/libdexfile/dex/dex_file.cc @@ -281,7 +281,7 @@ const DexFile::MethodId* DexFile::FindMethodId(const DexFile::TypeId& declaring_ // Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx const dex::TypeIndex class_idx = GetIndexForTypeId(declaring_klass); const dex::StringIndex name_idx = GetIndexForStringId(name); - const uint16_t proto_idx = GetIndexForProtoId(signature); + const dex::ProtoIndex proto_idx = GetIndexForProtoId(signature); int32_t lo = 0; int32_t hi = NumMethodIds() - 1; while (hi >= lo) { @@ -373,7 +373,8 @@ const DexFile::ProtoId* DexFile::FindProtoId(dex::TypeIndex return_type_idx, int32_t hi = NumProtoIds() - 1; while (hi >= lo) { int32_t mid = (hi + lo) / 2; - const DexFile::ProtoId& proto = GetProtoId(mid); + const dex::ProtoIndex proto_idx = static_cast(mid); + const DexFile::ProtoId& proto = GetProtoId(proto_idx); int compare = return_type_idx.index_ - proto.return_type_idx_.index_; if (compare == 0) { DexFileParameterIterator it(*this, proto); @@ -777,6 +778,11 @@ void EncodedArrayValueIterator::Next() { namespace dex { +std::ostream& operator<<(std::ostream& os, const ProtoIndex& index) { + os << "ProtoIndex[" << index.index_ << "]"; + return os; +} + std::ostream& operator<<(std::ostream& os, const StringIndex& index) { os << "StringIndex[" << index.index_ << "]"; return os; diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index 4ca735ad66..87d2c48ff1 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -189,7 +189,7 @@ class DexFile { // Raw method_id_item. struct MethodId { dex::TypeIndex class_idx_; // index into type_ids_ array for defining class - uint16_t proto_idx_; // index into proto_ids_ array for method prototype + dex::ProtoIndex proto_idx_; // index into proto_ids_ array for method prototype dex::StringIndex name_idx_; // index into string_ids_ array for method name private: @@ -692,15 +692,15 @@ class DexFile { } // Returns the ProtoId at the specified index. - const ProtoId& GetProtoId(uint16_t idx) const { - DCHECK_LT(idx, NumProtoIds()) << GetLocation(); - return proto_ids_[idx]; + const ProtoId& GetProtoId(dex::ProtoIndex idx) const { + DCHECK_LT(idx.index_, NumProtoIds()) << GetLocation(); + return proto_ids_[idx.index_]; } - uint16_t GetIndexForProtoId(const ProtoId& proto_id) const { + dex::ProtoIndex GetIndexForProtoId(const ProtoId& proto_id) const { CHECK_GE(&proto_id, proto_ids_) << GetLocation(); CHECK_LT(&proto_id, proto_ids_ + header_->proto_ids_size_) << GetLocation(); - return &proto_id - proto_ids_; + return dex::ProtoIndex(&proto_id - proto_ids_); } // Looks up a proto id for a given return type and signature type list @@ -722,7 +722,7 @@ class DexFile { const Signature CreateSignature(const StringPiece& signature) const; // Returns the short form method descriptor for the given prototype. - const char* GetShorty(uint32_t proto_idx) const; + const char* GetShorty(dex::ProtoIndex proto_idx) const; const TypeList* GetProtoParameters(const ProtoId& proto_id) const { return DataPointer(proto_id.parameters_off_); diff --git a/libdexfile/dex/dex_file_types.h b/libdexfile/dex/dex_file_types.h index 2bb70ff261..d4fb3de504 100644 --- a/libdexfile/dex/dex_file_types.h +++ b/libdexfile/dex/dex_file_types.h @@ -25,72 +25,66 @@ namespace dex { constexpr uint32_t kDexNoIndex = 0xFFFFFFFF; -class StringIndex { +template +class DexIndex { public: - uint32_t index_; + T index_; - constexpr StringIndex() : index_(std::numeric_limits::max()) {} - explicit constexpr StringIndex(uint32_t idx) : index_(idx) {} + constexpr DexIndex() : index_(std::numeric_limits::max()) {} + explicit constexpr DexIndex(T idx) : index_(idx) {} bool IsValid() const { return index_ != std::numeric_limits::max(); } - static StringIndex Invalid() { - return StringIndex(std::numeric_limits::max()); + static constexpr DexIndex Invalid() { + return DexIndex(std::numeric_limits::max()); } - - bool operator==(const StringIndex& other) const { + bool operator==(const DexIndex& other) const { return index_ == other.index_; } - bool operator!=(const StringIndex& other) const { + bool operator!=(const DexIndex& other) const { return index_ != other.index_; } - bool operator<(const StringIndex& other) const { + bool operator<(const DexIndex& other) const { return index_ < other.index_; } - bool operator<=(const StringIndex& other) const { + bool operator<=(const DexIndex& other) const { return index_ <= other.index_; } - bool operator>(const StringIndex& other) const { + bool operator>(const DexIndex& other) const { return index_ > other.index_; } - bool operator>=(const StringIndex& other) const { + bool operator>=(const DexIndex& other) const { return index_ >= other.index_; } }; -std::ostream& operator<<(std::ostream& os, const StringIndex& index); -class TypeIndex { +class ProtoIndex : public DexIndex { public: - uint16_t index_; - - constexpr TypeIndex() : index_(std::numeric_limits::max()) {} - explicit constexpr TypeIndex(uint16_t idx) : index_(idx) {} - - bool IsValid() const { - return index_ != std::numeric_limits::max(); - } - static TypeIndex Invalid() { - return TypeIndex(std::numeric_limits::max()); + ProtoIndex() {} + explicit constexpr ProtoIndex(uint16_t index) : DexIndex(index) {} + static constexpr ProtoIndex Invalid() { + return ProtoIndex(std::numeric_limits::max()); } +}; +std::ostream& operator<<(std::ostream& os, const ProtoIndex& index); - bool operator==(const TypeIndex& other) const { - return index_ == other.index_; - } - bool operator!=(const TypeIndex& other) const { - return index_ != other.index_; - } - bool operator<(const TypeIndex& other) const { - return index_ < other.index_; - } - bool operator<=(const TypeIndex& other) const { - return index_ <= other.index_; - } - bool operator>(const TypeIndex& other) const { - return index_ > other.index_; +class StringIndex : public DexIndex { + public: + StringIndex() {} + explicit constexpr StringIndex(uint32_t index) : DexIndex(index) {} + static constexpr StringIndex Invalid() { + return StringIndex(std::numeric_limits::max()); } - bool operator>=(const TypeIndex& other) const { - return index_ >= other.index_; +}; +std::ostream& operator<<(std::ostream& os, const StringIndex& index); + +class TypeIndex : public DexIndex { + public: + TypeIndex() {} + explicit constexpr TypeIndex(uint16_t index) : DexIndex(index) {} + static constexpr TypeIndex Invalid() { + return TypeIndex(std::numeric_limits::max()); } }; std::ostream& operator<<(std::ostream& os, const TypeIndex& index); @@ -100,15 +94,21 @@ std::ostream& operator<<(std::ostream& os, const TypeIndex& index); namespace std { +template<> struct hash { + size_t operator()(const art::dex::ProtoIndex& index) const { + return hash()(index.index_); + } +}; + template<> struct hash { size_t operator()(const art::dex::StringIndex& index) const { - return hash()(index.index_); + return hash()(index.index_); } }; template<> struct hash { size_t operator()(const art::dex::TypeIndex& index) const { - return hash()(index.index_); + return hash()(index.index_); } }; diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc index a32f64e49f..78db8b9a35 100644 --- a/libdexfile/dex/dex_file_verifier.cc +++ b/libdexfile/dex/dex_file_verifier.cc @@ -127,8 +127,9 @@ const DexFile::MethodId* DexFileVerifier::CheckLoadMethodId(uint32_t idx, const return &dex_file_->GetMethodId(idx); } -const DexFile::ProtoId* DexFileVerifier::CheckLoadProtoId(uint32_t idx, const char* err_string) { - if (UNLIKELY(!CheckIndex(idx, dex_file_->NumProtoIds(), err_string))) { +const DexFile::ProtoId* DexFileVerifier::CheckLoadProtoId(dex::ProtoIndex idx, + const char* err_string) { + if (UNLIKELY(!CheckIndex(idx.index_, dex_file_->NumProtoIds(), err_string))) { return nullptr; } return &dex_file_->GetProtoId(idx); @@ -2208,7 +2209,7 @@ bool DexFileVerifier::CheckInterMethodIdItem() { } // Check that the proto id is valid. - if (UNLIKELY(!CheckIndex(item->proto_idx_, dex_file_->NumProtoIds(), + if (UNLIKELY(!CheckIndex(item->proto_idx_.index_, dex_file_->NumProtoIds(), "inter_method_id_item proto_idx"))) { return false; } diff --git a/libdexfile/dex/dex_file_verifier.h b/libdexfile/dex/dex_file_verifier.h index 04d8d71fa8..43d1093809 100644 --- a/libdexfile/dex/dex_file_verifier.h +++ b/libdexfile/dex/dex_file_verifier.h @@ -164,7 +164,7 @@ class DexFileVerifier { // error if not. If there is an error, null is returned. const DexFile::FieldId* CheckLoadFieldId(uint32_t idx, const char* error_fmt); const DexFile::MethodId* CheckLoadMethodId(uint32_t idx, const char* error_fmt); - const DexFile::ProtoId* CheckLoadProtoId(uint32_t idx, const char* error_fmt); + const DexFile::ProtoId* CheckLoadProtoId(dex::ProtoIndex idx, const char* error_fmt); void ErrorStringPrintf(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR; diff --git a/libdexfile/dex/dex_file_verifier_test.cc b/libdexfile/dex/dex_file_verifier_test.cc index f82081f8ee..c9bac0fef2 100644 --- a/libdexfile/dex/dex_file_verifier_test.cc +++ b/libdexfile/dex/dex_file_verifier_test.cc @@ -161,7 +161,7 @@ TEST_F(DexFileVerifierTest, MethodId) { "method_id_proto_idx", [](DexFile* dex_file) { DexFile::MethodId* method_id = const_cast(&dex_file->GetMethodId(0)); - method_id->proto_idx_ = 0xFF; + method_id->proto_idx_ = dex::ProtoIndex(0xFF); }, "inter_method_id_item proto_idx"); @@ -1425,12 +1425,13 @@ TEST_F(DexFileVerifierTest, ProtoOrdering) { CHECK_LT(method_idx + 1u, dex_file->NumMethodIds()); CHECK_EQ(dex_file->GetMethodId(method_idx).name_idx_, dex_file->GetMethodId(method_idx + 1).name_idx_); - CHECK_EQ(dex_file->GetMethodId(method_idx).proto_idx_ + 1u, - dex_file->GetMethodId(method_idx + 1).proto_idx_); + CHECK_EQ(dex_file->GetMethodId(method_idx).proto_idx_.index_ + 1u, + dex_file->GetMethodId(method_idx + 1).proto_idx_.index_); // Their return types should be the same. - uint32_t proto1_idx = dex_file->GetMethodId(method_idx).proto_idx_; + dex::ProtoIndex proto1_idx = dex_file->GetMethodId(method_idx).proto_idx_; const DexFile::ProtoId& proto1 = dex_file->GetProtoId(proto1_idx); - const DexFile::ProtoId& proto2 = dex_file->GetProtoId(proto1_idx + 1u); + dex::ProtoIndex proto2_idx(proto1_idx.index_ + 1u); + const DexFile::ProtoId& proto2 = dex_file->GetProtoId(proto2_idx); CHECK_EQ(proto1.return_type_idx_, proto2.return_type_idx_); // And the first should not have any parameters while the second should have some. CHECK(!DexFileParameterIterator(*dex_file, proto1).HasNext()); diff --git a/libdexfile/dex/dex_instruction.cc b/libdexfile/dex/dex_instruction.cc index 886218129e..8378211c43 100644 --- a/libdexfile/dex/dex_instruction.cc +++ b/libdexfile/dex/dex_instruction.cc @@ -467,10 +467,10 @@ std::string Instruction::DumpString(const DexFile* file) const { case k45cc: { uint32_t arg[kMaxVarArgRegs]; GetVarArgs(arg); - uint32_t method_idx = VRegB_45cc(); - uint32_t proto_idx = VRegH_45cc(); + uint16_t method_idx = VRegB_45cc(); + dex::ProtoIndex proto_idx(VRegH_45cc()); os << opcode << " {"; - for (int i = 0; i < VRegA_45cc(); ++i) { + for (uint32_t i = 0; i < VRegA_45cc(); ++i) { if (i != 0) { os << ", "; } @@ -478,7 +478,8 @@ std::string Instruction::DumpString(const DexFile* file) const { } os << "}"; if (file != nullptr) { - os << ", " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx) + os << ", " << file->PrettyMethod(method_idx) + << ", " << file->GetShorty(proto_idx) << " // "; } else { os << ", "; @@ -490,18 +491,19 @@ std::string Instruction::DumpString(const DexFile* file) const { switch (Opcode()) { case INVOKE_POLYMORPHIC_RANGE: { if (file != nullptr) { - uint32_t method_idx = VRegB_4rcc(); - uint32_t proto_idx = VRegH_4rcc(); + uint16_t method_idx = VRegB_4rcc(); + dex::ProtoIndex proto_idx(VRegH_4rcc()); os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc()) - << "}, " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx) + << "}, " << file->PrettyMethod(method_idx) + << ", " << file->GetShorty(dex::ProtoIndex(proto_idx)) << " // method@" << method_idx << ", proto@" << proto_idx; break; } } FALLTHROUGH_INTENDED; default: { - uint32_t method_idx = VRegB_4rcc(); - uint32_t proto_idx = VRegH_4rcc(); + uint16_t method_idx = VRegB_4rcc(); + dex::ProtoIndex proto_idx(VRegH_4rcc()); os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc()) << "}, method@" << method_idx << ", proto@" << proto_idx; } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 4141a37366..a3dc14be59 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -8159,7 +8159,7 @@ ArtField* ClassLinker::ResolveFieldJLS(uint32_t field_idx, ObjPtr ClassLinker::ResolveMethodType( Thread* self, - uint32_t proto_idx, + dex::ProtoIndex proto_idx, Handle dex_cache, Handle class_loader) { DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); @@ -8221,7 +8221,7 @@ ObjPtr ClassLinker::ResolveMethodType( } ObjPtr ClassLinker::ResolveMethodType(Thread* self, - uint32_t proto_idx, + dex::ProtoIndex proto_idx, ArtMethod* referrer) { StackHandleScope<2> hs(self); Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index e935d1dfb8..792e73819b 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -395,14 +395,14 @@ class ClassLinker { // Resolve a method type with a given ID from the DexFile associated with a given DexCache // and ClassLoader, storing the result in the DexCache. ObjPtr ResolveMethodType(Thread* self, - uint32_t proto_idx, + dex::ProtoIndex proto_idx, Handle dex_cache, Handle class_loader) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); ObjPtr ResolveMethodType(Thread* self, - uint32_t proto_idx, + dex::ProtoIndex proto_idx, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/dex/art_dex_file_loader_test.cc b/runtime/dex/art_dex_file_loader_test.cc index 274a6df702..c0090e62f5 100644 --- a/runtime/dex/art_dex_file_loader_test.cc +++ b/runtime/dex/art_dex_file_loader_test.cc @@ -286,7 +286,7 @@ TEST_F(ArtDexFileLoaderTest, FindTypeId) { TEST_F(ArtDexFileLoaderTest, FindProtoId) { for (size_t i = 0; i < java_lang_dex_file_->NumProtoIds(); i++) { - const DexFile::ProtoId& to_find = java_lang_dex_file_->GetProtoId(i); + const DexFile::ProtoId& to_find = java_lang_dex_file_->GetProtoId(dex::ProtoIndex(i)); const DexFile::TypeList* to_find_tl = java_lang_dex_file_->GetProtoParameters(to_find); std::vector to_find_types; if (to_find_tl != nullptr) { @@ -297,7 +297,7 @@ TEST_F(ArtDexFileLoaderTest, FindProtoId) { const DexFile::ProtoId* found = java_lang_dex_file_->FindProtoId(to_find.return_type_idx_, to_find_types); ASSERT_TRUE(found != nullptr); - EXPECT_EQ(java_lang_dex_file_->GetIndexForProtoId(*found), i); + EXPECT_EQ(java_lang_dex_file_->GetIndexForProtoId(*found), dex::ProtoIndex(i)); } } diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index a58946ae66..7fc8db375b 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -268,7 +268,7 @@ ObjPtr ResolveMethodHandleFromCode(ArtMethod* referrer, } ObjPtr ResolveMethodTypeFromCode(ArtMethod* referrer, - uint32_t proto_idx) { + dex::ProtoIndex proto_idx) { Thread::PoisonObjectPointersIfDebug(); ObjPtr method_type = referrer->GetDexCache()->GetResolvedMethodType(proto_idx); diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index 0a3b5dfc93..e33de9c45a 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -158,7 +158,7 @@ ObjPtr ResolveMethodHandleFromCode(ArtMethod* referrer, REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); -ObjPtr ResolveMethodTypeFromCode(ArtMethod* referrer, uint32_t proto_idx) +ObjPtr ResolveMethodTypeFromCode(ArtMethod* referrer, dex::ProtoIndex proto_idx) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index cf9ddd8aa8..fa536c77a9 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -200,7 +200,7 @@ extern "C" mirror::MethodType* artResolveMethodTypeFromCode(uint32_t proto_idx, auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, CalleeSaveType::kSaveEverything); ArtMethod* caller = caller_and_outer.caller; - ObjPtr result = ResolveMethodTypeFromCode(caller, proto_idx); + ObjPtr result = ResolveMethodTypeFromCode(caller, dex::ProtoIndex(proto_idx)); return result.Ptr(); } diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 39429c5b41..7e3c3dbaa6 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -2766,7 +2766,7 @@ extern "C" uintptr_t artInvokePolymorphic( const Instruction& inst = caller_method->DexInstructions().InstructionAt(dex_pc); DCHECK(inst.Opcode() == Instruction::INVOKE_POLYMORPHIC || inst.Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE); - const uint32_t proto_idx = inst.VRegH(); + const dex::ProtoIndex proto_idx(inst.VRegH()); const char* shorty = caller_method->GetDexFile()->GetShorty(proto_idx); const size_t shorty_length = strlen(shorty); static const bool kMethodIsStatic = false; // invoke() and invokeExact() are not static. diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 8a85ee41f8..ded8cefb8f 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -626,7 +626,8 @@ static bool DoMethodHandleInvokeCommon(Thread* self, // The vRegH value gives the index of the proto_id associated with this // signature polymorphic call site. - const uint32_t callsite_proto_id = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc(); + const uint16_t vRegH = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc(); + const dex::ProtoIndex callsite_proto_id(vRegH); // Call through to the classlinker and ask it to resolve the static type associated // with the callsite. This information is stored in the dex cache so it's @@ -783,10 +784,10 @@ static bool DoVarHandleInvokeCommon(Thread* self, return false; } - const uint32_t vRegH = is_var_args ? inst->VRegH_45cc() : inst->VRegH_4rcc(); + const uint16_t vRegH = is_var_args ? inst->VRegH_45cc() : inst->VRegH_4rcc(); ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); Handle callsite_type(hs.NewHandle( - class_linker->ResolveMethodType(self, vRegH, shadow_frame.GetMethod()))); + class_linker->ResolveMethodType(self, dex::ProtoIndex(vRegH), shadow_frame.GetMethod()))); // This implies we couldn't resolve one or more types in this VarHandle. if (UNLIKELY(callsite_type == nullptr)) { CHECK(self->IsExceptionPending()); @@ -965,9 +966,10 @@ static bool GetArgumentForBootstrapMethod(Thread* self, StackHandleScope<2> hs(self); Handle class_loader(hs.NewHandle(referrer->GetClassLoader())); Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); - uint32_t index = static_cast(encoded_value->GetI()); + dex::ProtoIndex proto_idx(encoded_value->GetC()); ClassLinker* cl = Runtime::Current()->GetClassLinker(); - ObjPtr o = cl->ResolveMethodType(self, index, dex_cache, class_loader); + ObjPtr o = + cl->ResolveMethodType(self, proto_idx, dex_cache, class_loader); if (UNLIKELY(o.IsNull())) { DCHECK(self->IsExceptionPending()); return false; diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 0818e06675..67a0349d7a 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -217,7 +217,7 @@ static inline ObjPtr ResolveMethodHandle(Thread* self, } static inline ObjPtr ResolveMethodType(Thread* self, - uint32_t method_type_index, + dex::ProtoIndex method_type_index, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 283885e522..5c7838cd66 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -565,7 +565,7 @@ void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { PREAMBLE(); ClassLinker* cl = Runtime::Current()->GetClassLinker(); ObjPtr mt = cl->ResolveMethodType(self, - inst->VRegB_21c(), + dex::ProtoIndex(inst->VRegB_21c()), shadow_frame.GetMethod()); if (UNLIKELY(mt == nullptr)) { HANDLE_PENDING_EXCEPTION(); diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 2a9ef2ce98..1b39a7422d 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -408,7 +408,8 @@ extern "C" size_t MterpConstMethodType(uint32_t index, ShadowFrame* shadow_frame, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr mt = ResolveMethodType(self, index, shadow_frame->GetMethod()); + ObjPtr mt = + ResolveMethodType(self, dex::ProtoIndex(index), shadow_frame->GetMethod()); if (UNLIKELY(mt == nullptr)) { return true; } diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index 7a4876c412..72f1443dfa 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -127,23 +127,23 @@ inline void DexCache::ClearResolvedType(dex::TypeIndex type_idx) { } } -inline uint32_t DexCache::MethodTypeSlotIndex(uint32_t proto_idx) { +inline uint32_t DexCache::MethodTypeSlotIndex(dex::ProtoIndex proto_idx) { DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); - DCHECK_LT(proto_idx, GetDexFile()->NumProtoIds()); - const uint32_t slot_idx = proto_idx % kDexCacheMethodTypeCacheSize; + DCHECK_LT(proto_idx.index_, GetDexFile()->NumProtoIds()); + const uint32_t slot_idx = proto_idx.index_ % kDexCacheMethodTypeCacheSize; DCHECK_LT(slot_idx, NumResolvedMethodTypes()); return slot_idx; } -inline MethodType* DexCache::GetResolvedMethodType(uint32_t proto_idx) { +inline MethodType* DexCache::GetResolvedMethodType(dex::ProtoIndex proto_idx) { return GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].load( - std::memory_order_relaxed).GetObjectForIndex(proto_idx); + std::memory_order_relaxed).GetObjectForIndex(proto_idx.index_); } -inline void DexCache::SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) { +inline void DexCache::SetResolvedMethodType(dex::ProtoIndex proto_idx, MethodType* resolved) { DCHECK(resolved != nullptr); GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].store( - MethodTypeDexCachePair(resolved, proto_idx), std::memory_order_relaxed); + MethodTypeDexCachePair(resolved, proto_idx.index_), std::memory_order_relaxed); // TODO: Fine-grained marking, so that we don't need to go through all arrays in full. Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this); } diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index d940964edb..9aff9ec49a 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -307,9 +307,9 @@ class MANAGED DexCache FINAL : public Object { ALWAYS_INLINE void ClearResolvedField(uint32_t idx, PointerSize ptr_size) REQUIRES_SHARED(Locks::mutator_lock_); - MethodType* GetResolvedMethodType(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_); + MethodType* GetResolvedMethodType(dex::ProtoIndex proto_idx) REQUIRES_SHARED(Locks::mutator_lock_); - void SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) + void SetResolvedMethodType(dex::ProtoIndex proto_idx, MethodType* resolved) REQUIRES_SHARED(Locks::mutator_lock_); CallSite* GetResolvedCallSite(uint32_t call_site_idx) REQUIRES_SHARED(Locks::mutator_lock_); @@ -432,7 +432,7 @@ class MANAGED DexCache FINAL : public Object { uint32_t TypeSlotIndex(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_); uint32_t FieldSlotIndex(uint32_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_); uint32_t MethodSlotIndex(uint32_t method_idx) REQUIRES_SHARED(Locks::mutator_lock_); - uint32_t MethodTypeSlotIndex(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_); + uint32_t MethodTypeSlotIndex(dex::ProtoIndex proto_idx) REQUIRES_SHARED(Locks::mutator_lock_); private: void Init(const DexFile* dex_file, diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc index d2bff2c19a..97e0ce6684 100644 --- a/runtime/mirror/dex_cache_test.cc +++ b/runtime/mirror/dex_cache_test.cc @@ -169,9 +169,9 @@ TEST_F(DexCacheMethodHandlesTest, TestResolvedMethodTypes) { for (size_t i = 0; i < dex_file.NumProtoIds(); ++i) { const MethodTypeDexCachePair pair = method_types_cache[i].load(std::memory_order_relaxed); - if (pair.index == method1_id.proto_idx_) { + if (dex::ProtoIndex(pair.index) == method1_id.proto_idx_) { ASSERT_EQ(method1_type.Get(), pair.object.Read()); - } else if (pair.index == method2_id.proto_idx_) { + } else if (dex::ProtoIndex(pair.index) == method2_id.proto_idx_) { ASSERT_EQ(method2_type.Get(), pair.object.Read()); } else { ASSERT_TRUE(false); diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 3518d2facd..92ee98ae8a 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3086,7 +3086,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { DCHECK(HasFailures()); break; } - const uint32_t proto_idx = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc(); + const uint16_t vRegH = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc(); + const dex::ProtoIndex proto_idx(vRegH); const char* return_descriptor = dex_file_->GetReturnTypeDescriptor(dex_file_->GetProtoId(proto_idx)); const RegType& return_type = @@ -3117,7 +3118,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx)); it.Next(); // Skip to name. it.Next(); // Skip to method type of the method handle - const uint32_t proto_idx = static_cast(it.GetJavaValue().i); + const dex::ProtoIndex proto_idx(it.GetJavaValue().c); const DexFile::ProtoId& proto_id = dex_file_->GetProtoId(proto_idx); DexFileParameterIterator param_it(*dex_file_, proto_id); // Treat method as static as it has yet to be determined. @@ -4190,7 +4191,8 @@ ArtMethod* MethodVerifier::VerifyInvocationArgs( if (UNLIKELY(method_type == METHOD_POLYMORPHIC)) { // Process the signature of the calling site that is invoking the method handle. - DexFileParameterIterator it(*dex_file_, dex_file_->GetProtoId(inst->VRegH())); + dex::ProtoIndex proto_idx(inst->VRegH()); + DexFileParameterIterator it(*dex_file_, dex_file_->GetProtoId(proto_idx)); return VerifyInvocationArgsFromIterator(&it, inst, method_type, is_range, res_method); } else { // Process the target method's signature. -- GitLab From 8a22907f0c98fc82ec18ef233d3363db8404aab9 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 10 May 2018 16:34:14 +0100 Subject: [PATCH 394/749] Conditionalize LocAccessToEvent on Android target build. bug: 64382372 bug: 79671158 Test: m Change-Id: Ida6d672e1d88d9662718257439762cb916b9c7a0 --- build/art.go | 28 ++++++++++++++++++++++++++++ runtime/Android.bp | 3 +-- runtime/hidden_api.cc | 14 +++++++++++--- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/build/art.go b/build/art.go index 59480a0d0f..3dabce3975 100644 --- a/build/art.go +++ b/build/art.go @@ -278,6 +278,7 @@ func init() { android.RegisterModuleType("art_cc_test", artTest) android.RegisterModuleType("art_cc_test_library", artTestLibrary) android.RegisterModuleType("art_cc_defaults", artDefaultsFactory) + android.RegisterModuleType("libart_cc_defaults", libartDefaultsFactory) android.RegisterModuleType("art_global_defaults", artGlobalDefaultsFactory) android.RegisterModuleType("art_debug_defaults", artDebugDefaultsFactory) } @@ -304,6 +305,33 @@ func artDefaultsFactory() android.Module { return module } +func libartDefaultsFactory() android.Module { + c := &codegenProperties{} + module := cc.DefaultsFactory(c) + android.AddLoadHook(module, func(ctx android.LoadHookContext) { + codegen(ctx, c, true) + + type props struct { + Target struct { + Android struct { + Shared_libs []string + } + } + } + + p := &props{} + // TODO: express this in .bp instead b/79671158 + if !envTrue(ctx, "ART_TARGET_LINUX") { + p.Target.Android.Shared_libs = []string { + "libmetricslogger", + } + } + ctx.AppendProperties(p) + }) + + return module +} + func artLibrary() android.Module { m, _ := cc.NewLibrary(android.HostAndDeviceSupported) module := m.Init() diff --git a/runtime/Android.bp b/runtime/Android.bp index 64e6796ba0..6a585c17f9 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -23,7 +23,7 @@ JIT_DEBUG_REGISTER_CODE_LDFLAGS = [ "-Wl,--keep-unique,__dex_debug_register_code" ] -cc_defaults { +libart_cc_defaults { name: "libart_defaults", defaults: ["art_defaults"], host_supported: true, @@ -396,7 +396,6 @@ cc_defaults { "libbacktrace", "liblz4", "liblog", - "libmetricslogger", // For atrace, properties, ashmem, set_sched_policy and socket_peer_is_trusted. "libcutils", // For common macros. diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index 9354d721b7..ee518ae1ea 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -14,8 +14,6 @@ * limitations under the License. */ -#include - #include "hidden_api.h" #include @@ -24,11 +22,14 @@ #include "thread-current-inl.h" #include "well_known_classes.h" +#ifdef ART_TARGET_ANDROID +#include using android::metricslogger::ComplexEventLogger; using android::metricslogger::ACTION_HIDDEN_API_ACCESSED; using android::metricslogger::FIELD_HIDDEN_API_ACCESS_METHOD; using android::metricslogger::FIELD_HIDDEN_API_ACCESS_DENIED; using android::metricslogger::FIELD_HIDDEN_API_SIGNATURE; +#endif namespace art { namespace hiddenapi { @@ -137,6 +138,7 @@ void MemberSignature::WarnAboutAccess(AccessMethod access_method, LOG(WARNING) << "Accessing hidden " << (type_ == kField ? "field " : "method ") << Dumpable(*this) << " (" << list << ", " << access_method << ")"; } +#ifdef ART_TARGET_ANDROID // Convert an AccessMethod enum to a value for logging from the proto enum. // This method may look odd (the enum values are current the same), but it // prevents coupling the internal enum to the proto enum (which should never @@ -156,8 +158,10 @@ inline static int32_t GetEnumValueForLog(AccessMethod access_method) { DCHECK(false); } } +#endif void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action action_taken) { +#ifdef ART_TARGET_ANDROID if (access_method == kLinking || access_method == kNone) { // Linking warnings come from static analysis/compilation of the bytecode // and can contain false positives (i.e. code that is never run). We choose @@ -174,6 +178,10 @@ void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action act Dump(signature_str); log_maker.AddTaggedData(FIELD_HIDDEN_API_SIGNATURE, signature_str.str()); log_maker.Record(); +#else + UNUSED(access_method); + UNUSED(action_taken); +#endif } static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtField*) { @@ -228,7 +236,7 @@ Action GetMemberActionImpl(T* member, } } - if (kIsTargetBuild) { + if (kIsTargetBuild && !kIsTargetLinux) { uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate(); // Assert that RAND_MAX is big enough, to ensure sampling below works as expected. static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small"); -- GitLab From 8268cb677bd92bfbcfec7e803775c29687494e53 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Fri, 11 May 2018 15:06:17 +0100 Subject: [PATCH 395/749] Remove support for Valgrind in ART. - Disable test configuration art-gtest-valgrind64 (art-gtest-valgrind32 was already disabled). - Remove Makefile logic regarding testing with Valgrind. - Remove occurrences of `TEST_DISABLED_FOR_MEMORY_TOOL_VALGRIND`. - Replace occurrences of `TEST_DISABLED_FOR_MEMORY_TOOL_ASAN` with `TEST_DISABLED_FOR_MEMORY_TOOL`. - Replace the potentially dynamically evaluated `RUNNING_ON_MEMORY_TOOL` expression with constant `kRunningOnMemoryTool`. - Simplify and fold the logic of `art::ArenaAllocatorMemoryToolCheckImpl` and `art::ArenaAllocatorMemoryToolCheck` into `art::ArenaAllocatorMemoryTool`. - Adjust comments regarding memory tools. - Remove Valgrind suppression files. - Remove `--callgrind` option from tools/art. Test: art/test.py Bug: 77856586 Bug: 29282211 Change-Id: Ifdcbfccc1830104c455760457df66ede4a4cd135 --- Android.mk | 26 --- build/Android.bp | 2 - build/Android.gtest.mk | 151 ++---------------- build/Android.oat.mk | 108 +++++-------- cmdline/unit.h | 5 +- compiler/dex/inline_method_analyser.cc | 3 +- compiler/optimizing/instruction_simplifier.cc | 8 +- dex2oat/dex2oat.cc | 8 +- dex2oat/dex2oat_test.cc | 6 +- dex2oat/linker/image_writer.cc | 3 +- libartbase/base/arena_allocator.h | 29 +--- libartbase/base/arena_allocator_test.cc | 8 +- libartbase/base/common_art_test.h | 14 +- libartbase/base/malloc_arena_pool.cc | 6 +- libartbase/base/mem_map.cc | 10 +- libartbase/base/mem_map_test.cc | 46 +++--- libartbase/base/memory_tool.h | 56 +++---- libartbase/base/scoped_arena_containers.h | 2 +- libdexfile/dex/dex_file_tracking_registrar.cc | 3 +- patchoat/patchoat.cc | 4 +- runtime/base/mem_map_arena_pool.cc | 2 +- runtime/exec_utils_test.cc | 24 ++- runtime/gc/allocator/rosalloc.h | 4 +- runtime/gc/heap-inl.h | 4 +- runtime/gc/heap.cc | 3 +- runtime/gc/space/large_object_space.cc | 5 +- .../gc/space/memory_tool_malloc_space-inl.h | 141 +++++++++------- runtime/gc/space/rosalloc_space.cc | 6 +- runtime/gc/space/rosalloc_space.h | 4 +- runtime/interpreter/unstarted_runtime_test.cc | 5 - runtime/jit/jit.cc | 2 +- runtime/native_stack_dump.cc | 6 +- runtime/runtime.cc | 12 +- runtime/runtime_callbacks_test.cc | 4 +- runtime/thread.cc | 13 +- test/137-cfi/cfi.cc | 3 - test/testrunner/target_config.py | 6 +- test/valgrind-suppressions.txt | 87 ---------- test/valgrind-target-suppressions.txt | 76 --------- tools/art | 4 - 40 files changed, 275 insertions(+), 634 deletions(-) delete mode 100644 test/valgrind-suppressions.txt delete mode 100644 test/valgrind-target-suppressions.txt diff --git a/Android.mk b/Android.mk index d6472be895..b190a3f345 100644 --- a/Android.mk +++ b/Android.mk @@ -245,19 +245,6 @@ endif test-art-host-dexdump: $(addprefix $(HOST_OUT_EXECUTABLES)/, dexdump2 dexlist) ANDROID_HOST_OUT=$(realpath $(HOST_OUT)) art/test/dexdump/run-all-tests -# Valgrind. -.PHONY: valgrind-test-art-host -valgrind-test-art-host: valgrind-test-art-host-gtest - $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) - -.PHONY: valgrind-test-art-host32 -valgrind-test-art-host32: valgrind-test-art-host-gtest32 - $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) - -.PHONY: valgrind-test-art-host64 -valgrind-test-art-host64: valgrind-test-art-host-gtest64 - $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) - ######################################################################## # target test rules @@ -332,19 +319,6 @@ test-art-target-jit$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) endif -# Valgrind. -.PHONY: valgrind-test-art-target -valgrind-test-art-target: valgrind-test-art-target-gtest - $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) - -.PHONY: valgrind-test-art-target32 -valgrind-test-art-target32: valgrind-test-art-target-gtest32 - $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) - -.PHONY: valgrind-test-art-target64 -valgrind-test-art-target64: valgrind-test-art-target-gtest64 - $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) - ####################### # Fake packages for ART diff --git a/build/Android.bp b/build/Android.bp index 2a5598fb7a..3a1d5839f5 100644 --- a/build/Android.bp +++ b/build/Android.bp @@ -127,8 +127,6 @@ art_global_defaults { }, include_dirs: [ - "external/valgrind/include", - "external/valgrind", "external/vixl/src", ], diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 3daaf0156e..cdb39b93b8 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -398,15 +398,9 @@ endif ART_TEST_HOST_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_GTEST_RULES := -ART_TEST_HOST_VALGRIND_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := -ART_TEST_HOST_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := -ART_TEST_HOST_VALGRIND_GTEST_RULES := ART_TEST_TARGET_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := ART_TEST_TARGET_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := ART_TEST_TARGET_GTEST_RULES := -ART_TEST_TARGET_VALGRIND_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := -ART_TEST_TARGET_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := -ART_TEST_TARGET_VALGRIND_GTEST_RULES := ART_TEST_HOST_GTEST_DEPENDENCIES := ART_GTEST_TARGET_ANDROID_ROOT := '/system' @@ -414,40 +408,6 @@ ifneq ($(ART_TEST_ANDROID_ROOT),) ART_GTEST_TARGET_ANDROID_ROOT := $(ART_TEST_ANDROID_ROOT) endif -ART_VALGRIND_TARGET_DEPENDENCIES := - -# Has to match list in external/valgrind/Android.build_one.mk -ART_VALGRIND_SUPPORTED_ARCH := arm arm64 x86_64 - -# Valgrind is not supported for x86 -ifneq (,$(filter $(ART_VALGRIND_SUPPORTED_ARCH),$(TARGET_ARCH))) -art_vg_arch := $(if $(filter x86_64,$(TARGET_ARCH)),amd64,$(TARGET_ARCH)) -ART_VALGRIND_TARGET_DEPENDENCIES += \ - $(TARGET_OUT_EXECUTABLES)/valgrind \ - $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/memcheck-$(art_vg_arch)-linux \ - $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_core-$(art_vg_arch)-linux.so \ - $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_memcheck-$(art_vg_arch)-linux.so \ - $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/default.supp -art_vg_arch := -endif - -ifdef TARGET_2ND_ARCH -ifneq (,$(filter $(ART_VALGRIND_SUPPORTED_ARCH),$(TARGET_2ND_ARCH))) -ART_VALGRIND_TARGET_DEPENDENCIES += \ - $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/memcheck-$(TARGET_2ND_ARCH)-linux \ - $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_core-$(TARGET_2ND_ARCH)-linux.so \ - $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_memcheck-$(TARGET_2ND_ARCH)-linux.so -endif -endif - -include $(CLEAR_VARS) -LOCAL_MODULE := valgrind-target-suppressions.txt -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := test/valgrind-target-suppressions.txt -LOCAL_MODULE_PATH := $(ART_TARGET_TEST_OUT) -include $(BUILD_PREBUILT) - # Define a make rule for a target device gtest. # $(1): gtest name - the name of the test we're building such as leb128_test. # $(2): path relative to $OUT to the test binary @@ -467,10 +427,9 @@ define define-art-gtest-rule-target $$($(3)TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so \ $$($(3)TARGET_OUT_SHARED_LIBRARIES)/libopenjdkd.so \ $$(TARGET_OUT_JAVA_LIBRARIES)/core-libart-testdex.jar \ - $$(TARGET_OUT_JAVA_LIBRARIES)/core-oj-testdex.jar \ - $$(ART_TARGET_TEST_OUT)/valgrind-target-suppressions.txt + $$(TARGET_OUT_JAVA_LIBRARIES)/core-oj-testdex.jar -$$(gtest_rule) valgrind-$$(gtest_rule): PRIVATE_TARGET_EXE := $$(gtest_target_exe) +$$(gtest_rule): PRIVATE_TARGET_EXE := $$(gtest_target_exe) ifeq ($(ART_TEST_CHROOT),) # Non-chroot configuration. @@ -505,37 +464,7 @@ $$(gtest_rule): test-art-target-sync ART_TEST_TARGET_GTEST_RULES += $$(gtest_rule) ART_TEST_TARGET_GTEST_$(1)_RULES += $$(gtest_rule) -# File witnessing the success of the Valgrind gtest, the presence of which means the gtest's -# success. -valgrind_gtest_witness := \ - $(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/valgrind-$$(gtest_rule)-$$$$PPID - -valgrind-$$(gtest_rule): VALGRIND_GTEST_WITNESS := $$(valgrind_gtest_witness) - -.PHONY: valgrind-$$(gtest_rule) -valgrind-$$(gtest_rule): $(ART_VALGRIND_TARGET_DEPENDENCIES) test-art-target-sync - $(hide) adb shell touch $$(VALGRIND_GTEST_WITNESS) - $(hide) adb shell rm $$(VALGRIND_GTEST_WITNESS) - $(hide) adb shell chmod 755 $(maybe_art_test_chroot)$$(PRIVATE_TARGET_EXE) - $(hide) $$(call ART_TEST_SKIP,$$@) && \ - (adb shell "$(maybe_chroot_command) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ - ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \ - $(ART_GTEST_TARGET_ANDROID_ROOT)/bin/valgrind \ - --leak-check=full --error-exitcode=1 --workaround-gcc296-bugs=yes \ - --suppressions=$(ART_TARGET_TEST_DIR)/valgrind-target-suppressions.txt \ - --num-callers=50 --show-mismatched-frees=no $$(PRIVATE_TARGET_EXE) \ - && touch $$(VALGRIND_GTEST_WITNESS)" \ - && (adb pull $$(VALGRIND_GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ - || $$(call ART_TEST_FAILED,$$@)) - $(hide) rm -f /tmp/$$@-$$$$PPID - - ART_TEST_TARGET_VALGRIND_GTEST$$($(3)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += \ - valgrind-$$(gtest_rule) - ART_TEST_TARGET_VALGRIND_GTEST_RULES += valgrind-$$(gtest_rule) - ART_TEST_TARGET_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule) - # Clear locally defined variables. - valgrind_gtest_witness := gtest_witness := maybe_chroot_command := maybe_art_test_chroot := @@ -544,16 +473,6 @@ valgrind-$$(gtest_rule): $(ART_VALGRIND_TARGET_DEPENDENCIES) test-art-target-syn gtest_rule := endef # define-art-gtest-rule-target -ART_VALGRIND_DEPENDENCIES := \ - $(HOST_OUT_EXECUTABLES)/valgrind \ - $(HOST_OUT)/lib64/valgrind/memcheck-amd64-linux \ - $(HOST_OUT)/lib64/valgrind/memcheck-x86-linux \ - $(HOST_OUT)/lib64/valgrind/default.supp \ - $(HOST_OUT)/lib64/valgrind/vgpreload_core-amd64-linux.so \ - $(HOST_OUT)/lib64/valgrind/vgpreload_core-x86-linux.so \ - $(HOST_OUT)/lib64/valgrind/vgpreload_memcheck-amd64-linux.so \ - $(HOST_OUT)/lib64/valgrind/vgpreload_memcheck-x86-linux.so - # Define make rules for a host gtests. # $(1): gtest name - the name of the test we're building such as leb128_test. # $(2): path relative to $OUT to the test binary @@ -605,19 +524,6 @@ endif ART_TEST_HOST_GTEST_$(1)_RULES += $$(gtest_rule) -.PHONY: valgrind-$$(gtest_rule) -valgrind-$$(gtest_rule): $$(gtest_exe) $$(gtest_deps) $(ART_VALGRIND_DEPENDENCIES) - $(hide) $$(call ART_TEST_SKIP,$$@) && \ - VALGRIND_LIB=$(HOST_OUT)/lib64/valgrind \ - $(HOST_OUT_EXECUTABLES)/valgrind --leak-check=full --error-exitcode=1 \ - --suppressions=art/test/valgrind-suppressions.txt --num-callers=50 \ - $$< && \ - $$(call ART_TEST_PASSED,$$@) || $$(call ART_TEST_FAILED,$$@) - - ART_TEST_HOST_VALGRIND_GTEST$$($(3)ART_PHONY_TEST_HOST_SUFFIX)_RULES += valgrind-$$(gtest_rule) - ART_TEST_HOST_VALGRIND_GTEST_RULES += valgrind-$$(gtest_rule) - ART_TEST_HOST_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule) - # Clear locally defined variables. gtest_deps := gtest_exe := @@ -650,7 +556,6 @@ define define-art-gtest-target ifndef ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES := - ART_TEST_TARGET_VALGRIND_GTEST_$$(art_gtest_name)_RULES := endif $$(eval $$(call define-art-gtest-rule-target,$$(art_gtest_name),$$(art_gtest_filename),$(2),$$($(2)library_path))) @@ -670,7 +575,6 @@ define define-art-gtest-host art_gtest_name := $$(notdir $$(basename $$(art_gtest_filename))) ifndef ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES := - ART_TEST_HOST_VALGRIND_GTEST_$$(art_gtest_name)_RULES := endif $$(eval $$(call define-art-gtest-rule-host,$$(art_gtest_name),$$(art_gtest_filename),$(2))) @@ -689,13 +593,8 @@ define define-art-gtest-target-both test-art-target-gtest-$$(art_gtest_name): $$(ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES) $$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) -.PHONY: valgrind-test-art-target-gtest-$$(art_gtest_name) -valgrind-test-art-target-gtest-$$(art_gtest_name): $$(ART_TEST_TARGET_VALGRIND_GTEST_$$(art_gtest_name)_RULES) - $$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) - # Clear now unused variables. ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES := - ART_TEST_TARGET_VALGRIND_GTEST_$$(art_gtest_name)_RULES := art_gtest_name := endef # define-art-gtest-target-both @@ -708,13 +607,8 @@ define define-art-gtest-host-both test-art-host-gtest-$$(art_gtest_name): $$(ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES) $$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) -.PHONY: valgrind-test-art-host-gtest-$$(art_gtest_name) -valgrind-test-art-host-gtest-$$(art_gtest_name): $$(ART_TEST_HOST_VALGRIND_GTEST_$$(art_gtest_name)_RULES) - $$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) - # Clear now unused variables. ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES := - ART_TEST_HOST_VALGRIND_GTEST_$$(art_gtest_name)_RULES := art_gtest_name := endef # define-art-gtest-host-both @@ -740,12 +634,11 @@ RUNTIME_TARGET_GTEST_MAKE_TARGETS := $(foreach file, $(ART_TARGET_GTEST_FILES), $(eval RUNTIME_TARGET_GTEST_MAKE_TARGETS += $$(notdir $$(patsubst %/,%,$$(dir $$(file))))_$$(notdir $$(basename $$(file))))) COMPILER_TARGET_GTEST_MAKE_TARGETS := -# Define all the combinations of host/target, valgrind and suffix such as: -# test-art-host-gtest or valgrind-test-art-host-gtest64 +# Define all the combinations of host/target and suffix such as: +# test-art-host-gtest or test-art-host-gtest64 # $(1): host or target # $(2): HOST or TARGET -# $(3): valgrind- or undefined -# $(4): undefined, 32 or 64 +# $(3): undefined, 32 or 64 define define-test-art-gtest-combination ifeq ($(1),host) ifneq ($(2),HOST) @@ -760,12 +653,8 @@ define define-test-art-gtest-combination endif endif - rule_name := $(3)test-art-$(1)-gtest$(4) - ifeq ($(3),valgrind-) - dependencies := $$(ART_TEST_$(2)_VALGRIND_GTEST$(4)_RULES) - else - dependencies := $$(ART_TEST_$(2)_GTEST$(4)_RULES) - endif + rule_name := test-art-$(1)-gtest$(3) + dependencies := $$(ART_TEST_$(2)_GTEST$(3)_RULES) .PHONY: $$(rule_name) $$(rule_name): $$(dependencies) dx d8-compat-dx desugar @@ -776,21 +665,15 @@ $$(rule_name): $$(dependencies) dx d8-compat-dx desugar dependencies := endef # define-test-art-gtest-combination -$(eval $(call define-test-art-gtest-combination,target,TARGET,,)) -$(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,)) -$(eval $(call define-test-art-gtest-combination,target,TARGET,,$(ART_PHONY_TEST_TARGET_SUFFIX))) -$(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,$(ART_PHONY_TEST_TARGET_SUFFIX))) +$(eval $(call define-test-art-gtest-combination,target,TARGET,)) +$(eval $(call define-test-art-gtest-combination,target,TARGET,$(ART_PHONY_TEST_TARGET_SUFFIX))) ifdef 2ND_ART_PHONY_TEST_TARGET_SUFFIX -$(eval $(call define-test-art-gtest-combination,target,TARGET,,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX))) -$(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX))) +$(eval $(call define-test-art-gtest-combination,target,TARGET,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX))) endif -$(eval $(call define-test-art-gtest-combination,host,HOST,,)) -$(eval $(call define-test-art-gtest-combination,host,HOST,valgrind-,)) -$(eval $(call define-test-art-gtest-combination,host,HOST,,$(ART_PHONY_TEST_HOST_SUFFIX))) -$(eval $(call define-test-art-gtest-combination,host,HOST,valgrind-,$(ART_PHONY_TEST_HOST_SUFFIX))) +$(eval $(call define-test-art-gtest-combination,host,HOST,)) +$(eval $(call define-test-art-gtest-combination,host,HOST,$(ART_PHONY_TEST_HOST_SUFFIX))) ifneq ($(HOST_PREFER_32_BIT),true) -$(eval $(call define-test-art-gtest-combination,host,HOST,,$(2ND_ART_PHONY_TEST_HOST_SUFFIX))) -$(eval $(call define-test-art-gtest-combination,host,HOST,valgrind-,$(2ND_ART_PHONY_TEST_HOST_SUFFIX))) +$(eval $(call define-test-art-gtest-combination,host,HOST,$(2ND_ART_PHONY_TEST_HOST_SUFFIX))) endif # Clear locally defined variables. @@ -807,15 +690,9 @@ COMPILER_GTEST_HOST_SRC_FILES := ART_TEST_HOST_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_GTEST_RULES := -ART_TEST_HOST_VALGRIND_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := -ART_TEST_HOST_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := -ART_TEST_HOST_VALGRIND_GTEST_RULES := ART_TEST_TARGET_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := ART_TEST_TARGET_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := ART_TEST_TARGET_GTEST_RULES := -ART_TEST_TARGET_VALGRIND_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := -ART_TEST_TARGET_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := -ART_TEST_TARGET_VALGRIND_GTEST_RULES := ART_GTEST_TARGET_ANDROID_ROOT := ART_GTEST_class_linker_test_DEX_DEPS := ART_GTEST_class_table_test_DEX_DEPS := @@ -854,8 +731,6 @@ ART_GTEST_transaction_test_DEX_DEPS := ART_GTEST_dex2oat_environment_tests_DEX_DEPS := ART_GTEST_heap_verification_test_DEX_DEPS := ART_GTEST_verifier_deps_test_DEX_DEPS := -ART_VALGRIND_DEPENDENCIES := -ART_VALGRIND_TARGET_DEPENDENCIES := $(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_TARGET_GTEST_$(dir)_DEX :=)) $(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_HOST_GTEST_$(dir)_DEX :=)) ART_TEST_HOST_GTEST_MainStripped_DEX := diff --git a/build/Android.oat.mk b/build/Android.oat.mk index 517ac5c28d..ba3ef053de 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -37,11 +37,9 @@ else endif # Use dex2oat debug version for better error reporting -# $(1): compiler - optimizing, interpreter or interpreter-access-checks. +# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks). # $(2): 2ND_ or undefined, 2ND_ for 32-bit host builds. -# $(3): wrapper, e.g., valgrind. -# $(4): dex2oat suffix, e.g, valgrind requires 32 right now. -# $(5): multi-image. +# $(3): multi-image. # NB depending on HOST_CORE_DEX_LOCATIONS so we are sure to have the dex files in frameworks for # run-test --no-image define create-core-oat-host-rules @@ -65,11 +63,11 @@ define create-core-oat-host-rules endif ifneq ($(filter-out interpreter interp-ac optimizing,$(1)),) #Technically this test is not precise, but hopefully good enough. - $$(error found $(1) expected interpreter, interpreter-access-checks, or optimizing) + $$(error found $(1) expected interpreter, interp-ac, or optimizing) endif - # If $(5) is true, generate a multi-image. - ifeq ($(5),true) + # If $(3) is true, generate a multi-image. + ifeq ($(3),true) core_multi_infix := -multi core_multi_param := --multi-image --no-inline-from=core-oj-hostdex.jar core_multi_group := _multi @@ -79,22 +77,18 @@ define create-core-oat-host-rules core_multi_group := endif - core_image_name := $($(2)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(3)$(CORE_IMG_SUFFIX) - core_oat_name := $($(2)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(3)$(CORE_OAT_SUFFIX) + core_image_name := $($(2)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(CORE_IMG_SUFFIX) + core_oat_name := $($(2)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(CORE_OAT_SUFFIX) # Using the bitness suffix makes it easier to add as a dependency for the run-test mk. ifeq ($(2),) - $(3)HOST_CORE_IMAGE_$(1)$$(core_multi_group)_64 := $$(core_image_name) + HOST_CORE_IMAGE_$(1)$$(core_multi_group)_64 := $$(core_image_name) else - $(3)HOST_CORE_IMAGE_$(1)$$(core_multi_group)_32 := $$(core_image_name) + HOST_CORE_IMAGE_$(1)$$(core_multi_group)_32 := $$(core_image_name) endif - $(3)HOST_CORE_IMG_OUTS += $$(core_image_name) - $(3)HOST_CORE_OAT_OUTS += $$(core_oat_name) + HOST_CORE_IMG_OUTS += $$(core_image_name) + HOST_CORE_OAT_OUTS += $$(core_oat_name) - # If we have a wrapper, make the target phony. - ifneq ($(3),) -.PHONY: $$(core_image_name) - endif $$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options) $$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name) $$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name) @@ -102,7 +96,7 @@ $$(core_image_name): PRIVATE_CORE_MULTI_PARAM := $$(core_multi_param) $$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency) @echo "host dex2oat: $$@" @mkdir -p $$(dir $$@) - $$(hide) $(3) $$(DEX2OAT)$(4) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \ + $$(hide) $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \ --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \ --image-classes=$$(PRELOADED_CLASSES) $$(addprefix --dex-file=,$$(HOST_CORE_DEX_FILES)) \ $$(addprefix --dex-location=,$$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$$(PRIVATE_CORE_OAT_NAME) \ @@ -124,35 +118,27 @@ $$(core_oat_name): $$(core_image_name) core_infix := endef # create-core-oat-host-rules -# $(1): compiler - optimizing, interpreter or interpreter-access-checks. -# $(2): wrapper. -# $(3): dex2oat suffix. -# $(4): multi-image. +# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks). +# $(2): multi-image. define create-core-oat-host-rule-combination - $(call create-core-oat-host-rules,$(1),,$(2),$(3),$(4)) + $(call create-core-oat-host-rules,$(1),,$(2)) ifneq ($(HOST_PREFER_32_BIT),true) - $(call create-core-oat-host-rules,$(1),2ND_,$(2),$(3),$(4)) + $(call create-core-oat-host-rules,$(1),2ND_,$(2)) endif endef -$(eval $(call create-core-oat-host-rule-combination,optimizing,,,false)) -$(eval $(call create-core-oat-host-rule-combination,interpreter,,,false)) -$(eval $(call create-core-oat-host-rule-combination,interp-ac,,,false)) -$(eval $(call create-core-oat-host-rule-combination,optimizing,,,true)) -$(eval $(call create-core-oat-host-rule-combination,interpreter,,,true)) -$(eval $(call create-core-oat-host-rule-combination,interp-ac,,,true)) - -valgrindHOST_CORE_IMG_OUTS := -valgrindHOST_CORE_OAT_OUTS := -$(eval $(call create-core-oat-host-rule-combination,optimizing,valgrind,32,false)) -$(eval $(call create-core-oat-host-rule-combination,interpreter,valgrind,32,false)) -$(eval $(call create-core-oat-host-rule-combination,interp-ac,valgrind,32,false)) - -valgrind-test-art-host-dex2oat-host: $(valgrindHOST_CORE_IMG_OUTS) +$(eval $(call create-core-oat-host-rule-combination,optimizing,false)) +$(eval $(call create-core-oat-host-rule-combination,interpreter,false)) +$(eval $(call create-core-oat-host-rule-combination,interp-ac,false)) +$(eval $(call create-core-oat-host-rule-combination,optimizing,true)) +$(eval $(call create-core-oat-host-rule-combination,interpreter,true)) +$(eval $(call create-core-oat-host-rule-combination,interp-ac,true)) test-art-host-dex2oat-host: $(HOST_CORE_IMG_OUTS) +# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks). +# $(2): 2ND_ or undefined define create-core-oat-target-rules core_compile_options := core_image_name := @@ -176,36 +162,32 @@ define create-core-oat-target-rules endif ifneq ($(filter-out interpreter interp-ac optimizing,$(1)),) # Technically this test is not precise, but hopefully good enough. - $$(error found $(1) expected interpreter, interpreter-access-checks, or optimizing) + $$(error found $(1) expected interpreter, interp-ac, or optimizing) endif - core_image_name := $($(2)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$(3)$(CORE_IMG_SUFFIX) - core_oat_name := $($(2)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$(3)$(CORE_OAT_SUFFIX) + core_image_name := $($(2)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$(CORE_IMG_SUFFIX) + core_oat_name := $($(2)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$(CORE_OAT_SUFFIX) # Using the bitness suffix makes it easier to add as a dependency for the run-test mk. ifeq ($(2),) ifdef TARGET_2ND_ARCH - $(3)TARGET_CORE_IMAGE_$(1)_64 := $$(core_image_name) + TARGET_CORE_IMAGE_$(1)_64 := $$(core_image_name) else - $(3)TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name) + TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name) endif else - $(3)TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name) + TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name) endif - $(3)TARGET_CORE_IMG_OUTS += $$(core_image_name) - $(3)TARGET_CORE_OAT_OUTS += $$(core_oat_name) + TARGET_CORE_IMG_OUTS += $$(core_image_name) + TARGET_CORE_OAT_OUTS += $$(core_oat_name) - # If we have a wrapper, make the target phony. - ifneq ($(3),) -.PHONY: $$(core_image_name) - endif $$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options) $$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name) $$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name) $$(core_image_name): $$(TARGET_CORE_DEX_FILES) $$(core_dex2oat_dependency) @echo "target dex2oat: $$@" @mkdir -p $$(dir $$@) - $$(hide) $(4) $$(DEX2OAT)$(5) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \ + $$(hide) $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \ --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \ --image-classes=$$(PRELOADED_CLASSES) $$(addprefix --dex-file=,$$(TARGET_CORE_DEX_FILES)) \ $$(addprefix --dex-location=,$$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$$(PRIVATE_CORE_OAT_NAME) \ @@ -228,30 +210,18 @@ $$(core_oat_name): $$(core_image_name) core_infix := endef # create-core-oat-target-rules -# $(1): compiler - optimizing, interpreter or interpreter-access-checks. -# $(2): wrapper. -# $(3): dex2oat suffix. +# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks). define create-core-oat-target-rule-combination - $(call create-core-oat-target-rules,$(1),,$(2),$(3)) + $(call create-core-oat-target-rules,$(1),) ifdef TARGET_2ND_ARCH - $(call create-core-oat-target-rules,$(1),2ND_,$(2),$(3)) + $(call create-core-oat-target-rules,$(1),2ND_) endif endef -$(eval $(call create-core-oat-target-rule-combination,optimizing,,)) -$(eval $(call create-core-oat-target-rule-combination,interpreter,,)) -$(eval $(call create-core-oat-target-rule-combination,interp-ac,,)) - -valgrindTARGET_CORE_IMG_OUTS := -valgrindTARGET_CORE_OAT_OUTS := -$(eval $(call create-core-oat-target-rule-combination,optimizing,valgrind,32)) -$(eval $(call create-core-oat-target-rule-combination,interpreter,valgrind,32)) -$(eval $(call create-core-oat-target-rule-combination,interp-ac,valgrind,32)) - -valgrind-test-art-host-dex2oat-target: $(valgrindTARGET_CORE_IMG_OUTS) - -valgrind-test-art-host-dex2oat: valgrind-test-art-host-dex2oat-host valgrind-test-art-host-dex2oat-target +$(eval $(call create-core-oat-target-rule-combination,optimizing)) +$(eval $(call create-core-oat-target-rule-combination,interpreter)) +$(eval $(call create-core-oat-target-rule-combination,interp-ac)) # Define a default core image that can be used for things like gtests that # need some image to run, but don't otherwise care which image is used. diff --git a/cmdline/unit.h b/cmdline/unit.h index ad6a03d12f..f73981fbd3 100644 --- a/cmdline/unit.h +++ b/cmdline/unit.h @@ -21,8 +21,9 @@ namespace art { // Used for arguments that simply indicate presence (e.g. "-help") without any values. struct Unit { - // Avoid 'Conditional jump or move depends on uninitialised value(s)' errors - // when running valgrind by specifying a user-defined constructor. + // Historical note: We specified a user-defined constructor to avoid + // 'Conditional jump or move depends on uninitialised value(s)' errors + // when running Valgrind. Unit() {} Unit(const Unit&) = default; ~Unit() {} diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc index dc044c1210..fe8b766d0f 100644 --- a/compiler/dex/inline_method_analyser.cc +++ b/compiler/dex/inline_method_analyser.cc @@ -724,7 +724,8 @@ bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(ArtMethod* method, return false; } DCHECK_GE(field->GetOffset().Int32Value(), 0); - // Do not interleave function calls with bit field writes to placate valgrind. Bug: 27552451. + // Historical note: We made sure not to interleave function calls with bit field writes to + // placate Valgrind. Bug: 27552451. uint32_t field_offset = field->GetOffset().Uint32Value(); bool is_volatile = field->IsVolatile(); result->field_idx = field_idx; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index ca84d421a7..6e618f4d02 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -636,8 +636,8 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { return; } - // Note: The `outcome` is initialized to please valgrind - the compiler can reorder - // the return value check with the `outcome` check, b/27651442 . + // Historical note: The `outcome` was initialized to please Valgrind - the compiler can reorder + // the return value check with the `outcome` check, b/27651442. bool outcome = false; if (TypeCheckHasKnownOutcome(check_cast->GetTargetClassRTI(), object, &outcome)) { if (outcome) { @@ -682,8 +682,8 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { return; } - // Note: The `outcome` is initialized to please valgrind - the compiler can reorder - // the return value check with the `outcome` check, b/27651442 . + // Historical note: The `outcome` was initialized to please Valgrind - the compiler can reorder + // the return value check with the `outcome` check, b/27651442. bool outcome = false; if (TypeCheckHasKnownOutcome(instruction->GetTargetClassRTI(), object, &outcome)) { MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf); diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 6b65aca943..00c893a8b5 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -657,7 +657,7 @@ class Dex2Oat FINAL { // the runtime. LogCompletionTime(); - if (!kIsDebugBuild && !(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { + if (!kIsDebugBuild && !(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { // We want to just exit on non-debug builds, not bringing the runtime down // in an orderly fashion. So release the following fields. driver_.release(); @@ -3119,9 +3119,9 @@ static dex2oat::ReturnCode Dex2oat(int argc, char** argv) { int main(int argc, char** argv) { int result = static_cast(art::Dex2oat(argc, argv)); // Everything was done, do an explicit exit here to avoid running Runtime destructors that take - // time (bug 10645725) unless we're a debug or instrumented build or running on valgrind. Note: - // The Dex2Oat class should not destruct the runtime in this case. - if (!art::kIsDebugBuild && !art::kIsPGOInstrumentation && (RUNNING_ON_MEMORY_TOOL == 0)) { + // time (bug 10645725) unless we're a debug or instrumented build or running on a memory tool. + // Note: The Dex2Oat class should not destruct the runtime in this case. + if (!art::kIsDebugBuild && !art::kIsPGOInstrumentation && !art::kRunningOnMemoryTool) { _exit(result); } return result; diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 2fe16f7cb7..1d0735d807 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -472,8 +472,8 @@ class Dex2oatSwapUseTest : public Dex2oatSwapTest { }; TEST_F(Dex2oatSwapUseTest, CheckSwapUsage) { - // Native memory usage isn't correctly tracked under sanitization. - TEST_DISABLED_FOR_MEMORY_TOOL_ASAN(); + // Native memory usage isn't correctly tracked when running under ASan. + TEST_DISABLED_FOR_MEMORY_TOOL(); // The `native_alloc_2_ >= native_alloc_1_` assertion below may not // hold true on some x86 systems; disable this test while we @@ -1054,8 +1054,6 @@ TEST_F(Dex2oatWatchdogTest, TestWatchdogOK) { } TEST_F(Dex2oatWatchdogTest, TestWatchdogTrigger) { - TEST_DISABLED_FOR_MEMORY_TOOL_VALGRIND(); // b/63052624 - // The watchdog is independent of dex2oat and will not delete intermediates. It is possible // that the compilation succeeds and the file is completely written by the time the watchdog // kills dex2oat (but the dex2oat threads must have been scheduled pretty badly). diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 9e5cd8035c..b24cc38100 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -2094,7 +2094,8 @@ void ImageWriter::CopyAndFixupNativeData(size_t oat_index) { size_t size = ArtMethod::Size(target_ptr_size_); size_t alignment = ArtMethod::Alignment(target_ptr_size_); memcpy(dest, pair.first, LengthPrefixedArray::ComputeSize(0, size, alignment)); - // Clear padding to avoid non-deterministic data in the image (and placate valgrind). + // Clear padding to avoid non-deterministic data in the image. + // Historical note: We also did that to placate Valgrind. reinterpret_cast*>(dest)->ClearPadding(size, alignment); break; } diff --git a/libartbase/base/arena_allocator.h b/libartbase/base/arena_allocator.h index 211ff4f6ad..4ad77baa24 100644 --- a/libartbase/base/arena_allocator.h +++ b/libartbase/base/arena_allocator.h @@ -147,34 +147,9 @@ class ArenaAllocatorStatsImpl { typedef ArenaAllocatorStatsImpl ArenaAllocatorStats; -template -class ArenaAllocatorMemoryToolCheckImpl { - // This is the generic template but since there is a partial specialization - // for kValgrind == false, this can be instantiated only for kValgrind == true. - static_assert(kValgrind, "This template can be instantiated only for Valgrind."); - static_assert(kAvailable, "Valgrind implies memory tool availability."); - - public: - ArenaAllocatorMemoryToolCheckImpl() : is_running_on_valgrind_(RUNNING_ON_MEMORY_TOOL) { } - bool IsRunningOnMemoryTool() { return is_running_on_valgrind_; } - - private: - const bool is_running_on_valgrind_; -}; - -template -class ArenaAllocatorMemoryToolCheckImpl { - public: - ArenaAllocatorMemoryToolCheckImpl() { } - bool IsRunningOnMemoryTool() { return kAvailable; } -}; - -typedef ArenaAllocatorMemoryToolCheckImpl - ArenaAllocatorMemoryToolCheck; - -class ArenaAllocatorMemoryTool : private ArenaAllocatorMemoryToolCheck { +class ArenaAllocatorMemoryTool { public: - using ArenaAllocatorMemoryToolCheck::IsRunningOnMemoryTool; + bool IsRunningOnMemoryTool() { return kMemoryToolIsAvailable; } void MakeDefined(void* ptr, size_t size) { if (UNLIKELY(IsRunningOnMemoryTool())) { diff --git a/libartbase/base/arena_allocator_test.cc b/libartbase/base/arena_allocator_test.cc index e358710ca6..6323a2b97c 100644 --- a/libartbase/base/arena_allocator_test.cc +++ b/libartbase/base/arena_allocator_test.cc @@ -16,6 +16,7 @@ #include "arena_allocator-inl.h" #include "arena_bit_vector.h" +#include "base/common_art_test.h" #include "gtest/gtest.h" #include "malloc_arena_pool.h" #include "memory_tool.h" @@ -146,11 +147,8 @@ TEST_F(ArenaAllocatorTest, AllocAlignment) { } TEST_F(ArenaAllocatorTest, ReallocReuse) { - // Realloc does not reuse arenas when running under sanitization. So we cannot do those - if (RUNNING_ON_MEMORY_TOOL != 0) { - printf("WARNING: TEST DISABLED FOR MEMORY_TOOL\n"); - return; - } + // Realloc does not reuse arenas when running under sanitization. + TEST_DISABLED_FOR_MEMORY_TOOL(); { // Case 1: small aligned allocation, aligned extend inside arena. diff --git a/libartbase/base/common_art_test.h b/libartbase/base/common_art_test.h index a4764c275d..fe988a478a 100644 --- a/libartbase/base/common_art_test.h +++ b/libartbase/base/common_art_test.h @@ -207,23 +207,11 @@ using CommonArtTestWithParam = CommonArtTestBase>; } #define TEST_DISABLED_FOR_MEMORY_TOOL() \ - if (RUNNING_ON_MEMORY_TOOL > 0) { \ + if (kRunningOnMemoryTool) { \ printf("WARNING: TEST DISABLED FOR MEMORY TOOL\n"); \ return; \ } -#define TEST_DISABLED_FOR_MEMORY_TOOL_VALGRIND() \ - if (RUNNING_ON_MEMORY_TOOL > 0 && kMemoryToolIsValgrind) { \ - printf("WARNING: TEST DISABLED FOR MEMORY TOOL VALGRIND\n"); \ - return; \ - } - -#define TEST_DISABLED_FOR_MEMORY_TOOL_ASAN() \ - if (RUNNING_ON_MEMORY_TOOL > 0 && !kMemoryToolIsValgrind) { \ - printf("WARNING: TEST DISABLED FOR MEMORY TOOL ASAN\n"); \ - return; \ - } - #define TEST_DISABLED_FOR_HEAP_POISONING() \ if (kPoisonHeapReferences) { \ printf("WARNING: TEST DISABLED FOR HEAP POISONING\n"); \ diff --git a/libartbase/base/malloc_arena_pool.cc b/libartbase/base/malloc_arena_pool.cc index 144b06ceb9..15a5d71a6b 100644 --- a/libartbase/base/malloc_arena_pool.cc +++ b/libartbase/base/malloc_arena_pool.cc @@ -53,7 +53,7 @@ MallocArena::MallocArena(size_t size) { memory_ = unaligned_memory_; } else { memory_ = AlignUp(unaligned_memory_, ArenaAllocator::kArenaAlignment); - if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { + if (kRunningOnMemoryTool) { size_t head = memory_ - unaligned_memory_; size_t tail = overallocation - head; MEMORY_TOOL_MAKE_NOACCESS(unaligned_memory_, head); @@ -66,7 +66,7 @@ MallocArena::MallocArena(size_t size) { MallocArena::~MallocArena() { constexpr size_t overallocation = RequiredOverallocation(); - if (overallocation != 0u && UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { + if (overallocation != 0u && kRunningOnMemoryTool) { size_t head = memory_ - unaligned_memory_; size_t tail = overallocation - head; MEMORY_TOOL_MAKE_UNDEFINED(unaligned_memory_, head); @@ -132,7 +132,7 @@ size_t MallocArenaPool::GetBytesAllocated() const { } void MallocArenaPool::FreeArenaChain(Arena* first) { - if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { + if (kRunningOnMemoryTool) { for (Arena* arena = first; arena != nullptr; arena = arena->next_) { MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_); } diff --git a/libartbase/base/mem_map.cc b/libartbase/base/mem_map.cc index 9a1392ceee..4e799bf491 100644 --- a/libartbase/base/mem_map.cc +++ b/libartbase/base/mem_map.cc @@ -524,7 +524,7 @@ MemMap* MemMap::MapFileAtAddress(uint8_t* expected_ptr, (expected_ptr == nullptr) ? nullptr : (expected_ptr - page_offset); size_t redzone_size = 0; - if (RUNNING_ON_MEMORY_TOOL && kMemoryToolAddsRedzones && expected_ptr == nullptr) { + if (kRunningOnMemoryTool && kMemoryToolAddsRedzones && expected_ptr == nullptr) { redzone_size = kPageSize; page_aligned_byte_count += redzone_size; } @@ -713,9 +713,11 @@ void MemMap::MadviseDontNeedAndZero() { bool MemMap::Sync() { bool result; if (redzone_size_ != 0) { - // To avoid valgrind errors, temporarily lift the lower-end noaccess protection before passing - // it to msync() as it only accepts page-aligned base address, and exclude the higher-end - // noaccess protection from the msync range. b/27552451. + // To avoid errors when running on a memory tool, temporarily lift the lower-end noaccess + // protection before passing it to msync() as it only accepts page-aligned base address, + // and exclude the higher-end noaccess protection from the msync range. b/27552451. + // TODO: Valgrind is no longer supported, but Address Sanitizer is: + // check whether this special case is needed for ASan. uint8_t* base_begin = reinterpret_cast(base_begin_); MEMORY_TOOL_MAKE_DEFINED(base_begin, begin_ - base_begin); result = msync(BaseBegin(), End() - base_begin, MS_SYNC) == 0; diff --git a/libartbase/base/mem_map_test.cc b/libartbase/base/mem_map_test.cc index d956126df1..4a78bdcabe 100644 --- a/libartbase/base/mem_map_test.cc +++ b/libartbase/base/mem_map_test.cc @@ -471,31 +471,33 @@ TEST_F(MemMapTest, MapAnonymousExactAddr32bitHighAddr) { // cannot allocate in the 2GB-4GB region. TEST_DISABLED_FOR_MIPS(); + // This test may not work under Valgrind. + // TODO: Valgrind is no longer supported, but Address Sanitizer is: + // check whether this test works with ASan. + TEST_DISABLED_FOR_MEMORY_TOOL(); + CommonInit(); - // This test may not work under valgrind. - if (RUNNING_ON_MEMORY_TOOL == 0) { - constexpr size_t size = 0x100000; - // Try all addresses starting from 2GB to 4GB. - size_t start_addr = 2 * GB; - std::string error_msg; - std::unique_ptr map; - for (; start_addr <= std::numeric_limits::max() - size; start_addr += size) { - map.reset(MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr", - reinterpret_cast(start_addr), - size, - PROT_READ | PROT_WRITE, - /*low_4gb*/true, - false, - &error_msg)); - if (map != nullptr) { - break; - } + constexpr size_t size = 0x100000; + // Try all addresses starting from 2GB to 4GB. + size_t start_addr = 2 * GB; + std::string error_msg; + std::unique_ptr map; + for (; start_addr <= std::numeric_limits::max() - size; start_addr += size) { + map.reset(MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr", + reinterpret_cast(start_addr), + size, + PROT_READ | PROT_WRITE, + /*low_4gb*/true, + false, + &error_msg)); + if (map != nullptr) { + break; } - ASSERT_TRUE(map.get() != nullptr) << error_msg; - ASSERT_GE(reinterpret_cast(map->End()), 2u * GB); - ASSERT_TRUE(error_msg.empty()); - ASSERT_EQ(BaseBegin(map.get()), reinterpret_cast(start_addr)); } + ASSERT_TRUE(map.get() != nullptr) << error_msg; + ASSERT_GE(reinterpret_cast(map->End()), 2u * GB); + ASSERT_TRUE(error_msg.empty()); + ASSERT_EQ(BaseBegin(map.get()), reinterpret_cast(start_addr)); } TEST_F(MemMapTest, MapAnonymousOverflow) { diff --git a/libartbase/base/memory_tool.h b/libartbase/base/memory_tool.h index e1df99fed4..d381f010f5 100644 --- a/libartbase/base/memory_tool.h +++ b/libartbase/base/memory_tool.h @@ -19,53 +19,53 @@ #include +namespace art { + #if !defined(__has_feature) -#define __has_feature(x) 0 +# define __has_feature(x) 0 #endif #if __has_feature(address_sanitizer) -#include -#define ADDRESS_SANITIZER +# include +# define ADDRESS_SANITIZER -#ifdef ART_ENABLE_ADDRESS_SANITIZER -#define MEMORY_TOOL_MAKE_NOACCESS(p, s) __asan_poison_memory_region(p, s) -#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) __asan_unpoison_memory_region(p, s) -#define MEMORY_TOOL_MAKE_DEFINED(p, s) __asan_unpoison_memory_region(p, s) +# ifdef ART_ENABLE_ADDRESS_SANITIZER +# define MEMORY_TOOL_MAKE_NOACCESS(p, s) __asan_poison_memory_region(p, s) +# define MEMORY_TOOL_MAKE_UNDEFINED(p, s) __asan_unpoison_memory_region(p, s) +# define MEMORY_TOOL_MAKE_DEFINED(p, s) __asan_unpoison_memory_region(p, s) constexpr bool kMemoryToolIsAvailable = true; -#else -#define MEMORY_TOOL_MAKE_NOACCESS(p, s) do { (void)(p); (void)(s); } while (0) -#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) do { (void)(p); (void)(s); } while (0) -#define MEMORY_TOOL_MAKE_DEFINED(p, s) do { (void)(p); (void)(s); } while (0) +# else +# define MEMORY_TOOL_MAKE_NOACCESS(p, s) do { (void)(p); (void)(s); } while (0) +# define MEMORY_TOOL_MAKE_UNDEFINED(p, s) do { (void)(p); (void)(s); } while (0) +# define MEMORY_TOOL_MAKE_DEFINED(p, s) do { (void)(p); (void)(s); } while (0) constexpr bool kMemoryToolIsAvailable = false; -#endif +# endif extern "C" void __asan_handle_no_return(); -#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) -#define MEMORY_TOOL_HANDLE_NO_RETURN __asan_handle_no_return() -#define RUNNING_ON_MEMORY_TOOL 1U -constexpr bool kMemoryToolIsValgrind = false; +# define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) +# define MEMORY_TOOL_HANDLE_NO_RETURN __asan_handle_no_return() +constexpr bool kRunningOnMemoryTool = true; constexpr bool kMemoryToolDetectsLeaks = true; constexpr bool kMemoryToolAddsRedzones = true; constexpr size_t kMemoryToolStackGuardSizeScale = 2; #else -#include -#include -#define MEMORY_TOOL_MAKE_NOACCESS(p, s) VALGRIND_MAKE_MEM_NOACCESS(p, s) -#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) VALGRIND_MAKE_MEM_UNDEFINED(p, s) -#define MEMORY_TOOL_MAKE_DEFINED(p, s) VALGRIND_MAKE_MEM_DEFINED(p, s) -#define ATTRIBUTE_NO_SANITIZE_ADDRESS -#define MEMORY_TOOL_HANDLE_NO_RETURN do { } while (0) -#define RUNNING_ON_MEMORY_TOOL RUNNING_ON_VALGRIND -constexpr bool kMemoryToolIsAvailable = true; -constexpr bool kMemoryToolIsValgrind = true; -constexpr bool kMemoryToolDetectsLeaks = true; -constexpr bool kMemoryToolAddsRedzones = true; +# define MEMORY_TOOL_MAKE_NOACCESS(p, s) do { (void)(p); (void)(s); } while (0) +# define MEMORY_TOOL_MAKE_UNDEFINED(p, s) do { (void)(p); (void)(s); } while (0) +# define MEMORY_TOOL_MAKE_DEFINED(p, s) do { (void)(p); (void)(s); } while (0) +# define ATTRIBUTE_NO_SANITIZE_ADDRESS +# define MEMORY_TOOL_HANDLE_NO_RETURN do { } while (0) +constexpr bool kRunningOnMemoryTool = false; +constexpr bool kMemoryToolIsAvailable = false; +constexpr bool kMemoryToolDetectsLeaks = false; +constexpr bool kMemoryToolAddsRedzones = false; constexpr size_t kMemoryToolStackGuardSizeScale = 1; #endif +} // namespace art + #endif // ART_LIBARTBASE_BASE_MEMORY_TOOL_H_ diff --git a/libartbase/base/scoped_arena_containers.h b/libartbase/base/scoped_arena_containers.h index 41939816f5..679bcc0e26 100644 --- a/libartbase/base/scoped_arena_containers.h +++ b/libartbase/base/scoped_arena_containers.h @@ -228,7 +228,7 @@ class ArenaDelete { protected: // Used for variable sized objects such as RegisterLine. ALWAYS_INLINE void ProtectMemory(T* ptr, size_t size) const { - if (RUNNING_ON_MEMORY_TOOL > 0) { + if (kRunningOnMemoryTool) { // Writing to the memory will fail ift we already destroyed the pointer with // DestroyOnlyDelete since we make it no access. memset(ptr, kMagicFill, size); diff --git a/libdexfile/dex/dex_file_tracking_registrar.cc b/libdexfile/dex/dex_file_tracking_registrar.cc index 78ea9c16cb..551bea108c 100644 --- a/libdexfile/dex/dex_file_tracking_registrar.cc +++ b/libdexfile/dex/dex_file_tracking_registrar.cc @@ -130,7 +130,8 @@ inline void SetRegistrationRange(const void* begin, size_t size, bool should_poi MEMORY_TOOL_MAKE_NOACCESS(begin, size); } else { // Note: MEMORY_TOOL_MAKE_UNDEFINED has the same functionality with Address - // Sanitizer. The difference has not been tested with Valgrind + // Sanitizer. + // Historical note: The difference has not been tested with Valgrind. MEMORY_TOOL_MAKE_DEFINED(begin, size); } } diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index dc9d990e29..0889a8e6ae 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -610,7 +610,7 @@ bool PatchOat::Patch(const std::string& image_location, } } - if (!kIsDebugBuild && !(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { + if (!kIsDebugBuild && !(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { // We want to just exit on non-debug builds, not bringing the runtime down // in an orderly fashion. So release the following fields. runtime.release(); @@ -690,7 +690,7 @@ bool PatchOat::Verify(const std::string& image_location, } } - if (!kIsDebugBuild && !(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { + if (!kIsDebugBuild && !(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { // We want to just exit on non-debug builds, not bringing the runtime down // in an orderly fashion. So release the following fields. runtime.release(); diff --git a/runtime/base/mem_map_arena_pool.cc b/runtime/base/mem_map_arena_pool.cc index 9ac7886e5d..702f0e453b 100644 --- a/runtime/base/mem_map_arena_pool.cc +++ b/runtime/base/mem_map_arena_pool.cc @@ -125,7 +125,7 @@ size_t MemMapArenaPool::GetBytesAllocated() const { } void MemMapArenaPool::FreeArenaChain(Arena* first) { - if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { + if (kRunningOnMemoryTool) { for (Arena* arena = first; arena != nullptr; arena = arena->next_) { MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_); } diff --git a/runtime/exec_utils_test.cc b/runtime/exec_utils_test.cc index 68edfa8b72..a9c1ea2ae0 100644 --- a/runtime/exec_utils_test.cc +++ b/runtime/exec_utils_test.cc @@ -36,8 +36,10 @@ TEST_F(ExecUtilsTest, ExecSuccess) { command.push_back("/usr/bin/id"); } std::string error_msg; - if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { - // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks. + if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { + // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks. + // TODO: Valgrind is no longer supported, but Address Sanitizer is: + // check whether the following code works with ASan. EXPECT_TRUE(Exec(command, &error_msg)); } EXPECT_EQ(0U, error_msg.size()) << error_msg; @@ -50,8 +52,10 @@ TEST_F(ExecUtilsTest, ExecError) { std::vector command; command.push_back("bogus"); std::string error_msg; - if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { - // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks. + if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { + // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks. + // TODO: Valgrind is no longer supported, but Address Sanitizer is: + // check whether the following code works with ASan. EXPECT_FALSE(Exec(command, &error_msg)); EXPECT_FALSE(error_msg.empty()); } @@ -72,8 +76,10 @@ TEST_F(ExecUtilsTest, EnvSnapshotAdditionsAreNotVisible) { } command.push_back(kModifiedVariable); std::string error_msg; - if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { - // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks. + if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { + // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks. + // TODO: Valgrind is no longer supported, but Address Sanitizer is: + // check whether the following code works with ASan. EXPECT_FALSE(Exec(command, &error_msg)); EXPECT_NE(0U, error_msg.size()) << error_msg; } @@ -97,8 +103,10 @@ TEST_F(ExecUtilsTest, EnvSnapshotDeletionsAreNotVisible) { } command.push_back(kDeletedVariable); std::string error_msg; - if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { - // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks. + if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { + // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks. + // TODO: Valgrind is no longer supported, but Address Sanitizer is: + // check whether the following code works with ASan. EXPECT_TRUE(Exec(command, &error_msg)); EXPECT_EQ(0U, error_msg.size()) << error_msg; } diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h index 150fe956ae..30213d55c5 100644 --- a/runtime/gc/allocator/rosalloc.h +++ b/runtime/gc/allocator/rosalloc.h @@ -625,7 +625,7 @@ class RosAlloc { // If true, check that the returned memory is actually zero. static constexpr bool kCheckZeroMemory = kIsDebugBuild; - // Valgrind protects memory, so do not check memory when running under valgrind. In a normal + // Do not check memory when running under a memory tool. In a normal // build with kCheckZeroMemory the whole test should be optimized away. // TODO: Unprotect before checks. ALWAYS_INLINE bool ShouldCheckZeroMemory(); @@ -768,7 +768,7 @@ class RosAlloc { // greater than or equal to this value, release pages. const size_t page_release_size_threshold_; - // Whether this allocator is running under Valgrind. + // Whether this allocator is running on a memory tool. bool is_running_on_memory_tool_; // The base address of the memory region that's managed by this allocator. diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 948d23303c..675686830e 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -272,7 +272,7 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, } case kAllocatorTypeRosAlloc: { if (kInstrumented && UNLIKELY(is_running_on_memory_tool_)) { - // If running on valgrind or asan, we should be using the instrumented path. + // If running on ASan, we should be using the instrumented path. size_t max_bytes_tl_bulk_allocated = rosalloc_space_->MaxBytesBulkAllocatedFor(alloc_size); if (UNLIKELY(IsOutOfMemoryOnAllocation(allocator_type, max_bytes_tl_bulk_allocated, @@ -303,7 +303,7 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, } case kAllocatorTypeDlMalloc: { if (kInstrumented && UNLIKELY(is_running_on_memory_tool_)) { - // If running on valgrind, we should be using the instrumented path. + // If running on ASan, we should be using the instrumented path. ret = dlmalloc_space_->Alloc(self, alloc_size, bytes_allocated, diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index b004566ed1..12021b7f99 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -2248,7 +2248,8 @@ class ZygoteCompactingCollector FINAL : public collector::SemiSpace { // Add a new bin with the remaining space. AddBin(size - alloc_size, pos + alloc_size); } - // Copy the object over to its new location. Don't use alloc_size to avoid valgrind error. + // Copy the object over to its new location. + // Historical note: We did not use `alloc_size` to avoid a Valgrind error. memcpy(reinterpret_cast(forward_address), obj, obj_size); if (kUseBakerReadBarrier) { obj->AssertReadBarrierState(); diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc index 512cde484d..a24ca32314 100644 --- a/runtime/gc/space/large_object_space.cc +++ b/runtime/gc/space/large_object_space.cc @@ -45,8 +45,9 @@ class MemoryToolLargeObjectMapSpace FINAL : public LargeObjectMapSpace { } ~MemoryToolLargeObjectMapSpace() OVERRIDE { - // Keep valgrind happy if there is any large objects such as dex cache arrays which aren't - // freed since they are held live by the class linker. + // Historical note: We were deleting large objects to keep Valgrind happy if there were + // any large objects such as Dex cache arrays which aren't freed since they are held live + // by the class linker. MutexLock mu(Thread::Current(), lock_); for (auto& m : large_objects_) { delete m.second.mem_map; diff --git a/runtime/gc/space/memory_tool_malloc_space-inl.h b/runtime/gc/space/memory_tool_malloc_space-inl.h index 8282f3dda7..c022171082 100644 --- a/runtime/gc/space/memory_tool_malloc_space-inl.h +++ b/runtime/gc/space/memory_tool_malloc_space-inl.h @@ -30,11 +30,14 @@ namespace space { namespace memory_tool_details { template -inline mirror::Object* AdjustForValgrind(void* obj_with_rdz, size_t num_bytes, - size_t bytes_allocated, size_t usable_size, - size_t bytes_tl_bulk_allocated, - size_t* bytes_allocated_out, size_t* usable_size_out, - size_t* bytes_tl_bulk_allocated_out) { +inline mirror::Object* AdjustForMemoryTool(void* obj_with_rdz, + size_t num_bytes, + size_t bytes_allocated, + size_t usable_size, + size_t bytes_tl_bulk_allocated, + size_t* bytes_allocated_out, + size_t* usable_size_out, + size_t* bytes_tl_bulk_allocated_out) { if (bytes_allocated_out != nullptr) { *bytes_allocated_out = bytes_allocated; } @@ -84,24 +87,31 @@ template mirror::Object* MemoryToolMallocSpace::AllocWithGrowth( - Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out, + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::AllocWithGrowth( + Thread* self, + size_t num_bytes, + size_t* bytes_allocated_out, + size_t* usable_size_out, size_t* bytes_tl_bulk_allocated_out) { size_t bytes_allocated; size_t usable_size; size_t bytes_tl_bulk_allocated; - void* obj_with_rdz = S::AllocWithGrowth(self, num_bytes + 2 * kMemoryToolRedZoneBytes, - &bytes_allocated, &usable_size, + void* obj_with_rdz = S::AllocWithGrowth(self, + num_bytes + 2 * kMemoryToolRedZoneBytes, + &bytes_allocated, + &usable_size, &bytes_tl_bulk_allocated); if (obj_with_rdz == nullptr) { return nullptr; } - return memory_tool_details::AdjustForValgrind( - obj_with_rdz, num_bytes, - bytes_allocated, usable_size, + return memory_tool_details::AdjustForMemoryTool( + obj_with_rdz, + num_bytes, + bytes_allocated, + usable_size, bytes_tl_bulk_allocated, bytes_allocated_out, usable_size_out, @@ -113,27 +123,35 @@ template mirror::Object* MemoryToolMallocSpace::Alloc( - Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out, + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::Alloc( + Thread* self, + size_t num_bytes, + size_t* bytes_allocated_out, + size_t* usable_size_out, size_t* bytes_tl_bulk_allocated_out) { size_t bytes_allocated; size_t usable_size; size_t bytes_tl_bulk_allocated; - void* obj_with_rdz = S::Alloc(self, num_bytes + 2 * kMemoryToolRedZoneBytes, - &bytes_allocated, &usable_size, &bytes_tl_bulk_allocated); + void* obj_with_rdz = S::Alloc(self, + num_bytes + 2 * kMemoryToolRedZoneBytes, + &bytes_allocated, + &usable_size, + &bytes_tl_bulk_allocated); if (obj_with_rdz == nullptr) { return nullptr; } - return memory_tool_details::AdjustForValgrind(obj_with_rdz, num_bytes, - bytes_allocated, usable_size, - bytes_tl_bulk_allocated, - bytes_allocated_out, - usable_size_out, - bytes_tl_bulk_allocated_out); + return memory_tool_details::AdjustForMemoryTool( + obj_with_rdz, + num_bytes, + bytes_allocated, + usable_size, + bytes_tl_bulk_allocated, + bytes_allocated_out, + usable_size_out, + bytes_tl_bulk_allocated_out); } template mirror::Object* MemoryToolMallocSpace::AllocThreadUnsafe( - Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out, + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::AllocThreadUnsafe( + Thread* self, + size_t num_bytes, + size_t* bytes_allocated_out, + size_t* usable_size_out, size_t* bytes_tl_bulk_allocated_out) { size_t bytes_allocated; size_t usable_size; size_t bytes_tl_bulk_allocated; - void* obj_with_rdz = S::AllocThreadUnsafe(self, num_bytes + 2 * kMemoryToolRedZoneBytes, - &bytes_allocated, &usable_size, + void* obj_with_rdz = S::AllocThreadUnsafe(self, + num_bytes + 2 * kMemoryToolRedZoneBytes, + &bytes_allocated, + &usable_size, &bytes_tl_bulk_allocated); if (obj_with_rdz == nullptr) { return nullptr; } - return memory_tool_details::AdjustForValgrind( - obj_with_rdz, num_bytes, - bytes_allocated, usable_size, + return memory_tool_details::AdjustForMemoryTool( + obj_with_rdz, + num_bytes, + bytes_allocated, + usable_size, bytes_tl_bulk_allocated, bytes_allocated_out, usable_size_out, @@ -170,12 +195,14 @@ template size_t MemoryToolMallocSpace::AllocationSize( + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::AllocationSize( mirror::Object* obj, size_t* usable_size) { - size_t result = S::AllocationSize(reinterpret_cast( - reinterpret_cast(obj) - (kAdjustForRedzoneInAllocSize ? kMemoryToolRedZoneBytes : 0)), + size_t result = S::AllocationSize( + reinterpret_cast( + reinterpret_cast(obj) + - (kAdjustForRedzoneInAllocSize ? kMemoryToolRedZoneBytes : 0)), usable_size); if (usable_size != nullptr) { if (kUseObjSizeForUsable) { @@ -192,10 +219,9 @@ template size_t MemoryToolMallocSpace::Free( - Thread* self, mirror::Object* ptr) { + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::Free(Thread* self, mirror::Object* ptr) { void* obj_after_rdz = reinterpret_cast(ptr); uint8_t* obj_with_rdz = reinterpret_cast(obj_after_rdz) - kMemoryToolRedZoneBytes; @@ -220,10 +246,10 @@ template size_t MemoryToolMallocSpace::FreeList( - Thread* self, size_t num_ptrs, mirror::Object** ptrs) { + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::FreeList( + Thread* self, size_t num_ptrs, mirror::Object** ptrs) { size_t freed = 0; for (size_t i = 0; i < num_ptrs; i++) { freed += Free(self, ptrs[i]); @@ -238,11 +264,12 @@ template template MemoryToolMallocSpace::MemoryToolMallocSpace( - MemMap* mem_map, size_t initial_size, Params... params) : S(mem_map, initial_size, params...) { - // Don't want to change the valgrind states of the mem map here as the allocator is already + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::MemoryToolMallocSpace( + MemMap* mem_map, size_t initial_size, Params... params) + : S(mem_map, initial_size, params...) { + // Don't want to change the memory tool states of the mem map here as the allocator is already // initialized at this point and that may interfere with what the allocator does internally. Note // that the tail beyond the initial size is mprotected. } @@ -252,9 +279,9 @@ template size_t MemoryToolMallocSpace::MaxBytesBulkAllocatedFor(size_t num_bytes) { + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::MaxBytesBulkAllocatedFor(size_t num_bytes) { return S::MaxBytesBulkAllocatedFor(num_bytes + 2 * kMemoryToolRedZoneBytes); } diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc index e7865363a1..d698cf20ae 100644 --- a/runtime/gc/space/rosalloc_space.cc +++ b/runtime/gc/space/rosalloc_space.cc @@ -77,7 +77,7 @@ RosAllocSpace* RosAllocSpace::CreateFromMemMap(MemMap* mem_map, const std::strin // Everything is set so record in immutable structure and leave uint8_t* begin = mem_map->Begin(); - // TODO: Fix RosAllocSpace to support Valgrind/ASan. There is currently some issues with + // TODO: Fix RosAllocSpace to support ASan. There is currently some issues with // AllocationSize caused by redzones. b/12944686 if (running_on_memory_tool) { return new MemoryToolMallocSpace( @@ -382,12 +382,12 @@ size_t RosAllocSpace::AllocationSizeNonvirtual(mirror::Object* obj, size_t* usab size_t size = obj->SizeOf(); bool add_redzones = false; if (kMaybeIsRunningOnMemoryTool) { - add_redzones = RUNNING_ON_MEMORY_TOOL ? kMemoryToolAddsRedzones : 0; + add_redzones = kRunningOnMemoryTool ? kMemoryToolAddsRedzones : 0; if (add_redzones) { size += 2 * kDefaultMemoryToolRedZoneBytes; } } else { - DCHECK_EQ(RUNNING_ON_MEMORY_TOOL, 0U); + DCHECK(!kRunningOnMemoryTool); } size_t size_by_size = rosalloc_->UsableSize(size); if (kIsDebugBuild) { diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h index 9d16b87b7d..4c17233360 100644 --- a/runtime/gc/space/rosalloc_space.h +++ b/runtime/gc/space/rosalloc_space.h @@ -159,8 +159,8 @@ class RosAllocSpace : public MallocSpace { void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size, size_t maximum_size, bool low_memory_mode) OVERRIDE { - return CreateRosAlloc(base, morecore_start, initial_size, maximum_size, low_memory_mode, - RUNNING_ON_MEMORY_TOOL != 0); + return CreateRosAlloc( + base, morecore_start, initial_size, maximum_size, low_memory_mode, kRunningOnMemoryTool); } static allocator::RosAlloc* CreateRosAlloc(void* base, size_t morecore_start, size_t initial_size, size_t maximum_size, bool low_memory_mode, diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc index fd435627bf..aeb5f4b1ff 100644 --- a/runtime/interpreter/unstarted_runtime_test.cc +++ b/runtime/interpreter/unstarted_runtime_test.cc @@ -864,11 +864,6 @@ TEST_F(UnstartedRuntimeTest, Cos) { } TEST_F(UnstartedRuntimeTest, Pow) { - // Valgrind seems to get this wrong, actually. Disable for valgrind. - if (RUNNING_ON_MEMORY_TOOL != 0 && kMemoryToolIsValgrind) { - return; - } - Thread* self = Thread::Current(); ScopedObjectAccess soa(self); diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 0684b461ae..42a4a8d204 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -347,7 +347,7 @@ void Jit::DeleteThreadPool() { } // When running sanitized, let all tasks finish to not leak. Otherwise just clear the queue. - if (!RUNNING_ON_MEMORY_TOOL) { + if (!kRunningOnMemoryTool) { pool->StopWorkers(self); pool->RemoveAllTasks(self); } diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc index 14f3f45f9e..b3a47c3053 100644 --- a/runtime/native_stack_dump.cc +++ b/runtime/native_stack_dump.cc @@ -289,8 +289,10 @@ void DumpNativeStack(std::ostream& os, ArtMethod* current_method, void* ucontext_ptr, bool skip_frames) { - // b/18119146 - if (RUNNING_ON_MEMORY_TOOL != 0) { + // Historical note: This was disabled when running under Valgrind (b/18119146). + // TODO: Valgrind is no longer supported, but Address Sanitizer is: + // check whether this test works with ASan. + if (kRunningOnMemoryTool) { return; } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index b8775b874f..d0f365e2da 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -245,7 +245,7 @@ Runtime::Runtime() exit_(nullptr), abort_(nullptr), stats_enabled_(false), - is_running_on_memory_tool_(RUNNING_ON_MEMORY_TOOL), + is_running_on_memory_tool_(kRunningOnMemoryTool), instrumentation_(), main_thread_group_(nullptr), system_thread_group_(nullptr), @@ -1355,8 +1355,10 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { case InstructionSet::kMips: case InstructionSet::kMips64: implicit_null_checks_ = true; - // Installing stack protection does not play well with valgrind. - implicit_so_checks_ = !(RUNNING_ON_MEMORY_TOOL && kMemoryToolIsValgrind); + // Installing stack protection does not play well with Valgrind. + // TODO: Valgrind is no longer supported, but Address Sanitizer is: + // check whether setting `implicit_so_checks_` to `true` works with ASan. + implicit_so_checks_ = !kRunningOnMemoryTool; break; default: // Keep the defaults. @@ -1371,8 +1373,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // These need to be in a specific order. The null point check handler must be // after the suspend check and stack overflow check handlers. // - // Note: the instances attach themselves to the fault manager and are handled by it. The manager - // will delete the instance on Shutdown(). + // Note: the instances attach themselves to the fault manager and are handled by it. The + // manager will delete the instance on Shutdown(). if (implicit_suspend_checks_) { new SuspensionHandler(&fault_manager); } diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc index 72d9919971..54769f9c49 100644 --- a/runtime/runtime_callbacks_test.cc +++ b/runtime/runtime_callbacks_test.cc @@ -339,8 +339,8 @@ class RuntimeSigQuitCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest { }; TEST_F(RuntimeSigQuitCallbackRuntimeCallbacksTest, SigQuit) { - // SigQuit induces a dump. ASAN isn't happy with libunwind reading memory. - TEST_DISABLED_FOR_MEMORY_TOOL_ASAN(); + // SigQuit induces a dump. ASan isn't happy with libunwind reading memory. + TEST_DISABLED_FOR_MEMORY_TOOL(); // The runtime needs to be started for the signal handler. Thread* self = Thread::Current(); diff --git a/runtime/thread.cc b/runtime/thread.cc index eada24d257..73701a318b 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1115,21 +1115,10 @@ bool Thread::InitStackHwm() { Runtime* runtime = Runtime::Current(); bool implicit_stack_check = !runtime->ExplicitStackOverflowChecks() && !runtime->IsAotCompiler(); - // Valgrind on arm doesn't give the right values here. Do not install the guard page, and - // effectively disable stack overflow checks (we'll get segfaults, potentially) by setting - // stack_begin to 0. - const bool valgrind_on_arm = - (kRuntimeISA == InstructionSet::kArm || kRuntimeISA == InstructionSet::kArm64) && - kMemoryToolIsValgrind && - RUNNING_ON_MEMORY_TOOL != 0; - if (valgrind_on_arm) { - tlsPtr_.stack_begin = nullptr; - } - ResetDefaultStackEnd(); // Install the protected region if we are doing implicit overflow checks. - if (implicit_stack_check && !valgrind_on_arm) { + if (implicit_stack_check) { // The thread might have protected region at the bottom. We need // to install our own region so we need to move the limits // of the stack to make room for it. diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc index 49db0c82b5..a4d0d0cbe4 100644 --- a/test/137-cfi/cfi.cc +++ b/test/137-cfi/cfi.cc @@ -111,8 +111,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess( jint, jboolean) { #if __linux__ - // TODO: What to do on Valgrind? - std::unique_ptr bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid())); if (!bt->Unwind(0, nullptr)) { printf("Cannot unwind in process.\n"); @@ -188,7 +186,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess( jboolean, jint pid_int) { #if __linux__ - // TODO: What to do on Valgrind? pid_t pid = static_cast(pid_int); // OK, this is painful. debuggerd uses ptrace to unwind other processes. diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index e0757abbe0..faa4d91f58 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -266,14 +266,16 @@ target_config = { } }, 'art-gtest-valgrind32': { - # Disabled: x86 valgrind does not understand SSE4.x + # Disabled: Valgrind is no longer supported. + # Historical note: This was already disabled, as x86 valgrind did not understand SSE4.x # 'make' : 'valgrind-test-art-host32', 'env': { 'ART_USE_READ_BARRIER' : 'false' } }, 'art-gtest-valgrind64': { - 'make' : 'valgrind-test-art-host64', + # Disabled: Valgrind is no longer supported. + # 'make' : 'valgrind-test-art-host64', 'env': { 'ART_USE_READ_BARRIER' : 'false' } diff --git a/test/valgrind-suppressions.txt b/test/valgrind-suppressions.txt deleted file mode 100644 index a97d03c2d4..0000000000 --- a/test/valgrind-suppressions.txt +++ /dev/null @@ -1,87 +0,0 @@ -{ - b/27596582 - Memcheck:Cond - fun:index - fun:expand_dynamic_string_token - fun:_dl_map_object - fun:map_doit - fun:_dl_catch_error - fun:do_preload - fun:dl_main - fun:_dl_sysdep_start - fun:_dl_start_final - fun:_dl_start - obj:/lib/x86_64-linux-gnu/ld-2.19.so -} - -{ - b/31275764 - Memcheck:Leak - match-leak-kinds: definite - fun:malloc - ... - fun:_ZN3art7Runtime17InitNativeMethodsEv -} - -# SigQuit runs libbacktrace -{ - BackTraceReading64 - Memcheck:Addr8 - fun:access_mem_unrestricted - fun:_Uelf64_memory_read - fun:_Uelf64_valid_object_memory - fun:map_create_list - fun:unw_map_local_create - fun:_ZN14UnwindMapLocal5BuildEv - fun:_ZN12BacktraceMap6CreateEib -} -{ - BackTraceReading32 - Memcheck:Addr4 - fun:access_mem_unrestricted - fun:_Uelf32_memory_read - fun:_Uelf32_valid_object_memory - fun:map_create_list - fun:unw_map_local_create - fun:_ZN14UnwindMapLocal5BuildEv - fun:_ZN12BacktraceMap6CreateEib -} -{ - BackTraceReading64 - Memcheck:Addr8 - fun:access_mem_unrestricted - fun:_Uelf64_memory_read - fun:_Uelf64_get_load_base - fun:map_create_list - fun:unw_map_local_create - fun:_ZN14UnwindMapLocal5BuildEv - fun:_ZN12BacktraceMap6CreateEib -} -{ - BackTraceReading32 - Memcheck:Addr4 - fun:access_mem_unrestricted - fun:_Uelf32_memory_read - fun:_Uelf32_get_load_base - fun:map_create_list - fun:unw_map_local_create - fun:_ZN14UnwindMapLocal5BuildEv - fun:_ZN12BacktraceMap6CreateEib -} - -{ - process_vm_readv - Memcheck:Param - process_vm_readv(lvec[...]) - fun:process_vm_readv -} - -# Suppressions for IsAddressMapped check in MemMapTest -{ - MemMapTest_IsAddressMapped - Memcheck:Param - msync(start) - ... - fun:_ZN3art10MemMapTest15IsAddressMappedEPv - ... -} diff --git a/test/valgrind-target-suppressions.txt b/test/valgrind-target-suppressions.txt deleted file mode 100644 index 0d63a1c7aa..0000000000 --- a/test/valgrind-target-suppressions.txt +++ /dev/null @@ -1,76 +0,0 @@ -# Valgrind does not recognize the ashmen ioctl() calls on ARM64, so it assumes that a size -# parameter is a pointer. -{ - ashmem ioctl - Memcheck:Param - ioctl(generic) - ... - fun:ioctl - fun:ashmem_create_region -} - -# It seems that on ARM64 Valgrind considers the canary value used by the Clang stack protector to -# be an uninitialized value. -{ - jemalloc chunk_alloc_cache - Memcheck:Cond - fun:je_chunk_alloc_cache -} - -# The VectorImpl class does not hold a pointer to the allocated SharedBuffer structure, but to the -# beginning of the data, which is effectively an interior pointer. Valgrind has limitations when -# dealing with interior pointers. -{ - VectorImpl - Memcheck:Leak - match-leak-kinds:possible - fun:malloc - # The wildcards make this rule work both for 32-bit and 64-bit environments. - fun:_ZN7android12SharedBuffer5allocE? - fun:_ZN7android10VectorImpl5_growE?? -} - -# Clang/LLVM uses memcpy for *x = *y, even though x == y (which is undefined behavior). Ignore. -# b/29279679, https://llvm.org/bugs/show_bug.cgi?id=11763 -{ - MemCpySelfAssign - Memcheck:Overlap - fun:memcpy - ... - fun:je_malloc_tsd_boot0 -} - -# Setenv is known-leaking when overwriting mappings. This is triggered by re-initializing -# ANDROID_DATA. Ignore all setenv leaks. -{ - SetenvAndroidDataReinit - Memcheck:Leak - match-leak-kinds: definite - fun:malloc - fun:setenv -} - -{ - b/31275764 - Memcheck:Leak - match-leak-kinds: definite - fun:malloc - ... - fun:_ZN3art7Runtime17InitNativeMethodsEv -} - -# art::MemMap::MapInternal() uses msync() to check for the existence of memory mappings. -{ - art::MemMap::MapInternal() - Memcheck:Param - msync(start) - fun:msync - fun:_ZN3art6MemMap11MapInternalEPvmiiilb -} - -{ - process_vm_readv - Memcheck:Param - process_vm_readv(lvec[...]) - fun:process_vm_readv -} diff --git a/tools/art b/tools/art index 1c603d4fa7..781ee2f9cb 100644 --- a/tools/art +++ b/tools/art @@ -77,7 +77,6 @@ Usage: art [OPTIONS] [--] [ART_OPTIONS] CLASS Supported OPTIONS include: --32 Use the 32-bit Android Runtime. --64 Use the 64-bit Android Runtime. - --callgrind Launch the Android Runtime in callgrind. -d Use the debug ART library (libartd.so). --debug Equivalent to -d. --gdb Launch the Android Runtime in gdb. @@ -269,9 +268,6 @@ while [[ "$1" = "-"* ]]; do --64) ART_BINARY=dalvikvm64 ;; - --callgrind) - LAUNCH_WRAPPER="valgrind --tool=callgrind" - ;; -d) ;& # Fallthrough --debug) -- GitLab From 9e423afa4ff8790d8e04677d7a5546bbf628c512 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 14 May 2018 10:08:29 -0700 Subject: [PATCH 396/749] Handle unset ANDROID_DATA in DexLocationToOatFilename Return false instead of aborting, this can occur for preopt when using a class loader context. Bug: 67345922 Bug: 70934104 Test: test-art-host Change-Id: I4a5ef62090daad722e6db883a4c2bb41b8c68364 --- runtime/oat_file_assistant.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 7d69927ffb..c00e47f9b8 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -860,6 +860,13 @@ bool OatFileAssistant::DexLocationToOatFilename(const std::string& location, CHECK(oat_filename != nullptr); CHECK(error_msg != nullptr); + // If ANDROID_DATA is not set, return false instead of aborting. + // This can occur for preopt when using a class loader context. + if (GetAndroidDataSafe(error_msg) == nullptr) { + *error_msg = "GetAndroidDataSafe failed: " + *error_msg; + return false; + } + std::string cache_dir = GetDalvikCache(GetInstructionSetString(isa)); if (cache_dir.empty()) { *error_msg = "Dalvik cache directory does not exist"; -- GitLab From 1fee5dc295f32c6b1426e9de8a2fede86d0464f2 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 14 May 2018 14:20:51 -0700 Subject: [PATCH 397/749] Use std::cout for dexanalyze logging The ART default logger prints more info than required. Bug: 77721545 Bug: 77709234 Test: manual Change-Id: Id0cd74e1dd2632c5c203cb5ab2027effb4a72af7 --- tools/dexanalyze/dexanalyze.cc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tools/dexanalyze/dexanalyze.cc b/tools/dexanalyze/dexanalyze.cc index a5f647cc56..c4aebc6f56 100644 --- a/tools/dexanalyze/dexanalyze.cc +++ b/tools/dexanalyze/dexanalyze.cc @@ -15,6 +15,7 @@ */ #include +#include #include #include @@ -31,6 +32,15 @@ namespace art { class DexAnalyze { static const int kExitCodeUsageError = 1; + static void StdoutLogger(android::base::LogId, + android::base::LogSeverity, + const char*, + const char*, + unsigned int, + const char* message) { + std::cout << message << std::endl; + } + static int Usage(char** argv) { LOG(ERROR) << "Usage " << argv[0] << " [options] \n" @@ -105,6 +115,8 @@ class DexAnalyze { public: static int Run(int argc, char** argv) { + android::base::SetLogger(StdoutLogger); + Options options; int result = options.Parse(argc, argv); if (result != 0) { @@ -154,7 +166,6 @@ class DexAnalyze { } // namespace art int main(int argc, char** argv) { - android::base::SetLogger(android::base::StderrLogger); return art::DexAnalyze::Run(argc, argv); } -- GitLab From 537a4fed85530ac29fe5ff173c4b77538d8bbd0b Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Tue, 15 May 2018 13:57:58 +0100 Subject: [PATCH 398/749] ART: Add VarHandle accessors to invoke-polymorphic entrypoint Removes the need to interpret methods containing VarHandle accessor methods. Whilst there are VarHandle accessors that the compiler does not support, this will be the fallback path. Bug: 71781600 Test: art/test.py --host -r -t 712 Change-Id: I40314b773882faed554c31b7f34c0e319dcf8d45 --- runtime/Android.bp | 1 + .../quick/quick_trampoline_entrypoints.cc | 52 +++++++----- runtime/interpreter/interpreter_common.cc | 85 +++++-------------- runtime/mirror/var_handle.cc | 10 +-- runtime/mirror/var_handle.h | 21 ++--- runtime/var_handles.cc | 77 +++++++++++++++++ runtime/var_handles.h | 35 ++++++++ runtime/verifier/method_verifier.cc | 2 - 8 files changed, 181 insertions(+), 102 deletions(-) create mode 100644 runtime/var_handles.cc create mode 100644 runtime/var_handles.h diff --git a/runtime/Android.bp b/runtime/Android.bp index 3db4ee54c1..0540b2087e 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -195,6 +195,7 @@ libart_cc_defaults { "ti/agent.cc", "trace.cc", "transaction.cc", + "var_handles.cc", "vdex_file.cc", "verifier/instruction_flags.cc", "verifier/method_verifier.cc", diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 7e3c3dbaa6..0a186f4dc5 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -42,6 +42,7 @@ #include "mirror/method_handle_impl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" +#include "mirror/var_handle.h" #include "oat_file.h" #include "oat_quick_method_header.h" #include "quick_exception_handler.h" @@ -49,6 +50,7 @@ #include "scoped_thread_state_change-inl.h" #include "stack.h" #include "thread-inl.h" +#include "var_handles.h" #include "well_known_classes.h" namespace art { @@ -2789,13 +2791,6 @@ extern "C" uintptr_t artInvokePolymorphic( return static_cast('V'); } - // TODO(oth): Ensure this path isn't taken for VarHandle accessors (b/65872996). - DCHECK_EQ(resolved_method->GetDeclaringClass(), - WellKnownClasses::ToClass(WellKnownClasses::java_lang_invoke_MethodHandle)); - - Handle method_handle(hs.NewHandle( - ObjPtr::DownCast(MakeObjPtr(receiver_handle.Get())))); - Handle method_type( hs.NewHandle(linker->ResolveMethodType(self, proto_idx, caller_method))); @@ -2835,24 +2830,43 @@ extern "C" uintptr_t artInvokePolymorphic( // Call DoInvokePolymorphic with |is_range| = true, as shadow frame has argument registers in // consecutive order. RangeInstructionOperands operands(first_arg + 1, num_vregs - 1); - bool isExact = (jni::EncodeArtMethod(resolved_method) == - WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact); + Intrinsics intrinsic = static_cast(resolved_method->GetIntrinsic()); bool success = false; - if (isExact) { - success = MethodHandleInvokeExact(self, + if (resolved_method->GetDeclaringClass() == mirror::MethodHandle::StaticClass()) { + Handle method_handle(hs.NewHandle( + ObjPtr::DownCast(MakeObjPtr(receiver_handle.Get())))); + if (intrinsic == Intrinsics::kMethodHandleInvokeExact) { + success = MethodHandleInvokeExact(self, + *shadow_frame, + method_handle, + method_type, + &operands, + result); + } else { + DCHECK_EQ(static_cast(intrinsic), + static_cast(Intrinsics::kMethodHandleInvoke)); + success = MethodHandleInvoke(self, + *shadow_frame, + method_handle, + method_type, + &operands, + result); + } + } else { + DCHECK_EQ(mirror::VarHandle::StaticClass(), resolved_method->GetDeclaringClass()); + Handle var_handle(hs.NewHandle( + ObjPtr::DownCast(MakeObjPtr(receiver_handle.Get())))); + mirror::VarHandle::AccessMode access_mode = + mirror::VarHandle::GetAccessModeByIntrinsic(intrinsic); + success = VarHandleInvokeAccessor(self, *shadow_frame, - method_handle, + var_handle, method_type, + access_mode, &operands, result); - } else { - success = MethodHandleInvoke(self, - *shadow_frame, - method_handle, - method_type, - &operands, - result); } + DCHECK(success || self->IsExceptionPending()); // Pop transition record. diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index ded8cefb8f..d30bc10450 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -37,6 +37,7 @@ #include "stack.h" #include "thread-inl.h" #include "transaction.h" +#include "var_handles.h" #include "well_known_classes.h" namespace art { @@ -725,38 +726,6 @@ bool DoMethodHandleInvoke(Thread* self, } } -static bool DoVarHandleInvokeChecked(Thread* self, - Handle var_handle, - Handle callsite_type, - mirror::VarHandle::AccessMode access_mode, - ShadowFrame& shadow_frame, - InstructionOperands* operands, - JValue* result) - REQUIRES_SHARED(Locks::mutator_lock_) { - // TODO(oth): GetMethodTypeForAccessMode() allocates a MethodType() - // which is only required if we need to convert argument and/or - // return types. - StackHandleScope<1> hs(self); - Handle accessor_type(hs.NewHandle( - var_handle->GetMethodTypeForAccessMode(self, access_mode))); - const size_t num_vregs = accessor_type->NumberOfVRegs(); - const int num_params = accessor_type->GetPTypes()->GetLength(); - ShadowFrameAllocaUniquePtr accessor_frame = - CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC()); - ShadowFrameGetter getter(shadow_frame, operands); - static const uint32_t kFirstDestinationReg = 0; - ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg); - if (!PerformConversions(self, callsite_type, accessor_type, &getter, &setter, num_params)) { - return false; - } - RangeInstructionOperands accessor_operands(kFirstDestinationReg, - kFirstDestinationReg + num_vregs); - if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) { - return false; - } - return ConvertReturnValue(callsite_type, accessor_type, result); -} - static bool DoVarHandleInvokeCommon(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, @@ -769,21 +738,8 @@ static bool DoVarHandleInvokeCommon(Thread* self, return false; } - bool is_var_args = inst->HasVarArgs(); - const uint32_t vRegC = is_var_args ? inst->VRegC_45cc() : inst->VRegC_4rcc(); - ObjPtr receiver(shadow_frame.GetVRegReference(vRegC)); - if (receiver.IsNull()) { - ThrowNullPointerExceptionFromDexPC(); - return false; - } - StackHandleScope<2> hs(self); - Handle var_handle(hs.NewHandle(down_cast(receiver.Ptr()))); - if (!var_handle->IsAccessModeSupported(access_mode)) { - ThrowUnsupportedOperationException(); - return false; - } - + bool is_var_args = inst->HasVarArgs(); const uint16_t vRegH = is_var_args ? inst->VRegH_45cc() : inst->VRegH_4rcc(); ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); Handle callsite_type(hs.NewHandle( @@ -794,34 +750,31 @@ static bool DoVarHandleInvokeCommon(Thread* self, return false; } - if (!var_handle->IsMethodTypeCompatible(access_mode, callsite_type.Get())) { - ThrowWrongMethodTypeException(var_handle->GetMethodTypeForAccessMode(self, access_mode), - callsite_type.Get()); - return false; - } - + const uint32_t vRegC = is_var_args ? inst->VRegC_45cc() : inst->VRegC_4rcc(); + ObjPtr receiver(shadow_frame.GetVRegReference(vRegC)); + Handle var_handle(hs.NewHandle(down_cast(receiver.Ptr()))); if (is_var_args) { uint32_t args[Instruction::kMaxVarArgRegs]; inst->GetVarArgs(args, inst_data); VarArgsInstructionOperands all_operands(args, inst->VRegA_45cc()); NoReceiverInstructionOperands operands(&all_operands); - return DoVarHandleInvokeChecked(self, - var_handle, - callsite_type, - access_mode, - shadow_frame, - &operands, - result); + return VarHandleInvokeAccessor(self, + shadow_frame, + var_handle, + callsite_type, + access_mode, + &operands, + result); } else { RangeInstructionOperands all_operands(inst->VRegC_4rcc(), inst->VRegA_4rcc()); NoReceiverInstructionOperands operands(&all_operands); - return DoVarHandleInvokeChecked(self, - var_handle, - callsite_type, - access_mode, - shadow_frame, - &operands, - result); + return VarHandleInvokeAccessor(self, + shadow_frame, + var_handle, + callsite_type, + access_mode, + &operands, + result); } } diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc index b309f596fd..49cdd4ffde 100644 --- a/runtime/mirror/var_handle.cc +++ b/runtime/mirror/var_handle.cc @@ -1540,7 +1540,7 @@ MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self, AccessMode acces bool VarHandle::Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) { Class* klass = GetClass(); if (klass == FieldVarHandle::StaticClass()) { @@ -1671,7 +1671,7 @@ ArtField* FieldVarHandle::GetField() { bool FieldVarHandle::Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) { ShadowFrameGetter getter(*shadow_frame, operands); ArtField* field = GetField(); @@ -1743,7 +1743,7 @@ GcRoot FieldVarHandle::static_class_; bool ArrayElementVarHandle::Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) { ShadowFrameGetter getter(*shadow_frame, operands); @@ -1856,7 +1856,7 @@ bool ByteArrayViewVarHandle::GetNativeByteOrder() { bool ByteArrayViewVarHandle::Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) { ShadowFrameGetter getter(*shadow_frame, operands); @@ -1965,7 +1965,7 @@ bool ByteBufferViewVarHandle::GetNativeByteOrder() { bool ByteBufferViewVarHandle::Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) { ShadowFrameGetter getter(*shadow_frame, operands); diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h index d46d900a8d..6acd4e0bb1 100644 --- a/runtime/mirror/var_handle.h +++ b/runtime/mirror/var_handle.h @@ -100,11 +100,12 @@ class MANAGED VarHandle : public Object { } // Returns true if the MethodType specified is compatible with the - // method type associated with the specified AccessMode. The - // supplied MethodType is assumed to be from the point of invocation - // so it is valid for the supplied MethodType to have a void return - // value when the return value for the AccessMode is non-void. This - // corresponds to the result of the accessor being discarded. + // method type associated with the specified AccessMode with argument + // and return value conversions. The supplied MethodType is assumed + // to be from the point of invocation so it is valid for the supplied + // MethodType to have a void return value when the return value for + // the AccessMode is non-void. This corresponds to the result of the + // accessor being discarded. bool IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type) REQUIRES_SHARED(Locks::mutator_lock_); @@ -124,7 +125,7 @@ class MANAGED VarHandle : public Object { bool Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); @@ -192,7 +193,7 @@ class MANAGED FieldVarHandle : public VarHandle { public: bool Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); @@ -225,7 +226,7 @@ class MANAGED ArrayElementVarHandle : public VarHandle { public: bool Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); @@ -248,7 +249,7 @@ class MANAGED ByteArrayViewVarHandle : public VarHandle { public: bool Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); @@ -281,7 +282,7 @@ class MANAGED ByteBufferViewVarHandle : public VarHandle { public: bool Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/var_handles.cc b/runtime/var_handles.cc new file mode 100644 index 0000000000..b2416675f0 --- /dev/null +++ b/runtime/var_handles.cc @@ -0,0 +1,77 @@ +/* + * 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. + */ + +#include "var_handles.h" + +#include "common_throws.h" +#include "dex/dex_instruction.h" +#include "handle.h" +#include "method_handles-inl.h" +#include "mirror/method_type.h" +#include "mirror/var_handle.h" + +namespace art { + +bool VarHandleInvokeAccessor(Thread* self, + ShadowFrame& shadow_frame, + Handle var_handle, + Handle callsite_type, + const mirror::VarHandle::AccessMode access_mode, + const InstructionOperands* const operands, + JValue* result) { + if (var_handle.IsNull()) { + ThrowNullPointerExceptionFromDexPC(); + return false; + } + + if (!var_handle->IsAccessModeSupported(access_mode)) { + ThrowUnsupportedOperationException(); + return false; + } + + if (!var_handle->IsMethodTypeCompatible(access_mode, callsite_type.Get())) { + ThrowWrongMethodTypeException(var_handle->GetMethodTypeForAccessMode(self, access_mode), + callsite_type.Get()); + return false; + } + + StackHandleScope<1> hs(self); + + // TODO(oth): GetMethodTypeForAccessMode() allocates a MethodType() + // which is only required if we need to convert argument and/or + // return types. + Handle accessor_type(hs.NewHandle( + var_handle->GetMethodTypeForAccessMode(self, access_mode))); + const size_t num_vregs = accessor_type->NumberOfVRegs(); + const int num_params = accessor_type->GetPTypes()->GetLength(); + ShadowFrameAllocaUniquePtr accessor_frame = + CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC()); + + ShadowFrameGetter getter(shadow_frame, operands); + static const uint32_t kFirstDestinationReg = 0; + ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg); + if (!PerformConversions(self, callsite_type, accessor_type, &getter, &setter, num_params)) { + return false; + } + RangeInstructionOperands accessor_operands(kFirstDestinationReg, + kFirstDestinationReg + num_vregs); + if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) { + return false; + } + return ConvertReturnValue(callsite_type, accessor_type, result); +} + +} // namespace art diff --git a/runtime/var_handles.h b/runtime/var_handles.h new file mode 100644 index 0000000000..2ff8405f03 --- /dev/null +++ b/runtime/var_handles.h @@ -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. + */ + +#ifndef ART_RUNTIME_VAR_HANDLES_H_ +#define ART_RUNTIME_VAR_HANDLES_H_ + +#include "mirror/var_handle.h" + +namespace art { + +bool VarHandleInvokeAccessor(Thread* self, + ShadowFrame& shadow_frame, + Handle var_handle, + Handle callsite_type, + const mirror::VarHandle::AccessMode access_mode, + const InstructionOperands* const operands, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_); + +} // namespace art + +#endif // ART_RUNTIME_VAR_HANDLES_H_ diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 92ee98ae8a..91cec23dd5 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -4210,8 +4210,6 @@ bool MethodVerifier::CheckSignaturePolymorphicMethod(ArtMethod* method) { expected_return_descriptor = mirror::MethodHandle::GetReturnTypeDescriptor(method_name); } else if (klass == mirror::VarHandle::StaticClass()) { expected_return_descriptor = mirror::VarHandle::GetReturnTypeDescriptor(method_name); - // TODO: add compiler support for VarHandle accessor methods (b/71781600) - Fail(VERIFY_ERROR_FORCE_INTERPRETER); } else { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Signature polymorphic method in unsuppported class: " << klass->PrettyDescriptor(); -- GitLab From ba205000c1119f26575b417b665df37bd1d5ae95 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Tue, 15 May 2018 16:53:41 +0100 Subject: [PATCH 399/749] Refactor flaky ArtDexFileLoader IsPlatformDex gtest The gtest has been failing randomly, unable to open a dex file copied into a known location. Refactor the test: (a) to not copy/remove in SetUp()/TearDown() because that happens before every test in the test case, not just the one test where it is needed, and (b) to copy the file just before it is being opened and to remove the file as soon as it is not needed, and (c) into smaller tests, each testing one location, and (d) always print the error message ArtDexFileLoader failed with. Bug: 79177384 Test: make test-art-host-gtest-art_dex_file_loader_test Change-Id: Icfd55c1b88938cf88441d501b10e285f4fcdb60f --- runtime/dex/art_dex_file_loader_test.cc | 209 ++++++++++++++---------- 1 file changed, 119 insertions(+), 90 deletions(-) diff --git a/runtime/dex/art_dex_file_loader_test.cc b/runtime/dex/art_dex_file_loader_test.cc index c0090e62f5..d353c26b35 100644 --- a/runtime/dex/art_dex_file_loader_test.cc +++ b/runtime/dex/art_dex_file_loader_test.cc @@ -43,48 +43,7 @@ static void Copy(const std::string& src, const std::string& dst) { dst_stream << src_stream.rdbuf(); } -class ArtDexFileLoaderTest : public CommonRuntimeTest { - public: - virtual void SetUp() { - CommonRuntimeTest::SetUp(); - - std::string dex_location = GetTestDexFileName("Main"); - std::string multidex_location = GetTestDexFileName("MultiDex"); - - data_location_path_ = android_data_ + "/foo.jar"; - system_location_path_ = GetAndroidRoot() + "/foo.jar"; - system_framework_location_path_ = GetAndroidRoot() + "/framework/foo.jar"; - data_multi_location_path_ = android_data_ + "/multifoo.jar"; - system_multi_location_path_ = GetAndroidRoot() + "/multifoo.jar"; - system_framework_multi_location_path_ = GetAndroidRoot() + "/framework/multifoo.jar"; - - Copy(dex_location, data_location_path_); - Copy(dex_location, system_location_path_); - Copy(dex_location, system_framework_location_path_); - - Copy(multidex_location, data_multi_location_path_); - Copy(multidex_location, system_multi_location_path_); - Copy(multidex_location, system_framework_multi_location_path_); - } - - virtual void TearDown() { - remove(data_location_path_.c_str()); - remove(system_location_path_.c_str()); - remove(system_framework_location_path_.c_str()); - remove(data_multi_location_path_.c_str()); - remove(system_multi_location_path_.c_str()); - remove(system_framework_multi_location_path_.c_str()); - CommonRuntimeTest::TearDown(); - } - - protected: - std::string data_location_path_; - std::string system_location_path_; - std::string system_framework_location_path_; - std::string data_multi_location_path_; - std::string system_multi_location_path_; - std::string system_framework_multi_location_path_; -}; +class ArtDexFileLoaderTest : public CommonRuntimeTest {}; // TODO: Port OpenTestDexFile(s) need to be ported to use non-ART utilities, and // the tests that depend upon them should be moved to dex_file_loader_test.cc @@ -353,21 +312,24 @@ TEST_F(ArtDexFileLoaderTest, GetDexCanonicalLocation) { ASSERT_EQ(0, unlink(dex_location_sym.c_str())); } -TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_DataDir) { + // Load file from a non-system directory and check that it is not flagged as framework. + std::string data_location_path = android_data_ + "/foo.jar"; + ASSERT_FALSE(LocationIsOnSystemFramework(data_location_path.c_str())); + + Copy(GetTestDexFileName("Main"), data_location_path); + ArtDexFileLoader loader; - bool success; - std::string error_msg; std::vector> dex_files; - - // Load file from a non-system directory and check that it is not flagged as framework. - ASSERT_FALSE(LocationIsOnSystemFramework(data_location_path_.c_str())); - success = loader.Open(data_location_path_.c_str(), - data_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); + std::string error_msg; + bool success = loader.Open(data_location_path.c_str(), + data_location_path, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); ASSERT_TRUE(success) << error_msg; + ASSERT_GE(dex_files.size(), 1u); for (std::unique_ptr& dex_file : dex_files) { ASSERT_FALSE(dex_file->IsPlatformDexFile()); @@ -375,15 +337,27 @@ TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { dex_files.clear(); + ASSERT_EQ(0, remove(data_location_path.c_str())); +} + +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemDir) { // Load file from a system, non-framework directory and check that it is not flagged as framework. - ASSERT_FALSE(LocationIsOnSystemFramework(system_location_path_.c_str())); - success = loader.Open(system_location_path_.c_str(), - system_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); - ASSERT_TRUE(success); + std::string system_location_path = GetAndroidRoot() + "/foo.jar"; + ASSERT_FALSE(LocationIsOnSystemFramework(system_location_path.c_str())); + + Copy(GetTestDexFileName("Main"), system_location_path); + + ArtDexFileLoader loader; + std::vector> dex_files; + std::string error_msg; + bool success = loader.Open(system_location_path.c_str(), + system_location_path, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success) << error_msg; + ASSERT_GE(dex_files.size(), 1u); for (std::unique_ptr& dex_file : dex_files) { ASSERT_FALSE(dex_file->IsPlatformDexFile()); @@ -391,15 +365,27 @@ TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { dex_files.clear(); + ASSERT_EQ(0, remove(system_location_path.c_str())); +} + +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemFrameworkDir) { // Load file from a system/framework directory and check that it is flagged as a framework dex. - ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_location_path_.c_str())); - success = loader.Open(system_framework_location_path_.c_str(), - system_framework_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); - ASSERT_TRUE(success); + std::string system_framework_location_path = GetAndroidRoot() + "/framework/foo.jar"; + ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_location_path.c_str())); + + Copy(GetTestDexFileName("Main"), system_framework_location_path); + + ArtDexFileLoader loader; + std::vector> dex_files; + std::string error_msg; + bool success = loader.Open(system_framework_location_path.c_str(), + system_framework_location_path, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success) << error_msg; + ASSERT_GE(dex_files.size(), 1u); for (std::unique_ptr& dex_file : dex_files) { ASSERT_TRUE(dex_file->IsPlatformDexFile()); @@ -407,14 +393,27 @@ TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { dex_files.clear(); + ASSERT_EQ(0, remove(system_framework_location_path.c_str())); +} + +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_DataDir_MultiDex) { // Load multidex file from a non-system directory and check that it is not flagged as framework. - success = loader.Open(data_multi_location_path_.c_str(), - data_multi_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); + std::string data_multi_location_path = android_data_ + "/multifoo.jar"; + ASSERT_FALSE(LocationIsOnSystemFramework(data_multi_location_path.c_str())); + + Copy(GetTestDexFileName("MultiDex"), data_multi_location_path); + + ArtDexFileLoader loader; + std::vector> dex_files; + std::string error_msg; + bool success = loader.Open(data_multi_location_path.c_str(), + data_multi_location_path, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); ASSERT_TRUE(success) << error_msg; + ASSERT_GT(dex_files.size(), 1u); for (std::unique_ptr& dex_file : dex_files) { ASSERT_FALSE(dex_file->IsPlatformDexFile()); @@ -422,15 +421,28 @@ TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { dex_files.clear(); + ASSERT_EQ(0, remove(data_multi_location_path.c_str())); +} + +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemDir_MultiDex) { // Load multidex file from a system, non-framework directory and check that it is not flagged // as framework. - success = loader.Open(system_multi_location_path_.c_str(), - system_multi_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); - ASSERT_TRUE(success); + std::string system_multi_location_path = GetAndroidRoot() + "/multifoo.jar"; + ASSERT_FALSE(LocationIsOnSystemFramework(system_multi_location_path.c_str())); + + Copy(GetTestDexFileName("MultiDex"), system_multi_location_path); + + ArtDexFileLoader loader; + std::vector> dex_files; + std::string error_msg; + bool success = loader.Open(system_multi_location_path.c_str(), + system_multi_location_path, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success) << error_msg; + ASSERT_GT(dex_files.size(), 1u); for (std::unique_ptr& dex_file : dex_files) { ASSERT_FALSE(dex_file->IsPlatformDexFile()); @@ -438,19 +450,36 @@ TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { dex_files.clear(); + ASSERT_EQ(0, remove(system_multi_location_path.c_str())); +} + +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemFrameworkDir_MultiDex) { // Load multidex file from a system/framework directory and check that it is flagged as a // framework dex. - success = loader.Open(system_framework_multi_location_path_.c_str(), - system_framework_multi_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); - ASSERT_TRUE(success); + std::string system_framework_multi_location_path = GetAndroidRoot() + "/framework/multifoo.jar"; + ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_multi_location_path.c_str())); + + Copy(GetTestDexFileName("MultiDex"), system_framework_multi_location_path); + + ArtDexFileLoader loader; + std::vector> dex_files; + std::string error_msg; + bool success = loader.Open(system_framework_multi_location_path.c_str(), + system_framework_multi_location_path, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success) << error_msg; + ASSERT_GT(dex_files.size(), 1u); for (std::unique_ptr& dex_file : dex_files) { ASSERT_TRUE(dex_file->IsPlatformDexFile()); } + + dex_files.clear(); + + ASSERT_EQ(0, remove(system_framework_multi_location_path.c_str())); } } // namespace art -- GitLab From 8865914de5d83bb4d8165d26c01202bc2c500763 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Tue, 15 May 2018 10:53:06 -0700 Subject: [PATCH 400/749] Make static libtistress version It turned out to be difficult to actually run the libtistress on actual apps due to the fact it linked shared to a lot of libraries. This adds libtistresss and libtistressds targets which build static versions of libtistress both without and with debug checks. This also adds a simple Agent_OnAttach entrypoint for the agent. No special processing is performed. The agent is setup as though it were running OnLoad. Test: m -j50 libtistressds adb push $OUT/data/nativetest64/art/arm64/libtistressds.so /data/local/tmp adb shell setenforce 0 adb shell am start-activity \ --attach-agent /data/local/tmp/libtistressds.so=jvmti-stress,trace \ com.antonioleiva.bandhookkotlin/.ui.screens.main.MainActivity Change-Id: I75d3a81011864c62cde785fd7351c59dbd269237 --- test/Android.bp | 36 ++++++++++++++++++++++++++++++++---- test/ti-stress/stress.cc | 5 ++++- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/test/Android.bp b/test/Android.bp index 76189f62a9..84f2e224d5 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -370,22 +370,27 @@ cc_library_static { } art_cc_defaults { - name: "libtistress-defaults", + name: "libtistress-srcs", defaults: ["libartagent-defaults"], srcs: [ "ti-stress/stress.cc", ], + header_libs: ["libopenjdkjvmti_headers"], +} + +art_cc_defaults { + name: "libtistress-defaults", + defaults: ["libtistress-srcs"], shared_libs: [ "libbase", "slicer", ], - header_libs: ["libopenjdkjvmti_headers"], } art_cc_test_library { name: "libtistress", defaults: ["libtistress-defaults"], - shared_libs: ["libart"], + shared_libs: ["libartbase"], } art_cc_test_library { @@ -394,7 +399,30 @@ art_cc_test_library { "art_debug_defaults", "libtistress-defaults", ], - shared_libs: ["libartd"], + shared_libs: ["libartbased"], +} + +art_cc_defaults { + name: "libtistress-static-defaults", + defaults: ["libtistress-srcs"], + static_libs: art_static_dependencies + [ + "slicer", + ], +} + +art_cc_test_library { + name: "libtistresss", + defaults: ["libtistress-static-defaults"], + static_libs: ["libartbase"], +} + +art_cc_test_library { + name: "libtistressds", + defaults: [ + "art_debug_defaults", + "libtistress-static-defaults" + ], + static_libs: ["libartbased"], } cc_defaults { diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc index bbe74656dd..0eba7426c0 100644 --- a/test/ti-stress/stress.cc +++ b/test/ti-stress/stress.cc @@ -25,7 +25,6 @@ #include #include "base/utils.h" -#include "exec_utils.h" #include "jvmti.h" #pragma clang diagnostic push @@ -920,4 +919,8 @@ extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, return 0; } +extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) { + return Agent_OnLoad(vm, options, reserved); +} + } // namespace art -- GitLab From cf43fb6a1e676cc6bbc04c6591640f18643b1839 Mon Sep 17 00:00:00 2001 From: Artem Serov Date: Thu, 15 Feb 2018 14:43:48 +0000 Subject: [PATCH 401/749] ART: Enable scalar loop peeling and unrolling. Turn on scalar loop peeling and unrolling by default. Test: 482-checker-loop-back-edge-use, 530-checker-peel-unroll Test: test-art-host, test-art-target, boot-to-gui Change-Id: Ibfe1b54f790a97b281e85396da2985e0f22c2834 --- compiler/optimizing/loop_analysis.cc | 68 +- compiler/optimizing/loop_analysis.h | 23 +- compiler/optimizing/loop_optimization.cc | 17 +- compiler/optimizing/loop_optimization.h | 4 +- .../src/Main.java | 67 +- test/530-checker-peel-unroll/expected.txt | 1 + test/530-checker-peel-unroll/info.txt | 1 + test/530-checker-peel-unroll/src/Main.java | 822 ++++++++++++++++++ 8 files changed, 933 insertions(+), 70 deletions(-) create mode 100644 test/530-checker-peel-unroll/expected.txt create mode 100644 test/530-checker-peel-unroll/info.txt create mode 100644 test/530-checker-peel-unroll/src/Main.java diff --git a/compiler/optimizing/loop_analysis.cc b/compiler/optimizing/loop_analysis.cc index a0760eff69..a2124455e2 100644 --- a/compiler/optimizing/loop_analysis.cc +++ b/compiler/optimizing/loop_analysis.cc @@ -35,6 +35,9 @@ void LoopAnalysis::CalculateLoopBasicProperties(HLoopInformation* loop_info, for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* instruction = it.Current(); + if (it.Current()->GetType() == DataType::Type::kInt64) { + analysis_results->has_long_type_instructions_ = true; + } if (MakesScalarPeelingUnrollingNonBeneficial(instruction)) { analysis_results->has_instructions_preventing_scalar_peeling_ = true; analysis_results->has_instructions_preventing_scalar_unrolling_ = true; @@ -61,34 +64,29 @@ bool LoopAnalysis::HasLoopAtLeastOneInvariantExit(HLoopInformation* loop_info) { return false; } -class Arm64LoopHelper : public ArchDefaultLoopHelper { +// Default implementation of loop helper; used for all targets unless a custom implementation +// is provided. Enables scalar loop peeling and unrolling with the most conservative heuristics. +class ArchDefaultLoopHelper : public ArchNoOptsLoopHelper { public: // Scalar loop unrolling parameters and heuristics. // // Maximum possible unrolling factor. - static constexpr uint32_t kArm64ScalarMaxUnrollFactor = 2; + static constexpr uint32_t kScalarMaxUnrollFactor = 2; // Loop's maximum instruction count. Loops with higher count will not be peeled/unrolled. - static constexpr uint32_t kArm64ScalarHeuristicMaxBodySizeInstr = 40; + static constexpr uint32_t kScalarHeuristicMaxBodySizeInstr = 17; // Loop's maximum basic block count. Loops with higher count will not be peeled/unrolled. - static constexpr uint32_t kArm64ScalarHeuristicMaxBodySizeBlocks = 8; + static constexpr uint32_t kScalarHeuristicMaxBodySizeBlocks = 6; - // SIMD loop unrolling parameters and heuristics. - // - // Maximum possible unrolling factor. - static constexpr uint32_t kArm64SimdMaxUnrollFactor = 8; - // Loop's maximum instruction count. Loops with higher count will not be unrolled. - static constexpr uint32_t kArm64SimdHeuristicMaxBodySizeInstr = 50; - - bool IsLoopTooBigForScalarPeelingUnrolling(LoopAnalysisInfo* loop_analysis_info) const OVERRIDE { - size_t instr_num = loop_analysis_info->GetNumberOfInstructions(); - size_t bb_num = loop_analysis_info->GetNumberOfBasicBlocks(); - return (instr_num >= kArm64ScalarHeuristicMaxBodySizeInstr || - bb_num >= kArm64ScalarHeuristicMaxBodySizeBlocks); + bool IsLoopNonBeneficialForScalarOpts(LoopAnalysisInfo* loop_analysis_info) const OVERRIDE { + return loop_analysis_info->HasLongTypeInstructions() || + IsLoopTooBig(loop_analysis_info, + kScalarHeuristicMaxBodySizeInstr, + kScalarHeuristicMaxBodySizeBlocks); } uint32_t GetScalarUnrollingFactor(HLoopInformation* loop_info ATTRIBUTE_UNUSED, uint64_t trip_count) const OVERRIDE { - uint32_t desired_unrolling_factor = kArm64ScalarMaxUnrollFactor; + uint32_t desired_unrolling_factor = kScalarMaxUnrollFactor; if (trip_count < desired_unrolling_factor || trip_count % desired_unrolling_factor != 0) { return kNoUnrollingFactor; } @@ -98,6 +96,38 @@ class Arm64LoopHelper : public ArchDefaultLoopHelper { bool IsLoopPeelingEnabled() const OVERRIDE { return true; } + protected: + bool IsLoopTooBig(LoopAnalysisInfo* loop_analysis_info, + size_t instr_threshold, + size_t bb_threshold) const { + size_t instr_num = loop_analysis_info->GetNumberOfInstructions(); + size_t bb_num = loop_analysis_info->GetNumberOfBasicBlocks(); + return (instr_num >= instr_threshold || bb_num >= bb_threshold); + } +}; + +// Custom implementation of loop helper for arm64 target. Enables heuristics for scalar loop +// peeling and unrolling and supports SIMD loop unrolling. +class Arm64LoopHelper : public ArchDefaultLoopHelper { + public: + // SIMD loop unrolling parameters and heuristics. + // + // Maximum possible unrolling factor. + static constexpr uint32_t kArm64SimdMaxUnrollFactor = 8; + // Loop's maximum instruction count. Loops with higher count will not be unrolled. + static constexpr uint32_t kArm64SimdHeuristicMaxBodySizeInstr = 50; + + // Loop's maximum instruction count. Loops with higher count will not be peeled/unrolled. + static constexpr uint32_t kArm64ScalarHeuristicMaxBodySizeInstr = 40; + // Loop's maximum basic block count. Loops with higher count will not be peeled/unrolled. + static constexpr uint32_t kArm64ScalarHeuristicMaxBodySizeBlocks = 8; + + bool IsLoopNonBeneficialForScalarOpts(LoopAnalysisInfo* loop_analysis_info) const OVERRIDE { + return IsLoopTooBig(loop_analysis_info, + kArm64ScalarHeuristicMaxBodySizeInstr, + kArm64ScalarHeuristicMaxBodySizeBlocks); + } + uint32_t GetSIMDUnrollingFactor(HBasicBlock* block, int64_t trip_count, uint32_t max_peel, @@ -126,8 +156,8 @@ class Arm64LoopHelper : public ArchDefaultLoopHelper { } }; -ArchDefaultLoopHelper* ArchDefaultLoopHelper::Create(InstructionSet isa, - ArenaAllocator* allocator) { +ArchNoOptsLoopHelper* ArchNoOptsLoopHelper::Create(InstructionSet isa, + ArenaAllocator* allocator) { switch (isa) { case InstructionSet::kArm64: { return new (allocator) Arm64LoopHelper; diff --git a/compiler/optimizing/loop_analysis.h b/compiler/optimizing/loop_analysis.h index ece9858136..c09d3ff00f 100644 --- a/compiler/optimizing/loop_analysis.h +++ b/compiler/optimizing/loop_analysis.h @@ -35,6 +35,7 @@ class LoopAnalysisInfo : public ValueObject { exits_num_(0), has_instructions_preventing_scalar_peeling_(false), has_instructions_preventing_scalar_unrolling_(false), + has_long_type_instructions_(false), loop_info_(loop_info) {} size_t GetNumberOfBasicBlocks() const { return bb_num_; } @@ -49,6 +50,10 @@ class LoopAnalysisInfo : public ValueObject { return has_instructions_preventing_scalar_unrolling_; } + bool HasLongTypeInstructions() const { + return has_long_type_instructions_; + } + const HLoopInformation* GetLoopInfo() const { return loop_info_; } private: @@ -62,6 +67,9 @@ class LoopAnalysisInfo : public ValueObject { bool has_instructions_preventing_scalar_peeling_; // Whether the loop has instructions which make scalar loop unrolling non-beneficial. bool has_instructions_preventing_scalar_unrolling_; + // Whether the loop has instructions of primitive long type; unrolling these loop will + // likely introduce spill/fills on 32-bit targets. + bool has_long_type_instructions_; // Corresponding HLoopInformation. const HLoopInformation* loop_info_; @@ -117,22 +125,21 @@ class LoopAnalysis : public ValueObject { // To support peeling/unrolling for a new architecture one needs to create new helper class, // inherit it from this and add implementation for the following methods. // -class ArchDefaultLoopHelper : public ArenaObject { +class ArchNoOptsLoopHelper : public ArenaObject { public: - virtual ~ArchDefaultLoopHelper() {} + virtual ~ArchNoOptsLoopHelper() {} // Creates an instance of specialised helper for the target or default helper if the target // doesn't support loop peeling and unrolling. - static ArchDefaultLoopHelper* Create(InstructionSet isa, ArenaAllocator* allocator); + static ArchNoOptsLoopHelper* Create(InstructionSet isa, ArenaAllocator* allocator); - // Returns whether the loop is too big for loop peeling/unrolling by checking its total number of - // basic blocks and instructions. + // Returns whether the loop is not beneficial for loop peeling/unrolling. // - // If the loop body has too many instructions then peeling/unrolling optimization will not bring - // any noticeable performance improvement however will increase the code size. + // For example, if the loop body has too many instructions then peeling/unrolling optimization + // will not bring any noticeable performance improvement however will increase the code size. // // Returns 'true' by default, should be overridden by particular target loop helper. - virtual bool IsLoopTooBigForScalarPeelingUnrolling( + virtual bool IsLoopNonBeneficialForScalarOpts( LoopAnalysisInfo* loop_analysis_info ATTRIBUTE_UNUSED) const { return true; } // Returns optimal scalar unrolling factor for the loop. diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 1ce3524bd6..eda6bd1e86 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -33,9 +33,6 @@ namespace art { // Enables vectorization (SIMDization) in the loop optimizer. static constexpr bool kEnableVectorization = true; -// Enables scalar loop unrolling in the loop optimizer. -static constexpr bool kEnableScalarPeelingUnrolling = false; - // // Static helpers. // @@ -457,7 +454,7 @@ HLoopOptimization::HLoopOptimization(HGraph* graph, vector_header_(nullptr), vector_body_(nullptr), vector_index_(nullptr), - arch_loop_helper_(ArchDefaultLoopHelper::Create(compiler_driver_ != nullptr + arch_loop_helper_(ArchNoOptsLoopHelper::Create(compiler_driver_ != nullptr ? compiler_driver_->GetInstructionSet() : InstructionSet::kNone, global_allocator_)) { @@ -761,7 +758,7 @@ bool HLoopOptimization::OptimizeInnerLoop(LoopNode* node) { bool HLoopOptimization::TryUnrollingForBranchPenaltyReduction(LoopNode* node) { // Don't run peeling/unrolling if compiler_driver_ is nullptr (i.e., running under tests) // as InstructionSet is needed. - if (!kEnableScalarPeelingUnrolling || compiler_driver_ == nullptr) { + if (compiler_driver_ == nullptr) { return false; } @@ -781,9 +778,9 @@ bool HLoopOptimization::TryUnrollingForBranchPenaltyReduction(LoopNode* node) { LoopAnalysis::CalculateLoopBasicProperties(loop_info, &loop_analysis_info); // Check "IsLoopClonable" last as it can be time-consuming. - if (arch_loop_helper_->IsLoopTooBigForScalarPeelingUnrolling(&loop_analysis_info) || + if (loop_analysis_info.HasInstructionsPreventingScalarUnrolling() || + arch_loop_helper_->IsLoopNonBeneficialForScalarOpts(&loop_analysis_info) || (loop_analysis_info.GetNumberOfExits() > 1) || - loop_analysis_info.HasInstructionsPreventingScalarUnrolling() || !PeelUnrollHelper::IsLoopClonable(loop_info)) { return false; } @@ -807,7 +804,7 @@ bool HLoopOptimization::TryUnrollingForBranchPenaltyReduction(LoopNode* node) { bool HLoopOptimization::TryPeelingForLoopInvariantExitsElimination(LoopNode* node) { // Don't run peeling/unrolling if compiler_driver_ is nullptr (i.e., running under tests) // as InstructionSet is needed. - if (!kEnableScalarPeelingUnrolling || compiler_driver_ == nullptr) { + if (compiler_driver_ == nullptr) { return false; } @@ -821,8 +818,8 @@ bool HLoopOptimization::TryPeelingForLoopInvariantExitsElimination(LoopNode* nod LoopAnalysis::CalculateLoopBasicProperties(loop_info, &loop_analysis_info); // Check "IsLoopClonable" last as it can be time-consuming. - if (arch_loop_helper_->IsLoopTooBigForScalarPeelingUnrolling(&loop_analysis_info) || - loop_analysis_info.HasInstructionsPreventingScalarPeeling() || + if (loop_analysis_info.HasInstructionsPreventingScalarPeeling() || + arch_loop_helper_->IsLoopNonBeneficialForScalarOpts(&loop_analysis_info) || !LoopAnalysis::HasLoopAtLeastOneInvariantExit(loop_info) || !PeelUnrollHelper::IsLoopClonable(loop_info)) { return false; diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h index 7807da15ed..191a93da26 100644 --- a/compiler/optimizing/loop_optimization.h +++ b/compiler/optimizing/loop_optimization.h @@ -28,7 +28,7 @@ namespace art { class CompilerDriver; -class ArchDefaultLoopHelper; +class ArchNoOptsLoopHelper; /** * Loop optimizations. Builds a loop hierarchy and applies optimizations to @@ -308,7 +308,7 @@ class HLoopOptimization : public HOptimization { HInstruction* vector_index_; // normalized index of the new loop // Helper for target-specific behaviour for loop optimizations. - ArchDefaultLoopHelper* arch_loop_helper_; + ArchNoOptsLoopHelper* arch_loop_helper_; friend class LoopOptimizationTest; diff --git a/test/482-checker-loop-back-edge-use/src/Main.java b/test/482-checker-loop-back-edge-use/src/Main.java index 47823409a3..8311d8cc4f 100644 --- a/test/482-checker-loop-back-edge-use/src/Main.java +++ b/test/482-checker-loop-back-edge-use/src/Main.java @@ -18,26 +18,28 @@ public class Main { /// CHECK-START: void Main.loop1(boolean) liveness (after) - /// CHECK: <> ParameterValue liveness:<> ranges:{[<>,<>)} uses:[<>,<>] - /// CHECK: If [<>] liveness:<> - /// CHECK: Goto liveness:<> - /// CHECK: Exit + /// CHECK-DAG: <> ParameterValue liveness:<> ranges:{[<>,<>)} uses:[<>] + /// CHECK-DAG: If [<>] liveness:<> loop:none + /// CHECK-DAG: Goto loop:B{{\d+}} + /// CHECK-DAG: Exit /// CHECK-EVAL: <> + 1 == <> - /// CHECK-EVAL: <> + 2 == <> + // + // Loop invariant exit check is hoisted from the loop by peeling. public static void loop1(boolean incoming) { while (incoming) {} } /// CHECK-START: void Main.loop2(boolean) liveness (after) - /// CHECK: <> ParameterValue liveness:<> ranges:{[<>,<>)} uses:[<>,<>,<>] - /// CHECK: If [<>] liveness:<> - /// CHECK: Goto liveness:<> - /// CHECK: Goto liveness:<> + /// CHECK-DAG: <> ParameterValue liveness:<> ranges:{[<>,<>)} uses:[<>,<>] + /// CHECK-DAG: If [<>] liveness:<> loop:<> + /// CHECK-DAG: Goto liveness:<> loop:<> + /// CHECK-DAG: Goto liveness:<> loop:<> /// CHECK-EVAL: <> + 1 == <> /// CHECK-EVAL: <> < <> - /// CHECK-EVAL: <> + 2 == <> - /// CHECK-EVAL: <> + 2 == <> + /// CHECK-EVAL: <> + 2 == <> + // + // Loop invariant exit check is hoisted from the loop by peeling. public static void loop2(boolean incoming) { // Add some code at entry to avoid having the entry block be a pre header. @@ -122,17 +124,18 @@ public class Main { } /// CHECK-START: void Main.loop7(boolean) liveness (after) - /// CHECK: <> ParameterValue liveness:<> ranges:{[<>,<>)} uses:[<>,<>,<>,<>] - /// CHECK: InvokeVirtual [{{l\d+}},<>] method_name:java.io.PrintStream.println liveness:<> - /// CHECK: If [<>] liveness:<> - /// CHECK: Goto liveness:<> - /// CHECK: Goto liveness:<> - /// CHECK: Exit + /// CHECK-DAG: <> ParameterValue liveness:<> ranges:{[<>,<>)} uses:[<>,<>,<>] + /// CHECK-DAG: InvokeVirtual [{{l\d+}},<>] method_name:java.io.PrintStream.println liveness:<> + /// CHECK-DAG: If [<>] liveness:<> loop:<> + /// CHECK-DAG: Goto liveness:<> loop:<> + /// CHECK-DAG: Goto liveness:<> loop:<> + /// CHECK-DAG: Exit /// CHECK-EVAL: <> == <> /// CHECK-EVAL: <> + 1 == <> /// CHECK-EVAL: <> < <> - /// CHECK-EVAL: <> + 2 == <> - /// CHECK-EVAL: <> + 2 == <> + /// CHECK-EVAL: <> + 2 == <> + // + // Loop invariant exit check is hoisted from the loop by peeling. public static void loop7(boolean incoming) { // 'incoming' must have a use at both back edges. @@ -144,15 +147,16 @@ public class Main { } /// CHECK-START: void Main.loop8() liveness (after) - /// CHECK: <> StaticFieldGet liveness:<> ranges:{[<>,<>)} uses:[<>,<>,<>] - /// CHECK: If [<>] liveness:<> - /// CHECK: Goto liveness:<> - /// CHECK: Goto liveness:<> - /// CHECK: Exit + /// CHECK-DAG: <> StaticFieldGet liveness:<> ranges:{[<>,<>)} uses:[<>,<>] + /// CHECK-DAG: If [<>] liveness:<> loop:<> + /// CHECK-DAG: Goto liveness:<> loop:<> + /// CHECK-DAG: Goto liveness:<> loop:<> + /// CHECK-DAG: Exit /// CHECK-EVAL: <> + 1 == <> /// CHECK-EVAL: <> < <> - /// CHECK-EVAL: <> + 2 == <> - /// CHECK-EVAL: <> + 2 == <> + /// CHECK-EVAL: <> + 2 == <> + // + // Loop invariant exit check is hoisted from the loop by peeling. public static void loop8() { // 'incoming' must have a use at both back edges. @@ -171,14 +175,15 @@ public class Main { } /// CHECK-START: void Main.loop9() liveness (after) - /// CHECK: <> StaticFieldGet liveness:<> ranges:{[<>,<>)} uses:[<>,<>] - /// CHECK: If [<>] liveness:<> - /// CHECK: Goto liveness:<> - /// CHECK-DAG: Goto liveness:<> + /// CHECK-DAG: <> StaticFieldGet liveness:<> ranges:{[<>,<>)} uses:[<>] + /// CHECK-DAG: If [<>] liveness:<> loop:<> + /// CHECK-DAG: Goto liveness:<> loop:<> + /// CHECK-DAG: Goto liveness:<> loop:<> /// CHECK-DAG: Exit /// CHECK-EVAL: <> + 1 == <> /// CHECK-EVAL: <> < <> - /// CHECK-EVAL: <> + 2 == <> + // + // Loop invariant exit check is hoisted from the loop by peeling. public static void loop9() { // Add some code at entry to avoid having the entry block be a pre header. diff --git a/test/530-checker-peel-unroll/expected.txt b/test/530-checker-peel-unroll/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/530-checker-peel-unroll/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/530-checker-peel-unroll/info.txt b/test/530-checker-peel-unroll/info.txt new file mode 100644 index 0000000000..0e10b36442 --- /dev/null +++ b/test/530-checker-peel-unroll/info.txt @@ -0,0 +1 @@ +Test on loop optimizations, peeling and unrolling. diff --git a/test/530-checker-peel-unroll/src/Main.java b/test/530-checker-peel-unroll/src/Main.java new file mode 100644 index 0000000000..2051b47afe --- /dev/null +++ b/test/530-checker-peel-unroll/src/Main.java @@ -0,0 +1,822 @@ +/* + * 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. + */ + +// +// Test loop optimizations, in particular scalar loop peeling and unrolling. +public class Main { + + static final int LENGTH = 4 * 1024; + int[] a = new int[LENGTH]; + int[] b = new int[LENGTH]; + + private static final int LENGTH_A = LENGTH; + private static final int LENGTH_B = 16; + private static final int RESULT_POS = 4; + + double[][] mA; + double[][] mB; + double[][] mC; + + public Main() { + mA = new double[LENGTH_A][]; + mB = new double[LENGTH_B][]; + mC = new double[LENGTH_B][]; + for (int i = 0; i < LENGTH_A; i++) { + mA[i] = new double[LENGTH_B]; + } + + for (int i = 0; i < LENGTH_B; i++) { + mB[i] = new double[LENGTH_A]; + mC[i] = new double[LENGTH_B]; + } + } + + private static final void initMatrix(double[][] m) { + for (int i = 0; i < m.length; i++) { + for (int j = 0; j < m[i].length; j++) { + m[i][j] = (double) (i * LENGTH / (j + 1)); + } + } + } + + private static final void initIntArray(int[] a) { + for (int i = 0; i < LENGTH; i++) { + a[i] = i % 4; + } + } + + /// CHECK-START: void Main.unrollingLoadStoreElimination(int[]) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + + /// CHECK-START: void Main.unrollingLoadStoreElimination(int[]) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + private static final void unrollingLoadStoreElimination(int[] a) { + for (int i = 0; i < LENGTH - 2; i++) { + a[i] += a[i + 1]; + } + } + + /// CHECK-START: void Main.unrollingWhile(int[]) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> IntConstant 128 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Rem [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Phi [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + + /// CHECK-START: void Main.unrollingWhile(int[]) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> IntConstant 128 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Rem [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Rem [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: Phi [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + private static final void unrollingWhile(int[] a) { + int i = 0; + int s = 128; + while (i++ < LENGTH - 2) { + if (i % 2 == 0) { + a[i] = s++; + } + } + } + + // Simple check that loop unrolling has happened. + // + /// CHECK-START: void Main.unrollingSwitch(int[]) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4096 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + + /// CHECK-START: void Main.unrollingSwitch(int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4096 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + private static final void unrollingSwitch(int[] a) { + for (int i = 0; i < LENGTH; i++) { + switch (i % 3) { + case 2: + a[i]++; + break; + default: + break; + } + } + } + + // Simple check that loop unrolling has happened. + // + /// CHECK-START: void Main.unrollingSwapElements(int[]) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + + /// CHECK-START: void Main.unrollingSwapElements(int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + private static final void unrollingSwapElements(int[] array) { + for (int i = 0; i < LENGTH - 2; i++) { + if (array[i] > array[i + 1]) { + int temp = array[i + 1]; + array[i + 1] = array[i]; + array[i] = temp; + } + } + } + + // Simple check that loop unrolling has happened. + // + /// CHECK-START: void Main.unrollingRInnerproduct(double[][], double[][], double[][], int, int) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 16 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + + /// CHECK-START: void Main.unrollingRInnerproduct(double[][], double[][], double[][], int, int) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 16 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<> outer_loop:none + /// CHECK-NOT: ArraySet loop:<> outer_loop:none + private static final void unrollingRInnerproduct(double[][] result, + double[][] a, + double[][] b, + int row, + int column) { + // computes the inner product of A[row,*] and B[*,column] + int i; + result[row][column] = 0.0f; + for (i = 0; i < LENGTH_B; i++) { + result[row][column] = result[row][column] + a[row][i] * b[i][column]; + } + } + + // nested loop + // [[[]]] + + /// CHECK-START: void Main.unrollingInTheNest(int[], int[], int) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 128 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:<> + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + /// CHECK-NOT: If + + /// CHECK-START: void Main.unrollingInTheNest(int[], int[], int) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 128 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + // + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + /// CHECK-NOT: If + private static final void unrollingInTheNest(int[] a, int[] b, int x) { + for (int k = 0; k < 16; k++) { + for (int j = 0; j < 16; j++) { + for (int i = 0; i < 128; i++) { + b[x]++; + a[i] = a[i] + 1; + } + } + } + } + + // nested loop: + // [ + // if [] else [] + // ] + + /// CHECK-START: void Main.unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (before) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 128 loop:none + /// CHECK-DAG: <> IntConstant 100 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + // + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + /// CHECK-NOT: If + + /// CHECK-START: void Main.unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 128 loop:none + /// CHECK-DAG: <> IntConstant 100 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:<> + // + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:<> + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + /// CHECK-NOT: If + private static final void unrollingTwoLoopsInTheNest(int[] a, int[] b, int x) { + for (int k = 0; k < 128; k++) { + if (x > 100) { + for (int j = 0; j < 128; j++) { + a[x]++; + } + } else { + for (int i = 0; i < 128; i++) { + b[x]++; + } + } + } + } + + /// CHECK-START: void Main.noUnrollingOddTripCount(int[]) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4095 loop:none + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: Phi + /// CHECK-NOT: If + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.noUnrollingOddTripCount(int[]) loop_optimization (after) + /// CHECK-DAG: Phi loop:<> outer_loop:none + /// CHECK-DAG: If loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + // + /// CHECK-NOT: Phi + /// CHECK-NOT: If + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static final void noUnrollingOddTripCount(int[] a) { + for (int i = 0; i < LENGTH - 1; i++) { + a[i] += a[i + 1]; + } + } + + /// CHECK-START: void Main.noUnrollingNotKnownTripCount(int[], int) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: Phi + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.noUnrollingNotKnownTripCount(int[], int) loop_optimization (after) + /// CHECK-DAG: Phi loop:<> outer_loop:none + /// CHECK-DAG: If loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + // + /// CHECK-NOT: Phi + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static final void noUnrollingNotKnownTripCount(int[] a, int n) { + for (int i = 0; i < n; i++) { + a[i] += a[i + 1]; + } + } + + /// CHECK-START: void Main.peelingSimple(int[], boolean) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4096 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.peelingSimple(int[], boolean) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4096 loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:none + /// CHECK-DAG: If [<>] loop:none + /// CHECK-DAG: If [<>] loop:none + /// CHECK-DAG: ArrayGet loop:none + /// CHECK-DAG: ArraySet loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$final (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4096 loop:none + /// CHECK-DAG: If [<>] loop:none + /// CHECK-DAG: ArrayGet loop:none + /// CHECK-DAG: ArraySet loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: GreaterThanOrEqual + /// CHECK-NOT: If + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static final void peelingSimple(int[] a, boolean f) { + for (int i = 0; i < LENGTH; i++) { + if (f) { + break; + } + a[i] += 1; + } + } + + // Often used idiom that, when not hoisted, prevents BCE and vectorization. + // + /// CHECK-START: void Main.peelingAddInts(int[]) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> NullConstant loop:none + /// CHECK-DAG: <> Equal [<>,<>] loop:none + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + + /// CHECK-START: void Main.peelingAddInts(int[]) dead_code_elimination$final (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> NullConstant loop:none + /// CHECK-DAG: <> Equal [<>,<>] loop:none + /// CHECK-DAG: If [<>] loop:none + /// CHECK-DAG: ArraySet loop:none + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + // + /// CHECK-NOT: If [<>] loop:<> outer_loop:none + private static final void peelingAddInts(int[] a) { + for (int i = 0; a != null && i < a.length; i++) { + a[i] += 1; + } + } + + /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4096 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) dead_code_elimination$final (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4096 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArrayGet loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + private static final void peelingBreakFromNest(int[] a, boolean f) { + outer: + for (int i = 1; i < 32; i++) { + for (int j = 0; j < LENGTH; j++) { + if (f) { + break outer; + } + a[j] += 1; + } + } + } + + /// CHECK-START: int Main.peelingHoistOneControl(int) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> NotEqual [<>,<>] loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: If + + /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$final (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> NotEqual [<>,<>] loop:none + /// CHECK-DAG: If [<>] loop:none + /// CHECK-DAG: SuspendCheck loop:<> outer_loop:none + /// CHECK-DAG: Goto loop:<> outer_loop:none + // + // Check that the loop has no instruction except SuspendCheck and Goto (indefinite loop). + /// CHECK-NOT: loop:<> outer_loop:none + /// CHECK-NOT: If + /// CHECK-NOT: Phi + /// CHECK-NOT: Add + private static final int peelingHoistOneControl(int x) { + int i = 0; + while (true) { + if (x == 0) + return 1; + i++; + } + } + + /// CHECK-START: int Main.peelingHoistOneControl(int, int) loop_optimization (before) + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: If loop:<> outer_loop:none + /// CHECK-DAG: If loop:<> outer_loop:none + // + /// CHECK-START: int Main.peelingHoistOneControl(int, int) dead_code_elimination$final (after) + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: If loop:<> outer_loop:none + /// CHECK-NOT: If loop:<> outer_loop:none + private static final int peelingHoistOneControl(int x, int y) { + while (true) { + if (x == 0) + return 1; + if (y == 0) // no longer invariant + return 2; + y--; + } + } + + /// CHECK-START: int Main.peelingHoistTwoControl(int, int, int) loop_optimization (before) + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: If loop:<> outer_loop:none + /// CHECK-DAG: If loop:<> outer_loop:none + /// CHECK-DAG: If loop:<> outer_loop:none + // + /// CHECK-START: int Main.peelingHoistTwoControl(int, int, int) dead_code_elimination$final (after) + /// CHECK-DAG: <> Phi loop:<> outer_loop:none + /// CHECK-DAG: If loop:<> outer_loop:none + /// CHECK-NOT: If loop:<> outer_loop:none + private static final int peelingHoistTwoControl(int x, int y, int z) { + while (true) { + if (x == 0) + return 1; + if (y == 0) + return 2; + if (z == 0) // no longer invariant + return 3; + z--; + } + } + + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public void verifyUnrolling() { + initIntArray(a); + initIntArray(b); + + initMatrix(mA); + initMatrix(mB); + initMatrix(mC); + + int expected = 174291419; + int found = 0; + + unrollingWhile(a); + unrollingLoadStoreElimination(a); + unrollingSwitch(a); + unrollingSwapElements(a); + unrollingRInnerproduct(mC, mA, mB, RESULT_POS, RESULT_POS); + unrollingInTheNest(a, b, RESULT_POS); + unrollingTwoLoopsInTheNest(a, b, RESULT_POS); + + noUnrollingOddTripCount(b); + noUnrollingNotKnownTripCount(b, 128); + + for (int i = 0; i < LENGTH; i++) { + found += a[i]; + found += b[i]; + } + found += (int)mC[RESULT_POS][RESULT_POS]; + + expectEquals(expected, found); + } + + public void verifyPeeling() { + expectEquals(1, peelingHoistOneControl(0)); // anything else loops + expectEquals(1, peelingHoistOneControl(0, 0)); + expectEquals(1, peelingHoistOneControl(0, 1)); + expectEquals(2, peelingHoistOneControl(1, 0)); + expectEquals(2, peelingHoistOneControl(1, 1)); + expectEquals(1, peelingHoistTwoControl(0, 0, 0)); + expectEquals(1, peelingHoistTwoControl(0, 0, 1)); + expectEquals(1, peelingHoistTwoControl(0, 1, 0)); + expectEquals(1, peelingHoistTwoControl(0, 1, 1)); + expectEquals(2, peelingHoistTwoControl(1, 0, 0)); + expectEquals(2, peelingHoistTwoControl(1, 0, 1)); + expectEquals(3, peelingHoistTwoControl(1, 1, 0)); + expectEquals(3, peelingHoistTwoControl(1, 1, 1)); + + initIntArray(a); + peelingSimple(a, false); + peelingSimple(a, true); + peelingAddInts(a); + peelingAddInts(null); // okay + peelingBreakFromNest(a, false); + peelingBreakFromNest(a, true); + + int expected = 141312; + int found = 0; + for (int i = 0; i < a.length; i++) { + found += a[i]; + } + + expectEquals(expected, found); + } + + public static void main(String[] args) { + Main obj = new Main(); + + obj.verifyUnrolling(); + obj.verifyPeeling(); + + System.out.println("passed"); + } +} -- GitLab From b765a3f7f7b7c7efaab5fb331796b53439dbe04c Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Thu, 10 May 2018 14:47:48 -0700 Subject: [PATCH 402/749] Refactoring LSE/LSA: introduce heap location type Rationale: This refactoring introduces data types to heap locations. This will allow better type disambiguation in the future. As a first showcase, it already removes rather error-prone "exceptional" code in LSE dealing with array types on null values. Furthermore, many LSA specific details started to "leak" into clients, which is also error-prone. This refactoring moves such details back into just LSA, where it belongs. Test: test-art-host,target Bug: b/77906240 Change-Id: Id327bbe86dde451a942c9c5f9e83054c36241882 --- compiler/optimizing/data_type.h | 15 +++ compiler/optimizing/load_store_analysis.h | 59 +++++++-- .../optimizing/load_store_analysis_test.cc | 119 ++++++++++-------- compiler/optimizing/load_store_elimination.cc | 119 ++++-------------- compiler/optimizing/scheduler.cc | 20 +-- compiler/optimizing/scheduler.h | 8 +- compiler/optimizing/scheduler_test.cc | 24 ++-- test/586-checker-null-array-get/src/Main.java | 5 +- 8 files changed, 176 insertions(+), 193 deletions(-) diff --git a/compiler/optimizing/data_type.h b/compiler/optimizing/data_type.h index be26e67af3..5ac6e46003 100644 --- a/compiler/optimizing/data_type.h +++ b/compiler/optimizing/data_type.h @@ -216,6 +216,21 @@ class DataType { Size(result_type) > Size(input_type); } + static Type ToSigned(Type type) { + switch (type) { + case Type::kUint8: + return Type::kInt8; + case Type::kUint16: + return Type::kInt16; + case Type::kUint32: + return Type::kInt32; + case Type::kUint64: + return Type::kInt64; + default: + return type; + } + } + static const char* PrettyDescriptor(Type type); private: diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h index f84846d1b0..769a3f1b59 100644 --- a/compiler/optimizing/load_store_analysis.h +++ b/compiler/optimizing/load_store_analysis.h @@ -94,11 +94,13 @@ class HeapLocation : public ArenaObject { static constexpr int16_t kDeclaringClassDefIndexForArrays = -1; HeapLocation(ReferenceInfo* ref_info, + DataType::Type type, size_t offset, HInstruction* index, size_t vector_length, int16_t declaring_class_def_index) : ref_info_(ref_info), + type_(DataType::ToSigned(type)), offset_(offset), index_(index), vector_length_(vector_length), @@ -116,6 +118,7 @@ class HeapLocation : public ArenaObject { } ReferenceInfo* GetReferenceInfo() const { return ref_info_; } + DataType::Type GetType() const { return type_; } size_t GetOffset() const { return offset_; } HInstruction* GetIndex() const { return index_; } size_t GetVectorLength() const { return vector_length_; } @@ -149,6 +152,10 @@ class HeapLocation : public ArenaObject { private: // Reference for instance/static field, array element or vector data. ReferenceInfo* const ref_info_; + // Type of data residing at HeapLocation (always signed for integral + // data since e.g. a[i] and a[i] & 0xff are represented by differently + // signed types; char vs short are disambiguated through the reference). + const DataType::Type type_; // Offset of static/instance field. // Invalid when this HeapLocation is not field. const size_t offset_; @@ -237,19 +244,31 @@ class HeapLocationCollector : public HGraphVisitor { DCHECK(object != nullptr); DCHECK(field != nullptr); return FindHeapLocationIndex(FindReferenceInfoOf(HuntForOriginalReference(object)), + field->GetFieldType(), field->GetFieldOffset().SizeValue(), nullptr, HeapLocation::kScalar, field->GetDeclaringClassDefIndex()); } - size_t GetArrayHeapLocation(HInstruction* array, - HInstruction* index, - size_t vector_length = HeapLocation::kScalar) const { - DCHECK(array != nullptr); - DCHECK(index != nullptr); - DCHECK_GE(vector_length, HeapLocation::kScalar); + size_t GetArrayHeapLocation(HInstruction* instruction) const { + DCHECK(instruction != nullptr); + HInstruction* array = instruction->InputAt(0); + HInstruction* index = instruction->InputAt(1); + DataType::Type type = instruction->GetType(); + size_t vector_length = HeapLocation::kScalar; + if (instruction->IsArraySet()) { + type = instruction->AsArraySet()->GetComponentType(); + } else if (instruction->IsVecStore() || + instruction->IsVecLoad()) { + HVecOperation* vec_op = instruction->AsVecOperation(); + type = vec_op->GetPackedType(); + vector_length = vec_op->GetVectorLength(); + } else { + DCHECK(instruction->IsArrayGet()); + } return FindHeapLocationIndex(FindReferenceInfoOf(HuntForOriginalReference(array)), + type, HeapLocation::kInvalidFieldOffset, index, vector_length, @@ -279,13 +298,16 @@ class HeapLocationCollector : public HGraphVisitor { // In later analysis, ComputeMayAlias() and MayAlias() compute and tell whether // these indexes alias. size_t FindHeapLocationIndex(ReferenceInfo* ref_info, + DataType::Type type, size_t offset, HInstruction* index, size_t vector_length, int16_t declaring_class_def_index) const { + DataType::Type lookup_type = DataType::ToSigned(type); for (size_t i = 0; i < heap_locations_.size(); i++) { HeapLocation* loc = heap_locations_[i]; if (loc->GetReferenceInfo() == ref_info && + loc->GetType() == lookup_type && loc->GetOffset() == offset && loc->GetIndex() == index && loc->GetVectorLength() == vector_length && @@ -425,6 +447,7 @@ class HeapLocationCollector : public HGraphVisitor { } HeapLocation* GetOrCreateHeapLocation(HInstruction* ref, + DataType::Type type, size_t offset, HInstruction* index, size_t vector_length, @@ -432,10 +455,10 @@ class HeapLocationCollector : public HGraphVisitor { HInstruction* original_ref = HuntForOriginalReference(ref); ReferenceInfo* ref_info = GetOrCreateReferenceInfo(original_ref); size_t heap_location_idx = FindHeapLocationIndex( - ref_info, offset, index, vector_length, declaring_class_def_index); + ref_info, type, offset, index, vector_length, declaring_class_def_index); if (heap_location_idx == kHeapLocationNotFound) { HeapLocation* heap_loc = new (GetGraph()->GetAllocator()) - HeapLocation(ref_info, offset, index, vector_length, declaring_class_def_index); + HeapLocation(ref_info, type, offset, index, vector_length, declaring_class_def_index); heap_locations_.push_back(heap_loc); return heap_loc; } @@ -446,17 +469,23 @@ class HeapLocationCollector : public HGraphVisitor { if (field_info.IsVolatile()) { has_volatile_ = true; } + DataType::Type type = field_info.GetFieldType(); const uint16_t declaring_class_def_index = field_info.GetDeclaringClassDefIndex(); const size_t offset = field_info.GetFieldOffset().SizeValue(); return GetOrCreateHeapLocation(ref, + type, offset, nullptr, HeapLocation::kScalar, declaring_class_def_index); } - void VisitArrayAccess(HInstruction* array, HInstruction* index, size_t vector_length) { + void VisitArrayAccess(HInstruction* array, + HInstruction* index, + DataType::Type type, + size_t vector_length) { GetOrCreateHeapLocation(array, + type, HeapLocation::kInvalidFieldOffset, index, vector_length, @@ -510,28 +539,32 @@ class HeapLocationCollector : public HGraphVisitor { void VisitArrayGet(HArrayGet* instruction) OVERRIDE { HInstruction* array = instruction->InputAt(0); HInstruction* index = instruction->InputAt(1); - VisitArrayAccess(array, index, HeapLocation::kScalar); + DataType::Type type = instruction->GetType(); + VisitArrayAccess(array, index, type, HeapLocation::kScalar); CreateReferenceInfoForReferenceType(instruction); } void VisitArraySet(HArraySet* instruction) OVERRIDE { HInstruction* array = instruction->InputAt(0); HInstruction* index = instruction->InputAt(1); - VisitArrayAccess(array, index, HeapLocation::kScalar); + DataType::Type type = instruction->GetComponentType(); + VisitArrayAccess(array, index, type, HeapLocation::kScalar); has_heap_stores_ = true; } void VisitVecLoad(HVecLoad* instruction) OVERRIDE { HInstruction* array = instruction->InputAt(0); HInstruction* index = instruction->InputAt(1); - VisitArrayAccess(array, index, instruction->GetVectorLength()); + DataType::Type type = instruction->GetPackedType(); + VisitArrayAccess(array, index, type, instruction->GetVectorLength()); CreateReferenceInfoForReferenceType(instruction); } void VisitVecStore(HVecStore* instruction) OVERRIDE { HInstruction* array = instruction->InputAt(0); HInstruction* index = instruction->InputAt(1); - VisitArrayAccess(array, index, instruction->GetVectorLength()); + DataType::Type type = instruction->GetPackedType(); + VisitArrayAccess(array, index, type, instruction->GetVectorLength()); has_heap_stores_ = true; } diff --git a/compiler/optimizing/load_store_analysis_test.cc b/compiler/optimizing/load_store_analysis_test.cc index 56361a8c90..bfe7a4f72f 100644 --- a/compiler/optimizing/load_store_analysis_test.cc +++ b/compiler/optimizing/load_store_analysis_test.cc @@ -78,12 +78,16 @@ TEST_F(LoadStoreAnalysisTest, ArrayHeapLocations) { // Test queries on HeapLocationCollector's ref info and index records. ReferenceInfo* ref = heap_location_collector.FindReferenceInfoOf(array); + DataType::Type type = DataType::Type::kInt32; size_t field = HeapLocation::kInvalidFieldOffset; size_t vec = HeapLocation::kScalar; size_t class_def = HeapLocation::kDeclaringClassDefIndexForArrays; - size_t loc1 = heap_location_collector.FindHeapLocationIndex(ref, field, c1, vec, class_def); - size_t loc2 = heap_location_collector.FindHeapLocationIndex(ref, field, c2, vec, class_def); - size_t loc3 = heap_location_collector.FindHeapLocationIndex(ref, field, index, vec, class_def); + size_t loc1 = heap_location_collector.FindHeapLocationIndex( + ref, type, field, c1, vec, class_def); + size_t loc2 = heap_location_collector.FindHeapLocationIndex( + ref, type, field, c2, vec, class_def); + size_t loc3 = heap_location_collector.FindHeapLocationIndex( + ref, type, field, index, vec, class_def); // must find this reference info for array in HeapLocationCollector. ASSERT_TRUE(ref != nullptr); // must find these heap locations; @@ -246,28 +250,28 @@ TEST_F(LoadStoreAnalysisTest, ArrayIndexAliasingTest) { size_t loc2 = HeapLocationCollector::kHeapLocationNotFound; // Test alias: array[0] and array[1] - loc1 = heap_location_collector.GetArrayHeapLocation(array, c0); - loc2 = heap_location_collector.GetArrayHeapLocation(array, c1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set1); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set2); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+0] and array[i-0] - loc1 = heap_location_collector.GetArrayHeapLocation(array, add0); - loc2 = heap_location_collector.GetArrayHeapLocation(array, sub0); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set3); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set5); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+1] and array[i-1] - loc1 = heap_location_collector.GetArrayHeapLocation(array, add1); - loc2 = heap_location_collector.GetArrayHeapLocation(array, sub1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set4); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set6); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+1] and array[1-i] - loc1 = heap_location_collector.GetArrayHeapLocation(array, add1); - loc2 = heap_location_collector.GetArrayHeapLocation(array, rev_sub1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set4); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set7); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+1] and array[i-(-1)] - loc1 = heap_location_collector.GetArrayHeapLocation(array, add1); - loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_neg1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set4); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set8); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); } @@ -409,70 +413,75 @@ TEST_F(LoadStoreAnalysisTest, ArrayAliasingTest) { size_t loc1, loc2; // Test alias: array[0] and array[0,1,2,3] - loc1 = heap_location_collector.GetArrayHeapLocation(array, c0); - loc2 = heap_location_collector.GetArrayHeapLocation(array, c0, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_0); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_0); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); + // Test alias: array[0] and array[1,2,3,4] + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_0); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_1); + ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); + // Test alias: array[0] and array[8,9,10,11] - loc1 = heap_location_collector.GetArrayHeapLocation(array, c0); - loc2 = heap_location_collector.GetArrayHeapLocation(array, c8, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_0); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_8); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[1] and array[8,9,10,11] - loc1 = heap_location_collector.GetArrayHeapLocation(array, c1); - loc2 = heap_location_collector.GetArrayHeapLocation(array, c8, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_1); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_8); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[1] and array[0,1,2,3] - loc1 = heap_location_collector.GetArrayHeapLocation(array, c1); - loc2 = heap_location_collector.GetArrayHeapLocation(array, c0, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_1); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_0); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[0,1,2,3] and array[8,9,10,11] - loc1 = heap_location_collector.GetArrayHeapLocation(array, c0, 4); - loc2 = heap_location_collector.GetArrayHeapLocation(array, c8, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(vstore_0); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_8); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[0,1,2,3] and array[1,2,3,4] - loc1 = heap_location_collector.GetArrayHeapLocation(array, c1, 4); - loc2 = heap_location_collector.GetArrayHeapLocation(array, c0, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(vstore_0); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_1); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[0] and array[i,i+1,i+2,i+3] - loc1 = heap_location_collector.GetArrayHeapLocation(array, c0); - loc2 = heap_location_collector.GetArrayHeapLocation(array, index, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_0); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_i); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i] and array[0,1,2,3] - loc1 = heap_location_collector.GetArrayHeapLocation(array, index); - loc2 = heap_location_collector.GetArrayHeapLocation(array, c0, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_0); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i] and array[i,i+1,i+2,i+3] - loc1 = heap_location_collector.GetArrayHeapLocation(array, index); - loc2 = heap_location_collector.GetArrayHeapLocation(array, index, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_i); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i] and array[i+8,i+9,i+10,i+11] - loc1 = heap_location_collector.GetArrayHeapLocation(array, index); - loc2 = heap_location_collector.GetArrayHeapLocation(array, i_add8, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_i_add8); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+6,i+7,i+8,i+9] and array[i+8,i+9,i+10,i+11] // Test partial overlap. - loc1 = heap_location_collector.GetArrayHeapLocation(array, i_add6, 4); - loc2 = heap_location_collector.GetArrayHeapLocation(array, i_add8, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(vstore_i_add6); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_i_add8); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+6,i+7] and array[i,i+1,i+2,i+3] // Test different vector lengths. - loc1 = heap_location_collector.GetArrayHeapLocation(array, i_add6, 2); - loc2 = heap_location_collector.GetArrayHeapLocation(array, index, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(vstore_i_add6_vlen2); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_i); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+6,i+7] and array[i+8,i+9,i+10,i+11] - loc1 = heap_location_collector.GetArrayHeapLocation(array, i_add6, 2); - loc2 = heap_location_collector.GetArrayHeapLocation(array, i_add8, 4); + loc1 = heap_location_collector.GetArrayHeapLocation(vstore_i_add6_vlen2); + loc2 = heap_location_collector.GetArrayHeapLocation(vstore_i_add8); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); } @@ -563,33 +572,33 @@ TEST_F(LoadStoreAnalysisTest, ArrayIndexCalculationOverflowTest) { size_t loc2 = HeapLocationCollector::kHeapLocationNotFound; // Test alias: array[i+0x80000000] and array[i-0x80000000] - loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0x80000000); - loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000000); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_1); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_2); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+0x10] and array[i-0xFFFFFFF0] - loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0x10); - loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0xFFFFFFF0); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_3); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_4); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+0x7FFFFFFF] and array[i-0x80000001] - loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0x7FFFFFFF); - loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000001); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_5); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_6); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+0] and array[i-0] - loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0); - loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_7); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_8); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Should not alias: - loc1 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000000); - loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000001); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_2); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_6); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Should not alias: - loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0); - loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000000); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_7); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_2); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); } @@ -647,10 +656,10 @@ TEST_F(LoadStoreAnalysisTest, TestHuntOriginalRef) { // times the original reference has been transformed by BoundType, // NullCheck, IntermediateAddress, etc. ASSERT_EQ(heap_location_collector.GetNumberOfHeapLocations(), 1U); - size_t loc1 = heap_location_collector.GetArrayHeapLocation(array, c1); - size_t loc2 = heap_location_collector.GetArrayHeapLocation(bound_type, c1); - size_t loc3 = heap_location_collector.GetArrayHeapLocation(null_check, c1); - size_t loc4 = heap_location_collector.GetArrayHeapLocation(inter_addr, c1); + size_t loc1 = heap_location_collector.GetArrayHeapLocation(array_get1); + size_t loc2 = heap_location_collector.GetArrayHeapLocation(array_get2); + size_t loc3 = heap_location_collector.GetArrayHeapLocation(array_get3); + size_t loc4 = heap_location_collector.GetArrayHeapLocation(array_get4); ASSERT_TRUE(loc1 != HeapLocationCollector::kHeapLocationNotFound); ASSERT_EQ(loc1, loc2); ASSERT_EQ(loc1, loc3); diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index d598ff592d..35e64f75b9 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -160,7 +160,7 @@ class LSEVisitor : public HGraphDelegateVisitor { // Scan the list of removed loads to see if we can reuse `type_conversion`, if // the other removed load has the same substitute and type and is dominated - // by `type_conversioni`. + // by `type_conversion`. void TryToReuseTypeConversion(HInstruction* type_conversion, size_t index) { size_t size = removed_loads_.size(); HInstruction* load = removed_loads_[index]; @@ -542,16 +542,7 @@ class LSEVisitor : public HGraphDelegateVisitor { } } - void VisitGetLocation(HInstruction* instruction, - HInstruction* ref, - size_t offset, - HInstruction* index, - size_t vector_length, - int16_t declaring_class_def_index) { - HInstruction* original_ref = heap_location_collector_.HuntForOriginalReference(ref); - ReferenceInfo* ref_info = heap_location_collector_.FindReferenceInfoOf(original_ref); - size_t idx = heap_location_collector_.FindHeapLocationIndex( - ref_info, offset, index, vector_length, declaring_class_def_index); + void VisitGetLocation(HInstruction* instruction, size_t idx) { DCHECK_NE(idx, HeapLocationCollector::kHeapLocationNotFound); ScopedArenaVector& heap_values = heap_values_for_[instruction->GetBlock()->GetBlockId()]; @@ -569,23 +560,7 @@ class LSEVisitor : public HGraphDelegateVisitor { heap_values[idx] = instruction; KeepStoresIfAliasedToLocation(heap_values, idx); } else { - if (DataType::Kind(heap_value->GetType()) != DataType::Kind(instruction->GetType())) { - // The only situation where the same heap location has different type is when - // we do an array get on an instruction that originates from the null constant - // (the null could be behind a field access, an array access, a null check or - // a bound type). - // In order to stay properly typed on primitive types, we do not eliminate - // the array gets. - if (kIsDebugBuild) { - DCHECK(heap_value->IsArrayGet()) << heap_value->DebugName(); - DCHECK(instruction->IsArrayGet()) << instruction->DebugName(); - } - // Load isn't eliminated. Put the load as the value into the HeapLocation. - // This acts like GVN but with better aliasing analysis. - heap_values[idx] = instruction; - KeepStoresIfAliasedToLocation(heap_values, idx); - return; - } + // Load is eliminated. AddRemovedLoad(instruction, heap_value); TryRemovingNullCheck(instruction); } @@ -610,21 +585,11 @@ class LSEVisitor : public HGraphDelegateVisitor { return false; } - void VisitSetLocation(HInstruction* instruction, - HInstruction* ref, - size_t offset, - HInstruction* index, - size_t vector_length, - int16_t declaring_class_def_index, - HInstruction* value) { + void VisitSetLocation(HInstruction* instruction, size_t idx, HInstruction* value) { + DCHECK_NE(idx, HeapLocationCollector::kHeapLocationNotFound); DCHECK(!IsStore(value)) << value->DebugName(); // value may already have a substitute. value = FindSubstitute(value); - HInstruction* original_ref = heap_location_collector_.HuntForOriginalReference(ref); - ReferenceInfo* ref_info = heap_location_collector_.FindReferenceInfoOf(original_ref); - size_t idx = heap_location_collector_.FindHeapLocationIndex( - ref_info, offset, index, vector_length, declaring_class_def_index); - DCHECK_NE(idx, HeapLocationCollector::kHeapLocationNotFound); ScopedArenaVector& heap_values = heap_values_for_[instruction->GetBlock()->GetBlockId()]; HInstruction* heap_value = heap_values[idx]; @@ -644,7 +609,8 @@ class LSEVisitor : public HGraphDelegateVisitor { } else if (!loop_info->IsIrreducible()) { // instruction is a store in the loop so the loop must do write. DCHECK(side_effects_.GetLoopEffects(loop_info->GetHeader()).DoesAnyWrite()); - if (ref_info->IsSingleton() && !loop_info->IsDefinedOutOfTheLoop(original_ref)) { + ReferenceInfo* ref_info = heap_location_collector_.GetHeapLocation(idx)->GetReferenceInfo(); + if (ref_info->IsSingleton() && !loop_info->IsDefinedOutOfTheLoop(ref_info->GetReference())) { // original_ref is created inside the loop. Value stored to it isn't needed at // the loop header. This is true for outer loops also. possibly_redundant = true; @@ -686,79 +652,39 @@ class LSEVisitor : public HGraphDelegateVisitor { } void VisitInstanceFieldGet(HInstanceFieldGet* instruction) OVERRIDE { - HInstruction* obj = instruction->InputAt(0); - size_t offset = instruction->GetFieldInfo().GetFieldOffset().SizeValue(); - int16_t declaring_class_def_index = instruction->GetFieldInfo().GetDeclaringClassDefIndex(); - VisitGetLocation(instruction, - obj, - offset, - nullptr, - HeapLocation::kScalar, - declaring_class_def_index); + HInstruction* object = instruction->InputAt(0); + const FieldInfo& field = instruction->GetFieldInfo(); + VisitGetLocation(instruction, heap_location_collector_.GetFieldHeapLocation(object, &field)); } void VisitInstanceFieldSet(HInstanceFieldSet* instruction) OVERRIDE { - HInstruction* obj = instruction->InputAt(0); - size_t offset = instruction->GetFieldInfo().GetFieldOffset().SizeValue(); - int16_t declaring_class_def_index = instruction->GetFieldInfo().GetDeclaringClassDefIndex(); + HInstruction* object = instruction->InputAt(0); + const FieldInfo& field = instruction->GetFieldInfo(); HInstruction* value = instruction->InputAt(1); - VisitSetLocation(instruction, - obj, - offset, - nullptr, - HeapLocation::kScalar, - declaring_class_def_index, - value); + size_t idx = heap_location_collector_.GetFieldHeapLocation(object, &field); + VisitSetLocation(instruction, idx, value); } void VisitStaticFieldGet(HStaticFieldGet* instruction) OVERRIDE { HInstruction* cls = instruction->InputAt(0); - size_t offset = instruction->GetFieldInfo().GetFieldOffset().SizeValue(); - int16_t declaring_class_def_index = instruction->GetFieldInfo().GetDeclaringClassDefIndex(); - VisitGetLocation(instruction, - cls, - offset, - nullptr, - HeapLocation::kScalar, - declaring_class_def_index); + const FieldInfo& field = instruction->GetFieldInfo(); + VisitGetLocation(instruction, heap_location_collector_.GetFieldHeapLocation(cls, &field)); } void VisitStaticFieldSet(HStaticFieldSet* instruction) OVERRIDE { HInstruction* cls = instruction->InputAt(0); - size_t offset = instruction->GetFieldInfo().GetFieldOffset().SizeValue(); - int16_t declaring_class_def_index = instruction->GetFieldInfo().GetDeclaringClassDefIndex(); - HInstruction* value = instruction->InputAt(1); - VisitSetLocation(instruction, - cls, - offset, - nullptr, - HeapLocation::kScalar, - declaring_class_def_index, - value); + const FieldInfo& field = instruction->GetFieldInfo(); + size_t idx = heap_location_collector_.GetFieldHeapLocation(cls, &field); + VisitSetLocation(instruction, idx, instruction->InputAt(1)); } void VisitArrayGet(HArrayGet* instruction) OVERRIDE { - HInstruction* array = instruction->InputAt(0); - HInstruction* index = instruction->InputAt(1); - VisitGetLocation(instruction, - array, - HeapLocation::kInvalidFieldOffset, - index, - HeapLocation::kScalar, - HeapLocation::kDeclaringClassDefIndexForArrays); + VisitGetLocation(instruction, heap_location_collector_.GetArrayHeapLocation(instruction)); } void VisitArraySet(HArraySet* instruction) OVERRIDE { - HInstruction* array = instruction->InputAt(0); - HInstruction* index = instruction->InputAt(1); - HInstruction* value = instruction->InputAt(2); - VisitSetLocation(instruction, - array, - HeapLocation::kInvalidFieldOffset, - index, - HeapLocation::kScalar, - HeapLocation::kDeclaringClassDefIndexForArrays, - value); + size_t idx = heap_location_collector_.GetArrayHeapLocation(instruction); + VisitSetLocation(instruction, idx, instruction->InputAt(2)); } void VisitDeoptimize(HDeoptimize* instruction) { @@ -971,6 +897,7 @@ bool LoadStoreElimination::Run() { lse_visitor.VisitBasicBlock(block); } lse_visitor.RemoveInstructions(); + return true; } diff --git a/compiler/optimizing/scheduler.cc b/compiler/optimizing/scheduler.cc index e014efaf5c..588ea03d69 100644 --- a/compiler/optimizing/scheduler.cc +++ b/compiler/optimizing/scheduler.cc @@ -70,19 +70,19 @@ static bool MayHaveReorderingDependency(SideEffects node, SideEffects other) { return false; } -size_t SchedulingGraph::ArrayAccessHeapLocation(HInstruction* array, HInstruction* index) const { +size_t SchedulingGraph::ArrayAccessHeapLocation(HInstruction* instruction) const { DCHECK(heap_location_collector_ != nullptr); - size_t heap_loc = heap_location_collector_->GetArrayHeapLocation(array, index); + size_t heap_loc = heap_location_collector_->GetArrayHeapLocation(instruction); // This array access should be analyzed and added to HeapLocationCollector before. DCHECK(heap_loc != HeapLocationCollector::kHeapLocationNotFound); return heap_loc; } -bool SchedulingGraph::ArrayAccessMayAlias(const HInstruction* node, - const HInstruction* other) const { +bool SchedulingGraph::ArrayAccessMayAlias(HInstruction* node, + HInstruction* other) const { DCHECK(heap_location_collector_ != nullptr); - size_t node_heap_loc = ArrayAccessHeapLocation(node->InputAt(0), node->InputAt(1)); - size_t other_heap_loc = ArrayAccessHeapLocation(other->InputAt(0), other->InputAt(1)); + size_t node_heap_loc = ArrayAccessHeapLocation(node); + size_t other_heap_loc = ArrayAccessHeapLocation(other); // For example: arr[0] and arr[0] if (node_heap_loc == other_heap_loc) { @@ -194,8 +194,8 @@ bool SchedulingGraph::FieldAccessMayAlias(const HInstruction* node, return true; } -bool SchedulingGraph::HasMemoryDependency(const HInstruction* node, - const HInstruction* other) const { +bool SchedulingGraph::HasMemoryDependency(HInstruction* node, + HInstruction* other) const { if (!MayHaveReorderingDependency(node->GetSideEffects(), other->GetSideEffects())) { return false; } @@ -264,8 +264,8 @@ bool SchedulingGraph::HasExceptionDependency(const HInstruction* node, // Check whether `node` depends on `other`, taking into account `SideEffect` // information and `CanThrow` information. -bool SchedulingGraph::HasSideEffectDependency(const HInstruction* node, - const HInstruction* other) const { +bool SchedulingGraph::HasSideEffectDependency(HInstruction* node, + HInstruction* other) const { if (HasMemoryDependency(node, other)) { return true; } diff --git a/compiler/optimizing/scheduler.h b/compiler/optimizing/scheduler.h index 51cd20aea9..8e98f192d8 100644 --- a/compiler/optimizing/scheduler.h +++ b/compiler/optimizing/scheduler.h @@ -310,12 +310,12 @@ class SchedulingGraph : public ValueObject { void AddOtherDependency(SchedulingNode* node, SchedulingNode* dependency) { AddDependency(node, dependency, /*is_data_dependency*/false); } - bool HasMemoryDependency(const HInstruction* node, const HInstruction* other) const; + bool HasMemoryDependency(HInstruction* node, HInstruction* other) const; bool HasExceptionDependency(const HInstruction* node, const HInstruction* other) const; - bool HasSideEffectDependency(const HInstruction* node, const HInstruction* other) const; - bool ArrayAccessMayAlias(const HInstruction* node, const HInstruction* other) const; + bool HasSideEffectDependency(HInstruction* node, HInstruction* other) const; + bool ArrayAccessMayAlias(HInstruction* node, HInstruction* other) const; bool FieldAccessMayAlias(const HInstruction* node, const HInstruction* other) const; - size_t ArrayAccessHeapLocation(HInstruction* array, HInstruction* index) const; + size_t ArrayAccessHeapLocation(HInstruction* instruction) const; size_t FieldAccessHeapLocation(HInstruction* obj, const FieldInfo* field) const; // Add dependencies nodes for the given `HInstruction`: inputs, environments, and side-effects. diff --git a/compiler/optimizing/scheduler_test.cc b/compiler/optimizing/scheduler_test.cc index fb15fc8975..d4cae72c7e 100644 --- a/compiler/optimizing/scheduler_test.cc +++ b/compiler/optimizing/scheduler_test.cc @@ -296,38 +296,38 @@ class SchedulerTest : public OptimizingUnitTest { size_t loc2 = HeapLocationCollector::kHeapLocationNotFound; // Test side effect dependency: array[0] and array[1] - loc1 = heap_location_collector.GetArrayHeapLocation(arr, c0); - loc2 = heap_location_collector.GetArrayHeapLocation(arr, c1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_0); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_1); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_1, arr_set_0)); // Test side effect dependency based on LSA analysis: array[i] and array[j] - loc1 = heap_location_collector.GetArrayHeapLocation(arr, i); - loc2 = heap_location_collector.GetArrayHeapLocation(arr, j); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_j); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, arr_set_i)); // Test side effect dependency based on LSA analysis: array[i] and array[i+0] - loc1 = heap_location_collector.GetArrayHeapLocation(arr, i); - loc2 = heap_location_collector.GetArrayHeapLocation(arr, add0); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_add0); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_add0, arr_set_i)); // Test side effect dependency based on LSA analysis: array[i] and array[i-0] - loc1 = heap_location_collector.GetArrayHeapLocation(arr, i); - loc2 = heap_location_collector.GetArrayHeapLocation(arr, sub0); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_sub0); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_sub0, arr_set_i)); // Test side effect dependency based on LSA analysis: array[i] and array[i+1] - loc1 = heap_location_collector.GetArrayHeapLocation(arr, i); - loc2 = heap_location_collector.GetArrayHeapLocation(arr, add1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_add1); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_add1, arr_set_i)); // Test side effect dependency based on LSA analysis: array[i+1] and array[i-1] - loc1 = heap_location_collector.GetArrayHeapLocation(arr, add1); - loc2 = heap_location_collector.GetArrayHeapLocation(arr, sub1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_add1); + loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_sub1); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_sub1, arr_set_add1)); diff --git a/test/586-checker-null-array-get/src/Main.java b/test/586-checker-null-array-get/src/Main.java index de9429fd8b..ebe91cfe1e 100644 --- a/test/586-checker-null-array-get/src/Main.java +++ b/test/586-checker-null-array-get/src/Main.java @@ -107,9 +107,8 @@ public class Main { /// CHECK-DAG: <> ArrayGet [<>,{{i\d+}}] public static void bar() { // We create multiple accesses that will lead the bounds check - // elimination pass to add a HDeoptimize. Not having the bounds check helped - // the load store elimination think it could merge two ArrayGet with different - // types. + // elimination pass to add a HDeoptimize. Not having the bounds check + // makes the ArrayGets look almost the same if it were not for the type! String[] array = (String[])getNull(); objectField = array[0]; objectField = array[1]; -- GitLab From aa730efe50cbc76fc6954f1b323cf49aa275d1b2 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Tue, 15 May 2018 16:09:44 -0700 Subject: [PATCH 403/749] Add ti-fast jvmti agent. It is often useful to get a speed-of-light estimate for how heavy watching various JVMTI is. This was difficult to measure without creating a custom agent to exercise the feature. This adds a new ti-fast agent which can be used to run a program which has an agent that does nothing watching various events. Test: m -j50 libtifast adb push $OUT/system/lib64/libtifast.so /data/local/tmp adb shell setenforce 0 adb shell am start-activity \ --attach-agent /data/local/tmp/libtifast.so=MethodEntry,MethodExit \ com.antonioleiva.bandhookkotlin/.ui.screens.main.MainActivity Change-Id: Id97135bcae55f9a943108fd935031fd887fb913a --- tools/ti-fast/Android.bp | 56 +++++++++++ tools/ti-fast/README.md | 101 +++++++++++++++++++ tools/ti-fast/tifast.cc | 205 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 362 insertions(+) create mode 100644 tools/ti-fast/Android.bp create mode 100644 tools/ti-fast/README.md create mode 100644 tools/ti-fast/tifast.cc diff --git a/tools/ti-fast/Android.bp b/tools/ti-fast/Android.bp new file mode 100644 index 0000000000..fd867c9bcc --- /dev/null +++ b/tools/ti-fast/Android.bp @@ -0,0 +1,56 @@ +// +// 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. +// + +// Build variants {target,host} x {debug,ndebug} x {32,64} +cc_defaults { + name: "tifast-defaults", + host_supported: true, + srcs: ["tifast.cc"], + defaults: ["art_defaults"], + + // Note that this tool needs to be built for both 32-bit and 64-bit since it requires + // to be same ISA as what it is attached to. + compile_multilib: "both", + + shared_libs: [ + "libbase", + ], + header_libs: [ + "libopenjdkjvmti_headers", + ], + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, + symlink_preferred_arch: true, +} + +art_cc_library { + name: "libtifast", + defaults: ["tifast-defaults"], +} + +art_cc_library { + name: "libtifastd", + defaults: [ + "art_debug_defaults", + "tifast-defaults", + ], +} diff --git a/tools/ti-fast/README.md b/tools/ti-fast/README.md new file mode 100644 index 0000000000..bc468826ec --- /dev/null +++ b/tools/ti-fast/README.md @@ -0,0 +1,101 @@ +# tifast + +tifast is a JVMTI agent designed for profiling the performance impact listening +to various JVMTI events. It is called tifast since none of the event handlers do +anything meaning that it can be considered speed-of-light. + +# Usage +### Build +> `make libtifast` + +The libraries will be built for 32-bit, 64-bit, host and target. Below examples +assume you want to use the 64-bit version. + +### Command Line + +The agent is loaded using -agentpath like normal. It takes arguments in the +following format: +> `[log,][EventName1[,EventName2[,...]]]` + +* If 'log' is the first argument the event handlers will LOG(INFO) when they are + called. This behavior is static. The no-log methods have no branches and just + immediately return. + +* The event-names are the same names as are used in the jvmtiEventCallbacks + struct. + +* All required capabilities are automatically gained. No capabilities other than + those needed to listen for the events are gained. + +* Only events which do not require additional function calls to cause delivery + and are sent more than once are supported. + +#### Supported events + +The following events may be listened for with this agent + +* `SingleStep` + +* `MethodEntry` + +* `MethodExit` + +* `NativeMethodBind` + +* `Exception` + +* `ExceptionCatch` + +* `ThreadStart` + +* `ThreadEnd` + +* `ClassLoad` + +* `ClassPrepare` + +* `ClassFileLoadHook` + +* `CompiledMethodLoad` + +* `CompiledMethodUnload` + +* `DynamicCodeGenerated` + +* `DataDumpRequest` + +* `MonitorContendedEnter` + +* `MonitorContendedEntered` + +* `MonitorWait` + +* `MonitorWaited` + +* `ResourceExhausted` + +* `VMObjectAlloc` + +* `GarbageCollectionStart` + +* `GarbageCollectionFinish` + +All other events cannot be listened for by this agent. Most of these missing +events either require the use of other functions in order to be called +(`FramePop`, `ObjectFree`, etc) or are only called once (`VMInit`, `VMDeath`, +etc). + +#### ART +> `art -Xplugin:$ANDROID_HOST_OUT/lib64/libopenjdkjvmti.so '-agentpath:libtifast.so=MethodEntry' -cp tmp/java/helloworld.dex -Xint helloworld` + +* `-Xplugin` and `-agentpath` need to be used, otherwise the agent will fail during init. +* If using `libartd.so`, make sure to use the debug version of jvmti. + +> `adb shell setenforce 0` +> +> `adb push $ANDROID_PRODUCT_OUT/system/lib64/libtifast.so /data/local/tmp/` +> +> `adb shell am start-activity --attach-agent /data/local/tmp/libtifast.so=MonitorWait,ClassPrepare some.debuggable.apps/.the.app.MainActivity` + +#### RI +> `java '-agentpath:libtifast.so=MethodEntry' -cp tmp/helloworld/classes helloworld` diff --git a/tools/ti-fast/tifast.cc b/tools/ti-fast/tifast.cc new file mode 100644 index 0000000000..952fba8720 --- /dev/null +++ b/tools/ti-fast/tifast.cc @@ -0,0 +1,205 @@ +// 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. +// + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace tifast { + +#define EVENT(x) JVMTI_EVENT_ ## x + +namespace { + +static void AddCapsForEvent(jvmtiEvent event, jvmtiCapabilities* caps) { + switch (event) { +#define DO_CASE(name, cap_name) \ + case EVENT(name): \ + caps->cap_name = 1; \ + break + DO_CASE(SINGLE_STEP, can_generate_single_step_events); + DO_CASE(METHOD_ENTRY, can_generate_method_entry_events); + DO_CASE(METHOD_EXIT, can_generate_method_exit_events); + DO_CASE(NATIVE_METHOD_BIND, can_generate_native_method_bind_events); + DO_CASE(EXCEPTION, can_generate_exception_events); + DO_CASE(EXCEPTION_CATCH, can_generate_exception_events); + DO_CASE(COMPILED_METHOD_LOAD, can_generate_compiled_method_load_events); + DO_CASE(COMPILED_METHOD_UNLOAD, can_generate_compiled_method_load_events); + DO_CASE(MONITOR_CONTENDED_ENTER, can_generate_monitor_events); + DO_CASE(MONITOR_CONTENDED_ENTERED, can_generate_monitor_events); + DO_CASE(MONITOR_WAIT, can_generate_monitor_events); + DO_CASE(MONITOR_WAITED, can_generate_monitor_events); + DO_CASE(VM_OBJECT_ALLOC, can_generate_vm_object_alloc_events); + DO_CASE(GARBAGE_COLLECTION_START, can_generate_garbage_collection_events); + DO_CASE(GARBAGE_COLLECTION_FINISH, can_generate_garbage_collection_events); +#undef DO_CASE + default: break; + } +} + +// Setup for all supported events. Give a macro with fun(name, event_num, args) +#define FOR_ALL_SUPPORTED_EVENTS(fun) \ + fun(SingleStep, EVENT(SINGLE_STEP), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation)) \ + fun(MethodEntry, EVENT(METHOD_ENTRY), (jvmtiEnv*, JNIEnv*, jthread, jmethodID)) \ + fun(MethodExit, EVENT(METHOD_ENTRY), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jboolean, jvalue)) \ + fun(NativeMethodBind, EVENT(NATIVE_METHOD_BIND), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, void*, void**)) \ + fun(Exception, EVENT(EXCEPTION), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation, jobject, jmethodID, jlocation)) \ + fun(ExceptionCatch, EVENT(EXCEPTION_CATCH), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation, jobject)) \ + fun(ThreadStart, EVENT(THREAD_START), (jvmtiEnv*, JNIEnv*, jthread)) \ + fun(ThreadEnd, EVENT(THREAD_END), (jvmtiEnv*, JNIEnv*, jthread)) \ + fun(ClassLoad, EVENT(CLASS_LOAD), (jvmtiEnv*, JNIEnv*, jthread, jclass)) \ + fun(ClassPrepare, EVENT(CLASS_PREPARE), (jvmtiEnv*, JNIEnv*, jthread, jclass)) \ + fun(ClassFileLoadHook, EVENT(CLASS_FILE_LOAD_HOOK), (jvmtiEnv*, JNIEnv*, jclass, jobject, const char*, jobject, jint, const unsigned char*, jint*, unsigned char**)) \ + fun(CompiledMethodLoad, EVENT(COMPILED_METHOD_LOAD), (jvmtiEnv*, jmethodID, jint, const void*, jint, const jvmtiAddrLocationMap*, const void*)) \ + fun(CompiledMethodUnload, EVENT(COMPILED_METHOD_UNLOAD), (jvmtiEnv*, jmethodID, const void*)) \ + fun(DynamicCodeGenerated, EVENT(DYNAMIC_CODE_GENERATED), (jvmtiEnv*, const char*, const void*, jint)) \ + fun(DataDumpRequest, EVENT(DATA_DUMP_REQUEST), (jvmtiEnv*)) \ + fun(MonitorContendedEnter, EVENT(MONITOR_CONTENDED_ENTER), (jvmtiEnv*, JNIEnv*, jthread, jobject)) \ + fun(MonitorContendedEntered, EVENT(MONITOR_CONTENDED_ENTERED), (jvmtiEnv*, JNIEnv*, jthread, jobject)) \ + fun(MonitorWait, EVENT(MONITOR_WAIT), (jvmtiEnv*, JNIEnv*, jthread, jobject, jlong)) \ + fun(MonitorWaited, EVENT(MONITOR_WAITED), (jvmtiEnv*, JNIEnv*, jthread, jobject, jboolean)) \ + fun(ResourceExhausted, EVENT(RESOURCE_EXHAUSTED), (jvmtiEnv*, JNIEnv*, jint, const void*, const char*)) \ + fun(VMObjectAlloc, EVENT(VM_OBJECT_ALLOC), (jvmtiEnv*, JNIEnv*, jthread, jobject, jclass, jlong)) \ + fun(GarbageCollectionStart, EVENT(GARBAGE_COLLECTION_START), (jvmtiEnv*)) \ + fun(GarbageCollectionFinish, EVENT(GARBAGE_COLLECTION_FINISH), (jvmtiEnv*)) + +#define GENERATE_EMPTY_FUNCTION(name, number, args) \ + static void JNICALL empty ## name args { } +FOR_ALL_SUPPORTED_EVENTS(GENERATE_EMPTY_FUNCTION) +#undef GENERATE_EMPTY_FUNCTION + +static jvmtiEventCallbacks kEmptyCallbacks { +#define CREATE_EMPTY_EVENT_CALLBACKS(name, num, args) \ + .name = empty ## name, + FOR_ALL_SUPPORTED_EVENTS(CREATE_EMPTY_EVENT_CALLBACKS) +#undef CREATE_EMPTY_EVENT_CALLBACKS +}; + +#define GENERATE_LOG_FUNCTION(name, number, args) \ + static void JNICALL log ## name args { \ + LOG(INFO) << "Got event " << #name ; \ + } +FOR_ALL_SUPPORTED_EVENTS(GENERATE_LOG_FUNCTION) +#undef GENERATE_LOG_FUNCTION + +static jvmtiEventCallbacks kLogCallbacks { +#define CREATE_LOG_EVENT_CALLBACK(name, num, args) \ + .name = log ## name, + FOR_ALL_SUPPORTED_EVENTS(CREATE_LOG_EVENT_CALLBACK) +#undef CREATE_LOG_EVENT_CALLBACK +}; + +static jvmtiEvent NameToEvent(const std::string& desired_name) { +#define CHECK_NAME(name, event, args) \ + if (desired_name == #name) { \ + return event; \ + } + FOR_ALL_SUPPORTED_EVENTS(CHECK_NAME); + LOG(FATAL) << "Unknown event " << desired_name; + __builtin_unreachable(); +#undef CHECK_NAME +} + +#undef FOR_ALL_SUPPORTED_EVENTS +static std::vector GetRequestedEventList(const std::string& args) { + std::vector res; + std::stringstream args_stream(args); + std::string item; + while (std::getline(args_stream, item, ',')) { + if (item == "") { + continue; + } + res.push_back(NameToEvent(item)); + } + return res; +} + +} // namespace + +static jint AgentStart(JavaVM* vm, + char* options, + void* reserved ATTRIBUTE_UNUSED) { + jvmtiEnv* jvmti = nullptr; + jvmtiError error = JVMTI_ERROR_NONE; + { + jint res = 0; + res = vm->GetEnv(reinterpret_cast(&jvmti), JVMTI_VERSION_1_1); + + if (res != JNI_OK || jvmti == nullptr) { + LOG(ERROR) << "Unable to access JVMTI, error code " << res; + return JNI_ERR; + } + } + std::string args(options); + bool is_log = false; + if (args.compare(0, 3, "log") == 0) { + is_log = true; + args = args.substr(3); + } + + std::vector events = GetRequestedEventList(args); + + jvmtiCapabilities caps{}; + for (jvmtiEvent e : events) { + AddCapsForEvent(e, &caps); + } + error = jvmti->AddCapabilities(&caps); + if (error != JVMTI_ERROR_NONE) { + LOG(ERROR) << "Unable to set caps"; + return JNI_ERR; + } + + if (is_log) { + error = jvmti->SetEventCallbacks(&kLogCallbacks, static_cast(sizeof(kLogCallbacks))); + } else { + error = jvmti->SetEventCallbacks(&kEmptyCallbacks, static_cast(sizeof(kEmptyCallbacks))); + } + if (error != JVMTI_ERROR_NONE) { + LOG(ERROR) << "Unable to set event callbacks."; + return JNI_ERR; + } + for (jvmtiEvent e : events) { + error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, + e, + nullptr /* all threads */); + if (error != JVMTI_ERROR_NONE) { + LOG(ERROR) << "Unable to enable event " << e; + return JNI_ERR; + } + } + return JNI_OK; +} + +// Late attachment (e.g. 'am attach-agent'). +extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm, char* options, void* reserved) { + return AgentStart(vm, options, reserved); +} + +// Early attachment +extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) { + return AgentStart(jvm, options, reserved); +} + +} // namespace tifast + -- GitLab From 904e75a66edea51adc11f61ad8ccaab557402095 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Tue, 15 May 2018 13:45:08 +0100 Subject: [PATCH 404/749] Fix stripping of access flags during JVMTI redefine When JVMTI redefines a method/field, it replaces the guts of an ArtMethod with the implementation in a provided dex file. This process includes overwriting the intrinsics ordinal (stored in ArtMethod's access flags) so that the new implementation is picked up. This overwrite, however, does not check if the ArtMethod is an intrinsic in the first place and will clear the bits regardless. This caused an issue for hidden API as its access flags conflict with those of intrinsics. All redefined framework classes would therefore become completely visible to all callers. This patch fixes the issue by adding a IsIntrisic() check around the function which clears the access flags. Bug: 79698297 Test: art/test.py -b --host -r -t 999-redefine-hiddenapi Test: art/test.py -b --host -r -t 950-redefine-intrinsic Change-Id: I7e607d874cc732ceb118d58e4cd40ff4353215f5 --- runtime/art_method.cc | 17 +++ runtime/art_method.h | 4 +- test/999-redefine-hiddenapi/api-blacklist.txt | 2 + test/999-redefine-hiddenapi/build | 17 +++ test/999-redefine-hiddenapi/expected.txt | 1 + test/999-redefine-hiddenapi/info.txt | 1 + test/999-redefine-hiddenapi/run | 17 +++ .../src-ex/Test999.java | 25 ++++ .../src-redefine/art/Test999.java | 25 ++++ .../src-redefine/gen.sh | 30 +++++ test/999-redefine-hiddenapi/src/Main.java | 111 ++++++++++++++++++ .../src/art/Redefinition.java | 91 ++++++++++++++ test/etc/default-build | 5 + 13 files changed, 343 insertions(+), 3 deletions(-) create mode 100644 test/999-redefine-hiddenapi/api-blacklist.txt create mode 100644 test/999-redefine-hiddenapi/build create mode 100644 test/999-redefine-hiddenapi/expected.txt create mode 100644 test/999-redefine-hiddenapi/info.txt create mode 100755 test/999-redefine-hiddenapi/run create mode 100644 test/999-redefine-hiddenapi/src-ex/Test999.java create mode 100644 test/999-redefine-hiddenapi/src-redefine/art/Test999.java create mode 100755 test/999-redefine-hiddenapi/src-redefine/gen.sh create mode 100644 test/999-redefine-hiddenapi/src/Main.java create mode 100644 test/999-redefine-hiddenapi/src/art/Redefinition.java diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 87fcb20698..608e33cf65 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -710,6 +710,23 @@ bool ArtMethod::HasAnyCompiledCode() { return GetOatMethodQuickCode(runtime->GetClassLinker()->GetImagePointerSize()) != nullptr; } +void ArtMethod::SetNotIntrinsic() { + if (!IsIntrinsic()) { + return; + } + + // Query the hidden API access flags of the intrinsic. + HiddenApiAccessFlags::ApiList intrinsic_api_list = GetHiddenApiAccessFlags(); + + // Clear intrinsic-related access flags. + ClearAccessFlags(kAccIntrinsic | kAccIntrinsicBits); + + // Re-apply hidden API access flags now that the method is not an intrinsic. + SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(GetAccessFlags(), intrinsic_api_list)); + DCHECK_EQ(GetHiddenApiAccessFlags(), intrinsic_api_list); +} + + void ArtMethod::CopyFrom(ArtMethod* src, PointerSize image_pointer_size) { memcpy(reinterpret_cast(this), reinterpret_cast(src), Size(image_pointer_size)); diff --git a/runtime/art_method.h b/runtime/art_method.h index acaa4a68a1..012d706756 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -194,9 +194,7 @@ class ArtMethod FINAL { return (GetAccessFlags() & kAccIntrinsicBits) >> kAccFlagsShift; } - void SetNotIntrinsic() REQUIRES_SHARED(Locks::mutator_lock_) { - ClearAccessFlags(kAccIntrinsic | kAccIntrinsicBits); - } + void SetNotIntrinsic() REQUIRES_SHARED(Locks::mutator_lock_); bool IsCopied() { static_assert((kAccCopied & (kAccIntrinsic | kAccIntrinsicBits)) == 0, diff --git a/test/999-redefine-hiddenapi/api-blacklist.txt b/test/999-redefine-hiddenapi/api-blacklist.txt new file mode 100644 index 0000000000..63e37aa757 --- /dev/null +++ b/test/999-redefine-hiddenapi/api-blacklist.txt @@ -0,0 +1,2 @@ +Lart/Test999;->foo()V +Lart/Test999;->bar:I diff --git a/test/999-redefine-hiddenapi/build b/test/999-redefine-hiddenapi/build new file mode 100644 index 0000000000..f4b029fb82 --- /dev/null +++ b/test/999-redefine-hiddenapi/build @@ -0,0 +1,17 @@ +#!/bin/bash +# +# 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 +# +# 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. + +USE_HIDDENAPI=true ./default-build "$@" diff --git a/test/999-redefine-hiddenapi/expected.txt b/test/999-redefine-hiddenapi/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/999-redefine-hiddenapi/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/999-redefine-hiddenapi/info.txt b/test/999-redefine-hiddenapi/info.txt new file mode 100644 index 0000000000..87bc30cf80 --- /dev/null +++ b/test/999-redefine-hiddenapi/info.txt @@ -0,0 +1 @@ +Tests that JVMTI class redefinition does not strip away hidden API access flags. diff --git a/test/999-redefine-hiddenapi/run b/test/999-redefine-hiddenapi/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/999-redefine-hiddenapi/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +./default-run "$@" --jvmti diff --git a/test/999-redefine-hiddenapi/src-ex/Test999.java b/test/999-redefine-hiddenapi/src-ex/Test999.java new file mode 100644 index 0000000000..97495c5a47 --- /dev/null +++ b/test/999-redefine-hiddenapi/src-ex/Test999.java @@ -0,0 +1,25 @@ +/* + * 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 art; + +public class Test999 { + public void foo() { + System.out.println("hello"); + } + + public int bar = 42; +} diff --git a/test/999-redefine-hiddenapi/src-redefine/art/Test999.java b/test/999-redefine-hiddenapi/src-redefine/art/Test999.java new file mode 100644 index 0000000000..c1b838ccc7 --- /dev/null +++ b/test/999-redefine-hiddenapi/src-redefine/art/Test999.java @@ -0,0 +1,25 @@ +/* + * 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 art; + +public class Test999 { + public void foo() { + System.out.println("Goodbye"); + } + + public int bar = 64; +} diff --git a/test/999-redefine-hiddenapi/src-redefine/gen.sh b/test/999-redefine-hiddenapi/src-redefine/gen.sh new file mode 100755 index 0000000000..6948cbbfc3 --- /dev/null +++ b/test/999-redefine-hiddenapi/src-redefine/gen.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# +# 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 +# +# 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. + +set -e +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +TMP=`mktemp -d` + +CLASS "art/Test999" + +(cd "$TMP" && javac -d "${TMP}" "$DIR/${CLASS}.java" && d8 --output . "$TMP/${CLASS}.class") + +echo ' private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(' +base64 "${TMP}/${CLASS}.class" | sed -E 's/^/ "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/' +echo ' private static final byte[] DEX_BYTES = Base64.getDecoder().decode(' +base64 "${TMP}/classes.dex" | sed -E 's/^/ "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/' + +rm -rf "$TMP" diff --git a/test/999-redefine-hiddenapi/src/Main.java b/test/999-redefine-hiddenapi/src/Main.java new file mode 100644 index 0000000000..c6365ac234 --- /dev/null +++ b/test/999-redefine-hiddenapi/src/Main.java @@ -0,0 +1,111 @@ +/* + * 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.io.File; +import java.lang.reflect.Method; +import java.util.Base64; + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[0]); + + // Run the initialization routine. This will enable hidden API checks in + // the runtime, in case they are not enabled by default. + init(); + + // Load the '-ex' APK and attach it to the boot class path. + appendToBootClassLoader(DEX_EXTRA); + + // Find the test class in boot class loader and verify that its members are hidden. + Class klass = Class.forName("art.Test999", true, BOOT_CLASS_LOADER); + assertMethodIsHidden(klass, "before redefinition"); + assertFieldIsHidden(klass, "before redefinition"); + + // Redefine the class using JVMTI. + art.Redefinition.setTestConfiguration(art.Redefinition.Config.COMMON_REDEFINE); + art.Redefinition.doCommonClassRedefinition(klass, CLASS_BYTES, DEX_BYTES); + + // Verify that the class members are still hidden. + assertMethodIsHidden(klass, "after redefinition"); + assertFieldIsHidden(klass, "after redefinition"); + } + + private static void assertMethodIsHidden(Class klass, String msg) throws Exception { + try { + klass.getDeclaredMethod("foo"); + // Unexpected. Should have thrown NoSuchMethodException. + throw new Exception("Method should not be accessible " + msg); + } catch (NoSuchMethodException ex) { + // Expected. + } + } + + private static void assertFieldIsHidden(Class klass, String msg) throws Exception { + try { + klass.getDeclaredField("bar"); + // Unexpected. Should have thrown NoSuchFieldException. + throw new Exception("Field should not be accessible " + msg); + } catch (NoSuchFieldException ex) { + // Expected. + } + } + + private static final String DEX_EXTRA = + new File(System.getenv("DEX_LOCATION"), "999-redefine-hiddenapi-ex.jar").getAbsolutePath(); + + private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader(); + + // Native functions. Note that these are implemented in 674-hiddenapi/hiddenapi.cc. + private static native void appendToBootClassLoader(String dexPath); + private static native void init(); + + /** + * base64 encoded class/dex file for + * + * public class Test999 { + * public void foo() { + * System.out.println("Goodbye"); + * } + * + * public int bar = 64; + * } + */ + private static final byte[] CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADUAIAoABwARCQAGABIJABMAFAgAFQoAFgAXBwAYBwAZAQADYmFyAQABSQEABjxpbml0" + + "PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAANmb28BAApTb3VyY2VGaWxlAQAMVGVz" + + "dDk5OS5qYXZhDAAKAAsMAAgACQcAGgwAGwAcAQAHR29vZGJ5ZQcAHQwAHgAfAQALYXJ0L1Rlc3Q5" + + "OTkBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lv" + + "L1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xh" + + "bmcvU3RyaW5nOylWACEABgAHAAAAAQABAAgACQAAAAIAAQAKAAsAAQAMAAAAJwACAAEAAAALKrcA" + + "ASoQQLUAArEAAAABAA0AAAAKAAIAAAATAAQAGAABAA4ACwABAAwAAAAlAAIAAQAAAAmyAAMSBLYA" + + "BbEAAAABAA0AAAAKAAIAAAAVAAgAFgABAA8AAAACABA="); + private static final byte[] DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQD0dZ+IWxOi+cJDSWjfTnUerlZj1Lll3ONIAwAAcAAAAHhWNBIAAAAAAAAAAJwCAAAQ" + + "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAACAAAA5AAAAAQAAAD0AAAAAQAAABQBAAAUAgAANAEAAIYB" + + "AACOAQAAlwEAAJoBAACpAQAAwAEAANQBAADoAQAA/AEAAAoCAAANAgAAEQIAABYCAAAbAgAAIAIA" + + "ACkCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAACAAQAA" + + "AQAAAAsAAAAFAAIADQAAAAEAAAAAAAAAAQAAAAwAAAACAAEADgAAAAMAAAAAAAAAAQAAAAEAAAAD" + + "AAAAAAAAAAgAAAAAAAAAhwIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" + + "AQACAAAAeQEAAAgAAABiAAEAGgEBAG4gAgAQAA4AEwAOQAAVAA54AAAAAQAAAAQABjxpbml0PgAH" + + "R29vZGJ5ZQABSQANTGFydC9UZXN0OTk5OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9s" + + "YW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0" + + "OTk5LmphdmEAAVYAAlZMAANiYXIAA2ZvbwADb3V0AAdwcmludGxuAFx+fkQ4eyJtaW4tYXBpIjox" + + "LCJzaGEtMSI6IjU2YzJlMzBmNTIzM2I4NDRmZjZkZGQ4N2ZiNTNkMzRmYjE3MjM3ZGYiLCJ2ZXJz" + + "aW9uIjoidjEuMi4xNS1kZXYifQAAAQEBAAEAgYAEtAIBAdQCAAAAAAAOAAAAAAAAAAEAAAAAAAAA" + + "AQAAABAAAABwAAAAAgAAAAcAAACwAAAAAwAAAAIAAADMAAAABAAAAAIAAADkAAAABQAAAAQAAAD0" + + "AAAABgAAAAEAAAAUAQAAASAAAAIAAAA0AQAAAyAAAAIAAAB0AQAAARAAAAEAAACAAQAAAiAAABAA" + + "AACGAQAAACAAAAEAAACHAgAAAxAAAAEAAACYAgAAABAAAAEAAACcAgAA"); +} diff --git a/test/999-redefine-hiddenapi/src/art/Redefinition.java b/test/999-redefine-hiddenapi/src/art/Redefinition.java new file mode 100644 index 0000000000..1eec70b48c --- /dev/null +++ b/test/999-redefine-hiddenapi/src/art/Redefinition.java @@ -0,0 +1,91 @@ +/* + * 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 art; + +import java.util.ArrayList; +// Common Redefinition functions. Placed here for use by CTS +public class Redefinition { + public static final class CommonClassDefinition { + public final Class target; + public final byte[] class_file_bytes; + public final byte[] dex_file_bytes; + + public CommonClassDefinition(Class target, byte[] class_file_bytes, byte[] dex_file_bytes) { + this.target = target; + this.class_file_bytes = class_file_bytes; + this.dex_file_bytes = dex_file_bytes; + } + } + + // A set of possible test configurations. Test should set this if they need to. + // This must be kept in sync with the defines in ti-agent/common_helper.cc + public static enum Config { + COMMON_REDEFINE(0), + COMMON_RETRANSFORM(1), + COMMON_TRANSFORM(2); + + private final int val; + private Config(int val) { + this.val = val; + } + } + + public static void setTestConfiguration(Config type) { + nativeSetTestConfiguration(type.val); + } + + private static native void nativeSetTestConfiguration(int type); + + // Transforms the class + public static native void doCommonClassRedefinition(Class target, + byte[] classfile, + byte[] dexfile); + + public static void doMultiClassRedefinition(CommonClassDefinition... defs) { + ArrayList> classes = new ArrayList<>(); + ArrayList class_files = new ArrayList<>(); + ArrayList dex_files = new ArrayList<>(); + + for (CommonClassDefinition d : defs) { + classes.add(d.target); + class_files.add(d.class_file_bytes); + dex_files.add(d.dex_file_bytes); + } + doCommonMultiClassRedefinition(classes.toArray(new Class[0]), + class_files.toArray(new byte[0][]), + dex_files.toArray(new byte[0][])); + } + + public static void addMultiTransformationResults(CommonClassDefinition... defs) { + for (CommonClassDefinition d : defs) { + addCommonTransformationResult(d.target.getCanonicalName(), + d.class_file_bytes, + d.dex_file_bytes); + } + } + + public static native void doCommonMultiClassRedefinition(Class[] targets, + byte[][] classfiles, + byte[][] dexfiles); + public static native void doCommonClassRetransformation(Class... target); + public static native void setPopRetransformations(boolean pop); + public static native void popTransformationFor(String name); + public static native void enableCommonRetransformation(boolean enable); + public static native void addCommonTransformationResult(String target_name, + byte[] class_bytes, + byte[] dex_bytes); +} diff --git a/test/etc/default-build b/test/etc/default-build index 8bb898c7c1..c61de0ab6e 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -561,6 +561,11 @@ fi if [[ -d classes-ex ]] && [ ${NEED_DEX} = "true" ]; then make_dex classes-ex + # Apply hiddenapi on the dex files if the test has API list file(s). + if [ ${USE_HIDDENAPI} = "true" -a ${HAS_HIDDENAPI_SPEC} = "true" ]; then + make_hiddenapi classes-ex.dex + fi + # quick shuffle so that the stored name is "classes.dex" mv classes.dex classes-1.dex mv classes-ex.dex classes.dex -- GitLab From c4d5c13c2ca6268ba577fff6d7e646a421d3a03f Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Tue, 15 May 2018 13:57:58 +0100 Subject: [PATCH 405/749] ART: Faster VarHandle accessors for exact callsite matches Adds a faster path for VarHandle accessors invoked through the runtime. The code previously assumed that some kind of argument or return type conversion would be necessary. Bug: 73275005 Test: art/test.py --host --gcstress -r -t 712 Change-Id: Ie27c6d8c3bdf80f5932d38d19c67a6bf633af8f0 --- runtime/mirror/var_handle.cc | 43 +++-- runtime/mirror/var_handle.h | 19 ++- runtime/mirror/var_handle_test.cc | 264 +++++++++++++++++------------- runtime/var_handles.cc | 13 +- 4 files changed, 191 insertions(+), 148 deletions(-) diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc index 49cdd4ffde..d31e06cf36 100644 --- a/runtime/mirror/var_handle.cc +++ b/runtime/mirror/var_handle.cc @@ -1425,21 +1425,24 @@ int32_t VarHandle::GetAccessModesBitMask() { return GetField32(AccessModesBitMaskOffset()); } -bool VarHandle::IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type) { - StackHandleScope<3> hs(Thread::Current()); - Handle mt_rtype(hs.NewHandle(method_type->GetRType())); - Handle vh(hs.NewHandle(this)); - Handle var_type(hs.NewHandle(vh->GetVarType())); +VarHandle::MatchKind VarHandle::GetMethodTypeMatchForAccessMode(AccessMode access_mode, + MethodType* method_type) { + MatchKind match = MatchKind::kExact; + + ObjPtr vh = this; + ObjPtr var_type = vh->GetVarType(); + ObjPtr mt_rtype = method_type->GetRType(); AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); - // Check return type first. - if (mt_rtype->GetPrimitiveType() == Primitive::Type::kPrimVoid) { - // The result of the operation will be discarded. The return type - // of the VarHandle is immaterial. - } else { - ObjPtr vh_rtype(GetReturnType(access_mode_template, var_type.Get())); - if (!IsReturnTypeConvertible(vh_rtype, mt_rtype.Get())) { - return false; + // Check return type first. If the return type of the method + // of the VarHandle is immaterial. + if (mt_rtype->GetPrimitiveType() != Primitive::Type::kPrimVoid) { + ObjPtr vh_rtype = GetReturnType(access_mode_template, var_type.Ptr()); + if (vh_rtype != mt_rtype) { + if (!IsReturnTypeConvertible(vh_rtype, mt_rtype)) { + return MatchKind::kNone; + } + match = MatchKind::kWithConversions; } } @@ -1447,21 +1450,25 @@ bool VarHandle::IsMethodTypeCompatible(AccessMode access_mode, MethodType* metho ObjPtr vh_ptypes[VarHandle::kMaxAccessorParameters]; const int32_t vh_ptypes_count = BuildParameterArray(vh_ptypes, access_mode_template, - var_type.Get(), + var_type, GetCoordinateType0(), GetCoordinateType1()); if (vh_ptypes_count != method_type->GetPTypes()->GetLength()) { - return false; + return MatchKind::kNone; } // Check the parameter types are compatible. ObjPtr> mt_ptypes = method_type->GetPTypes(); for (int32_t i = 0; i < vh_ptypes_count; ++i) { + if (mt_ptypes->Get(i) == vh_ptypes[i]) { + continue; + } if (!IsParameterTypeConvertible(mt_ptypes->Get(i), vh_ptypes[i])) { - return false; + return MatchKind::kNone; } + match = MatchKind::kWithConversions; } - return true; + return match; } bool VarHandle::IsInvokerMethodTypeCompatible(AccessMode access_mode, @@ -1508,7 +1515,7 @@ bool VarHandle::IsInvokerMethodTypeCompatible(AccessMode access_mode, MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self, ObjPtr var_handle, AccessMode access_mode) { - // This is a static as the var_handle might be moved by the GC during it's execution. + // This is a static method as the var_handle might be moved by the GC during it's execution. AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); StackHandleScope<3> hs(self); diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h index 6acd4e0bb1..eb3704ee48 100644 --- a/runtime/mirror/var_handle.h +++ b/runtime/mirror/var_handle.h @@ -99,15 +99,16 @@ class MANAGED VarHandle : public Object { return (GetAccessModesBitMask() & (1u << static_cast(accessMode))) != 0; } - // Returns true if the MethodType specified is compatible with the - // method type associated with the specified AccessMode with argument - // and return value conversions. The supplied MethodType is assumed - // to be from the point of invocation so it is valid for the supplied - // MethodType to have a void return value when the return value for - // the AccessMode is non-void. This corresponds to the result of the - // accessor being discarded. - bool IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type) - REQUIRES_SHARED(Locks::mutator_lock_); + enum MatchKind : uint8_t { + kNone, + kWithConversions, + kExact + }; + + // Returns match information on the compatability between the exact method type for + // 'access_mode' and the provided 'method_type'. + MatchKind GetMethodTypeMatchForAccessMode(AccessMode access_mode, MethodType* method_type) + REQUIRES_SHARED(Locks::mutator_lock_); // Returns true if the MethodType specified is compatible with the // specified access_mode if the first parameter of method_type is diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc index d9fa07f207..005aba3edd 100644 --- a/runtime/mirror/var_handle_test.cc +++ b/runtime/mirror/var_handle_test.cc @@ -246,6 +246,47 @@ static MethodType* MethodTypeOf(const std::string& method_descriptor) { return MethodType::Create(self, rtype, ptypes); } +static bool AccessModeMatch(VarHandle* vh, + VarHandle::AccessMode access_mode, + MethodType* method_type, + VarHandle::MatchKind expected_match) + REQUIRES_SHARED(Locks::mutator_lock_) { + return vh->GetMethodTypeMatchForAccessMode(access_mode, method_type) == expected_match; +} + +template +static bool AccessModeExactMatch(Handle vh, + VarHandle::AccessMode access_mode, + const char* descriptor) + REQUIRES_SHARED(Locks::mutator_lock_) { + return AccessModeMatch(vh.Get(), + access_mode, + MethodTypeOf(descriptor), + VarHandle::MatchKind::kExact); +} + +template +static bool AccessModeWithConversionsMatch(Handle vh, + VarHandle::AccessMode access_mode, + const char* descriptor) + REQUIRES_SHARED(Locks::mutator_lock_) { + return AccessModeMatch(vh.Get(), + access_mode, + MethodTypeOf(descriptor), + VarHandle::MatchKind::kWithConversions); +} + +template +static bool AccessModeNoMatch(Handle vh, + VarHandle::AccessMode access_mode, + const char* descriptor) + REQUIRES_SHARED(Locks::mutator_lock_) { + return AccessModeMatch(vh.Get(), + access_mode, + MethodTypeOf(descriptor), + VarHandle::MatchKind::kNone); +} + TEST_F(VarHandleTest, InstanceFieldVarHandle) { Thread * const self = Thread::Current(); ScopedObjectAccess soa(self); @@ -296,47 +337,53 @@ TEST_F(VarHandleTest, InstanceFieldVarHandle) { // Check compatibility - "Get" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)I"))); - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;)I")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;)V")); + EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;)D")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)Z")); } // Check compatibility - "Set" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;I)V")); + EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;S)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndSet" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, - MethodTypeOf("(Ljava/lang/Integer;II)I"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)Z")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)V")); + EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;II)Ljava/lang/Boolean;")); + EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;IB)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;II)I")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndExchange" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)I"))); - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(IIII)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)I")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)V")); + EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;II)J")); + EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;BS)F")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;I)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(IIII)V")); } // Check compatibility - "GetAndUpdate" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)I"))); - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)S"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;I)I")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;I)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;I)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)S")); } // Check synthesized method types match expected forms. @@ -430,48 +477,47 @@ TEST_F(VarHandleTest, StaticFieldVarHandle) { // Check compatibility - "Get" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()I"))); - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "()I")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "()V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)Z")); } // Check compatibility - "Set" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(F)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(I)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(F)V")); } // Check compatibility - "CompareAndSet" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, - MethodTypeOf("(II)Ljava/lang/String;"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(II)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)Ljava/lang/String;")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndExchange" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)I"))); - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(ID)I"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)S"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(IIJ)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(II)I")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(II)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(ID)I")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)S")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(IIJ)V")); } // Check compatibility - "GetAndUpdate" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)I"))); - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(I)I")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(I)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(I)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)V")); } // Check synthesized method types match expected forms. @@ -594,50 +640,46 @@ TEST_F(VarHandleTest, ArrayElementVarHandle) { // Check compatibility - "Get" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Ljava/lang/String;"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;Ljava/lang/String;)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;I)Ljava/lang/String;")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;I)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;Ljava/lang/String;)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)Z")); } // Check compatibility - "Set" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;I)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;I)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndSet" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet; - EXPECT_TRUE( - vh->IsMethodTypeCompatible( - access_mode, - MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, - MethodTypeOf("([Ljava/lang/String;III)I"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;III)I")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;I)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndExchange" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;II)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;II)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(III)V")); } // Check compatibility - "GetAndUpdate" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(II)V")); } // Check synthesized method types match expected forms. @@ -747,50 +789,46 @@ TEST_F(VarHandleTest, ByteArrayViewVarHandle) { // Check compatibility - "Get" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)C"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BC)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BI)C")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BI)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BC)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)Z")); } // Check compatibility - "Set" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BIC)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BI)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BI)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndSet" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet; - EXPECT_TRUE( - vh->IsMethodTypeCompatible( - access_mode, - MethodTypeOf("([BICC)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, - MethodTypeOf("([BIII)I"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BICC)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BIII)I")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BI)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndExchange" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BICC)C"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BICC)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BII)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BICC)C")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BICC)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BII)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(III)V")); } // Check compatibility - "GetAndUpdate" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)C"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BIC)C")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BIC)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BIC)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(II)V")); } // Check synthesized method types match expected forms. @@ -900,50 +938,46 @@ TEST_F(VarHandleTest, ByteBufferViewVarHandle) { // Check compatibility - "Get" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)D"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;D)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)D")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;D)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)Z")); } // Check compatibility - "Set" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndSet" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet; - EXPECT_TRUE( - vh->IsMethodTypeCompatible( - access_mode, - MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, - MethodTypeOf("(Ljava/nio/ByteBuffer;IDI)D"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDD)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDI)D")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndExchange" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)D"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;II)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDD)D")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDD)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;II)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(III)V")); } // Check compatibility - "GetAndUpdate" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)D"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)D")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(II)V")); } // Check synthesized method types match expected forms. diff --git a/runtime/var_handles.cc b/runtime/var_handles.cc index b2416675f0..d71745e531 100644 --- a/runtime/var_handles.cc +++ b/runtime/var_handles.cc @@ -42,24 +42,25 @@ bool VarHandleInvokeAccessor(Thread* self, return false; } - if (!var_handle->IsMethodTypeCompatible(access_mode, callsite_type.Get())) { + mirror::VarHandle::MatchKind match_kind = + var_handle->GetMethodTypeMatchForAccessMode(access_mode, callsite_type.Get()); + if (match_kind == mirror::VarHandle::MatchKind::kExact) { + return var_handle->Access(access_mode, &shadow_frame, operands, result); + } + if (match_kind == mirror::VarHandle::MatchKind::kNone) { ThrowWrongMethodTypeException(var_handle->GetMethodTypeForAccessMode(self, access_mode), callsite_type.Get()); return false; } + DCHECK_EQ(mirror::VarHandle::MatchKind::kWithConversions, match_kind); StackHandleScope<1> hs(self); - - // TODO(oth): GetMethodTypeForAccessMode() allocates a MethodType() - // which is only required if we need to convert argument and/or - // return types. Handle accessor_type(hs.NewHandle( var_handle->GetMethodTypeForAccessMode(self, access_mode))); const size_t num_vregs = accessor_type->NumberOfVRegs(); const int num_params = accessor_type->GetPTypes()->GetLength(); ShadowFrameAllocaUniquePtr accessor_frame = CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC()); - ShadowFrameGetter getter(shadow_frame, operands); static const uint32_t kFirstDestinationReg = 0; ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg); -- GitLab From acc2519556d6a0bfb84032c064027a16ce111815 Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Wed, 16 May 2018 13:27:02 +0100 Subject: [PATCH 406/749] Track (non-public) API change in String There's an associated change in libcore which changes the API on String. Bug: 74379469 Test: Treehugger Change-Id: Ie77ed1ea2e9ae0749b2a49d92e8df85a3e07958c --- test/100-reflect2/expected.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt index 6a9bf61d25..2b57824464 100644 --- a/test/100-reflect2/expected.txt +++ b/test/100-reflect2/expected.txt @@ -33,7 +33,7 @@ z (class java.lang.Character) 14 (class java.lang.Short) [java.lang.String(int,int,char[]), public java.lang.String(), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder)] [private final int java.lang.String.count, private int java.lang.String.hash, private static final java.io.ObjectStreamField[] java.lang.String.serialPersistentFields, private static final long java.lang.String.serialVersionUID, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER] -[native void java.lang.String.getCharsNoCheck(int,int,char[],int), private boolean java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native java.lang.String java.lang.String.doReplace(char,char), private native java.lang.String java.lang.String.fastSubstring(int,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int), void java.lang.String.getChars(char[],int)] +[native void java.lang.String.getCharsNoCheck(int,int,char[],int), private boolean java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native java.lang.String java.lang.String.doReplace(char,char), private native java.lang.String java.lang.String.fastSubstring(int,int), private static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), private static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(char[],int,int,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(char[],int,int,java.lang.String,int), void java.lang.String.getChars(char[],int)] [] [interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence] 0 -- GitLab From 90701d5416f800e9865d7dce39b0c4225b35217b Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Wed, 16 May 2018 12:59:32 +0100 Subject: [PATCH 407/749] Do not run 999-redefine-hiddenapi under jvm/redefine-stress The recently added run-test is specific to ART and depends on its internal implementation details. Do not run it under JVM, or in configs which stress JVMTI. Bug: 79698297 Test: art/test.py -b -r -t 999-redefine-hiddenapi --jvm Change-Id: I8f34f9ed0c97fa4061cca2e0c9e7b8aa61e1d12b --- test/knownfailures.json | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/knownfailures.json b/test/knownfailures.json index f473a99a27..a202044786 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -454,7 +454,8 @@ "674-hiddenapi", "649-vdex-duplicate-method", "804-class-extends-itself", - "921-hello-failure" + "921-hello-failure", + "999-redefine-hiddenapi" ], "description": [ "Tests that use illegal dex files or otherwise break dexter assumptions" @@ -471,7 +472,8 @@ "629-vdex-speed", "647-jni-get-field-id", "674-hiddenapi", - "944-transform-classloaders" + "944-transform-classloaders", + "999-redefine-hiddenapi" ], "description": [ "Tests that use custom class loaders or other features not supported ", @@ -876,7 +878,6 @@ "667-jit-jni-stub", "667-out-of-bounds", "668-aiobe", - "674-hiddenapi", "674-hotness-compiled", "674-vdex-uncompress", "675-checker-unverified-method", @@ -954,8 +955,10 @@ }, { "tests": ["616-cha-unloading", + "674-hiddenapi", "678-quickening", - "679-locks"], + "679-locks", + "999-redefine-hiddenapi"], "variant": "jvm", "description": ["Doesn't run on RI."] }, -- GitLab From d147e2fb824a92acb9cd86bd92d3f1c137d488c5 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 16 May 2018 11:37:41 +0100 Subject: [PATCH 408/749] Don't compile methods with irreducible loops and String.. The current code doesn't work when dex register aliases. bug: 78493232 Test: m Change-Id: I1c296f6dc914388844ae5eb7d84f3bd7d81e1f87 --- compiler/optimizing/instruction_builder.cc | 10 ++++- .../optimizing/optimizing_compiler_stats.h | 1 + .../smali/TestCase.smali | 43 ++++++++++++------- test/563-checker-fakestring/src/Main.java | 11 ++++- 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 9647dd5d41..a36e80379d 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1360,9 +1360,15 @@ bool HInstructionBuilder::HandleStringInit(HInvoke* invoke, if (arg_this->IsNewInstance()) { ssa_builder_->AddUninitializedString(arg_this->AsNewInstance()); } else { + // The only reason a HPhi can flow in a String. is when there is an + // irreducible loop, which will create HPhi for all dex registers at loop entry. DCHECK(arg_this->IsPhi()); - // NewInstance is not the direct input of the StringFactory call. It might - // be redundant but optimizing this case is not worth the effort. + DCHECK(graph_->HasIrreducibleLoops()); + // Don't bother compiling a method in that situation. While we could look at all + // phis related to the HNewInstance, it's not worth the trouble. + MaybeRecordStat(compilation_stats_, + MethodCompilationStat::kNotCompiledIrreducibleAndStringInit); + return false; } // Walk over all vregs and replace any occurrence of `arg_this` with `invoke`. diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 9a26f2f6c4..f246228074 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -50,6 +50,7 @@ enum class MethodCompilationStat { kNotCompiledThrowCatchLoop, kNotCompiledAmbiguousArrayOp, kNotCompiledHugeMethod, + kNotCompiledIrreducibleAndStringInit, kNotCompiledLargeMethodNoBranches, kNotCompiledMalformedOpcode, kNotCompiledNoCodegen, diff --git a/test/563-checker-fakestring/smali/TestCase.smali b/test/563-checker-fakestring/smali/TestCase.smali index adafb78bd7..8898c48ea1 100644 --- a/test/563-checker-fakestring/smali/TestCase.smali +++ b/test/563-checker-fakestring/smali/TestCase.smali @@ -133,17 +133,8 @@ .end method -# Test that the compiler does not assume that the first argument of String. -# is a NewInstance by inserting an irreducible loop between them (b/26676472). - -# We verify the type of the input instruction (Phi) in debuggable mode, because -# it is eliminated by later stages of SsaBuilder otherwise. - -## CHECK-START-DEBUGGABLE: java.lang.String TestCase.thisNotNewInstance1(byte[], boolean) register (after) -## CHECK-DAG: InvokeStaticOrDirect env:[[<>,{{.*]]}} -## CHECK-DAG: <> Phi - -.method public static thisNotNewInstance1([BZ)Ljava/lang/String; +# Test #1 for irreducible loops and String.. +.method public static irreducibleLoopAndStringInit1([BZ)Ljava/lang/String; .registers 5 new-instance v0, Ljava/lang/String; @@ -164,11 +155,8 @@ .end method -## CHECK-START-DEBUGGABLE: java.lang.String TestCase.thisNotNewInstance2(byte[], boolean) register (after) -## CHECK-DAG: InvokeStaticOrDirect env:[[<>,{{.*]]}} -## CHECK-DAG: <> Phi - -.method public static thisNotNewInstance2([BZ)Ljava/lang/String; +# Test #2 for irreducible loops and String.. +.method public static irreducibleLoopAndStringInit2([BZ)Ljava/lang/String; .registers 5 new-instance v0, Ljava/lang/String; @@ -188,3 +176,26 @@ return-object v0 .end method + +# Test #3 for irreducible loops and String. alias. +.method public static irreducibleLoopAndStringInit3([BZ)Ljava/lang/String; + .registers 5 + + new-instance v0, Ljava/lang/String; + move-object v2, v0 + + # Irreducible loop + if-eqz p1, :loop_entry + :loop_header + const v1, 0x1 + xor-int p1, p1, v1 + :loop_entry + if-eqz p1, :string_init + goto :loop_header + + :string_init + const-string v1, "UTF8" + invoke-direct {v0, p0, v1}, Ljava/lang/String;->([BLjava/lang/String;)V + return-object v2 + +.end method diff --git a/test/563-checker-fakestring/src/Main.java b/test/563-checker-fakestring/src/Main.java index 78cb37ae09..d38b7f4f23 100644 --- a/test/563-checker-fakestring/src/Main.java +++ b/test/563-checker-fakestring/src/Main.java @@ -65,14 +65,21 @@ public class Main { } { - Method m = c.getMethod("thisNotNewInstance1", byte[].class, boolean.class); + Method m = c.getMethod("irreducibleLoopAndStringInit1", byte[].class, boolean.class); String result = (String) m.invoke(null, new Object[] { testData, true }); assertEqual(testString, result); result = (String) m.invoke(null, new Object[] { testData, false }); assertEqual(testString, result); } { - Method m = c.getMethod("thisNotNewInstance2", byte[].class, boolean.class); + Method m = c.getMethod("irreducibleLoopAndStringInit2", byte[].class, boolean.class); + String result = (String) m.invoke(null, new Object[] { testData, true }); + assertEqual(testString, result); + result = (String) m.invoke(null, new Object[] { testData, false }); + assertEqual(testString, result); + } + { + Method m = c.getMethod("irreducibleLoopAndStringInit3", byte[].class, boolean.class); String result = (String) m.invoke(null, new Object[] { testData, true }); assertEqual(testString, result); result = (String) m.invoke(null, new Object[] { testData, false }); -- GitLab From 1f010164bea4714e9f653c52e8948c5d5305040b Mon Sep 17 00:00:00 2001 From: David Sehr Date: Tue, 15 May 2018 08:59:32 -0700 Subject: [PATCH 409/749] Move ArtDexFileLoader to libdexfile Adds a dependency from libdexfile on libartbase, but allows the use of MemMap loaders, etc. Bug: 78652467 Test: make -j 40 checkbuild Change-Id: I15cf33893ca9192050762f8350a3cc1e39f88dc5 --- adbconnection/Android.bp | 2 ++ compiler/Android.bp | 2 ++ dex2oat/Android.bp | 6 +++++- dexdump/Android.bp | 2 ++ dexlayout/Android.bp | 5 +++++ dexlist/Android.bp | 6 +++++- dexoptanalyzer/Android.bp | 2 ++ imgdiag/Android.bp | 2 ++ libdexfile/Android.bp | 15 ++++++--------- .../dex/art_dex_file_loader.cc | 0 {runtime => libdexfile}/dex/art_dex_file_loader.h | 6 +++--- .../dex/art_dex_file_loader_test.cc | 0 libprofile/Android.bp | 2 ++ oatdump/Android.bp | 4 ++++ openjdkjvm/Android.bp | 10 ++++++++-- openjdkjvmti/Android.bp | 2 ++ patchoat/Android.bp | 1 - profman/Android.bp | 2 ++ runtime/Android.bp | 6 ++---- simulator/Android.bp | 4 ++++ test/Android.bp | 9 +++++++++ test/etc/run-test-jar | 4 ++-- tools/cpp-define-generator/Android.bp | 1 + tools/dexanalyze/Android.bp | 1 + tools/hiddenapi/Android.bp | 2 ++ tools/public.libraries.buildbot.txt | 2 ++ tools/veridex/Android.bp | 6 +++++- 27 files changed, 80 insertions(+), 24 deletions(-) rename {runtime => libdexfile}/dex/art_dex_file_loader.cc (100%) rename {runtime => libdexfile}/dex/art_dex_file_loader.h (97%) rename {runtime => libdexfile}/dex/art_dex_file_loader_test.cc (100%) diff --git a/adbconnection/Android.bp b/adbconnection/Android.bp index 441b706556..95fc274286 100644 --- a/adbconnection/Android.bp +++ b/adbconnection/Android.bp @@ -65,6 +65,7 @@ art_cc_library { defaults: ["adbconnection-defaults"], shared_libs: [ "libart", + "libartbase", ], } @@ -76,5 +77,6 @@ art_cc_library { ], shared_libs: [ "libartd", + "libartbased", ], } diff --git a/compiler/Android.bp b/compiler/Android.bp index 5884a548be..be963fbbdb 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -246,6 +246,7 @@ art_cc_library { "libart", "libprofile", "libdexfile", + "libartbase", ], target: { @@ -295,6 +296,7 @@ art_cc_library { "libartd", "libprofiled", "libdexfiled", + "libartbased", ], } diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index 18548baf7f..e74947ad6a 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -202,6 +202,7 @@ art_cc_binary { "libart-dexlayout", "libart", "libdexfile", + "libartbase", "libbase", "liblz4", "libsigchain", @@ -239,6 +240,7 @@ art_cc_binary { "libartd-dexlayout", "libartd", "libdexfiled", + "libartbased", "libbase", "liblz4", "libsigchain", @@ -267,12 +269,13 @@ art_cc_binary { "-z muldefs", ], static_libs: [ - "libprofile", "libart-dex2oat", "libart-compiler", "libart-dexlayout", "libart", + "libartbase", "libdexfile", + "libprofile", "libvixl-arm", "libvixl-arm64", ] + art_static_dependencies, @@ -309,6 +312,7 @@ art_cc_binary { "libartd-compiler", "libartd-dexlayout", "libartd", + "libartbased", "libprofiled", "libdexfiled", "libvixld-arm", diff --git a/dexdump/Android.bp b/dexdump/Android.bp index c63d6c319e..2f0962c19a 100644 --- a/dexdump/Android.bp +++ b/dexdump/Android.bp @@ -35,6 +35,7 @@ art_cc_binary { host_supported: true, shared_libs: [ "libdexfile", + "libartbase", "libbase", ], } @@ -46,6 +47,7 @@ art_cc_binary { device_supported: false, static_libs: [ "libdexfile", + "libartbase", ] + art_static_dependencies, target: { darwin: { diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp index b009774582..bf56239397 100644 --- a/dexlayout/Android.bp +++ b/dexlayout/Android.bp @@ -41,6 +41,7 @@ art_cc_library { shared_libs: [ "libart", "libdexfile", + "libartbase", "libprofile", ], @@ -62,6 +63,7 @@ art_cc_library { shared_libs: [ "libartd", "libdexfiled", + "libartbased", "libprofiled", ], } @@ -82,6 +84,7 @@ art_cc_binary { shared_libs: [ "libprofile", "libart", + "libartbase", "libart-dexlayout", ], } @@ -95,6 +98,7 @@ art_cc_binary { shared_libs: [ "libprofiled", "libartd", + "libartbased", "libartd-dexlayout", ], } @@ -117,6 +121,7 @@ art_cc_binary { cflags: ["-Wall"], shared_libs: [ "libart", + "libartbase", "libart-dexlayout", "libbase", ], diff --git a/dexlist/Android.bp b/dexlist/Android.bp index 2703732db6..bd521acbc0 100644 --- a/dexlist/Android.bp +++ b/dexlist/Android.bp @@ -17,7 +17,11 @@ art_cc_binary { host_supported: true, srcs: ["dexlist.cc"], cflags: ["-Wall", "-Werror"], - shared_libs: ["libdexfile", "libbase"], + shared_libs: [ + "libdexfile", + "libartbase", + "libbase" + ], // TODO: fix b/72216369 and remove the need for this. include_dirs: [ "art/runtime" // dex utils. diff --git a/dexoptanalyzer/Android.bp b/dexoptanalyzer/Android.bp index 33366ad371..99a11cd59b 100644 --- a/dexoptanalyzer/Android.bp +++ b/dexoptanalyzer/Android.bp @@ -38,6 +38,7 @@ art_cc_binary { defaults: ["dexoptanalyzer-defaults"], shared_libs: [ "libart", + "libartbase", ], } @@ -49,6 +50,7 @@ art_cc_binary { ], shared_libs: [ "libartd", + "libartbased", ], } diff --git a/imgdiag/Android.bp b/imgdiag/Android.bp index 2b89497e8e..972c8f7189 100644 --- a/imgdiag/Android.bp +++ b/imgdiag/Android.bp @@ -57,6 +57,7 @@ art_cc_binary { defaults: ["imgdiag-defaults"], shared_libs: [ "libart", + "libartbase", "libart-compiler", ], } @@ -69,6 +70,7 @@ art_cc_binary { ], shared_libs: [ "libartd", + "libartbased", "libartd-compiler", ], } diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp index 9d49f371f8..3818624d7a 100644 --- a/libdexfile/Android.bp +++ b/libdexfile/Android.bp @@ -19,6 +19,7 @@ cc_defaults { defaults: ["art_defaults"], host_supported: true, srcs: [ + "dex/art_dex_file_loader.cc", "dex/compact_dex_file.cc", "dex/compact_offset_table.cc", "dex/descriptors_names.cc", @@ -55,25 +56,20 @@ cc_defaults { }, generated_sources: ["dexfile_operator_srcs"], shared_libs: [ + // For MemMap. "libartbase", - // Important note: relying on libartbase's header_lib is perfectly acceptable. - // However, relying on the libartbase shared library introduces further, possibly cyclic, - // dependencies for clients outside of ART. "liblog", + // For atrace. + "libcutils", // For common macros. "libbase", "libz", ], - header_libs: [ - "art_libartbase_headers", - ], export_include_dirs: ["."], export_shared_lib_headers: [ + "libartbase", "libbase", ], - export_header_lib_headers: [ - "art_libartbase_headers", - ], } gensrcs { @@ -115,6 +111,7 @@ art_cc_test { "art_gtest_defaults", ], srcs: [ + "dex/art_dex_file_loader_test.cc", "dex/code_item_accessors_test.cc", "dex/compact_dex_file_test.cc", "dex/compact_offset_table_test.cc", diff --git a/runtime/dex/art_dex_file_loader.cc b/libdexfile/dex/art_dex_file_loader.cc similarity index 100% rename from runtime/dex/art_dex_file_loader.cc rename to libdexfile/dex/art_dex_file_loader.cc diff --git a/runtime/dex/art_dex_file_loader.h b/libdexfile/dex/art_dex_file_loader.h similarity index 97% rename from runtime/dex/art_dex_file_loader.h rename to libdexfile/dex/art_dex_file_loader.h index 7577945632..a460aee60f 100644 --- a/runtime/dex/art_dex_file_loader.h +++ b/libdexfile/dex/art_dex_file_loader.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DEX_ART_DEX_FILE_LOADER_H_ -#define ART_RUNTIME_DEX_ART_DEX_FILE_LOADER_H_ +#ifndef ART_LIBDEXFILE_DEX_ART_DEX_FILE_LOADER_H_ +#define ART_LIBDEXFILE_DEX_ART_DEX_FILE_LOADER_H_ #include #include @@ -137,4 +137,4 @@ class ArtDexFileLoader : public DexFileLoader { } // namespace art -#endif // ART_RUNTIME_DEX_ART_DEX_FILE_LOADER_H_ +#endif // ART_LIBDEXFILE_DEX_ART_DEX_FILE_LOADER_H_ diff --git a/runtime/dex/art_dex_file_loader_test.cc b/libdexfile/dex/art_dex_file_loader_test.cc similarity index 100% rename from runtime/dex/art_dex_file_loader_test.cc rename to libdexfile/dex/art_dex_file_loader_test.cc diff --git a/libprofile/Android.bp b/libprofile/Android.bp index bcb90cb680..b9883f658d 100644 --- a/libprofile/Android.bp +++ b/libprofile/Android.bp @@ -45,6 +45,7 @@ cc_defaults { shared_libs: [ "libartbase", "libdexfile", + "libartbase", // For atrace. "libcutils", ], @@ -97,6 +98,7 @@ art_cc_test { shared_libs: [ "libartbased", "libdexfiled", + "libartbased", "libziparchive", ], } diff --git a/oatdump/Android.bp b/oatdump/Android.bp index 535acdff62..77dede3e10 100644 --- a/oatdump/Android.bp +++ b/oatdump/Android.bp @@ -39,6 +39,7 @@ art_cc_binary { "libart-compiler", "libart-disassembler", "libdexfile", + "libartbase", "libprofile", "libbase", ], @@ -55,6 +56,7 @@ art_cc_binary { "libartd-compiler", "libartd-disassembler", "libdexfiled", + "libartbased", "libprofiled", "libbase", ], @@ -82,6 +84,7 @@ art_cc_binary { "libart", "libdexfile", "libprofile", + "libartbase", "libart-compiler", "libart-disassembler", "libvixl-arm", @@ -117,6 +120,7 @@ art_cc_binary { "libartd", "libdexfiled", "libprofiled", + "libartbased", "libartd-compiler", "libartd-disassembler", "libvixld-arm", diff --git a/openjdkjvm/Android.bp b/openjdkjvm/Android.bp index a17899358c..907315e4e0 100644 --- a/openjdkjvm/Android.bp +++ b/openjdkjvm/Android.bp @@ -29,7 +29,10 @@ cc_defaults { art_cc_library { name: "libopenjdkjvm", defaults: ["libopenjdkjvm_defaults"], - shared_libs: ["libart"], + shared_libs: [ + "libart", + "libartbase", + ], } art_cc_library { @@ -38,5 +41,8 @@ art_cc_library { "art_debug_defaults", "libopenjdkjvm_defaults", ], - shared_libs: ["libartd"], + shared_libs: [ + "libartd", + "libartbased", + ], } diff --git a/openjdkjvmti/Android.bp b/openjdkjvmti/Android.bp index 81b69e8c95..d8902d60d1 100644 --- a/openjdkjvmti/Android.bp +++ b/openjdkjvmti/Android.bp @@ -71,6 +71,7 @@ art_cc_library { "libart-compiler", "libart-dexlayout", "libdexfile", + "libartbase", ], } @@ -85,5 +86,6 @@ art_cc_library { "libartd-compiler", "libartd-dexlayout", "libdexfiled", + "libartbased", ], } diff --git a/patchoat/Android.bp b/patchoat/Android.bp index 1e2f328ffd..13c8f475f5 100644 --- a/patchoat/Android.bp +++ b/patchoat/Android.bp @@ -59,7 +59,6 @@ art_cc_test { "patchoat_test.cc", ], shared_libs: [ - "libartd", "libcrypto", // For computing the digest of image file ], } diff --git a/profman/Android.bp b/profman/Android.bp index 3c8c72c34a..5aaccb0cef 100644 --- a/profman/Android.bp +++ b/profman/Android.bp @@ -42,6 +42,7 @@ art_cc_binary { "libart", "libprofile", "libdexfile", + "libartbase", ], } @@ -55,6 +56,7 @@ art_cc_binary { "libartd", "libprofiled", "libdexfiled", + "libartbased", ], } diff --git a/runtime/Android.bp b/runtime/Android.bp index 0540b2087e..92607f51a0 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -45,7 +45,6 @@ libart_cc_defaults { "compiler_filter.cc", "debug_print.cc", "debugger.cc", - "dex/art_dex_file_loader.cc", "dex/dex_file_annotations.cc", "dex_to_dex_decompiler.cc", "elf_file.cc", @@ -464,9 +463,9 @@ art_cc_library { keep_symbols: true, }, whole_static_libs: [ - "libartbase", ], shared_libs: [ + "libartbase", "libdexfile", "libprofile", ], @@ -489,9 +488,9 @@ art_cc_library { "libart_defaults", ], whole_static_libs: [ - "libartbased", ], shared_libs: [ + "libartbased", "libdexfiled", "libprofiled", ], @@ -545,7 +544,6 @@ art_cc_test { "class_loader_context_test.cc", "class_table_test.cc", "compiler_filter_test.cc", - "dex/art_dex_file_loader_test.cc", "entrypoints/math_entrypoints_test.cc", "entrypoints/quick/quick_trampoline_entrypoints_test.cc", "entrypoints_order_test.cc", diff --git a/simulator/Android.bp b/simulator/Android.bp index 74b5a900b4..8690426466 100644 --- a/simulator/Android.bp +++ b/simulator/Android.bp @@ -44,6 +44,7 @@ art_cc_library { defaults: ["libart_simulator_defaults"], shared_libs: [ "libart", + "libartbase", "libvixl-arm64", ], } @@ -56,6 +57,7 @@ art_cc_library { ], shared_libs: [ "libartd", + "libartbased", "libvixld-arm64", ], } @@ -80,6 +82,7 @@ art_cc_library { name: "libart-simulator-container", defaults: ["libart_simulator_container_defaults"], shared_libs: [ + "libartbase", "libart", ], } @@ -91,6 +94,7 @@ art_cc_library { "libart_simulator_container_defaults", ], shared_libs: [ + "libartbased", "libartd", ], } diff --git a/test/Android.bp b/test/Android.bp index 84f2e224d5..7909bf897a 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -64,6 +64,7 @@ art_cc_defaults { "libart-gtest", "libdexfiled", "libprofiled", + "libartbased", "libbase", "libicuuc", @@ -117,6 +118,7 @@ art_cc_defaults { "libartd-compiler", "libdexfiled", "libprofiled", + "libartbased", ], static_libs: [ "libgtest", @@ -156,6 +158,7 @@ art_cc_library { "libartd-compiler", "libdexfiled", "libprofiled", + "libartbased", "libbase", "libbacktrace", ], @@ -187,6 +190,7 @@ art_cc_test_library { "libart", "libdexfile", "libprofile", + "libartbase", ], } @@ -201,6 +205,7 @@ art_cc_test_library { "libartd", "libdexfiled", "libprofiled", + "libartbased", ], } @@ -322,6 +327,7 @@ art_cc_test_library { "libart", "libdexfile", "libprofile", + "libartbase", ], } @@ -335,6 +341,7 @@ art_cc_test_library { "libartd", "libdexfiled", "libprofiled", + "libartbased", ], } @@ -496,6 +503,7 @@ art_cc_test_library { "libart", "libdexfile", "libprofile", + "libartbase", ], } @@ -509,6 +517,7 @@ art_cc_test_library { "libartd", "libdexfiled", "libprofiled", + "libartbased", ], } diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index fad801192a..1ba433e974 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -861,9 +861,9 @@ if [ "$HOST" = "n" ]; then # System libraries needed by libarttestd.so PUBLIC_LIBS=libc++.so:libbacktrace.so:libbase.so:libnativehelper.so if [ "$TEST_IS_NDEBUG" = "y" ]; then - PUBLIC_LIBS=$PUBLIC_LIBS:libart.so:libdexfile.so:libprofile.so + PUBLIC_LIBS=$PUBLIC_LIBS:libart.so:libdexfile.so:libprofile.so:libartbase.so else - PUBLIC_LIBS=$PUBLIC_LIBS:libartd.so:libdexfiled.so:libprofiled.so + PUBLIC_LIBS=$PUBLIC_LIBS:libartd.so:libdexfiled.so:libprofiled.so:libartbased.so fi # Create a script with the command. The command can get longer than the longest diff --git a/tools/cpp-define-generator/Android.bp b/tools/cpp-define-generator/Android.bp index 39e57bd667..23cc917f1d 100644 --- a/tools/cpp-define-generator/Android.bp +++ b/tools/cpp-define-generator/Android.bp @@ -31,6 +31,7 @@ cc_binary { // Do not use art_cc_binary because HOST_PREFER_32_BIT is incompatib include_dirs: [ "art/libartbase", "art/libdexfile", + "art/libartbase", "art/runtime", ], srcs: ["main.cc"], diff --git a/tools/dexanalyze/Android.bp b/tools/dexanalyze/Android.bp index 2754e6445e..a229d73d01 100644 --- a/tools/dexanalyze/Android.bp +++ b/tools/dexanalyze/Android.bp @@ -37,6 +37,7 @@ art_cc_binary { defaults: ["dexanalyze-defaults"], shared_libs: [ "libdexfile", + "libartbase", "libbase", ], } diff --git a/tools/hiddenapi/Android.bp b/tools/hiddenapi/Android.bp index af87d31e53..3b364b67c3 100644 --- a/tools/hiddenapi/Android.bp +++ b/tools/hiddenapi/Android.bp @@ -40,6 +40,7 @@ art_cc_binary { shared_libs: [ "libart", "libdexfile", + "libartbase", ], } @@ -52,6 +53,7 @@ art_cc_binary { shared_libs: [ "libartd", "libdexfiled", + "libartbased", ], } diff --git a/tools/public.libraries.buildbot.txt b/tools/public.libraries.buildbot.txt index 6c8114558e..9b171a2502 100644 --- a/tools/public.libraries.buildbot.txt +++ b/tools/public.libraries.buildbot.txt @@ -1,5 +1,7 @@ libart.so libartd.so +libartbase.so +libartbased.so libdexfile.so libdexfiled.so libbacktrace.so diff --git a/tools/veridex/Android.bp b/tools/veridex/Android.bp index 570960c4da..5186c43ca2 100644 --- a/tools/veridex/Android.bp +++ b/tools/veridex/Android.bp @@ -24,7 +24,11 @@ cc_binary { "veridex.cc", ], cflags: ["-Wall", "-Werror"], - shared_libs: ["libdexfile", "libbase"], + shared_libs: [ + "libdexfile", + "libartbase", + "libbase", + ], header_libs: [ "art_libartbase_headers", ], -- GitLab From c5b7564fdc187853d1f8266a54316ef402f83e02 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 16 May 2018 15:12:11 -0700 Subject: [PATCH 410/749] ART: Fix jvalue includes JValue::SetL is defined in the -inl header. Fix up some clients. Test: mmma art Change-Id: I903532c767873847f7746680cf35083cb3408ecf --- runtime/common_dex_operations.h | 1 + runtime/interpreter/interpreter_common.cc | 2 +- runtime/jvalue-inl.h | 2 +- runtime/jvalue.h | 3 ++- runtime/method_handles-inl.h | 2 +- runtime/method_handles.cc | 1 - runtime/native/java_lang_reflect_Field.cc | 1 + runtime/reflection.cc | 1 + 8 files changed, 8 insertions(+), 5 deletions(-) diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h index 37e074d552..9c2a40b50d 100644 --- a/runtime/common_dex_operations.h +++ b/runtime/common_dex_operations.h @@ -29,6 +29,7 @@ #include "instrumentation.h" #include "interpreter/shadow_frame.h" #include "interpreter/unstarted_runtime.h" +#include "jvalue-inl.h" #include "mirror/class.h" #include "mirror/object.h" #include "obj_ptr-inl.h" diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index d30bc10450..5a50ec5586 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -24,7 +24,7 @@ #include "entrypoints/runtime_asm_entrypoints.h" #include "intrinsics_enum.h" #include "jit/jit.h" -#include "jvalue.h" +#include "jvalue-inl.h" #include "method_handles-inl.h" #include "method_handles.h" #include "mirror/array-inl.h" diff --git a/runtime/jvalue-inl.h b/runtime/jvalue-inl.h index 25e34b2a74..5bd4f176f6 100644 --- a/runtime/jvalue-inl.h +++ b/runtime/jvalue-inl.h @@ -19,7 +19,7 @@ #include "jvalue.h" -#include "obj_ptr.h" +#include "obj_ptr-inl.h" namespace art { diff --git a/runtime/jvalue.h b/runtime/jvalue.h index 266abcf399..b42d995d5c 100644 --- a/runtime/jvalue.h +++ b/runtime/jvalue.h @@ -33,7 +33,7 @@ union PACKED(alignof(mirror::Object*)) JValue { // We default initialize JValue instances to all-zeros. JValue() : j(0) {} - template static JValue FromPrimitive(T v); + template ALWAYS_INLINE static JValue FromPrimitive(T v); int8_t GetB() const { return b; } void SetB(int8_t new_b) { @@ -62,6 +62,7 @@ union PACKED(alignof(mirror::Object*)) JValue { mirror::Object* GetL() const REQUIRES_SHARED(Locks::mutator_lock_) { return l; } + ALWAYS_INLINE void SetL(ObjPtr new_l) REQUIRES_SHARED(Locks::mutator_lock_); int16_t GetS() const { return s; } diff --git a/runtime/method_handles-inl.h b/runtime/method_handles-inl.h index 41c8384e0d..00a8c00880 100644 --- a/runtime/method_handles-inl.h +++ b/runtime/method_handles-inl.h @@ -22,7 +22,7 @@ #include "common_throws.h" #include "dex/dex_instruction.h" #include "interpreter/interpreter_common.h" -#include "jvalue.h" +#include "jvalue-inl.h" #include "mirror/class.h" #include "mirror/method_type.h" #include "mirror/object.h" diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 64ab78997f..1d45aaeb2e 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -20,7 +20,6 @@ #include "common_dex_operations.h" #include "jvalue-inl.h" -#include "jvalue.h" #include "mirror/emulated_stack_frame.h" #include "mirror/method_handle_impl-inl.h" #include "mirror/method_type.h" diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index e0afbee845..25599843e9 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -27,6 +27,7 @@ #include "dex/dex_file-inl.h" #include "dex/dex_file_annotations.h" #include "jni/jni_internal.h" +#include "jvalue-inl.h" #include "mirror/class-inl.h" #include "mirror/field-inl.h" #include "native_util.h" diff --git a/runtime/reflection.cc b/runtime/reflection.cc index dfa4b3daab..66eba1e1d4 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -25,6 +25,7 @@ #include "indirect_reference_table-inl.h" #include "jni/java_vm_ext.h" #include "jni/jni_internal.h" +#include "jvalue-inl.h" #include "mirror/class-inl.h" #include "mirror/executable.h" #include "mirror/object_array-inl.h" -- GitLab From dc2578172bd3eee7be97d6eba32f435c16e601ac Mon Sep 17 00:00:00 2001 From: Alex Light Date: Wed, 16 May 2018 16:37:07 -0700 Subject: [PATCH 411/749] Fix typo in tifast.cc Had METHOD_ENTRY instead of METHOD_EXIT in one of the event listings. Test: m -j50 libtifast adb push $OUT/system/lib64/libtifast.so /data/local/tmp adb shell setenforce 0 adb shell am start-activity \ --attach-agent /data/local/tmp/libtifast.so=log,MethodExit \ com.antonioleiva.bandhookkotlin/.ui.screens.main.MainActivity Examine logcat for MethodExit. Change-Id: I3879da37da19743012643d13ae5b14c130611857 --- tools/ti-fast/tifast.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ti-fast/tifast.cc b/tools/ti-fast/tifast.cc index 952fba8720..428304e517 100644 --- a/tools/ti-fast/tifast.cc +++ b/tools/ti-fast/tifast.cc @@ -62,7 +62,7 @@ static void AddCapsForEvent(jvmtiEvent event, jvmtiCapabilities* caps) { #define FOR_ALL_SUPPORTED_EVENTS(fun) \ fun(SingleStep, EVENT(SINGLE_STEP), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation)) \ fun(MethodEntry, EVENT(METHOD_ENTRY), (jvmtiEnv*, JNIEnv*, jthread, jmethodID)) \ - fun(MethodExit, EVENT(METHOD_ENTRY), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jboolean, jvalue)) \ + fun(MethodExit, EVENT(METHOD_EXIT), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jboolean, jvalue)) \ fun(NativeMethodBind, EVENT(NATIVE_METHOD_BIND), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, void*, void**)) \ fun(Exception, EVENT(EXCEPTION), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation, jobject, jmethodID, jlocation)) \ fun(ExceptionCatch, EVENT(EXCEPTION_CATCH), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation, jobject)) \ -- GitLab From 35ddc6fc26594326ddfddeac447e7aa1321726ff Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 9 May 2018 11:34:07 -0700 Subject: [PATCH 412/749] Add some experiments to analyze string usage Analyze shared prefixes for strings across dex files and estimate possible savings from supporting a per string prefix. The estimation is not yet optimal. Bug: 77709234 Test: dexanalyze Change-Id: I2e9f8a09595b54ea4a3e331efde32f9c1689fc82 --- tools/dexanalyze/dexanalyze.cc | 10 ++- tools/dexanalyze/dexanalyze_experiments.cc | 82 +++++++++++++++++++++- tools/dexanalyze/dexanalyze_experiments.h | 21 +++++- 3 files changed, 108 insertions(+), 5 deletions(-) diff --git a/tools/dexanalyze/dexanalyze.cc b/tools/dexanalyze/dexanalyze.cc index c4aebc6f56..0b08beed70 100644 --- a/tools/dexanalyze/dexanalyze.cc +++ b/tools/dexanalyze/dexanalyze.cc @@ -63,6 +63,8 @@ class DexAnalyze { run_all_experiments_ = true; } else if (arg == "-count-indices") { exp_count_indices_ = true; + } else if (arg == "-analyze-strings") { + exp_analyze_strings_ = true; } else if (arg == "-d") { dump_per_input_dex_ = true; } else if (!arg.empty() && arg[0] == '-') { @@ -82,6 +84,7 @@ class DexAnalyze { bool run_dex_file_verifier_ = true; bool dump_per_input_dex_ = false; bool exp_count_indices_ = false; + bool exp_analyze_strings_ = false; bool run_all_experiments_ = false; std::vector filenames_; }; @@ -92,25 +95,30 @@ class DexAnalyze { if (options->run_all_experiments_ || options->exp_count_indices_) { experiments_.emplace_back(new CountDexIndices); } + if (options->run_all_experiments_ || options->exp_analyze_strings_) { + experiments_.emplace_back(new AnalyzeStrings); + } } bool ProcessDexFile(const DexFile& dex_file) { for (std::unique_ptr& experiment : experiments_) { experiment->ProcessDexFile(dex_file); } + total_size_ += dex_file.Size(); ++dex_count_; return true; } void Dump(std::ostream& os) { for (std::unique_ptr& experiment : experiments_) { - experiment->Dump(os); + experiment->Dump(os, total_size_); } } const Options* const options_; std::vector> experiments_; size_t dex_count_ = 0; + uint64_t total_size_ = 0u; }; public: diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc index e1f119df59..bfeb4b9d72 100644 --- a/tools/dexanalyze/dexanalyze_experiments.cc +++ b/tools/dexanalyze/dexanalyze_experiments.cc @@ -15,12 +15,91 @@ */ #include "dexanalyze_experiments.h" + +#include +#include +#include +#include +#include + +#include "android-base/stringprintf.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_instruction-inl.h" #include "dex/standard_dex_file.h" namespace art { +std::string Percent(uint64_t value, uint64_t max) { + if (max == 0) { + ++max; + } + return android::base::StringPrintf("%" PRId64 "(%.2f%%)", + value, + static_cast(value * 100) / static_cast(max)); +} + +static size_t PrefixLen(const std::string& a, const std::string& b) { + size_t len = 0; + for (; len < a.length() && len < b.length() && a[len] == b[len]; ++len) {} + return len; +} + +void AnalyzeStrings::ProcessDexFile(const DexFile& dex_file) { + std::vector strings; + for (size_t i = 0; i < dex_file.NumStringIds(); ++i) { + uint32_t length = 0; + const char* data = + dex_file.GetStringDataAndUtf16Length(dex_file.GetStringId(dex::StringIndex(i)), &length); + strings.push_back(data); + } + // Note that the strings are probably already sorted. + std::sort(strings.begin(), strings.end()); + + // Tunable parameters. + static const size_t kMinPrefixLen = 3; + static const size_t kPrefixConstantCost = 5; + static const size_t kPrefixIndexCost = 2; + + // Calculate total shared prefix. + std::vector shared_len; + std::set prefixes; + for (size_t i = 0; i < strings.size(); ++i) { + size_t best_len = 0; + if (i > 0) { + best_len = std::max(best_len, PrefixLen(strings[i], strings[i - 1])); + } + if (i < strings.size() - 1) { + best_len = std::max(best_len, PrefixLen(strings[i], strings[i + 1])); + } + std::string prefix; + if (best_len >= kMinPrefixLen) { + prefix = strings[i].substr(0, best_len); + prefixes.insert(prefix); + total_prefix_savings_ += prefix.length(); + } + total_prefix_index_cost_ += kPrefixIndexCost; + } + total_num_prefixes_ += prefixes.size(); + for (const std::string& s : prefixes) { + // 4 bytes for an offset, one for length. + total_prefix_dict_ += s.length(); + total_prefix_table_ += kPrefixConstantCost; + } +} + +void AnalyzeStrings::Dump(std::ostream& os, uint64_t total_size) const { + os << "Total shared prefix bytes " << Percent(total_prefix_savings_, total_size) << "\n"; + os << "Prefix dictionary cost " << Percent(total_prefix_dict_, total_size) << "\n"; + os << "Prefix table cost " << Percent(total_prefix_table_, total_size) << "\n"; + os << "Prefix index cost " << Percent(total_prefix_index_cost_, total_size) << "\n"; + int64_t net_savings = total_prefix_savings_; + net_savings -= total_prefix_dict_; + net_savings -= total_prefix_table_; + net_savings -= total_prefix_index_cost_; + os << "Prefix net savings " << Percent(net_savings, total_size) << "\n"; + os << "Prefix dictionary elements " << total_num_prefixes_ << "\n"; +} + void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { num_string_ids_ += dex_file.NumStringIds(); num_method_ids_ += dex_file.NumMethodIds(); @@ -107,7 +186,7 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { } } -void CountDexIndices::Dump(std::ostream& os) const { +void CountDexIndices::Dump(std::ostream& os, uint64_t total_size) const { os << "Num string ids: " << num_string_ids_ << "\n"; os << "Num method ids: " << num_method_ids_ << "\n"; os << "Num field ids: " << num_field_ids_ << "\n"; @@ -127,6 +206,7 @@ void CountDexIndices::Dump(std::ostream& os) const { os << "Same class invoke: " << same_class_total << "\n"; os << "Other class invoke: " << other_class_total << "\n"; os << "Invokes from code: " << (same_class_total + other_class_total) << "\n"; + os << "Total dex size: " << total_size << "\n"; } } // namespace art diff --git a/tools/dexanalyze/dexanalyze_experiments.h b/tools/dexanalyze/dexanalyze_experiments.h index 5d0f51b821..6f70f5d166 100644 --- a/tools/dexanalyze/dexanalyze_experiments.h +++ b/tools/dexanalyze/dexanalyze_experiments.h @@ -24,12 +24,28 @@ namespace art { class DexFile; +std::string Percent(uint64_t value, uint64_t max); + // An experiment a stateful visitor that runs on dex files. Results are cumulative. class Experiment { public: virtual ~Experiment() {} virtual void ProcessDexFile(const DexFile& dex_file) = 0; - virtual void Dump(std::ostream& os) const = 0; + virtual void Dump(std::ostream& os, uint64_t total_size) const = 0; +}; + +// Analyze string data and strings accessed from code. +class AnalyzeStrings : public Experiment { + public: + void ProcessDexFile(const DexFile& dex_file); + void Dump(std::ostream& os, uint64_t total_size) const; + + private: + int64_t total_prefix_savings_ = 0u; + int64_t total_prefix_dict_ = 0u; + int64_t total_prefix_table_ = 0u; + int64_t total_prefix_index_cost_ = 0u; + int64_t total_num_prefixes_ = 0u; }; // Count numbers of dex indices. @@ -37,7 +53,7 @@ class CountDexIndices : public Experiment { public: void ProcessDexFile(const DexFile& dex_file); - void Dump(std::ostream& os) const; + void Dump(std::ostream& os, uint64_t total_size) const; private: // Total string ids loaded from dex code. @@ -65,4 +81,3 @@ class CountDexIndices : public Experiment { } // namespace art #endif // ART_TOOLS_DEXANALYZE_DEXANALYZE_EXPERIMENTS_H_ - -- GitLab From cc05247bf7ca217bc826951091f95b121b275543 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Wed, 16 May 2018 17:12:44 +0100 Subject: [PATCH 413/749] ART: minor clean-up for var_handles.cc Test: art/test.py --host --64 -r -t 712 Change-Id: Ib05064008c50cca21d3f900cbb966ea4c6bdfc1c --- runtime/var_handles.cc | 68 +++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/runtime/var_handles.cc b/runtime/var_handles.cc index d71745e531..e6730c6114 100644 --- a/runtime/var_handles.cc +++ b/runtime/var_handles.cc @@ -25,6 +25,39 @@ namespace art { +namespace { + +bool VarHandleInvokeAccessorWithConversions(Thread* self, + ShadowFrame& shadow_frame, + Handle var_handle, + Handle callsite_type, + const mirror::VarHandle::AccessMode access_mode, + const InstructionOperands* const operands, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + StackHandleScope<1> hs(self); + Handle accessor_type(hs.NewHandle( + var_handle->GetMethodTypeForAccessMode(self, access_mode))); + const size_t num_vregs = accessor_type->NumberOfVRegs(); + const int num_params = accessor_type->GetPTypes()->GetLength(); + ShadowFrameAllocaUniquePtr accessor_frame = + CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC()); + ShadowFrameGetter getter(shadow_frame, operands); + static const uint32_t kFirstDestinationReg = 0; + ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg); + if (!PerformConversions(self, callsite_type, accessor_type, &getter, &setter, num_params)) { + return false; + } + RangeInstructionOperands accessor_operands(kFirstDestinationReg, + kFirstDestinationReg + num_vregs); + if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) { + return false; + } + return ConvertReturnValue(callsite_type, accessor_type, result); +} + +} // namespace + bool VarHandleInvokeAccessor(Thread* self, ShadowFrame& shadow_frame, Handle var_handle, @@ -44,35 +77,22 @@ bool VarHandleInvokeAccessor(Thread* self, mirror::VarHandle::MatchKind match_kind = var_handle->GetMethodTypeMatchForAccessMode(access_mode, callsite_type.Get()); - if (match_kind == mirror::VarHandle::MatchKind::kExact) { + if (LIKELY(match_kind == mirror::VarHandle::MatchKind::kExact)) { return var_handle->Access(access_mode, &shadow_frame, operands, result); - } - if (match_kind == mirror::VarHandle::MatchKind::kNone) { + } else if (match_kind == mirror::VarHandle::MatchKind::kWithConversions) { + return VarHandleInvokeAccessorWithConversions(self, + shadow_frame, + var_handle, + callsite_type, + access_mode, + operands, + result); + } else { + DCHECK_EQ(match_kind, mirror::VarHandle::MatchKind::kNone); ThrowWrongMethodTypeException(var_handle->GetMethodTypeForAccessMode(self, access_mode), callsite_type.Get()); return false; } - DCHECK_EQ(mirror::VarHandle::MatchKind::kWithConversions, match_kind); - - StackHandleScope<1> hs(self); - Handle accessor_type(hs.NewHandle( - var_handle->GetMethodTypeForAccessMode(self, access_mode))); - const size_t num_vregs = accessor_type->NumberOfVRegs(); - const int num_params = accessor_type->GetPTypes()->GetLength(); - ShadowFrameAllocaUniquePtr accessor_frame = - CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC()); - ShadowFrameGetter getter(shadow_frame, operands); - static const uint32_t kFirstDestinationReg = 0; - ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg); - if (!PerformConversions(self, callsite_type, accessor_type, &getter, &setter, num_params)) { - return false; - } - RangeInstructionOperands accessor_operands(kFirstDestinationReg, - kFirstDestinationReg + num_vregs); - if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) { - return false; - } - return ConvertReturnValue(callsite_type, accessor_type, result); } } // namespace art -- GitLab From 47b95800f25c26fa92ede5eccb0216bddd6aa3b2 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 16 May 2018 15:42:17 +0100 Subject: [PATCH 414/749] Add a new thread pool pthread priority option. Also refactor code to not duplicate information. bug: 79570024 Test: m Change-Id: Ib5b8c980d34b2ae92530825fceab89fa0e32baa5 --- runtime/jit/jit.cc | 79 +++++-------- runtime/jit/jit.h | 229 +++++++++++++++++++----------------- runtime/parsed_options.cc | 3 + runtime/runtime_options.def | 1 + 4 files changed, 160 insertions(+), 152 deletions(-) diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 0684b461ae..86e69f49a4 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -46,8 +46,6 @@ namespace art { namespace jit { static constexpr bool kEnableOnStackReplacement = true; -// At what priority to schedule jit threads. 9 is the lowest foreground priority on device. -static constexpr int kJitPoolThreadPthreadPriority = 9; // Different compilation threshold constants. These can be overridden on the command line. static constexpr size_t kJitDefaultCompileThreshold = 10000; // Non-debug default. @@ -80,6 +78,8 @@ JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& opt options.Exists(RuntimeArgumentMap::DumpJITInfoOnShutdown); jit_options->profile_saver_options_ = options.GetOrDefault(RuntimeArgumentMap::ProfileSaverOpts); + jit_options->thread_pool_pthread_priority_ = + options.GetOrDefault(RuntimeArgumentMap::JITPoolThreadPthreadPriority); if (options.Exists(RuntimeArgumentMap::JITCompileThreshold)) { jit_options->compile_threshold_ = *options.Get(RuntimeArgumentMap::JITCompileThreshold); @@ -167,21 +167,14 @@ void Jit::AddTimingLogger(const TimingLogger& logger) { cumulative_timings_.AddLogger(logger); } -Jit::Jit() : dump_info_on_shutdown_(false), - cumulative_timings_("JIT timings"), - memory_use_("Memory used for compilation", 16), - lock_("JIT memory use lock"), - use_jit_compilation_(true), - hot_method_threshold_(0), - warm_method_threshold_(0), - osr_method_threshold_(0), - priority_thread_weight_(0), - invoke_transition_weight_(0) {} +Jit::Jit(JitOptions* options) : options_(options), + cumulative_timings_("JIT timings"), + memory_use_("Memory used for compilation", 16), + lock_("JIT memory use lock") {} Jit* Jit::Create(JitOptions* options, std::string* error_msg) { DCHECK(options->UseJitCompilation() || options->GetProfileSaverOptions().IsEnabled()); - std::unique_ptr jit(new Jit); - jit->dump_info_on_shutdown_ = options->DumpJitInfoOnShutdown(); + std::unique_ptr jit(new Jit(options)); if (jit_compiler_handle_ == nullptr && !LoadCompiler(error_msg)) { return nullptr; } @@ -195,8 +188,6 @@ Jit* Jit::Create(JitOptions* options, std::string* error_msg) { if (jit->GetCodeCache() == nullptr) { return nullptr; } - jit->use_jit_compilation_ = options->UseJitCompilation(); - jit->profile_saver_options_ = options->GetProfileSaverOptions(); VLOG(jit) << "JIT created with initial_capacity=" << PrettySize(options->GetCodeCacheInitialCapacity()) << ", max_capacity=" << PrettySize(options->GetCodeCacheMaxCapacity()) @@ -204,12 +195,6 @@ Jit* Jit::Create(JitOptions* options, std::string* error_msg) { << ", profile_saver_options=" << options->GetProfileSaverOptions(); - jit->hot_method_threshold_ = options->GetCompileThreshold(); - jit->warm_method_threshold_ = options->GetWarmupThreshold(); - jit->osr_method_threshold_ = options->GetOsrThreshold(); - jit->priority_thread_weight_ = options->GetPriorityThreadWeight(); - jit->invoke_transition_weight_ = options->GetInvokeTransitionWeight(); - jit->CreateThreadPool(); // Notify native debugger about the classes already loaded before the creation of the jit. @@ -330,7 +315,7 @@ void Jit::CreateThreadPool() { constexpr bool kJitPoolNeedsPeers = true; thread_pool_.reset(new ThreadPool("Jit thread pool", 1, kJitPoolNeedsPeers)); - thread_pool_->SetPthreadPriority(kJitPoolThreadPthreadPriority); + thread_pool_->SetPthreadPriority(options_->GetThreadPoolPthreadPriority()); Start(); } @@ -360,8 +345,8 @@ void Jit::DeleteThreadPool() { void Jit::StartProfileSaver(const std::string& filename, const std::vector& code_paths) { - if (profile_saver_options_.IsEnabled()) { - ProfileSaver::Start(profile_saver_options_, + if (options_->GetSaveProfilingInfo()) { + ProfileSaver::Start(options_->GetProfileSaverOptions(), filename, code_cache_.get(), code_paths); @@ -369,8 +354,8 @@ void Jit::StartProfileSaver(const std::string& filename, } void Jit::StopProfileSaver() { - if (profile_saver_options_.IsEnabled() && ProfileSaver::IsStarted()) { - ProfileSaver::Stop(dump_info_on_shutdown_); + if (options_->GetSaveProfilingInfo() && ProfileSaver::IsStarted()) { + ProfileSaver::Stop(options_->DumpJitInfoOnShutdown()); } } @@ -383,8 +368,8 @@ bool Jit::CanInvokeCompiledCode(ArtMethod* method) { } Jit::~Jit() { - DCHECK(!profile_saver_options_.IsEnabled() || !ProfileSaver::IsStarted()); - if (dump_info_on_shutdown_) { + DCHECK(!options_->GetSaveProfilingInfo() || !ProfileSaver::IsStarted()); + if (options_->DumpJitInfoOnShutdown()) { DumpInfo(LOG_STREAM(INFO)); Runtime::Current()->DumpDeoptimizations(LOG_STREAM(INFO)); } @@ -671,25 +656,25 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ if (IgnoreSamplesForMethod(method)) { return; } - if (hot_method_threshold_ == 0) { + if (HotMethodThreshold() == 0) { // Tests might request JIT on first use (compiled synchronously in the interpreter). return; } DCHECK(thread_pool_ != nullptr); - DCHECK_GT(warm_method_threshold_, 0); - DCHECK_GT(hot_method_threshold_, warm_method_threshold_); - DCHECK_GT(osr_method_threshold_, hot_method_threshold_); - DCHECK_GE(priority_thread_weight_, 1); - DCHECK_LE(priority_thread_weight_, hot_method_threshold_); + DCHECK_GT(WarmMethodThreshold(), 0); + DCHECK_GT(HotMethodThreshold(), WarmMethodThreshold()); + DCHECK_GT(OSRMethodThreshold(), HotMethodThreshold()); + DCHECK_GE(PriorityThreadWeight(), 1); + DCHECK_LE(PriorityThreadWeight(), HotMethodThreshold()); - int32_t starting_count = method->GetCounter(); + uint16_t starting_count = method->GetCounter(); if (Jit::ShouldUsePriorityThreadWeight(self)) { - count *= priority_thread_weight_; + count *= PriorityThreadWeight(); } - int32_t new_count = starting_count + count; // int32 here to avoid wrap-around; + uint32_t new_count = starting_count + count; // Note: Native method have no "warm" state or profiling info. - if (LIKELY(!method->IsNative()) && starting_count < warm_method_threshold_) { - if ((new_count >= warm_method_threshold_) && + if (LIKELY(!method->IsNative()) && starting_count < WarmMethodThreshold()) { + if ((new_count >= WarmMethodThreshold()) && (method->GetProfilingInfo(kRuntimePointerSize) == nullptr)) { bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false); if (success) { @@ -710,23 +695,23 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ } } // Avoid jumping more than one state at a time. - new_count = std::min(new_count, hot_method_threshold_ - 1); - } else if (use_jit_compilation_) { - if (starting_count < hot_method_threshold_) { - if ((new_count >= hot_method_threshold_) && + new_count = std::min(new_count, static_cast(HotMethodThreshold() - 1)); + } else if (UseJitCompilation()) { + if (starting_count < HotMethodThreshold()) { + if ((new_count >= HotMethodThreshold()) && !code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) { DCHECK(thread_pool_ != nullptr); thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile)); } // Avoid jumping more than one state at a time. - new_count = std::min(new_count, osr_method_threshold_ - 1); - } else if (starting_count < osr_method_threshold_) { + new_count = std::min(new_count, static_cast(OSRMethodThreshold() - 1)); + } else if (starting_count < OSRMethodThreshold()) { if (!with_backedges) { // If the samples don't contain any back edge, we don't increment the hotness. return; } DCHECK(!method->IsNative()); // No back edges reported for native methods. - if ((new_count >= osr_method_threshold_) && !code_cache_->IsOsrCompiled(method)) { + if ((new_count >= OSRMethodThreshold()) && !code_cache_->IsOsrCompiled(method)) { DCHECK(thread_pool_ != nullptr); thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr)); } diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index 4b8b8919d1..edaf348cc4 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -44,6 +44,110 @@ class JitOptions; static constexpr int16_t kJitCheckForOSR = -1; static constexpr int16_t kJitHotnessDisabled = -2; +// At what priority to schedule jit threads. 9 is the lowest foreground priority on device. +// See android/os/Process.java. +static constexpr int kJitPoolThreadPthreadDefaultPriority = 9; + +class JitOptions { + public: + static JitOptions* CreateFromRuntimeArguments(const RuntimeArgumentMap& options); + + uint16_t GetCompileThreshold() const { + return compile_threshold_; + } + + uint16_t GetWarmupThreshold() const { + return warmup_threshold_; + } + + uint16_t GetOsrThreshold() const { + return osr_threshold_; + } + + uint16_t GetPriorityThreadWeight() const { + return priority_thread_weight_; + } + + uint16_t GetInvokeTransitionWeight() const { + return invoke_transition_weight_; + } + + size_t GetCodeCacheInitialCapacity() const { + return code_cache_initial_capacity_; + } + + size_t GetCodeCacheMaxCapacity() const { + return code_cache_max_capacity_; + } + + bool DumpJitInfoOnShutdown() const { + return dump_info_on_shutdown_; + } + + const ProfileSaverOptions& GetProfileSaverOptions() const { + return profile_saver_options_; + } + + bool GetSaveProfilingInfo() const { + return profile_saver_options_.IsEnabled(); + } + + int GetThreadPoolPthreadPriority() const { + return thread_pool_pthread_priority_; + } + + bool UseJitCompilation() const { + return use_jit_compilation_; + } + + void SetUseJitCompilation(bool b) { + use_jit_compilation_ = b; + } + + void SetSaveProfilingInfo(bool save_profiling_info) { + profile_saver_options_.SetEnabled(save_profiling_info); + } + + void SetWaitForJitNotificationsToSaveProfile(bool value) { + profile_saver_options_.SetWaitForJitNotificationsToSave(value); + } + + void SetProfileAOTCode(bool value) { + profile_saver_options_.SetProfileAOTCode(value); + } + + void SetJitAtFirstUse() { + use_jit_compilation_ = true; + compile_threshold_ = 0; + } + + private: + bool use_jit_compilation_; + size_t code_cache_initial_capacity_; + size_t code_cache_max_capacity_; + uint16_t compile_threshold_; + uint16_t warmup_threshold_; + uint16_t osr_threshold_; + uint16_t priority_thread_weight_; + uint16_t invoke_transition_weight_; + bool dump_info_on_shutdown_; + int thread_pool_pthread_priority_; + ProfileSaverOptions profile_saver_options_; + + JitOptions() + : use_jit_compilation_(false), + code_cache_initial_capacity_(0), + code_cache_max_capacity_(0), + compile_threshold_(0), + warmup_threshold_(0), + osr_threshold_(0), + priority_thread_weight_(0), + invoke_transition_weight_(0), + dump_info_on_shutdown_(false), + thread_pool_pthread_priority_(kJitPoolThreadPthreadDefaultPriority) {} + + DISALLOW_COPY_AND_ASSIGN(JitOptions); +}; class Jit { public: @@ -77,29 +181,29 @@ class Jit { REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); - size_t OSRMethodThreshold() const { - return osr_method_threshold_; + uint16_t OSRMethodThreshold() const { + return options_->GetOsrThreshold(); } - size_t HotMethodThreshold() const { - return hot_method_threshold_; + uint16_t HotMethodThreshold() const { + return options_->GetCompileThreshold(); } - size_t WarmMethodThreshold() const { - return warm_method_threshold_; + uint16_t WarmMethodThreshold() const { + return options_->GetWarmupThreshold(); } uint16_t PriorityThreadWeight() const { - return priority_thread_weight_; + return options_->GetPriorityThreadWeight(); } // Returns false if we only need to save profile information and not compile methods. bool UseJitCompilation() const { - return use_jit_compilation_; + return options_->UseJitCompilation(); } bool GetSaveProfilingInfo() const { - return profile_saver_options_.IsEnabled(); + return options_->GetSaveProfilingInfo(); } // Wait until there is no more pending compilation tasks. @@ -120,12 +224,12 @@ class Jit { void NotifyInterpreterToCompiledCodeTransition(Thread* self, ArtMethod* caller) REQUIRES_SHARED(Locks::mutator_lock_) { - AddSamples(self, caller, invoke_transition_weight_, false); + AddSamples(self, caller, options_->GetInvokeTransitionWeight(), false); } void NotifyCompiledCodeToInterpreterTransition(Thread* self, ArtMethod* callee) REQUIRES_SHARED(Locks::mutator_lock_) { - AddSamples(self, callee, invoke_transition_weight_, false); + AddSamples(self, callee, options_->GetInvokeTransitionWeight(), false); } // Starts the profile saver if the config options allow profile recording. @@ -177,7 +281,7 @@ class Jit { void Start(); private: - Jit(); + explicit Jit(JitOptions* options); static bool LoadCompiler(std::string* error_msg); @@ -189,107 +293,22 @@ class Jit { static bool (*jit_compile_method_)(void*, ArtMethod*, Thread*, bool); static void (*jit_types_loaded_)(void*, mirror::Class**, size_t count); + // We make this static to simplify the interaction with libart-compiler.so. + static bool generate_debug_info_; + + const JitOptions* const options_; + + std::unique_ptr code_cache_; + std::unique_ptr thread_pool_; + // Performance monitoring. - bool dump_info_on_shutdown_; CumulativeLogger cumulative_timings_; Histogram memory_use_ GUARDED_BY(lock_); Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - std::unique_ptr code_cache_; - - bool use_jit_compilation_; - ProfileSaverOptions profile_saver_options_; - static bool generate_debug_info_; - uint16_t hot_method_threshold_; - uint16_t warm_method_threshold_; - uint16_t osr_method_threshold_; - uint16_t priority_thread_weight_; - uint16_t invoke_transition_weight_; - std::unique_ptr thread_pool_; - DISALLOW_COPY_AND_ASSIGN(Jit); }; -class JitOptions { - public: - static JitOptions* CreateFromRuntimeArguments(const RuntimeArgumentMap& options); - size_t GetCompileThreshold() const { - return compile_threshold_; - } - size_t GetWarmupThreshold() const { - return warmup_threshold_; - } - size_t GetOsrThreshold() const { - return osr_threshold_; - } - uint16_t GetPriorityThreadWeight() const { - return priority_thread_weight_; - } - size_t GetInvokeTransitionWeight() const { - return invoke_transition_weight_; - } - size_t GetCodeCacheInitialCapacity() const { - return code_cache_initial_capacity_; - } - size_t GetCodeCacheMaxCapacity() const { - return code_cache_max_capacity_; - } - bool DumpJitInfoOnShutdown() const { - return dump_info_on_shutdown_; - } - const ProfileSaverOptions& GetProfileSaverOptions() const { - return profile_saver_options_; - } - bool GetSaveProfilingInfo() const { - return profile_saver_options_.IsEnabled(); - } - bool UseJitCompilation() const { - return use_jit_compilation_; - } - void SetUseJitCompilation(bool b) { - use_jit_compilation_ = b; - } - void SetSaveProfilingInfo(bool save_profiling_info) { - profile_saver_options_.SetEnabled(save_profiling_info); - } - void SetWaitForJitNotificationsToSaveProfile(bool value) { - profile_saver_options_.SetWaitForJitNotificationsToSave(value); - } - void SetProfileAOTCode(bool value) { - profile_saver_options_.SetProfileAOTCode(value); - } - - void SetJitAtFirstUse() { - use_jit_compilation_ = true; - compile_threshold_ = 0; - } - - private: - bool use_jit_compilation_; - size_t code_cache_initial_capacity_; - size_t code_cache_max_capacity_; - size_t compile_threshold_; - size_t warmup_threshold_; - size_t osr_threshold_; - uint16_t priority_thread_weight_; - size_t invoke_transition_weight_; - bool dump_info_on_shutdown_; - ProfileSaverOptions profile_saver_options_; - - JitOptions() - : use_jit_compilation_(false), - code_cache_initial_capacity_(0), - code_cache_max_capacity_(0), - compile_threshold_(0), - warmup_threshold_(0), - osr_threshold_(0), - priority_thread_weight_(0), - invoke_transition_weight_(0), - dump_info_on_shutdown_(false) {} - - DISALLOW_COPY_AND_ASSIGN(JitOptions); -}; - // Helper class to stop the JIT for a given scope. This will wait for the JIT to quiesce. class ScopedJitSuspend { public: diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 3aa481af8c..7383d477bb 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -194,6 +194,9 @@ std::unique_ptr ParsedOptions::MakeParser(bool ignore_unrecognize .Define("-Xjittransitionweight:_") .WithType() .IntoKey(M::JITInvokeTransitionWeight) + .Define("-Xjitpthreadpriority:_") + .WithType() + .IntoKey(M::JITPoolThreadPthreadPriority) .Define("-Xjitsaveprofilinginfo") .WithType() .AppendValues() diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 427385d914..e647423b9c 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -77,6 +77,7 @@ RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold) RUNTIME_OPTIONS_KEY (unsigned int, JITOsrThreshold) RUNTIME_OPTIONS_KEY (unsigned int, JITPriorityThreadWeight) RUNTIME_OPTIONS_KEY (unsigned int, JITInvokeTransitionWeight) +RUNTIME_OPTIONS_KEY (int, JITPoolThreadPthreadPriority, jit::kJitPoolThreadPthreadDefaultPriority) RUNTIME_OPTIONS_KEY (MemoryKiB, JITCodeCacheInitialCapacity, jit::JitCodeCache::kInitialCapacity) RUNTIME_OPTIONS_KEY (MemoryKiB, JITCodeCacheMaxCapacity, jit::JitCodeCache::kMaxCapacity) RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \ -- GitLab From 0226c1d40d6532757fc258cb0586a338a8cd2931 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 17 May 2018 00:19:12 -0700 Subject: [PATCH 415/749] Add some extra dexanalyze tests Based on dexdump tests, test for: - Missing arguments - Invalid input - Using core dex as input Test: test-art-host-gtest-dexanalyze_test Bug: 77721545 Change-Id: I8ce0607b6d3d60171de00a83047536195a81e26f --- build/Android.gtest.mk | 10 ++++++++++ tools/dexanalyze/dexanalyze.cc | 6 +++--- tools/dexanalyze/dexanalyze_test.cc | 12 ++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 3daaf0156e..b481352b60 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -277,6 +277,16 @@ ART_GTEST_dexdump_test_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_DEFAULT_32) \ dexdump2-target +# The dexanalyze test requires an image and the dexanalyze utility. +ART_GTEST_dexanalyze_test_HOST_DEPS := \ + $(HOST_CORE_IMAGE_DEFAULT_64) \ + $(HOST_CORE_IMAGE_DEFAULT_32) \ + dexanalyze-host +ART_GTEST_dexanalyze_test_TARGET_DEPS := \ + $(TARGET_CORE_IMAGE_DEFAULT_64) \ + $(TARGET_CORE_IMAGE_DEFAULT_32) \ + dexanalyze-target + # The dexlayout test requires an image and the dexlayout utility. # TODO: rename into dexdump when migration completes ART_GTEST_dexlayout_test_HOST_DEPS := \ diff --git a/tools/dexanalyze/dexanalyze.cc b/tools/dexanalyze/dexanalyze.cc index c4aebc6f56..a21b9dc925 100644 --- a/tools/dexanalyze/dexanalyze.cc +++ b/tools/dexanalyze/dexanalyze.cc @@ -130,7 +130,7 @@ class DexAnalyze { // TODO: once added, use an api to android::base to read a std::vector. if (!android::base::ReadFileToString(filename.c_str(), &content)) { LOG(ERROR) << "ReadFileToString failed for " + filename << std::endl; - continue; + return 2; } std::vector> dex_files; const DexFileLoader dex_file_loader; @@ -142,14 +142,14 @@ class DexAnalyze { &error_msg, &dex_files)) { LOG(ERROR) << "OpenAll failed for " + filename << " with " << error_msg << std::endl; - continue; + return 3; } for (std::unique_ptr& dex_file : dex_files) { if (options.dump_per_input_dex_) { Analysis current(&options); if (!current.ProcessDexFile(*dex_file)) { LOG(ERROR) << "Failed to process " << filename << " with error " << error_msg; - continue; + return 4; } LOG(INFO) << "Analysis for " << dex_file->GetLocation() << std::endl; current.Dump(LOG_STREAM(INFO)); diff --git a/tools/dexanalyze/dexanalyze_test.cc b/tools/dexanalyze/dexanalyze_test.cc index c9b8f53d24..96be3f9b8d 100644 --- a/tools/dexanalyze/dexanalyze_test.cc +++ b/tools/dexanalyze/dexanalyze_test.cc @@ -36,10 +36,22 @@ class DexAnalyzeTest : public CommonRuntimeTest { } }; +TEST_F(DexAnalyzeTest, NoInputFileGiven) { + DexAnalyzeExec({ "-a" }, /*expect_success*/ false); +} + +TEST_F(DexAnalyzeTest, CantOpenInput) { + DexAnalyzeExec({ "-a", "/non/existent/path" }, /*expect_success*/ false); +} + TEST_F(DexAnalyzeTest, TestAnalyzeMultidex) { DexAnalyzeExec({ "-a", GetTestDexFileName("MultiDex") }, /*expect_success*/ true); } +TEST_F(DexAnalyzeTest, TestAnalizeCoreDex) { + DexAnalyzeExec({ "-a", GetLibCoreDexFileNames()[0] }, /*expect_success*/ true); +} + TEST_F(DexAnalyzeTest, TestInvalidArg) { DexAnalyzeExec({ "-invalid-option" }, /*expect_success*/ false); } -- GitLab From 671af6c3f3858bb372fb4946b14c069c2d456a60 Mon Sep 17 00:00:00 2001 From: David Sehr Date: Thu, 17 May 2018 11:00:35 -0700 Subject: [PATCH 416/749] Make dexlayout and profman independent of libart Remove the libart dependency from these two tools. Dexdiag remains dependent because vdex_file.* is in runtime. It could possibly be moved also. Bug: 78652467 Test: make -j 40 checkbuild make -j 40 test-art-host-gtest Change-Id: I68a62f8b2a2730067aee5ff5e0cf81acdca0d703 --- dexlayout/Android.bp | 10 +++++----- dexlayout/dexdiag.cc | 10 ++++++---- dexlayout/dexlayout_main.cc | 8 ++++++-- profman/profman.cc | 11 ++++++++--- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp index bf56239397..147af0c6a9 100644 --- a/dexlayout/Android.bp +++ b/dexlayout/Android.bp @@ -39,7 +39,6 @@ art_cc_library { "dex2oat-pgo-defaults", ], shared_libs: [ - "libart", "libdexfile", "libartbase", "libprofile", @@ -61,7 +60,6 @@ art_cc_library { "art_debug_defaults", ], shared_libs: [ - "libartd", "libdexfiled", "libartbased", "libprofiled", @@ -72,7 +70,6 @@ cc_defaults { name: "dexlayout-defaults", defaults: ["art_defaults"], host_supported: true, - srcs: ["dexlayout_main.cc"], shared_libs: [ "libbase", ], @@ -81,9 +78,10 @@ cc_defaults { art_cc_binary { name: "dexlayout", defaults: ["dexlayout-defaults"], + srcs: ["dexlayout_main.cc"], shared_libs: [ + "libdexfile", "libprofile", - "libart", "libartbase", "libart-dexlayout", ], @@ -95,9 +93,10 @@ art_cc_binary { "art_debug_defaults", "dexlayout-defaults", ], + srcs: ["dexlayout_main.cc"], shared_libs: [ + "libdexfiled", "libprofiled", - "libartd", "libartbased", "libartd-dexlayout", ], @@ -121,6 +120,7 @@ art_cc_binary { cflags: ["-Wall"], shared_libs: [ "libart", + "libdexfile", "libartbase", "libart-dexlayout", "libbase", diff --git a/dexlayout/dexdiag.cc b/dexlayout/dexdiag.cc index 6cb141f688..aa4e6d031e 100644 --- a/dexlayout/dexdiag.cc +++ b/dexlayout/dexdiag.cc @@ -27,7 +27,6 @@ #include "android-base/stringprintf.h" #include "base/logging.h" // For InitLogging. -#include "base/mutex.h" #include "base/stringpiece.h" #include "dexlayout.h" @@ -37,7 +36,6 @@ #ifdef ART_TARGET_ANDROID #include "pagemap/pagemap.h" #endif -#include "runtime.h" #include "vdex_file.h" namespace art { @@ -446,6 +444,11 @@ static void Usage(const char* cmd) { PrintLetterKey(); } +NO_RETURN static void Abort(const char* msg) { + std::cerr << msg; + exit(1); +} + static int DexDiagMain(int argc, char* argv[]) { if (argc < 2) { Usage(argv[0]); @@ -471,8 +474,7 @@ static int DexDiagMain(int argc, char* argv[]) { } // Art specific set up. - Locks::Init(); - InitLogging(argv, Runtime::Abort); + InitLogging(argv, Abort); MemMap::Init(); #ifdef ART_TARGET_ANDROID diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc index 185c1420ab..71e56d19ea 100644 --- a/dexlayout/dexlayout_main.cc +++ b/dexlayout/dexlayout_main.cc @@ -34,7 +34,6 @@ #include "base/logging.h" // For InitLogging. #include "base/mem_map.h" #include "profile/profile_compilation_info.h" -#include "runtime.h" namespace art { @@ -66,12 +65,17 @@ static void Usage(void) { LOG(ERROR) << " -x : compact dex generation level, either 'none' or 'fast'"; } +NO_RETURN static void Abort(const char* msg) { + LOG(ERROR) << msg; + exit(1); +} + /* * Main driver of the dexlayout utility. */ int DexlayoutDriver(int argc, char** argv) { // Art specific set up. - InitLogging(argv, Runtime::Abort); + InitLogging(argv, Abort); MemMap::Init(); Options options; diff --git a/profman/profman.cc b/profman/profman.cc index cd88d03929..1f7723946c 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -33,6 +33,7 @@ #include "base/dumpable.h" #include "base/logging.h" // For InitLogging. +#include "base/mem_map.h" #include "base/scoped_flock.h" #include "base/stringpiece.h" #include "base/time_utils.h" @@ -49,7 +50,6 @@ #include "dex/type_reference.h" #include "profile/profile_compilation_info.h" #include "profile_assistant.h" -#include "runtime.h" namespace art { @@ -177,6 +177,11 @@ static constexpr char kMethodFlagStringHot = 'H'; static constexpr char kMethodFlagStringStartup = 'S'; static constexpr char kMethodFlagStringPostStartup = 'P'; +NO_RETURN static void Abort(const char* msg) { + LOG(ERROR) << msg; + exit(1); +} + // TODO(calin): This class has grown too much from its initial design. Split the functionality // into smaller, more contained pieces. class ProfMan FINAL { @@ -202,8 +207,8 @@ class ProfMan FINAL { original_argc = argc; original_argv = argv; - Locks::Init(); - InitLogging(argv, Runtime::Abort); + MemMap::Init(); + InitLogging(argv, Abort); // Skip over the command name. argv++; -- GitLab From 9abc31e41d0983b3d0a111ea7752bd392f1c309e Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 17 May 2018 11:47:09 -0700 Subject: [PATCH 417/749] ART: Refactor String.init binding Do not use JNI infrastructure, and move the initialization to the classlinker's FinishInit. This means the binding is available after the minimal set of initializations. We could consider moving the functionality even earlier, as soon as String methods are known and StringFactory can be loaded, but this placement works well enough and is nicely isolated. Bug: 79902155 Test: m test-art-host Change-Id: If4e9f1424668eb200ff5544bc21f5cea6151e4b3 --- runtime/class_linker.cc | 15 ++++++++++++++ runtime/well_known_classes.cc | 38 +++++++++++++++++++++++------------ runtime/well_known_classes.h | 6 +++--- 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index be9e08fbec..b88aa5e07a 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -828,9 +828,24 @@ bool ClassLinker::InitWithoutImage(std::vector> b return true; } +static void CreateStringInitBindings(Thread* self, ClassLinker* class_linker) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Find String. -> StringFactory bindings. + ObjPtr string_factory_class = + class_linker->FindSystemClass(self, "Ljava/lang/StringFactory;"); + CHECK(string_factory_class != nullptr); + ObjPtr string_class = + class_linker->GetClassRoot(ClassLinker::ClassRoot::kJavaLangString); + WellKnownClasses::InitStringInit(string_class, string_factory_class); + // Update the primordial thread. + self->InitStringEntryPoints(); +} + void ClassLinker::FinishInit(Thread* self) { VLOG(startup) << "ClassLinker::FinishInit entering"; + CreateStringInitBindings(self, this); + // Let the heap know some key offsets into java.lang.ref instances // Note: we hard code the field indexes here rather than using FindInstanceField // as the types of the field can't be resolved prior to the runtime being diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 4843061be6..f7cdf3920a 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -23,6 +23,8 @@ #include #include +#include "base/enums.h" +#include "class_linker.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "hidden_api.h" #include "jni/jni_internal.h" @@ -30,6 +32,7 @@ #include "mirror/throwable.h" #include "nativehelper/scoped_local_ref.h" #include "obj_ptr-inl.h" +#include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" @@ -231,19 +234,28 @@ static jmethodID CachePrimitiveBoxingMethod(JNIEnv* env, char prim_name, const c V(java_lang_String_init_StringBuilder, "(Ljava/lang/StringBuilder;)V", newStringFromStringBuilder, "newStringFromStringBuilder", "(Ljava/lang/StringBuilder;)Ljava/lang/String;", NewStringFromStringBuilder) \ #define STATIC_STRING_INIT(init_runtime_name, init_signature, new_runtime_name, ...) \ - static ArtMethod* init_runtime_name; \ - static ArtMethod* new_runtime_name; + static ArtMethod* init_runtime_name = nullptr; \ + static ArtMethod* new_runtime_name = nullptr; STRING_INIT_LIST(STATIC_STRING_INIT) #undef STATIC_STRING_INIT -void WellKnownClasses::InitStringInit(JNIEnv* env) { - ScopedObjectAccess soa(Thread::Current()); - #define LOAD_STRING_INIT(init_runtime_name, init_signature, new_runtime_name, \ - new_java_name, new_signature, ...) \ - init_runtime_name = jni::DecodeArtMethod( \ - CacheMethod(env, java_lang_String, false, "", init_signature)); \ - new_runtime_name = jni::DecodeArtMethod( \ - CacheMethod(env, java_lang_StringFactory, true, new_java_name, new_signature)); +void WellKnownClasses::InitStringInit(ObjPtr string_class, + ObjPtr string_builder_class) { + PointerSize p_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + auto find_method = [p_size](ObjPtr klass, + const char* name, + const char* sig, + bool expext_static) REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* ret = klass->FindClassMethod(name, sig, p_size); + CHECK(ret != nullptr); + CHECK_EQ(expext_static, ret->IsStatic()); + return ret; + }; + + #define LOAD_STRING_INIT(init_runtime_name, init_signature, new_runtime_name, \ + new_java_name, new_signature, ...) \ + init_runtime_name = find_method(string_class, "", init_signature, false); \ + new_runtime_name = find_method(string_builder_class, new_java_name, new_signature, true); STRING_INIT_LIST(LOAD_STRING_INIT) #undef LOAD_STRING_INIT } @@ -252,6 +264,7 @@ void Thread::InitStringEntryPoints() { QuickEntryPoints* qpoints = &tlsPtr_.quick_entrypoints; #define SET_ENTRY_POINT(init_runtime_name, init_signature, new_runtime_name, \ new_java_name, new_signature, entry_point_name) \ + DCHECK(!Runtime::Current()->IsStarted() || (new_runtime_name) != nullptr); \ qpoints->p ## entry_point_name = reinterpret_cast(new_runtime_name); STRING_INIT_LIST(SET_ENTRY_POINT) #undef SET_ENTRY_POINT @@ -260,7 +273,9 @@ void Thread::InitStringEntryPoints() { ArtMethod* WellKnownClasses::StringInitToStringFactory(ArtMethod* string_init) { #define TO_STRING_FACTORY(init_runtime_name, init_signature, new_runtime_name, \ new_java_name, new_signature, entry_point_name) \ + DCHECK((init_runtime_name) != nullptr); \ if (string_init == (init_runtime_name)) { \ + DCHECK((new_runtime_name) != nullptr); \ return (new_runtime_name); \ } STRING_INIT_LIST(TO_STRING_FACTORY) @@ -410,9 +425,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Integer_valueOf = CachePrimitiveBoxingMethod(env, 'I', "java/lang/Integer"); java_lang_Long_valueOf = CachePrimitiveBoxingMethod(env, 'J', "java/lang/Long"); java_lang_Short_valueOf = CachePrimitiveBoxingMethod(env, 'S', "java/lang/Short"); - - InitStringInit(env); - Thread::Current()->InitStringEntryPoints(); } void WellKnownClasses::LateInit(JNIEnv* env) { diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index 25c07b27de..c06e4a71ce 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -40,6 +40,9 @@ struct WellKnownClasses { static void Clear(); + static void InitStringInit(ObjPtr string_class, + ObjPtr string_builder_class) + REQUIRES_SHARED(Locks::mutator_lock_); static ArtMethod* StringInitToStringFactory(ArtMethod* method); static uint32_t StringInitToEntryPoint(ArtMethod* method); @@ -168,9 +171,6 @@ struct WellKnownClasses { static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_length; static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_offset; static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_type; - - private: - static void InitStringInit(JNIEnv* env); }; } // namespace art -- GitLab From 9b5ddff6f74073a2b45669eff8379ed796dbf488 Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Wed, 16 May 2018 11:18:10 +0100 Subject: [PATCH 418/749] Hidden API: only log what we deny. Only print a "Accessing hidden ..." warning in logcat when we deny access to any API, or if the app is debuggable. This reduces log spam. Update test expectations accordingly. Bug: 79914966 Test: $ art/test.py -b --host -t 674-hiddenapi (cherry picked from commit 46b26278907301dcc27010b397d1a4bd1cd53b33) Merged-In: Ic6dfa0dd519a8854e3a40ba19c9a001c0c2a378b Change-Id: Ieda769d51e53ec4b7712d0bb1bf76e1a95d2120f --- runtime/hidden_api.cc | 8 +++++--- test/674-hiddenapi/src-ex/ChildClass.java | 8 ++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index ee518ae1ea..e41d1d3eb9 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -40,7 +40,7 @@ namespace hiddenapi { // Note that when flipping this flag, you must also update the expectations of test 674-hiddenapi // as it affects whether or not we warn for light grey APIs that have been added to the exemptions // list. -static constexpr bool kLogAllAccesses = true; +static constexpr bool kLogAllAccesses = false; static inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { switch (value) { @@ -219,7 +219,8 @@ Action GetMemberActionImpl(T* member, // - for non-debuggable apps, there is no distinction between light grey & whitelisted APIs. // - we want to avoid the overhead of checking for exemptions for light greylisted APIs whenever // possible. - if (kLogAllAccesses || action == kDeny || runtime->IsJavaDebuggable()) { + const bool shouldWarn = kLogAllAccesses || runtime->IsJavaDebuggable(); + if (shouldWarn || action == kDeny) { if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) { action = kAllow; // Avoid re-examining the exemption list next time. @@ -260,7 +261,8 @@ Action GetMemberActionImpl(T* member, MaybeWhitelistMember(runtime, member); // If this action requires a UI warning, set the appropriate flag. - if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) { + if (shouldWarn && + (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag())) { runtime->SetPendingHiddenApiWarning(true); } } diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java index 0349e8fe46..d5966cde36 100644 --- a/test/674-hiddenapi/src-ex/ChildClass.java +++ b/test/674-hiddenapi/src-ex/ChildClass.java @@ -91,12 +91,12 @@ public class ChildClass { // Run meaningful combinations of access flags. for (Hiddenness hiddenness : Hiddenness.values()) { final Behaviour expected; - if (isSameBoot || hiddenness == Hiddenness.Whitelist || everythingWhitelisted) { + // Warnings are now disabled whenever access is granted, even for + // greylisted APIs. This is the behaviour for release builds. + if (isSameBoot || hiddenness != Hiddenness.Blacklist || everythingWhitelisted) { expected = Behaviour.Granted; - } else if (hiddenness == Hiddenness.Blacklist) { - expected = Behaviour.Denied; } else { - expected = Behaviour.Warning; + expected = Behaviour.Denied; } for (boolean isStatic : booleanValues) { -- GitLab From eef7757941f2681814462b0f55d29a562c0200da Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Fri, 18 May 2018 13:32:09 +0100 Subject: [PATCH 419/749] Fix test 674-hiddenapi when debuggable. The runtime warns for debuggable apps even when access to an API is allowed. Update the expectations of this test accordingly. This requires exposing Runtime::IsJavaDebuggable to the java code so it knows what to expect. Test: $ art/test.py -b --host --debuggable -t 674-hiddenapi Bug: 79914966 Change-Id: I339f205d7153cf7b1c12dc06813c768608921684 --- runtime/native/dalvik_system_VMRuntime.cc | 5 +++++ test/674-hiddenapi/src-ex/ChildClass.java | 9 +++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 7f7b524227..6c820190b4 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -185,6 +185,10 @@ static jboolean VMRuntime_isNativeDebuggable(JNIEnv*, jobject) { return Runtime::Current()->IsNativeDebuggable(); } +static jboolean VMRuntime_isJavaDebuggable(JNIEnv*, jobject) { + return Runtime::Current()->IsJavaDebuggable(); +} + static jobjectArray VMRuntime_properties(JNIEnv* env, jobject) { DCHECK(WellKnownClasses::java_lang_String != nullptr); @@ -702,6 +706,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"), FAST_NATIVE_METHOD(VMRuntime, isDebuggerActive, "()Z"), FAST_NATIVE_METHOD(VMRuntime, isNativeDebuggable, "()Z"), + NATIVE_METHOD(VMRuntime, isJavaDebuggable, "()Z"), NATIVE_METHOD(VMRuntime, nativeSetTargetHeapUtilization, "(F)V"), FAST_NATIVE_METHOD(VMRuntime, newNonMovableArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"), FAST_NATIVE_METHOD(VMRuntime, newUnpaddedArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"), diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java index d5966cde36..db3ba6d8bf 100644 --- a/test/674-hiddenapi/src-ex/ChildClass.java +++ b/test/674-hiddenapi/src-ex/ChildClass.java @@ -87,16 +87,21 @@ public class ChildClass { ChildClass.everythingWhitelisted = everythingWhitelisted; boolean isSameBoot = (isParentInBoot == isChildInBoot); + boolean isDebuggable = VMRuntime.getRuntime().isJavaDebuggable(); // Run meaningful combinations of access flags. for (Hiddenness hiddenness : Hiddenness.values()) { final Behaviour expected; // Warnings are now disabled whenever access is granted, even for // greylisted APIs. This is the behaviour for release builds. - if (isSameBoot || hiddenness != Hiddenness.Blacklist || everythingWhitelisted) { + if (isSameBoot || everythingWhitelisted || hiddenness == Hiddenness.Whitelist) { expected = Behaviour.Granted; - } else { + } else if (hiddenness == Hiddenness.Blacklist) { expected = Behaviour.Denied; + } else if (isDebuggable) { + expected = Behaviour.Warning; + } else { + expected = Behaviour.Granted; } for (boolean isStatic : booleanValues) { -- GitLab From b1f8b738f1da2745dd8058cbd360a61278cb35e4 Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Fri, 18 May 2018 13:50:34 +0100 Subject: [PATCH 420/749] Track changes in java.lang.(StringBuilder|String) After recent commit 9abc31e in platform/art changes have been made to use new String() in Java source where possible rather than StringFactory. Bug: 79902155 Test: make test-art-host Change-Id: Idb65112832694c19977d979af4e223f0081abc71 --- test/988-method-trace/expected.txt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/988-method-trace/expected.txt b/test/988-method-trace/expected.txt index 7f64e23a77..6e16722d53 100644 --- a/test/988-method-trace/expected.txt +++ b/test/988-method-trace/expected.txt @@ -130,8 +130,10 @@ fibonacci(5)=5 ....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(java.lang.String) -> ...<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> ...=> public java.lang.String java.lang.StringBuilder.toString() -....=> static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -....<= static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> +....=> public static java.lang.String java.lang.StringFactory.newStringFromChars(char[],int,int) +.....=> static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) +.....<= static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> +....<= public static java.lang.String java.lang.StringFactory.newStringFromChars(char[],int,int) -> ...<= public java.lang.String java.lang.StringBuilder.toString() -> ...=> public java.lang.Error(java.lang.String) ....=> public java.lang.Throwable(java.lang.String) @@ -231,8 +233,10 @@ fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0 ....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(java.lang.String) -> ...<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> ...=> public java.lang.String java.lang.StringBuilder.toString() -....=> static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -....<= static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> +....=> public static java.lang.String java.lang.StringFactory.newStringFromChars(char[],int,int) +.....=> static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) +.....<= static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> +....<= public static java.lang.String java.lang.StringFactory.newStringFromChars(char[],int,int) -> ...<= public java.lang.String java.lang.StringBuilder.toString() -> ...=> public java.lang.Error(java.lang.String) ....=> public java.lang.Throwable(java.lang.String) -- GitLab From 481d45eda4b4bd9b785ba5cc7ac0bcd3cb4ad325 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Fri, 18 May 2018 14:25:46 +0100 Subject: [PATCH 421/749] Ignore failures in run-test 080-oom-throw with JIT. This test sometimes fails with JIT with an unexpectedly thrown exception before or during an (expected) thrown OutOfMemoryError. Disable it while we investigate the cause of this flakiness. Test: Relying on pre- and post-submit testing. Bug: 77567088 Change-Id: Ib0eeacd281fcd62ea21ba0714276d1ebb03d130a --- test/knownfailures.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/knownfailures.json b/test/knownfailures.json index a202044786..31a0eef998 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -984,5 +984,11 @@ "991-field-trace-2"], "variant": "gcstress & debug & target", "description": ["Test can time out on gcstress with debug"] + }, + { + "tests": ["080-oom-throw"], + "variant": "jit", + "bug": "b/77567088", + "description": ["Test throws exception before or during OOME."] } ] -- GitLab From 4dc09e7261dede72d916059d1f751a48cd08dbb5 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Fri, 11 May 2018 14:40:31 -0700 Subject: [PATCH 422/749] LSE improvement: better singleton array optimization Rationale: In a recent LSA/LSE refactoring, we removed the "exceptional" situation on mismatched arrays from the load-elimination branch for merged values. As a direct result, we can relax the condition on removing stores for singleton arrays into return blocks a bit, as done in this CL (and shown with some tests). Test: test-art-host,target Bug: b/77906240 Change-Id: I32c89057168730f82d1d7c41155a9ff71b126204 --- compiler/optimizing/load_store_elimination.cc | 9 +- compiler/optimizing/nodes.cc | 5 + compiler/optimizing/nodes.h | 1 + test/530-checker-lse/src/Main.java | 129 ++++++++++++++++++ 4 files changed, 142 insertions(+), 2 deletions(-) diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 35e64f75b9..28ac94273c 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -458,8 +458,13 @@ class LSEVisitor : public HGraphDelegateVisitor { } if (from_all_predecessors) { if (ref_info->IsSingletonAndRemovable() && - block->IsSingleReturnOrReturnVoidAllowingPhis()) { - // Values in the singleton are not needed anymore. + (block->IsSingleReturnOrReturnVoidAllowingPhis() || + (block->EndsWithReturn() && (merged_value != kUnknownHeapValue || + merged_store_value != kUnknownHeapValue)))) { + // Values in the singleton are not needed anymore: + // (1) if this block consists of a sole return, or + // (2) if this block returns and a usable merged value is obtained + // (loads prior to the return will always use that value). } else if (!IsStore(merged_value)) { // We don't track merged value as a store anymore. We have to // hold the stores in predecessors live here. diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 5f2833e9a6..7f78dc257e 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1951,6 +1951,11 @@ bool HBasicBlock::EndsWithControlFlowInstruction() const { return !GetInstructions().IsEmpty() && GetLastInstruction()->IsControlFlow(); } +bool HBasicBlock::EndsWithReturn() const { + return !GetInstructions().IsEmpty() && + (GetLastInstruction()->IsReturn() || GetLastInstruction()->IsReturnVoid()); +} + bool HBasicBlock::EndsWithIf() const { return !GetInstructions().IsEmpty() && GetLastInstruction()->IsIf(); } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index e786502dee..09d9c57a33 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1285,6 +1285,7 @@ class HBasicBlock : public ArenaObject { void SetLifetimeEnd(size_t end) { lifetime_end_ = end; } bool EndsWithControlFlowInstruction() const; + bool EndsWithReturn() const; bool EndsWithIf() const; bool EndsWithTryBoundary() const; bool HasSinglePhi() const; diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java index ebde3bf845..93c153821b 100644 --- a/test/530-checker-lse/src/Main.java +++ b/test/530-checker-lse/src/Main.java @@ -1137,6 +1137,126 @@ public class Main { static Object[] sArray; + /// CHECK-START: int Main.testLocalArrayMerge1(boolean) load_store_elimination (before) + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> NewArray + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: <> ArrayGet [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.testLocalArrayMerge1(boolean) load_store_elimination (after) + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.testLocalArrayMerge1(boolean) load_store_elimination (after) + /// CHECK-NOT: NewArray + /// CHECK-NOT: ArraySet + /// CHECK-NOT: ArrayGet + private static int testLocalArrayMerge1(boolean x) { + // The explicit store can be removed right away + // since it is equivalent to the default. + int[] a = { 0 }; + // The diamond pattern stores/load can be replaced + // by the direct value. + if (x) { + a[0] = 1; + } else { + a[0] = 1; + } + return a[0]; + } + + /// CHECK-START: int Main.testLocalArrayMerge2(boolean) load_store_elimination (before) + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> IntConstant 2 + /// CHECK-DAG: <> IntConstant 3 + /// CHECK-DAG: <> NewArray + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: <> ArrayGet [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.testLocalArrayMerge2(boolean) load_store_elimination (after) + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> NewArray + /// CHECK-DAG: <> ArrayGet [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.testLocalArrayMerge2(boolean) load_store_elimination (after) + /// CHECK-DAG: ArraySet + /// CHECK-DAG: ArraySet + /// CHECK-NOT: ArraySet + private static int testLocalArrayMerge2(boolean x) { + // The explicit store can be removed eventually even + // though it is not equivalent to the default. + int[] a = { 1 }; + // The diamond pattern stores/load remain. + if (x) { + a[0] = 2; + } else { + a[0] = 3; + } + return a[0]; + } + + /// CHECK-START: int Main.testLocalArrayMerge3(boolean) load_store_elimination (after) + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> IntConstant 2 + /// CHECK-DAG: <> NewArray + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: <> ArrayGet [<>,<>] + /// CHECK-DAG: Return [<>] + private static int testLocalArrayMerge3(boolean x) { + // All stores/load remain. + int[] a = { 1 }; + if (x) { + a[0] = 2; + } + return a[0]; + } + + /// CHECK-START: int Main.testLocalArrayMerge4(boolean) load_store_elimination (before) + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> NewArray + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: ArraySet [<>,<>,<>] + /// CHECK-DAG: <> ArrayGet [<>,<>] + /// CHECK-DAG: <> ArrayGet [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.testLocalArrayMerge4(boolean) load_store_elimination (after) + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> TypeConversion [<>] + /// CHECK-DAG: <> TypeConversion [<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.testLocalArrayMerge4(boolean) load_store_elimination (after) + /// CHECK-NOT: NewArray + /// CHECK-NOT: ArraySet + /// CHECK-NOT: ArrayGet + private static int testLocalArrayMerge4(boolean x) { + byte[] a = { 0 }; + if (x) { + a[0] = 1; + } else { + a[0] = 1; + } + // Differently typed (signed vs unsigned), + // but same reference. + return a[0] + (a[0] & 0xff); + } + static void assertIntEquals(int result, int expected) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); @@ -1271,6 +1391,15 @@ public class Main { assertIntEquals(testclass2.i, 55); assertIntEquals(testStoreStoreWithDeoptimize(new int[4]), 4); + + assertIntEquals(testLocalArrayMerge1(true), 1); + assertIntEquals(testLocalArrayMerge1(false), 1); + assertIntEquals(testLocalArrayMerge2(true), 2); + assertIntEquals(testLocalArrayMerge2(false), 3); + assertIntEquals(testLocalArrayMerge3(true), 2); + assertIntEquals(testLocalArrayMerge3(false), 1); + assertIntEquals(testLocalArrayMerge4(true), 2); + assertIntEquals(testLocalArrayMerge4(false), 2); } static boolean sFlag; -- GitLab From f275979ca2d2809d19210195c22526861554fac0 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 18 May 2018 13:16:54 -0700 Subject: [PATCH 423/749] Add ASCII vs UTF-16 string data analysis Measure how many strings are fully ASCII characters or a UTF-16 mix. Bug: 77721545 Test: test-art-host-gtest-dexanalyze_test Change-Id: Ic59613596c1542c693bcf49cee379310414c44cd --- tools/dexanalyze/dexanalyze.cc | 13 ++++++++----- tools/dexanalyze/dexanalyze_experiments.cc | 22 ++++++++++++++++++++-- tools/dexanalyze/dexanalyze_experiments.h | 3 +++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/tools/dexanalyze/dexanalyze.cc b/tools/dexanalyze/dexanalyze.cc index 58b1fc7ba3..46c48520e3 100644 --- a/tools/dexanalyze/dexanalyze.cc +++ b/tools/dexanalyze/dexanalyze.cc @@ -30,7 +30,10 @@ namespace art { class DexAnalyze { - static const int kExitCodeUsageError = 1; + static constexpr int kExitCodeUsageError = 1; + static constexpr int kExitCodeFailedToOpenFile = 2; + static constexpr int kExitCodeFailedToOpenDex = 3; + static constexpr int kExitCodeFailedToProcessDex = 4; static void StdoutLogger(android::base::LogId, android::base::LogSeverity, @@ -135,10 +138,10 @@ class DexAnalyze { Analysis cumulative(&options); for (const std::string& filename : options.filenames_) { std::string content; - // TODO: once added, use an api to android::base to read a std::vector. + // TODO: once added, use an API to android::base to read a std::vector. if (!android::base::ReadFileToString(filename.c_str(), &content)) { LOG(ERROR) << "ReadFileToString failed for " + filename << std::endl; - return 2; + return kExitCodeFailedToOpenFile; } std::vector> dex_files; const DexFileLoader dex_file_loader; @@ -150,14 +153,14 @@ class DexAnalyze { &error_msg, &dex_files)) { LOG(ERROR) << "OpenAll failed for " + filename << " with " << error_msg << std::endl; - return 3; + return kExitCodeFailedToOpenDex; } for (std::unique_ptr& dex_file : dex_files) { if (options.dump_per_input_dex_) { Analysis current(&options); if (!current.ProcessDexFile(*dex_file)) { LOG(ERROR) << "Failed to process " << filename << " with error " << error_msg; - return 4; + return kExitCodeFailedToProcessDex; } LOG(INFO) << "Analysis for " << dex_file->GetLocation() << std::endl; current.Dump(LOG_STREAM(INFO)); diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc index bfeb4b9d72..adc515472d 100644 --- a/tools/dexanalyze/dexanalyze_experiments.cc +++ b/tools/dexanalyze/dexanalyze_experiments.cc @@ -26,6 +26,7 @@ #include "dex/code_item_accessors-inl.h" #include "dex/dex_instruction-inl.h" #include "dex/standard_dex_file.h" +#include "dex/utf-inl.h" namespace art { @@ -48,8 +49,20 @@ void AnalyzeStrings::ProcessDexFile(const DexFile& dex_file) { std::vector strings; for (size_t i = 0; i < dex_file.NumStringIds(); ++i) { uint32_t length = 0; - const char* data = - dex_file.GetStringDataAndUtf16Length(dex_file.GetStringId(dex::StringIndex(i)), &length); + const char* data = dex_file.StringDataAndUtf16LengthByIdx(dex::StringIndex(i), &length); + // Analyze if the string has any UTF16 chars. + bool have_wide_char = false; + const char* ptr = data; + for (size_t j = 0; j < length; ++j) { + have_wide_char = have_wide_char || GetUtf16FromUtf8(&ptr) >= 0x100; + } + if (have_wide_char) { + wide_string_bytes_ += 2 * length; + } else { + ascii_string_bytes_ += length; + } + string_data_bytes_ += ptr - data; + strings.push_back(data); } // Note that the strings are probably already sorted. @@ -88,6 +101,11 @@ void AnalyzeStrings::ProcessDexFile(const DexFile& dex_file) { } void AnalyzeStrings::Dump(std::ostream& os, uint64_t total_size) const { + os << "Total string data bytes " << Percent(string_data_bytes_, total_size) << "\n"; + os << "UTF-16 string data bytes " << Percent(wide_string_bytes_, total_size) << "\n"; + os << "ASCII string data bytes " << Percent(ascii_string_bytes_, total_size) << "\n"; + + // Prefix based strings. os << "Total shared prefix bytes " << Percent(total_prefix_savings_, total_size) << "\n"; os << "Prefix dictionary cost " << Percent(total_prefix_dict_, total_size) << "\n"; os << "Prefix table cost " << Percent(total_prefix_table_, total_size) << "\n"; diff --git a/tools/dexanalyze/dexanalyze_experiments.h b/tools/dexanalyze/dexanalyze_experiments.h index 6f70f5d166..0fb4d32005 100644 --- a/tools/dexanalyze/dexanalyze_experiments.h +++ b/tools/dexanalyze/dexanalyze_experiments.h @@ -41,6 +41,9 @@ class AnalyzeStrings : public Experiment { void Dump(std::ostream& os, uint64_t total_size) const; private: + int64_t wide_string_bytes_ = 0u; + int64_t ascii_string_bytes_ = 0u; + int64_t string_data_bytes_ = 0u; int64_t total_prefix_savings_ = 0u; int64_t total_prefix_dict_ = 0u; int64_t total_prefix_table_ = 0u; -- GitLab From a218b3ef41b17dfb77f79930b46768b99d8ce5d8 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 18 May 2018 14:37:38 -0700 Subject: [PATCH 424/749] Re-enable LZ4_compress_HC in image writer This mode is probably no longer flaky. If there are issues, it should be caught by the decoding verification. Test: test-art-host-gtest Bug: 27773950 Change-Id: I3310b7447ca77eceddbc93235f756bc57b852b99 --- dex2oat/linker/image_writer.cc | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 492f9ce5c1..01726af1d6 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -248,7 +248,6 @@ bool ImageWriter::Write(int image_fd, CHECK_EQ(image_header->storage_mode_, image_storage_mode_); switch (image_storage_mode_) { - case ImageHeader::kStorageModeLZ4HC: // Fall-through. case ImageHeader::kStorageModeLZ4: { const size_t compressed_max_size = LZ4_compressBound(image_data_size); compressed_data.reset(new char[compressed_max_size]); @@ -257,22 +256,20 @@ bool ImageWriter::Write(int image_fd, &compressed_data[0], image_data_size, compressed_max_size); - break; } - /* - * Disabled due to image_test64 flakyness. Both use same decompression. b/27560444 case ImageHeader::kStorageModeLZ4HC: { // Bound is same as non HC. const size_t compressed_max_size = LZ4_compressBound(image_data_size); compressed_data.reset(new char[compressed_max_size]); - data_size = LZ4_compressHC( + data_size = LZ4_compress_HC( reinterpret_cast(image_info.image_->Begin()) + sizeof(ImageHeader), &compressed_data[0], - image_data_size); + image_data_size, + compressed_max_size, + LZ4HC_CLEVEL_MAX); break; } - */ case ImageHeader::kStorageModeUncompressed: { data_size = image_data_size; image_data_to_write = image_data; -- GitLab From c8b1d5e0f2112c78aeed0d114b0c4f8b6a234c10 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 15 May 2018 16:07:12 +0100 Subject: [PATCH 425/749] ARM/ARM64: Improve lock/unlock entrypoints. Do the same in fewer instructions. Test: Pixel 2 XL boots. Test: testrunner.py --target --optimizing Change-Id: I8003481116fd3dc6a1559b84fdc776b92dba0c68 --- runtime/arch/arm/quick_entrypoints_arm.S | 267 +++++++++--------- runtime/arch/arm64/quick_entrypoints_arm64.S | 113 ++++---- runtime/arch/x86/quick_entrypoints_x86.S | 4 +- .../arch/x86_64/quick_entrypoints_x86_64.S | 4 +- runtime/generated/asm_support_gen.h | 18 +- runtime/lock_word.h | 4 +- .../constant_lockword.def | 22 +- 7 files changed, 214 insertions(+), 218 deletions(-) diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index cd00125de5..311e838fb3 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -55,7 +55,7 @@ @ Load kSaveAllCalleeSaves Method* into rTemp. ldr \rTemp, [\rTemp, #RUNTIME_SAVE_ALL_CALLEE_SAVES_METHOD_OFFSET] str \rTemp, [sp, #0] @ Place Method* at bottom of stack. - str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. + str sp, [rSELF, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. // Ugly compile-time check, but we only have the preprocessor. #if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVES != 36 + 64 + 12) @@ -86,7 +86,7 @@ @ Load kSaveRefsOnly Method* into rTemp. ldr \rTemp, [\rTemp, #RUNTIME_SAVE_REFS_ONLY_METHOD_OFFSET] str \rTemp, [sp, #0] @ Place Method* at bottom of stack. - str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. + str sp, [rSELF, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. // Ugly compile-time check, but we only have the preprocessor. #if (FRAME_SIZE_SAVE_REFS_ONLY != 28 + 4) @@ -147,13 +147,13 @@ @ Load kSaveRefsAndArgs Method* into rTemp. ldr \rTemp, [\rTemp, #RUNTIME_SAVE_REFS_AND_ARGS_METHOD_OFFSET] str \rTemp, [sp, #0] @ Place Method* at bottom of stack. - str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. + str sp, [rSELF, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. .endm .macro SETUP_SAVE_REFS_AND_ARGS_FRAME_WITH_METHOD_IN_R0 SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY str r0, [sp, #0] @ Store ArtMethod* to bottom of stack. - str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. + str sp, [rSELF, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. .endm .macro RESTORE_SAVE_REFS_AND_ARGS_FRAME @@ -193,7 +193,7 @@ @ Load kSaveEverything Method* into rTemp. ldr \rTemp, [\rTemp, #\runtime_method_offset] str \rTemp, [sp, #0] @ Place Method* at bottom of stack. - str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. + str sp, [rSELF, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. // Ugly compile-time check, but we only have the preprocessor. #if (FRAME_SIZE_SAVE_EVERYTHING != 56 + 128 + 8) @@ -301,7 +301,7 @@ * exception is Thread::Current()->exception_ when the runtime method frame is ready. */ .macro DELIVER_PENDING_EXCEPTION_FRAME_READY - mov r0, r9 @ pass Thread::Current + mov r0, rSELF @ pass Thread::Current bl artDeliverPendingExceptionFromCode @ artDeliverPendingExceptionFromCode(Thread*) .endm @@ -318,7 +318,7 @@ .extern \cxx_name ENTRY \c_name SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r0 @ save all registers as basis for long jump context - mov r0, r9 @ pass Thread::Current + mov r0, rSELF @ pass Thread::Current bl \cxx_name @ \cxx_name(Thread*) END \c_name .endm @@ -327,7 +327,7 @@ END \c_name .extern \cxx_name ENTRY \c_name SETUP_SAVE_EVERYTHING_FRAME r0 @ save all registers as basis for long jump context - mov r0, r9 @ pass Thread::Current + mov r0, rSELF @ pass Thread::Current bl \cxx_name @ \cxx_name(Thread*) END \c_name .endm @@ -336,7 +336,7 @@ END \c_name .extern \cxx_name ENTRY \c_name SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r1 @ save all registers as basis for long jump context - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current bl \cxx_name @ \cxx_name(Thread*) END \c_name .endm @@ -345,13 +345,13 @@ END \c_name .extern \cxx_name ENTRY \c_name SETUP_SAVE_EVERYTHING_FRAME r2 @ save all registers as basis for long jump context - mov r2, r9 @ pass Thread::Current + mov r2, rSELF @ pass Thread::Current bl \cxx_name @ \cxx_name(Thread*) END \c_name .endm .macro RETURN_OR_DELIVER_PENDING_EXCEPTION_REG reg - ldr \reg, [r9, #THREAD_EXCEPTION_OFFSET] // Get exception field. + ldr \reg, [rSELF, #THREAD_EXCEPTION_OFFSET] @ Get exception field. cbnz \reg, 1f bx lr 1: @@ -377,7 +377,7 @@ END \c_name .extern \entrypoint ENTRY \name SETUP_SAVE_REFS_ONLY_FRAME r1 @ save callee saves in case of GC - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current bl \entrypoint @ (uint32_t field_idx, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER @@ -389,7 +389,7 @@ END \name .extern \entrypoint ENTRY \name SETUP_SAVE_REFS_ONLY_FRAME r2 @ save callee saves in case of GC - mov r2, r9 @ pass Thread::Current + mov r2, rSELF @ pass Thread::Current bl \entrypoint @ (field_idx, Object*, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER @@ -401,7 +401,7 @@ END \name .extern \entrypoint ENTRY \name SETUP_SAVE_REFS_ONLY_FRAME r3 @ save callee saves in case of GC - mov r3, r9 @ pass Thread::Current + mov r3, rSELF @ pass Thread::Current bl \entrypoint @ (field_idx, Object*, new_val, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME @ TODO: we can clearly save an add here REFRESH_MARKING_REGISTER @@ -448,7 +448,7 @@ ENTRY art_quick_throw_null_pointer_exception_from_signal @ save all registers as basis for long jump context SETUP_SAVE_EVERYTHING_FRAME_CORE_REGS_SAVED r1 mov r0, lr @ pass the fault address stored in LR by the fault handler. - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current bl artThrowNullPointerExceptionFromSignal @ (Thread*) END art_quick_throw_null_pointer_exception_from_signal @@ -494,7 +494,7 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFr .macro INVOKE_TRAMPOLINE_BODY cxx_name .extern \cxx_name SETUP_SAVE_REFS_AND_ARGS_FRAME r2 @ save callee saves in case allocation triggers GC - mov r2, r9 @ pass Thread::Current + mov r2, rSELF @ pass Thread::Current mov r3, sp bl \cxx_name @ (method_idx, this, Thread*, SP) mov r12, r1 @ save Method*->code_ @@ -682,50 +682,48 @@ TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, */ .extern artLockObjectFromCode ENTRY art_quick_lock_object + ldr r1, [rSELF, #THREAD_ID_OFFSET] cbz r0, .Lslow_lock .Lretry_lock: - ldr r2, [r9, #THREAD_ID_OFFSET] - ldrex r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] - mov r3, r1 - and r3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED @ zero the gc bits - cbnz r3, .Lnot_unlocked @ already thin locked - @ unlocked case - r1: original lock word that's zero except for the read barrier bits. - orr r2, r1, r2 @ r2 holds thread id with count of 0 with preserved read barrier bits - strex r3, r2, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] - cbnz r3, .Llock_strex_fail @ store failed, retry - dmb ish @ full (LoadLoad|LoadStore) memory barrier + ldrex r2, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] + eor r3, r2, r1 @ Prepare the value to store if unlocked + @ (thread id, count of 0 and preserved read barrier bits), + @ or prepare to compare thread id for recursive lock check + @ (lock_word.ThreadId() ^ self->ThreadId()). + ands ip, r2, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED @ Test the non-gc bits. + bne .Lnot_unlocked @ Check if unlocked. + @ unlocked case - store r3: original lock word plus thread id, preserved read barrier bits. + strex r2, r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] + cbnz r2, .Llock_strex_fail @ If store failed, retry. + dmb ish @ Full (LoadLoad|LoadStore) memory barrier. bx lr -.Lnot_unlocked: @ r1: original lock word, r2: thread_id with count of 0 and zero read barrier bits - lsr r3, r1, LOCK_WORD_STATE_SHIFT - cbnz r3, .Lslow_lock @ if either of the top two bits are set, go slow path - eor r2, r1, r2 @ lock_word.ThreadId() ^ self->ThreadId() - uxth r2, r2 @ zero top 16 bits - cbnz r2, .Lslow_lock @ lock word and self thread id's match -> recursive lock - @ else contention, go to slow path - mov r3, r1 @ copy the lock word to check count overflow. - and r3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED @ zero the gc bits. - add r2, r3, #LOCK_WORD_THIN_LOCK_COUNT_ONE @ increment count in lock word placing in r2 to check overflow - lsr r3, r2, #LOCK_WORD_GC_STATE_SHIFT @ if the first gc state bit is set, we overflowed. - cbnz r3, .Lslow_lock @ if we overflow the count go slow path - add r2, r1, #LOCK_WORD_THIN_LOCK_COUNT_ONE @ increment count for real - strex r3, r2, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] @ strex necessary for read barrier bits - cbnz r3, .Llock_strex_fail @ strex failed, retry +.Lnot_unlocked: @ r2: original lock word, r1: thread_id, r3: r2 ^ r1 +#if LOCK_WORD_THIN_LOCK_COUNT_SHIFT + LOCK_WORD_THIN_LOCK_COUNT_SIZE != LOCK_WORD_GC_STATE_SHIFT +#error "Expecting thin lock count and gc state in consecutive bits." +#endif + @ Check lock word state and thread id together, + bfc r3, #LOCK_WORD_THIN_LOCK_COUNT_SHIFT, #(LOCK_WORD_THIN_LOCK_COUNT_SIZE + LOCK_WORD_GC_STATE_SIZE) + cbnz r3, .Lslow_lock @ if either of the top two bits are set, or the lock word's + @ thread id did not match, go slow path. + add r3, r2, #LOCK_WORD_THIN_LOCK_COUNT_ONE @ Increment the recursive lock count. + @ Extract the new thin lock count for overflow check. + ubfx r2, r3, #LOCK_WORD_THIN_LOCK_COUNT_SHIFT, #LOCK_WORD_THIN_LOCK_COUNT_SIZE + cbz r2, .Lslow_lock @ Zero as the new count indicates overflow, go slow path. + strex r2, r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] @ strex necessary for read barrier bits. + cbnz r2, .Llock_strex_fail @ If strex failed, retry. bx lr .Llock_strex_fail: b .Lretry_lock @ retry -.Lslow_lock: - SETUP_SAVE_REFS_ONLY_FRAME r1 @ save callee saves in case we block - mov r1, r9 @ pass Thread::Current - bl artLockObjectFromCode @ (Object* obj, Thread*) - RESTORE_SAVE_REFS_ONLY_FRAME - REFRESH_MARKING_REGISTER - RETURN_IF_RESULT_IS_ZERO - DELIVER_PENDING_EXCEPTION +// Note: the slow path is actually the art_quick_lock_object_no_inline (tail call). END art_quick_lock_object ENTRY art_quick_lock_object_no_inline + // This is also the slow path for art_quick_lock_object. Note that we + // need a local label, the assembler complains about target being out of + // range if we try to jump to `art_quick_lock_object_no_inline`. +.Lslow_lock: SETUP_SAVE_REFS_ONLY_FRAME r1 @ save callee saves in case we block - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current bl artLockObjectFromCode @ (Object* obj, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER @@ -739,62 +737,59 @@ END art_quick_lock_object_no_inline */ .extern artUnlockObjectFromCode ENTRY art_quick_unlock_object + ldr r1, [rSELF, #THREAD_ID_OFFSET] cbz r0, .Lslow_unlock .Lretry_unlock: #ifndef USE_READ_BARRIER - ldr r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] + ldr r2, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] #else - ldrex r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] @ Need to use atomic instructions for read barrier + @ Need to use atomic instructions for read barrier. + ldrex r2, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] #endif - lsr r2, r1, #LOCK_WORD_STATE_SHIFT - cbnz r2, .Lslow_unlock @ if either of the top two bits are set, go slow path - ldr r2, [r9, #THREAD_ID_OFFSET] - mov r3, r1 @ copy lock word to check thread id equality - and r3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED @ zero the gc bits - eor r3, r3, r2 @ lock_word.ThreadId() ^ self->ThreadId() - uxth r3, r3 @ zero top 16 bits - cbnz r3, .Lslow_unlock @ do lock word and self thread id's match? - mov r3, r1 @ copy lock word to detect transition to unlocked - and r3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED @ zero the gc bits - cmp r3, #LOCK_WORD_THIN_LOCK_COUNT_ONE - bpl .Lrecursive_thin_unlock - @ transition to unlocked - mov r3, r1 - and r3, #LOCK_WORD_GC_STATE_MASK_SHIFTED @ r3: zero except for the preserved gc bits - dmb ish @ full (LoadStore|StoreStore) memory barrier + eor r3, r2, r1 @ Prepare the value to store if simply locked + @ (mostly 0s, and preserved read barrier bits), + @ or prepare to compare thread id for recursive lock check + @ (lock_word.ThreadId() ^ self->ThreadId()). + ands ip, r3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED @ Test the non-gc bits. + bne .Lnot_simply_locked @ Locked recursively or by other thread? + @ Transition to unlocked. + dmb ish @ Full (LoadStore|StoreStore) memory barrier. #ifndef USE_READ_BARRIER str r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] #else strex r2, r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] @ strex necessary for read barrier bits - cbnz r2, .Lunlock_strex_fail @ store failed, retry + cbnz r2, .Lunlock_strex_fail @ If the store failed, retry. #endif bx lr -.Lrecursive_thin_unlock: @ r1: original lock word - sub r1, r1, #LOCK_WORD_THIN_LOCK_COUNT_ONE @ decrement count +.Lnot_simply_locked: @ r2: original lock word, r1: thread_id, r3: r2 ^ r1 +#if LOCK_WORD_THIN_LOCK_COUNT_SHIFT + LOCK_WORD_THIN_LOCK_COUNT_SIZE != LOCK_WORD_GC_STATE_SHIFT +#error "Expecting thin lock count and gc state in consecutive bits." +#endif + @ Check lock word state and thread id together, + bfc r3, #LOCK_WORD_THIN_LOCK_COUNT_SHIFT, #(LOCK_WORD_THIN_LOCK_COUNT_SIZE + LOCK_WORD_GC_STATE_SIZE) + cbnz r3, .Lslow_unlock @ if either of the top two bits are set, or the lock word's + @ thread id did not match, go slow path. + sub r3, r2, #LOCK_WORD_THIN_LOCK_COUNT_ONE @ Decrement recursive lock count. #ifndef USE_READ_BARRIER - str r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] + str r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] #else - strex r2, r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] @ strex necessary for read barrier bits - cbnz r2, .Lunlock_strex_fail @ store failed, retry + strex r2, r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] @ strex necessary for read barrier bits. + cbnz r2, .Lunlock_strex_fail @ If the store failed, retry. #endif bx lr .Lunlock_strex_fail: b .Lretry_unlock @ retry -.Lslow_unlock: - @ save callee saves in case exception allocation triggers GC - SETUP_SAVE_REFS_ONLY_FRAME r1 - mov r1, r9 @ pass Thread::Current - bl artUnlockObjectFromCode @ (Object* obj, Thread*) - RESTORE_SAVE_REFS_ONLY_FRAME - REFRESH_MARKING_REGISTER - RETURN_IF_RESULT_IS_ZERO - DELIVER_PENDING_EXCEPTION +// Note: the slow path is actually the art_quick_unlock_object_no_inline (tail call). END art_quick_unlock_object ENTRY art_quick_unlock_object_no_inline + // This is also the slow path for art_quick_unlock_object. Note that we + // need a local label, the assembler complains about target being out of + // range if we try to jump to `art_quick_unlock_object_no_inline`. +.Lslow_unlock: @ save callee saves in case exception allocation triggers GC SETUP_SAVE_REFS_ONLY_FRAME r1 - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current bl artUnlockObjectFromCode @ (Object* obj, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER @@ -832,7 +827,7 @@ ENTRY art_quick_check_instance_of .Lthrow_class_cast_exception_for_bitstring_check: SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r2 @ save all registers as basis for long jump context - mov r2, r9 @ pass Thread::Current + mov r2, rSELF @ pass Thread::Current bl artThrowClassCastExceptionForObject @ (Object*, Class*, Thread*) bkpt END art_quick_check_instance_of @@ -917,7 +912,7 @@ ENTRY art_quick_aput_obj add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET POISON_HEAP_REF r2 str r2, [r3, r1, lsl #2] - ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET] + ldr r3, [rSELF, #THREAD_CARD_TABLE_OFFSET] lsr r0, r0, #CARD_TABLE_CARD_SHIFT strb r3, [r3, r0] blx lr @@ -945,7 +940,7 @@ ENTRY art_quick_aput_obj add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET POISON_HEAP_REF r2 str r2, [r3, r1, lsl #2] - ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET] + ldr r3, [rSELF, #THREAD_CARD_TABLE_OFFSET] lsr r0, r0, #CARD_TABLE_CARD_SHIFT strb r3, [r3, r0] blx lr @@ -954,7 +949,7 @@ ENTRY art_quick_aput_obj /* No need to repeat restore cfi directives, the ones above apply here. */ SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r3 mov r1, r2 - mov r2, r9 @ pass Thread::Current + mov r2, rSELF @ pass Thread::Current bl artThrowArrayStoreException @ (Class*, Class*, Thread*) bkpt @ unreached END art_quick_aput_obj @@ -964,7 +959,7 @@ END art_quick_aput_obj .extern \entrypoint ENTRY \name SETUP_SAVE_REFS_ONLY_FRAME r1 @ save callee saves in case of GC - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current bl \entrypoint @ (uint32_t type_idx, Method* method, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER @@ -977,7 +972,7 @@ END \name .extern \entrypoint ENTRY \name SETUP_SAVE_REFS_ONLY_FRAME r2 @ save callee saves in case of GC - mov r2, r9 @ pass Thread::Current + mov r2, rSELF @ pass Thread::Current bl \entrypoint @ (uint32_t type_idx, Method* method, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER @@ -990,7 +985,7 @@ END \name .extern \entrypoint ENTRY \name SETUP_SAVE_REFS_ONLY_FRAME r3 @ save callee saves in case of GC - mov r3, r9 @ pass Thread::Current + mov r3, rSELF @ pass Thread::Current @ (uint32_t type_idx, Method* method, int32_t component_count, Thread*) bl \entrypoint RESTORE_SAVE_REFS_ONLY_FRAME @@ -1004,7 +999,7 @@ END \name .extern \entrypoint ENTRY \name SETUP_SAVE_REFS_ONLY_FRAME r12 @ save callee saves in case of GC - str r9, [sp, #-16]! @ expand the frame and pass Thread::Current + str rSELF, [sp, #-16]! @ expand the frame and pass Thread::Current .cfi_adjust_cfa_offset 16 bl \entrypoint add sp, #16 @ strip the extra frame @@ -1023,7 +1018,7 @@ END \name .extern \entrypoint ENTRY \name SETUP_SAVE_EVERYTHING_FRAME r1, \runtime_method_offset @ save everything in case of GC - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current bl \entrypoint @ (uint32_t index, Thread*) cbz r0, 1f @ If result is null, deliver the OOME. .cfi_remember_state @@ -1065,9 +1060,9 @@ ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, .extern artGet64StaticFromCompiledCode ENTRY art_quick_get64_static SETUP_SAVE_REFS_ONLY_FRAME r2 @ save callee saves in case of GC - mov r1, r9 @ pass Thread::Current - bl artGet64StaticFromCompiledCode @ (uint32_t field_idx, Thread*) - ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ + mov r1, rSELF @ pass Thread::Current + bl artGet64StaticFromCompiledCode @ (uint32_t field_idx, Thread*) + ldr r2, [rSELF, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER cbnz r2, 1f @ success if no exception pending @@ -1091,9 +1086,9 @@ TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCo .extern artGet64InstanceFromCompiledCode ENTRY art_quick_get64_instance SETUP_SAVE_REFS_ONLY_FRAME r2 @ save callee saves in case of GC - mov r2, r9 @ pass Thread::Current - bl artGet64InstanceFromCompiledCode @ (field_idx, Object*, Thread*) - ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ + mov r2, rSELF @ pass Thread::Current + bl artGet64InstanceFromCompiledCode @ (field_idx, Object*, Thread*) + ldr r2, [rSELF, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER cbnz r2, 1f @ success if no exception pending @@ -1125,7 +1120,7 @@ THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCompiled ENTRY art_quick_set64_instance SETUP_SAVE_REFS_ONLY_FRAME r12 @ save callee saves in case of GC @ r2:r3 contain the wide argument - str r9, [sp, #-16]! @ expand the frame and pass Thread::Current + str rSELF, [sp, #-16]! @ expand the frame and pass Thread::Current .cfi_adjust_cfa_offset 16 bl artSet64InstanceFromCompiledCode @ (field_idx, Object*, new_val, Thread*) add sp, #16 @ release out args @@ -1140,7 +1135,7 @@ END art_quick_set64_instance ENTRY art_quick_set64_static SETUP_SAVE_REFS_ONLY_FRAME r12 @ save callee saves in case of GC @ r2:r3 contain the wide argument - str r9, [sp, #-16]! @ expand the frame and pass Thread::Current + str rSELF, [sp, #-16]! @ expand the frame and pass Thread::Current .cfi_adjust_cfa_offset 16 bl artSet64StaticFromCompiledCode @ (field_idx, new_val, Thread*) add sp, #16 @ release out args @@ -1185,12 +1180,12 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB) .macro ART_QUICK_ALLOC_OBJECT_ROSALLOC c_name, cxx_name, isInitialized ENTRY \c_name // Fast path rosalloc allocation. - // r0: type/return value, r9: Thread::Current + // r0: type/return value, rSELF (r9): Thread::Current // r1, r2, r3, r12: free. - ldr r3, [r9, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET] // Check if the thread local + ldr r3, [rSELF, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET] // Check if the thread local // allocation stack has room. // TODO: consider using ldrd. - ldr r12, [r9, #THREAD_LOCAL_ALLOC_STACK_END_OFFSET] + ldr r12, [rSELF, #THREAD_LOCAL_ALLOC_STACK_END_OFFSET] cmp r3, r12 bhs .Lslow_path\c_name @@ -1208,7 +1203,7 @@ ENTRY \c_name // from the size. Since the size is // already aligned we can combine the // two shifts together. - add r12, r9, r3, lsr #(ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT - POINTER_SIZE_SHIFT) + add r12, rSELF, r3, lsr #(ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT - POINTER_SIZE_SHIFT) // Subtract pointer size since ther // are no runs for 0 byte allocations // and the size is already aligned. @@ -1236,9 +1231,9 @@ ENTRY \c_name // local allocation stack and // increment the thread local // allocation stack top. - ldr r1, [r9, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET] + ldr r1, [rSELF, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET] str r3, [r1], #COMPRESSED_REFERENCE_SIZE // (Increment r1 as a side effect.) - str r1, [r9, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET] + str r1, [rSELF, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET] // Decrement the size of the free list // After this "STR" the object is published to the thread local allocation stack, @@ -1287,7 +1282,7 @@ ENTRY \c_name .Lslow_path\c_name: SETUP_SAVE_REFS_ONLY_FRAME r2 @ save callee saves in case of GC - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current bl \cxx_name @ (mirror::Class* cls, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER @@ -1301,7 +1296,7 @@ ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, art // The common fast path code for art_quick_alloc_object_resolved/initialized_tlab // and art_quick_alloc_object_resolved/initialized_region_tlab. // -// r0: type r9: Thread::Current, r1, r2, r3, r12: free. +// r0: type, rSELF (r9): Thread::Current, r1, r2, r3, r12: free. // Need to preserve r0 to the slow path. // // If isInitialized=1 then the compiler assumes the object's class has already been initialized. @@ -1313,7 +1308,7 @@ ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, art #if !((THREAD_LOCAL_POS_OFFSET + 4 == THREAD_LOCAL_END_OFFSET) && (THREAD_LOCAL_POS_OFFSET % 8 == 0)) #error "Thread::thread_local_pos/end must be consecutive and are 8 byte aligned for performance" #endif - ldrd r12, r3, [r9, #THREAD_LOCAL_POS_OFFSET] + ldrd r12, r3, [rSELF, #THREAD_LOCAL_POS_OFFSET] sub r12, r3, r12 // Compute the remaining buf size. ldr r3, [r0, #MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET] // Load the object size (r3). cmp r3, r12 // Check if it fits. @@ -1326,9 +1321,9 @@ ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, art // "Point of no slow path". Won't go to the slow path from here on. OK to clobber r0 and r1. // Reload old thread_local_pos (r0) // for the return value. - ldr r2, [r9, #THREAD_LOCAL_POS_OFFSET] + ldr r2, [rSELF, #THREAD_LOCAL_POS_OFFSET] add r1, r2, r3 - str r1, [r9, #THREAD_LOCAL_POS_OFFSET] // Store new thread_local_pos. + str r1, [rSELF, #THREAD_LOCAL_POS_OFFSET] // Store new thread_local_pos. // After this "STR" the object is published to the thread local allocation stack, // and it will be observable from a runtime internal (eg. Heap::VisitObjects) point of view. // It is not yet visible to the running (user) compiled code until after the return. @@ -1346,9 +1341,9 @@ ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, art // // (Note: The actual check is done by checking that the object's class pointer is non-null. // Also, unlike rosalloc, the object can never be observed as null). - ldr r1, [r9, #THREAD_LOCAL_OBJECTS_OFFSET] // Increment thread_local_objects. + ldr r1, [rSELF, #THREAD_LOCAL_OBJECTS_OFFSET] // Increment thread_local_objects. add r1, r1, #1 - str r1, [r9, #THREAD_LOCAL_OBJECTS_OFFSET] + str r1, [rSELF, #THREAD_LOCAL_OBJECTS_OFFSET] POISON_HEAP_REF r0 str r0, [r2, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer. // Fence. This is "ish" not "ishst" so @@ -1375,12 +1370,12 @@ ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, art .macro GENERATE_ALLOC_OBJECT_RESOLVED_TLAB name, entrypoint, isInitialized ENTRY \name // Fast path tlab allocation. - // r0: type, r9: Thread::Current + // r0: type, rSELF (r9): Thread::Current // r1, r2, r3, r12: free. ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lslow_path\name, \isInitialized .Lslow_path\name: SETUP_SAVE_REFS_ONLY_FRAME r2 // Save callee saves in case of GC. - mov r1, r9 // Pass Thread::Current. + mov r1, rSELF // Pass Thread::Current. bl \entrypoint // (mirror::Class* klass, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER @@ -1397,7 +1392,7 @@ GENERATE_ALLOC_OBJECT_RESOLVED_TLAB art_quick_alloc_object_initialized_tlab, art // The common fast path code for art_quick_alloc_array_resolved/initialized_tlab // and art_quick_alloc_array_resolved/initialized_region_tlab. // -// r0: type r1: component_count r2: total_size r9: Thread::Current, r3, r12: free. +// r0: type, r1: component_count, r2: total_size, rSELF (r9): Thread::Current, r3, r12: free. // Need to preserve r0 and r1 to the slow path. .macro ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED_WITH_SIZE slowPathLabel and r2, r2, #OBJECT_ALIGNMENT_MASK_TOGGLED // Apply alignment mask @@ -1409,7 +1404,7 @@ GENERATE_ALLOC_OBJECT_RESOLVED_TLAB art_quick_alloc_object_initialized_tlab, art #if !((THREAD_LOCAL_POS_OFFSET + 4 == THREAD_LOCAL_END_OFFSET) && (THREAD_LOCAL_POS_OFFSET % 8 == 0)) #error "Thread::thread_local_pos/end must be consecutive and are 8 byte aligned for performance" #endif - ldrd r3, r12, [r9, #THREAD_LOCAL_POS_OFFSET] + ldrd r3, r12, [rSELF, #THREAD_LOCAL_POS_OFFSET] sub r12, r12, r3 // Compute the remaining buf size. cmp r2, r12 // Check if the total_size fits. // The array class is always initialized here. Unlike new-instance, @@ -1417,10 +1412,10 @@ GENERATE_ALLOC_OBJECT_RESOLVED_TLAB art_quick_alloc_object_initialized_tlab, art bhi \slowPathLabel // "Point of no slow path". Won't go to the slow path from here on. OK to clobber r0 and r1. add r2, r2, r3 - str r2, [r9, #THREAD_LOCAL_POS_OFFSET] // Store new thread_local_pos. - ldr r2, [r9, #THREAD_LOCAL_OBJECTS_OFFSET] // Increment thread_local_objects. + str r2, [rSELF, #THREAD_LOCAL_POS_OFFSET] // Store new thread_local_pos. + ldr r2, [rSELF, #THREAD_LOCAL_OBJECTS_OFFSET] // Increment thread_local_objects. add r2, r2, #1 - str r2, [r9, #THREAD_LOCAL_OBJECTS_OFFSET] + str r2, [rSELF, #THREAD_LOCAL_OBJECTS_OFFSET] POISON_HEAP_REF r0 str r0, [r3, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer. str r1, [r3, #MIRROR_ARRAY_LENGTH_OFFSET] // Store the array length. @@ -1443,7 +1438,7 @@ ENTRY \name // Fast path array allocation for region tlab allocation. // r0: mirror::Class* type // r1: int32_t component_count - // r9: thread + // rSELF (r9): thread // r2, r3, r12: free. \size_setup .Lslow_path\name ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED_WITH_SIZE .Lslow_path\name @@ -1452,7 +1447,7 @@ ENTRY \name // r1: int32_t component_count // r2: Thread* self SETUP_SAVE_REFS_ONLY_FRAME r2 // save callee saves in case of GC - mov r2, r9 // pass Thread::Current + mov r2, rSELF // pass Thread::Current bl \entrypoint RESTORE_SAVE_REFS_ONLY_FRAME REFRESH_MARKING_REGISTER @@ -1575,10 +1570,10 @@ END art_quick_implicit_suspend .extern artQuickProxyInvokeHandler ENTRY art_quick_proxy_invoke_handler SETUP_SAVE_REFS_AND_ARGS_FRAME_WITH_METHOD_IN_R0 - mov r2, r9 @ pass Thread::Current + mov r2, rSELF @ pass Thread::Current mov r3, sp @ pass SP blx artQuickProxyInvokeHandler @ (Method* proxy method, receiver, Thread*, SP) - ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ + ldr r2, [rSELF, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ // Tear down the callee-save frame. Skip arg registers. add sp, #(FRAME_SIZE_SAVE_REFS_AND_ARGS - FRAME_SIZE_SAVE_REFS_ONLY) .cfi_adjust_cfa_offset -(FRAME_SIZE_SAVE_REFS_AND_ARGS - FRAME_SIZE_SAVE_REFS_ONLY) @@ -1706,7 +1701,7 @@ END art_quick_imt_conflict_trampoline .extern artQuickResolutionTrampoline ENTRY art_quick_resolution_trampoline SETUP_SAVE_REFS_AND_ARGS_FRAME r2 - mov r2, r9 @ pass Thread::Current + mov r2, rSELF @ pass Thread::Current mov r3, sp @ pass SP blx artQuickResolutionTrampoline @ (Method* called, receiver, Thread*, SP) cbz r0, 1f @ is code pointer null? goto exception @@ -1780,10 +1775,10 @@ ENTRY art_quick_generic_jni_trampoline blx artQuickGenericJniEndTrampoline // Restore self pointer. - mov r9, r11 + mov rSELF, r11 // Pending exceptions possible. - ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ + ldr r2, [rSELF, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ cbnz r2, .Lexception_in_native // Tear down the alloca. @@ -1804,7 +1799,7 @@ ENTRY art_quick_generic_jni_trampoline .cfi_adjust_cfa_offset FRAME_SIZE_SAVE_REFS_AND_ARGS-FRAME_SIZE_SAVE_REFS_ONLY .Lexception_in_native: - ldr ip, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] + ldr ip, [rSELF, #THREAD_TOP_QUICK_FRAME_OFFSET] add ip, ip, #-1 // Remove the GenericJNI tag. ADD/SUB writing directly to SP is UNPREDICTABLE. mov sp, ip .cfi_def_cfa_register sp @@ -1815,10 +1810,10 @@ END art_quick_generic_jni_trampoline .extern artQuickToInterpreterBridge ENTRY art_quick_to_interpreter_bridge SETUP_SAVE_REFS_AND_ARGS_FRAME r1 - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current mov r2, sp @ pass SP blx artQuickToInterpreterBridge @ (Method* method, Thread*, SP) - ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ + ldr r2, [rSELF, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ // Tear down the callee-save frame. Skip arg registers. add sp, #(FRAME_SIZE_SAVE_REFS_AND_ARGS - FRAME_SIZE_SAVE_REFS_ONLY) .cfi_adjust_cfa_offset -(FRAME_SIZE_SAVE_REFS_AND_ARGS - FRAME_SIZE_SAVE_REFS_ONLY) @@ -1846,7 +1841,7 @@ ENTRY art_quick_instrumentation_entry SETUP_SAVE_REFS_AND_ARGS_FRAME r2 @ preserve r0 (not normally an arg) knowing there is a spare slot in kSaveRefsAndArgs. str r0, [sp, #4] - mov r2, r9 @ pass Thread::Current + mov r2, rSELF @ pass Thread::Current mov r3, sp @ pass SP blx artInstrumentationMethodEntryFromCode @ (Method*, Object*, Thread*, SP) cbz r0, .Ldeliver_instrumentation_entry_exception @@ -1872,7 +1867,7 @@ ENTRY art_quick_instrumentation_exit add r3, sp, #8 @ store fpr_res pointer, in kSaveEverything frame add r2, sp, #136 @ store gpr_res pointer, in kSaveEverything frame mov r1, sp @ pass SP - mov r0, r9 @ pass Thread::Current + mov r0, rSELF @ pass Thread::Current blx artInstrumentationMethodExitFromCode @ (Thread*, SP, gpr_res*, fpr_res*) cbz r0, .Ldo_deliver_instrumentation_exception @@ -1901,7 +1896,7 @@ END art_quick_instrumentation_exit .extern artDeoptimize ENTRY art_quick_deoptimize SETUP_SAVE_EVERYTHING_FRAME r0 - mov r0, r9 @ pass Thread::Current + mov r0, rSELF @ pass Thread::Current blx artDeoptimize @ (Thread*) END art_quick_deoptimize @@ -1912,7 +1907,7 @@ END art_quick_deoptimize .extern artDeoptimizeFromCompiledCode ENTRY art_quick_deoptimize_from_compiled_code SETUP_SAVE_EVERYTHING_FRAME r1 - mov r1, r9 @ pass Thread::Current + mov r1, rSELF @ pass Thread::Current blx artDeoptimizeFromCompiledCode @ (DeoptimizationKind, Thread*) END art_quick_deoptimize_from_compiled_code @@ -2691,7 +2686,7 @@ END art_quick_read_barrier_mark_introspection .extern artInvokePolymorphic ENTRY art_quick_invoke_polymorphic SETUP_SAVE_REFS_AND_ARGS_FRAME r2 - mov r2, r9 @ pass Thread::Current + mov r2, rSELF @ pass Thread::Current mov r3, sp @ pass SP mov r0, #0 @ initialize 64-bit JValue as zero. str r0, [sp, #-4]! diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index ac5b2b8b88..14d0cc7db4 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1151,45 +1151,36 @@ END art_quick_do_long_jump */ .extern artLockObjectFromCode ENTRY art_quick_lock_object - cbz w0, .Lslow_lock - add x4, x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET // exclusive load/store has no immediate anymore + ldr w1, [xSELF, #THREAD_ID_OFFSET] + cbz w0, art_quick_lock_object_no_inline + // Exclusive load/store has no immediate anymore. + add x4, x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET .Lretry_lock: - ldr w2, [xSELF, #THREAD_ID_OFFSET] // TODO: Can the thread ID really change during the loop? - ldaxr w1, [x4] // acquire needed only in most common case - and w3, w1, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED // zero the gc bits - cbnz w3, .Lnot_unlocked // already thin locked - // unlocked case - x1: original lock word that's zero except for the read barrier bits. - orr x2, x1, x2 // x2 holds thread id with count of 0 with preserved read barrier bits - stxr w3, w2, [x4] - cbnz w3, .Llock_stxr_fail // store failed, retry + ldaxr w2, [x4] // Acquire needed only in most common case. + eor w3, w2, w1 // Prepare the value to store if unlocked + // (thread id, count of 0 and preserved read barrier bits), + // or prepare to compare thread id for recursive lock check + // (lock_word.ThreadId() ^ self->ThreadId()). + tst w2, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED // Test the non-gc bits. + b.ne .Lnot_unlocked // Check if unlocked. + // unlocked case - store w3: original lock word plus thread id, preserved read barrier bits. + stxr w2, w3, [x4] + cbnz w2, .Lretry_lock // If the store failed, retry. ret -.Lnot_unlocked: // x1: original lock word - lsr w3, w1, LOCK_WORD_STATE_SHIFT - cbnz w3, .Lslow_lock // if either of the top two bits are set, go slow path - eor w2, w1, w2 // lock_word.ThreadId() ^ self->ThreadId() - uxth w2, w2 // zero top 16 bits - cbnz w2, .Lslow_lock // lock word and self thread id's match -> recursive lock - // else contention, go to slow path - and w3, w1, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED // zero the gc bits. - add w2, w3, #LOCK_WORD_THIN_LOCK_COUNT_ONE // increment count in lock word placing in w2 to check overflow - lsr w3, w2, #LOCK_WORD_GC_STATE_SHIFT // if the first gc state bit is set, we overflowed. - cbnz w3, .Lslow_lock // if we overflow the count go slow path - add w2, w1, #LOCK_WORD_THIN_LOCK_COUNT_ONE // increment count for real - stxr w3, w2, [x4] - cbnz w3, .Llock_stxr_fail // store failed, retry +.Lnot_unlocked: // w2: original lock word, w1: thread id, w3: w2 ^ w1 + // Check lock word state and thread id together, + tst w3, #(LOCK_WORD_STATE_MASK_SHIFTED | LOCK_WORD_THIN_LOCK_OWNER_MASK_SHIFTED) + b.ne art_quick_lock_object_no_inline + add w3, w2, #LOCK_WORD_THIN_LOCK_COUNT_ONE // Increment the recursive lock count. + tst w3, #LOCK_WORD_THIN_LOCK_COUNT_MASK_SHIFTED // Test the new thin lock count. + b.eq art_quick_lock_object_no_inline // Zero as the new count indicates overflow, go slow path. + stxr w2, w3, [x4] + cbnz w2, .Lretry_lock // If the store failed, retry. ret -.Llock_stxr_fail: - b .Lretry_lock // retry -.Lslow_lock: - SETUP_SAVE_REFS_ONLY_FRAME // save callee saves in case we block - mov x1, xSELF // pass Thread::Current - bl artLockObjectFromCode // (Object* obj, Thread*) - RESTORE_SAVE_REFS_ONLY_FRAME - REFRESH_MARKING_REGISTER - RETURN_IF_W0_IS_ZERO_OR_DELIVER END art_quick_lock_object ENTRY art_quick_lock_object_no_inline + // This is also the slow path for art_quick_lock_object. SETUP_SAVE_REFS_ONLY_FRAME // save callee saves in case we block mov x1, xSELF // pass Thread::Current bl artLockObjectFromCode // (Object* obj, Thread*) @@ -1206,54 +1197,46 @@ END art_quick_lock_object_no_inline */ .extern artUnlockObjectFromCode ENTRY art_quick_unlock_object - cbz x0, .Lslow_unlock - add x4, x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET // exclusive load/store has no immediate anymore + ldr w1, [xSELF, #THREAD_ID_OFFSET] + cbz x0, art_quick_unlock_object_no_inline + // Exclusive load/store has no immediate anymore. + add x4, x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET .Lretry_unlock: #ifndef USE_READ_BARRIER - ldr w1, [x4] + ldr w2, [x4] #else - ldxr w1, [x4] // Need to use atomic instructions for read barrier + ldxr w2, [x4] // Need to use atomic instructions for read barrier. #endif - lsr w2, w1, LOCK_WORD_STATE_SHIFT - cbnz w2, .Lslow_unlock // if either of the top two bits are set, go slow path - ldr w2, [xSELF, #THREAD_ID_OFFSET] - and w3, w1, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED // zero the gc bits - eor w3, w3, w2 // lock_word.ThreadId() ^ self->ThreadId() - uxth w3, w3 // zero top 16 bits - cbnz w3, .Lslow_unlock // do lock word and self thread id's match? - and w3, w1, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED // zero the gc bits - cmp w3, #LOCK_WORD_THIN_LOCK_COUNT_ONE - bpl .Lrecursive_thin_unlock - // transition to unlocked - and w3, w1, #LOCK_WORD_GC_STATE_MASK_SHIFTED // w3: zero except for the preserved read barrier bits + eor w3, w2, w1 // Prepare the value to store if simply locked + // (mostly 0s, and preserved read barrier bits), + // or prepare to compare thread id for recursive lock check + // (lock_word.ThreadId() ^ self->ThreadId()). + tst w3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED // Test the non-gc bits. + b.ne .Lnot_simply_locked // Locked recursively or by other thread? + // Transition to unlocked. #ifndef USE_READ_BARRIER stlr w3, [x4] #else - stlxr w2, w3, [x4] // Need to use atomic instructions for read barrier - cbnz w2, .Lunlock_stxr_fail // store failed, retry + stlxr w2, w3, [x4] // Need to use atomic instructions for read barrier. + cbnz w2, .Lretry_unlock // If the store failed, retry. #endif ret -.Lrecursive_thin_unlock: // w1: original lock word - sub w1, w1, #LOCK_WORD_THIN_LOCK_COUNT_ONE // decrement count +.Lnot_simply_locked: + // Check lock word state and thread id together, + tst w3, #(LOCK_WORD_STATE_MASK_SHIFTED | LOCK_WORD_THIN_LOCK_OWNER_MASK_SHIFTED) + b.ne art_quick_unlock_object_no_inline + sub w3, w2, #LOCK_WORD_THIN_LOCK_COUNT_ONE // decrement count #ifndef USE_READ_BARRIER - str w1, [x4] + str w3, [x4] #else - stxr w2, w1, [x4] // Need to use atomic instructions for read barrier - cbnz w2, .Lunlock_stxr_fail // store failed, retry + stxr w2, w3, [x4] // Need to use atomic instructions for read barrier. + cbnz w2, .Lretry_unlock // If the store failed, retry. #endif ret -.Lunlock_stxr_fail: - b .Lretry_unlock // retry -.Lslow_unlock: - SETUP_SAVE_REFS_ONLY_FRAME // save callee saves in case exception allocation triggers GC - mov x1, xSELF // pass Thread::Current - bl artUnlockObjectFromCode // (Object* obj, Thread*) - RESTORE_SAVE_REFS_ONLY_FRAME - REFRESH_MARKING_REGISTER - RETURN_IF_W0_IS_ZERO_OR_DELIVER END art_quick_unlock_object ENTRY art_quick_unlock_object_no_inline + // This is also the slow path for art_quick_unlock_object. SETUP_SAVE_REFS_ONLY_FRAME // save callee saves in case exception allocation triggers GC mov x1, xSELF // pass Thread::Current bl artUnlockObjectFromCode // (Object* obj, Thread*) diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 8ab4ce160f..b89d45f617 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1292,7 +1292,7 @@ DEFINE_FUNCTION art_quick_lock_object jz .Lslow_lock .Lretry_lock: movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%eax), %ecx // ecx := lock word - test LITERAL(LOCK_WORD_STATE_MASK), %ecx // test the 2 high bits. + test LITERAL(LOCK_WORD_STATE_MASK_SHIFTED), %ecx // test the 2 high bits. jne .Lslow_lock // slow path if either of the two high bits are set. movl %ecx, %edx // save lock word (edx) to keep read barrier bits. andl LITERAL(LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED), %ecx // zero the gc bits. @@ -1362,7 +1362,7 @@ DEFINE_FUNCTION art_quick_unlock_object .Lretry_unlock: movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%eax), %ecx // ecx := lock word movl %fs:THREAD_ID_OFFSET, %edx // edx := thread id - test LITERAL(LOCK_WORD_STATE_MASK), %ecx + test LITERAL(LOCK_WORD_STATE_MASK_SHIFTED), %ecx jnz .Lslow_unlock // lock word contains a monitor cmpw %cx, %dx // does the thread id match? jne .Lslow_unlock diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index eb945ed366..c179033e6b 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -1312,7 +1312,7 @@ DEFINE_FUNCTION art_quick_lock_object jz .Lslow_lock .Lretry_lock: movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%edi), %ecx // ecx := lock word. - test LITERAL(LOCK_WORD_STATE_MASK), %ecx // Test the 2 high bits. + test LITERAL(LOCK_WORD_STATE_MASK_SHIFTED), %ecx // Test the 2 high bits. jne .Lslow_lock // Slow path if either of the two high bits are set. movl %ecx, %edx // save lock word (edx) to keep read barrier bits. andl LITERAL(LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED), %ecx // zero the gc bits. @@ -1362,7 +1362,7 @@ DEFINE_FUNCTION art_quick_unlock_object .Lretry_unlock: movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%edi), %ecx // ecx := lock word movl %gs:THREAD_ID_OFFSET, %edx // edx := thread id - test LITERAL(LOCK_WORD_STATE_MASK), %ecx + test LITERAL(LOCK_WORD_STATE_MASK_SHIFTED), %ecx jnz .Lslow_unlock // lock word contains a monitor cmpw %cx, %dx // does the thread id match? jne .Lslow_unlock diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h index 46630dbeef..464c2b749f 100644 --- a/runtime/generated/asm_support_gen.h +++ b/runtime/generated/asm_support_gen.h @@ -90,16 +90,24 @@ DEFINE_CHECK_EQ(static_cast(CARD_TABLE_CARD_SHIFT), (static_cast DEFINE_CHECK_EQ(static_cast(MIN_LARGE_OBJECT_THRESHOLD), (static_cast(art::gc::Heap::kMinLargeObjectThreshold))) #define LOCK_WORD_STATE_SHIFT 30 DEFINE_CHECK_EQ(static_cast(LOCK_WORD_STATE_SHIFT), (static_cast(art::LockWord::kStateShift))) -#define LOCK_WORD_STATE_MASK 0xc0000000 -DEFINE_CHECK_EQ(static_cast(LOCK_WORD_STATE_MASK), (static_cast(art::LockWord::kStateMaskShifted))) +#define LOCK_WORD_STATE_MASK_SHIFTED 0xc0000000 +DEFINE_CHECK_EQ(static_cast(LOCK_WORD_STATE_MASK_SHIFTED), (static_cast(art::LockWord::kStateMaskShifted))) #define LOCK_WORD_READ_BARRIER_STATE_SHIFT 28 DEFINE_CHECK_EQ(static_cast(LOCK_WORD_READ_BARRIER_STATE_SHIFT), (static_cast(art::LockWord::kReadBarrierStateShift))) #define LOCK_WORD_READ_BARRIER_STATE_MASK 0x10000000 DEFINE_CHECK_EQ(static_cast(LOCK_WORD_READ_BARRIER_STATE_MASK), (static_cast(art::LockWord::kReadBarrierStateMaskShifted))) #define LOCK_WORD_READ_BARRIER_STATE_MASK_TOGGLED 0xefffffff DEFINE_CHECK_EQ(static_cast(LOCK_WORD_READ_BARRIER_STATE_MASK_TOGGLED), (static_cast(art::LockWord::kReadBarrierStateMaskShiftedToggled))) -#define LOCK_WORD_THIN_LOCK_COUNT_ONE 65536 -DEFINE_CHECK_EQ(static_cast(LOCK_WORD_THIN_LOCK_COUNT_ONE), (static_cast(art::LockWord::kThinLockCountOne))) +#define LOCK_WORD_THIN_LOCK_COUNT_SIZE 12 +DEFINE_CHECK_EQ(static_cast(LOCK_WORD_THIN_LOCK_COUNT_SIZE), (static_cast(art::LockWord::kThinLockCountSize))) +#define LOCK_WORD_THIN_LOCK_COUNT_SHIFT 16 +DEFINE_CHECK_EQ(static_cast(LOCK_WORD_THIN_LOCK_COUNT_SHIFT), (static_cast(art::LockWord::kThinLockCountShift))) +#define LOCK_WORD_THIN_LOCK_COUNT_MASK_SHIFTED 0xfff0000 +DEFINE_CHECK_EQ(static_cast(LOCK_WORD_THIN_LOCK_COUNT_MASK_SHIFTED), (static_cast(art::LockWord::kThinLockCountMaskShifted))) +#define LOCK_WORD_THIN_LOCK_COUNT_ONE 0x10000 +DEFINE_CHECK_EQ(static_cast(LOCK_WORD_THIN_LOCK_COUNT_ONE), (static_cast(art::LockWord::kThinLockCountOne))) +#define LOCK_WORD_THIN_LOCK_OWNER_MASK_SHIFTED 0xffff +DEFINE_CHECK_EQ(static_cast(LOCK_WORD_THIN_LOCK_OWNER_MASK_SHIFTED), (static_cast(art::LockWord::kThinLockOwnerMaskShifted))) #define LOCK_WORD_STATE_FORWARDING_ADDRESS 0x3 DEFINE_CHECK_EQ(static_cast(LOCK_WORD_STATE_FORWARDING_ADDRESS), (static_cast(art::LockWord::kStateForwardingAddress))) #define LOCK_WORD_STATE_FORWARDING_ADDRESS_OVERFLOW 0x40000000 @@ -110,6 +118,8 @@ DEFINE_CHECK_EQ(static_cast(LOCK_WORD_STATE_FORWARDING_ADDRESS_SHIFT), DEFINE_CHECK_EQ(static_cast(LOCK_WORD_GC_STATE_MASK_SHIFTED), (static_cast(art::LockWord::kGCStateMaskShifted))) #define LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED 0xcfffffff DEFINE_CHECK_EQ(static_cast(LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED), (static_cast(art::LockWord::kGCStateMaskShiftedToggled))) +#define LOCK_WORD_GC_STATE_SIZE 2 +DEFINE_CHECK_EQ(static_cast(LOCK_WORD_GC_STATE_SIZE), (static_cast(art::LockWord::kGCStateSize))) #define LOCK_WORD_GC_STATE_SHIFT 28 DEFINE_CHECK_EQ(static_cast(LOCK_WORD_GC_STATE_SHIFT), (static_cast(art::LockWord::kGCStateShift))) #define LOCK_WORD_MARK_BIT_SHIFT 29 diff --git a/runtime/lock_word.h b/runtime/lock_word.h index 09d856f89b..ce7fe34b22 100644 --- a/runtime/lock_word.h +++ b/runtime/lock_word.h @@ -75,16 +75,18 @@ class LockWord { // Remaining bits are the recursive lock count. kThinLockCountSize = 32 - kThinLockOwnerSize - kStateSize - kReadBarrierStateSize - kMarkBitStateSize, - // Thin lock bits. Owner in lowest bits. + // Thin lock bits. Owner in lowest bits. kThinLockOwnerShift = 0, kThinLockOwnerMask = (1 << kThinLockOwnerSize) - 1, + kThinLockOwnerMaskShifted = kThinLockOwnerMask << kThinLockOwnerShift, kThinLockMaxOwner = kThinLockOwnerMask, // Count in higher bits. kThinLockCountShift = kThinLockOwnerSize + kThinLockOwnerShift, kThinLockCountMask = (1 << kThinLockCountSize) - 1, kThinLockMaxCount = kThinLockCountMask, kThinLockCountOne = 1 << kThinLockCountShift, // == 65536 (0x10000) + kThinLockCountMaskShifted = kThinLockCountMask << kThinLockCountShift, // State in the highest bits. kStateShift = kReadBarrierStateSize + kThinLockCountSize + kThinLockCountShift + diff --git a/tools/cpp-define-generator/constant_lockword.def b/tools/cpp-define-generator/constant_lockword.def index 08d588505d..977d1ca12d 100644 --- a/tools/cpp-define-generator/constant_lockword.def +++ b/tools/cpp-define-generator/constant_lockword.def @@ -23,23 +23,29 @@ #define DEFINE_LOCK_WORD_EXPR(macro_name, type, constant_field_name) \ DEFINE_EXPR(LOCK_WORD_ ## macro_name, type, art::LockWord::constant_field_name) +// FIXME: The naming is inconsistent, the `Shifted` -> `_SHIFTED` suffix is sometimes missing. DEFINE_LOCK_WORD_EXPR(STATE_SHIFT, int32_t, kStateShift) -DEFINE_LOCK_WORD_EXPR(STATE_MASK, uint32_t, kStateMaskShifted) +DEFINE_LOCK_WORD_EXPR(STATE_MASK_SHIFTED, uint32_t, kStateMaskShifted) DEFINE_LOCK_WORD_EXPR(READ_BARRIER_STATE_SHIFT, int32_t, kReadBarrierStateShift) -DEFINE_LOCK_WORD_EXPR(READ_BARRIER_STATE_MASK, uint32_t, kReadBarrierStateMaskShifted) +DEFINE_LOCK_WORD_EXPR(READ_BARRIER_STATE_MASK, uint32_t, kReadBarrierStateMaskShifted) DEFINE_LOCK_WORD_EXPR(READ_BARRIER_STATE_MASK_TOGGLED, uint32_t, kReadBarrierStateMaskShiftedToggled) -DEFINE_LOCK_WORD_EXPR(THIN_LOCK_COUNT_ONE, int32_t, kThinLockCountOne) +DEFINE_LOCK_WORD_EXPR(THIN_LOCK_COUNT_SIZE, int32_t, kThinLockCountSize) +DEFINE_LOCK_WORD_EXPR(THIN_LOCK_COUNT_SHIFT, int32_t, kThinLockCountShift) +DEFINE_LOCK_WORD_EXPR(THIN_LOCK_COUNT_MASK_SHIFTED, uint32_t, kThinLockCountMaskShifted) +DEFINE_LOCK_WORD_EXPR(THIN_LOCK_COUNT_ONE, uint32_t, kThinLockCountOne) +DEFINE_LOCK_WORD_EXPR(THIN_LOCK_OWNER_MASK_SHIFTED, uint32_t, kThinLockOwnerMaskShifted) -DEFINE_LOCK_WORD_EXPR(STATE_FORWARDING_ADDRESS, uint32_t, kStateForwardingAddress) +DEFINE_LOCK_WORD_EXPR(STATE_FORWARDING_ADDRESS, uint32_t, kStateForwardingAddress) DEFINE_LOCK_WORD_EXPR(STATE_FORWARDING_ADDRESS_OVERFLOW, uint32_t, kStateForwardingAddressOverflow) DEFINE_LOCK_WORD_EXPR(STATE_FORWARDING_ADDRESS_SHIFT, uint32_t, kForwardingAddressShift) -DEFINE_LOCK_WORD_EXPR(GC_STATE_MASK_SHIFTED, uint32_t, kGCStateMaskShifted) +DEFINE_LOCK_WORD_EXPR(GC_STATE_MASK_SHIFTED, uint32_t, kGCStateMaskShifted) DEFINE_LOCK_WORD_EXPR(GC_STATE_MASK_SHIFTED_TOGGLED, uint32_t, kGCStateMaskShiftedToggled) -DEFINE_LOCK_WORD_EXPR(GC_STATE_SHIFT, int32_t, kGCStateShift) +DEFINE_LOCK_WORD_EXPR(GC_STATE_SIZE, int32_t, kGCStateSize) +DEFINE_LOCK_WORD_EXPR(GC_STATE_SHIFT, int32_t, kGCStateShift) -DEFINE_LOCK_WORD_EXPR(MARK_BIT_SHIFT, int32_t, kMarkBitStateShift) -DEFINE_LOCK_WORD_EXPR(MARK_BIT_MASK_SHIFTED, uint32_t, kMarkBitStateMaskShifted) +DEFINE_LOCK_WORD_EXPR(MARK_BIT_SHIFT, int32_t, kMarkBitStateShift) +DEFINE_LOCK_WORD_EXPR(MARK_BIT_MASK_SHIFTED, uint32_t, kMarkBitStateMaskShifted) #undef DEFINE_LOCK_WORD_EXPR -- GitLab From 68fefacc54b9d918a5b17b99ac7e72421d1b1f94 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Thu, 10 May 2018 17:49:33 +0100 Subject: [PATCH 426/749] Move and rewrite bit loading/storing methods. Move bit loading/storing methods to BitMemoryRegion and rewrite them. Enforce natural alignment of the data pointer in BitMemoryRegion. This probably would not be reasonable in MemoryRegion, but it is fine here since the BitMemoryRegion already has extra bit offset. The alignment makes it possible to simplify and optimize the methods. This makes the stackmap reading code 33% faster. (measured by the time needed to verify all stackmap fields on ARM). Test: m -j40 test-art-host-gtest Change-Id: I00be8052969a6056b262df4cd2066ffd86043196 --- compiler/optimizing/stack_map_stream.cc | 12 ++- libartbase/Android.bp | 1 + libartbase/base/bit_memory_region.h | 108 ++++++++++++++++++---- libartbase/base/bit_memory_region_test.cc | 83 +++++++++++++++++ libartbase/base/bit_utils.h | 5 + libartbase/base/memory_region.cc | 32 ------- libartbase/base/memory_region.h | 61 ------------ libartbase/base/memory_region_test.cc | 33 ------- runtime/method_info.h | 10 +- runtime/stack_map.h | 16 ++-- 10 files changed, 197 insertions(+), 164 deletions(-) create mode 100644 libartbase/base/bit_memory_region_test.cc diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 7010e3f380..bf7c5542ef 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -427,10 +427,11 @@ void StackMapStream::FillInCodeInfo(MemoryRegion region) { if (stack_mask_bits > 0) { size_t stack_mask_bytes = RoundUp(stack_mask_bits, kBitsPerByte) / kBitsPerByte; for (size_t i = 0; i < encoding.stack_mask.num_entries; ++i) { - MemoryRegion source(&stack_masks_[i * stack_mask_bytes], stack_mask_bytes); - BitMemoryRegion stack_mask = code_info.GetStackMask(i, encoding); - for (size_t bit_index = 0; bit_index < stack_mask_bits; ++bit_index) { - stack_mask.StoreBit(bit_index, source.LoadBit(bit_index)); + BitMemoryRegion src(MemoryRegion(&stack_masks_[i * stack_mask_bytes], stack_mask_bytes)); + BitMemoryRegion dst = code_info.GetStackMask(i, encoding); + for (size_t bit_index = 0; bit_index < stack_mask_bits; bit_index += BitSizeOf()) { + size_t num_bits = std::min(stack_mask_bits - bit_index, BitSizeOf()); + dst.StoreBits(bit_index, src.LoadBits(bit_index, num_bits), num_bits); } } } @@ -600,8 +601,9 @@ size_t StackMapStream::PrepareStackMasks(size_t entry_size_in_bits) { for (StackMapEntry& stack_map : stack_maps_) { size_t index = dedup.size(); MemoryRegion stack_mask(stack_masks_.data() + index * byte_entry_size, byte_entry_size); + BitMemoryRegion stack_mask_bits(stack_mask); for (size_t i = 0; i < entry_size_in_bits; i++) { - stack_mask.StoreBit(i, stack_map.sp_mask != nullptr && stack_map.sp_mask->IsBitSet(i)); + stack_mask_bits.StoreBit(i, stack_map.sp_mask != nullptr && stack_map.sp_mask->IsBitSet(i)); } stack_map.stack_mask_index = dedup.emplace(stack_mask, index).first->second; } diff --git a/libartbase/Android.bp b/libartbase/Android.bp index c8a06ed3b0..692c97f1c0 100644 --- a/libartbase/Android.bp +++ b/libartbase/Android.bp @@ -147,6 +147,7 @@ art_cc_test { "arch/instruction_set_test.cc", "base/arena_allocator_test.cc", "base/bit_field_test.cc", + "base/bit_memory_region_test.cc", "base/bit_string_test.cc", "base/bit_struct_test.cc", "base/bit_utils_test.cc", diff --git a/libartbase/base/bit_memory_region.h b/libartbase/base/bit_memory_region.h index f3926bc9d8..dd16957340 100644 --- a/libartbase/base/bit_memory_region.h +++ b/libartbase/base/bit_memory_region.h @@ -19,6 +19,9 @@ #include "memory_region.h" +#include "bit_utils.h" +#include "memory_tool.h" + namespace art { // Bit memory region is a bit offset subregion of a normal memoryregion. This is useful for @@ -26,46 +29,113 @@ namespace art { class BitMemoryRegion FINAL : public ValueObject { public: BitMemoryRegion() = default; - ALWAYS_INLINE BitMemoryRegion(MemoryRegion region, size_t bit_offset, size_t bit_size) { - bit_start_ = bit_offset % kBitsPerByte; - const size_t start = bit_offset / kBitsPerByte; - const size_t end = (bit_offset + bit_size + kBitsPerByte - 1) / kBitsPerByte; - region_ = region.Subregion(start, end - start); + ALWAYS_INLINE explicit BitMemoryRegion(MemoryRegion region) + : data_(reinterpret_cast(AlignDown(region.pointer(), sizeof(uintptr_t)))), + bit_start_(8 * (reinterpret_cast(region.pointer()) % sizeof(uintptr_t))), + bit_size_(region.size_in_bits()) { + } + ALWAYS_INLINE BitMemoryRegion(MemoryRegion region, size_t bit_offset, size_t bit_length) + : BitMemoryRegion(region) { + DCHECK_LE(bit_offset, bit_size_); + DCHECK_LE(bit_length, bit_size_ - bit_offset); + bit_start_ += bit_offset; + bit_size_ = bit_length; } - void* pointer() const { return region_.pointer(); } - size_t size() const { return region_.size(); } - size_t BitOffset() const { return bit_start_; } + ALWAYS_INLINE bool IsValid() const { return data_ != nullptr; } + size_t size_in_bits() const { - return region_.size_in_bits(); + return bit_size_; } - ALWAYS_INLINE BitMemoryRegion Subregion(size_t bit_offset, size_t bit_size) const { - return BitMemoryRegion(region_, bit_start_ + bit_offset, bit_size); + ALWAYS_INLINE BitMemoryRegion Subregion(size_t bit_offset, size_t bit_length) const { + DCHECK_LE(bit_offset, bit_size_); + DCHECK_LE(bit_length, bit_size_ - bit_offset); + BitMemoryRegion result = *this; + result.bit_start_ += bit_offset; + result.bit_size_ = bit_length; + return result; } // Load a single bit in the region. The bit at offset 0 is the least // significant bit in the first byte. + ATTRIBUTE_NO_SANITIZE_ADDRESS // We might touch extra bytes due to the alignment. ALWAYS_INLINE bool LoadBit(uintptr_t bit_offset) const { - return region_.LoadBit(bit_offset + bit_start_); + DCHECK_LT(bit_offset, bit_size_); + size_t index = (bit_start_ + bit_offset) / kBitsPerIntPtrT; + size_t shift = (bit_start_ + bit_offset) % kBitsPerIntPtrT; + return ((data_[index] >> shift) & 1) != 0; } ALWAYS_INLINE void StoreBit(uintptr_t bit_offset, bool value) const { - region_.StoreBit(bit_offset + bit_start_, value); + DCHECK_LT(bit_offset, bit_size_); + uint8_t* data = reinterpret_cast(data_); + size_t index = (bit_start_ + bit_offset) / kBitsPerByte; + size_t shift = (bit_start_ + bit_offset) % kBitsPerByte; + data[index] &= ~(1 << shift); // Clear bit. + data[index] |= (value ? 1 : 0) << shift; // Set bit. + DCHECK_EQ(value, LoadBit(bit_offset)); + } + + // Load `bit_length` bits from `data` starting at given `bit_offset`. + // The least significant bit is stored in the smallest memory offset. + ATTRIBUTE_NO_SANITIZE_ADDRESS // We might touch extra bytes due to the alignment. + ALWAYS_INLINE uint32_t LoadBits(size_t bit_offset, size_t bit_length) const { + DCHECK(IsAligned(data_)); + DCHECK_LE(bit_offset, bit_size_); + DCHECK_LE(bit_length, bit_size_ - bit_offset); + DCHECK_LE(bit_length, BitSizeOf()); + if (bit_length == 0) { + return 0; + } + uintptr_t mask = std::numeric_limits::max() >> (kBitsPerIntPtrT - bit_length); + size_t index = (bit_start_ + bit_offset) / kBitsPerIntPtrT; + size_t shift = (bit_start_ + bit_offset) % kBitsPerIntPtrT; + uintptr_t value = data_[index] >> shift; + size_t finished_bits = kBitsPerIntPtrT - shift; + if (finished_bits < bit_length) { + value |= data_[index + 1] << finished_bits; + } + return value & mask; } - ALWAYS_INLINE uint32_t LoadBits(uintptr_t bit_offset, size_t length) const { - return region_.LoadBits(bit_offset + bit_start_, length); + // Store `bit_length` bits in `data` starting at given `bit_offset`. + // The least significant bit is stored in the smallest memory offset. + ALWAYS_INLINE void StoreBits(size_t bit_offset, uint32_t value, size_t bit_length) { + DCHECK_LE(bit_offset, bit_size_); + DCHECK_LE(bit_length, bit_size_ - bit_offset); + DCHECK_LE(bit_length, BitSizeOf()); + DCHECK_LE(value, MaxInt(bit_length)); + if (bit_length == 0) { + return; + } + // Write data byte by byte to avoid races with other threads + // on bytes that do not overlap with this region. + uint8_t* data = reinterpret_cast(data_); + uint32_t mask = std::numeric_limits::max() >> (BitSizeOf() - bit_length); + size_t index = (bit_start_ + bit_offset) / kBitsPerByte; + size_t shift = (bit_start_ + bit_offset) % kBitsPerByte; + data[index] &= ~(mask << shift); // Clear bits. + data[index] |= (value << shift); // Set bits. + size_t finished_bits = kBitsPerByte - shift; + for (int i = 1; finished_bits < bit_length; i++, finished_bits += kBitsPerByte) { + data[index + i] &= ~(mask >> finished_bits); // Clear bits. + data[index + i] |= (value >> finished_bits); // Set bits. + } + DCHECK_EQ(value, LoadBits(bit_offset, bit_length)); } - // Store at a bit offset from inside the bit memory region. - ALWAYS_INLINE void StoreBits(uintptr_t bit_offset, uint32_t value, size_t length) { - region_.StoreBits(bit_offset + bit_start_, value, length); + ALWAYS_INLINE bool Equals(const BitMemoryRegion& other) const { + return data_ == other.data_ && + bit_start_ == other.bit_start_ && + bit_size_ == other.bit_size_; } private: - MemoryRegion region_; + // The data pointer must be naturally aligned. This makes loading code faster. + uintptr_t* data_ = nullptr; size_t bit_start_ = 0; + size_t bit_size_ = 0; }; } // namespace art diff --git a/libartbase/base/bit_memory_region_test.cc b/libartbase/base/bit_memory_region_test.cc new file mode 100644 index 0000000000..b7546985a9 --- /dev/null +++ b/libartbase/base/bit_memory_region_test.cc @@ -0,0 +1,83 @@ +/* + * 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. + */ + +#include "bit_memory_region.h" + +#include "gtest/gtest.h" + +namespace art { + +static void CheckBits(uint8_t* data, + size_t size, + uint32_t init, + size_t offset, + size_t length, + uint32_t value) { + for (size_t i = 0; i < size * kBitsPerByte; i++) { + uint8_t expected = (offset <= i && i < offset + length) ? value >> (i - offset) : init; + uint8_t actual = data[i / kBitsPerByte] >> (i % kBitsPerByte); + EXPECT_EQ(expected & 1, actual & 1); + } +} + +TEST(BitMemoryRegion, TestBit) { + uint8_t data[sizeof(uint32_t) * 2]; + for (size_t bit_offset = 0; bit_offset < 2 * sizeof(uint32_t) * kBitsPerByte; ++bit_offset) { + for (uint32_t initial_value = 0; initial_value <= 1; initial_value++) { + for (uint32_t value = 0; value <= 1; value++) { + // Check Store and Load with bit_offset set on the region. + std::fill_n(data, sizeof(data), initial_value * 0xFF); + BitMemoryRegion bmr1(MemoryRegion(&data, sizeof(data)), bit_offset, 1); + bmr1.StoreBit(0, value); + EXPECT_EQ(bmr1.LoadBit(0), value); + CheckBits(data, sizeof(data), initial_value, bit_offset, 1, value); + // Check Store and Load with bit_offset set on the methods. + std::fill_n(data, sizeof(data), initial_value * 0xFF); + BitMemoryRegion bmr2(MemoryRegion(&data, sizeof(data))); + bmr2.StoreBit(bit_offset, value); + EXPECT_EQ(bmr2.LoadBit(bit_offset), value); + CheckBits(data, sizeof(data), initial_value, bit_offset, 1, value); + } + } + } +} + +TEST(BitMemoryRegion, TestBits) { + uint8_t data[sizeof(uint32_t) * 4]; + for (size_t bit_offset = 0; bit_offset < 3 * sizeof(uint32_t) * kBitsPerByte; ++bit_offset) { + uint32_t mask = 0; + for (size_t bit_length = 0; bit_length < sizeof(uint32_t) * kBitsPerByte; ++bit_length) { + const uint32_t value = 0xDEADBEEF & mask; + for (uint32_t initial_value = 0; initial_value <= 1; initial_value++) { + // Check Store and Load with bit_offset set on the region. + std::fill_n(data, sizeof(data), initial_value * 0xFF); + BitMemoryRegion bmr1(MemoryRegion(&data, sizeof(data)), bit_offset, bit_length); + bmr1.StoreBits(0, value, bit_length); + EXPECT_EQ(bmr1.LoadBits(0, bit_length), value); + CheckBits(data, sizeof(data), initial_value, bit_offset, bit_length, value); + // Check Store and Load with bit_offset set on the methods. + std::fill_n(data, sizeof(data), initial_value * 0xFF); + BitMemoryRegion bmr2(MemoryRegion(&data, sizeof(data))); + bmr2.StoreBits(bit_offset, value, bit_length); + EXPECT_EQ(bmr2.LoadBits(bit_offset, bit_length), value); + CheckBits(data, sizeof(data), initial_value, bit_offset, bit_length, value); + } + mask = (mask << 1) | 1; + } + } +} + +} // namespace art diff --git a/libartbase/base/bit_utils.h b/libartbase/base/bit_utils.h index 04f0e8518c..58cc78ccca 100644 --- a/libartbase/base/bit_utils.h +++ b/libartbase/base/bit_utils.h @@ -22,6 +22,7 @@ #include +#include "globals.h" #include "stl_util_identity.h" namespace art { @@ -499,6 +500,10 @@ inline static constexpr T BitFieldExtract(T value, size_t lsb, size_t width) { return bitfield_unsigned; } +inline static constexpr size_t BitsToBytesRoundUp(size_t num_bits) { + return RoundUp(num_bits, kBitsPerByte) / kBitsPerByte; +} + } // namespace art #endif // ART_LIBARTBASE_BASE_BIT_UTILS_H_ diff --git a/libartbase/base/memory_region.cc b/libartbase/base/memory_region.cc index 862ff73639..d207872487 100644 --- a/libartbase/base/memory_region.cc +++ b/libartbase/base/memory_region.cc @@ -29,36 +29,4 @@ void MemoryRegion::CopyFrom(size_t offset, const MemoryRegion& from) const { memmove(reinterpret_cast(begin() + offset), from.pointer(), from.size()); } -void MemoryRegion::StoreBits(uintptr_t bit_offset, uint32_t value, size_t length) { - DCHECK_LE(value, MaxInt(length)); - DCHECK_LE(length, BitSizeOf()); - DCHECK_LE(bit_offset + length, size_in_bits()); - if (length == 0) { - return; - } - // Bits are stored in this order {7 6 5 4 3 2 1 0}. - // How many remaining bits in current byte is (bit_offset % kBitsPerByte) + 1. - uint8_t* out = ComputeInternalPointer(bit_offset >> kBitsPerByteLog2); - size_t orig_len = length; - uint32_t orig_value = value; - uintptr_t bit_remainder = bit_offset % kBitsPerByte; - while (true) { - const uintptr_t remaining_bits = kBitsPerByte - bit_remainder; - if (length <= remaining_bits) { - // Length is smaller than all of remainder bits. - size_t mask = ((1 << length) - 1) << bit_remainder; - *out = (*out & ~mask) | (value << bit_remainder); - break; - } - // Copy remaining bits in current byte. - size_t value_mask = (1 << remaining_bits) - 1; - *out = (*out & ~(value_mask << bit_remainder)) | ((value & value_mask) << bit_remainder); - value >>= remaining_bits; - bit_remainder = 0; - length -= remaining_bits; - ++out; - } - DCHECK_EQ(LoadBits(bit_offset, orig_len), orig_value) << bit_offset << " " << orig_len; -} - } // namespace art diff --git a/libartbase/base/memory_region.h b/libartbase/base/memory_region.h index 3d00f5b939..206032923f 100644 --- a/libartbase/base/memory_region.h +++ b/libartbase/base/memory_region.h @@ -109,67 +109,6 @@ class MemoryRegion FINAL : public ValueObject { return ComputeInternalPointer(offset); } - // Load a single bit in the region. The bit at offset 0 is the least - // significant bit in the first byte. - ALWAYS_INLINE bool LoadBit(uintptr_t bit_offset) const { - uint8_t bit_mask; - uint8_t byte = *ComputeBitPointer(bit_offset, &bit_mask); - return byte & bit_mask; - } - - ALWAYS_INLINE void StoreBit(uintptr_t bit_offset, bool value) const { - uint8_t bit_mask; - uint8_t* byte = ComputeBitPointer(bit_offset, &bit_mask); - if (value) { - *byte |= bit_mask; - } else { - *byte &= ~bit_mask; - } - } - - // Load `length` bits from the region starting at bit offset `bit_offset`. - // The bit at the smallest offset is the least significant bit in the - // loaded value. `length` must not be larger than the number of bits - // contained in the return value (32). - ALWAYS_INLINE uint32_t LoadBits(uintptr_t bit_offset, size_t length) const { - DCHECK_LE(length, BitSizeOf()); - DCHECK_LE(bit_offset + length, size_in_bits()); - if (UNLIKELY(length == 0)) { - // Do not touch any memory if the range is empty. - return 0; - } - const uint8_t* address = begin() + bit_offset / kBitsPerByte; - const uint32_t shift = bit_offset & (kBitsPerByte - 1); - // Load the value (reading only the strictly needed bytes). - const uint32_t load_bit_count = shift + length; - uint32_t value = address[0] >> shift; - if (load_bit_count > 8) { - value |= static_cast(address[1]) << (8 - shift); - if (load_bit_count > 16) { - value |= static_cast(address[2]) << (16 - shift); - if (load_bit_count > 24) { - value |= static_cast(address[3]) << (24 - shift); - if (load_bit_count > 32) { - value |= static_cast(address[4]) << (32 - shift); - } - } - } - } - // Clear unwanted most significant bits. - uint32_t clear_bit_count = BitSizeOf(value) - length; - value = (value << clear_bit_count) >> clear_bit_count; - for (size_t i = 0; i < length; ++i) { - DCHECK_EQ((value >> i) & 1, LoadBit(bit_offset + i)); - } - return value; - } - - // Store `value` on `length` bits in the region starting at bit offset - // `bit_offset`. The bit at the smallest offset is the least significant - // bit of the stored `value`. `value` must not be larger than `length` - // bits. - void StoreBits(uintptr_t bit_offset, uint32_t value, size_t length); - void CopyFrom(size_t offset, const MemoryRegion& from) const; template diff --git a/libartbase/base/memory_region_test.cc b/libartbase/base/memory_region_test.cc index e3aead47fa..72e03a485a 100644 --- a/libartbase/base/memory_region_test.cc +++ b/libartbase/base/memory_region_test.cc @@ -18,8 +18,6 @@ #include "gtest/gtest.h" -#include "bit_memory_region.h" - namespace art { TEST(MemoryRegion, LoadUnaligned) { @@ -57,35 +55,4 @@ TEST(MemoryRegion, StoreUnaligned) { } } -TEST(MemoryRegion, TestBits) { - const size_t n = 8; - uint8_t data[n] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - MemoryRegion region(&data, n); - uint32_t value = 0xDEADBEEF; - // Try various offsets and lengths. - for (size_t bit_offset = 0; bit_offset < 2 * kBitsPerByte; ++bit_offset) { - for (size_t length = 0; length < 2 * kBitsPerByte; ++length) { - const uint32_t length_mask = (1 << length) - 1; - uint32_t masked_value = value & length_mask; - BitMemoryRegion bmr(region, bit_offset, length); - region.StoreBits(bit_offset, masked_value, length); - EXPECT_EQ(region.LoadBits(bit_offset, length), masked_value); - EXPECT_EQ(bmr.LoadBits(0, length), masked_value); - // Check adjacent bits to make sure they were not incorrectly cleared. - EXPECT_EQ(region.LoadBits(0, bit_offset), (1u << bit_offset) - 1); - EXPECT_EQ(region.LoadBits(bit_offset + length, length), length_mask); - region.StoreBits(bit_offset, length_mask, length); - // Store with bit memory region. - bmr.StoreBits(0, masked_value, length); - EXPECT_EQ(bmr.LoadBits(0, length), masked_value); - // Check adjacent bits to make sure they were not incorrectly cleared. - EXPECT_EQ(region.LoadBits(0, bit_offset), (1u << bit_offset) - 1); - EXPECT_EQ(region.LoadBits(bit_offset + length, length), length_mask); - region.StoreBits(bit_offset, length_mask, length); - // Flip the value to try different edge bit combinations. - value = ~value; - } - } -} - } // namespace art diff --git a/runtime/method_info.h b/runtime/method_info.h index b00ddc660f..6f74678e42 100644 --- a/runtime/method_info.h +++ b/runtime/method_info.h @@ -21,7 +21,7 @@ #include "base/leb128.h" #include "base/macros.h" -#include "base/memory_region.h" +#include "base/bit_memory_region.h" namespace art { @@ -35,8 +35,8 @@ class MethodInfo { explicit MethodInfo(const uint8_t* ptr) { if (ptr != nullptr) { num_method_indices_ = DecodeUnsignedLeb128(&ptr); - region_ = MemoryRegion(const_cast(ptr), - num_method_indices_ * sizeof(MethodIndexType)); + region_ = BitMemoryRegion( + MemoryRegion(const_cast(ptr), num_method_indices_ * sizeof(MethodIndexType))); } } @@ -44,7 +44,7 @@ class MethodInfo { MethodInfo(uint8_t* ptr, size_t num_method_indices) : num_method_indices_(num_method_indices) { DCHECK(ptr != nullptr); ptr = EncodeUnsignedLeb128(ptr, num_method_indices_); - region_ = MemoryRegion(ptr, num_method_indices_ * sizeof(MethodIndexType)); + region_ = BitMemoryRegion(MemoryRegion(ptr, num_method_indices_ * sizeof(MethodIndexType))); } static size_t ComputeSize(size_t num_method_indices) { @@ -71,7 +71,7 @@ class MethodInfo { private: size_t num_method_indices_ = 0u; - MemoryRegion region_; + BitMemoryRegion region_; }; } // namespace art diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 38397643b6..7d3d56a1d2 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -452,7 +452,7 @@ class DexRegisterMap { explicit DexRegisterMap(MemoryRegion region) : region_(region) {} DexRegisterMap() {} - bool IsValid() const { return region_.pointer() != nullptr; } + bool IsValid() const { return region_.IsValid(); } // Get the surface kind of Dex register `dex_register_number`. DexRegisterLocation::Kind GetLocationKind(uint16_t dex_register_number, @@ -627,7 +627,7 @@ class DexRegisterMap { // Return the size of the DexRegisterMap object, in bytes. size_t Size() const { - return region_.size(); + return BitsToBytesRoundUp(region_.size_in_bits()); } void Dump(VariableIndentationOutputStream* vios, @@ -650,7 +650,7 @@ class DexRegisterMap { static constexpr int kFixedSize = 0; - MemoryRegion region_; + BitMemoryRegion region_; friend class CodeInfo; friend class StackMapStream; @@ -678,7 +678,7 @@ struct FieldEncoding { template ALWAYS_INLINE void Store(Region region, int32_t value) const { - region.StoreBits(start_offset_, value - min_value_, BitSize()); + region.StoreBits(start_offset_, static_cast(value - min_value_), BitSize()); DCHECK_EQ(Load(region), value); } @@ -805,7 +805,7 @@ class StackMap { StackMap() {} explicit StackMap(BitMemoryRegion region) : region_(region) {} - ALWAYS_INLINE bool IsValid() const { return region_.pointer() != nullptr; } + ALWAYS_INLINE bool IsValid() const { return region_.IsValid(); } ALWAYS_INLINE uint32_t GetDexPc(const StackMapEncoding& encoding) const { return encoding.GetDexPcEncoding().Load(region_); @@ -868,9 +868,7 @@ class StackMap { } ALWAYS_INLINE bool Equals(const StackMap& other) const { - return region_.pointer() == other.region_.pointer() && - region_.size() == other.region_.size() && - region_.BitOffset() == other.region_.BitOffset(); + return region_.Equals(other.region_); } void Dump(VariableIndentationOutputStream* vios, @@ -1257,7 +1255,7 @@ class InvokeInfo { return method_info.GetMethodIndex(GetMethodIndexIdx(encoding)); } - bool IsValid() const { return region_.pointer() != nullptr; } + bool IsValid() const { return region_.IsValid(); } private: BitMemoryRegion region_; -- GitLab From 2c5827a67a7f3d307384e98757f91c8b5d49f94f Mon Sep 17 00:00:00 2001 From: Treehugger Robot Date: Thu, 17 May 2018 22:26:08 +0000 Subject: [PATCH 427/749] ART: Simplify operands in InstructionBuilder Test: art/test.py --host --optimizing --jit -r Change-Id: Ia408ae00637aa6a05a3ba54e1144ea00c6b335b5 --- compiler/optimizing/instruction_builder.cc | 165 ++++++--------------- compiler/optimizing/instruction_builder.h | 31 +--- libdexfile/dex/dex_instruction-inl.h | 3 +- libdexfile/dex/dex_instruction.h | 4 +- 4 files changed, 53 insertions(+), 150 deletions(-) diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 42031f9e25..24dc2ee9b4 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -448,11 +448,9 @@ void HInstructionBuilder::BuildIntrinsic(ArtMethod* method) { invoke_type, target_method, HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + RangeInstructionOperands operands(graph_->GetNumberOfVRegs() - in_vregs, in_vregs); HandleInvoke(invoke, - in_vregs, - /* args */ nullptr, - graph_->GetNumberOfVRegs() - in_vregs, - /* is_range */ true, + operands, dex_file_->GetMethodShorty(method_idx), /* clinit_check */ nullptr, /* is_unresolved */ false); @@ -916,10 +914,7 @@ static bool IsStringConstructor(ArtMethod* method) { bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, uint32_t dex_pc, uint32_t method_idx, - uint32_t number_of_vreg_arguments, - bool is_range, - uint32_t* args, - uint32_t register_index) { + const InstructionOperands& operands) { InvokeType invoke_type = GetInvokeTypeFromOpCode(instruction.Opcode()); const char* descriptor = dex_file_->GetMethodShorty(method_idx); DataType::Type return_type = DataType::FromShorty(descriptor[0]); @@ -943,12 +938,9 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, method_idx, invoke_type); return HandleInvoke(invoke, - number_of_vreg_arguments, - args, - register_index, - is_range, + operands, descriptor, - nullptr, /* clinit_check */ + nullptr /* clinit_check */, true /* is_unresolved */); } @@ -976,12 +968,7 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, invoke_type, target_method, HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit); - return HandleStringInit(invoke, - number_of_vreg_arguments, - args, - register_index, - is_range, - descriptor); + return HandleStringInit(invoke, operands, descriptor); } // Potential class initialization check, in the case of a static method call. @@ -1042,26 +1029,16 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, ImTable::GetImtIndex(resolved_method)); } - return HandleInvoke(invoke, - number_of_vreg_arguments, - args, - register_index, - is_range, - descriptor, - clinit_check, - false /* is_unresolved */); + return HandleInvoke(invoke, operands, descriptor, clinit_check, false /* is_unresolved */); } bool HInstructionBuilder::BuildInvokePolymorphic(const Instruction& instruction ATTRIBUTE_UNUSED, uint32_t dex_pc, uint32_t method_idx, dex::ProtoIndex proto_idx, - uint32_t number_of_vreg_arguments, - bool is_range, - uint32_t* args, - uint32_t register_index) { + const InstructionOperands& operands) { const char* descriptor = dex_file_->GetShorty(proto_idx); - DCHECK_EQ(1 + ArtMethod::NumArgRegisters(descriptor), number_of_vreg_arguments); + DCHECK_EQ(1 + ArtMethod::NumArgRegisters(descriptor), operands.GetNumberOfOperands()); DataType::Type return_type = DataType::FromShorty(descriptor[0]); size_t number_of_arguments = strlen(descriptor); HInvoke* invoke = new (allocator_) HInvokePolymorphic(allocator_, @@ -1070,10 +1047,7 @@ bool HInstructionBuilder::BuildInvokePolymorphic(const Instruction& instruction dex_pc, method_idx); return HandleInvoke(invoke, - number_of_vreg_arguments, - args, - register_index, - is_range, + operands, descriptor, nullptr /* clinit_check */, false /* is_unresolved */); @@ -1222,26 +1196,22 @@ HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke( } bool HInstructionBuilder::SetupInvokeArguments(HInvoke* invoke, - uint32_t number_of_vreg_arguments, - uint32_t* args, - uint32_t register_index, - bool is_range, + const InstructionOperands& operands, const char* descriptor, size_t start_index, size_t* argument_index) { uint32_t descriptor_index = 1; // Skip the return type. - + const size_t number_of_operands = operands.GetNumberOfOperands(); for (size_t i = start_index; // Make sure we don't go over the expected arguments or over the number of // dex registers given. If the instruction was seen as dead by the verifier, // it hasn't been properly checked. - (i < number_of_vreg_arguments) && (*argument_index < invoke->GetNumberOfArguments()); + (i < number_of_operands) && (*argument_index < invoke->GetNumberOfArguments()); i++, (*argument_index)++) { DataType::Type type = DataType::FromShorty(descriptor[descriptor_index++]); bool is_wide = (type == DataType::Type::kInt64) || (type == DataType::Type::kFloat64); - if (!is_range - && is_wide - && ((i + 1 == number_of_vreg_arguments) || (args[i] + 1 != args[i + 1]))) { + if (is_wide && ((i + 1 == number_of_operands) || + (operands.GetOperand(i) + 1 != operands.GetOperand(i + 1)))) { // Longs and doubles should be in pairs, that is, sequential registers. The verifier should // reject any class where this is violated. However, the verifier only does these checks // on non trivially dead instructions, so we just bailout the compilation. @@ -1252,7 +1222,7 @@ bool HInstructionBuilder::SetupInvokeArguments(HInvoke* invoke, MethodCompilationStat::kNotCompiledMalformedOpcode); return false; } - HInstruction* arg = LoadLocal(is_range ? register_index + i : args[i], type); + HInstruction* arg = LoadLocal(operands.GetOperand(i), type); invoke->SetArgumentAt(*argument_index, arg); if (is_wide) { i++; @@ -1279,10 +1249,7 @@ bool HInstructionBuilder::SetupInvokeArguments(HInvoke* invoke, } bool HInstructionBuilder::HandleInvoke(HInvoke* invoke, - uint32_t number_of_vreg_arguments, - uint32_t* args, - uint32_t register_index, - bool is_range, + const InstructionOperands& operands, const char* descriptor, HClinitCheck* clinit_check, bool is_unresolved) { @@ -1291,7 +1258,7 @@ bool HInstructionBuilder::HandleInvoke(HInvoke* invoke, size_t start_index = 0; size_t argument_index = 0; if (invoke->GetInvokeType() != InvokeType::kStatic) { // Instance call. - uint32_t obj_reg = is_range ? register_index : args[0]; + uint32_t obj_reg = operands.GetOperand(0); HInstruction* arg = is_unresolved ? LoadLocal(obj_reg, DataType::Type::kReference) : LoadNullCheckedLocal(obj_reg, invoke->GetDexPc()); @@ -1300,14 +1267,7 @@ bool HInstructionBuilder::HandleInvoke(HInvoke* invoke, argument_index = 1; } - if (!SetupInvokeArguments(invoke, - number_of_vreg_arguments, - args, - register_index, - is_range, - descriptor, - start_index, - &argument_index)) { + if (!SetupInvokeArguments(invoke, operands, descriptor, start_index, &argument_index)) { return false; } @@ -1327,24 +1287,14 @@ bool HInstructionBuilder::HandleInvoke(HInvoke* invoke, } bool HInstructionBuilder::HandleStringInit(HInvoke* invoke, - uint32_t number_of_vreg_arguments, - uint32_t* args, - uint32_t register_index, - bool is_range, + const InstructionOperands& operands, const char* descriptor) { DCHECK(invoke->IsInvokeStaticOrDirect()); DCHECK(invoke->AsInvokeStaticOrDirect()->IsStringInit()); size_t start_index = 1; size_t argument_index = 0; - if (!SetupInvokeArguments(invoke, - number_of_vreg_arguments, - args, - register_index, - is_range, - descriptor, - start_index, - &argument_index)) { + if (!SetupInvokeArguments(invoke, operands, descriptor, start_index, &argument_index)) { return false; } @@ -1352,7 +1302,7 @@ bool HInstructionBuilder::HandleStringInit(HInvoke* invoke, // This is a StringFactory call, not an actual String constructor. Its result // replaces the empty String pre-allocated by NewInstance. - uint32_t orig_this_reg = is_range ? register_index : args[0]; + uint32_t orig_this_reg = operands.GetOperand(0); HInstruction* arg_this = LoadLocal(orig_this_reg, DataType::Type::kReference); // Replacing the NewInstance might render it redundant. Keep a list of these @@ -1705,11 +1655,9 @@ void HInstructionBuilder::BuildArrayAccess(const Instruction& instruction, HNewArray* HInstructionBuilder::BuildFilledNewArray(uint32_t dex_pc, dex::TypeIndex type_index, - uint32_t number_of_vreg_arguments, - bool is_range, - uint32_t* args, - uint32_t register_index) { - HInstruction* length = graph_->GetIntConstant(number_of_vreg_arguments, dex_pc); + const InstructionOperands& operands) { + const size_t number_of_operands = operands.GetNumberOfOperands(); + HInstruction* length = graph_->GetIntConstant(number_of_operands, dex_pc); HLoadClass* cls = BuildLoadClass(type_index, dex_pc); HNewArray* const object = new (allocator_) HNewArray(cls, length, dex_pc); AppendInstruction(object); @@ -1723,8 +1671,8 @@ HNewArray* HInstructionBuilder::BuildFilledNewArray(uint32_t dex_pc, bool is_reference_array = (primitive == 'L') || (primitive == '['); DataType::Type type = is_reference_array ? DataType::Type::kReference : DataType::Type::kInt32; - for (size_t i = 0; i < number_of_vreg_arguments; ++i) { - HInstruction* value = LoadLocal(is_range ? register_index + i : args[i], type); + for (size_t i = 0; i < number_of_operands; ++i) { + HInstruction* value = LoadLocal(operands.GetOperand(i), type); HInstruction* index = graph_->GetIntConstant(i, dex_pc); HArraySet* aset = new (allocator_) HArraySet(object, index, value, type, dex_pc); ssa_builder_->MaybeAddAmbiguousArraySet(aset); @@ -2157,11 +2105,10 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, } else { method_idx = instruction.VRegB_35c(); } - uint32_t number_of_vreg_arguments = instruction.VRegA_35c(); uint32_t args[5]; - instruction.GetVarArgs(args); - if (!BuildInvoke(instruction, dex_pc, method_idx, - number_of_vreg_arguments, false, args, -1)) { + uint32_t number_of_vreg_arguments = instruction.GetVarArgs(args); + VarArgsInstructionOperands operands(args, number_of_vreg_arguments); + if (!BuildInvoke(instruction, dex_pc, method_idx, operands)) { return false; } break; @@ -2184,10 +2131,8 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, } else { method_idx = instruction.VRegB_3rc(); } - uint32_t number_of_vreg_arguments = instruction.VRegA_3rc(); - uint32_t register_index = instruction.VRegC(); - if (!BuildInvoke(instruction, dex_pc, method_idx, - number_of_vreg_arguments, true, nullptr, register_index)) { + RangeInstructionOperands operands(instruction.VRegC(), instruction.VRegA_3rc()); + if (!BuildInvoke(instruction, dex_pc, method_idx, operands)) { return false; } break; @@ -2196,32 +2141,17 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::INVOKE_POLYMORPHIC: { uint16_t method_idx = instruction.VRegB_45cc(); dex::ProtoIndex proto_idx(instruction.VRegH_45cc()); - uint32_t number_of_vreg_arguments = instruction.VRegA_45cc(); uint32_t args[5]; - instruction.GetVarArgs(args); - return BuildInvokePolymorphic(instruction, - dex_pc, - method_idx, - proto_idx, - number_of_vreg_arguments, - false, - args, - -1); + uint32_t number_of_vreg_arguments = instruction.GetVarArgs(args); + VarArgsInstructionOperands operands(args, number_of_vreg_arguments); + return BuildInvokePolymorphic(instruction, dex_pc, method_idx, proto_idx, operands); } case Instruction::INVOKE_POLYMORPHIC_RANGE: { uint16_t method_idx = instruction.VRegB_4rcc(); dex::ProtoIndex proto_idx(instruction.VRegH_4rcc()); - uint32_t number_of_vreg_arguments = instruction.VRegA_4rcc(); - uint32_t register_index = instruction.VRegC_4rcc(); - return BuildInvokePolymorphic(instruction, - dex_pc, - method_idx, - proto_idx, - number_of_vreg_arguments, - true, - nullptr, - register_index); + RangeInstructionOperands operands(instruction.VRegC_4rcc(), instruction.VRegA_4rcc()); + return BuildInvokePolymorphic(instruction, dex_pc, method_idx, proto_idx, operands); } case Instruction::NEG_INT: { @@ -2769,30 +2699,19 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, } case Instruction::FILLED_NEW_ARRAY: { - uint32_t number_of_vreg_arguments = instruction.VRegA_35c(); dex::TypeIndex type_index(instruction.VRegB_35c()); uint32_t args[5]; - instruction.GetVarArgs(args); - HNewArray* new_array = BuildFilledNewArray(dex_pc, - type_index, - number_of_vreg_arguments, - /* is_range */ false, - args, - /* register_index */ 0); + uint32_t number_of_vreg_arguments = instruction.GetVarArgs(args); + VarArgsInstructionOperands operands(args, number_of_vreg_arguments); + HNewArray* new_array = BuildFilledNewArray(dex_pc, type_index, operands); BuildConstructorFenceForAllocation(new_array); break; } case Instruction::FILLED_NEW_ARRAY_RANGE: { - uint32_t number_of_vreg_arguments = instruction.VRegA_3rc(); dex::TypeIndex type_index(instruction.VRegB_3rc()); - uint32_t register_index = instruction.VRegC_3rc(); - HNewArray* new_array = BuildFilledNewArray(dex_pc, - type_index, - number_of_vreg_arguments, - /* is_range */ true, - /* args*/ nullptr, - register_index); + RangeInstructionOperands operands(instruction.VRegC_3rc(), instruction.VRegA_3rc()); + HNewArray* new_array = BuildFilledNewArray(dex_pc, type_index, operands); BuildConstructorFenceForAllocation(new_array); break; } diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index 9d886a8ef2..2218a691ea 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -38,6 +38,7 @@ class CompilerDriver; class DexCompilationUnit; class HBasicBlockBuilder; class Instruction; +class InstructionOperands; class OptimizingCompilerStats; class ScopedObjectAccess; class SsaBuilder; @@ -168,10 +169,7 @@ class HInstructionBuilder : public ValueObject { bool BuildInvoke(const Instruction& instruction, uint32_t dex_pc, uint32_t method_idx, - uint32_t number_of_vreg_arguments, - bool is_range, - uint32_t* args, - uint32_t register_index); + const InstructionOperands& operands); // Builds an invocation node for invoke-polymorphic and returns whether the // instruction is supported. @@ -179,18 +177,12 @@ class HInstructionBuilder : public ValueObject { uint32_t dex_pc, uint32_t method_idx, dex::ProtoIndex proto_idx, - uint32_t number_of_vreg_arguments, - bool is_range, - uint32_t* args, - uint32_t register_index); + const InstructionOperands& operands); // Builds a new array node and the instructions that fill it. HNewArray* BuildFilledNewArray(uint32_t dex_pc, dex::TypeIndex type_index, - uint32_t number_of_vreg_arguments, - bool is_range, - uint32_t* args, - uint32_t register_index); + const InstructionOperands& operands); void BuildFillArrayData(const Instruction& instruction, uint32_t dex_pc); @@ -260,28 +252,19 @@ class HInstructionBuilder : public ValueObject { HInvoke* invoke); bool SetupInvokeArguments(HInvoke* invoke, - uint32_t number_of_vreg_arguments, - uint32_t* args, - uint32_t register_index, - bool is_range, + const InstructionOperands& operands, const char* descriptor, size_t start_index, size_t* argument_index); bool HandleInvoke(HInvoke* invoke, - uint32_t number_of_vreg_arguments, - uint32_t* args, - uint32_t register_index, - bool is_range, + const InstructionOperands& operands, const char* descriptor, HClinitCheck* clinit_check, bool is_unresolved); bool HandleStringInit(HInvoke* invoke, - uint32_t number_of_vreg_arguments, - uint32_t* args, - uint32_t register_index, - bool is_range, + const InstructionOperands& operands, const char* descriptor); void HandleStringInitResult(HInvokeStaticOrDirect* invoke); diff --git a/libdexfile/dex/dex_instruction-inl.h b/libdexfile/dex/dex_instruction-inl.h index 6bef18c85f..e0cffdd2f4 100644 --- a/libdexfile/dex/dex_instruction-inl.h +++ b/libdexfile/dex/dex_instruction-inl.h @@ -508,7 +508,7 @@ inline bool Instruction::HasVarArgs() const { return (FormatOf(Opcode()) == k35c) || (FormatOf(Opcode()) == k45cc); } -inline void Instruction::GetVarArgs(uint32_t arg[kMaxVarArgRegs], uint16_t inst_data) const { +inline uint32_t Instruction::GetVarArgs(uint32_t arg[kMaxVarArgRegs], uint16_t inst_data) const { DCHECK(HasVarArgs()); /* @@ -551,6 +551,7 @@ inline void Instruction::GetVarArgs(uint32_t arg[kMaxVarArgRegs], uint16_t inst_ default: // case 0 break; // Valid, but no need to do anything. } + return count; } } // namespace art diff --git a/libdexfile/dex/dex_instruction.h b/libdexfile/dex/dex_instruction.h index bf5083622b..6807025e13 100644 --- a/libdexfile/dex/dex_instruction.h +++ b/libdexfile/dex/dex_instruction.h @@ -462,8 +462,8 @@ class Instruction { // Fills the given array with the 'arg' array of the instruction. bool HasVarArgs() const; - void GetVarArgs(uint32_t args[kMaxVarArgRegs], uint16_t inst_data) const; - void GetVarArgs(uint32_t args[kMaxVarArgRegs]) const { + uint32_t GetVarArgs(uint32_t args[kMaxVarArgRegs], uint16_t inst_data) const; + uint32_t GetVarArgs(uint32_t args[kMaxVarArgRegs]) const { return GetVarArgs(args, Fetch16(0)); } -- GitLab From b674a1435c552e26dbd81aa77c22f4db454f0006 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Mon, 21 May 2018 10:27:06 +0000 Subject: [PATCH 428/749] Revert^2 "Move kVRegSize to globals.h to reduce include dependencies." This crashed the linker before. I will try again. This reverts commit a9d5ccdf887ef5c9f61481f036773bdfadd39fd7. Change-Id: Ic204f9564a1fba97fcbf29cb4dc5ade25a9ce864 --- libartbase/base/globals.h | 3 +++ runtime/stack_map.h | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libartbase/base/globals.h b/libartbase/base/globals.h index 69d1a64a3b..39e0c509cd 100644 --- a/libartbase/base/globals.h +++ b/libartbase/base/globals.h @@ -38,6 +38,9 @@ static constexpr size_t kStackAlignment = 16; // compile-time constant so the compiler can generate better code. static constexpr int kPageSize = 4096; +// Size of Dex virtual registers. +static constexpr size_t kVRegSize = 4; + // Returns whether the given memory offset can be used for generating // an implicit null check. static inline bool CanDoImplicitNullCheckOn(uintptr_t offset) { diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 38397643b6..274e2b20b4 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -37,9 +37,6 @@ class VariableIndentationOutputStream; // (signed) values. static constexpr ssize_t kFrameSlotSize = 4; -// Size of Dex virtual registers. -static constexpr size_t kVRegSize = 4; - class ArtMethod; class CodeInfo; class StackMapEncoding; -- GitLab From 763a14a393922a773221c33f77444bcb0672d3ed Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 21 May 2018 13:16:51 +0100 Subject: [PATCH 429/749] Fix golem runs. Test: m Change-Id: I292ad7f6461ad1835e631826db998d966b5055ce --- Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/Android.mk b/Android.mk index d6472be895..08a1a105b8 100644 --- a/Android.mk +++ b/Android.mk @@ -487,6 +487,7 @@ build-art-target-golem: dex2oat dalvikvm patchoat linker libstdc++ \ sed -i '/libartd.so/d' $(TARGET_OUT)/etc/public.libraries.txt sed -i '/libdexfiled.so/d' $(TARGET_OUT)/etc/public.libraries.txt sed -i '/libprofiled.so/d' $(TARGET_OUT)/etc/public.libraries.txt + sed -i '/libartbased.so/d' $(TARGET_OUT)/etc/public.libraries.txt ######################################################################## # Phony target for building what go/lem requires on host. -- GitLab From e64d58c983fd44af319e2a5140f02b7ecbfccd91 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 21 May 2018 14:17:59 +0100 Subject: [PATCH 430/749] Fix typo that leads to crash. Typo got introduced in: https://android-review.googlesource.com/#/c/platform/art/+/682841/ bug: 80004139 Test: 677-fsi2 Change-Id: I2744257afd49ee069d87b2637c1cd3427ca61927 --- libartbase/base/file_utils.cc | 3 ++- test/677-fsi2/expected.txt | 4 ++++ test/677-fsi2/info.txt | 1 + test/677-fsi2/run | 25 +++++++++++++++++++++++++ test/677-fsi2/src/Main.java | 21 +++++++++++++++++++++ 5 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 test/677-fsi2/expected.txt create mode 100644 test/677-fsi2/info.txt create mode 100644 test/677-fsi2/run create mode 100644 test/677-fsi2/src/Main.java diff --git a/libartbase/base/file_utils.cc b/libartbase/base/file_utils.cc index 9450e1e8c1..56934aca1f 100644 --- a/libartbase/base/file_utils.cc +++ b/libartbase/base/file_utils.cc @@ -264,7 +264,8 @@ std::string ReplaceFileExtension(const std::string& filename, const std::string& bool LocationIsOnSystem(const char* path) { UniqueCPtr full_path(realpath(path, nullptr)); - return path != nullptr && android::base::StartsWith(full_path.get(), GetAndroidRoot().c_str()); + return full_path != nullptr && + android::base::StartsWith(full_path.get(), GetAndroidRoot().c_str()); } bool LocationIsOnSystemFramework(const char* full_path) { diff --git a/test/677-fsi2/expected.txt b/test/677-fsi2/expected.txt new file mode 100644 index 0000000000..de008470fe --- /dev/null +++ b/test/677-fsi2/expected.txt @@ -0,0 +1,4 @@ +Run default +Hello World +Run without dex2oat +Hello World diff --git a/test/677-fsi2/info.txt b/test/677-fsi2/info.txt new file mode 100644 index 0000000000..ed0a0f2388 --- /dev/null +++ b/test/677-fsi2/info.txt @@ -0,0 +1 @@ +Test that -Xonly-use-system-oat-files works. diff --git a/test/677-fsi2/run b/test/677-fsi2/run new file mode 100644 index 0000000000..039a6a78f0 --- /dev/null +++ b/test/677-fsi2/run @@ -0,0 +1,25 @@ +#!/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. + +echo "Run default" +${RUN} $@ --runtime-option -Xonly-use-system-oat-files +return_status1=$? + +echo "Run without dex2oat" +${RUN} $@ --no-dex2oat --runtime-option -Xonly-use-system-oat-files +return_status2=$? + +(exit $return_status1) && (exit $return_status2) diff --git a/test/677-fsi2/src/Main.java b/test/677-fsi2/src/Main.java new file mode 100644 index 0000000000..834075f67a --- /dev/null +++ b/test/677-fsi2/src/Main.java @@ -0,0 +1,21 @@ +/* + * 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 Main { + public static void main(String[] args) { + System.out.println("Hello World"); + } +} -- GitLab From 3f383468e14822b9eb125d087e3e38df8b0cf1f5 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Thu, 17 May 2018 14:03:39 +0100 Subject: [PATCH 431/749] ART: Faster 712-varhandle-invocations Reduce number of allocations when running 712-varhandle-invocations as it timeouts under gcstress. In the runtime, avoid allocating a MethodType when raising a WrongMethodTypeException when dispatching an erroneous VarHandle accessor. In the test, limit the number of incorrect types tested in boxing test portion of 712 which is particularly slow. And pre-allocate boxed values and share across sub-tests. The total time to run 712-varhandle-invocations is reduced by 45% on host and 33% on angler. Test: art/test/run-test --host --64 --gcstress 712 Bug: 73275005 Change-Id: If5b323a61291d490f51638d416c2529874282f1c --- runtime/common_throws.cc | 13 +- runtime/common_throws.h | 4 + runtime/mirror/var_handle.cc | 31 +++++ runtime/mirror/var_handle.h | 5 + runtime/var_handles.cc | 4 +- .../src/SampleValues.java | 130 ++++++++++++++++++ .../src/VarHandleUnitTestCollector.java | 32 ++++- .../util-src/generate_java.py | 10 +- 8 files changed, 214 insertions(+), 15 deletions(-) create mode 100644 test/712-varhandle-invocations/src/SampleValues.java diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index 8fd95ed890..657a78bd2f 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -880,11 +880,14 @@ void ThrowVerifyError(ObjPtr referrer, const char* fmt, ...) { void ThrowWrongMethodTypeException(ObjPtr expected_type, ObjPtr actual_type) { - ThrowException("Ljava/lang/invoke/WrongMethodTypeException;", - nullptr, - StringPrintf("Expected %s but was %s", - expected_type->PrettyDescriptor().c_str(), - actual_type->PrettyDescriptor().c_str()).c_str()); + ThrowWrongMethodTypeException(expected_type->PrettyDescriptor(), actual_type->PrettyDescriptor()); +} + +void ThrowWrongMethodTypeException(const std::string& expected_descriptor, + const std::string& actual_descriptor) { + std::ostringstream msg; + msg << "Expected " << expected_descriptor << " but was " << actual_descriptor; + ThrowException("Ljava/lang/invoke/WrongMethodTypeException;", nullptr, msg.str().c_str()); } } // namespace art diff --git a/runtime/common_throws.h b/runtime/common_throws.h index 29a056e9ea..6acff6f222 100644 --- a/runtime/common_throws.h +++ b/runtime/common_throws.h @@ -274,6 +274,10 @@ void ThrowWrongMethodTypeException(ObjPtr callee_type, ObjPtr callsite_type) REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; +void ThrowWrongMethodTypeException(const std::string& expected_descriptor, + const std::string& actual_descriptor) + REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; + } // namespace art #endif // ART_RUNTIME_COMMON_THROWS_H_ diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc index d31e06cf36..44c819aaf7 100644 --- a/runtime/mirror/var_handle.cc +++ b/runtime/mirror/var_handle.cc @@ -1545,6 +1545,37 @@ MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self, AccessMode acces return GetMethodTypeForAccessMode(self, this, access_mode); } +std::string VarHandle::PrettyDescriptorForAccessMode(AccessMode access_mode) { + // Effect MethodType::PrettyDescriptor() without first creating a method type first. + std::ostringstream oss; + oss << '('; + + AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); + ObjPtr var_type = GetVarType(); + ObjPtr ctypes[2] = { GetCoordinateType0(), GetCoordinateType1() }; + const int32_t ptypes_count = GetNumberOfParameters(access_mode_template, ctypes[0], ctypes[1]); + int32_t ptypes_done = 0; + for (ObjPtr ctype : ctypes) { + if (!ctype.IsNull()) { + if (ptypes_done != 0) { + oss << ", "; + } + oss << ctype->PrettyDescriptor();; + ptypes_done++; + } + } + while (ptypes_done != ptypes_count) { + if (ptypes_done != 0) { + oss << ", "; + } + oss << var_type->PrettyDescriptor(); + ptypes_done++; + } + ObjPtr rtype = GetReturnType(access_mode_template, var_type); + oss << ')' << rtype->PrettyDescriptor(); + return oss.str(); +} + bool VarHandle::Access(AccessMode access_mode, ShadowFrame* shadow_frame, const InstructionOperands* const operands, diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h index eb3704ee48..5186d43830 100644 --- a/runtime/mirror/var_handle.h +++ b/runtime/mirror/var_handle.h @@ -124,6 +124,11 @@ class MANAGED VarHandle : public Object { MethodType* GetMethodTypeForAccessMode(Thread* self, AccessMode accessMode) REQUIRES_SHARED(Locks::mutator_lock_); + // Returns a string representing the descriptor of the MethodType associated with + // this AccessMode. + std::string PrettyDescriptorForAccessMode(AccessMode access_mode) + REQUIRES_SHARED(Locks::mutator_lock_); + bool Access(AccessMode access_mode, ShadowFrame* shadow_frame, const InstructionOperands* const operands, diff --git a/runtime/var_handles.cc b/runtime/var_handles.cc index e6730c6114..f08742fcd7 100644 --- a/runtime/var_handles.cc +++ b/runtime/var_handles.cc @@ -89,8 +89,8 @@ bool VarHandleInvokeAccessor(Thread* self, result); } else { DCHECK_EQ(match_kind, mirror::VarHandle::MatchKind::kNone); - ThrowWrongMethodTypeException(var_handle->GetMethodTypeForAccessMode(self, access_mode), - callsite_type.Get()); + ThrowWrongMethodTypeException(var_handle->PrettyDescriptorForAccessMode(access_mode), + callsite_type->PrettyDescriptor()); return false; } } diff --git a/test/712-varhandle-invocations/src/SampleValues.java b/test/712-varhandle-invocations/src/SampleValues.java new file mode 100644 index 0000000000..79f4f194bd --- /dev/null +++ b/test/712-varhandle-invocations/src/SampleValues.java @@ -0,0 +1,130 @@ +/* + * 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. + */ + +/** Sample values for use in VarHandle tests. These are here to avoid repeatedly boxing which + * makes gcstress tests run slowly. */ +public class SampleValues { + public static final boolean[] PRIMITIVE_BOOLEANS = new boolean[] {true, false}; + + public static final Boolean[] BOOLEANS = new Boolean[] {true, false}; + + public static final byte[] PRIMITIVE_BYTES = + new byte[] {(byte) -128, (byte) -61, (byte) 7, (byte) 127, (byte) 33}; + + public static final Byte[] BYTES = + new Byte[] {(byte) -128, (byte) -61, (byte) 7, (byte) 127, (byte) 33}; + + public static final short[] PRIMITIVE_SHORTS = + new short[] {(short) -32768, (short) -384, (short) 32767, (short) 0xaa55}; + + public static final Short[] SHORTS = + new Short[] {(short) -32768, (short) -384, (short) 32767, (short) 0xaa55}; + + public static final char[] PRIMITIVE_CHARS = + new char[] {'A', '#', '$', 'Z', 't', 'c'}; + + public static final Character[] CHARACTERS = + new Character[] {'A', '#', '$', 'Z', 't', 'c'}; + + public static final int[] PRIMITIVE_INTS = + new int[] {-0x01234567, 0x7f6e5d4c, 0x12345678, 0x10215220, 42}; + + public static final Integer[] INTEGERS = + new Integer[] {-0x01234567, 0x7f6e5d4c, 0x12345678, 0x10215220, 42}; + + public static final long[] PRIMITIVE_LONGS = + new long[] {-0x0123456789abcdefl, 0x789abcdef0123456l, 0xfedcba9876543210l}; + + public static final Long[] LONGS = + new Long[] {-0x0123456789abcdefl, 0x789abcdef0123456l, 0xfedcba9876543210l}; + + public static final float[] PRIMITIVE_FLOATS = + new float[] {-7.77e23f, 1.234e-17f, 3.40e36f, -8.888e3f, 4.442e11f}; + + public static final Float[] FLOATS = + new Float[] {-7.77e23f, 1.234e-17f, 3.40e36f, -8.888e3f, 4.442e11f}; + + public static final double[] PRIMITIVE_DOUBLES = + new double[] {-1.0e-200, 1.11e200, 3.141, 1.1111, 6.022e23, 6.626e-34}; + + public static final Double[] DOUBLES = + new Double[] {-1.0e-200, 1.11e200, 3.141, 1.1111, 6.022e23, 6.626e-34}; + + public static boolean get_boolean(int index) { + return PRIMITIVE_BOOLEANS[index]; + } + + public static Boolean get_Boolean(int index) { + return BOOLEANS[index]; + } + + public static byte get_byte(int index) { + return PRIMITIVE_BYTES[index]; + } + + public static Byte get_Byte(int index) { + return BYTES[index]; + } + + public static short get_short(int index) { + return PRIMITIVE_SHORTS[index]; + } + + public static Short get_Short(int index) { + return SHORTS[index]; + } + + public static char get_char(int index) { + return PRIMITIVE_CHARS[index]; + } + + public static Character get_Character(int index) { + return CHARACTERS[index]; + } + + public static int get_int(int index) { + return PRIMITIVE_INTS[index]; + } + + public static Integer get_Integer(int index) { + return INTEGERS[index]; + } + + public static long get_long(int index) { + return PRIMITIVE_LONGS[index]; + } + + public static Long get_Long(int index) { + return LONGS[index]; + } + + public static float get_float(int index) { + return PRIMITIVE_FLOATS[index]; + } + + public static Float get_Float(int index) { + return FLOATS[index]; + } + + public static double get_double(int index) { + return PRIMITIVE_DOUBLES[index]; + } + + public static Double get_Double(int index) { + return DOUBLES[index]; + } +} + diff --git a/test/712-varhandle-invocations/src/VarHandleUnitTestCollector.java b/test/712-varhandle-invocations/src/VarHandleUnitTestCollector.java index bc64c0c8b1..5a69b549cf 100644 --- a/test/712-varhandle-invocations/src/VarHandleUnitTestCollector.java +++ b/test/712-varhandle-invocations/src/VarHandleUnitTestCollector.java @@ -19,30 +19,52 @@ import java.io.PrintStream; // Results collector for VarHandle Unit tests public final class VarHandleUnitTestCollector { private final PrintStream out = System.out; + private final boolean verbose = false; private int numberOfSuccesses; private int numberOfSkips; private int numberOfFailures; + private int consecutiveResults = 0; + private String current; + private long startMillis; public void start(String testName) { - out.print(testName); - out.print("..."); + out.append(testName) + .append("..."); + consecutiveResults = 0; + current = testName; + startMillis = System.currentTimeMillis(); + } + + private void printStatus(String status) { + out.print(status); + if (verbose) { + out.print('['); + out.print(System.currentTimeMillis() - startMillis); + out.print(']'); + } + out.println(); } public void skip() { numberOfSkips += 1; - out.println("SKIP"); + printStatus("SKIP"); + consecutiveResults++; } public void success() { numberOfSuccesses += 1; - out.println("OK"); + printStatus("OK"); + if (consecutiveResults++ > 1) { + throw new AssertionError("Oops: " + consecutiveResults); + } } public void fail(String errorMessage) { numberOfFailures += 1; - out.println("FAIL"); + printStatus("FAIL"); out.print(errorMessage); + consecutiveResults++; } public void printSummary() { diff --git a/test/712-varhandle-invocations/util-src/generate_java.py b/test/712-varhandle-invocations/util-src/generate_java.py index 9520b53844..f535b400f8 100644 --- a/test/712-varhandle-invocations/util-src/generate_java.py +++ b/test/712-varhandle-invocations/util-src/generate_java.py @@ -757,7 +757,9 @@ public class ${test_class} extends VarHandleUnitTest { """) with io.StringIO() as body_text: compatible_types = types_that_widen_to(var_type) - for value_type in VALUE_TYPES: + incompatible_types = { RANDOM.choice(list(VALUE_TYPES - compatible_types)) } + test_types = compatible_types | incompatible_types + for value_type in test_types: print("try {", file=body_text) return_type = accessor.get_return_type(var_type) if return_type: @@ -765,7 +767,7 @@ public class ${test_class} extends VarHandleUnitTest { print("vh.{0}(this".format(accessor.method_name), end="", file=body_text) num_args = accessor.get_number_of_var_type_arguments() for i in range(0, num_args): - print(", {0}({1})".format(value_type.boxing_method(), value_type.examples[i]), end="", file=body_text) + print(", SampleValues.get_{0}({1})".format(value_type.boxed_type, i), end="", file=body_text) print(");", file=body_text) if value_type in compatible_types: print(" assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.{0}));".format(accessor.access_mode), @@ -817,7 +819,9 @@ public class ${test_class} extends VarHandleUnitTest { with io.StringIO() as body_text: return_type = accessor.get_return_type(var_type) compatible_types = { return_type } - for value_type in VALUE_TYPES: + incompatible_types = { RANDOM.choice(list(VALUE_TYPES - compatible_types)) } + test_types = compatible_types | incompatible_types + for value_type in test_types: print("try {", file=body_text) print("{0} result = ({0}) ".format(value_type.boxed_type), end="", file=body_text) print("vh.{0}(this".format(accessor.method_name), end="", file=body_text) -- GitLab From 4cb27ed3a37a53eeb5cc2206b62fe12c33124bb9 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Mon, 21 May 2018 09:47:31 +0100 Subject: [PATCH 432/749] ART: Remove 712 from knownfailures.txt Bug: b/73275005 Test: buildbot Change-Id: I2772f9146346539fc498904ba90edf4aaf327414 --- test/knownfailures.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/knownfailures.json b/test/knownfailures.json index 31a0eef998..c2f977a4ad 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -650,12 +650,6 @@ "variant": "target", "description": ["Requires zip, which isn't available on device"] }, - { - "tests": "712-varhandle-invocations", - "variant": "speed-profile & debug & gcstress & target", - "bug": "b/73275005", - "description": ["Time out"] - }, { "tests": ["1941-dispose-stress", "522-checker-regression-monitor-exit"], "variant": "jvm", -- GitLab From fd1328762612a77257183579860030dc6d63ca76 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 21 May 2018 18:04:38 +0100 Subject: [PATCH 433/749] Disable 677-fsi2 on RI. Test: m bug: 80004139 Change-Id: I2615a3509f01dadcadab2912d867b27f062737f3 --- test/knownfailures.json | 1 + 1 file changed, 1 insertion(+) diff --git a/test/knownfailures.json b/test/knownfailures.json index 31a0eef998..7d2cae829e 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -956,6 +956,7 @@ { "tests": ["616-cha-unloading", "674-hiddenapi", + "677-fsi2", "678-quickening", "679-locks", "999-redefine-hiddenapi"], -- GitLab From 052f8ca1776ed7deb4f036498edd69eb6a1b942f Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Thu, 26 Apr 2018 15:42:54 +0100 Subject: [PATCH 434/749] Rewrite stackmap encoding code. Remove most of the code related to handling of bit encodings. The design is still same; the encodings are just more implicit. Most of the complexity is replaced with a single BitTable class, which is a generic purpose table of tightly bit-packed integers. It has its own header which stores the bit-encoding of columns, and that removes the need to handle the encodings explicitly. Other classes, like StackMap, are accessors into the BitTable, with named getter methods for the individual columns. This CL saves ~1% of .oat file size (~4% of stackmap size). Test: test-art-host-gtest Change-Id: I7e92683753b0cc376300e3b23d892feac3670890 --- compiler/debug/elf_debug_info_writer.h | 7 +- compiler/debug/elf_debug_line_writer.h | 13 +- compiler/debug/elf_debug_loc_writer.h | 17 +- compiler/optimizing/code_generator.cc | 3 +- compiler/optimizing/stack_map_stream.cc | 395 +++---- compiler/optimizing/stack_map_stream.h | 41 +- compiler/optimizing/stack_map_test.cc | 545 +++++----- libartbase/Android.bp | 1 + libartbase/base/bit_memory_region.h | 13 + libartbase/base/bit_table.h | 220 ++++ libartbase/base/bit_table_test.cc | 117 ++ oatdump/oatdump.cc | 93 +- oatdump/oatdump_test.h | 1 - runtime/check_reference_map_visitor.h | 13 +- runtime/entrypoints/entrypoint_utils-inl.h | 15 +- runtime/entrypoints/entrypoint_utils.cc | 12 +- .../quick/quick_trampoline_entrypoints.cc | 47 +- runtime/jit/jit.cc | 14 +- runtime/oat.h | 4 +- runtime/oat_quick_method_header.cc | 17 +- runtime/oat_quick_method_header.h | 5 - runtime/quick_exception_handler.cc | 47 +- runtime/stack.cc | 58 +- runtime/stack_map.cc | 113 +- runtime/stack_map.h | 997 ++++-------------- runtime/thread.cc | 20 +- .../polymorphic_inline.cc | 5 +- 27 files changed, 1193 insertions(+), 1640 deletions(-) create mode 100644 libartbase/base/bit_table.h create mode 100644 libartbase/base/bit_table_test.cc diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index 893cad288b..87e679fbea 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -207,11 +207,10 @@ class ElfCompilationUnitWriter { std::vector dex_reg_maps; if (accessor.HasCodeItem() && mi->code_info != nullptr) { const CodeInfo code_info(mi->code_info); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - for (size_t s = 0; s < code_info.GetNumberOfStackMaps(encoding); ++s) { - const StackMap& stack_map = code_info.GetStackMapAt(s, encoding); + for (size_t s = 0; s < code_info.GetNumberOfStackMaps(); ++s) { + const StackMap stack_map = code_info.GetStackMapAt(s); dex_reg_maps.push_back(code_info.GetDexRegisterMapOf( - stack_map, encoding, accessor.RegistersSize())); + stack_map, accessor.RegistersSize())); } } diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index 44504c1efb..a7adab5506 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -100,15 +100,14 @@ class ElfDebugLineWriter { if (mi->code_info != nullptr) { // Use stack maps to create mapping table from pc to dex. const CodeInfo code_info(mi->code_info); - const CodeInfoEncoding encoding = code_info.ExtractEncoding(); - pc2dex_map.reserve(code_info.GetNumberOfStackMaps(encoding)); - for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(encoding); s++) { - StackMap stack_map = code_info.GetStackMapAt(s, encoding); + pc2dex_map.reserve(code_info.GetNumberOfStackMaps()); + for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) { + StackMap stack_map = code_info.GetStackMapAt(s); DCHECK(stack_map.IsValid()); - const uint32_t pc = stack_map.GetNativePcOffset(encoding.stack_map.encoding, isa); - const int32_t dex = stack_map.GetDexPc(encoding.stack_map.encoding); + const uint32_t pc = stack_map.GetNativePcOffset(isa); + const int32_t dex = stack_map.GetDexPc(); pc2dex_map.push_back({pc, dex}); - if (stack_map.HasDexRegisterMap(encoding.stack_map.encoding)) { + if (stack_map.HasDexRegisterMap()) { // Guess that the first map with local variables is the end of prologue. prologue_end = std::min(prologue_end, pc); } diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h index 9ea9f01cd9..c1bf915212 100644 --- a/compiler/debug/elf_debug_loc_writer.h +++ b/compiler/debug/elf_debug_loc_writer.h @@ -99,12 +99,11 @@ static std::vector GetVariableLocations( // Get stack maps sorted by pc (they might not be sorted internally). // TODO(dsrbecky) Remove this once stackmaps get sorted by pc. const CodeInfo code_info(method_info->code_info); - const CodeInfoEncoding encoding = code_info.ExtractEncoding(); std::map stack_maps; // low_pc -> stack_map_index. - for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(encoding); s++) { - StackMap stack_map = code_info.GetStackMapAt(s, encoding); + for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) { + StackMap stack_map = code_info.GetStackMapAt(s); DCHECK(stack_map.IsValid()); - if (!stack_map.HasDexRegisterMap(encoding.stack_map.encoding)) { + if (!stack_map.HasDexRegisterMap()) { // The compiler creates stackmaps without register maps at the start of // basic blocks in order to keep instruction-accurate line number mapping. // However, we never stop at those (breakpoint locations always have map). @@ -112,7 +111,7 @@ static std::vector GetVariableLocations( // The main reason for this is to save space by avoiding undefined gaps. continue; } - const uint32_t pc_offset = stack_map.GetNativePcOffset(encoding.stack_map.encoding, isa); + const uint32_t pc_offset = stack_map.GetNativePcOffset(isa); DCHECK_LE(pc_offset, method_info->code_size); DCHECK_LE(compilation_unit_code_address, method_info->code_address); const uint32_t low_pc = dchecked_integral_cast( @@ -124,7 +123,7 @@ static std::vector GetVariableLocations( for (auto it = stack_maps.begin(); it != stack_maps.end(); it++) { const uint32_t low_pc = it->first; const uint32_t stack_map_index = it->second; - const StackMap& stack_map = code_info.GetStackMapAt(stack_map_index, encoding); + const StackMap stack_map = code_info.GetStackMapAt(stack_map_index); auto next_it = it; next_it++; const uint32_t high_pc = next_it != stack_maps.end() @@ -136,7 +135,7 @@ static std::vector GetVariableLocations( } // Check that the stack map is in the requested range. - uint32_t dex_pc = stack_map.GetDexPc(encoding.stack_map.encoding); + uint32_t dex_pc = stack_map.GetDexPc(); if (!(dex_pc_low <= dex_pc && dex_pc < dex_pc_high)) { // The variable is not in scope at this PC. Therefore omit the entry. // Note that this is different to None() entry which means in scope, but unknown location. @@ -151,10 +150,10 @@ static std::vector GetVariableLocations( DCHECK(dex_register_map.IsValid()); CodeItemDataAccessor accessor(*method_info->dex_file, method_info->code_item); reg_lo = dex_register_map.GetDexRegisterLocation( - vreg, accessor.RegistersSize(), code_info, encoding); + vreg, accessor.RegistersSize(), code_info); if (is64bitValue) { reg_hi = dex_register_map.GetDexRegisterLocation( - vreg + 1, accessor.RegistersSize(), code_info, encoding); + vreg + 1, accessor.RegistersSize(), code_info); } // Add location entry for this address range. diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index fb556f435a..de1be5b871 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -975,11 +975,10 @@ static void CheckCovers(uint32_t dex_pc, const CodeInfo& code_info, const ArenaVector& loop_headers, ArenaVector* covered) { - CodeInfoEncoding encoding = code_info.ExtractEncoding(); for (size_t i = 0; i < loop_headers.size(); ++i) { if (loop_headers[i]->GetDexPc() == dex_pc) { if (graph.IsCompilingOsr()) { - DCHECK(code_info.GetOsrStackMapForDexPc(dex_pc, encoding).IsValid()); + DCHECK(code_info.GetOsrStackMapForDexPc(dex_pc).IsValid()); } ++(*covered)[i]; } diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index bf7c5542ef..aa28c8b500 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -51,15 +51,7 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, if (sp_mask != nullptr) { stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet()); } - if (inlining_depth > 0) { - number_of_stack_maps_with_inline_info_++; - } - // Note: dex_pc can be kNoDexPc for native method intrinsics. - if (dex_pc != dex::kDexNoIndex && (dex_pc_max_ == dex::kDexNoIndex || dex_pc_max_ < dex_pc)) { - dex_pc_max_ = dex_pc; - } - register_mask_max_ = std::max(register_mask_max_, register_mask); current_dex_register_ = 0; } @@ -146,51 +138,6 @@ void StackMapStream::EndInlineInfoEntry() { current_inline_info_ = InlineInfoEntry(); } -CodeOffset StackMapStream::ComputeMaxNativePcCodeOffset() const { - CodeOffset max_native_pc_offset; - for (const StackMapEntry& entry : stack_maps_) { - max_native_pc_offset = std::max(max_native_pc_offset, entry.native_pc_code_offset); - } - return max_native_pc_offset; -} - -size_t StackMapStream::PrepareForFillIn() { - CodeInfoEncoding encoding; - encoding.dex_register_map.num_entries = 0; // TODO: Remove this field. - encoding.dex_register_map.num_bytes = ComputeDexRegisterMapsSize(); - encoding.location_catalog.num_entries = location_catalog_entries_.size(); - encoding.location_catalog.num_bytes = ComputeDexRegisterLocationCatalogSize(); - encoding.inline_info.num_entries = inline_infos_.size(); - // Must be done before calling ComputeInlineInfoEncoding since ComputeInlineInfoEncoding requires - // dex_method_index_idx to be filled in. - PrepareMethodIndices(); - ComputeInlineInfoEncoding(&encoding.inline_info.encoding, - encoding.dex_register_map.num_bytes); - CodeOffset max_native_pc_offset = ComputeMaxNativePcCodeOffset(); - // Prepare the CodeInfo variable-sized encoding. - encoding.stack_mask.encoding.num_bits = stack_mask_max_ + 1; // Need room for max element too. - encoding.stack_mask.num_entries = PrepareStackMasks(encoding.stack_mask.encoding.num_bits); - encoding.register_mask.encoding.num_bits = MinimumBitsToStore(register_mask_max_); - encoding.register_mask.num_entries = PrepareRegisterMasks(); - encoding.stack_map.num_entries = stack_maps_.size(); - encoding.stack_map.encoding.SetFromSizes( - // The stack map contains compressed native PC offsets. - max_native_pc_offset.CompressedValue(), - dex_pc_max_, - encoding.dex_register_map.num_bytes, - encoding.inline_info.num_entries, - encoding.register_mask.num_entries, - encoding.stack_mask.num_entries); - ComputeInvokeInfoEncoding(&encoding); - DCHECK_EQ(code_info_encoding_.size(), 0u); - encoding.Compress(&code_info_encoding_); - encoding.ComputeTableOffsets(); - // Compute table offsets so we can get the non header size. - DCHECK_EQ(encoding.HeaderSize(), code_info_encoding_.size()); - needed_size_ = code_info_encoding_.size() + encoding.NonHeaderSize(); - return needed_size_; -} - size_t StackMapStream::ComputeDexRegisterLocationCatalogSize() const { size_t size = DexRegisterLocationCatalog::kFixedSize; for (const DexRegisterLocation& dex_register_location : location_catalog_entries_) { @@ -204,6 +151,10 @@ size_t StackMapStream::DexRegisterMapEntry::ComputeSize(size_t catalog_size) con if (num_dex_registers == 0u) { return 0u; // No register map will be emitted. } + size_t number_of_live_dex_registers = live_dex_registers_mask->NumSetBits(); + if (live_dex_registers_mask->NumSetBits() == 0) { + return 0u; // No register map will be emitted. + } DCHECK(live_dex_registers_mask != nullptr); // Size of the map in bytes. @@ -211,7 +162,6 @@ size_t StackMapStream::DexRegisterMapEntry::ComputeSize(size_t catalog_size) con // Add the live bit mask for the Dex register liveness. size += DexRegisterMap::GetLiveBitMaskSize(num_dex_registers); // Compute the size of the set of live Dex register entries. - size_t number_of_live_dex_registers = live_dex_registers_mask->NumSetBits(); size_t map_entries_size_in_bits = DexRegisterMap::SingleEntrySizeInBits(catalog_size) * number_of_live_dex_registers; size_t map_entries_size_in_bytes = @@ -220,86 +170,6 @@ size_t StackMapStream::DexRegisterMapEntry::ComputeSize(size_t catalog_size) con return size; } -size_t StackMapStream::ComputeDexRegisterMapsSize() const { - size_t size = 0; - for (const DexRegisterMapEntry& entry : dex_register_entries_) { - size += entry.ComputeSize(location_catalog_entries_.size()); - } - return size; -} - -void StackMapStream::ComputeInvokeInfoEncoding(CodeInfoEncoding* encoding) { - DCHECK(encoding != nullptr); - uint32_t native_pc_max = 0; - uint16_t method_index_max = 0; - size_t invoke_infos_count = 0; - size_t invoke_type_max = 0; - for (const StackMapEntry& entry : stack_maps_) { - if (entry.dex_method_index != dex::kDexNoIndex) { - native_pc_max = std::max(native_pc_max, entry.native_pc_code_offset.CompressedValue()); - method_index_max = std::max(method_index_max, static_cast(entry.dex_method_index)); - invoke_type_max = std::max(invoke_type_max, static_cast(entry.invoke_type)); - ++invoke_infos_count; - } - } - encoding->invoke_info.num_entries = invoke_infos_count; - encoding->invoke_info.encoding.SetFromSizes(native_pc_max, invoke_type_max, method_index_max); -} - -void StackMapStream::ComputeInlineInfoEncoding(InlineInfoEncoding* encoding, - size_t dex_register_maps_bytes) { - uint32_t method_index_max = 0; - uint32_t dex_pc_max = dex::kDexNoIndex; - uint32_t extra_data_max = 0; - - uint32_t inline_info_index = 0; - for (const StackMapEntry& entry : stack_maps_) { - for (size_t j = 0; j < entry.inlining_depth; ++j) { - InlineInfoEntry inline_entry = inline_infos_[inline_info_index++]; - if (inline_entry.method == nullptr) { - method_index_max = std::max(method_index_max, inline_entry.dex_method_index_idx); - extra_data_max = std::max(extra_data_max, 1u); - } else { - method_index_max = std::max( - method_index_max, High32Bits(reinterpret_cast(inline_entry.method))); - extra_data_max = std::max( - extra_data_max, Low32Bits(reinterpret_cast(inline_entry.method))); - } - if (inline_entry.dex_pc != dex::kDexNoIndex && - (dex_pc_max == dex::kDexNoIndex || dex_pc_max < inline_entry.dex_pc)) { - dex_pc_max = inline_entry.dex_pc; - } - } - } - DCHECK_EQ(inline_info_index, inline_infos_.size()); - - encoding->SetFromSizes(method_index_max, dex_pc_max, extra_data_max, dex_register_maps_bytes); -} - -size_t StackMapStream::MaybeCopyDexRegisterMap(DexRegisterMapEntry& entry, - size_t* current_offset, - MemoryRegion dex_register_locations_region) { - DCHECK(current_offset != nullptr); - if ((entry.num_dex_registers == 0) || (entry.live_dex_registers_mask->NumSetBits() == 0)) { - // No dex register map needed. - return StackMap::kNoDexRegisterMap; - } - if (entry.offset == DexRegisterMapEntry::kOffsetUnassigned) { - // Not already copied, need to copy and and assign an offset. - entry.offset = *current_offset; - const size_t entry_size = entry.ComputeSize(location_catalog_entries_.size()); - DexRegisterMap dex_register_map( - dex_register_locations_region.Subregion(entry.offset, entry_size)); - *current_offset += entry_size; - // Fill in the map since it was just added. - FillInDexRegisterMap(dex_register_map, - entry.num_dex_registers, - *entry.live_dex_registers_mask, - entry.locations_start_index); - } - return entry.offset; -} - void StackMapStream::FillInMethodInfo(MemoryRegion region) { { MethodInfo info(region.begin(), method_indices_.size()); @@ -318,30 +188,64 @@ void StackMapStream::FillInMethodInfo(MemoryRegion region) { } } -void StackMapStream::FillInCodeInfo(MemoryRegion region) { - DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry"; - DCHECK_NE(0u, needed_size_) << "PrepareForFillIn not called before FillIn"; +template +static MemoryRegion EncodeMemoryRegion(Vector* out, size_t* bit_offset, uint32_t bit_length) { + uint32_t byte_length = BitsToBytesRoundUp(bit_length); + EncodeVarintBits(out, bit_offset, byte_length); + *bit_offset = RoundUp(*bit_offset, kBitsPerByte); + out->resize(out->size() + byte_length); + MemoryRegion region(out->data() + *bit_offset / kBitsPerByte, byte_length); + *bit_offset += kBitsPerByte * byte_length; + return region; +} - DCHECK_EQ(region.size(), needed_size_); +template +using ScopedBitTableBuilder = BitTableBuilder>; - // Note that the memory region does not have to be zeroed when we JIT code - // because we do not use the arena allocator there. +size_t StackMapStream::PrepareForFillIn() { + size_t bit_offset = 0; + out_.clear(); - // Write the CodeInfo header. - region.CopyFrom(0, MemoryRegion(code_info_encoding_.data(), code_info_encoding_.size())); + // Decide the offsets of dex register map entries, but do not write them out yet. + // Needs to be done first as it modifies the stack map entry. + size_t dex_register_map_bytes = 0; + for (DexRegisterMapEntry& entry : dex_register_entries_) { + size_t size = entry.ComputeSize(location_catalog_entries_.size()); + entry.offset = size == 0 ? DexRegisterMapEntry::kOffsetUnassigned : dex_register_map_bytes; + dex_register_map_bytes += size; + } - CodeInfo code_info(region); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - DCHECK_EQ(encoding.stack_map.num_entries, stack_maps_.size()); + // Must be done before calling ComputeInlineInfoEncoding since ComputeInlineInfoEncoding requires + // dex_method_index_idx to be filled in. + PrepareMethodIndices(); - MemoryRegion dex_register_locations_region = region.Subregion( - encoding.dex_register_map.byte_offset, - encoding.dex_register_map.num_bytes); + // Dedup stack masks. Needs to be done first as it modifies the stack map entry. + size_t stack_mask_bits = stack_mask_max_ + 1; // Need room for max element too. + size_t num_stack_masks = PrepareStackMasks(stack_mask_bits); + + // Dedup register masks. Needs to be done first as it modifies the stack map entry. + size_t num_register_masks = PrepareRegisterMasks(); + + // Write dex register maps. + MemoryRegion dex_register_map_region = + EncodeMemoryRegion(&out_, &bit_offset, dex_register_map_bytes * kBitsPerByte); + for (DexRegisterMapEntry& entry : dex_register_entries_) { + size_t entry_size = entry.ComputeSize(location_catalog_entries_.size()); + if (entry_size != 0) { + DexRegisterMap dex_register_map( + dex_register_map_region.Subregion(entry.offset, entry_size)); + FillInDexRegisterMap(dex_register_map, + entry.num_dex_registers, + *entry.live_dex_registers_mask, + entry.locations_start_index); + } + } - // Set the Dex register location catalog. - MemoryRegion dex_register_location_catalog_region = region.Subregion( - encoding.location_catalog.byte_offset, - encoding.location_catalog.num_bytes); + // Write dex register catalog. + EncodeVarintBits(&out_, &bit_offset, location_catalog_entries_.size()); + size_t location_catalog_bytes = ComputeDexRegisterLocationCatalogSize(); + MemoryRegion dex_register_location_catalog_region = + EncodeMemoryRegion(&out_, &bit_offset, location_catalog_bytes * kBitsPerByte); DexRegisterLocationCatalog dex_register_location_catalog(dex_register_location_catalog_region); // Offset in `dex_register_location_catalog` where to store the next // register location. @@ -353,82 +257,70 @@ void StackMapStream::FillInCodeInfo(MemoryRegion region) { // Ensure we reached the end of the Dex registers location_catalog. DCHECK_EQ(location_catalog_offset, dex_register_location_catalog_region.size()); - ArenaBitVector empty_bitmask(allocator_, 0, /* expandable */ false, kArenaAllocStackMapStream); - uintptr_t next_dex_register_map_offset = 0; - uintptr_t next_inline_info_index = 0; - size_t invoke_info_idx = 0; - for (size_t i = 0, e = stack_maps_.size(); i < e; ++i) { - StackMap stack_map = code_info.GetStackMapAt(i, encoding); - StackMapEntry entry = stack_maps_[i]; - - stack_map.SetDexPc(encoding.stack_map.encoding, entry.dex_pc); - stack_map.SetNativePcCodeOffset(encoding.stack_map.encoding, entry.native_pc_code_offset); - stack_map.SetRegisterMaskIndex(encoding.stack_map.encoding, entry.register_mask_index); - stack_map.SetStackMaskIndex(encoding.stack_map.encoding, entry.stack_mask_index); - - size_t offset = MaybeCopyDexRegisterMap(dex_register_entries_[entry.dex_register_map_index], - &next_dex_register_map_offset, - dex_register_locations_region); - stack_map.SetDexRegisterMapOffset(encoding.stack_map.encoding, offset); - + // Write stack maps. + ScopedArenaAllocatorAdapter adapter = allocator_->Adapter(kArenaAllocStackMapStream); + ScopedBitTableBuilder stack_map_builder((adapter)); + ScopedBitTableBuilder invoke_info_builder((adapter)); + ScopedBitTableBuilder inline_info_builder((adapter)); + for (const StackMapEntry& entry : stack_maps_) { if (entry.dex_method_index != dex::kDexNoIndex) { - InvokeInfo invoke_info(code_info.GetInvokeInfo(encoding, invoke_info_idx)); - invoke_info.SetNativePcCodeOffset(encoding.invoke_info.encoding, entry.native_pc_code_offset); - invoke_info.SetInvokeType(encoding.invoke_info.encoding, entry.invoke_type); - invoke_info.SetMethodIndexIdx(encoding.invoke_info.encoding, entry.dex_method_index_idx); - ++invoke_info_idx; + invoke_info_builder.AddRow( + entry.native_pc_code_offset.CompressedValue(), + entry.invoke_type, + entry.dex_method_index_idx); } // Set the inlining info. - if (entry.inlining_depth != 0) { - InlineInfo inline_info = code_info.GetInlineInfo(next_inline_info_index, encoding); - - // Fill in the index. - stack_map.SetInlineInfoIndex(encoding.stack_map.encoding, next_inline_info_index); - DCHECK_EQ(next_inline_info_index, entry.inline_infos_start_index); - next_inline_info_index += entry.inlining_depth; - - inline_info.SetDepth(encoding.inline_info.encoding, entry.inlining_depth); - DCHECK_LE(entry.inline_infos_start_index + entry.inlining_depth, inline_infos_.size()); - - for (size_t depth = 0; depth < entry.inlining_depth; ++depth) { - InlineInfoEntry inline_entry = inline_infos_[depth + entry.inline_infos_start_index]; - if (inline_entry.method != nullptr) { - inline_info.SetMethodIndexIdxAtDepth( - encoding.inline_info.encoding, - depth, - High32Bits(reinterpret_cast(inline_entry.method))); - inline_info.SetExtraDataAtDepth( - encoding.inline_info.encoding, - depth, - Low32Bits(reinterpret_cast(inline_entry.method))); - } else { - inline_info.SetMethodIndexIdxAtDepth(encoding.inline_info.encoding, - depth, - inline_entry.dex_method_index_idx); - inline_info.SetExtraDataAtDepth(encoding.inline_info.encoding, depth, 1); - } - inline_info.SetDexPcAtDepth(encoding.inline_info.encoding, depth, inline_entry.dex_pc); - size_t dex_register_map_offset = MaybeCopyDexRegisterMap( - dex_register_entries_[inline_entry.dex_register_map_index], - &next_dex_register_map_offset, - dex_register_locations_region); - inline_info.SetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding, - depth, - dex_register_map_offset); + uint32_t inline_info_index = StackMap::kNoValue; + DCHECK_LE(entry.inline_infos_start_index + entry.inlining_depth, inline_infos_.size()); + for (size_t depth = 0; depth < entry.inlining_depth; ++depth) { + InlineInfoEntry inline_entry = inline_infos_[depth + entry.inline_infos_start_index]; + uint32_t method_index_idx = inline_entry.dex_method_index_idx; + uint32_t extra_data = 1; + if (inline_entry.method != nullptr) { + method_index_idx = High32Bits(reinterpret_cast(inline_entry.method)); + extra_data = Low32Bits(reinterpret_cast(inline_entry.method)); + } + uint32_t index = inline_info_builder.AddRow( + (depth == entry.inlining_depth - 1) ? InlineInfo::kLast : InlineInfo::kMore, + method_index_idx, + inline_entry.dex_pc, + extra_data, + dex_register_entries_[inline_entry.dex_register_map_index].offset); + if (depth == 0) { + inline_info_index = index; } - } else if (encoding.stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) { - stack_map.SetInlineInfoIndex(encoding.stack_map.encoding, StackMap::kNoInlineInfo); } + stack_map_builder.AddRow( + entry.native_pc_code_offset.CompressedValue(), + entry.dex_pc, + dex_register_entries_[entry.dex_register_map_index].offset, + inline_info_index, + entry.register_mask_index, + entry.stack_mask_index); + } + stack_map_builder.Encode(&out_, &bit_offset); + invoke_info_builder.Encode(&out_, &bit_offset); + inline_info_builder.Encode(&out_, &bit_offset); + + // Write register masks table. + ScopedBitTableBuilder<1> register_mask_builder((adapter)); + for (size_t i = 0; i < num_register_masks; ++i) { + register_mask_builder.AddRow(register_masks_[i]); } + register_mask_builder.Encode(&out_, &bit_offset); // Write stack masks table. - const size_t stack_mask_bits = encoding.stack_mask.encoding.BitSize(); + EncodeVarintBits(&out_, &bit_offset, stack_mask_bits); + out_.resize(BitsToBytesRoundUp(bit_offset + stack_mask_bits * num_stack_masks)); + BitMemoryRegion stack_mask_region(MemoryRegion(out_.data(), out_.size()), + bit_offset, + stack_mask_bits * num_stack_masks); if (stack_mask_bits > 0) { - size_t stack_mask_bytes = RoundUp(stack_mask_bits, kBitsPerByte) / kBitsPerByte; - for (size_t i = 0; i < encoding.stack_mask.num_entries; ++i) { + for (size_t i = 0; i < num_stack_masks; ++i) { + size_t stack_mask_bytes = BitsToBytesRoundUp(stack_mask_bits); BitMemoryRegion src(MemoryRegion(&stack_masks_[i * stack_mask_bytes], stack_mask_bytes)); - BitMemoryRegion dst = code_info.GetStackMask(i, encoding); + BitMemoryRegion dst = stack_mask_region.Subregion(i * stack_mask_bits, stack_mask_bits); for (size_t bit_index = 0; bit_index < stack_mask_bits; bit_index += BitSizeOf()) { size_t num_bits = std::min(stack_mask_bits - bit_index, BitSizeOf()); dst.StoreBits(bit_index, src.LoadBits(bit_index, num_bits), num_bits); @@ -436,11 +328,16 @@ void StackMapStream::FillInCodeInfo(MemoryRegion region) { } } - // Write register masks table. - for (size_t i = 0; i < encoding.register_mask.num_entries; ++i) { - BitMemoryRegion register_mask = code_info.GetRegisterMask(i, encoding); - register_mask.StoreBits(0, register_masks_[i], encoding.register_mask.encoding.BitSize()); - } + return UnsignedLeb128Size(out_.size()) + out_.size(); +} + +void StackMapStream::FillInCodeInfo(MemoryRegion region) { + DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry"; + DCHECK_NE(0u, out_.size()) << "PrepareForFillIn not called before FillIn"; + DCHECK_EQ(region.size(), UnsignedLeb128Size(out_.size()) + out_.size()); + + uint8_t* ptr = EncodeUnsignedLeb128(region.begin(), out_.size()); + region.CopyFromVector(ptr - region.begin(), out_); // Verify all written data in debug build. if (kIsDebugBuild) { @@ -528,7 +425,6 @@ void StackMapStream::CheckDexRegisterMap(const CodeInfo& code_info, size_t num_dex_registers, BitVector* live_dex_registers_mask, size_t dex_register_locations_index) const { - CodeInfoEncoding encoding = code_info.ExtractEncoding(); for (size_t reg = 0; reg < num_dex_registers; reg++) { // Find the location we tried to encode. DexRegisterLocation expected = DexRegisterLocation::None(); @@ -543,7 +439,7 @@ void StackMapStream::CheckDexRegisterMap(const CodeInfo& code_info, } else { DCHECK(dex_register_map.IsDexRegisterLive(reg)); DexRegisterLocation seen = dex_register_map.GetDexRegisterLocation( - reg, num_dex_registers, code_info, encoding); + reg, num_dex_registers, code_info); DCHECK_EQ(expected.GetKind(), seen.GetKind()); DCHECK_EQ(expected.GetValue(), seen.GetValue()); } @@ -613,23 +509,23 @@ size_t StackMapStream::PrepareStackMasks(size_t entry_size_in_bits) { // Check that all StackMapStream inputs are correctly encoded by trying to read them back. void StackMapStream::CheckCodeInfo(MemoryRegion region) const { CodeInfo code_info(region); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - DCHECK_EQ(code_info.GetNumberOfStackMaps(encoding), stack_maps_.size()); + DCHECK_EQ(code_info.GetNumberOfStackMaps(), stack_maps_.size()); + DCHECK_EQ(code_info.GetNumberOfStackMaskBits(), static_cast(stack_mask_max_ + 1)); + DCHECK_EQ(code_info.GetNumberOfLocationCatalogEntries(), location_catalog_entries_.size()); size_t invoke_info_index = 0; for (size_t s = 0; s < stack_maps_.size(); ++s) { - const StackMap stack_map = code_info.GetStackMapAt(s, encoding); - const StackMapEncoding& stack_map_encoding = encoding.stack_map.encoding; + const StackMap stack_map = code_info.GetStackMapAt(s); StackMapEntry entry = stack_maps_[s]; // Check main stack map fields. - DCHECK_EQ(stack_map.GetNativePcOffset(stack_map_encoding, instruction_set_), + DCHECK_EQ(stack_map.GetNativePcOffset(instruction_set_), entry.native_pc_code_offset.Uint32Value(instruction_set_)); - DCHECK_EQ(stack_map.GetDexPc(stack_map_encoding), entry.dex_pc); - DCHECK_EQ(stack_map.GetRegisterMaskIndex(stack_map_encoding), entry.register_mask_index); - DCHECK_EQ(code_info.GetRegisterMaskOf(encoding, stack_map), entry.register_mask); - const size_t num_stack_mask_bits = code_info.GetNumberOfStackMaskBits(encoding); - DCHECK_EQ(stack_map.GetStackMaskIndex(stack_map_encoding), entry.stack_mask_index); - BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, stack_map); + DCHECK_EQ(stack_map.GetDexPc(), entry.dex_pc); + DCHECK_EQ(stack_map.GetRegisterMaskIndex(), entry.register_mask_index); + DCHECK_EQ(code_info.GetRegisterMaskOf(stack_map), entry.register_mask); + const size_t num_stack_mask_bits = code_info.GetNumberOfStackMaskBits(); + DCHECK_EQ(stack_map.GetStackMaskIndex(), entry.stack_mask_index); + BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map); if (entry.sp_mask != nullptr) { DCHECK_GE(stack_mask.size_in_bits(), entry.sp_mask->GetNumberOfBits()); for (size_t b = 0; b < num_stack_mask_bits; b++) { @@ -641,38 +537,36 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { } } if (entry.dex_method_index != dex::kDexNoIndex) { - InvokeInfo invoke_info = code_info.GetInvokeInfo(encoding, invoke_info_index); - DCHECK_EQ(invoke_info.GetNativePcOffset(encoding.invoke_info.encoding, instruction_set_), + InvokeInfo invoke_info = code_info.GetInvokeInfo(invoke_info_index); + DCHECK_EQ(invoke_info.GetNativePcOffset(instruction_set_), entry.native_pc_code_offset.Uint32Value(instruction_set_)); - DCHECK_EQ(invoke_info.GetInvokeType(encoding.invoke_info.encoding), entry.invoke_type); - DCHECK_EQ(invoke_info.GetMethodIndexIdx(encoding.invoke_info.encoding), - entry.dex_method_index_idx); + DCHECK_EQ(invoke_info.GetInvokeType(), entry.invoke_type); + DCHECK_EQ(invoke_info.GetMethodIndexIdx(), entry.dex_method_index_idx); invoke_info_index++; } CheckDexRegisterMap(code_info, code_info.GetDexRegisterMapOf( - stack_map, encoding, entry.dex_register_entry.num_dex_registers), + stack_map, entry.dex_register_entry.num_dex_registers), entry.dex_register_entry.num_dex_registers, entry.dex_register_entry.live_dex_registers_mask, entry.dex_register_entry.locations_start_index); // Check inline info. - DCHECK_EQ(stack_map.HasInlineInfo(stack_map_encoding), (entry.inlining_depth != 0)); + DCHECK_EQ(stack_map.HasInlineInfo(), (entry.inlining_depth != 0)); if (entry.inlining_depth != 0) { - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); - DCHECK_EQ(inline_info.GetDepth(encoding.inline_info.encoding), entry.inlining_depth); + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); + DCHECK_EQ(inline_info.GetDepth(), entry.inlining_depth); for (size_t d = 0; d < entry.inlining_depth; ++d) { size_t inline_info_index = entry.inline_infos_start_index + d; DCHECK_LT(inline_info_index, inline_infos_.size()); InlineInfoEntry inline_entry = inline_infos_[inline_info_index]; - DCHECK_EQ(inline_info.GetDexPcAtDepth(encoding.inline_info.encoding, d), - inline_entry.dex_pc); - if (inline_info.EncodesArtMethodAtDepth(encoding.inline_info.encoding, d)) { - DCHECK_EQ(inline_info.GetArtMethodAtDepth(encoding.inline_info.encoding, d), + DCHECK_EQ(inline_info.GetDexPcAtDepth(d), inline_entry.dex_pc); + if (inline_info.EncodesArtMethodAtDepth(d)) { + DCHECK_EQ(inline_info.GetArtMethodAtDepth(d), inline_entry.method); } else { const size_t method_index_idx = - inline_info.GetMethodIndexIdxAtDepth(encoding.inline_info.encoding, d); + inline_info.GetMethodIndexIdxAtDepth(d); DCHECK_EQ(method_index_idx, inline_entry.dex_method_index_idx); DCHECK_EQ(method_indices_[method_index_idx], inline_entry.method_index); } @@ -681,7 +575,6 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { code_info.GetDexRegisterMapAtDepth( d, inline_info, - encoding, inline_entry.dex_register_entry.num_dex_registers), inline_entry.dex_register_entry.num_dex_registers, inline_entry.dex_register_entry.live_dex_registers_mask, @@ -692,7 +585,7 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { } size_t StackMapStream::ComputeMethodInfoSize() const { - DCHECK_NE(0u, needed_size_) << "PrepareForFillIn not called before " << __FUNCTION__; + DCHECK_NE(0u, out_.size()) << "PrepareForFillIn not called before " << __FUNCTION__; return MethodInfo::ComputeSize(method_indices_.size()); } diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index 268e9bd6e0..ea97cf6530 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -73,36 +73,32 @@ class StackMapStream : public ValueObject { method_indices_(allocator->Adapter(kArenaAllocStackMapStream)), dex_register_entries_(allocator->Adapter(kArenaAllocStackMapStream)), stack_mask_max_(-1), - dex_pc_max_(kNoDexPc), - register_mask_max_(0), - number_of_stack_maps_with_inline_info_(0), + out_(allocator->Adapter(kArenaAllocStackMapStream)), dex_map_hash_to_stack_map_indices_(std::less(), allocator->Adapter(kArenaAllocStackMapStream)), current_entry_(), current_inline_info_(), - code_info_encoding_(allocator->Adapter(kArenaAllocStackMapStream)), - needed_size_(0), current_dex_register_(0), in_inline_frame_(false) { stack_maps_.reserve(10); + out_.reserve(64); location_catalog_entries_.reserve(4); dex_register_locations_.reserve(10 * 4); inline_infos_.reserve(2); - code_info_encoding_.reserve(16); } // A dex register map entry for a single stack map entry, contains what registers are live as // well as indices into the location catalog. class DexRegisterMapEntry { public: - static const size_t kOffsetUnassigned = -1; + static const uint32_t kOffsetUnassigned = -1; BitVector* live_dex_registers_mask; uint32_t num_dex_registers; size_t locations_start_index; // Computed fields size_t hash = 0; - size_t offset = kOffsetUnassigned; + uint32_t offset = kOffsetUnassigned; size_t ComputeSize(size_t catalog_size) const; }; @@ -113,7 +109,7 @@ class StackMapStream : public ValueObject { CodeOffset native_pc_code_offset; uint32_t register_mask; BitVector* sp_mask; - uint8_t inlining_depth; + uint32_t inlining_depth; size_t inline_infos_start_index; uint32_t stack_mask_index; uint32_t register_mask_index; @@ -174,11 +170,6 @@ class StackMapStream : public ValueObject { private: size_t ComputeDexRegisterLocationCatalogSize() const; - size_t ComputeDexRegisterMapsSize() const; - void ComputeInlineInfoEncoding(InlineInfoEncoding* encoding, - size_t dex_register_maps_bytes); - - CodeOffset ComputeMaxNativePcCodeOffset() const; // Returns the number of unique stack masks. size_t PrepareStackMasks(size_t entry_size_in_bits); @@ -196,25 +187,12 @@ class StackMapStream : public ValueObject { // Return true if the two dex register map entries are equal. bool DexRegisterMapEntryEquals(const DexRegisterMapEntry& a, const DexRegisterMapEntry& b) const; - // Fill in the corresponding entries of a register map. - void ComputeInvokeInfoEncoding(CodeInfoEncoding* encoding); - - // Returns the index of an entry with the same dex register map as the current_entry, - // or kNoSameDexMapFound if no such entry exists. - size_t FindEntryWithTheSameDexMap(); - bool HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const; - // Fill in the corresponding entries of a register map. void FillInDexRegisterMap(DexRegisterMap dex_register_map, uint32_t num_dex_registers, const BitVector& live_dex_registers_mask, uint32_t start_index_in_dex_register_locations) const; - // Returns the offset for the dex register inside of the dex register location region. See FillIn. - // Only copies the dex register map if the offset for the entry is not already assigned. - size_t MaybeCopyDexRegisterMap(DexRegisterMapEntry& entry, - size_t* current_offset, - MemoryRegion dex_register_locations_region); void CheckDexRegisterMap(const CodeInfo& code_info, const DexRegisterMap& dex_register_map, size_t num_dex_registers, @@ -244,21 +222,16 @@ class StackMapStream : public ValueObject { ScopedArenaVector method_indices_; ScopedArenaVector dex_register_entries_; int stack_mask_max_; - uint32_t dex_pc_max_; - uint32_t register_mask_max_; - size_t number_of_stack_maps_with_inline_info_; + + ScopedArenaVector out_; ScopedArenaSafeMap> dex_map_hash_to_stack_map_indices_; StackMapEntry current_entry_; InlineInfoEntry current_inline_info_; - ScopedArenaVector code_info_encoding_; - size_t needed_size_; uint32_t current_dex_register_; bool in_inline_frame_; - static constexpr uint32_t kNoSameDexMapFound = -1; - DISALLOW_COPY_AND_ASSIGN(StackMapStream); }; diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index e36c592662..9db7588b3a 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -29,14 +29,13 @@ namespace art { // to the given bit vector. Returns true if they are same. static bool CheckStackMask( const CodeInfo& code_info, - const CodeInfoEncoding& encoding, const StackMap& stack_map, const BitVector& bit_vector) { - BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, stack_map); - if (bit_vector.GetNumberOfBits() > encoding.stack_mask.encoding.BitSize()) { + BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map); + if (bit_vector.GetNumberOfBits() > code_info.GetNumberOfStackMaskBits()) { return false; } - for (size_t i = 0; i < encoding.stack_mask.encoding.BitSize(); ++i) { + for (size_t i = 0; i < code_info.GetNumberOfStackMaskBits(); ++i) { if (stack_mask.LoadBit(i) != bit_vector.IsBitSet(i)) { return false; } @@ -65,30 +64,29 @@ TEST(StackMapTest, Test1) { stream.FillInCodeInfo(region); CodeInfo code_info(region); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - ASSERT_EQ(1u, code_info.GetNumberOfStackMaps(encoding)); + ASSERT_EQ(1u, code_info.GetNumberOfStackMaps()); - uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding); + uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); ASSERT_EQ(2u, number_of_catalog_entries); - DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); // The Dex register location catalog contains: // - one 1-byte short Dex register location, and // - one 5-byte large Dex register location. size_t expected_location_catalog_size = 1u + 5u; ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); - StackMap stack_map = code_info.GetStackMapAt(0, encoding); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); - ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding)); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); - ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map)); + StackMap stack_map = code_info.GetStackMapAt(0); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); + ASSERT_EQ(0u, stack_map.GetDexPc()); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map)); - ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask)); + ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask)); - ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); + ASSERT_TRUE(stack_map.HasDexRegisterMap()); DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); + code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); @@ -99,16 +97,16 @@ TEST(StackMapTest, Test1) { ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationKind( - 0, number_of_dex_registers, code_info, encoding)); + 0, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind( - 1, number_of_dex_registers, code_info, encoding)); + 1, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationInternalKind( - 0, number_of_dex_registers, code_info, encoding)); + 0, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind( - 1, number_of_dex_registers, code_info, encoding)); + 1, number_of_dex_registers, code_info)); ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes( - 0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info, encoding)); + 0, number_of_dex_registers, code_info)); + ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info)); size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( 0, number_of_dex_registers, number_of_catalog_entries); @@ -125,7 +123,7 @@ TEST(StackMapTest, Test1) { ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); - ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); + ASSERT_FALSE(stack_map.HasInlineInfo()); } TEST(StackMapTest, Test2) { @@ -179,12 +177,11 @@ TEST(StackMapTest, Test2) { stream.FillInCodeInfo(region); CodeInfo code_info(region); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - ASSERT_EQ(4u, code_info.GetNumberOfStackMaps(encoding)); + ASSERT_EQ(4u, code_info.GetNumberOfStackMaps()); - uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding); + uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); ASSERT_EQ(7u, number_of_catalog_entries); - DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); // The Dex register location catalog contains: // - six 1-byte short Dex register locations, and // - one 5-byte large Dex register location. @@ -193,18 +190,18 @@ TEST(StackMapTest, Test2) { // First stack map. { - StackMap stack_map = code_info.GetStackMapAt(0, encoding); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); - ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding)); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); - ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map)); + StackMap stack_map = code_info.GetStackMapAt(0); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); + ASSERT_EQ(0u, stack_map.GetDexPc()); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map)); - ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask1)); + ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask1)); - ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); + ASSERT_TRUE(stack_map.HasDexRegisterMap()); DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); + code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); @@ -215,16 +212,16 @@ TEST(StackMapTest, Test2) { ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationKind( - 0, number_of_dex_registers, code_info, encoding)); + 0, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind( - 1, number_of_dex_registers, code_info, encoding)); + 1, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationInternalKind( - 0, number_of_dex_registers, code_info, encoding)); + 0, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind( - 1, number_of_dex_registers, code_info, encoding)); + 1, number_of_dex_registers, code_info)); ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes( - 0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info, encoding)); + 0, number_of_dex_registers, code_info)); + ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info)); size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( 0, number_of_dex_registers, number_of_catalog_entries); @@ -241,29 +238,29 @@ TEST(StackMapTest, Test2) { ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); - ASSERT_TRUE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); - ASSERT_EQ(2u, inline_info.GetDepth(encoding.inline_info.encoding)); - ASSERT_EQ(3u, inline_info.GetDexPcAtDepth(encoding.inline_info.encoding, 0)); - ASSERT_EQ(2u, inline_info.GetDexPcAtDepth(encoding.inline_info.encoding, 1)); - ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 0)); - ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 1)); + ASSERT_TRUE(stack_map.HasInlineInfo()); + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); + ASSERT_EQ(2u, inline_info.GetDepth()); + ASSERT_EQ(3u, inline_info.GetDexPcAtDepth(0)); + ASSERT_EQ(2u, inline_info.GetDexPcAtDepth(1)); + ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(0)); + ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(1)); } // Second stack map. { - StackMap stack_map = code_info.GetStackMapAt(1, encoding); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u, encoding))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u, encoding))); - ASSERT_EQ(1u, stack_map.GetDexPc(encoding.stack_map.encoding)); - ASSERT_EQ(128u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); - ASSERT_EQ(0xFFu, code_info.GetRegisterMaskOf(encoding, stack_map)); + StackMap stack_map = code_info.GetStackMapAt(1); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u))); + ASSERT_EQ(1u, stack_map.GetDexPc()); + ASSERT_EQ(128u, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(0xFFu, code_info.GetRegisterMaskOf(stack_map)); - ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask2)); + ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask2)); - ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); + ASSERT_TRUE(stack_map.HasDexRegisterMap()); DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); + code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); @@ -274,17 +271,17 @@ TEST(StackMapTest, Test2) { ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationKind( - 0, number_of_dex_registers, code_info, encoding)); + 0, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationKind( - 1, number_of_dex_registers, code_info, encoding)); + 1, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationInternalKind( - 0, number_of_dex_registers, code_info, encoding)); + 0, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationInternalKind( - 1, number_of_dex_registers, code_info, encoding)); + 1, number_of_dex_registers, code_info)); ASSERT_EQ(18, dex_register_map.GetMachineRegister( - 0, number_of_dex_registers, code_info, encoding)); + 0, number_of_dex_registers, code_info)); ASSERT_EQ(3, dex_register_map.GetMachineRegister( - 1, number_of_dex_registers, code_info, encoding)); + 1, number_of_dex_registers, code_info)); size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( 0, number_of_dex_registers, number_of_catalog_entries); @@ -301,23 +298,23 @@ TEST(StackMapTest, Test2) { ASSERT_EQ(18, location0.GetValue()); ASSERT_EQ(3, location1.GetValue()); - ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); + ASSERT_FALSE(stack_map.HasInlineInfo()); } // Third stack map. { - StackMap stack_map = code_info.GetStackMapAt(2, encoding); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(2u, encoding))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(192u, encoding))); - ASSERT_EQ(2u, stack_map.GetDexPc(encoding.stack_map.encoding)); - ASSERT_EQ(192u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); - ASSERT_EQ(0xABu, code_info.GetRegisterMaskOf(encoding, stack_map)); + StackMap stack_map = code_info.GetStackMapAt(2); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(2u))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(192u))); + ASSERT_EQ(2u, stack_map.GetDexPc()); + ASSERT_EQ(192u, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(0xABu, code_info.GetRegisterMaskOf(stack_map)); - ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask3)); + ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask3)); - ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); + ASSERT_TRUE(stack_map.HasDexRegisterMap()); DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); + code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); @@ -328,17 +325,17 @@ TEST(StackMapTest, Test2) { ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationKind( - 0, number_of_dex_registers, code_info, encoding)); + 0, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kInRegisterHigh, dex_register_map.GetLocationKind( - 1, number_of_dex_registers, code_info, encoding)); + 1, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationInternalKind( - 0, number_of_dex_registers, code_info, encoding)); + 0, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kInRegisterHigh, dex_register_map.GetLocationInternalKind( - 1, number_of_dex_registers, code_info, encoding)); + 1, number_of_dex_registers, code_info)); ASSERT_EQ(6, dex_register_map.GetMachineRegister( - 0, number_of_dex_registers, code_info, encoding)); + 0, number_of_dex_registers, code_info)); ASSERT_EQ(8, dex_register_map.GetMachineRegister( - 1, number_of_dex_registers, code_info, encoding)); + 1, number_of_dex_registers, code_info)); size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( 0, number_of_dex_registers, number_of_catalog_entries); @@ -355,23 +352,23 @@ TEST(StackMapTest, Test2) { ASSERT_EQ(6, location0.GetValue()); ASSERT_EQ(8, location1.GetValue()); - ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); + ASSERT_FALSE(stack_map.HasInlineInfo()); } // Fourth stack map. { - StackMap stack_map = code_info.GetStackMapAt(3, encoding); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(3u, encoding))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(256u, encoding))); - ASSERT_EQ(3u, stack_map.GetDexPc(encoding.stack_map.encoding)); - ASSERT_EQ(256u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); - ASSERT_EQ(0xCDu, code_info.GetRegisterMaskOf(encoding, stack_map)); + StackMap stack_map = code_info.GetStackMapAt(3); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(3u))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(256u))); + ASSERT_EQ(3u, stack_map.GetDexPc()); + ASSERT_EQ(256u, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(0xCDu, code_info.GetRegisterMaskOf(stack_map)); - ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask4)); + ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask4)); - ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); + ASSERT_TRUE(stack_map.HasDexRegisterMap()); DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); + code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); @@ -382,17 +379,17 @@ TEST(StackMapTest, Test2) { ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationKind( - 0, number_of_dex_registers, code_info, encoding)); + 0, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kInFpuRegisterHigh, dex_register_map.GetLocationKind( - 1, number_of_dex_registers, code_info, encoding)); + 1, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationInternalKind( - 0, number_of_dex_registers, code_info, encoding)); + 0, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kInFpuRegisterHigh, dex_register_map.GetLocationInternalKind( - 1, number_of_dex_registers, code_info, encoding)); + 1, number_of_dex_registers, code_info)); ASSERT_EQ(3, dex_register_map.GetMachineRegister( - 0, number_of_dex_registers, code_info, encoding)); + 0, number_of_dex_registers, code_info)); ASSERT_EQ(1, dex_register_map.GetMachineRegister( - 1, number_of_dex_registers, code_info, encoding)); + 1, number_of_dex_registers, code_info)); size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( 0, number_of_dex_registers, number_of_catalog_entries); @@ -409,7 +406,7 @@ TEST(StackMapTest, Test2) { ASSERT_EQ(3, location0.GetValue()); ASSERT_EQ(1, location1.GetValue()); - ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); + ASSERT_FALSE(stack_map.HasInlineInfo()); } } @@ -440,12 +437,11 @@ TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { stream.FillInCodeInfo(region); CodeInfo code_info(region); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - ASSERT_EQ(1u, code_info.GetNumberOfStackMaps(encoding)); + ASSERT_EQ(1u, code_info.GetNumberOfStackMaps()); - uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding); + uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); ASSERT_EQ(2u, number_of_catalog_entries); - DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); // The Dex register location catalog contains: // - one 1-byte short Dex register locations, and // - one 5-byte large Dex register location. @@ -454,17 +450,17 @@ TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { // First stack map. { - StackMap stack_map = code_info.GetStackMapAt(0, encoding); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); - ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding)); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); - ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map)); + StackMap stack_map = code_info.GetStackMapAt(0); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); + ASSERT_EQ(0u, stack_map.GetDexPc()); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map)); - ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask1)); + ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask1)); - ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); - DexRegisterMap map(code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers)); + ASSERT_TRUE(stack_map.HasDexRegisterMap()); + DexRegisterMap map(code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers)); ASSERT_TRUE(map.IsDexRegisterLive(0)); ASSERT_TRUE(map.IsDexRegisterLive(1)); ASSERT_EQ(2u, map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); @@ -474,15 +470,15 @@ TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { size_t expected_map_size = 1u + 1u; ASSERT_EQ(expected_map_size, map.Size()); - ASSERT_EQ(Kind::kInStack, map.GetLocationKind(0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kInStack, map.GetLocationKind(0, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kConstant, - map.GetLocationKind(1, number_of_dex_registers, code_info, encoding)); + map.GetLocationKind(1, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kInStack, - map.GetLocationInternalKind(0, number_of_dex_registers, code_info, encoding)); + map.GetLocationInternalKind(0, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kConstantLargeValue, - map.GetLocationInternalKind(1, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(0, map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(-2, map.GetConstant(1, number_of_dex_registers, code_info, encoding)); + map.GetLocationInternalKind(1, number_of_dex_registers, code_info)); + ASSERT_EQ(0, map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info)); + ASSERT_EQ(-2, map.GetConstant(1, number_of_dex_registers, code_info)); const size_t index0 = map.GetLocationCatalogEntryIndex(0, number_of_dex_registers, number_of_catalog_entries); @@ -501,10 +497,10 @@ TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { // Test that the inline info dex register map deduplicated to the same offset as the stack map // one. - ASSERT_TRUE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); - EXPECT_EQ(inline_info.GetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding, 0), - stack_map.GetDexRegisterMapOffset(encoding.stack_map.encoding)); + ASSERT_TRUE(stack_map.HasInlineInfo()); + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); + EXPECT_EQ(inline_info.GetDexRegisterMapOffsetAtDepth(0), + stack_map.GetDexRegisterMapOffset()); } } @@ -527,27 +523,26 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { stream.FillInCodeInfo(region); CodeInfo code_info(region); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - ASSERT_EQ(1u, code_info.GetNumberOfStackMaps(encoding)); + ASSERT_EQ(1u, code_info.GetNumberOfStackMaps()); - uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding); + uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); ASSERT_EQ(1u, number_of_catalog_entries); - DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); // The Dex register location catalog contains: // - one 5-byte large Dex register location. size_t expected_location_catalog_size = 5u; ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); - StackMap stack_map = code_info.GetStackMapAt(0, encoding); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); - ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding)); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); - ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map)); + StackMap stack_map = code_info.GetStackMapAt(0); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); + ASSERT_EQ(0u, stack_map.GetDexPc()); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map)); - ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); + ASSERT_TRUE(stack_map.HasDexRegisterMap()); DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); + code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); ASSERT_FALSE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); ASSERT_EQ(1u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); @@ -558,14 +553,14 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); ASSERT_EQ(Kind::kNone, dex_register_map.GetLocationKind( - 0, number_of_dex_registers, code_info, encoding)); + 0, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind( - 1, number_of_dex_registers, code_info, encoding)); + 1, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kNone, dex_register_map.GetLocationInternalKind( - 0, number_of_dex_registers, code_info, encoding)); + 0, number_of_dex_registers, code_info)); ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind( - 1, number_of_dex_registers, code_info, encoding)); - ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info, encoding)); + 1, number_of_dex_registers, code_info)); + ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info)); size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( 0, number_of_dex_registers, number_of_catalog_entries); @@ -582,7 +577,7 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); - ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); + ASSERT_FALSE(stack_map.HasInlineInfo()); } // Generate a stack map whose dex register offset is @@ -620,11 +615,10 @@ TEST(StackMapTest, DexRegisterMapOffsetOverflow) { stream.FillInCodeInfo(region); CodeInfo code_info(region); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); // The location catalog contains two entries (DexRegisterLocation(kConstant, 0) // and DexRegisterLocation(kConstant, 1)), therefore the location catalog index // has a size of 1 bit. - uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding); + uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); ASSERT_EQ(2u, number_of_catalog_entries); ASSERT_EQ(1u, DexRegisterMap::SingleEntrySizeInBits(number_of_catalog_entries)); @@ -635,21 +629,21 @@ TEST(StackMapTest, DexRegisterMapOffsetOverflow) { // locations (that is, 127 bytes of data). // Hence it has a size of 255 bytes, and therefore... ASSERT_EQ(128u, DexRegisterMap::GetLiveBitMaskSize(number_of_dex_registers)); - StackMap stack_map0 = code_info.GetStackMapAt(0, encoding); + StackMap stack_map0 = code_info.GetStackMapAt(0); DexRegisterMap dex_register_map0 = - code_info.GetDexRegisterMapOf(stack_map0, encoding, number_of_dex_registers); + code_info.GetDexRegisterMapOf(stack_map0, number_of_dex_registers); ASSERT_EQ(127u, dex_register_map0.GetLocationMappingDataSize(number_of_dex_registers, number_of_catalog_entries)); ASSERT_EQ(255u, dex_register_map0.Size()); - StackMap stack_map1 = code_info.GetStackMapAt(1, encoding); - ASSERT_TRUE(stack_map1.HasDexRegisterMap(encoding.stack_map.encoding)); + StackMap stack_map1 = code_info.GetStackMapAt(1); + ASSERT_TRUE(stack_map1.HasDexRegisterMap()); // ...the offset of the second Dex register map (relative to the // beginning of the Dex register maps region) is 255 (i.e., // kNoDexRegisterMapSmallEncoding). - ASSERT_NE(stack_map1.GetDexRegisterMapOffset(encoding.stack_map.encoding), - StackMap::kNoDexRegisterMap); - ASSERT_EQ(stack_map1.GetDexRegisterMapOffset(encoding.stack_map.encoding), 0xFFu); + ASSERT_NE(stack_map1.GetDexRegisterMapOffset(), + StackMap::kNoValue); + ASSERT_EQ(stack_map1.GetDexRegisterMapOffset(), 0xFFu); } TEST(StackMapTest, TestShareDexRegisterMap) { @@ -682,33 +676,32 @@ TEST(StackMapTest, TestShareDexRegisterMap) { stream.FillInCodeInfo(region); CodeInfo ci(region); - CodeInfoEncoding encoding = ci.ExtractEncoding(); // Verify first stack map. - StackMap sm0 = ci.GetStackMapAt(0, encoding); - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, encoding, number_of_dex_registers); - ASSERT_EQ(0, dex_registers0.GetMachineRegister(0, number_of_dex_registers, ci, encoding)); - ASSERT_EQ(-2, dex_registers0.GetConstant(1, number_of_dex_registers, ci, encoding)); + StackMap sm0 = ci.GetStackMapAt(0); + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, number_of_dex_registers); + ASSERT_EQ(0, dex_registers0.GetMachineRegister(0, number_of_dex_registers, ci)); + ASSERT_EQ(-2, dex_registers0.GetConstant(1, number_of_dex_registers, ci)); // Verify second stack map. - StackMap sm1 = ci.GetStackMapAt(1, encoding); - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1, encoding, number_of_dex_registers); - ASSERT_EQ(0, dex_registers1.GetMachineRegister(0, number_of_dex_registers, ci, encoding)); - ASSERT_EQ(-2, dex_registers1.GetConstant(1, number_of_dex_registers, ci, encoding)); + StackMap sm1 = ci.GetStackMapAt(1); + DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1, number_of_dex_registers); + ASSERT_EQ(0, dex_registers1.GetMachineRegister(0, number_of_dex_registers, ci)); + ASSERT_EQ(-2, dex_registers1.GetConstant(1, number_of_dex_registers, ci)); // Verify third stack map. - StackMap sm2 = ci.GetStackMapAt(2, encoding); - DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2, encoding, number_of_dex_registers); - ASSERT_EQ(2, dex_registers2.GetMachineRegister(0, number_of_dex_registers, ci, encoding)); - ASSERT_EQ(-2, dex_registers2.GetConstant(1, number_of_dex_registers, ci, encoding)); + StackMap sm2 = ci.GetStackMapAt(2); + DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2, number_of_dex_registers); + ASSERT_EQ(2, dex_registers2.GetMachineRegister(0, number_of_dex_registers, ci)); + ASSERT_EQ(-2, dex_registers2.GetConstant(1, number_of_dex_registers, ci)); // Verify dex register map offsets. - ASSERT_EQ(sm0.GetDexRegisterMapOffset(encoding.stack_map.encoding), - sm1.GetDexRegisterMapOffset(encoding.stack_map.encoding)); - ASSERT_NE(sm0.GetDexRegisterMapOffset(encoding.stack_map.encoding), - sm2.GetDexRegisterMapOffset(encoding.stack_map.encoding)); - ASSERT_NE(sm1.GetDexRegisterMapOffset(encoding.stack_map.encoding), - sm2.GetDexRegisterMapOffset(encoding.stack_map.encoding)); + ASSERT_EQ(sm0.GetDexRegisterMapOffset(), + sm1.GetDexRegisterMapOffset()); + ASSERT_NE(sm0.GetDexRegisterMapOffset(), + sm2.GetDexRegisterMapOffset()); + ASSERT_NE(sm1.GetDexRegisterMapOffset(), + sm2.GetDexRegisterMapOffset()); } TEST(StackMapTest, TestNoDexRegisterMap) { @@ -732,33 +725,32 @@ TEST(StackMapTest, TestNoDexRegisterMap) { stream.FillInCodeInfo(region); CodeInfo code_info(region); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - ASSERT_EQ(2u, code_info.GetNumberOfStackMaps(encoding)); + ASSERT_EQ(2u, code_info.GetNumberOfStackMaps()); - uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding); + uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); ASSERT_EQ(0u, number_of_catalog_entries); - DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); ASSERT_EQ(0u, location_catalog.Size()); - StackMap stack_map = code_info.GetStackMapAt(0, encoding); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); - ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding)); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); - ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map)); - - ASSERT_FALSE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); - ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); - - stack_map = code_info.GetStackMapAt(1, encoding); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1, encoding))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(68, encoding))); - ASSERT_EQ(1u, stack_map.GetDexPc(encoding.stack_map.encoding)); - ASSERT_EQ(68u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); - ASSERT_EQ(0x4u, code_info.GetRegisterMaskOf(encoding, stack_map)); - - ASSERT_FALSE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); - ASSERT_FALSE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); + StackMap stack_map = code_info.GetStackMapAt(0); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); + ASSERT_EQ(0u, stack_map.GetDexPc()); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map)); + + ASSERT_FALSE(stack_map.HasDexRegisterMap()); + ASSERT_FALSE(stack_map.HasInlineInfo()); + + stack_map = code_info.GetStackMapAt(1); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(68))); + ASSERT_EQ(1u, stack_map.GetDexPc()); + ASSERT_EQ(68u, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(0x4u, code_info.GetRegisterMaskOf(stack_map)); + + ASSERT_FALSE(stack_map.HasDexRegisterMap()); + ASSERT_FALSE(stack_map.HasInlineInfo()); } TEST(StackMapTest, InlineTest) { @@ -835,100 +827,99 @@ TEST(StackMapTest, InlineTest) { stream.FillInCodeInfo(region); CodeInfo ci(region); - CodeInfoEncoding encoding = ci.ExtractEncoding(); { // Verify first stack map. - StackMap sm0 = ci.GetStackMapAt(0, encoding); - - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, encoding, 2); - ASSERT_EQ(0, dex_registers0.GetStackOffsetInBytes(0, 2, ci, encoding)); - ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci, encoding)); - - InlineInfo if0 = ci.GetInlineInfoOf(sm0, encoding); - ASSERT_EQ(2u, if0.GetDepth(encoding.inline_info.encoding)); - ASSERT_EQ(2u, if0.GetDexPcAtDepth(encoding.inline_info.encoding, 0)); - ASSERT_TRUE(if0.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 0)); - ASSERT_EQ(3u, if0.GetDexPcAtDepth(encoding.inline_info.encoding, 1)); - ASSERT_TRUE(if0.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 1)); - - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if0, encoding, 1); - ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding)); - - DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if0, encoding, 3); - ASSERT_EQ(16, dex_registers2.GetStackOffsetInBytes(0, 3, ci, encoding)); - ASSERT_EQ(20, dex_registers2.GetConstant(1, 3, ci, encoding)); - ASSERT_EQ(15, dex_registers2.GetMachineRegister(2, 3, ci, encoding)); + StackMap sm0 = ci.GetStackMapAt(0); + + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, 2); + ASSERT_EQ(0, dex_registers0.GetStackOffsetInBytes(0, 2, ci)); + ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci)); + + InlineInfo if0 = ci.GetInlineInfoOf(sm0); + ASSERT_EQ(2u, if0.GetDepth()); + ASSERT_EQ(2u, if0.GetDexPcAtDepth(0)); + ASSERT_TRUE(if0.EncodesArtMethodAtDepth(0)); + ASSERT_EQ(3u, if0.GetDexPcAtDepth(1)); + ASSERT_TRUE(if0.EncodesArtMethodAtDepth(1)); + + DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if0, 1); + ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0, 1, ci)); + + DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if0, 3); + ASSERT_EQ(16, dex_registers2.GetStackOffsetInBytes(0, 3, ci)); + ASSERT_EQ(20, dex_registers2.GetConstant(1, 3, ci)); + ASSERT_EQ(15, dex_registers2.GetMachineRegister(2, 3, ci)); } { // Verify second stack map. - StackMap sm1 = ci.GetStackMapAt(1, encoding); - - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm1, encoding, 2); - ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci, encoding)); - ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci, encoding)); - - InlineInfo if1 = ci.GetInlineInfoOf(sm1, encoding); - ASSERT_EQ(3u, if1.GetDepth(encoding.inline_info.encoding)); - ASSERT_EQ(2u, if1.GetDexPcAtDepth(encoding.inline_info.encoding, 0)); - ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 0)); - ASSERT_EQ(3u, if1.GetDexPcAtDepth(encoding.inline_info.encoding, 1)); - ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 1)); - ASSERT_EQ(5u, if1.GetDexPcAtDepth(encoding.inline_info.encoding, 2)); - ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 2)); - - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if1, encoding, 1); - ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding)); - - DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if1, encoding, 3); - ASSERT_EQ(80, dex_registers2.GetStackOffsetInBytes(0, 3, ci, encoding)); - ASSERT_EQ(10, dex_registers2.GetConstant(1, 3, ci, encoding)); - ASSERT_EQ(5, dex_registers2.GetMachineRegister(2, 3, ci, encoding)); - - ASSERT_FALSE(if1.HasDexRegisterMapAtDepth(encoding.inline_info.encoding, 2)); + StackMap sm1 = ci.GetStackMapAt(1); + + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm1, 2); + ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci)); + ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci)); + + InlineInfo if1 = ci.GetInlineInfoOf(sm1); + ASSERT_EQ(3u, if1.GetDepth()); + ASSERT_EQ(2u, if1.GetDexPcAtDepth(0)); + ASSERT_TRUE(if1.EncodesArtMethodAtDepth(0)); + ASSERT_EQ(3u, if1.GetDexPcAtDepth(1)); + ASSERT_TRUE(if1.EncodesArtMethodAtDepth(1)); + ASSERT_EQ(5u, if1.GetDexPcAtDepth(2)); + ASSERT_TRUE(if1.EncodesArtMethodAtDepth(2)); + + DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if1, 1); + ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0, 1, ci)); + + DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if1, 3); + ASSERT_EQ(80, dex_registers2.GetStackOffsetInBytes(0, 3, ci)); + ASSERT_EQ(10, dex_registers2.GetConstant(1, 3, ci)); + ASSERT_EQ(5, dex_registers2.GetMachineRegister(2, 3, ci)); + + ASSERT_FALSE(if1.HasDexRegisterMapAtDepth(2)); } { // Verify third stack map. - StackMap sm2 = ci.GetStackMapAt(2, encoding); + StackMap sm2 = ci.GetStackMapAt(2); - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm2, encoding, 2); + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm2, 2); ASSERT_FALSE(dex_registers0.IsDexRegisterLive(0)); - ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci, encoding)); - ASSERT_FALSE(sm2.HasInlineInfo(encoding.stack_map.encoding)); + ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci)); + ASSERT_FALSE(sm2.HasInlineInfo()); } { // Verify fourth stack map. - StackMap sm3 = ci.GetStackMapAt(3, encoding); + StackMap sm3 = ci.GetStackMapAt(3); - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm3, encoding, 2); - ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci, encoding)); - ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci, encoding)); + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm3, 2); + ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci)); + ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci)); - InlineInfo if2 = ci.GetInlineInfoOf(sm3, encoding); - ASSERT_EQ(3u, if2.GetDepth(encoding.inline_info.encoding)); - ASSERT_EQ(2u, if2.GetDexPcAtDepth(encoding.inline_info.encoding, 0)); - ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 0)); - ASSERT_EQ(5u, if2.GetDexPcAtDepth(encoding.inline_info.encoding, 1)); - ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 1)); - ASSERT_EQ(10u, if2.GetDexPcAtDepth(encoding.inline_info.encoding, 2)); - ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info.encoding, 2)); + InlineInfo if2 = ci.GetInlineInfoOf(sm3); + ASSERT_EQ(3u, if2.GetDepth()); + ASSERT_EQ(2u, if2.GetDexPcAtDepth(0)); + ASSERT_TRUE(if2.EncodesArtMethodAtDepth(0)); + ASSERT_EQ(5u, if2.GetDexPcAtDepth(1)); + ASSERT_TRUE(if2.EncodesArtMethodAtDepth(1)); + ASSERT_EQ(10u, if2.GetDexPcAtDepth(2)); + ASSERT_TRUE(if2.EncodesArtMethodAtDepth(2)); - ASSERT_FALSE(if2.HasDexRegisterMapAtDepth(encoding.inline_info.encoding, 0)); + ASSERT_FALSE(if2.HasDexRegisterMapAtDepth(0)); - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(1, if2, encoding, 1); - ASSERT_EQ(2, dex_registers1.GetMachineRegister(0, 1, ci, encoding)); + DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(1, if2, 1); + ASSERT_EQ(2, dex_registers1.GetMachineRegister(0, 1, ci)); - DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(2, if2, encoding, 2); + DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(2, if2, 2); ASSERT_FALSE(dex_registers2.IsDexRegisterLive(0)); - ASSERT_EQ(3, dex_registers2.GetMachineRegister(1, 2, ci, encoding)); + ASSERT_EQ(3, dex_registers2.GetMachineRegister(1, 2, ci)); } } TEST(StackMapTest, CodeOffsetTest) { - // Test minimum alignments, encoding, and decoding. + // Test minimum alignments, and decoding. CodeOffset offset_thumb2 = CodeOffset::FromOffset(kThumb2InstructionAlignment, InstructionSet::kThumb2); CodeOffset offset_arm64 = @@ -969,13 +960,12 @@ TEST(StackMapTest, TestDeduplicateStackMask) { stream.FillInCodeInfo(region); CodeInfo code_info(region); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - ASSERT_EQ(2u, code_info.GetNumberOfStackMaps(encoding)); + ASSERT_EQ(2u, code_info.GetNumberOfStackMaps()); - StackMap stack_map1 = code_info.GetStackMapForNativePcOffset(4, encoding); - StackMap stack_map2 = code_info.GetStackMapForNativePcOffset(8, encoding); - EXPECT_EQ(stack_map1.GetStackMaskIndex(encoding.stack_map.encoding), - stack_map2.GetStackMaskIndex(encoding.stack_map.encoding)); + StackMap stack_map1 = code_info.GetStackMapForNativePcOffset(4); + StackMap stack_map2 = code_info.GetStackMapForNativePcOffset(8); + EXPECT_EQ(stack_map1.GetStackMaskIndex(), + stack_map2.GetStackMaskIndex()); } TEST(StackMapTest, TestInvokeInfo) { @@ -1007,26 +997,25 @@ TEST(StackMapTest, TestInvokeInfo) { CodeInfo code_info(code_info_region); MethodInfo method_info(method_info_region.begin()); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - ASSERT_EQ(3u, code_info.GetNumberOfStackMaps(encoding)); + ASSERT_EQ(3u, code_info.GetNumberOfStackMaps()); - InvokeInfo invoke1(code_info.GetInvokeInfoForNativePcOffset(4, encoding)); - InvokeInfo invoke2(code_info.GetInvokeInfoForNativePcOffset(8, encoding)); - InvokeInfo invoke3(code_info.GetInvokeInfoForNativePcOffset(16, encoding)); - InvokeInfo invoke_invalid(code_info.GetInvokeInfoForNativePcOffset(12, encoding)); + InvokeInfo invoke1(code_info.GetInvokeInfoForNativePcOffset(4)); + InvokeInfo invoke2(code_info.GetInvokeInfoForNativePcOffset(8)); + InvokeInfo invoke3(code_info.GetInvokeInfoForNativePcOffset(16)); + InvokeInfo invoke_invalid(code_info.GetInvokeInfoForNativePcOffset(12)); EXPECT_FALSE(invoke_invalid.IsValid()); // No entry for that index. EXPECT_TRUE(invoke1.IsValid()); EXPECT_TRUE(invoke2.IsValid()); EXPECT_TRUE(invoke3.IsValid()); - EXPECT_EQ(invoke1.GetInvokeType(encoding.invoke_info.encoding), kSuper); - EXPECT_EQ(invoke1.GetMethodIndex(encoding.invoke_info.encoding, method_info), 1u); - EXPECT_EQ(invoke1.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA), 4u); - EXPECT_EQ(invoke2.GetInvokeType(encoding.invoke_info.encoding), kStatic); - EXPECT_EQ(invoke2.GetMethodIndex(encoding.invoke_info.encoding, method_info), 3u); - EXPECT_EQ(invoke2.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA), 8u); - EXPECT_EQ(invoke3.GetInvokeType(encoding.invoke_info.encoding), kDirect); - EXPECT_EQ(invoke3.GetMethodIndex(encoding.invoke_info.encoding, method_info), 65535u); - EXPECT_EQ(invoke3.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA), 16u); + EXPECT_EQ(invoke1.GetInvokeType(), kSuper); + EXPECT_EQ(invoke1.GetMethodIndex(method_info), 1u); + EXPECT_EQ(invoke1.GetNativePcOffset(kRuntimeISA), 4u); + EXPECT_EQ(invoke2.GetInvokeType(), kStatic); + EXPECT_EQ(invoke2.GetMethodIndex(method_info), 3u); + EXPECT_EQ(invoke2.GetNativePcOffset(kRuntimeISA), 8u); + EXPECT_EQ(invoke3.GetInvokeType(), kDirect); + EXPECT_EQ(invoke3.GetMethodIndex(method_info), 65535u); + EXPECT_EQ(invoke3.GetNativePcOffset(kRuntimeISA), 16u); } } // namespace art diff --git a/libartbase/Android.bp b/libartbase/Android.bp index 692c97f1c0..adf0ad6376 100644 --- a/libartbase/Android.bp +++ b/libartbase/Android.bp @@ -150,6 +150,7 @@ art_cc_test { "base/bit_memory_region_test.cc", "base/bit_string_test.cc", "base/bit_struct_test.cc", + "base/bit_table_test.cc", "base/bit_utils_test.cc", "base/bit_vector_test.cc", "base/file_utils_test.cc", diff --git a/libartbase/base/bit_memory_region.h b/libartbase/base/bit_memory_region.h index dd16957340..3f4d0ba55b 100644 --- a/libartbase/base/bit_memory_region.h +++ b/libartbase/base/bit_memory_region.h @@ -99,6 +99,13 @@ class BitMemoryRegion FINAL : public ValueObject { return value & mask; } + // Load bits starting at given `bit_offset`, and advance the `bit_offset`. + ALWAYS_INLINE uint32_t LoadBitsAndAdvance(size_t* bit_offset, size_t bit_length) const { + uint32_t result = LoadBits(*bit_offset, bit_length); + *bit_offset += bit_length; + return result; + } + // Store `bit_length` bits in `data` starting at given `bit_offset`. // The least significant bit is stored in the smallest memory offset. ALWAYS_INLINE void StoreBits(size_t bit_offset, uint32_t value, size_t bit_length) { @@ -125,6 +132,12 @@ class BitMemoryRegion FINAL : public ValueObject { DCHECK_EQ(value, LoadBits(bit_offset, bit_length)); } + // Store bits starting at given `bit_offset`, and advance the `bit_offset`. + ALWAYS_INLINE void StoreBitsAndAdvance(size_t* bit_offset, uint32_t value, size_t bit_length) { + StoreBits(*bit_offset, value, bit_length); + *bit_offset += bit_length; + } + ALWAYS_INLINE bool Equals(const BitMemoryRegion& other) const { return data_ == other.data_ && bit_start_ == other.bit_start_ && diff --git a/libartbase/base/bit_table.h b/libartbase/base/bit_table.h new file mode 100644 index 0000000000..24bdd13324 --- /dev/null +++ b/libartbase/base/bit_table.h @@ -0,0 +1,220 @@ +/* + * 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. + */ + +#ifndef ART_LIBARTBASE_BASE_BIT_TABLE_H_ +#define ART_LIBARTBASE_BASE_BIT_TABLE_H_ + +#include + +#include "base/bit_memory_region.h" +#include "base/bit_utils.h" +#include "base/memory_region.h" + +namespace art { + +constexpr uint32_t kVarintHeaderBits = 4; +constexpr uint32_t kVarintSmallValue = 11; // Maximum value which is stored as-is. + +// Load variable-length bit-packed integer from `data` starting at `bit_offset`. +// The first four bits determine the variable length of the encoded integer: +// Values 0..11 represent the result as-is, with no further following bits. +// Values 12..15 mean the result is in the next 8/16/24/32-bits respectively. +ALWAYS_INLINE static inline uint32_t DecodeVarintBits(BitMemoryRegion region, size_t* bit_offset) { + uint32_t x = region.LoadBitsAndAdvance(bit_offset, kVarintHeaderBits); + if (x > kVarintSmallValue) { + x = region.LoadBitsAndAdvance(bit_offset, (x - kVarintSmallValue) * kBitsPerByte); + } + return x; +} + +// Store variable-length bit-packed integer from `data` starting at `bit_offset`. +template +ALWAYS_INLINE static inline void EncodeVarintBits(Vector* out, size_t* bit_offset, uint32_t value) { + if (value <= kVarintSmallValue) { + out->resize(BitsToBytesRoundUp(*bit_offset + kVarintHeaderBits)); + BitMemoryRegion region(MemoryRegion(out->data(), out->size())); + region.StoreBitsAndAdvance(bit_offset, value, kVarintHeaderBits); + } else { + uint32_t num_bits = RoundUp(MinimumBitsToStore(value), kBitsPerByte); + out->resize(BitsToBytesRoundUp(*bit_offset + kVarintHeaderBits + num_bits)); + BitMemoryRegion region(MemoryRegion(out->data(), out->size())); + uint32_t header = kVarintSmallValue + num_bits / kBitsPerByte; + region.StoreBitsAndAdvance(bit_offset, header, kVarintHeaderBits); + region.StoreBitsAndAdvance(bit_offset, value, num_bits); + } +} + +template +class BitTable { + public: + class Accessor { + public: + static constexpr uint32_t kNoValue = std::numeric_limits::max(); + + Accessor(const BitTable* table, uint32_t row) : table_(table), row_(row) {} + + ALWAYS_INLINE uint32_t Row() const { return row_; } + + ALWAYS_INLINE bool IsValid() const { return table_ != nullptr && row_ < table_->NumRows(); } + + template + ALWAYS_INLINE uint32_t Get() const { + static_assert(Column < kNumColumns, "Column out of bounds"); + return table_->Get(row_, Column); + } + + ALWAYS_INLINE bool Equals(const Accessor& other) { + return this->table_ == other.table_ && this->row_ == other.row_; + } + + Accessor& operator++() { + row_++; + return *this; + } + + protected: + const BitTable* table_; + uint32_t row_; + }; + + static constexpr uint32_t kValueBias = -1; + + BitTable() {} + BitTable(void* data, size_t size, size_t* bit_offset = 0) { + Decode(BitMemoryRegion(MemoryRegion(data, size)), bit_offset); + } + + ALWAYS_INLINE void Decode(BitMemoryRegion region, size_t* bit_offset) { + // Decode row count and column sizes from the table header. + num_rows_ = DecodeVarintBits(region, bit_offset); + if (num_rows_ != 0) { + column_offset_[0] = 0; + for (uint32_t i = 0; i < kNumColumns; i++) { + size_t column_end = column_offset_[i] + DecodeVarintBits(region, bit_offset); + column_offset_[i + 1] = column_end; + DCHECK_EQ(column_offset_[i + 1], column_end) << "Overflow"; + } + } + + // Record the region which contains the table data and skip past it. + table_data_ = region.Subregion(*bit_offset, num_rows_ * NumRowBits()); + *bit_offset += table_data_.size_in_bits(); + } + + ALWAYS_INLINE uint32_t Get(uint32_t row, uint32_t column = 0) const { + DCHECK_LT(row, num_rows_); + DCHECK_LT(column, kNumColumns); + size_t offset = row * NumRowBits() + column_offset_[column]; + return table_data_.LoadBits(offset, NumColumnBits(column)) + kValueBias; + } + + size_t NumRows() const { return num_rows_; } + + uint32_t NumRowBits() const { return column_offset_[kNumColumns]; } + + constexpr size_t NumColumns() const { return kNumColumns; } + + uint32_t NumColumnBits(uint32_t column) const { + return column_offset_[column + 1] - column_offset_[column]; + } + + size_t DataBitSize() const { return num_rows_ * column_offset_[kNumColumns]; } + + protected: + BitMemoryRegion table_data_; + size_t num_rows_ = 0; + + uint16_t column_offset_[kNumColumns + 1] = {}; +}; + +template +constexpr uint32_t BitTable::Accessor::kNoValue; + +template +constexpr uint32_t BitTable::kValueBias; + +template> +class BitTableBuilder { + public: + explicit BitTableBuilder(Alloc alloc = Alloc()) : buffer_(alloc) {} + + template + uint32_t AddRow(T ... values) { + constexpr size_t count = sizeof...(values); + static_assert(count == kNumColumns, "Incorrect argument count"); + uint32_t data[count] = { values... }; + buffer_.insert(buffer_.end(), data, data + count); + return num_rows_++; + } + + ALWAYS_INLINE uint32_t Get(uint32_t row, uint32_t column) const { + return buffer_[row * kNumColumns + column]; + } + + template + void Encode(Vector* out, size_t* bit_offset) { + constexpr uint32_t bias = BitTable::kValueBias; + size_t initial_bit_offset = *bit_offset; + // Measure data size. + uint32_t max_column_value[kNumColumns] = {}; + for (uint32_t r = 0; r < num_rows_; r++) { + for (uint32_t c = 0; c < kNumColumns; c++) { + max_column_value[c] |= Get(r, c) - bias; + } + } + // Write table header. + uint32_t table_data_bits = 0; + uint32_t column_bits[kNumColumns] = {}; + EncodeVarintBits(out, bit_offset, num_rows_); + if (num_rows_ != 0) { + for (uint32_t c = 0; c < kNumColumns; c++) { + column_bits[c] = MinimumBitsToStore(max_column_value[c]); + EncodeVarintBits(out, bit_offset, column_bits[c]); + table_data_bits += num_rows_ * column_bits[c]; + } + } + // Write table data. + out->resize(BitsToBytesRoundUp(*bit_offset + table_data_bits)); + BitMemoryRegion region(MemoryRegion(out->data(), out->size())); + for (uint32_t r = 0; r < num_rows_; r++) { + for (uint32_t c = 0; c < kNumColumns; c++) { + region.StoreBitsAndAdvance(bit_offset, Get(r, c) - bias, column_bits[c]); + } + } + // Verify the written data. + if (kIsDebugBuild) { + BitTable table; + table.Decode(region, &initial_bit_offset); + DCHECK_EQ(this->num_rows_, table.NumRows()); + for (uint32_t c = 0; c < kNumColumns; c++) { + DCHECK_EQ(column_bits[c], table.NumColumnBits(c)); + } + for (uint32_t r = 0; r < num_rows_; r++) { + for (uint32_t c = 0; c < kNumColumns; c++) { + DCHECK_EQ(this->Get(r, c), table.Get(r, c)) << " (" << r << ", " << c << ")"; + } + } + } + } + + protected: + std::vector buffer_; + uint32_t num_rows_ = 0; +}; + +} // namespace art + +#endif // ART_LIBARTBASE_BASE_BIT_TABLE_H_ diff --git a/libartbase/base/bit_table_test.cc b/libartbase/base/bit_table_test.cc new file mode 100644 index 0000000000..25bfcf095e --- /dev/null +++ b/libartbase/base/bit_table_test.cc @@ -0,0 +1,117 @@ +/* + * 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. + */ + +#include "bit_table.h" + +#include "gtest/gtest.h" + +namespace art { + +TEST(BitTableTest, TestVarint) { + for (size_t start_bit_offset = 0; start_bit_offset <= 32; start_bit_offset++) { + uint32_t values[] = { 0, 1, 11, 12, 15, 16, 255, 256, ~1u, ~0u }; + for (uint32_t value : values) { + std::vector buffer; + size_t encode_bit_offset = start_bit_offset; + EncodeVarintBits(&buffer, &encode_bit_offset, value); + + size_t decode_bit_offset = start_bit_offset; + BitMemoryRegion region(MemoryRegion(buffer.data(), buffer.size())); + uint32_t result = DecodeVarintBits(region, &decode_bit_offset); + EXPECT_EQ(encode_bit_offset, decode_bit_offset); + EXPECT_EQ(value, result); + } + } +} + +TEST(BitTableTest, TestEmptyTable) { + std::vector buffer; + size_t encode_bit_offset = 0; + BitTableBuilder<1> builder; + builder.Encode(&buffer, &encode_bit_offset); + + size_t decode_bit_offset = 0; + BitTable<1> table(buffer.data(), buffer.size(), &decode_bit_offset); + EXPECT_EQ(encode_bit_offset, decode_bit_offset); + EXPECT_EQ(0u, table.NumRows()); +} + +TEST(BitTableTest, TestSingleColumnTable) { + constexpr uint32_t kNoValue = -1; + std::vector buffer; + size_t encode_bit_offset = 0; + BitTableBuilder<1> builder; + builder.AddRow(42u); + builder.AddRow(kNoValue); + builder.AddRow(1000u); + builder.AddRow(kNoValue); + builder.Encode(&buffer, &encode_bit_offset); + + size_t decode_bit_offset = 0; + BitTable<1> table(buffer.data(), buffer.size(), &decode_bit_offset); + EXPECT_EQ(encode_bit_offset, decode_bit_offset); + EXPECT_EQ(4u, table.NumRows()); + EXPECT_EQ(42u, table.Get(0)); + EXPECT_EQ(kNoValue, table.Get(1)); + EXPECT_EQ(1000u, table.Get(2)); + EXPECT_EQ(kNoValue, table.Get(3)); + EXPECT_EQ(10u, table.NumColumnBits(0)); +} + +TEST(BitTableTest, TestUnalignedTable) { + for (size_t start_bit_offset = 0; start_bit_offset <= 32; start_bit_offset++) { + std::vector buffer; + size_t encode_bit_offset = start_bit_offset; + BitTableBuilder<1> builder; + builder.AddRow(42u); + builder.Encode(&buffer, &encode_bit_offset); + + size_t decode_bit_offset = start_bit_offset; + BitTable<1> table(buffer.data(), buffer.size(), &decode_bit_offset); + EXPECT_EQ(encode_bit_offset, decode_bit_offset) << " start_bit_offset=" << start_bit_offset; + EXPECT_EQ(1u, table.NumRows()); + EXPECT_EQ(42u, table.Get(0)); + } +} + +TEST(BitTableTest, TestBigTable) { + constexpr uint32_t kNoValue = -1; + std::vector buffer; + size_t encode_bit_offset = 0; + BitTableBuilder<4> builder; + builder.AddRow(42u, kNoValue, 0u, static_cast(-2)); + builder.AddRow(62u, kNoValue, 63u, static_cast(-3)); + builder.Encode(&buffer, &encode_bit_offset); + + size_t decode_bit_offset = 0; + BitTable<4> table(buffer.data(), buffer.size(), &decode_bit_offset); + EXPECT_EQ(encode_bit_offset, decode_bit_offset); + EXPECT_EQ(2u, table.NumRows()); + EXPECT_EQ(42u, table.Get(0, 0)); + EXPECT_EQ(kNoValue, table.Get(0, 1)); + EXPECT_EQ(0u, table.Get(0, 2)); + EXPECT_EQ(static_cast(-2), table.Get(0, 3)); + EXPECT_EQ(62u, table.Get(1, 0)); + EXPECT_EQ(kNoValue, table.Get(1, 1)); + EXPECT_EQ(63u, table.Get(1, 2)); + EXPECT_EQ(static_cast(-3), table.Get(1, 3)); + EXPECT_EQ(6u, table.NumColumnBits(0)); + EXPECT_EQ(0u, table.NumColumnBits(1)); + EXPECT_EQ(7u, table.NumColumnBits(2)); + EXPECT_EQ(32u, table.NumColumnBits(3)); +} + +} // namespace art diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 44050ff8ed..5c20efa3f7 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -753,7 +753,7 @@ class OatDumper { kByteKindQuickMethodHeader, kByteKindCodeInfoLocationCatalog, kByteKindCodeInfoDexRegisterMap, - kByteKindCodeInfoEncoding, + kByteKindCodeInfo, kByteKindCodeInfoInvokeInfo, kByteKindCodeInfoStackMasks, kByteKindCodeInfoRegisterMasks, @@ -800,7 +800,7 @@ class OatDumper { if (sum > 0) { Dump(os, "Code ", bits[kByteKindCode], sum); Dump(os, "QuickMethodHeader ", bits[kByteKindQuickMethodHeader], sum); - Dump(os, "CodeInfoEncoding ", bits[kByteKindCodeInfoEncoding], sum); + Dump(os, "CodeInfo ", bits[kByteKindCodeInfo], sum); Dump(os, "CodeInfoLocationCatalog ", bits[kByteKindCodeInfoLocationCatalog], sum); Dump(os, "CodeInfoDexRegisterMap ", bits[kByteKindCodeInfoDexRegisterMap], sum); Dump(os, "CodeInfoStackMasks ", bits[kByteKindCodeInfoStackMasks], sum); @@ -819,7 +819,7 @@ class OatDumper { stack_map_bits, "stack map"); Dump(os, - "StackMapDexPcEncoding ", + "StackMapDexPc ", bits[kByteKindStackMapDexPc], stack_map_bits, "stack map"); @@ -1732,8 +1732,7 @@ class OatDumper { public: explicit StackMapsHelper(const uint8_t* raw_code_info, InstructionSet instruction_set) : code_info_(raw_code_info), - encoding_(code_info_.ExtractEncoding()), - number_of_stack_maps_(code_info_.GetNumberOfStackMaps(encoding_)), + number_of_stack_maps_(code_info_.GetNumberOfStackMaps()), indexes_(), offset_(static_cast(-1)), stack_map_index_(0u), @@ -1741,11 +1740,11 @@ class OatDumper { if (number_of_stack_maps_ != 0u) { // Check if native PCs are ordered. bool ordered = true; - StackMap last = code_info_.GetStackMapAt(0u, encoding_); + StackMap last = code_info_.GetStackMapAt(0u); for (size_t i = 1; i != number_of_stack_maps_; ++i) { - StackMap current = code_info_.GetStackMapAt(i, encoding_); - if (last.GetNativePcOffset(encoding_.stack_map.encoding, instruction_set) > - current.GetNativePcOffset(encoding_.stack_map.encoding, instruction_set)) { + StackMap current = code_info_.GetStackMapAt(i); + if (last.GetNativePcOffset(instruction_set) > + current.GetNativePcOffset(instruction_set)) { ordered = false; break; } @@ -1760,18 +1759,15 @@ class OatDumper { std::sort(indexes_.begin(), indexes_.end(), [this](size_t lhs, size_t rhs) { - StackMap left = code_info_.GetStackMapAt(lhs, encoding_); - uint32_t left_pc = left.GetNativePcOffset(encoding_.stack_map.encoding, - instruction_set_); - StackMap right = code_info_.GetStackMapAt(rhs, encoding_); - uint32_t right_pc = right.GetNativePcOffset(encoding_.stack_map.encoding, - instruction_set_); + StackMap left = code_info_.GetStackMapAt(lhs); + uint32_t left_pc = left.GetNativePcOffset(instruction_set_); + StackMap right = code_info_.GetStackMapAt(rhs); + uint32_t right_pc = right.GetNativePcOffset(instruction_set_); // If the PCs are the same, compare indexes to preserve the original order. return (left_pc < right_pc) || (left_pc == right_pc && lhs < rhs); }); } - offset_ = GetStackMapAt(0).GetNativePcOffset(encoding_.stack_map.encoding, - instruction_set_); + offset_ = GetStackMapAt(0).GetNativePcOffset(instruction_set_); } } @@ -1779,10 +1775,6 @@ class OatDumper { return code_info_; } - const CodeInfoEncoding& GetEncoding() const { - return encoding_; - } - uint32_t GetOffset() const { return offset_; } @@ -1795,8 +1787,7 @@ class OatDumper { ++stack_map_index_; offset_ = (stack_map_index_ == number_of_stack_maps_) ? static_cast(-1) - : GetStackMapAt(stack_map_index_).GetNativePcOffset(encoding_.stack_map.encoding, - instruction_set_); + : GetStackMapAt(stack_map_index_).GetNativePcOffset(instruction_set_); } private: @@ -1805,11 +1796,10 @@ class OatDumper { i = indexes_[i]; } DCHECK_LT(i, number_of_stack_maps_); - return code_info_.GetStackMapAt(i, encoding_); + return code_info_.GetStackMapAt(i); } const CodeInfo code_info_; - const CodeInfoEncoding encoding_; const size_t number_of_stack_maps_; dchecked_vector indexes_; // Used if stack map native PCs are not ordered. uint32_t offset_; @@ -1835,79 +1825,75 @@ class OatDumper { StackMapsHelper helper(oat_method.GetVmapTable(), instruction_set_); MethodInfo method_info(oat_method.GetOatQuickMethodHeader()->GetOptimizedMethodInfo()); { - CodeInfoEncoding encoding(helper.GetEncoding()); - StackMapEncoding stack_map_encoding(encoding.stack_map.encoding); - const size_t num_stack_maps = encoding.stack_map.num_entries; - if (stats_.AddBitsIfUnique(Stats::kByteKindCodeInfoEncoding, - encoding.HeaderSize() * kBitsPerByte, + const CodeInfo code_info = helper.GetCodeInfo(); + const BitTable& stack_maps = code_info.stack_maps_; + const size_t num_stack_maps = stack_maps.NumRows(); + if (stats_.AddBitsIfUnique(Stats::kByteKindCodeInfo, + code_info.size_ * kBitsPerByte, oat_method.GetVmapTable())) { // Stack maps stats_.AddBits( Stats::kByteKindStackMapNativePc, - stack_map_encoding.GetNativePcEncoding().BitSize() * num_stack_maps); + stack_maps.NumColumnBits(StackMap::kNativePcOffset) * num_stack_maps); stats_.AddBits( Stats::kByteKindStackMapDexPc, - stack_map_encoding.GetDexPcEncoding().BitSize() * num_stack_maps); + stack_maps.NumColumnBits(StackMap::kDexPc) * num_stack_maps); stats_.AddBits( Stats::kByteKindStackMapDexRegisterMap, - stack_map_encoding.GetDexRegisterMapEncoding().BitSize() * num_stack_maps); + stack_maps.NumColumnBits(StackMap::kDexRegisterMapOffset) * num_stack_maps); stats_.AddBits( Stats::kByteKindStackMapInlineInfoIndex, - stack_map_encoding.GetInlineInfoEncoding().BitSize() * num_stack_maps); + stack_maps.NumColumnBits(StackMap::kInlineInfoIndex) * num_stack_maps); stats_.AddBits( Stats::kByteKindStackMapRegisterMaskIndex, - stack_map_encoding.GetRegisterMaskIndexEncoding().BitSize() * num_stack_maps); + stack_maps.NumColumnBits(StackMap::kRegisterMaskIndex) * num_stack_maps); stats_.AddBits( Stats::kByteKindStackMapStackMaskIndex, - stack_map_encoding.GetStackMaskIndexEncoding().BitSize() * num_stack_maps); + stack_maps.NumColumnBits(StackMap::kStackMaskIndex) * num_stack_maps); // Stack masks stats_.AddBits( Stats::kByteKindCodeInfoStackMasks, - encoding.stack_mask.encoding.BitSize() * encoding.stack_mask.num_entries); + code_info.stack_masks_.size_in_bits()); // Register masks stats_.AddBits( Stats::kByteKindCodeInfoRegisterMasks, - encoding.register_mask.encoding.BitSize() * encoding.register_mask.num_entries); + code_info.register_masks_.DataBitSize()); // Invoke infos - if (encoding.invoke_info.num_entries > 0u) { - stats_.AddBits( - Stats::kByteKindCodeInfoInvokeInfo, - encoding.invoke_info.encoding.BitSize() * encoding.invoke_info.num_entries); - } + stats_.AddBits( + Stats::kByteKindCodeInfoInvokeInfo, + code_info.invoke_infos_.DataBitSize()); // Location catalog const size_t location_catalog_bytes = - helper.GetCodeInfo().GetDexRegisterLocationCatalogSize(encoding); + helper.GetCodeInfo().GetDexRegisterLocationCatalogSize(); stats_.AddBits(Stats::kByteKindCodeInfoLocationCatalog, kBitsPerByte * location_catalog_bytes); // Dex register bytes. const size_t dex_register_bytes = - helper.GetCodeInfo().GetDexRegisterMapsSize(encoding, - code_item_accessor.RegistersSize()); + helper.GetCodeInfo().GetDexRegisterMapsSize(code_item_accessor.RegistersSize()); stats_.AddBits( Stats::kByteKindCodeInfoDexRegisterMap, kBitsPerByte * dex_register_bytes); // Inline infos. - const size_t num_inline_infos = encoding.inline_info.num_entries; + const BitTable& inline_infos = code_info.inline_infos_; + const size_t num_inline_infos = inline_infos.NumRows(); if (num_inline_infos > 0u) { stats_.AddBits( Stats::kByteKindInlineInfoMethodIndexIdx, - encoding.inline_info.encoding.GetMethodIndexIdxEncoding().BitSize() * - num_inline_infos); + inline_infos.NumColumnBits(InlineInfo::kMethodIndexIdx) * num_inline_infos); stats_.AddBits( Stats::kByteKindInlineInfoDexPc, - encoding.inline_info.encoding.GetDexPcEncoding().BitSize() * num_inline_infos); + inline_infos.NumColumnBits(InlineInfo::kDexPc) * num_inline_infos); stats_.AddBits( Stats::kByteKindInlineInfoExtraData, - encoding.inline_info.encoding.GetExtraDataEncoding().BitSize() * num_inline_infos); + inline_infos.NumColumnBits(InlineInfo::kExtraData) * num_inline_infos); stats_.AddBits( Stats::kByteKindInlineInfoDexRegisterMap, - encoding.inline_info.encoding.GetDexRegisterMapEncoding().BitSize() * - num_inline_infos); + inline_infos.NumColumnBits(InlineInfo::kDexRegisterMapOffset) * num_inline_infos); stats_.AddBits(Stats::kByteKindInlineInfoIsLast, num_inline_infos); } } @@ -1922,7 +1908,6 @@ class OatDumper { DCHECK(stack_map.IsValid()); stack_map.Dump(vios, helper.GetCodeInfo(), - helper.GetEncoding(), method_info, oat_method.GetCodeOffset(), code_item_accessor.RegistersSize(), diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h index b85730d25e..bbe89ca33c 100644 --- a/oatdump/oatdump_test.h +++ b/oatdump/oatdump_test.h @@ -162,7 +162,6 @@ class OatDumpTest : public CommonRuntimeTest { // Code and dex code do not show up if list only. expected_prefixes.push_back("DEX CODE:"); expected_prefixes.push_back("CODE:"); - expected_prefixes.push_back("CodeInfoEncoding"); expected_prefixes.push_back("CodeInfoInlineInfo"); } if (mode == kModeArt) { diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h index e2ad7fd83f..6917899bff 100644 --- a/runtime/check_reference_map_visitor.h +++ b/runtime/check_reference_map_visitor.h @@ -64,20 +64,19 @@ class CheckReferenceMapVisitor : public StackVisitor { void CheckOptimizedMethod(int* registers, int number_of_references, uint32_t native_pc_offset) REQUIRES_SHARED(Locks::mutator_lock_) { ArtMethod* m = GetMethod(); - CodeInfo code_info = GetCurrentOatQuickMethodHeader()->GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); + CodeInfo code_info(GetCurrentOatQuickMethodHeader()); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); CodeItemDataAccessor accessor(m->DexInstructionData()); uint16_t number_of_dex_registers = accessor.RegistersSize(); DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); - uint32_t register_mask = code_info.GetRegisterMaskOf(encoding, stack_map); - BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, stack_map); + code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + uint32_t register_mask = code_info.GetRegisterMaskOf(stack_map); + BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map); for (int i = 0; i < number_of_references; ++i) { int reg = registers[i]; CHECK_LT(reg, accessor.RegistersSize()); DexRegisterLocation location = dex_register_map.GetDexRegisterLocation( - reg, number_of_dex_registers, code_info, encoding); + reg, number_of_dex_registers, code_info); switch (location.GetKind()) { case DexRegisterLocation::Kind::kNone: // Not set, should not be a reference. diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index d4e7492f00..f6b1c73230 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -47,7 +47,6 @@ namespace art { inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, const MethodInfo& method_info, const InlineInfo& inline_info, - const InlineInfoEncoding& encoding, uint8_t inlining_depth) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(!outer_method->IsObsolete()); @@ -57,12 +56,12 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, // suspended while executing it. ScopedAssertNoThreadSuspension sants(__FUNCTION__); - if (inline_info.EncodesArtMethodAtDepth(encoding, inlining_depth)) { - return inline_info.GetArtMethodAtDepth(encoding, inlining_depth); + if (inline_info.EncodesArtMethodAtDepth(inlining_depth)) { + return inline_info.GetArtMethodAtDepth(inlining_depth); } - uint32_t method_index = inline_info.GetMethodIndexAtDepth(encoding, method_info, inlining_depth); - if (inline_info.GetDexPcAtDepth(encoding, inlining_depth) == static_cast(-1)) { + uint32_t method_index = inline_info.GetMethodIndexAtDepth(method_info, inlining_depth); + if (inline_info.GetDexPcAtDepth(inlining_depth) == static_cast(-1)) { // "charAt" special case. It is the only non-leaf method we inline across dex files. ArtMethod* inlined_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt); DCHECK_EQ(inlined_method->GetDexMethodIndex(), method_index); @@ -73,9 +72,9 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); ArtMethod* method = outer_method; for (uint32_t depth = 0, end = inlining_depth + 1u; depth != end; ++depth) { - DCHECK(!inline_info.EncodesArtMethodAtDepth(encoding, depth)); - DCHECK_NE(inline_info.GetDexPcAtDepth(encoding, depth), static_cast(-1)); - method_index = inline_info.GetMethodIndexAtDepth(encoding, method_info, depth); + DCHECK(!inline_info.EncodesArtMethodAtDepth(depth)); + DCHECK_NE(inline_info.GetDexPcAtDepth(depth), static_cast(-1)); + method_index = inline_info.GetMethodIndexAtDepth(method_info, depth); ArtMethod* inlined_method = class_linker->LookupResolvedMethod(method_index, method->GetDexCache(), method->GetClassLoader()); diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 7fc8db375b..91faa40df9 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -201,18 +201,16 @@ static inline ArtMethod* DoGetCalleeSaveMethodCaller(ArtMethod* outer_method, DCHECK(current_code != nullptr); DCHECK(current_code->IsOptimized()); uintptr_t native_pc_offset = current_code->NativeQuickPcOffset(caller_pc); - CodeInfo code_info = current_code->GetOptimizedCodeInfo(); + CodeInfo code_info(current_code); MethodInfo method_info = current_code->GetOptimizedMethodInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); DCHECK(stack_map.IsValid()); - if (stack_map.HasInlineInfo(encoding.stack_map.encoding)) { - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); + if (stack_map.HasInlineInfo()) { + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); caller = GetResolvedMethod(outer_method, method_info, inline_info, - encoding.inline_info.encoding, - inline_info.GetDepth(encoding.inline_info.encoding) - 1); + inline_info.GetDepth() - 1); } } if (kIsDebugBuild && do_caller_check) { diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 0a186f4dc5..e83bcd812b 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -356,16 +356,14 @@ class QuickArgumentVisitor { uintptr_t outer_pc_offset = current_code->NativeQuickPcOffset(outer_pc); if (current_code->IsOptimized()) { - CodeInfo code_info = current_code->GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(outer_pc_offset, encoding); + CodeInfo code_info(current_code); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(outer_pc_offset); DCHECK(stack_map.IsValid()); - if (stack_map.HasInlineInfo(encoding.stack_map.encoding)) { - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); - return inline_info.GetDexPcAtDepth(encoding.inline_info.encoding, - inline_info.GetDepth(encoding.inline_info.encoding)-1); + if (stack_map.HasInlineInfo()) { + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); + return inline_info.GetDexPcAtDepth(inline_info.GetDepth()-1); } else { - return stack_map.GetDexPc(encoding.stack_map.encoding); + return stack_map.GetDexPc(); } } else { return current_code->ToDexPc(*caller_sp, outer_pc); @@ -385,13 +383,12 @@ class QuickArgumentVisitor { return false; } uintptr_t outer_pc_offset = current_code->NativeQuickPcOffset(outer_pc); - CodeInfo code_info = current_code->GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); + CodeInfo code_info(current_code); MethodInfo method_info = current_code->GetOptimizedMethodInfo(); - InvokeInfo invoke(code_info.GetInvokeInfoForNativePcOffset(outer_pc_offset, encoding)); + InvokeInfo invoke(code_info.GetInvokeInfoForNativePcOffset(outer_pc_offset)); if (invoke.IsValid()) { - *invoke_type = static_cast(invoke.GetInvokeType(encoding.invoke_info.encoding)); - *dex_method_index = invoke.GetMethodIndex(encoding.invoke_info.encoding, method_info); + *invoke_type = static_cast(invoke.GetInvokeType()); + *dex_method_index = invoke.GetMethodIndex(method_info); return true; } return false; @@ -1230,12 +1227,11 @@ static void DumpB74410240DebugData(ArtMethod** sp) REQUIRES_SHARED(Locks::mutato CHECK(current_code != nullptr); CHECK(current_code->IsOptimized()); uintptr_t native_pc_offset = current_code->NativeQuickPcOffset(caller_pc); - CodeInfo code_info = current_code->GetOptimizedCodeInfo(); + CodeInfo code_info(current_code); MethodInfo method_info = current_code->GetOptimizedMethodInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); CHECK(stack_map.IsValid()); - uint32_t dex_pc = stack_map.GetDexPc(encoding.stack_map.encoding); + uint32_t dex_pc = stack_map.GetDexPc(); // 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). @@ -1249,20 +1245,17 @@ static void DumpB74410240DebugData(ArtMethod** sp) REQUIRES_SHARED(Locks::mutato 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); + if (stack_map.HasInlineInfo()) { + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); + size_t depth = inline_info.GetDepth(); 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)) { + dex_pc = inline_info.GetDexPcAtDepth(d); + if (inline_info.EncodesArtMethodAtDepth(d)) { tag = "encoded "; - caller = inline_info.GetArtMethodAtDepth(inline_info_encoding, d); + caller = inline_info.GetArtMethodAtDepth(d); } else { - uint32_t method_index = inline_info.GetMethodIndexAtDepth(inline_info_encoding, - method_info, - d); + uint32_t method_index = inline_info.GetMethodIndexAtDepth(method_info, d); if (dex_pc == static_cast(-1)) { tag = "special "; CHECK_EQ(d + 1u, depth); diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 86e69f49a4..5d4b9e8cc9 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -473,11 +473,10 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, return false; } - CodeInfo code_info = osr_method->GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); + CodeInfo code_info(osr_method); // Find stack map starting at the target dex_pc. - StackMap stack_map = code_info.GetOsrStackMapForDexPc(dex_pc + dex_pc_offset, encoding); + StackMap stack_map = code_info.GetOsrStackMapForDexPc(dex_pc + dex_pc_offset); if (!stack_map.IsValid()) { // There is no OSR stack map for this dex pc offset. Just return to the interpreter in the // hope that the next branch has one. @@ -494,7 +493,7 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, // We found a stack map, now fill the frame with dex register values from the interpreter's // shadow frame. DexRegisterMap vreg_map = - code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_vregs); + code_info.GetDexRegisterMapOf(stack_map, number_of_vregs); frame_size = osr_method->GetFrameSizeInBytes(); @@ -516,7 +515,7 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, } else { for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) { DexRegisterLocation::Kind location = - vreg_map.GetLocationKind(vreg, number_of_vregs, code_info, encoding); + vreg_map.GetLocationKind(vreg, number_of_vregs, code_info); if (location == DexRegisterLocation::Kind::kNone) { // Dex register is dead or uninitialized. continue; @@ -532,15 +531,14 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, int32_t vreg_value = shadow_frame->GetVReg(vreg); int32_t slot_offset = vreg_map.GetStackOffsetInBytes(vreg, number_of_vregs, - code_info, - encoding); + code_info); DCHECK_LT(slot_offset, static_cast(frame_size)); DCHECK_GT(slot_offset, 0); (reinterpret_cast(memory))[slot_offset / sizeof(int32_t)] = vreg_value; } } - native_pc = stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA) + + native_pc = stack_map.GetNativePcOffset(kRuntimeISA) + osr_method->GetEntryPoint(); VLOG(jit) << "Jumping to " << method_name diff --git a/runtime/oat.h b/runtime/oat.h index 6c683f1541..7b8f71a3f3 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: compiler support const-method-handle - static constexpr uint8_t kOatVersion[] = { '1', '4', '3', '\0' }; + // Last oat version changed reason: Refactor stackmap encoding. + static constexpr uint8_t kOatVersion[] = { '1', '4', '4', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_quick_method_header.cc b/runtime/oat_quick_method_header.cc index 98238e5600..aed6bc57b3 100644 --- a/runtime/oat_quick_method_header.cc +++ b/runtime/oat_quick_method_header.cc @@ -19,6 +19,7 @@ #include "art_method.h" #include "dex/dex_file_types.h" #include "scoped_thread_state_change-inl.h" +#include "stack_map.h" #include "thread.h" namespace art { @@ -42,11 +43,10 @@ uint32_t OatQuickMethodHeader::ToDexPc(ArtMethod* method, const void* entry_point = GetEntryPoint(); uint32_t sought_offset = pc - reinterpret_cast(entry_point); if (IsOptimized()) { - CodeInfo code_info = GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(sought_offset, encoding); + CodeInfo code_info(this); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(sought_offset); if (stack_map.IsValid()) { - return stack_map.GetDexPc(encoding.stack_map.encoding); + return stack_map.GetDexPc(); } } else { DCHECK(method->IsNative()); @@ -71,18 +71,17 @@ uintptr_t OatQuickMethodHeader::ToNativeQuickPc(ArtMethod* method, DCHECK(!method->IsNative()); DCHECK(IsOptimized()); // Search for the dex-to-pc mapping in stack maps. - CodeInfo code_info = GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); + CodeInfo code_info(this); // All stack maps are stored in the same CodeItem section, safepoint stack // maps first, then catch stack maps. We use `is_for_catch_handler` to select // the order of iteration. StackMap stack_map = - LIKELY(is_for_catch_handler) ? code_info.GetCatchStackMapForDexPc(dex_pc, encoding) - : code_info.GetStackMapForDexPc(dex_pc, encoding); + LIKELY(is_for_catch_handler) ? code_info.GetCatchStackMapForDexPc(dex_pc) + : code_info.GetStackMapForDexPc(dex_pc); if (stack_map.IsValid()) { return reinterpret_cast(entry_point) + - stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA); + stack_map.GetNativePcOffset(kRuntimeISA); } if (abort_on_failure) { ScopedObjectAccess soa(Thread::Current()); diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h index f0966b7bfa..d6762d6bc6 100644 --- a/runtime/oat_quick_method_header.h +++ b/runtime/oat_quick_method_header.h @@ -22,7 +22,6 @@ #include "base/utils.h" #include "method_info.h" #include "quick/quick_method_frame_info.h" -#include "stack_map.h" namespace art { @@ -75,10 +74,6 @@ class PACKED(4) OatQuickMethodHeader { return code_ - vmap_table_offset_; } - CodeInfo GetOptimizedCodeInfo() const { - return CodeInfo(GetOptimizedCodeInfoPtr()); - } - const void* GetOptimizedMethodInfoPtr() const { DCHECK(IsOptimized()); return reinterpret_cast(code_ - method_info_offset_); diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 077aa33925..c555fca23c 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -224,30 +224,29 @@ void QuickExceptionHandler::SetCatchEnvironmentForOptimizedHandler(StackVisitor* CodeItemDataAccessor accessor(handler_method_->DexInstructionData()); const size_t number_of_vregs = accessor.RegistersSize(); - CodeInfo code_info = handler_method_header_->GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); + CodeInfo code_info(handler_method_header_); // Find stack map of the catch block. - StackMap catch_stack_map = code_info.GetCatchStackMapForDexPc(GetHandlerDexPc(), encoding); + StackMap catch_stack_map = code_info.GetCatchStackMapForDexPc(GetHandlerDexPc()); DCHECK(catch_stack_map.IsValid()); DexRegisterMap catch_vreg_map = - code_info.GetDexRegisterMapOf(catch_stack_map, encoding, number_of_vregs); + code_info.GetDexRegisterMapOf(catch_stack_map, number_of_vregs); if (!catch_vreg_map.IsValid()) { return; } // Find stack map of the throwing instruction. StackMap throw_stack_map = - code_info.GetStackMapForNativePcOffset(stack_visitor->GetNativePcOffset(), encoding); + code_info.GetStackMapForNativePcOffset(stack_visitor->GetNativePcOffset()); DCHECK(throw_stack_map.IsValid()); DexRegisterMap throw_vreg_map = - code_info.GetDexRegisterMapOf(throw_stack_map, encoding, number_of_vregs); + code_info.GetDexRegisterMapOf(throw_stack_map, number_of_vregs); DCHECK(throw_vreg_map.IsValid()); // Copy values between them. for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) { DexRegisterLocation::Kind catch_location = - catch_vreg_map.GetLocationKind(vreg, number_of_vregs, code_info, encoding); + catch_vreg_map.GetLocationKind(vreg, number_of_vregs, code_info); if (catch_location == DexRegisterLocation::Kind::kNone) { continue; } @@ -257,8 +256,7 @@ void QuickExceptionHandler::SetCatchEnvironmentForOptimizedHandler(StackVisitor* uint32_t vreg_value; VRegKind vreg_kind = ToVRegKind(throw_vreg_map.GetLocationKind(vreg, number_of_vregs, - code_info, - encoding)); + code_info)); bool get_vreg_success = stack_visitor->GetVReg(stack_visitor->GetMethod(), vreg, vreg_kind, @@ -271,8 +269,7 @@ void QuickExceptionHandler::SetCatchEnvironmentForOptimizedHandler(StackVisitor* // Copy value to the catch phi's stack slot. int32_t slot_offset = catch_vreg_map.GetStackOffsetInBytes(vreg, number_of_vregs, - code_info, - encoding); + code_info); ArtMethod** frame_top = stack_visitor->GetCurrentQuickFrame(); uint8_t* slot_address = reinterpret_cast(frame_top) + slot_offset; uint32_t* slot_ptr = reinterpret_cast(slot_address); @@ -404,20 +401,18 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { const bool* updated_vregs) REQUIRES_SHARED(Locks::mutator_lock_) { const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); - CodeInfo code_info = method_header->GetOptimizedCodeInfo(); + CodeInfo code_info(method_header); uintptr_t native_pc_offset = method_header->NativeQuickPcOffset(GetCurrentQuickFramePc()); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); CodeItemDataAccessor accessor(m->DexInstructionData()); const size_t number_of_vregs = accessor.RegistersSize(); - uint32_t register_mask = code_info.GetRegisterMaskOf(encoding, stack_map); - BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, stack_map); + uint32_t register_mask = code_info.GetRegisterMaskOf(stack_map); + BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map); DexRegisterMap vreg_map = IsInInlinedFrame() ? code_info.GetDexRegisterMapAtDepth(GetCurrentInliningDepth() - 1, - code_info.GetInlineInfoOf(stack_map, encoding), - encoding, + code_info.GetInlineInfoOf(stack_map), number_of_vregs) - : code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_vregs); + : code_info.GetDexRegisterMapOf(stack_map, number_of_vregs); if (!vreg_map.IsValid()) { return; @@ -430,7 +425,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { } DexRegisterLocation::Kind location = - vreg_map.GetLocationKind(vreg, number_of_vregs, code_info, encoding); + vreg_map.GetLocationKind(vreg, number_of_vregs, code_info); static constexpr uint32_t kDeadValue = 0xEBADDE09; uint32_t value = kDeadValue; bool is_reference = false; @@ -439,12 +434,11 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { case DexRegisterLocation::Kind::kInStack: { const int32_t offset = vreg_map.GetStackOffsetInBytes(vreg, number_of_vregs, - code_info, - encoding); + code_info); const uint8_t* addr = reinterpret_cast(GetCurrentQuickFrame()) + offset; value = *reinterpret_cast(addr); uint32_t bit = (offset >> 2); - if (bit < encoding.stack_mask.encoding.BitSize() && stack_mask.LoadBit(bit)) { + if (bit < code_info.GetNumberOfStackMaskBits() && stack_mask.LoadBit(bit)) { is_reference = true; } break; @@ -453,7 +447,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { case DexRegisterLocation::Kind::kInRegisterHigh: case DexRegisterLocation::Kind::kInFpuRegister: case DexRegisterLocation::Kind::kInFpuRegisterHigh: { - uint32_t reg = vreg_map.GetMachineRegister(vreg, number_of_vregs, code_info, encoding); + uint32_t reg = vreg_map.GetMachineRegister(vreg, number_of_vregs, code_info); bool result = GetRegisterIfAccessible(reg, ToVRegKind(location), &value); CHECK(result); if (location == DexRegisterLocation::Kind::kInRegister) { @@ -464,7 +458,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { break; } case DexRegisterLocation::Kind::kConstant: { - value = vreg_map.GetConstant(vreg, number_of_vregs, code_info, encoding); + value = vreg_map.GetConstant(vreg, number_of_vregs, code_info); if (value == 0) { // Make it a reference for extra safety. is_reference = true; @@ -479,8 +473,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { << "Unexpected location kind " << vreg_map.GetLocationInternalKind(vreg, number_of_vregs, - code_info, - encoding); + code_info); UNREACHABLE(); } } diff --git a/runtime/stack.cc b/runtime/stack.cc index 229238e0f7..740d870af3 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -75,15 +75,14 @@ StackVisitor::StackVisitor(Thread* thread, } } -static InlineInfo GetCurrentInlineInfo(const OatQuickMethodHeader* method_header, +static InlineInfo GetCurrentInlineInfo(CodeInfo& code_info, + const OatQuickMethodHeader* method_header, uintptr_t cur_quick_frame_pc) REQUIRES_SHARED(Locks::mutator_lock_) { uint32_t native_pc_offset = method_header->NativeQuickPcOffset(cur_quick_frame_pc); - CodeInfo code_info = method_header->GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); DCHECK(stack_map.IsValid()); - return code_info.GetInlineInfoOf(stack_map, encoding); + return code_info.GetInlineInfoOf(stack_map); } ArtMethod* StackVisitor::GetMethod() const { @@ -92,16 +91,16 @@ ArtMethod* StackVisitor::GetMethod() const { } else if (cur_quick_frame_ != nullptr) { if (IsInInlinedFrame()) { size_t depth_in_stack_map = current_inlining_depth_ - 1; - InlineInfo inline_info = GetCurrentInlineInfo(GetCurrentOatQuickMethodHeader(), - cur_quick_frame_pc_); const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); - CodeInfoEncoding encoding = method_header->GetOptimizedCodeInfo().ExtractEncoding(); + CodeInfo code_info(method_header); + InlineInfo inline_info = GetCurrentInlineInfo(code_info, + method_header, + cur_quick_frame_pc_); MethodInfo method_info = method_header->GetOptimizedMethodInfo(); DCHECK(walk_kind_ != StackWalkKind::kSkipInlinedFrames); return GetResolvedMethod(*GetCurrentQuickFrame(), method_info, inline_info, - encoding.inline_info.encoding, depth_in_stack_map); } else { return *cur_quick_frame_; @@ -115,11 +114,11 @@ uint32_t StackVisitor::GetDexPc(bool abort_on_failure) const { return cur_shadow_frame_->GetDexPC(); } else if (cur_quick_frame_ != nullptr) { if (IsInInlinedFrame()) { - size_t depth_in_stack_map = current_inlining_depth_ - 1; const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); - CodeInfoEncoding encoding = method_header->GetOptimizedCodeInfo().ExtractEncoding(); - return GetCurrentInlineInfo(GetCurrentOatQuickMethodHeader(), cur_quick_frame_pc_). - GetDexPcAtDepth(encoding.inline_info.encoding, depth_in_stack_map); + CodeInfo code_info(method_header); + size_t depth_in_stack_map = current_inlining_depth_ - 1; + return GetCurrentInlineInfo(code_info, method_header, cur_quick_frame_pc_). + GetDexPcAtDepth(depth_in_stack_map); } else if (cur_oat_quick_method_header_ == nullptr) { return dex::kDexNoIndex; } else { @@ -229,32 +228,29 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin uint16_t number_of_dex_registers = accessor.RegistersSize(); DCHECK_LT(vreg, number_of_dex_registers); const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); - CodeInfo code_info = method_header->GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); + CodeInfo code_info(method_header); uint32_t native_pc_offset = method_header->NativeQuickPcOffset(cur_quick_frame_pc_); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); DCHECK(stack_map.IsValid()); size_t depth_in_stack_map = current_inlining_depth_ - 1; DexRegisterMap dex_register_map = IsInInlinedFrame() ? code_info.GetDexRegisterMapAtDepth(depth_in_stack_map, - code_info.GetInlineInfoOf(stack_map, encoding), - encoding, + code_info.GetInlineInfoOf(stack_map), number_of_dex_registers) - : code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); + : code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); if (!dex_register_map.IsValid()) { return false; } DexRegisterLocation::Kind location_kind = - dex_register_map.GetLocationKind(vreg, number_of_dex_registers, code_info, encoding); + dex_register_map.GetLocationKind(vreg, number_of_dex_registers, code_info); switch (location_kind) { case DexRegisterLocation::Kind::kInStack: { const int32_t offset = dex_register_map.GetStackOffsetInBytes(vreg, number_of_dex_registers, - code_info, - encoding); + code_info); const uint8_t* addr = reinterpret_cast(cur_quick_frame_) + offset; *val = *reinterpret_cast(addr); return true; @@ -264,11 +260,11 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin case DexRegisterLocation::Kind::kInFpuRegister: case DexRegisterLocation::Kind::kInFpuRegisterHigh: { uint32_t reg = - dex_register_map.GetMachineRegister(vreg, number_of_dex_registers, code_info, encoding); + dex_register_map.GetMachineRegister(vreg, number_of_dex_registers, code_info); return GetRegisterIfAccessible(reg, kind, val); } case DexRegisterLocation::Kind::kConstant: - *val = dex_register_map.GetConstant(vreg, number_of_dex_registers, code_info, encoding); + *val = dex_register_map.GetConstant(vreg, number_of_dex_registers, code_info); return true; case DexRegisterLocation::Kind::kNone: return false; @@ -277,8 +273,7 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin << "Unexpected location kind " << dex_register_map.GetLocationInternalKind(vreg, number_of_dex_registers, - code_info, - encoding); + code_info); UNREACHABLE(); } } @@ -830,15 +825,14 @@ void StackVisitor::WalkStack(bool include_transitions) { if ((walk_kind_ == StackWalkKind::kIncludeInlinedFrames) && (cur_oat_quick_method_header_ != nullptr) && cur_oat_quick_method_header_->IsOptimized()) { - CodeInfo code_info = cur_oat_quick_method_header_->GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); + CodeInfo code_info(cur_oat_quick_method_header_); uint32_t native_pc_offset = cur_oat_quick_method_header_->NativeQuickPcOffset(cur_quick_frame_pc_); - StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); - if (stack_map.IsValid() && stack_map.HasInlineInfo(encoding.stack_map.encoding)) { - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); + if (stack_map.IsValid() && stack_map.HasInlineInfo()) { + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); DCHECK_EQ(current_inlining_depth_, 0u); - for (current_inlining_depth_ = inline_info.GetDepth(encoding.inline_info.encoding); + for (current_inlining_depth_ = inline_info.GetDepth(); current_inlining_depth_ != 0; --current_inlining_depth_) { bool should_continue = VisitFrame(); diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index 9c7b6875cf..2b7e8dd748 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -25,8 +25,6 @@ namespace art { constexpr size_t DexRegisterLocationCatalog::kNoLocationEntryIndex; -constexpr uint32_t StackMap::kNoDexRegisterMap; -constexpr uint32_t StackMap::kNoInlineInfo; std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation::Kind& kind) { using Kind = DexRegisterLocation::Kind; @@ -56,27 +54,25 @@ std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation::Kind& DexRegisterLocation::Kind DexRegisterMap::GetLocationInternalKind( uint16_t dex_register_number, uint16_t number_of_dex_registers, - const CodeInfo& code_info, - const CodeInfoEncoding& enc) const { + const CodeInfo& code_info) const { DexRegisterLocationCatalog dex_register_location_catalog = - code_info.GetDexRegisterLocationCatalog(enc); + code_info.GetDexRegisterLocationCatalog(); size_t location_catalog_entry_index = GetLocationCatalogEntryIndex( dex_register_number, number_of_dex_registers, - code_info.GetNumberOfLocationCatalogEntries(enc)); + code_info.GetNumberOfLocationCatalogEntries()); return dex_register_location_catalog.GetLocationInternalKind(location_catalog_entry_index); } DexRegisterLocation DexRegisterMap::GetDexRegisterLocation(uint16_t dex_register_number, uint16_t number_of_dex_registers, - const CodeInfo& code_info, - const CodeInfoEncoding& enc) const { + const CodeInfo& code_info) const { DexRegisterLocationCatalog dex_register_location_catalog = - code_info.GetDexRegisterLocationCatalog(enc); + code_info.GetDexRegisterLocationCatalog(); size_t location_catalog_entry_index = GetLocationCatalogEntryIndex( dex_register_number, number_of_dex_registers, - code_info.GetNumberOfLocationCatalogEntries(enc)); + code_info.GetNumberOfLocationCatalogEntries()); return dex_register_location_catalog.GetDexRegisterLocation(location_catalog_entry_index); } @@ -90,27 +86,28 @@ static void DumpRegisterMapping(std::ostream& os, << " (" << location.GetValue() << ")" << suffix << '\n'; } -void StackMapEncoding::Dump(VariableIndentationOutputStream* vios) const { +void StackMap::DumpEncoding(const BitTable<6>& table, + VariableIndentationOutputStream* vios) { vios->Stream() << "StackMapEncoding" - << " (native_pc_bit_offset=" << static_cast(kNativePcBitOffset) - << ", dex_pc_bit_offset=" << static_cast(dex_pc_bit_offset_) - << ", dex_register_map_bit_offset=" << static_cast(dex_register_map_bit_offset_) - << ", inline_info_bit_offset=" << static_cast(inline_info_bit_offset_) - << ", register_mask_bit_offset=" << static_cast(register_mask_index_bit_offset_) - << ", stack_mask_index_bit_offset=" << static_cast(stack_mask_index_bit_offset_) - << ", total_bit_size=" << static_cast(total_bit_size_) + << " (NativePcOffsetBits=" << table.NumColumnBits(kNativePcOffset) + << ", DexPcBits=" << table.NumColumnBits(kDexPc) + << ", DexRegisterMapOffsetBits=" << table.NumColumnBits(kDexRegisterMapOffset) + << ", InlineInfoIndexBits=" << table.NumColumnBits(kInlineInfoIndex) + << ", RegisterMaskIndexBits=" << table.NumColumnBits(kRegisterMaskIndex) + << ", StackMaskIndexBits=" << table.NumColumnBits(kStackMaskIndex) << ")\n"; } -void InlineInfoEncoding::Dump(VariableIndentationOutputStream* vios) const { +void InlineInfo::DumpEncoding(const BitTable<5>& table, + VariableIndentationOutputStream* vios) { vios->Stream() << "InlineInfoEncoding" - << " (method_index_bit_offset=" << static_cast(kMethodIndexBitOffset) - << ", dex_pc_bit_offset=" << static_cast(dex_pc_bit_offset_) - << ", extra_data_bit_offset=" << static_cast(extra_data_bit_offset_) - << ", dex_register_map_bit_offset=" << static_cast(dex_register_map_bit_offset_) - << ", total_bit_size=" << static_cast(total_bit_size_) + << " (IsLastBits=" << table.NumColumnBits(kIsLast) + << ", MethodIndexIdxBits=" << table.NumColumnBits(kMethodIndexIdx) + << ", DexPcBits=" << table.NumColumnBits(kDexPc) + << ", ExtraDataBits=" << table.NumColumnBits(kExtraData) + << ", DexRegisterMapOffsetBits=" << table.NumColumnBits(kDexRegisterMapOffset) << ")\n"; } @@ -120,26 +117,24 @@ void CodeInfo::Dump(VariableIndentationOutputStream* vios, bool dump_stack_maps, InstructionSet instruction_set, const MethodInfo& method_info) const { - CodeInfoEncoding encoding = ExtractEncoding(); - size_t number_of_stack_maps = GetNumberOfStackMaps(encoding); + size_t number_of_stack_maps = GetNumberOfStackMaps(); vios->Stream() << "Optimized CodeInfo (number_of_dex_registers=" << number_of_dex_registers << ", number_of_stack_maps=" << number_of_stack_maps << ")\n"; ScopedIndentation indent1(vios); - encoding.stack_map.encoding.Dump(vios); - if (HasInlineInfo(encoding)) { - encoding.inline_info.encoding.Dump(vios); + StackMap::DumpEncoding(stack_maps_, vios); + if (HasInlineInfo()) { + InlineInfo::DumpEncoding(inline_infos_, vios); } // Display the Dex register location catalog. - GetDexRegisterLocationCatalog(encoding).Dump(vios, *this); + GetDexRegisterLocationCatalog().Dump(vios, *this); // Display stack maps along with (live) Dex register maps. if (dump_stack_maps) { for (size_t i = 0; i < number_of_stack_maps; ++i) { - StackMap stack_map = GetStackMapAt(i, encoding); + StackMap stack_map = GetStackMapAt(i); stack_map.Dump(vios, *this, - encoding, method_info, code_offset, number_of_dex_registers, @@ -153,9 +148,8 @@ void CodeInfo::Dump(VariableIndentationOutputStream* vios, void DexRegisterLocationCatalog::Dump(VariableIndentationOutputStream* vios, const CodeInfo& code_info) { - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - size_t number_of_location_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding); - size_t location_catalog_size_in_bytes = code_info.GetDexRegisterLocationCatalogSize(encoding); + size_t number_of_location_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); + size_t location_catalog_size_in_bytes = code_info.GetDexRegisterLocationCatalogSize(); vios->Stream() << "DexRegisterLocationCatalog (number_of_entries=" << number_of_location_catalog_entries << ", size_in_bytes=" << location_catalog_size_in_bytes << ")\n"; @@ -169,8 +163,7 @@ void DexRegisterLocationCatalog::Dump(VariableIndentationOutputStream* vios, void DexRegisterMap::Dump(VariableIndentationOutputStream* vios, const CodeInfo& code_info, uint16_t number_of_dex_registers) const { - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - size_t number_of_location_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding); + size_t number_of_location_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); // TODO: Display the bit mask of live Dex registers. for (size_t j = 0; j < number_of_dex_registers; ++j) { if (IsDexRegisterLive(j)) { @@ -178,8 +171,7 @@ void DexRegisterMap::Dump(VariableIndentationOutputStream* vios, j, number_of_dex_registers, number_of_location_catalog_entries); DexRegisterLocation location = GetDexRegisterLocation(j, number_of_dex_registers, - code_info, - encoding); + code_info); ScopedIndentation indent1(vios); DumpRegisterMapping( vios->Stream(), j, location, "v", @@ -190,38 +182,35 @@ void DexRegisterMap::Dump(VariableIndentationOutputStream* vios, void StackMap::Dump(VariableIndentationOutputStream* vios, const CodeInfo& code_info, - const CodeInfoEncoding& encoding, const MethodInfo& method_info, uint32_t code_offset, uint16_t number_of_dex_registers, InstructionSet instruction_set, const std::string& header_suffix) const { - StackMapEncoding stack_map_encoding = encoding.stack_map.encoding; - const uint32_t pc_offset = GetNativePcOffset(stack_map_encoding, instruction_set); + const uint32_t pc_offset = GetNativePcOffset(instruction_set); vios->Stream() << "StackMap" << header_suffix << std::hex << " [native_pc=0x" << code_offset + pc_offset << "]" - << " [entry_size=0x" << encoding.stack_map.encoding.BitSize() << " bits]" - << " (dex_pc=0x" << GetDexPc(stack_map_encoding) + << " (dex_pc=0x" << GetDexPc() << ", native_pc_offset=0x" << pc_offset - << ", dex_register_map_offset=0x" << GetDexRegisterMapOffset(stack_map_encoding) - << ", inline_info_offset=0x" << GetInlineInfoIndex(stack_map_encoding) - << ", register_mask=0x" << code_info.GetRegisterMaskOf(encoding, *this) + << ", dex_register_map_offset=0x" << GetDexRegisterMapOffset() + << ", inline_info_offset=0x" << GetInlineInfoIndex() + << ", register_mask=0x" << code_info.GetRegisterMaskOf(*this) << std::dec << ", stack_mask=0b"; - BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, *this); - for (size_t i = 0, e = encoding.stack_mask.encoding.BitSize(); i < e; ++i) { + BitMemoryRegion stack_mask = code_info.GetStackMaskOf(*this); + for (size_t i = 0, e = code_info.GetNumberOfStackMaskBits(); i < e; ++i) { vios->Stream() << stack_mask.LoadBit(e - i - 1); } vios->Stream() << ")\n"; - if (HasDexRegisterMap(stack_map_encoding)) { + if (HasDexRegisterMap()) { DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf( - *this, encoding, number_of_dex_registers); + *this, number_of_dex_registers); dex_register_map.Dump(vios, code_info, number_of_dex_registers); } - if (HasInlineInfo(stack_map_encoding)) { - InlineInfo inline_info = code_info.GetInlineInfoOf(*this, encoding); + if (HasInlineInfo()) { + InlineInfo inline_info = code_info.GetInlineInfoOf(*this); // We do not know the length of the dex register maps of inlined frames // at this level, so we just pass null to `InlineInfo::Dump` to tell // it not to look at these maps. @@ -233,29 +222,27 @@ void InlineInfo::Dump(VariableIndentationOutputStream* vios, const CodeInfo& code_info, const MethodInfo& method_info, uint16_t number_of_dex_registers[]) const { - InlineInfoEncoding inline_info_encoding = code_info.ExtractEncoding().inline_info.encoding; vios->Stream() << "InlineInfo with depth " - << static_cast(GetDepth(inline_info_encoding)) + << static_cast(GetDepth()) << "\n"; - for (size_t i = 0; i < GetDepth(inline_info_encoding); ++i) { + for (size_t i = 0; i < GetDepth(); ++i) { vios->Stream() << " At depth " << i << std::hex - << " (dex_pc=0x" << GetDexPcAtDepth(inline_info_encoding, i); - if (EncodesArtMethodAtDepth(inline_info_encoding, i)) { + << " (dex_pc=0x" << GetDexPcAtDepth(i); + if (EncodesArtMethodAtDepth(i)) { ScopedObjectAccess soa(Thread::Current()); - vios->Stream() << ", method=" << GetArtMethodAtDepth(inline_info_encoding, i)->PrettyMethod(); + vios->Stream() << ", method=" << GetArtMethodAtDepth(i)->PrettyMethod(); } else { vios->Stream() << std::dec - << ", method_index=" << GetMethodIndexAtDepth(inline_info_encoding, method_info, i); + << ", method_index=" << GetMethodIndexAtDepth(method_info, i); } vios->Stream() << ")\n"; - if (HasDexRegisterMapAtDepth(inline_info_encoding, i) && (number_of_dex_registers != nullptr)) { - CodeInfoEncoding encoding = code_info.ExtractEncoding(); + if (HasDexRegisterMapAtDepth(i) && (number_of_dex_registers != nullptr)) { DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapAtDepth(i, *this, encoding, number_of_dex_registers[i]); + code_info.GetDexRegisterMapAtDepth(i, *this, number_of_dex_registers[i]); ScopedIndentation indent1(vios); dex_register_map.Dump(vios, code_info, number_of_dex_registers[i]); } diff --git a/runtime/stack_map.h b/runtime/stack_map.h index fb1867488e..91cecf0690 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -21,12 +21,14 @@ #include "arch/code_offset.h" #include "base/bit_memory_region.h" +#include "base/bit_table.h" #include "base/bit_utils.h" #include "base/bit_vector.h" #include "base/leb128.h" #include "base/memory_region.h" #include "dex/dex_file_types.h" #include "method_info.h" +#include "oat_quick_method_header.h" namespace art { @@ -39,8 +41,6 @@ static constexpr ssize_t kFrameSlotSize = 4; class ArtMethod; class CodeInfo; -class StackMapEncoding; -struct CodeInfoEncoding; /** * Classes in the following file are wrapper on stack map information backed @@ -454,30 +454,26 @@ class DexRegisterMap { // Get the surface kind of Dex register `dex_register_number`. DexRegisterLocation::Kind GetLocationKind(uint16_t dex_register_number, uint16_t number_of_dex_registers, - const CodeInfo& code_info, - const CodeInfoEncoding& enc) const { + const CodeInfo& code_info) const { return DexRegisterLocation::ConvertToSurfaceKind( - GetLocationInternalKind(dex_register_number, number_of_dex_registers, code_info, enc)); + GetLocationInternalKind(dex_register_number, number_of_dex_registers, code_info)); } // Get the internal kind of Dex register `dex_register_number`. DexRegisterLocation::Kind GetLocationInternalKind(uint16_t dex_register_number, uint16_t number_of_dex_registers, - const CodeInfo& code_info, - const CodeInfoEncoding& enc) const; + const CodeInfo& code_info) const; // Get the Dex register location `dex_register_number`. DexRegisterLocation GetDexRegisterLocation(uint16_t dex_register_number, uint16_t number_of_dex_registers, - const CodeInfo& code_info, - const CodeInfoEncoding& enc) const; + const CodeInfo& code_info) const; int32_t GetStackOffsetInBytes(uint16_t dex_register_number, uint16_t number_of_dex_registers, - const CodeInfo& code_info, - const CodeInfoEncoding& enc) const { + const CodeInfo& code_info) const { DexRegisterLocation location = - GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info, enc); + GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info); DCHECK(location.GetKind() == DexRegisterLocation::Kind::kInStack); // GetDexRegisterLocation returns the offset in bytes. return location.GetValue(); @@ -485,20 +481,18 @@ class DexRegisterMap { int32_t GetConstant(uint16_t dex_register_number, uint16_t number_of_dex_registers, - const CodeInfo& code_info, - const CodeInfoEncoding& enc) const { + const CodeInfo& code_info) const { DexRegisterLocation location = - GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info, enc); + GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info); DCHECK_EQ(location.GetKind(), DexRegisterLocation::Kind::kConstant); return location.GetValue(); } int32_t GetMachineRegister(uint16_t dex_register_number, uint16_t number_of_dex_registers, - const CodeInfo& code_info, - const CodeInfoEncoding& enc) const { + const CodeInfo& code_info) const { DexRegisterLocation location = - GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info, enc); + GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info); DCHECK(location.GetInternalKind() == DexRegisterLocation::Kind::kInRegister || location.GetInternalKind() == DexRegisterLocation::Kind::kInRegisterHigh || location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegister || @@ -653,137 +647,6 @@ class DexRegisterMap { friend class StackMapStream; }; -// Represents bit range of bit-packed integer field. -// We reuse the idea from ULEB128p1 to support encoding of -1 (aka 0xFFFFFFFF). -// If min_value is set to -1, we implicitly subtract one from any loaded value, -// and add one to any stored value. This is generalized to any negative values. -// In other words, min_value acts as a base and the stored value is added to it. -struct FieldEncoding { - FieldEncoding(size_t start_offset, size_t end_offset, int32_t min_value = 0) - : start_offset_(start_offset), end_offset_(end_offset), min_value_(min_value) { - DCHECK_LE(start_offset_, end_offset_); - DCHECK_LE(BitSize(), 32u); - } - - ALWAYS_INLINE size_t BitSize() const { return end_offset_ - start_offset_; } - - template - ALWAYS_INLINE int32_t Load(const Region& region) const { - DCHECK_LE(end_offset_, region.size_in_bits()); - return static_cast(region.LoadBits(start_offset_, BitSize())) + min_value_; - } - - template - ALWAYS_INLINE void Store(Region region, int32_t value) const { - region.StoreBits(start_offset_, static_cast(value - min_value_), BitSize()); - DCHECK_EQ(Load(region), value); - } - - private: - size_t start_offset_; - size_t end_offset_; - int32_t min_value_; -}; - -class StackMapEncoding { - public: - StackMapEncoding() - : dex_pc_bit_offset_(0), - dex_register_map_bit_offset_(0), - inline_info_bit_offset_(0), - register_mask_index_bit_offset_(0), - stack_mask_index_bit_offset_(0), - total_bit_size_(0) {} - - // Set stack map bit layout based on given sizes. - // Returns the size of stack map in bits. - size_t SetFromSizes(size_t native_pc_max, - size_t dex_pc_max, - size_t dex_register_map_size, - size_t number_of_inline_info, - size_t number_of_register_masks, - size_t number_of_stack_masks) { - total_bit_size_ = 0; - DCHECK_EQ(kNativePcBitOffset, total_bit_size_); - total_bit_size_ += MinimumBitsToStore(native_pc_max); - - dex_pc_bit_offset_ = total_bit_size_; - // Note: We're not encoding the dex pc if there is none. That's the case - // for an intrinsified native method, such as String.charAt(). - if (dex_pc_max != dex::kDexNoIndex) { - total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max); - } - - // We also need +1 for kNoDexRegisterMap, but since the size is strictly - // greater than any offset we might try to encode, we already implicitly have it. - dex_register_map_bit_offset_ = total_bit_size_; - total_bit_size_ += MinimumBitsToStore(dex_register_map_size); - - // We also need +1 for kNoInlineInfo, but since the inline_info_size is strictly - // greater than the offset we might try to encode, we already implicitly have it. - // If inline_info_size is zero, we can encode only kNoInlineInfo (in zero bits). - inline_info_bit_offset_ = total_bit_size_; - total_bit_size_ += MinimumBitsToStore(number_of_inline_info); - - register_mask_index_bit_offset_ = total_bit_size_; - total_bit_size_ += MinimumBitsToStore(number_of_register_masks); - - stack_mask_index_bit_offset_ = total_bit_size_; - total_bit_size_ += MinimumBitsToStore(number_of_stack_masks); - - return total_bit_size_; - } - - ALWAYS_INLINE FieldEncoding GetNativePcEncoding() const { - return FieldEncoding(kNativePcBitOffset, dex_pc_bit_offset_); - } - ALWAYS_INLINE FieldEncoding GetDexPcEncoding() const { - return FieldEncoding(dex_pc_bit_offset_, dex_register_map_bit_offset_, -1 /* min_value */); - } - ALWAYS_INLINE FieldEncoding GetDexRegisterMapEncoding() const { - return FieldEncoding(dex_register_map_bit_offset_, inline_info_bit_offset_, -1 /* min_value */); - } - ALWAYS_INLINE FieldEncoding GetInlineInfoEncoding() const { - return FieldEncoding(inline_info_bit_offset_, - register_mask_index_bit_offset_, - -1 /* min_value */); - } - ALWAYS_INLINE FieldEncoding GetRegisterMaskIndexEncoding() const { - return FieldEncoding(register_mask_index_bit_offset_, stack_mask_index_bit_offset_); - } - ALWAYS_INLINE FieldEncoding GetStackMaskIndexEncoding() const { - return FieldEncoding(stack_mask_index_bit_offset_, total_bit_size_); - } - ALWAYS_INLINE size_t BitSize() const { - return total_bit_size_; - } - - // Encode the encoding into the vector. - template - void Encode(Vector* dest) const { - static_assert(alignof(StackMapEncoding) == 1, "Should not require alignment"); - const uint8_t* ptr = reinterpret_cast(this); - dest->insert(dest->end(), ptr, ptr + sizeof(*this)); - } - - // Decode the encoding from a pointer, updates the pointer. - void Decode(const uint8_t** ptr) { - *this = *reinterpret_cast(*ptr); - *ptr += sizeof(*this); - } - - void Dump(VariableIndentationOutputStream* vios) const; - - private: - static constexpr size_t kNativePcBitOffset = 0; - uint8_t dex_pc_bit_offset_; - uint8_t dex_register_map_bit_offset_; - uint8_t inline_info_bit_offset_; - uint8_t register_mask_index_bit_offset_; - uint8_t stack_mask_index_bit_offset_; - uint8_t total_bit_size_; -}; - /** * A Stack Map holds compilation information for a specific PC necessary for: * - Mapping it to a dex PC, @@ -791,246 +654,101 @@ class StackMapEncoding { * - Knowing which registers hold objects, * - Knowing the inlining information, * - Knowing the values of dex registers. - * - * The information is of the form: - * - * [native_pc_offset, dex_pc, dex_register_map_offset, inlining_info_index, register_mask_index, - * stack_mask_index]. */ -class StackMap { +class StackMap : public BitTable<6>::Accessor { public: - StackMap() {} - explicit StackMap(BitMemoryRegion region) : region_(region) {} - - ALWAYS_INLINE bool IsValid() const { return region_.IsValid(); } - - ALWAYS_INLINE uint32_t GetDexPc(const StackMapEncoding& encoding) const { - return encoding.GetDexPcEncoding().Load(region_); - } + enum Field { + kNativePcOffset, + kDexPc, + kDexRegisterMapOffset, + kInlineInfoIndex, + kRegisterMaskIndex, + kStackMaskIndex, + kCount, + }; - ALWAYS_INLINE void SetDexPc(const StackMapEncoding& encoding, uint32_t dex_pc) { - encoding.GetDexPcEncoding().Store(region_, dex_pc); - } + StackMap() : BitTable::Accessor(nullptr, -1) {} + StackMap(const BitTable* table, uint32_t row) + : BitTable::Accessor(table, row) {} - ALWAYS_INLINE uint32_t GetNativePcOffset(const StackMapEncoding& encoding, - InstructionSet instruction_set) const { - CodeOffset offset( - CodeOffset::FromCompressedOffset(encoding.GetNativePcEncoding().Load(region_))); + ALWAYS_INLINE uint32_t GetNativePcOffset(InstructionSet instruction_set) const { + CodeOffset offset(CodeOffset::FromCompressedOffset(Get())); return offset.Uint32Value(instruction_set); } - ALWAYS_INLINE void SetNativePcCodeOffset(const StackMapEncoding& encoding, - CodeOffset native_pc_offset) { - encoding.GetNativePcEncoding().Store(region_, native_pc_offset.CompressedValue()); - } - - ALWAYS_INLINE uint32_t GetDexRegisterMapOffset(const StackMapEncoding& encoding) const { - return encoding.GetDexRegisterMapEncoding().Load(region_); - } - - ALWAYS_INLINE void SetDexRegisterMapOffset(const StackMapEncoding& encoding, uint32_t offset) { - encoding.GetDexRegisterMapEncoding().Store(region_, offset); - } - - ALWAYS_INLINE uint32_t GetInlineInfoIndex(const StackMapEncoding& encoding) const { - return encoding.GetInlineInfoEncoding().Load(region_); - } - - ALWAYS_INLINE void SetInlineInfoIndex(const StackMapEncoding& encoding, uint32_t index) { - encoding.GetInlineInfoEncoding().Store(region_, index); - } - - ALWAYS_INLINE uint32_t GetRegisterMaskIndex(const StackMapEncoding& encoding) const { - return encoding.GetRegisterMaskIndexEncoding().Load(region_); - } + uint32_t GetDexPc() const { return Get(); } - ALWAYS_INLINE void SetRegisterMaskIndex(const StackMapEncoding& encoding, uint32_t mask) { - encoding.GetRegisterMaskIndexEncoding().Store(region_, mask); - } + uint32_t GetDexRegisterMapOffset() const { return Get(); } + bool HasDexRegisterMap() const { return GetDexRegisterMapOffset() != kNoValue; } - ALWAYS_INLINE uint32_t GetStackMaskIndex(const StackMapEncoding& encoding) const { - return encoding.GetStackMaskIndexEncoding().Load(region_); - } + uint32_t GetInlineInfoIndex() const { return Get(); } + bool HasInlineInfo() const { return GetInlineInfoIndex() != kNoValue; } - ALWAYS_INLINE void SetStackMaskIndex(const StackMapEncoding& encoding, uint32_t mask) { - encoding.GetStackMaskIndexEncoding().Store(region_, mask); - } + uint32_t GetRegisterMaskIndex() const { return Get(); } - ALWAYS_INLINE bool HasDexRegisterMap(const StackMapEncoding& encoding) const { - return GetDexRegisterMapOffset(encoding) != kNoDexRegisterMap; - } - - ALWAYS_INLINE bool HasInlineInfo(const StackMapEncoding& encoding) const { - return GetInlineInfoIndex(encoding) != kNoInlineInfo; - } - - ALWAYS_INLINE bool Equals(const StackMap& other) const { - return region_.Equals(other.region_); - } + uint32_t GetStackMaskIndex() const { return Get(); } + static void DumpEncoding(const BitTable<6>& table, VariableIndentationOutputStream* vios); void Dump(VariableIndentationOutputStream* vios, const CodeInfo& code_info, - const CodeInfoEncoding& encoding, const MethodInfo& method_info, uint32_t code_offset, uint16_t number_of_dex_registers, InstructionSet instruction_set, const std::string& header_suffix = "") const; - - // Special (invalid) offset for the DexRegisterMapOffset field meaning - // that there is no Dex register map for this stack map. - static constexpr uint32_t kNoDexRegisterMap = -1; - - // Special (invalid) offset for the InlineDescriptorOffset field meaning - // that there is no inline info for this stack map. - static constexpr uint32_t kNoInlineInfo = -1; - - private: - static constexpr int kFixedSize = 0; - - BitMemoryRegion region_; - - friend class StackMapStream; -}; - -class InlineInfoEncoding { - public: - void SetFromSizes(size_t method_index_idx_max, - size_t dex_pc_max, - size_t extra_data_max, - size_t dex_register_map_size) { - total_bit_size_ = kMethodIndexBitOffset; - total_bit_size_ += MinimumBitsToStore(method_index_idx_max); - - dex_pc_bit_offset_ = dchecked_integral_cast(total_bit_size_); - // Note: We're not encoding the dex pc if there is none. That's the case - // for an intrinsified native method, such as String.charAt(). - if (dex_pc_max != dex::kDexNoIndex) { - total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max); - } - - extra_data_bit_offset_ = dchecked_integral_cast(total_bit_size_); - total_bit_size_ += MinimumBitsToStore(extra_data_max); - - // We also need +1 for kNoDexRegisterMap, but since the size is strictly - // greater than any offset we might try to encode, we already implicitly have it. - dex_register_map_bit_offset_ = dchecked_integral_cast(total_bit_size_); - total_bit_size_ += MinimumBitsToStore(dex_register_map_size); - } - - ALWAYS_INLINE FieldEncoding GetMethodIndexIdxEncoding() const { - return FieldEncoding(kMethodIndexBitOffset, dex_pc_bit_offset_); - } - ALWAYS_INLINE FieldEncoding GetDexPcEncoding() const { - return FieldEncoding(dex_pc_bit_offset_, extra_data_bit_offset_, -1 /* min_value */); - } - ALWAYS_INLINE FieldEncoding GetExtraDataEncoding() const { - return FieldEncoding(extra_data_bit_offset_, dex_register_map_bit_offset_); - } - ALWAYS_INLINE FieldEncoding GetDexRegisterMapEncoding() const { - return FieldEncoding(dex_register_map_bit_offset_, total_bit_size_, -1 /* min_value */); - } - ALWAYS_INLINE size_t BitSize() const { - return total_bit_size_; - } - - void Dump(VariableIndentationOutputStream* vios) const; - - // Encode the encoding into the vector. - template - void Encode(Vector* dest) const { - static_assert(alignof(InlineInfoEncoding) == 1, "Should not require alignment"); - const uint8_t* ptr = reinterpret_cast(this); - dest->insert(dest->end(), ptr, ptr + sizeof(*this)); - } - - // Decode the encoding from a pointer, updates the pointer. - void Decode(const uint8_t** ptr) { - *this = *reinterpret_cast(*ptr); - *ptr += sizeof(*this); - } - - private: - static constexpr uint8_t kIsLastBitOffset = 0; - static constexpr uint8_t kMethodIndexBitOffset = 1; - uint8_t dex_pc_bit_offset_; - uint8_t extra_data_bit_offset_; - uint8_t dex_register_map_bit_offset_; - uint8_t total_bit_size_; }; /** - * Inline information for a specific PC. The information is of the form: - * - * [is_last, - * method_index (or ArtMethod high bits), - * dex_pc, - * extra_data (ArtMethod low bits or 1), - * dex_register_map_offset]+. + * Inline information for a specific PC. + * The row referenced from the StackMap holds information at depth 0. + * Following rows hold information for further depths. */ -class InlineInfo { +class InlineInfo : public BitTable<5>::Accessor { public: - explicit InlineInfo(BitMemoryRegion region) : region_(region) {} - - ALWAYS_INLINE uint32_t GetDepth(const InlineInfoEncoding& encoding) const { - size_t depth = 0; - while (!GetRegionAtDepth(encoding, depth++).LoadBit(0)) { } // Check is_last bit. - return depth; - } - - ALWAYS_INLINE void SetDepth(const InlineInfoEncoding& encoding, uint32_t depth) { - DCHECK_GT(depth, 0u); - for (size_t d = 0; d < depth; ++d) { - GetRegionAtDepth(encoding, d).StoreBit(0, d == depth - 1); // Set is_last bit. - } - } + enum Field { + kIsLast, // Determines if there are further rows for further depths. + kMethodIndexIdx, // Method index or ArtMethod high bits. + kDexPc, + kExtraData, // ArtMethod low bits or 1. + kDexRegisterMapOffset, + kCount, + }; + static constexpr uint32_t kLast = -1; + static constexpr uint32_t kMore = 0; - ALWAYS_INLINE uint32_t GetMethodIndexIdxAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth) const { - DCHECK(!EncodesArtMethodAtDepth(encoding, depth)); - return encoding.GetMethodIndexIdxEncoding().Load(GetRegionAtDepth(encoding, depth)); - } + InlineInfo(const BitTable* table, uint32_t row) + : BitTable::Accessor(table, row) {} - ALWAYS_INLINE void SetMethodIndexIdxAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth, - uint32_t index) { - encoding.GetMethodIndexIdxEncoding().Store(GetRegionAtDepth(encoding, depth), index); + ALWAYS_INLINE InlineInfo AtDepth(uint32_t depth) const { + return InlineInfo(table_, this->row_ + depth); } - - ALWAYS_INLINE uint32_t GetMethodIndexAtDepth(const InlineInfoEncoding& encoding, - const MethodInfo& method_info, - uint32_t depth) const { - return method_info.GetMethodIndex(GetMethodIndexIdxAtDepth(encoding, depth)); + uint32_t GetDepth() const { + size_t depth = 0; + while (AtDepth(depth++).Get() == kMore) { } + return depth; } - ALWAYS_INLINE uint32_t GetDexPcAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth) const { - return encoding.GetDexPcEncoding().Load(GetRegionAtDepth(encoding, depth)); + uint32_t GetMethodIndexIdxAtDepth(uint32_t depth) const { + DCHECK(!EncodesArtMethodAtDepth(depth)); + return AtDepth(depth).Get(); } - ALWAYS_INLINE void SetDexPcAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth, - uint32_t dex_pc) { - encoding.GetDexPcEncoding().Store(GetRegionAtDepth(encoding, depth), dex_pc); + uint32_t GetMethodIndexAtDepth(const MethodInfo& method_info, uint32_t depth) const { + return method_info.GetMethodIndex(GetMethodIndexIdxAtDepth(depth)); } - ALWAYS_INLINE bool EncodesArtMethodAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth) const { - return (encoding.GetExtraDataEncoding().Load(GetRegionAtDepth(encoding, depth)) & 1) == 0; + uint32_t GetDexPcAtDepth(uint32_t depth) const { + return AtDepth(depth).Get(); } - ALWAYS_INLINE void SetExtraDataAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth, - uint32_t extra_data) { - encoding.GetExtraDataEncoding().Store(GetRegionAtDepth(encoding, depth), extra_data); + bool EncodesArtMethodAtDepth(uint32_t depth) const { + return (AtDepth(depth).Get() & 1) == 0; } - ALWAYS_INLINE ArtMethod* GetArtMethodAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth) const { - uint32_t low_bits = encoding.GetExtraDataEncoding().Load(GetRegionAtDepth(encoding, depth)); - uint32_t high_bits = encoding.GetMethodIndexIdxEncoding().Load( - GetRegionAtDepth(encoding, depth)); + ArtMethod* GetArtMethodAtDepth(uint32_t depth) const { + uint32_t low_bits = AtDepth(depth).Get(); + uint32_t high_bits = AtDepth(depth).Get(); if (high_bits == 0) { return reinterpret_cast(low_bits); } else { @@ -1040,411 +758,132 @@ class InlineInfo { } } - ALWAYS_INLINE uint32_t GetDexRegisterMapOffsetAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth) const { - return encoding.GetDexRegisterMapEncoding().Load(GetRegionAtDepth(encoding, depth)); + uint32_t GetDexRegisterMapOffsetAtDepth(uint32_t depth) const { + return AtDepth(depth).Get(); } - ALWAYS_INLINE void SetDexRegisterMapOffsetAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth, - uint32_t offset) { - encoding.GetDexRegisterMapEncoding().Store(GetRegionAtDepth(encoding, depth), offset); - } - - ALWAYS_INLINE bool HasDexRegisterMapAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth) const { - return GetDexRegisterMapOffsetAtDepth(encoding, depth) != StackMap::kNoDexRegisterMap; + bool HasDexRegisterMapAtDepth(uint32_t depth) const { + return GetDexRegisterMapOffsetAtDepth(depth) != StackMap::kNoValue; } + static void DumpEncoding(const BitTable<5>& table, VariableIndentationOutputStream* vios); void Dump(VariableIndentationOutputStream* vios, const CodeInfo& info, const MethodInfo& method_info, uint16_t* number_of_dex_registers) const; - - private: - ALWAYS_INLINE BitMemoryRegion GetRegionAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth) const { - size_t entry_size = encoding.BitSize(); - DCHECK_GT(entry_size, 0u); - return region_.Subregion(depth * entry_size, entry_size); - } - - BitMemoryRegion region_; -}; - -// Bit sized region encoding, may be more than 255 bits. -class BitRegionEncoding { - public: - uint32_t num_bits = 0; - - ALWAYS_INLINE size_t BitSize() const { - return num_bits; - } - - template - void Encode(Vector* dest) const { - EncodeUnsignedLeb128(dest, num_bits); // Use leb in case num_bits is greater than 255. - } - - void Decode(const uint8_t** ptr) { - num_bits = DecodeUnsignedLeb128(ptr); - } -}; - -// A table of bit sized encodings. -template -struct BitEncodingTable { - static constexpr size_t kInvalidOffset = static_cast(-1); - // How the encoding is laid out (serialized). - Encoding encoding; - - // Number of entries in the table (serialized). - size_t num_entries; - - // Bit offset for the base of the table (computed). - size_t bit_offset = kInvalidOffset; - - template - void Encode(Vector* dest) const { - EncodeUnsignedLeb128(dest, num_entries); - encoding.Encode(dest); - } - - ALWAYS_INLINE void Decode(const uint8_t** ptr) { - num_entries = DecodeUnsignedLeb128(ptr); - encoding.Decode(ptr); - } - - // Set the bit offset in the table and adds the space used by the table to offset. - void UpdateBitOffset(size_t* offset) { - DCHECK(offset != nullptr); - bit_offset = *offset; - *offset += encoding.BitSize() * num_entries; - } - - // Return the bit region for the map at index i. - ALWAYS_INLINE BitMemoryRegion BitRegion(MemoryRegion region, size_t index) const { - DCHECK_NE(bit_offset, kInvalidOffset) << "Invalid table offset"; - DCHECK_LT(index, num_entries); - const size_t map_size = encoding.BitSize(); - return BitMemoryRegion(region, bit_offset + index * map_size, map_size); - } -}; - -// A byte sized table of possible variable sized encodings. -struct ByteSizedTable { - static constexpr size_t kInvalidOffset = static_cast(-1); - - // Number of entries in the table (serialized). - size_t num_entries = 0; - - // Number of bytes of the table (serialized). - size_t num_bytes; - - // Bit offset for the base of the table (computed). - size_t byte_offset = kInvalidOffset; - - template - void Encode(Vector* dest) const { - EncodeUnsignedLeb128(dest, num_entries); - EncodeUnsignedLeb128(dest, num_bytes); - } - - ALWAYS_INLINE void Decode(const uint8_t** ptr) { - num_entries = DecodeUnsignedLeb128(ptr); - num_bytes = DecodeUnsignedLeb128(ptr); - } - - // Set the bit offset of the table. Adds the total bit size of the table to offset. - void UpdateBitOffset(size_t* offset) { - DCHECK(offset != nullptr); - DCHECK_ALIGNED(*offset, kBitsPerByte); - byte_offset = *offset / kBitsPerByte; - *offset += num_bytes * kBitsPerByte; - } }; -// Format is [native pc, invoke type, method index]. -class InvokeInfoEncoding { +class InvokeInfo : public BitTable<3>::Accessor { public: - void SetFromSizes(size_t native_pc_max, - size_t invoke_type_max, - size_t method_index_max) { - total_bit_size_ = 0; - DCHECK_EQ(kNativePcBitOffset, total_bit_size_); - total_bit_size_ += MinimumBitsToStore(native_pc_max); - invoke_type_bit_offset_ = total_bit_size_; - total_bit_size_ += MinimumBitsToStore(invoke_type_max); - method_index_bit_offset_ = total_bit_size_; - total_bit_size_ += MinimumBitsToStore(method_index_max); - } - - ALWAYS_INLINE FieldEncoding GetNativePcEncoding() const { - return FieldEncoding(kNativePcBitOffset, invoke_type_bit_offset_); - } - - ALWAYS_INLINE FieldEncoding GetInvokeTypeEncoding() const { - return FieldEncoding(invoke_type_bit_offset_, method_index_bit_offset_); - } - - ALWAYS_INLINE FieldEncoding GetMethodIndexEncoding() const { - return FieldEncoding(method_index_bit_offset_, total_bit_size_); - } - - ALWAYS_INLINE size_t BitSize() const { - return total_bit_size_; - } - - template - void Encode(Vector* dest) const { - static_assert(alignof(InvokeInfoEncoding) == 1, "Should not require alignment"); - const uint8_t* ptr = reinterpret_cast(this); - dest->insert(dest->end(), ptr, ptr + sizeof(*this)); - } - - void Decode(const uint8_t** ptr) { - *this = *reinterpret_cast(*ptr); - *ptr += sizeof(*this); - } + enum Field { + kNativePcOffset, + kInvokeType, + kMethodIndexIdx, + kCount, + }; - private: - static constexpr uint8_t kNativePcBitOffset = 0; - uint8_t invoke_type_bit_offset_; - uint8_t method_index_bit_offset_; - uint8_t total_bit_size_; -}; + InvokeInfo(const BitTable* table, uint32_t row) + : BitTable::Accessor(table, row) {} -class InvokeInfo { - public: - explicit InvokeInfo(BitMemoryRegion region) : region_(region) {} - - ALWAYS_INLINE uint32_t GetNativePcOffset(const InvokeInfoEncoding& encoding, - InstructionSet instruction_set) const { - CodeOffset offset( - CodeOffset::FromCompressedOffset(encoding.GetNativePcEncoding().Load(region_))); + ALWAYS_INLINE uint32_t GetNativePcOffset(InstructionSet instruction_set) const { + CodeOffset offset(CodeOffset::FromCompressedOffset(Get())); return offset.Uint32Value(instruction_set); } - ALWAYS_INLINE void SetNativePcCodeOffset(const InvokeInfoEncoding& encoding, - CodeOffset native_pc_offset) { - encoding.GetNativePcEncoding().Store(region_, native_pc_offset.CompressedValue()); - } - - ALWAYS_INLINE uint32_t GetInvokeType(const InvokeInfoEncoding& encoding) const { - return encoding.GetInvokeTypeEncoding().Load(region_); - } - - ALWAYS_INLINE void SetInvokeType(const InvokeInfoEncoding& encoding, uint32_t invoke_type) { - encoding.GetInvokeTypeEncoding().Store(region_, invoke_type); - } - - ALWAYS_INLINE uint32_t GetMethodIndexIdx(const InvokeInfoEncoding& encoding) const { - return encoding.GetMethodIndexEncoding().Load(region_); - } - - ALWAYS_INLINE void SetMethodIndexIdx(const InvokeInfoEncoding& encoding, - uint32_t method_index_idx) { - encoding.GetMethodIndexEncoding().Store(region_, method_index_idx); - } - - ALWAYS_INLINE uint32_t GetMethodIndex(const InvokeInfoEncoding& encoding, - MethodInfo method_info) const { - return method_info.GetMethodIndex(GetMethodIndexIdx(encoding)); - } - - bool IsValid() const { return region_.IsValid(); } - - private: - BitMemoryRegion region_; -}; - -// Most of the fields are encoded as ULEB128 to save space. -struct CodeInfoEncoding { - using SizeType = uint32_t; - - static constexpr SizeType kInvalidSize = std::numeric_limits::max(); - - // Byte sized tables go first to avoid unnecessary alignment bits. - ByteSizedTable dex_register_map; - ByteSizedTable location_catalog; - BitEncodingTable stack_map; - BitEncodingTable register_mask; - BitEncodingTable stack_mask; - BitEncodingTable invoke_info; - BitEncodingTable inline_info; - - CodeInfoEncoding() {} - - explicit CodeInfoEncoding(const void* data) { - const uint8_t* ptr = reinterpret_cast(data); - dex_register_map.Decode(&ptr); - location_catalog.Decode(&ptr); - stack_map.Decode(&ptr); - register_mask.Decode(&ptr); - stack_mask.Decode(&ptr); - invoke_info.Decode(&ptr); - if (stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) { - inline_info.Decode(&ptr); - } else { - inline_info = BitEncodingTable(); - } - cache_header_size = - dchecked_integral_cast(ptr - reinterpret_cast(data)); - ComputeTableOffsets(); - } - - // Compress is not const since it calculates cache_header_size. This is used by PrepareForFillIn. - template - void Compress(Vector* dest) { - dex_register_map.Encode(dest); - location_catalog.Encode(dest); - stack_map.Encode(dest); - register_mask.Encode(dest); - stack_mask.Encode(dest); - invoke_info.Encode(dest); - if (stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) { - inline_info.Encode(dest); - } - cache_header_size = dest->size(); - } - - ALWAYS_INLINE void ComputeTableOffsets() { - // Skip the header. - size_t bit_offset = HeaderSize() * kBitsPerByte; - // The byte tables must be aligned so they must go first. - dex_register_map.UpdateBitOffset(&bit_offset); - location_catalog.UpdateBitOffset(&bit_offset); - // Other tables don't require alignment. - stack_map.UpdateBitOffset(&bit_offset); - register_mask.UpdateBitOffset(&bit_offset); - stack_mask.UpdateBitOffset(&bit_offset); - invoke_info.UpdateBitOffset(&bit_offset); - inline_info.UpdateBitOffset(&bit_offset); - cache_non_header_size = RoundUp(bit_offset, kBitsPerByte) / kBitsPerByte - HeaderSize(); - } + uint32_t GetInvokeType() const { return Get(); } - ALWAYS_INLINE size_t HeaderSize() const { - DCHECK_NE(cache_header_size, kInvalidSize) << "Uninitialized"; - return cache_header_size; - } + uint32_t GetMethodIndexIdx() const { return Get(); } - ALWAYS_INLINE size_t NonHeaderSize() const { - DCHECK_NE(cache_non_header_size, kInvalidSize) << "Uninitialized"; - return cache_non_header_size; + uint32_t GetMethodIndex(MethodInfo method_info) const { + return method_info.GetMethodIndex(GetMethodIndexIdx()); } - - private: - // Computed fields (not serialized). - // Header size in bytes, cached to avoid needing to re-decoding the encoding in HeaderSize. - SizeType cache_header_size = kInvalidSize; - // Non header size in bytes, cached to avoid needing to re-decoding the encoding in NonHeaderSize. - SizeType cache_non_header_size = kInvalidSize; }; /** * Wrapper around all compiler information collected for a method. * The information is of the form: * - * [CodeInfoEncoding, DexRegisterMap+, DexLocationCatalog+, StackMap+, RegisterMask+, StackMask+, - * InlineInfo*] - * - * where CodeInfoEncoding is of the form: + * [BitTable
, BitTable, BitTable, BitTable, + * BitTable, BitTable, DexRegisterMap, DexLocationCatalog] * - * [ByteSizedTable(dex_register_map), ByteSizedTable(location_catalog), - * BitEncodingTable, BitEncodingTable, - * BitEncodingTable, BitEncodingTable] */ class CodeInfo { public: - explicit CodeInfo(MemoryRegion region) : region_(region) { - } - explicit CodeInfo(const void* data) { - CodeInfoEncoding encoding = CodeInfoEncoding(data); - region_ = MemoryRegion(const_cast(data), - encoding.HeaderSize() + encoding.NonHeaderSize()); + Decode(reinterpret_cast(data)); } - CodeInfoEncoding ExtractEncoding() const { - CodeInfoEncoding encoding(region_.begin()); - AssertValidStackMap(encoding); - return encoding; + explicit CodeInfo(MemoryRegion region) : CodeInfo(region.begin()) { + DCHECK_EQ(size_, region.size()); } - bool HasInlineInfo(const CodeInfoEncoding& encoding) const { - return encoding.stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0; + explicit CodeInfo(const OatQuickMethodHeader* header) + : CodeInfo(header->GetOptimizedCodeInfoPtr()) { } - DexRegisterLocationCatalog GetDexRegisterLocationCatalog(const CodeInfoEncoding& encoding) const { - return DexRegisterLocationCatalog(region_.Subregion(encoding.location_catalog.byte_offset, - encoding.location_catalog.num_bytes)); + size_t Size() const { + return size_; } - ALWAYS_INLINE size_t GetNumberOfStackMaskBits(const CodeInfoEncoding& encoding) const { - return encoding.stack_mask.encoding.BitSize(); + bool HasInlineInfo() const { + return stack_maps_.NumColumnBits(StackMap::kInlineInfoIndex) != 0; } - ALWAYS_INLINE StackMap GetStackMapAt(size_t index, const CodeInfoEncoding& encoding) const { - return StackMap(encoding.stack_map.BitRegion(region_, index)); + DexRegisterLocationCatalog GetDexRegisterLocationCatalog() const { + return DexRegisterLocationCatalog(location_catalog_); } - BitMemoryRegion GetStackMask(size_t index, const CodeInfoEncoding& encoding) const { - return encoding.stack_mask.BitRegion(region_, index); + ALWAYS_INLINE size_t GetNumberOfStackMaskBits() const { + return stack_mask_bits_; } - BitMemoryRegion GetStackMaskOf(const CodeInfoEncoding& encoding, - const StackMap& stack_map) const { - return GetStackMask(stack_map.GetStackMaskIndex(encoding.stack_map.encoding), encoding); + ALWAYS_INLINE StackMap GetStackMapAt(size_t index) const { + return StackMap(&stack_maps_, index); } - BitMemoryRegion GetRegisterMask(size_t index, const CodeInfoEncoding& encoding) const { - return encoding.register_mask.BitRegion(region_, index); + BitMemoryRegion GetStackMask(size_t index) const { + return stack_masks_.Subregion(index * stack_mask_bits_, stack_mask_bits_); } - uint32_t GetRegisterMaskOf(const CodeInfoEncoding& encoding, const StackMap& stack_map) const { - size_t index = stack_map.GetRegisterMaskIndex(encoding.stack_map.encoding); - return GetRegisterMask(index, encoding).LoadBits(0u, encoding.register_mask.encoding.BitSize()); + BitMemoryRegion GetStackMaskOf(const StackMap& stack_map) const { + return GetStackMask(stack_map.GetStackMaskIndex()); } - uint32_t GetNumberOfLocationCatalogEntries(const CodeInfoEncoding& encoding) const { - return encoding.location_catalog.num_entries; + uint32_t GetRegisterMaskOf(const StackMap& stack_map) const { + return register_masks_.Get(stack_map.GetRegisterMaskIndex()); } - uint32_t GetDexRegisterLocationCatalogSize(const CodeInfoEncoding& encoding) const { - return encoding.location_catalog.num_bytes; + uint32_t GetNumberOfLocationCatalogEntries() const { + return location_catalog_entries_; } - uint32_t GetNumberOfStackMaps(const CodeInfoEncoding& encoding) const { - return encoding.stack_map.num_entries; + uint32_t GetDexRegisterLocationCatalogSize() const { + return location_catalog_.size(); } - // Get the size of all the stack maps of this CodeInfo object, in bits. Not byte aligned. - ALWAYS_INLINE size_t GetStackMapsSizeInBits(const CodeInfoEncoding& encoding) const { - return encoding.stack_map.encoding.BitSize() * GetNumberOfStackMaps(encoding); + uint32_t GetNumberOfStackMaps() const { + return stack_maps_.NumRows(); } - InvokeInfo GetInvokeInfo(const CodeInfoEncoding& encoding, size_t index) const { - return InvokeInfo(encoding.invoke_info.BitRegion(region_, index)); + InvokeInfo GetInvokeInfo(size_t index) const { + return InvokeInfo(&invoke_infos_, index); } DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, - const CodeInfoEncoding& encoding, size_t number_of_dex_registers) const { - if (!stack_map.HasDexRegisterMap(encoding.stack_map.encoding)) { + if (!stack_map.HasDexRegisterMap()) { return DexRegisterMap(); } - const uint32_t offset = encoding.dex_register_map.byte_offset + - stack_map.GetDexRegisterMapOffset(encoding.stack_map.encoding); - size_t size = ComputeDexRegisterMapSizeOf(encoding, offset, number_of_dex_registers); - return DexRegisterMap(region_.Subregion(offset, size)); + const uint32_t offset = stack_map.GetDexRegisterMapOffset(); + size_t size = ComputeDexRegisterMapSizeOf(offset, number_of_dex_registers); + return DexRegisterMap(dex_register_maps_.Subregion(offset, size)); } - size_t GetDexRegisterMapsSize(const CodeInfoEncoding& encoding, - uint32_t number_of_dex_registers) const { + size_t GetDexRegisterMapsSize(uint32_t number_of_dex_registers) const { size_t total = 0; - for (size_t i = 0, e = GetNumberOfStackMaps(encoding); i < e; ++i) { - StackMap stack_map = GetStackMapAt(i, encoding); - DexRegisterMap map(GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers)); + for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) { + StackMap stack_map = GetStackMapAt(i); + DexRegisterMap map(GetDexRegisterMapOf(stack_map, number_of_dex_registers)); total += map.Size(); } return total; @@ -1453,38 +892,30 @@ class CodeInfo { // Return the `DexRegisterMap` pointed by `inline_info` at depth `depth`. DexRegisterMap GetDexRegisterMapAtDepth(uint8_t depth, InlineInfo inline_info, - const CodeInfoEncoding& encoding, uint32_t number_of_dex_registers) const { - if (!inline_info.HasDexRegisterMapAtDepth(encoding.inline_info.encoding, depth)) { + if (!inline_info.HasDexRegisterMapAtDepth(depth)) { return DexRegisterMap(); } else { - uint32_t offset = encoding.dex_register_map.byte_offset + - inline_info.GetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding, depth); - size_t size = ComputeDexRegisterMapSizeOf(encoding, offset, number_of_dex_registers); - return DexRegisterMap(region_.Subregion(offset, size)); + uint32_t offset = inline_info.GetDexRegisterMapOffsetAtDepth(depth); + size_t size = ComputeDexRegisterMapSizeOf(offset, number_of_dex_registers); + return DexRegisterMap(dex_register_maps_.Subregion(offset, size)); } } - InlineInfo GetInlineInfo(size_t index, const CodeInfoEncoding& encoding) const { - // Since we do not know the depth, we just return the whole remaining map. The caller may - // access the inline info for arbitrary depths. To return the precise inline info we would need - // to count the depth before returning. - // TODO: Clean this up. - const size_t bit_offset = encoding.inline_info.bit_offset + - index * encoding.inline_info.encoding.BitSize(); - return InlineInfo(BitMemoryRegion(region_, bit_offset, region_.size_in_bits() - bit_offset)); + InlineInfo GetInlineInfo(size_t index) const { + return InlineInfo(&inline_infos_, index); } - InlineInfo GetInlineInfoOf(StackMap stack_map, const CodeInfoEncoding& encoding) const { - DCHECK(stack_map.HasInlineInfo(encoding.stack_map.encoding)); - uint32_t index = stack_map.GetInlineInfoIndex(encoding.stack_map.encoding); - return GetInlineInfo(index, encoding); + InlineInfo GetInlineInfoOf(StackMap stack_map) const { + DCHECK(stack_map.HasInlineInfo()); + uint32_t index = stack_map.GetInlineInfoIndex(); + return GetInlineInfo(index); } - StackMap GetStackMapForDexPc(uint32_t dex_pc, const CodeInfoEncoding& encoding) const { - for (size_t i = 0, e = GetNumberOfStackMaps(encoding); i < e; ++i) { - StackMap stack_map = GetStackMapAt(i, encoding); - if (stack_map.GetDexPc(encoding.stack_map.encoding) == dex_pc) { + StackMap GetStackMapForDexPc(uint32_t dex_pc) const { + for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) { + StackMap stack_map = GetStackMapAt(i); + if (stack_map.GetDexPc() == dex_pc) { return stack_map; } } @@ -1493,40 +924,39 @@ class CodeInfo { // Searches the stack map list backwards because catch stack maps are stored // at the end. - StackMap GetCatchStackMapForDexPc(uint32_t dex_pc, const CodeInfoEncoding& encoding) const { - for (size_t i = GetNumberOfStackMaps(encoding); i > 0; --i) { - StackMap stack_map = GetStackMapAt(i - 1, encoding); - if (stack_map.GetDexPc(encoding.stack_map.encoding) == dex_pc) { + StackMap GetCatchStackMapForDexPc(uint32_t dex_pc) const { + for (size_t i = GetNumberOfStackMaps(); i > 0; --i) { + StackMap stack_map = GetStackMapAt(i - 1); + if (stack_map.GetDexPc() == dex_pc) { return stack_map; } } return StackMap(); } - StackMap GetOsrStackMapForDexPc(uint32_t dex_pc, const CodeInfoEncoding& encoding) const { - size_t e = GetNumberOfStackMaps(encoding); + StackMap GetOsrStackMapForDexPc(uint32_t dex_pc) const { + size_t e = GetNumberOfStackMaps(); if (e == 0) { // There cannot be OSR stack map if there is no stack map. return StackMap(); } // Walk over all stack maps. If two consecutive stack maps are identical, then we // have found a stack map suitable for OSR. - const StackMapEncoding& stack_map_encoding = encoding.stack_map.encoding; for (size_t i = 0; i < e - 1; ++i) { - StackMap stack_map = GetStackMapAt(i, encoding); - if (stack_map.GetDexPc(stack_map_encoding) == dex_pc) { - StackMap other = GetStackMapAt(i + 1, encoding); - if (other.GetDexPc(stack_map_encoding) == dex_pc && - other.GetNativePcOffset(stack_map_encoding, kRuntimeISA) == - stack_map.GetNativePcOffset(stack_map_encoding, kRuntimeISA)) { - DCHECK_EQ(other.GetDexRegisterMapOffset(stack_map_encoding), - stack_map.GetDexRegisterMapOffset(stack_map_encoding)); - DCHECK(!stack_map.HasInlineInfo(stack_map_encoding)); + StackMap stack_map = GetStackMapAt(i); + if (stack_map.GetDexPc() == dex_pc) { + StackMap other = GetStackMapAt(i + 1); + if (other.GetDexPc() == dex_pc && + other.GetNativePcOffset(kRuntimeISA) == + stack_map.GetNativePcOffset(kRuntimeISA)) { + DCHECK_EQ(other.GetDexRegisterMapOffset(), + stack_map.GetDexRegisterMapOffset()); + DCHECK(!stack_map.HasInlineInfo()); if (i < e - 2) { // Make sure there are not three identical stack maps following each other. DCHECK_NE( - stack_map.GetNativePcOffset(stack_map_encoding, kRuntimeISA), - GetStackMapAt(i + 2, encoding).GetNativePcOffset(stack_map_encoding, kRuntimeISA)); + stack_map.GetNativePcOffset(kRuntimeISA), + GetStackMapAt(i + 2).GetNativePcOffset(kRuntimeISA)); } return stack_map; } @@ -1535,30 +965,27 @@ class CodeInfo { return StackMap(); } - StackMap GetStackMapForNativePcOffset(uint32_t native_pc_offset, - const CodeInfoEncoding& encoding) const { + StackMap GetStackMapForNativePcOffset(uint32_t native_pc_offset) const { // TODO: Safepoint stack maps are sorted by native_pc_offset but catch stack // maps are not. If we knew that the method does not have try/catch, // we could do binary search. - for (size_t i = 0, e = GetNumberOfStackMaps(encoding); i < e; ++i) { - StackMap stack_map = GetStackMapAt(i, encoding); - if (stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA) == - native_pc_offset) { + for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) { + StackMap stack_map = GetStackMapAt(i); + if (stack_map.GetNativePcOffset(kRuntimeISA) == native_pc_offset) { return stack_map; } } return StackMap(); } - InvokeInfo GetInvokeInfoForNativePcOffset(uint32_t native_pc_offset, - const CodeInfoEncoding& encoding) { - for (size_t index = 0; index < encoding.invoke_info.num_entries; index++) { - InvokeInfo item = GetInvokeInfo(encoding, index); - if (item.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA) == native_pc_offset) { + InvokeInfo GetInvokeInfoForNativePcOffset(uint32_t native_pc_offset) { + for (size_t index = 0; index < invoke_infos_.NumRows(); index++) { + InvokeInfo item = GetInvokeInfo(index); + if (item.GetNativePcOffset(kRuntimeISA) == native_pc_offset) { return item; } } - return InvokeInfo(BitMemoryRegion()); + return InvokeInfo(&invoke_infos_, -1); } // Dump this CodeInfo object on `os`. `code_offset` is the (absolute) @@ -1573,23 +1000,10 @@ class CodeInfo { InstructionSet instruction_set, const MethodInfo& method_info) const; - // Check that the code info has valid stack map and abort if it does not. - void AssertValidStackMap(const CodeInfoEncoding& encoding) const { - if (region_.size() != 0 && region_.size_in_bits() < GetStackMapsSizeInBits(encoding)) { - LOG(FATAL) << region_.size() << "\n" - << encoding.HeaderSize() << "\n" - << encoding.NonHeaderSize() << "\n" - << encoding.location_catalog.num_entries << "\n" - << encoding.stack_map.num_entries << "\n" - << encoding.stack_map.encoding.BitSize(); - } - } - private: // Compute the size of the Dex register map associated to the stack map at // `dex_register_map_offset_in_code_info`. - size_t ComputeDexRegisterMapSizeOf(const CodeInfoEncoding& encoding, - uint32_t dex_register_map_offset_in_code_info, + size_t ComputeDexRegisterMapSizeOf(uint32_t dex_register_map_offset, uint16_t number_of_dex_registers) const { // Offset where the actual mapping data starts within art::DexRegisterMap. size_t location_mapping_data_offset_in_dex_register_map = @@ -1597,12 +1011,12 @@ class CodeInfo { // Create a temporary art::DexRegisterMap to be able to call // art::DexRegisterMap::GetNumberOfLiveDexRegisters and DexRegisterMap dex_register_map_without_locations( - MemoryRegion(region_.Subregion(dex_register_map_offset_in_code_info, - location_mapping_data_offset_in_dex_register_map))); + MemoryRegion(dex_register_maps_.Subregion(dex_register_map_offset, + location_mapping_data_offset_in_dex_register_map))); size_t number_of_live_dex_registers = dex_register_map_without_locations.GetNumberOfLiveDexRegisters(number_of_dex_registers); size_t location_mapping_data_size_in_bits = - DexRegisterMap::SingleEntrySizeInBits(GetNumberOfLocationCatalogEntries(encoding)) + DexRegisterMap::SingleEntrySizeInBits(GetNumberOfLocationCatalogEntries()) * number_of_live_dex_registers; size_t location_mapping_data_size_in_bytes = RoundUp(location_mapping_data_size_in_bits, kBitsPerByte) / kBitsPerByte; @@ -1611,37 +1025,42 @@ class CodeInfo { return dex_register_map_size; } - // Compute the size of a Dex register location catalog starting at offset `origin` - // in `region_` and containing `number_of_dex_locations` entries. - size_t ComputeDexRegisterLocationCatalogSize(uint32_t origin, - uint32_t number_of_dex_locations) const { - // TODO: Ideally, we would like to use art::DexRegisterLocationCatalog::Size or - // art::DexRegisterLocationCatalog::FindLocationOffset, but the - // DexRegisterLocationCatalog is not yet built. Try to factor common code. - size_t offset = origin + DexRegisterLocationCatalog::kFixedSize; - - // Skip the first `number_of_dex_locations - 1` entries. - for (uint16_t i = 0; i < number_of_dex_locations; ++i) { - // Read the first next byte and inspect its first 3 bits to decide - // whether it is a short or a large location. - DexRegisterLocationCatalog::ShortLocation first_byte = - region_.LoadUnaligned(offset); - DexRegisterLocation::Kind kind = - DexRegisterLocationCatalog::ExtractKindFromShortLocation(first_byte); - if (DexRegisterLocation::IsShortLocationKind(kind)) { - // Short location. Skip the current byte. - offset += DexRegisterLocationCatalog::SingleShortEntrySize(); - } else { - // Large location. Skip the 5 next bytes. - offset += DexRegisterLocationCatalog::SingleLargeEntrySize(); - } - } - size_t size = offset - origin; - return size; - } - - MemoryRegion region_; - friend class StackMapStream; + MemoryRegion DecodeMemoryRegion(MemoryRegion& region, size_t* bit_offset) { + size_t length = DecodeVarintBits(BitMemoryRegion(region), bit_offset); + size_t offset = BitsToBytesRoundUp(*bit_offset);; + *bit_offset = (offset + length) * kBitsPerByte; + return region.Subregion(offset, length); + } + + void Decode(const uint8_t* data) { + size_t non_header_size = DecodeUnsignedLeb128(&data); + MemoryRegion region(const_cast(data), non_header_size); + BitMemoryRegion bit_region(region); + size_t bit_offset = 0; + size_ = UnsignedLeb128Size(non_header_size) + non_header_size; + dex_register_maps_ = DecodeMemoryRegion(region, &bit_offset); + location_catalog_entries_ = DecodeVarintBits(bit_region, &bit_offset); + location_catalog_ = DecodeMemoryRegion(region, &bit_offset); + stack_maps_.Decode(bit_region, &bit_offset); + invoke_infos_.Decode(bit_region, &bit_offset); + inline_infos_.Decode(bit_region, &bit_offset); + register_masks_.Decode(bit_region, &bit_offset); + stack_mask_bits_ = DecodeVarintBits(bit_region, &bit_offset); + stack_masks_ = bit_region.Subregion(bit_offset, non_header_size * kBitsPerByte - bit_offset); + } + + size_t size_; + MemoryRegion dex_register_maps_; + uint32_t location_catalog_entries_; + MemoryRegion location_catalog_; + BitTable stack_maps_; + BitTable invoke_infos_; + BitTable inline_infos_; + BitTable<1> register_masks_; + uint32_t stack_mask_bits_ = 0; + BitMemoryRegion stack_masks_; + + friend class OatDumper; }; #undef ELEMENT_BYTE_OFFSET_AFTER diff --git a/runtime/thread.cc b/runtime/thread.cc index eada24d257..81ed722fcc 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -3559,16 +3559,15 @@ class ReferenceMapVisitor : public StackVisitor { StackReference* vreg_base = reinterpret_cast*>( reinterpret_cast(cur_quick_frame)); uintptr_t native_pc_offset = method_header->NativeQuickPcOffset(GetCurrentQuickFramePc()); - CodeInfo code_info = method_header->GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = code_info.ExtractEncoding(); - StackMap map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); + CodeInfo code_info(method_header); + StackMap map = code_info.GetStackMapForNativePcOffset(native_pc_offset); DCHECK(map.IsValid()); - T vreg_info(m, code_info, encoding, map, visitor_); + T vreg_info(m, code_info, map, visitor_); // Visit stack entries that hold pointers. - const size_t number_of_bits = code_info.GetNumberOfStackMaskBits(encoding); - BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, map); + const size_t number_of_bits = code_info.GetNumberOfStackMaskBits(); + BitMemoryRegion stack_mask = code_info.GetStackMaskOf(map); for (size_t i = 0; i < number_of_bits; ++i) { if (stack_mask.LoadBit(i)) { StackReference* ref_addr = vreg_base + i; @@ -3583,7 +3582,7 @@ class ReferenceMapVisitor : public StackVisitor { } } // Visit callee-save registers that hold pointers. - uint32_t register_mask = code_info.GetRegisterMaskOf(encoding, map); + uint32_t register_mask = code_info.GetRegisterMaskOf(map); for (size_t i = 0; i < BitSizeOf(); ++i) { if (register_mask & (1 << i)) { mirror::Object** ref_addr = reinterpret_cast(GetGPRAddress(i)); @@ -3631,7 +3630,6 @@ class ReferenceMapVisitor : public StackVisitor { struct UndefinedVRegInfo { UndefinedVRegInfo(ArtMethod* method ATTRIBUTE_UNUSED, const CodeInfo& code_info ATTRIBUTE_UNUSED, - const CodeInfoEncoding& encoding ATTRIBUTE_UNUSED, const StackMap& map ATTRIBUTE_UNUSED, RootVisitor& _visitor) : visitor(_visitor) { @@ -3662,14 +3660,11 @@ class ReferenceMapVisitor : public StackVisitor { struct StackMapVRegInfo { StackMapVRegInfo(ArtMethod* method, const CodeInfo& _code_info, - const CodeInfoEncoding& _encoding, const StackMap& map, RootVisitor& _visitor) : number_of_dex_registers(method->DexInstructionData().RegistersSize()), code_info(_code_info), - encoding(_encoding), dex_register_map(code_info.GetDexRegisterMapOf(map, - encoding, number_of_dex_registers)), visitor(_visitor) { } @@ -3684,7 +3679,7 @@ class ReferenceMapVisitor : public StackVisitor { bool found = false; for (size_t dex_reg = 0; dex_reg != number_of_dex_registers; ++dex_reg) { DexRegisterLocation location = dex_register_map.GetDexRegisterLocation( - dex_reg, number_of_dex_registers, code_info, encoding); + dex_reg, number_of_dex_registers, code_info); if (location.GetKind() == kind && static_cast(location.GetValue()) == index) { visitor(ref, dex_reg, stack_visitor); found = true; @@ -3718,7 +3713,6 @@ class ReferenceMapVisitor : public StackVisitor { size_t number_of_dex_registers; const CodeInfo& code_info; - const CodeInfoEncoding& encoding; DexRegisterMap dex_register_map; RootVisitor& visitor; }; diff --git a/test/566-polymorphic-inlining/polymorphic_inline.cc b/test/566-polymorphic-inlining/polymorphic_inline.cc index e2b8aa037f..7c1507fb5c 100644 --- a/test/566-polymorphic-inlining/polymorphic_inline.cc +++ b/test/566-polymorphic-inlining/polymorphic_inline.cc @@ -48,9 +48,8 @@ static void do_checks(jclass cls, const char* method_name) { } } - CodeInfo info = header->GetOptimizedCodeInfo(); - CodeInfoEncoding encoding = info.ExtractEncoding(); - CHECK(info.HasInlineInfo(encoding)); + CodeInfo info(header); + CHECK(info.HasInlineInfo()); } static void allocate_profiling_info(jclass cls, const char* method_name) { -- GitLab From 4fe708c211c8367bae104616269ce62eb5465291 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Mon, 21 May 2018 20:31:24 +0100 Subject: [PATCH 435/749] Remove "stack_map.h" include from code_generator. It is not needed and it increases header dependencies. Test: Build Change-Id: I51fcef5025defe5e4185e9c4fde18b363194789e --- compiler/optimizing/code_generator.h | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index bcb25997f4..a340446ac3 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -35,7 +35,6 @@ #include "optimizing_compiler_stats.h" #include "read_barrier_option.h" #include "stack.h" -#include "stack_map.h" #include "utils/label.h" namespace art { -- GitLab From c2b4db61e5d0d9ec40b87b9a051aa1ac15ed1294 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 18 May 2018 13:58:12 -0700 Subject: [PATCH 436/749] Add ClassAccessor Aims to replace ClassDataItemIterator with a cleaner and simpler abstraction. Bug: 77709234 Bug: 79758018 Test: test-art-host Change-Id: I871a3e1cf213e0d81bfe4bb77790fbab2d13e44c --- compiler/dex/dex_to_dex_decompiler_test.cc | 24 ++-- dexdump/dexdump.cc | 19 +-- libdexfile/dex/class_accessor-inl.h | 104 ++++++++++++++++ libdexfile/dex/class_accessor.h | 131 +++++++++++++++++++++ tools/dexanalyze/dexanalyze_experiments.cc | 125 ++++++++++---------- 5 files changed, 309 insertions(+), 94 deletions(-) create mode 100644 libdexfile/dex/class_accessor-inl.h create mode 100644 libdexfile/dex/class_accessor.h diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc index 19b190093f..082e6091d2 100644 --- a/compiler/dex/dex_to_dex_decompiler_test.cc +++ b/compiler/dex/dex_to_dex_decompiler_test.cc @@ -20,6 +20,7 @@ #include "common_compiler_test.h" #include "compiled_method-inl.h" #include "compiler_callbacks.h" +#include "dex/class_accessor-inl.h" #include "dex/dex_file.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" @@ -82,30 +83,21 @@ class DexToDexDecompilerTest : public CommonCompilerTest { // Unquicken the dex file. for (uint32_t i = 0; i < updated_dex_file->NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = updated_dex_file->GetClassDef(i); - const uint8_t* class_data = updated_dex_file->GetClassData(class_def); - if (class_data == nullptr) { - continue; - } - ClassDataItemIterator it(*updated_dex_file, class_data); - it.SkipAllFields(); - // Unquicken each method. - while (it.HasNextMethod()) { - uint32_t method_idx = it.GetMemberIndex(); - CompiledMethod* compiled_method = - compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx)); + ClassAccessor accessor(*updated_dex_file, updated_dex_file->GetClassDef(i)); + accessor.VisitMethods([&](const ClassAccessor::Method& method) { + CompiledMethod* compiled_method = compiler_driver_->GetCompiledMethod( + MethodReference(updated_dex_file, + method.GetIndex())); ArrayRef table; if (compiled_method != nullptr) { table = compiled_method->GetVmapTable(); } optimizer::ArtDecompileDEX(*updated_dex_file, - *it.GetMethodCodeItem(), + *accessor.GetCodeItem(method), table, /* decompile_return_instruction */ true); - it.Next(); - } - DCHECK(!it.HasNext()); + }); } // Make sure after unquickening we go back to the same contents as the original dex file. diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index e72d49e05f..f5a13f0920 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -45,6 +45,7 @@ #include "android-base/logging.h" #include "android-base/stringprintf.h" +#include "dex/class_accessor-inl.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_exception_helpers.h" @@ -611,19 +612,11 @@ static void dumpClassDef(const DexFile* pDexFile, int idx) { pClassDef.class_data_off_, pClassDef.class_data_off_); // Fields and methods. - const u1* pEncodedData = pDexFile->GetClassData(pClassDef); - if (pEncodedData != nullptr) { - ClassDataItemIterator pClassData(*pDexFile, pEncodedData); - fprintf(gOutFile, "static_fields_size : %d\n", pClassData.NumStaticFields()); - fprintf(gOutFile, "instance_fields_size: %d\n", pClassData.NumInstanceFields()); - fprintf(gOutFile, "direct_methods_size : %d\n", pClassData.NumDirectMethods()); - fprintf(gOutFile, "virtual_methods_size: %d\n", pClassData.NumVirtualMethods()); - } else { - fprintf(gOutFile, "static_fields_size : 0\n"); - fprintf(gOutFile, "instance_fields_size: 0\n"); - fprintf(gOutFile, "direct_methods_size : 0\n"); - fprintf(gOutFile, "virtual_methods_size: 0\n"); - } + ClassAccessor accessor(*pDexFile, pClassDef); + fprintf(gOutFile, "static_fields_size : %d\n", accessor.NumStaticFields()); + fprintf(gOutFile, "instance_fields_size: %d\n", accessor.NumInstanceFields()); + fprintf(gOutFile, "direct_methods_size : %d\n", accessor.NumDirectMethods()); + fprintf(gOutFile, "virtual_methods_size: %d\n", accessor.NumVirtualMethods()); fprintf(gOutFile, "\n"); } diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h new file mode 100644 index 0000000000..bcd0a7b66c --- /dev/null +++ b/libdexfile/dex/class_accessor-inl.h @@ -0,0 +1,104 @@ +/* + * 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. + */ + +#ifndef ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_ +#define ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_ + +#include "class_accessor.h" + +#include "base/leb128.h" + +namespace art { + +inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const DexFile::ClassDef& class_def) + : ClassAccessor(dex_file, dex_file.GetClassData(class_def)) {} + +inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const uint8_t* class_data) + : dex_file_(dex_file), + ptr_pos_(class_data), + num_static_fields_(class_data != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u), + num_instance_fields_(class_data != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u), + num_direct_methods_(class_data != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u), + num_virtual_methods_(class_data != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u) {} + +inline const uint8_t* ClassAccessor::Method::Read(const uint8_t* ptr) { + method_idx_ += DecodeUnsignedLeb128(&ptr); + access_flags_ = DecodeUnsignedLeb128(&ptr); + code_off_ = DecodeUnsignedLeb128(&ptr); + return ptr; +} + +inline const uint8_t* ClassAccessor::Field::Read(const uint8_t* ptr) { + field_idx_ += DecodeUnsignedLeb128(&ptr); + access_flags_ = DecodeUnsignedLeb128(&ptr); + return ptr; +} + +template +inline void ClassAccessor::VisitMethodsAndFields( + const StaticFieldVisitor& static_field_visitor, + const InstanceFieldVisitor& instance_field_visitor, + const DirectMethodVisitor& direct_method_visitor, + const VirtualMethodVisitor& virtual_method_visitor) const { + const uint8_t* ptr = ptr_pos_; + for (size_t i = 0; i < num_static_fields_; ++i) { + Field data; + ptr = data.Read(ptr); + static_field_visitor(data); + } + for (size_t i = 0; i < num_instance_fields_; ++i) { + Field data; + ptr = data.Read(ptr); + instance_field_visitor(data); + } + for (size_t i = 0; i < num_direct_methods_; ++i) { + Method data; + ptr = data.Read(ptr); + direct_method_visitor(data); + } + for (size_t i = 0; i < num_virtual_methods_; ++i) { + Method data; + ptr = data.Read(ptr); + virtual_method_visitor(data); + } +} + +template +inline void ClassAccessor::VisitMethods(const DirectMethodVisitor& direct_method_visitor, + const VirtualMethodVisitor& virtual_method_visitor) const { + VisitMethodsAndFields(VoidFunctor(), + VoidFunctor(), + direct_method_visitor, + virtual_method_visitor); +} + +// Visit direct and virtual methods. +template +inline void ClassAccessor::VisitMethods(const MethodVisitor& method_visitor) const { + VisitMethods(method_visitor, method_visitor); +} + +inline const DexFile::CodeItem* ClassAccessor::GetCodeItem(const Method& method) const { + return dex_file_.GetCodeItem(method.GetCodeItemOffset()); +} + +} // namespace art + +#endif // ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_ diff --git a/libdexfile/dex/class_accessor.h b/libdexfile/dex/class_accessor.h new file mode 100644 index 0000000000..59a6b5dfa5 --- /dev/null +++ b/libdexfile/dex/class_accessor.h @@ -0,0 +1,131 @@ +/* + * 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. + */ + +#ifndef ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_H_ +#define ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_H_ + +#include "base/utils.h" +#include "dex_file.h" + +namespace art { + +// Classes to access Dex data. +class ClassAccessor { + public: + // Class method data. + class Method { + public: + uint32_t GetIndex() const { + return method_idx_; + } + + uint32_t GetAccessFlags() const { + return access_flags_; + } + + uint32_t GetCodeItemOffset() const { + return code_off_; + } + + private: + const uint8_t* Read(const uint8_t* ptr); + + // A decoded version of the method of a class_data_item. + uint32_t method_idx_ = 0u; + uint32_t access_flags_ = 0u; + uint32_t code_off_ = 0u; + + friend class ClassAccessor; + }; + + // Class field data. + class Field { + public: + uint32_t GetIndex() const { + return field_idx_; + } + + uint32_t GetAccessFlags() const { + return access_flags_; + } + + private: + const uint8_t* Read(const uint8_t* ptr); + + // A decoded version of the field of a class_data_item. + uint32_t field_idx_ = 0u; + uint32_t access_flags_ = 0u; + + friend class ClassAccessor; + }; + + ALWAYS_INLINE ClassAccessor(const DexFile& dex_file, const DexFile::ClassDef& class_def); + + // Return the code item for a method. + const DexFile::CodeItem* GetCodeItem(const Method& method) const; + + // Iterator data is not very iterator friendly, use visitors to get around this. + template + void VisitMethodsAndFields(const StaticFieldVisitor& static_field_visitor, + const InstanceFieldVisitor& instance_field_visitor, + const DirectMethodVisitor& direct_method_visitor, + const VirtualMethodVisitor& virtual_method_visitor) const; + + template + void VisitMethods(const DirectMethodVisitor& direct_method_visitor, + const VirtualMethodVisitor& virtual_method_visitor) const; + + // Visit direct and virtual methods. + template + void VisitMethods(const MethodVisitor& method_visitor) const; + + uint32_t NumStaticFields() const { + return num_static_fields_; + } + + uint32_t NumInstanceFields() const { + return num_instance_fields_; + } + + uint32_t NumDirectMethods() const { + return num_direct_methods_; + } + + uint32_t NumVirtualMethods() const { + return num_virtual_methods_; + } + + protected: + ALWAYS_INLINE ClassAccessor(const DexFile& dex_file, const uint8_t* class_data); + + const DexFile& dex_file_; + const uint8_t* ptr_pos_ = nullptr; // Pointer into stream of class_data_item. + const uint32_t num_static_fields_ = 0u; + const uint32_t num_instance_fields_ = 0u; + const uint32_t num_direct_methods_ = 0u; + const uint32_t num_virtual_methods_ = 0u; + // Only cache descriptor. + const void* class_def_ = nullptr; + const void* class_data_ = nullptr; +}; + +} // namespace art + +#endif // ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_H_ diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc index adc515472d..f9bf45da4f 100644 --- a/tools/dexanalyze/dexanalyze_experiments.cc +++ b/tools/dexanalyze/dexanalyze_experiments.cc @@ -23,6 +23,7 @@ #include #include "android-base/stringprintf.h" +#include "dex/class_accessor-inl.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_instruction-inl.h" #include "dex/standard_dex_file.h" @@ -126,79 +127,73 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { num_class_defs_ += dex_file.NumClassDefs(); for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); ++class_def_index) { const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { - continue; - } - ClassDataItemIterator it(dex_file, class_data); - it.SkipAllFields(); + ClassAccessor accessor(dex_file, class_def); std::set unique_method_ids; std::set unique_string_ids; - while (it.HasNextMethod()) { - const DexFile::CodeItem* code_item = it.GetMethodCodeItem(); - if (code_item != nullptr) { - CodeItemInstructionAccessor instructions(dex_file, code_item); - const uint16_t* code_ptr = instructions.Insns(); - dex_code_bytes_ += instructions.InsnsSizeInCodeUnits() * sizeof(code_ptr[0]); - for (const DexInstructionPcPair& inst : instructions) { - switch (inst->Opcode()) { - case Instruction::CONST_STRING: { - const dex::StringIndex string_index(inst->VRegB_21c()); - unique_string_ids.insert(string_index.index_); - ++num_string_ids_from_code_; - break; - } - case Instruction::CONST_STRING_JUMBO: { - const dex::StringIndex string_index(inst->VRegB_31c()); - unique_string_ids.insert(string_index.index_); - ++num_string_ids_from_code_; - break; - } - // Invoke cases. - case Instruction::INVOKE_VIRTUAL: - case Instruction::INVOKE_VIRTUAL_RANGE: { - bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE); - uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); - if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) { - ++same_class_virtual_; - } else { - ++other_class_virtual_; - unique_method_ids.insert(method_idx); - } - break; + accessor.VisitMethods([&](const ClassAccessor::Method& method) { + const DexFile::CodeItem* code_item = accessor.GetCodeItem(method); + if (code_item == nullptr) { + return; + } + CodeItemInstructionAccessor instructions(dex_file, code_item); + const uint16_t* code_ptr = instructions.Insns(); + dex_code_bytes_ += instructions.InsnsSizeInCodeUnits() * sizeof(code_ptr[0]); + for (const DexInstructionPcPair& inst : instructions) { + switch (inst->Opcode()) { + case Instruction::CONST_STRING: { + const dex::StringIndex string_index(inst->VRegB_21c()); + unique_string_ids.insert(string_index.index_); + ++num_string_ids_from_code_; + break; + } + case Instruction::CONST_STRING_JUMBO: { + const dex::StringIndex string_index(inst->VRegB_31c()); + unique_string_ids.insert(string_index.index_); + ++num_string_ids_from_code_; + break; + } + // Invoke cases. + case Instruction::INVOKE_VIRTUAL: + case Instruction::INVOKE_VIRTUAL_RANGE: { + bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE); + uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); + if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) { + ++same_class_virtual_; + } else { + ++other_class_virtual_; + unique_method_ids.insert(method_idx); } - case Instruction::INVOKE_DIRECT: - case Instruction::INVOKE_DIRECT_RANGE: { - bool is_range = (inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE); - uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) { - ++same_class_direct_; - } else { - ++other_class_direct_; - unique_method_ids.insert(method_idx); - } - break; + break; + } + case Instruction::INVOKE_DIRECT: + case Instruction::INVOKE_DIRECT_RANGE: { + bool is_range = (inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE); + uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) { + ++same_class_direct_; + } else { + ++other_class_direct_; + unique_method_ids.insert(method_idx); } - case Instruction::INVOKE_STATIC: - case Instruction::INVOKE_STATIC_RANGE: { - bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE); - uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) { - ++same_class_static_; - } else { - ++other_class_static_; - unique_method_ids.insert(method_idx); - } - break; + break; + } + case Instruction::INVOKE_STATIC: + case Instruction::INVOKE_STATIC_RANGE: { + bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE); + uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) { + ++same_class_static_; + } else { + ++other_class_static_; + unique_method_ids.insert(method_idx); } - default: - break; + break; } + default: + break; } } - it.Next(); - } - DCHECK(!it.HasNext()); + }); total_unique_method_idx_ += unique_method_ids.size(); total_unique_string_ids_ += unique_string_ids.size(); } -- GitLab From 673726b39689afe5496bbb0bac5ec52b4d50d334 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 21 May 2018 14:19:15 -0700 Subject: [PATCH 437/749] Add plugin for testing method tracing speed. It can be useful to test how much overhead the ART method tracing infrastructure has. Since normal method tracing APIs (eg -Xmethod-trace, JVMTI events) have significant overhead for synchronization or other reasons we created a simple plugin that begins method tracing but doesn't record or even look at the trace events. This lets us have a rough idea how much simply sending these events costs us. Test: ./test/run-test --host \ --runtime-option \ -Xplugin:$ANDROID_HOST_OUT/lib/libtracefast-trampolined.so \ 001-Main Change-Id: I1fac4cdd0dfa780cb4efe195025b3d29e8cf619c --- tools/tracefast-plugin/Android.bp | 108 +++++++++++++++++ tools/tracefast-plugin/tracefast.cc | 177 ++++++++++++++++++++++++++++ 2 files changed, 285 insertions(+) create mode 100644 tools/tracefast-plugin/Android.bp create mode 100644 tools/tracefast-plugin/tracefast.cc diff --git a/tools/tracefast-plugin/Android.bp b/tools/tracefast-plugin/Android.bp new file mode 100644 index 0000000000..1d7dd302c0 --- /dev/null +++ b/tools/tracefast-plugin/Android.bp @@ -0,0 +1,108 @@ +// +// 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. +// + +// Build variants {target,host} x {debug,ndebug} x {32,64} + +cc_defaults { + name: "tracefast-defaults", + host_supported: true, + srcs: ["tracefast.cc"], + defaults: ["art_defaults"], + + // Note that this tool needs to be built for both 32-bit and 64-bit since it requires + // to be same ISA as what it is attached to. + compile_multilib: "both", + + shared_libs: [ + "libbase", + ], + target: { + android: { + shared_libs: [ + "libcutils", + ], + }, + darwin: { + enabled: false, + }, + }, + header_libs: [ + "libnativehelper_header_only", + ], + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, + symlink_preferred_arch: true, +} + +cc_defaults { + name: "tracefast-interpreter-defaults", + defaults: ["tracefast-defaults"], + cflags: ["-DTRACEFAST_INTERPRETER=1"], +} + +cc_defaults { + name: "tracefast-trampoline-defaults", + defaults: ["tracefast-defaults"], + cflags: ["-DTRACEFAST_TRAMPOLINE=1"], +} + +art_cc_library { + name: "libtracefast-interpreter", + defaults: ["tracefast-interpreter-defaults"], + shared_libs: [ + "libart", + "libartbase", + ], +} + +art_cc_library { + name: "libtracefast-interpreterd", + defaults: [ + "art_debug_defaults", + "tracefast-interpreter-defaults", + ], + shared_libs: [ + "libartd", + "libartbased", + ], +} + +art_cc_library { + name: "libtracefast-trampoline", + defaults: ["tracefast-trampoline-defaults"], + shared_libs: [ + "libart", + "libartbase", + ], +} + +art_cc_library { + name: "libtracefast-trampolined", + defaults: [ + "art_debug_defaults", + "tracefast-trampoline-defaults", + ], + shared_libs: [ + "libartd", + "libartbased", + ], +} diff --git a/tools/tracefast-plugin/tracefast.cc b/tools/tracefast-plugin/tracefast.cc new file mode 100644 index 0000000000..ed6ac3d199 --- /dev/null +++ b/tools/tracefast-plugin/tracefast.cc @@ -0,0 +1,177 @@ +/* + * 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. + */ + +#include "gc/scoped_gc_critical_section.h" +#include "instrumentation.h" +#include "runtime.h" +#include "runtime_callbacks.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-inl.h" +#include "thread_list.h" + +namespace tracefast { + +#if ((!defined(TRACEFAST_INTERPRETER) && !defined(TRACEFAST_TRAMPOLINE)) || \ + (defined(TRACEFAST_INTERPRETER) && defined(TRACEFAST_TRAMPOLINE))) +#error Must set one of TRACEFAST_TRAMPOLINE or TRACEFAST_INTERPRETER during build +#endif + + +#ifdef TRACEFAST_INTERPRETER +static constexpr const char* kTracerInstrumentationKey = "tracefast_INTERPRETER"; +static constexpr bool kNeedsInterpreter = true; +#else // defined(TRACEFAST_TRAMPOLINE) +static constexpr const char* kTracerInstrumentationKey = "tracefast_TRAMPOLINE"; +static constexpr bool kNeedsInterpreter = false; +#endif // TRACEFAST_INITERPRETER + +class Tracer FINAL : public art::instrumentation::InstrumentationListener { + public: + Tracer() {} + + void MethodEntered(art::Thread* thread ATTRIBUTE_UNUSED, + art::Handle this_object ATTRIBUTE_UNUSED, + art::ArtMethod* method ATTRIBUTE_UNUSED, + uint32_t dex_pc ATTRIBUTE_UNUSED) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { } + + void MethodExited(art::Thread* thread ATTRIBUTE_UNUSED, + art::Handle this_object ATTRIBUTE_UNUSED, + art::ArtMethod* method ATTRIBUTE_UNUSED, + uint32_t dex_pc ATTRIBUTE_UNUSED, + art::Handle return_value ATTRIBUTE_UNUSED) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { } + + void MethodExited(art::Thread* thread ATTRIBUTE_UNUSED, + art::Handle this_object ATTRIBUTE_UNUSED, + art::ArtMethod* method ATTRIBUTE_UNUSED, + uint32_t dex_pc ATTRIBUTE_UNUSED, + const art::JValue& return_value ATTRIBUTE_UNUSED) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { } + + void MethodUnwind(art::Thread* thread ATTRIBUTE_UNUSED, + art::Handle this_object ATTRIBUTE_UNUSED, + art::ArtMethod* method ATTRIBUTE_UNUSED, + uint32_t dex_pc ATTRIBUTE_UNUSED) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { } + + void DexPcMoved(art::Thread* thread ATTRIBUTE_UNUSED, + art::Handle this_object ATTRIBUTE_UNUSED, + art::ArtMethod* method ATTRIBUTE_UNUSED, + uint32_t new_dex_pc ATTRIBUTE_UNUSED) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { } + + void FieldRead(art::Thread* thread ATTRIBUTE_UNUSED, + art::Handle this_object ATTRIBUTE_UNUSED, + art::ArtMethod* method ATTRIBUTE_UNUSED, + uint32_t dex_pc ATTRIBUTE_UNUSED, + art::ArtField* field ATTRIBUTE_UNUSED) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { } + + void FieldWritten(art::Thread* thread ATTRIBUTE_UNUSED, + art::Handle this_object ATTRIBUTE_UNUSED, + art::ArtMethod* method ATTRIBUTE_UNUSED, + uint32_t dex_pc ATTRIBUTE_UNUSED, + art::ArtField* field ATTRIBUTE_UNUSED, + art::Handle field_value ATTRIBUTE_UNUSED) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { } + + void FieldWritten(art::Thread* thread ATTRIBUTE_UNUSED, + art::Handle this_object ATTRIBUTE_UNUSED, + art::ArtMethod* method ATTRIBUTE_UNUSED, + uint32_t dex_pc ATTRIBUTE_UNUSED, + art::ArtField* field ATTRIBUTE_UNUSED, + const art::JValue& field_value ATTRIBUTE_UNUSED) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { } + + void ExceptionThrown(art::Thread* thread ATTRIBUTE_UNUSED, + art::Handle exception_object ATTRIBUTE_UNUSED) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { } + + void ExceptionHandled(art::Thread* self ATTRIBUTE_UNUSED, + art::Handle throwable ATTRIBUTE_UNUSED) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { } + + void Branch(art::Thread* thread ATTRIBUTE_UNUSED, + art::ArtMethod* method ATTRIBUTE_UNUSED, + uint32_t dex_pc ATTRIBUTE_UNUSED, + int32_t dex_pc_offset ATTRIBUTE_UNUSED) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { } + + void InvokeVirtualOrInterface(art::Thread* thread ATTRIBUTE_UNUSED, + art::Handle this_object ATTRIBUTE_UNUSED, + art::ArtMethod* caller ATTRIBUTE_UNUSED, + uint32_t dex_pc ATTRIBUTE_UNUSED, + art::ArtMethod* callee ATTRIBUTE_UNUSED) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { } + + void WatchedFramePop(art::Thread* thread ATTRIBUTE_UNUSED, + const art::ShadowFrame& frame ATTRIBUTE_UNUSED) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { } + + private: + DISALLOW_COPY_AND_ASSIGN(Tracer); +}; + +Tracer gEmptyTracer; + +static void StartTracing() REQUIRES(!art::Locks::mutator_lock_, + !art::Locks::thread_list_lock_, + !art::Locks::thread_suspend_count_lock_) { + art::Thread* self = art::Thread::Current(); + art::Runtime* runtime = art::Runtime::Current(); + art::gc::ScopedGCCriticalSection gcs(self, + art::gc::kGcCauseInstrumentation, + art::gc::kCollectorTypeInstrumentation); + art::ScopedSuspendAll ssa("starting fast tracing"); + runtime->GetInstrumentation()->AddListener(&gEmptyTracer, + art::instrumentation::Instrumentation::kMethodEntered | + art::instrumentation::Instrumentation::kMethodExited | + art::instrumentation::Instrumentation::kMethodUnwind); + runtime->GetInstrumentation()->EnableMethodTracing(kTracerInstrumentationKey, kNeedsInterpreter); +} + +class TraceFastPhaseCB : public art::RuntimePhaseCallback { + public: + TraceFastPhaseCB() {} + + void NextRuntimePhase(art::RuntimePhaseCallback::RuntimePhase phase) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (phase == art::RuntimePhaseCallback::RuntimePhase::kInit) { + art::ScopedThreadSuspension sts(art::Thread::Current(), + art::ThreadState::kWaitingForMethodTracingStart); + StartTracing(); + } + } +}; +TraceFastPhaseCB gPhaseCallback; + +// The plugin initialization function. +extern "C" bool ArtPlugin_Initialize() REQUIRES_SHARED(art::Locks::mutator_lock_) { + art::Runtime* runtime = art::Runtime::Current(); + art::ScopedThreadSuspension stsc(art::Thread::Current(), + art::ThreadState::kWaitingForMethodTracingStart); + art::ScopedSuspendAll ssa("Add phase callback"); + runtime->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&gPhaseCallback); + return true; +} + +extern "C" bool ArtPlugin_Deinitialize() { + // Don't need to bother doing anything. + return true; +} + +} // namespace tracefast -- GitLab From cfb4ed5aa6126dc15d83a648ee0b2b67abdf9870 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Tue, 22 May 2018 10:48:52 +0100 Subject: [PATCH 438/749] ART: Fix for pkill on device in run-jdwp-tests.sh Adds workaround to run-jdwp-tests.sh for pkill on old devices running within the ART buildbots. Test: manual Bug: 28877702 Change-Id: I419165fd9f254dae4db94a0e7fffb66feed22360 --- tools/run-jdwp-tests.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index d376cad9da..eebc09278a 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -360,7 +360,9 @@ echo "Killing stalled dalvikvm processes..." if [[ $mode == "host" ]]; then pkill -9 -f /bin/dalvikvm else - adb shell pkill -9 -f /bin/dalvikvm + # Tests may run on older Android versions where pkill requires "-l SIGNAL" + # rather than "-SIGNAL". + adb shell pkill -l 9 -f /bin/dalvikvm fi echo "Done." -- GitLab From d3083dd15af1cb4ffc13d87a7d2c3be2edb9199d Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 17 May 2018 08:43:47 +0100 Subject: [PATCH 439/749] Refactor runtime callee save frame info. And avoid storing the info in Runtime. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing --jit Test: Pixel 2 XL boots. Test: testrunner.py --target --optimizing --jit Change-Id: Ib14853fc06c420753993e1f9e82a1b01f5e35e8c --- compiler/optimizing/code_generator_arm64.h | 1 - compiler/utils/x86/constants_x86.h | 15 --- runtime/arch/arch_test.cc | 50 +++------ ...ame_info_arm.h => callee_save_frame_arm.h} | 91 ++++++++-------- ...info_arm64.h => callee_save_frame_arm64.h} | 91 ++++++++-------- ...e_info_mips.h => callee_save_frame_mips.h} | 78 +++++++++----- runtime/arch/mips/fault_handler_mips.cc | 2 +- ...fo_mips64.h => callee_save_frame_mips64.h} | 78 +++++++++----- runtime/arch/mips64/fault_handler_mips64.cc | 2 +- runtime/arch/x86/callee_save_frame_x86.h | 98 +++++++++++++++++ .../arch/x86/quick_method_frame_info_x86.h | 89 ---------------- runtime/arch/x86/registers_x86.h | 14 +++ ...fo_x86_64.h => callee_save_frame_x86_64.h} | 74 ++++++++----- runtime/entrypoints/entrypoint_utils.cc | 4 +- runtime/entrypoints/quick/callee_save_frame.h | 86 +++++---------- .../quick/quick_trampoline_entrypoints.cc | 60 ++++------- .../quick_trampoline_entrypoints_test.cc | 100 +++++------------- runtime/runtime-inl.h | 11 +- runtime/runtime.cc | 53 +++------- runtime/runtime.h | 5 - runtime/stack.cc | 9 +- 21 files changed, 469 insertions(+), 542 deletions(-) rename runtime/arch/arm/{quick_method_frame_info_arm.h => callee_save_frame_arm.h} (52%) rename runtime/arch/arm64/{quick_method_frame_info_arm64.h => callee_save_frame_arm64.h} (61%) rename runtime/arch/mips/{quick_method_frame_info_mips.h => callee_save_frame_mips.h} (66%) rename runtime/arch/mips64/{quick_method_frame_info_mips64.h => callee_save_frame_mips64.h} (61%) create mode 100644 runtime/arch/x86/callee_save_frame_x86.h delete mode 100644 runtime/arch/x86/quick_method_frame_info_x86.h rename runtime/arch/x86_64/{quick_method_frame_info_x86_64.h => callee_save_frame_x86_64.h} (54%) diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index aa343b1185..e7fe5b71b7 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -17,7 +17,6 @@ #ifndef ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_ #define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_ -#include "arch/arm64/quick_method_frame_info_arm64.h" #include "base/bit_field.h" #include "code_generator.h" #include "common_arm64.h" diff --git a/compiler/utils/x86/constants_x86.h b/compiler/utils/x86/constants_x86.h index 73ef028075..a782b16c6b 100644 --- a/compiler/utils/x86/constants_x86.h +++ b/compiler/utils/x86/constants_x86.h @@ -40,21 +40,6 @@ enum ByteRegister { kNoByteRegister = -1 // Signals an illegal register. }; - -enum XmmRegister { - XMM0 = 0, - XMM1 = 1, - XMM2 = 2, - XMM3 = 3, - XMM4 = 4, - XMM5 = 5, - XMM6 = 6, - XMM7 = 7, - kNumberOfXmmRegisters = 8, - kNoXmmRegister = -1 // Signals an illegal register. -}; -std::ostream& operator<<(std::ostream& os, const XmmRegister& reg); - enum X87Register { ST0 = 0, ST1 = 1, diff --git a/runtime/arch/arch_test.cc b/runtime/arch/arch_test.cc index 1ba4070056..d4ceede07a 100644 --- a/runtime/arch/arch_test.cc +++ b/runtime/arch/arch_test.cc @@ -18,6 +18,7 @@ #include "art_method-inl.h" #include "base/callee_save_type.h" +#include "entrypoints/quick/callee_save_frame.h" #include "common_runtime_test.h" #include "quick/quick_method_frame_info.h" @@ -57,21 +58,6 @@ class ArchTest : public CommonRuntimeTest { void FinalizeSetup() OVERRIDE { ASSERT_EQ(InstructionSet::kX86_64, Runtime::Current()->GetInstructionSet()); } - - static void CheckFrameSize(InstructionSet isa, CalleeSaveType type, uint32_t save_size) - NO_THREAD_SAFETY_ANALYSIS { - Runtime* const runtime = Runtime::Current(); - Thread* const self = Thread::Current(); - ScopedObjectAccess soa(self); // So we can create callee-save methods. - - runtime->SetInstructionSet(isa); - ArtMethod* save_method = runtime->CreateCalleeSaveMethod(); - runtime->SetCalleeSaveMethod(save_method, type); - QuickMethodFrameInfo frame_info = runtime->GetRuntimeMethodFrameInfo(save_method); - EXPECT_EQ(frame_info.FrameSizeInBytes(), save_size) << "Expected and real size differs for " - << type << " core spills=" << std::hex << frame_info.CoreSpillMask() << " fp spills=" - << frame_info.FpSpillMask() << std::dec; - } }; TEST_F(ArchTest, CheckCommonOffsetsAndSizes) { @@ -205,26 +191,20 @@ static constexpr size_t kFrameSizeSaveEverything = FRAME_SIZE_SAVE_EVERYTHING; } // namespace x86_64 // Check architecture specific constants are sound. -#define TEST_ARCH(Arch, arch) \ - TEST_F(ArchTest, Arch) { \ - CheckFrameSize(InstructionSet::k##Arch, \ - CalleeSaveType::kSaveAllCalleeSaves, \ - arch::kFrameSizeSaveAllCalleeSaves); \ - CheckFrameSize(InstructionSet::k##Arch, \ - CalleeSaveType::kSaveRefsOnly, \ - arch::kFrameSizeSaveRefsOnly); \ - CheckFrameSize(InstructionSet::k##Arch, \ - CalleeSaveType::kSaveRefsAndArgs, \ - arch::kFrameSizeSaveRefsAndArgs); \ - CheckFrameSize(InstructionSet::k##Arch, \ - CalleeSaveType::kSaveEverything, \ - arch::kFrameSizeSaveEverything); \ - CheckFrameSize(InstructionSet::k##Arch, \ - CalleeSaveType::kSaveEverythingForClinit, \ - arch::kFrameSizeSaveEverythingForClinit); \ - CheckFrameSize(InstructionSet::k##Arch, \ - CalleeSaveType::kSaveEverythingForSuspendCheck, \ - arch::kFrameSizeSaveEverythingForSuspendCheck); \ +// We expect the return PC to be stored at the highest address slot in the frame. +#define TEST_ARCH_TYPE(Arch, arch, type) \ + EXPECT_EQ(arch::Arch##CalleeSaveFrame::GetFrameSize(CalleeSaveType::k##type), \ + arch::kFrameSize##type); \ + EXPECT_EQ(arch::Arch##CalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::k##type), \ + arch::kFrameSize##type - static_cast(k##Arch##PointerSize)) +#define TEST_ARCH(Arch, arch) \ + TEST_F(ArchTest, Arch) { \ + TEST_ARCH_TYPE(Arch, arch, SaveAllCalleeSaves); \ + TEST_ARCH_TYPE(Arch, arch, SaveRefsOnly); \ + TEST_ARCH_TYPE(Arch, arch, SaveRefsAndArgs); \ + TEST_ARCH_TYPE(Arch, arch, SaveEverything); \ + TEST_ARCH_TYPE(Arch, arch, SaveEverythingForClinit); \ + TEST_ARCH_TYPE(Arch, arch, SaveEverythingForSuspendCheck); \ } TEST_ARCH(Arm, arm) TEST_ARCH(Arm64, arm64) diff --git a/runtime/arch/arm/quick_method_frame_info_arm.h b/runtime/arch/arm/callee_save_frame_arm.h similarity index 52% rename from runtime/arch/arm/quick_method_frame_info_arm.h rename to runtime/arch/arm/callee_save_frame_arm.h index 5c5b81baae..11eefb9283 100644 --- a/runtime/arch/arm/quick_method_frame_info_arm.h +++ b/runtime/arch/arm/callee_save_frame_arm.h @@ -14,13 +14,14 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_ARCH_ARM_QUICK_METHOD_FRAME_INFO_ARM_H_ -#define ART_RUNTIME_ARCH_ARM_QUICK_METHOD_FRAME_INFO_ARM_H_ +#ifndef ART_RUNTIME_ARCH_ARM_CALLEE_SAVE_FRAME_ARM_H_ +#define ART_RUNTIME_ARCH_ARM_CALLEE_SAVE_FRAME_ARM_H_ #include "arch/instruction_set.h" #include "base/bit_utils.h" #include "base/callee_save_type.h" #include "base/enums.h" +#include "base/globals.h" #include "quick/quick_method_frame_info.h" #include "registers_arm.h" @@ -55,56 +56,56 @@ static constexpr uint32_t kArmCalleeSaveFpAllSpills = static constexpr uint32_t kArmCalleeSaveFpEverythingSpills = kArmCalleeSaveFpArgSpills | kArmCalleeSaveFpAllSpills; -constexpr uint32_t ArmCalleeSaveCoreSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kArmCalleeSaveAlwaysSpills | kArmCalleeSaveRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kArmCalleeSaveArgSpills : 0) | - (type == CalleeSaveType::kSaveAllCalleeSaves ? kArmCalleeSaveAllSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kArmCalleeSaveEverythingSpills : 0); -} +class ArmCalleeSaveFrame { + public: + static constexpr uint32_t GetCoreSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kArmCalleeSaveAlwaysSpills | kArmCalleeSaveRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kArmCalleeSaveArgSpills : 0) | + (type == CalleeSaveType::kSaveAllCalleeSaves ? kArmCalleeSaveAllSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kArmCalleeSaveEverythingSpills : 0); + } -constexpr uint32_t ArmCalleeSaveFpSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kArmCalleeSaveFpAlwaysSpills | kArmCalleeSaveFpRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kArmCalleeSaveFpArgSpills : 0) | - (type == CalleeSaveType::kSaveAllCalleeSaves ? kArmCalleeSaveFpAllSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kArmCalleeSaveFpEverythingSpills : 0); -} + static constexpr uint32_t GetFpSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kArmCalleeSaveFpAlwaysSpills | kArmCalleeSaveFpRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kArmCalleeSaveFpArgSpills : 0) | + (type == CalleeSaveType::kSaveAllCalleeSaves ? kArmCalleeSaveFpAllSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kArmCalleeSaveFpEverythingSpills : 0); + } -constexpr uint32_t ArmCalleeSaveFrameSize(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return RoundUp((POPCOUNT(ArmCalleeSaveCoreSpills(type)) /* gprs */ + - POPCOUNT(ArmCalleeSaveFpSpills(type)) /* fprs */ + - 1 /* Method* */) * static_cast(kArmPointerSize), kStackAlignment); -} + static constexpr uint32_t GetFrameSize(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return RoundUp((POPCOUNT(GetCoreSpills(type)) /* gprs */ + + POPCOUNT(GetFpSpills(type)) /* fprs */ + + 1 /* Method* */) * static_cast(kArmPointerSize), kStackAlignment); + } -constexpr QuickMethodFrameInfo ArmCalleeSaveMethodFrameInfo(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return QuickMethodFrameInfo(ArmCalleeSaveFrameSize(type), - ArmCalleeSaveCoreSpills(type), - ArmCalleeSaveFpSpills(type)); -} + static constexpr QuickMethodFrameInfo GetMethodFrameInfo(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return QuickMethodFrameInfo(GetFrameSize(type), GetCoreSpills(type), GetFpSpills(type)); + } -constexpr size_t ArmCalleeSaveFpr1Offset(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return ArmCalleeSaveFrameSize(type) - - (POPCOUNT(ArmCalleeSaveCoreSpills(type)) + - POPCOUNT(ArmCalleeSaveFpSpills(type))) * static_cast(kArmPointerSize); -} + static constexpr size_t GetFpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + (POPCOUNT(GetCoreSpills(type)) + + POPCOUNT(GetFpSpills(type))) * static_cast(kArmPointerSize); + } -constexpr size_t ArmCalleeSaveGpr1Offset(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return ArmCalleeSaveFrameSize(type) - - POPCOUNT(ArmCalleeSaveCoreSpills(type)) * static_cast(kArmPointerSize); -} + static constexpr size_t GetGpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + POPCOUNT(GetCoreSpills(type)) * static_cast(kArmPointerSize); + } -constexpr size_t ArmCalleeSaveLrOffset(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return ArmCalleeSaveFrameSize(type) - - POPCOUNT(ArmCalleeSaveCoreSpills(type) & (-(1 << LR))) * static_cast(kArmPointerSize); -} + static constexpr size_t GetReturnPcOffset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - static_cast(kArmPointerSize); + } +}; } // namespace arm } // namespace art -#endif // ART_RUNTIME_ARCH_ARM_QUICK_METHOD_FRAME_INFO_ARM_H_ +#endif // ART_RUNTIME_ARCH_ARM_CALLEE_SAVE_FRAME_ARM_H_ diff --git a/runtime/arch/arm64/quick_method_frame_info_arm64.h b/runtime/arch/arm64/callee_save_frame_arm64.h similarity index 61% rename from runtime/arch/arm64/quick_method_frame_info_arm64.h rename to runtime/arch/arm64/callee_save_frame_arm64.h index 2d2b500ae9..bc36bfabec 100644 --- a/runtime/arch/arm64/quick_method_frame_info_arm64.h +++ b/runtime/arch/arm64/callee_save_frame_arm64.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_ARCH_ARM64_QUICK_METHOD_FRAME_INFO_ARM64_H_ -#define ART_RUNTIME_ARCH_ARM64_QUICK_METHOD_FRAME_INFO_ARM64_H_ +#ifndef ART_RUNTIME_ARCH_ARM64_CALLEE_SAVE_FRAME_ARM64_H_ +#define ART_RUNTIME_ARCH_ARM64_CALLEE_SAVE_FRAME_ARM64_H_ #include "arch/instruction_set.h" #include "base/bit_utils.h" @@ -79,57 +79,56 @@ static constexpr uint32_t kArm64CalleeSaveFpEverythingSpills = (1 << art::arm64::D27) | (1 << art::arm64::D28) | (1 << art::arm64::D29) | (1 << art::arm64::D30) | (1 << art::arm64::D31); -constexpr uint32_t Arm64CalleeSaveCoreSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kArm64CalleeSaveAlwaysSpills | kArm64CalleeSaveRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kArm64CalleeSaveArgSpills : 0) | - (type == CalleeSaveType::kSaveAllCalleeSaves ? kArm64CalleeSaveAllSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kArm64CalleeSaveEverythingSpills : 0); -} +class Arm64CalleeSaveFrame { + public: + static constexpr uint32_t GetCoreSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kArm64CalleeSaveAlwaysSpills | kArm64CalleeSaveRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kArm64CalleeSaveArgSpills : 0) | + (type == CalleeSaveType::kSaveAllCalleeSaves ? kArm64CalleeSaveAllSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kArm64CalleeSaveEverythingSpills : 0); + } -constexpr uint32_t Arm64CalleeSaveFpSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kArm64CalleeSaveFpAlwaysSpills | kArm64CalleeSaveFpRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kArm64CalleeSaveFpArgSpills : 0) | - (type == CalleeSaveType::kSaveAllCalleeSaves ? kArm64CalleeSaveFpAllSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kArm64CalleeSaveFpEverythingSpills : 0); -} + static constexpr uint32_t GetFpSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kArm64CalleeSaveFpAlwaysSpills | kArm64CalleeSaveFpRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kArm64CalleeSaveFpArgSpills : 0) | + (type == CalleeSaveType::kSaveAllCalleeSaves ? kArm64CalleeSaveFpAllSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kArm64CalleeSaveFpEverythingSpills : 0); + } -constexpr uint32_t Arm64CalleeSaveFrameSize(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return RoundUp((POPCOUNT(Arm64CalleeSaveCoreSpills(type)) /* gprs */ + - POPCOUNT(Arm64CalleeSaveFpSpills(type)) /* fprs */ + - 1 /* Method* */) * static_cast(kArm64PointerSize), kStackAlignment); -} + static constexpr uint32_t GetFrameSize(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return RoundUp((POPCOUNT(GetCoreSpills(type)) /* gprs */ + + POPCOUNT(GetFpSpills(type)) /* fprs */ + + 1 /* Method* */) * static_cast(kArm64PointerSize), kStackAlignment); + } -constexpr QuickMethodFrameInfo Arm64CalleeSaveMethodFrameInfo(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return QuickMethodFrameInfo(Arm64CalleeSaveFrameSize(type), - Arm64CalleeSaveCoreSpills(type), - Arm64CalleeSaveFpSpills(type)); -} + static constexpr QuickMethodFrameInfo GetMethodFrameInfo(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return QuickMethodFrameInfo(GetFrameSize(type), GetCoreSpills(type), GetFpSpills(type)); + } -constexpr size_t Arm64CalleeSaveFpr1Offset(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return Arm64CalleeSaveFrameSize(type) - - (POPCOUNT(Arm64CalleeSaveCoreSpills(type)) + - POPCOUNT(Arm64CalleeSaveFpSpills(type))) * static_cast(kArm64PointerSize); -} + static constexpr size_t GetFpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + (POPCOUNT(GetCoreSpills(type)) + + POPCOUNT(GetFpSpills(type))) * static_cast(kArm64PointerSize); + } -constexpr size_t Arm64CalleeSaveGpr1Offset(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return Arm64CalleeSaveFrameSize(type) - - POPCOUNT(Arm64CalleeSaveCoreSpills(type)) * static_cast(kArm64PointerSize); -} + static constexpr size_t GetGpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + POPCOUNT(GetCoreSpills(type)) * static_cast(kArm64PointerSize); + } -constexpr size_t Arm64CalleeSaveLrOffset(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return Arm64CalleeSaveFrameSize(type) - - POPCOUNT(Arm64CalleeSaveCoreSpills(type) & (-(1 << LR))) * - static_cast(kArm64PointerSize); -} + static constexpr size_t GetReturnPcOffset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - static_cast(kArm64PointerSize); + } +}; } // namespace arm64 } // namespace art -#endif // ART_RUNTIME_ARCH_ARM64_QUICK_METHOD_FRAME_INFO_ARM64_H_ +#endif // ART_RUNTIME_ARCH_ARM64_CALLEE_SAVE_FRAME_ARM64_H_ diff --git a/runtime/arch/mips/quick_method_frame_info_mips.h b/runtime/arch/mips/callee_save_frame_mips.h similarity index 66% rename from runtime/arch/mips/quick_method_frame_info_mips.h rename to runtime/arch/mips/callee_save_frame_mips.h index 8c86252152..6e88d08432 100644 --- a/runtime/arch/mips/quick_method_frame_info_mips.h +++ b/runtime/arch/mips/callee_save_frame_mips.h @@ -14,13 +14,14 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_ARCH_MIPS_QUICK_METHOD_FRAME_INFO_MIPS_H_ -#define ART_RUNTIME_ARCH_MIPS_QUICK_METHOD_FRAME_INFO_MIPS_H_ +#ifndef ART_RUNTIME_ARCH_MIPS_CALLEE_SAVE_FRAME_MIPS_H_ +#define ART_RUNTIME_ARCH_MIPS_CALLEE_SAVE_FRAME_MIPS_H_ #include "arch/instruction_set.h" #include "base/bit_utils.h" #include "base/callee_save_type.h" #include "base/enums.h" +#include "base/globals.h" #include "quick/quick_method_frame_info.h" #include "registers_mips.h" @@ -80,37 +81,56 @@ static constexpr uint32_t kMipsCalleeSaveFpEverythingSpills = (1 << art::mips::F24) | (1 << art::mips::F25) | (1 << art::mips::F26) | (1 << art::mips::F27) | (1 << art::mips::F28) | (1 << art::mips::F29) | (1 << art::mips::F30) | (1u << art::mips::F31); -constexpr uint32_t MipsCalleeSaveCoreSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kMipsCalleeSaveAlwaysSpills | kMipsCalleeSaveRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kMipsCalleeSaveArgSpills : 0) | - (type == CalleeSaveType::kSaveAllCalleeSaves ? kMipsCalleeSaveAllSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kMipsCalleeSaveEverythingSpills : 0); -} +class MipsCalleeSaveFrame { + public: + static constexpr uint32_t GetCoreSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kMipsCalleeSaveAlwaysSpills | kMipsCalleeSaveRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kMipsCalleeSaveArgSpills : 0) | + (type == CalleeSaveType::kSaveAllCalleeSaves ? kMipsCalleeSaveAllSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kMipsCalleeSaveEverythingSpills : 0); + } -constexpr uint32_t MipsCalleeSaveFPSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kMipsCalleeSaveFpAlwaysSpills | kMipsCalleeSaveFpRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kMipsCalleeSaveFpArgSpills : 0) | - (type == CalleeSaveType::kSaveAllCalleeSaves ? kMipsCalleeSaveAllFPSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kMipsCalleeSaveFpEverythingSpills : 0); -} + static constexpr uint32_t GetFpSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kMipsCalleeSaveFpAlwaysSpills | kMipsCalleeSaveFpRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kMipsCalleeSaveFpArgSpills : 0) | + (type == CalleeSaveType::kSaveAllCalleeSaves ? kMipsCalleeSaveAllFPSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kMipsCalleeSaveFpEverythingSpills : 0); + } -constexpr uint32_t MipsCalleeSaveFrameSize(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return RoundUp((POPCOUNT(MipsCalleeSaveCoreSpills(type)) /* gprs */ + - POPCOUNT(MipsCalleeSaveFPSpills(type)) /* fprs */ + - 1 /* Method* */) * static_cast(kMipsPointerSize), kStackAlignment); -} + static constexpr uint32_t GetFrameSize(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return RoundUp((POPCOUNT(GetCoreSpills(type)) /* gprs */ + + POPCOUNT(GetFpSpills(type)) /* fprs */ + + 1 /* Method* */) * static_cast(kMipsPointerSize), kStackAlignment); + } -constexpr QuickMethodFrameInfo MipsCalleeSaveMethodFrameInfo(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return QuickMethodFrameInfo(MipsCalleeSaveFrameSize(type), - MipsCalleeSaveCoreSpills(type), - MipsCalleeSaveFPSpills(type)); -} + static constexpr QuickMethodFrameInfo GetMethodFrameInfo(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return QuickMethodFrameInfo(GetFrameSize(type), GetCoreSpills(type), GetFpSpills(type)); + } + + static constexpr size_t GetFpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + (POPCOUNT(GetCoreSpills(type)) + + POPCOUNT(GetFpSpills(type))) * static_cast(kMipsPointerSize); + } + + static constexpr size_t GetGpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + POPCOUNT(GetCoreSpills(type)) * static_cast(kMipsPointerSize); + } + + static constexpr size_t GetReturnPcOffset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - static_cast(kMipsPointerSize); + } +}; } // namespace mips } // namespace art -#endif // ART_RUNTIME_ARCH_MIPS_QUICK_METHOD_FRAME_INFO_MIPS_H_ +#endif // ART_RUNTIME_ARCH_MIPS_CALLEE_SAVE_FRAME_MIPS_H_ diff --git a/runtime/arch/mips/fault_handler_mips.cc b/runtime/arch/mips/fault_handler_mips.cc index d5a9b1589e..7c8ac288c3 100644 --- a/runtime/arch/mips/fault_handler_mips.cc +++ b/runtime/arch/mips/fault_handler_mips.cc @@ -17,13 +17,13 @@ #include #include "fault_handler.h" +#include "arch/mips/callee_save_frame_mips.h" #include "art_method.h" #include "base/callee_save_type.h" #include "base/globals.h" #include "base/hex_dump.h" #include "base/logging.h" // For VLOG. #include "base/macros.h" -#include "quick_method_frame_info_mips.h" #include "registers_mips.h" #include "thread-current-inl.h" diff --git a/runtime/arch/mips64/quick_method_frame_info_mips64.h b/runtime/arch/mips64/callee_save_frame_mips64.h similarity index 61% rename from runtime/arch/mips64/quick_method_frame_info_mips64.h rename to runtime/arch/mips64/callee_save_frame_mips64.h index 520f6319d5..59529a0c7b 100644 --- a/runtime/arch/mips64/quick_method_frame_info_mips64.h +++ b/runtime/arch/mips64/callee_save_frame_mips64.h @@ -14,13 +14,14 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_ARCH_MIPS64_QUICK_METHOD_FRAME_INFO_MIPS64_H_ -#define ART_RUNTIME_ARCH_MIPS64_QUICK_METHOD_FRAME_INFO_MIPS64_H_ +#ifndef ART_RUNTIME_ARCH_MIPS64_CALLEE_SAVE_FRAME_MIPS64_H_ +#define ART_RUNTIME_ARCH_MIPS64_CALLEE_SAVE_FRAME_MIPS64_H_ #include "arch/instruction_set.h" #include "base/bit_utils.h" #include "base/callee_save_type.h" #include "base/enums.h" +#include "base/globals.h" #include "quick/quick_method_frame_info.h" #include "registers_mips64.h" @@ -71,37 +72,56 @@ static constexpr uint32_t kMips64CalleeSaveFpEverythingSpills = (1 << art::mips64::F27) | (1 << art::mips64::F28) | (1 << art::mips64::F29) | (1 << art::mips64::F30) | (1 << art::mips64::F31); -constexpr uint32_t Mips64CalleeSaveCoreSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kMips64CalleeSaveAlwaysSpills | kMips64CalleeSaveRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kMips64CalleeSaveArgSpills : 0) | - (type == CalleeSaveType::kSaveAllCalleeSaves ? kMips64CalleeSaveAllSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kMips64CalleeSaveEverythingSpills : 0); -} +class Mips64CalleeSaveFrame { + public: + static constexpr uint32_t GetCoreSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kMips64CalleeSaveAlwaysSpills | kMips64CalleeSaveRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kMips64CalleeSaveArgSpills : 0) | + (type == CalleeSaveType::kSaveAllCalleeSaves ? kMips64CalleeSaveAllSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kMips64CalleeSaveEverythingSpills : 0); + } -constexpr uint32_t Mips64CalleeSaveFpSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kMips64CalleeSaveFpRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kMips64CalleeSaveFpArgSpills : 0) | - (type == CalleeSaveType::kSaveAllCalleeSaves ? kMips64CalleeSaveFpAllSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kMips64CalleeSaveFpEverythingSpills : 0); -} + static constexpr uint32_t GetFpSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kMips64CalleeSaveFpRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kMips64CalleeSaveFpArgSpills : 0) | + (type == CalleeSaveType::kSaveAllCalleeSaves ? kMips64CalleeSaveFpAllSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kMips64CalleeSaveFpEverythingSpills : 0); + } -constexpr uint32_t Mips64CalleeSaveFrameSize(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return RoundUp((POPCOUNT(Mips64CalleeSaveCoreSpills(type)) /* gprs */ + - POPCOUNT(Mips64CalleeSaveFpSpills(type)) /* fprs */ + - + 1 /* Method* */) * static_cast(kMips64PointerSize), kStackAlignment); -} + static constexpr uint32_t GetFrameSize(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return RoundUp((POPCOUNT(GetCoreSpills(type)) /* gprs */ + + POPCOUNT(GetFpSpills(type)) /* fprs */ + + + 1 /* Method* */) * static_cast(kMips64PointerSize), kStackAlignment); + } -constexpr QuickMethodFrameInfo Mips64CalleeSaveMethodFrameInfo(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return QuickMethodFrameInfo(Mips64CalleeSaveFrameSize(type), - Mips64CalleeSaveCoreSpills(type), - Mips64CalleeSaveFpSpills(type)); -} + static constexpr QuickMethodFrameInfo GetMethodFrameInfo(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return QuickMethodFrameInfo(GetFrameSize(type), GetCoreSpills(type), GetFpSpills(type)); + } + + static constexpr size_t GetFpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + (POPCOUNT(GetCoreSpills(type)) + + POPCOUNT(GetFpSpills(type))) * static_cast(kMips64PointerSize); + } + + static constexpr size_t GetGpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + POPCOUNT(GetCoreSpills(type)) * static_cast(kMips64PointerSize); + } + + static constexpr size_t GetReturnPcOffset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - static_cast(kMips64PointerSize); + } +}; } // namespace mips64 } // namespace art -#endif // ART_RUNTIME_ARCH_MIPS64_QUICK_METHOD_FRAME_INFO_MIPS64_H_ +#endif // ART_RUNTIME_ARCH_MIPS64_CALLEE_SAVE_FRAME_MIPS64_H_ diff --git a/runtime/arch/mips64/fault_handler_mips64.cc b/runtime/arch/mips64/fault_handler_mips64.cc index 695da4794b..85f3528ec4 100644 --- a/runtime/arch/mips64/fault_handler_mips64.cc +++ b/runtime/arch/mips64/fault_handler_mips64.cc @@ -18,13 +18,13 @@ #include +#include "arch/mips64/callee_save_frame_mips64.h" #include "art_method.h" #include "base/callee_save_type.h" #include "base/globals.h" #include "base/hex_dump.h" #include "base/logging.h" // For VLOG. #include "base/macros.h" -#include "quick_method_frame_info_mips64.h" #include "registers_mips64.h" #include "thread-current-inl.h" diff --git a/runtime/arch/x86/callee_save_frame_x86.h b/runtime/arch/x86/callee_save_frame_x86.h new file mode 100644 index 0000000000..f336f43aa3 --- /dev/null +++ b/runtime/arch/x86/callee_save_frame_x86.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_ARCH_X86_CALLEE_SAVE_FRAME_X86_H_ +#define ART_RUNTIME_ARCH_X86_CALLEE_SAVE_FRAME_X86_H_ + +#include "arch/instruction_set.h" +#include "base/bit_utils.h" +#include "base/callee_save_type.h" +#include "base/enums.h" +#include "base/globals.h" +#include "quick/quick_method_frame_info.h" +#include "registers_x86.h" + +namespace art { +namespace x86 { + +static constexpr uint32_t kX86CalleeSaveAlwaysSpills = + (1 << art::x86::kNumberOfCpuRegisters); // Fake return address callee save. +static constexpr uint32_t kX86CalleeSaveRefSpills = + (1 << art::x86::EBP) | (1 << art::x86::ESI) | (1 << art::x86::EDI); +static constexpr uint32_t kX86CalleeSaveArgSpills = + (1 << art::x86::ECX) | (1 << art::x86::EDX) | (1 << art::x86::EBX); +static constexpr uint32_t kX86CalleeSaveEverythingSpills = + (1 << art::x86::EAX) | (1 << art::x86::ECX) | (1 << art::x86::EDX) | (1 << art::x86::EBX); + +static constexpr uint32_t kX86CalleeSaveFpArgSpills = + (1 << art::x86::XMM0) | (1 << art::x86::XMM1) | + (1 << art::x86::XMM2) | (1 << art::x86::XMM3); +static constexpr uint32_t kX86CalleeSaveFpEverythingSpills = + (1 << art::x86::XMM0) | (1 << art::x86::XMM1) | + (1 << art::x86::XMM2) | (1 << art::x86::XMM3) | + (1 << art::x86::XMM4) | (1 << art::x86::XMM5) | + (1 << art::x86::XMM6) | (1 << art::x86::XMM7); + +class X86CalleeSaveFrame { + public: + static constexpr uint32_t GetCoreSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kX86CalleeSaveAlwaysSpills | kX86CalleeSaveRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kX86CalleeSaveArgSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kX86CalleeSaveEverythingSpills : 0); + } + + static constexpr uint32_t GetFpSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return (type == CalleeSaveType::kSaveRefsAndArgs ? kX86CalleeSaveFpArgSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kX86CalleeSaveFpEverythingSpills : 0); + } + + static constexpr uint32_t GetFrameSize(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return RoundUp((POPCOUNT(GetCoreSpills(type)) /* gprs */ + + 2 * POPCOUNT(GetFpSpills(type)) /* fprs */ + + 1 /* Method* */) * static_cast(kX86PointerSize), kStackAlignment); + } + + static constexpr QuickMethodFrameInfo GetMethodFrameInfo(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return QuickMethodFrameInfo(GetFrameSize(type), GetCoreSpills(type), GetFpSpills(type)); + } + + static constexpr size_t GetFpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + (POPCOUNT(GetCoreSpills(type)) + + 2 * POPCOUNT(GetFpSpills(type))) * static_cast(kX86PointerSize); + } + + static constexpr size_t GetGpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + POPCOUNT(GetCoreSpills(type)) * static_cast(kX86PointerSize); + } + + static constexpr size_t GetReturnPcOffset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - static_cast(kX86PointerSize); + } +}; + +} // namespace x86 +} // namespace art + +#endif // ART_RUNTIME_ARCH_X86_CALLEE_SAVE_FRAME_X86_H_ diff --git a/runtime/arch/x86/quick_method_frame_info_x86.h b/runtime/arch/x86/quick_method_frame_info_x86.h deleted file mode 100644 index 9a6633365c..0000000000 --- a/runtime/arch/x86/quick_method_frame_info_x86.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_ARCH_X86_QUICK_METHOD_FRAME_INFO_X86_H_ -#define ART_RUNTIME_ARCH_X86_QUICK_METHOD_FRAME_INFO_X86_H_ - -#include "arch/instruction_set.h" -#include "base/bit_utils.h" -#include "base/callee_save_type.h" -#include "base/enums.h" -#include "quick/quick_method_frame_info.h" -#include "registers_x86.h" - -namespace art { -namespace x86 { - -enum XMM { - XMM0 = 0, - XMM1 = 1, - XMM2 = 2, - XMM3 = 3, - XMM4 = 4, - XMM5 = 5, - XMM6 = 6, - XMM7 = 7, -}; - -static constexpr uint32_t kX86CalleeSaveAlwaysSpills = - (1 << art::x86::kNumberOfCpuRegisters); // Fake return address callee save. -static constexpr uint32_t kX86CalleeSaveRefSpills = - (1 << art::x86::EBP) | (1 << art::x86::ESI) | (1 << art::x86::EDI); -static constexpr uint32_t kX86CalleeSaveArgSpills = - (1 << art::x86::ECX) | (1 << art::x86::EDX) | (1 << art::x86::EBX); -static constexpr uint32_t kX86CalleeSaveEverythingSpills = - (1 << art::x86::EAX) | (1 << art::x86::ECX) | (1 << art::x86::EDX) | (1 << art::x86::EBX); - -static constexpr uint32_t kX86CalleeSaveFpArgSpills = - (1 << art::x86::XMM0) | (1 << art::x86::XMM1) | - (1 << art::x86::XMM2) | (1 << art::x86::XMM3); -static constexpr uint32_t kX86CalleeSaveFpEverythingSpills = - (1 << art::x86::XMM0) | (1 << art::x86::XMM1) | - (1 << art::x86::XMM2) | (1 << art::x86::XMM3) | - (1 << art::x86::XMM4) | (1 << art::x86::XMM5) | - (1 << art::x86::XMM6) | (1 << art::x86::XMM7); - -constexpr uint32_t X86CalleeSaveCoreSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kX86CalleeSaveAlwaysSpills | kX86CalleeSaveRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kX86CalleeSaveArgSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kX86CalleeSaveEverythingSpills : 0); -} - -constexpr uint32_t X86CalleeSaveFpSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return (type == CalleeSaveType::kSaveRefsAndArgs ? kX86CalleeSaveFpArgSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kX86CalleeSaveFpEverythingSpills : 0); -} - -constexpr uint32_t X86CalleeSaveFrameSize(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return RoundUp((POPCOUNT(X86CalleeSaveCoreSpills(type)) /* gprs */ + - 2 * POPCOUNT(X86CalleeSaveFpSpills(type)) /* fprs */ + - 1 /* Method* */) * static_cast(kX86PointerSize), kStackAlignment); -} - -constexpr QuickMethodFrameInfo X86CalleeSaveMethodFrameInfo(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return QuickMethodFrameInfo(X86CalleeSaveFrameSize(type), - X86CalleeSaveCoreSpills(type), - X86CalleeSaveFpSpills(type)); -} - -} // namespace x86 -} // namespace art - -#endif // ART_RUNTIME_ARCH_X86_QUICK_METHOD_FRAME_INFO_X86_H_ diff --git a/runtime/arch/x86/registers_x86.h b/runtime/arch/x86/registers_x86.h index 5a5d226319..d3b959fc53 100644 --- a/runtime/arch/x86/registers_x86.h +++ b/runtime/arch/x86/registers_x86.h @@ -42,6 +42,20 @@ enum Register { }; std::ostream& operator<<(std::ostream& os, const Register& rhs); +enum XmmRegister { + XMM0 = 0, + XMM1 = 1, + XMM2 = 2, + XMM3 = 3, + XMM4 = 4, + XMM5 = 5, + XMM6 = 6, + XMM7 = 7, + kNumberOfXmmRegisters = 8, + kNoXmmRegister = -1 // Signals an illegal register. +}; +std::ostream& operator<<(std::ostream& os, const XmmRegister& reg); + } // namespace x86 } // namespace art diff --git a/runtime/arch/x86_64/quick_method_frame_info_x86_64.h b/runtime/arch/x86_64/callee_save_frame_x86_64.h similarity index 54% rename from runtime/arch/x86_64/quick_method_frame_info_x86_64.h rename to runtime/arch/x86_64/callee_save_frame_x86_64.h index ebf976ef71..228a902d38 100644 --- a/runtime/arch/x86_64/quick_method_frame_info_x86_64.h +++ b/runtime/arch/x86_64/callee_save_frame_x86_64.h @@ -14,13 +14,14 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_ARCH_X86_64_QUICK_METHOD_FRAME_INFO_X86_64_H_ -#define ART_RUNTIME_ARCH_X86_64_QUICK_METHOD_FRAME_INFO_X86_64_H_ +#ifndef ART_RUNTIME_ARCH_X86_64_CALLEE_SAVE_FRAME_X86_64_H_ +#define ART_RUNTIME_ARCH_X86_64_CALLEE_SAVE_FRAME_X86_64_H_ #include "arch/instruction_set.h" #include "base/bit_utils.h" #include "base/callee_save_type.h" #include "base/enums.h" +#include "base/globals.h" #include "quick/quick_method_frame_info.h" #include "registers_x86_64.h" @@ -55,35 +56,54 @@ static constexpr uint32_t kX86_64CalleeSaveFpEverythingSpills = (1 << art::x86_64::XMM8) | (1 << art::x86_64::XMM9) | (1 << art::x86_64::XMM10) | (1 << art::x86_64::XMM11); -constexpr uint32_t X86_64CalleeSaveCoreSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kX86_64CalleeSaveAlwaysSpills | kX86_64CalleeSaveRefSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kX86_64CalleeSaveArgSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kX86_64CalleeSaveEverythingSpills : 0); -} +class X86_64CalleeSaveFrame { + public: + static constexpr uint32_t GetCoreSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kX86_64CalleeSaveAlwaysSpills | kX86_64CalleeSaveRefSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kX86_64CalleeSaveArgSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kX86_64CalleeSaveEverythingSpills : 0); + } -constexpr uint32_t X86_64CalleeSaveFpSpills(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return kX86_64CalleeSaveFpSpills | - (type == CalleeSaveType::kSaveRefsAndArgs ? kX86_64CalleeSaveFpArgSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kX86_64CalleeSaveFpEverythingSpills : 0); -} + static constexpr uint32_t GetFpSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return kX86_64CalleeSaveFpSpills | + (type == CalleeSaveType::kSaveRefsAndArgs ? kX86_64CalleeSaveFpArgSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kX86_64CalleeSaveFpEverythingSpills : 0); + } -constexpr uint32_t X86_64CalleeSaveFrameSize(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return RoundUp((POPCOUNT(X86_64CalleeSaveCoreSpills(type)) /* gprs */ + - POPCOUNT(X86_64CalleeSaveFpSpills(type)) /* fprs */ + - 1 /* Method* */) * static_cast(kX86_64PointerSize), kStackAlignment); -} + static constexpr uint32_t GetFrameSize(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return RoundUp((POPCOUNT(GetCoreSpills(type)) /* gprs */ + + POPCOUNT(GetFpSpills(type)) /* fprs */ + + 1 /* Method* */) * static_cast(kX86_64PointerSize), kStackAlignment); + } -constexpr QuickMethodFrameInfo X86_64CalleeSaveMethodFrameInfo(CalleeSaveType type) { - type = GetCanonicalCalleeSaveType(type); - return QuickMethodFrameInfo(X86_64CalleeSaveFrameSize(type), - X86_64CalleeSaveCoreSpills(type), - X86_64CalleeSaveFpSpills(type)); -} + static constexpr QuickMethodFrameInfo GetMethodFrameInfo(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return QuickMethodFrameInfo(GetFrameSize(type), GetCoreSpills(type), GetFpSpills(type)); + } + + static constexpr size_t GetFpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + (POPCOUNT(GetCoreSpills(type)) + + POPCOUNT(GetFpSpills(type))) * static_cast(kX86_64PointerSize); + } + + static constexpr size_t GetGpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - + POPCOUNT(GetCoreSpills(type)) * static_cast(kX86_64PointerSize); + } + + static constexpr size_t GetReturnPcOffset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); + return GetFrameSize(type) - static_cast(kX86_64PointerSize); + } +}; } // namespace x86_64 } // namespace art -#endif // ART_RUNTIME_ARCH_X86_64_QUICK_METHOD_FRAME_INFO_X86_64_H_ +#endif // ART_RUNTIME_ARCH_X86_64_CALLEE_SAVE_FRAME_X86_64_H_ diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index a58946ae66..7536910cee 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -180,10 +180,10 @@ static inline std::pair DoGetCalleeSaveMethodOuterCallerA ArtMethod** sp, CalleeSaveType type) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(type)); - const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA, type); + const size_t callee_frame_size = RuntimeCalleeSaveFrame::GetFrameSize(type); auto** caller_sp = reinterpret_cast( reinterpret_cast(sp) + callee_frame_size); - const size_t callee_return_pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, type); + const size_t callee_return_pc_offset = RuntimeCalleeSaveFrame::GetReturnPcOffset(type); uintptr_t caller_pc = *reinterpret_cast( (reinterpret_cast(sp) + callee_return_pc_offset)); ArtMethod* outer_method = *caller_sp; diff --git a/runtime/entrypoints/quick/callee_save_frame.h b/runtime/entrypoints/quick/callee_save_frame.h index ef27ca325c..6f1bbaa093 100644 --- a/runtime/entrypoints/quick/callee_save_frame.h +++ b/runtime/entrypoints/quick/callee_save_frame.h @@ -21,16 +21,17 @@ #include "base/callee_save_type.h" #include "base/enums.h" #include "base/mutex.h" +#include "quick/quick_method_frame_info.h" #include "thread-inl.h" // Specific frame size code is in architecture-specific files. We include this to compile-time // specialize the code. -#include "arch/arm/quick_method_frame_info_arm.h" -#include "arch/arm64/quick_method_frame_info_arm64.h" -#include "arch/mips/quick_method_frame_info_mips.h" -#include "arch/mips64/quick_method_frame_info_mips64.h" -#include "arch/x86/quick_method_frame_info_x86.h" -#include "arch/x86_64/quick_method_frame_info_x86_64.h" +#include "arch/arm/callee_save_frame_arm.h" +#include "arch/arm64/callee_save_frame_arm64.h" +#include "arch/mips/callee_save_frame_mips.h" +#include "arch/mips64/callee_save_frame_mips64.h" +#include "arch/x86/callee_save_frame_x86.h" +#include "arch/x86_64/callee_save_frame_x86_64.h" namespace art { class ArtMethod; @@ -67,57 +68,28 @@ class ScopedQuickEntrypointChecks { bool exit_check_; }; -static constexpr size_t GetCalleeSaveFrameSize(InstructionSet isa, CalleeSaveType type) { - switch (isa) { - case InstructionSet::kArm: - case InstructionSet::kThumb2: - return arm::ArmCalleeSaveFrameSize(type); - case InstructionSet::kArm64: - return arm64::Arm64CalleeSaveFrameSize(type); - case InstructionSet::kMips: - return mips::MipsCalleeSaveFrameSize(type); - case InstructionSet::kMips64: - return mips64::Mips64CalleeSaveFrameSize(type); - case InstructionSet::kX86: - return x86::X86CalleeSaveFrameSize(type); - case InstructionSet::kX86_64: - return x86_64::X86_64CalleeSaveFrameSize(type); - case InstructionSet::kNone: - LOG(FATAL) << "kNone has no frame size"; - UNREACHABLE(); - } - LOG(FATAL) << "Unknown ISA " << isa; - UNREACHABLE(); -} - -// Note: this specialized statement is sanity-checked in the quick-trampoline gtest. -static constexpr PointerSize GetConstExprPointerSize(InstructionSet isa) { - switch (isa) { - case InstructionSet::kArm: - case InstructionSet::kThumb2: - return kArmPointerSize; - case InstructionSet::kArm64: - return kArm64PointerSize; - case InstructionSet::kMips: - return kMipsPointerSize; - case InstructionSet::kMips64: - return kMips64PointerSize; - case InstructionSet::kX86: - return kX86PointerSize; - case InstructionSet::kX86_64: - return kX86_64PointerSize; - case InstructionSet::kNone: - LOG(FATAL) << "kNone has no pointer size"; - UNREACHABLE(); - } - LOG(FATAL) << "Unknown ISA " << isa; - UNREACHABLE(); -} - -// Note: this specialized statement is sanity-checked in the quick-trampoline gtest. -static constexpr size_t GetCalleeSaveReturnPcOffset(InstructionSet isa, CalleeSaveType type) { - return GetCalleeSaveFrameSize(isa, type) - static_cast(GetConstExprPointerSize(isa)); -} +namespace detail_ { + +template +struct CSFSelector; // No definition for unspecialized callee save frame selector. + +// Note: kThumb2 is never the kRuntimeISA. +template <> +struct CSFSelector { using type = arm::ArmCalleeSaveFrame; }; +template <> +struct CSFSelector { using type = arm64::Arm64CalleeSaveFrame; }; +template <> +struct CSFSelector { using type = mips::MipsCalleeSaveFrame; }; +template <> +struct CSFSelector { using type = mips64::Mips64CalleeSaveFrame; }; +template <> +struct CSFSelector { using type = x86::X86CalleeSaveFrame; }; +template <> +struct CSFSelector { using type = x86_64::X86_64CalleeSaveFrame; }; + +} // namespace detail_ + +using RuntimeCalleeSaveFrame = detail_::CSFSelector::type; } // namespace art diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 39429c5b41..d0aec0309d 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -26,6 +26,7 @@ #include "dex/dex_instruction-inl.h" #include "dex/method_reference.h" #include "entrypoints/entrypoint_utils-inl.h" +#include "entrypoints/quick/callee_save_frame.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/card_table-inl.h" #include "imt_conflict_table.h" @@ -59,7 +60,16 @@ class QuickArgumentVisitor { static constexpr size_t kBytesStackArgLocation = 4; // Frame size in bytes of a callee-save frame for RefsAndArgs. static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_FrameSize = - GetCalleeSaveFrameSize(kRuntimeISA, CalleeSaveType::kSaveRefsAndArgs); + RuntimeCalleeSaveFrame::GetFrameSize(CalleeSaveType::kSaveRefsAndArgs); + // Offset of first GPR arg. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = + RuntimeCalleeSaveFrame::GetGpr1Offset(CalleeSaveType::kSaveRefsAndArgs); + // Offset of first FPR arg. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = + RuntimeCalleeSaveFrame::GetFpr1Offset(CalleeSaveType::kSaveRefsAndArgs); + // Offset of return address. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_ReturnPcOffset = + RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveRefsAndArgs); #if defined(__arm__) // The callee save frame is pointed to by SP. // | argN | | @@ -87,12 +97,6 @@ class QuickArgumentVisitor { static constexpr size_t kNumQuickGprArgs = 3; static constexpr size_t kNumQuickFprArgs = 16; static constexpr bool kGprFprLockstep = false; - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = - arm::ArmCalleeSaveFpr1Offset(CalleeSaveType::kSaveRefsAndArgs); // Offset of first FPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = - arm::ArmCalleeSaveGpr1Offset(CalleeSaveType::kSaveRefsAndArgs); // Offset of first GPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = - arm::ArmCalleeSaveLrOffset(CalleeSaveType::kSaveRefsAndArgs); // Offset of return address. static size_t GprIndexToGprOffset(uint32_t gpr_index) { return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA); } @@ -125,15 +129,6 @@ class QuickArgumentVisitor { static constexpr size_t kNumQuickGprArgs = 7; // 7 arguments passed in GPRs. static constexpr size_t kNumQuickFprArgs = 8; // 8 arguments passed in FPRs. static constexpr bool kGprFprLockstep = false; - // Offset of first FPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = - arm64::Arm64CalleeSaveFpr1Offset(CalleeSaveType::kSaveRefsAndArgs); - // Offset of first GPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = - arm64::Arm64CalleeSaveGpr1Offset(CalleeSaveType::kSaveRefsAndArgs); - // Offset of return address. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = - arm64::Arm64CalleeSaveLrOffset(CalleeSaveType::kSaveRefsAndArgs); static size_t GprIndexToGprOffset(uint32_t gpr_index) { return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA); } @@ -177,9 +172,6 @@ class QuickArgumentVisitor { // passed only in even numbered registers and each // double occupies two registers. static constexpr bool kGprFprLockstep = false; - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 8; // Offset of first FPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 56; // Offset of first GPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 108; // Offset of return address. static size_t GprIndexToGprOffset(uint32_t gpr_index) { return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA); } @@ -221,9 +213,6 @@ class QuickArgumentVisitor { static constexpr size_t kNumQuickFprArgs = 7; // 7 arguments passed in FPRs. static constexpr bool kGprFprLockstep = true; - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 24; // Offset of first FPR arg (F13). - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 80; // Offset of first GPR arg (A1). - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 200; // Offset of return address. static size_t GprIndexToGprOffset(uint32_t gpr_index) { return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA); } @@ -254,9 +243,6 @@ class QuickArgumentVisitor { static constexpr size_t kNumQuickGprArgs = 3; // 3 arguments passed in GPRs. static constexpr size_t kNumQuickFprArgs = 4; // 4 arguments passed in FPRs. static constexpr bool kGprFprLockstep = false; - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 4; // Offset of first FPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 4 + 4*8; // Offset of first GPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 28 + 4*8; // Offset of return address. static size_t GprIndexToGprOffset(uint32_t gpr_index) { return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA); } @@ -296,9 +282,6 @@ class QuickArgumentVisitor { static constexpr size_t kNumQuickGprArgs = 5; // 5 arguments passed in GPRs. static constexpr size_t kNumQuickFprArgs = 8; // 8 arguments passed in FPRs. static constexpr bool kGprFprLockstep = false; - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 16; // Offset of first FPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 80 + 4*8; // Offset of first GPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 168 + 4*8; // Offset of return address. static size_t GprIndexToGprOffset(uint32_t gpr_index) { switch (gpr_index) { case 0: return (4 * GetBytesPerGprSpillLocation(kRuntimeISA)); @@ -345,8 +328,8 @@ class QuickArgumentVisitor { static uint32_t GetCallingDexPc(ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK((*sp)->IsCalleeSaveMethod()); - const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA, - CalleeSaveType::kSaveRefsAndArgs); + constexpr size_t callee_frame_size = + RuntimeCalleeSaveFrame::GetFrameSize(CalleeSaveType::kSaveRefsAndArgs); ArtMethod** caller_sp = reinterpret_cast( reinterpret_cast(sp) + callee_frame_size); uintptr_t outer_pc = QuickArgumentVisitor::GetCallingPc(sp); @@ -373,8 +356,8 @@ class QuickArgumentVisitor { static bool GetInvokeType(ArtMethod** sp, InvokeType* invoke_type, uint32_t* dex_method_index) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK((*sp)->IsCalleeSaveMethod()); - const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA, - CalleeSaveType::kSaveRefsAndArgs); + constexpr size_t callee_frame_size = + RuntimeCalleeSaveFrame::GetFrameSize(CalleeSaveType::kSaveRefsAndArgs); ArtMethod** caller_sp = reinterpret_cast( reinterpret_cast(sp) + callee_frame_size); uintptr_t outer_pc = QuickArgumentVisitor::GetCallingPc(sp); @@ -398,8 +381,9 @@ class QuickArgumentVisitor { // For the given quick ref and args quick frame, return the caller's PC. static uintptr_t GetCallingPc(ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK((*sp)->IsCalleeSaveMethod()); - uint8_t* lr = reinterpret_cast(sp) + kQuickCalleeSaveFrame_RefAndArgs_LrOffset; - return *reinterpret_cast(lr); + uint8_t* return_adress_spill = + reinterpret_cast(sp) + kQuickCalleeSaveFrame_RefAndArgs_ReturnPcOffset; + return *reinterpret_cast(return_adress_spill); } QuickArgumentVisitor(ArtMethod** sp, bool is_static, const char* shorty, @@ -1157,8 +1141,8 @@ extern "C" TwoWordReturn artInstrumentationMethodExitFromCode(Thread* self, CHECK(!self->IsExceptionPending()) << "Enter instrumentation exit stub with pending exception " << self->GetException()->Dump(); // Compute address of return PC and sanity check that it currently holds 0. - size_t return_pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, - CalleeSaveType::kSaveEverything); + constexpr size_t return_pc_offset = + RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveEverything); uintptr_t* return_pc = reinterpret_cast(reinterpret_cast(sp) + return_pc_offset); CHECK_EQ(*return_pc, 0U); @@ -1210,10 +1194,10 @@ static void DumpB74410240DebugData(ArtMethod** sp) REQUIRES_SHARED(Locks::mutato constexpr CalleeSaveType type = CalleeSaveType::kSaveRefsAndArgs; CHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(type)); - const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA, type); + constexpr size_t callee_frame_size = RuntimeCalleeSaveFrame::GetFrameSize(type); auto** caller_sp = reinterpret_cast( reinterpret_cast(sp) + callee_frame_size); - const size_t callee_return_pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, type); + constexpr size_t callee_return_pc_offset = RuntimeCalleeSaveFrame::GetReturnPcOffset(type); uintptr_t caller_pc = *reinterpret_cast( (reinterpret_cast(sp) + callee_return_pc_offset)); ArtMethod* outer_method = *caller_sp; diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc index 77b3132bdc..89694e351a 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc @@ -54,15 +54,6 @@ class QuickTrampolineEntrypointsTest : public CommonRuntimeTest { return save_method; } - static void CheckFrameSize(InstructionSet isa, CalleeSaveType type, uint32_t save_size) - NO_THREAD_SAFETY_ANALYSIS { - ArtMethod* save_method = CreateCalleeSaveMethod(isa, type); - QuickMethodFrameInfo frame_info = Runtime::Current()->GetRuntimeMethodFrameInfo(save_method); - EXPECT_EQ(frame_info.FrameSizeInBytes(), save_size) << "Expected and real size differs for " - << type << " core spills=" << std::hex << frame_info.CoreSpillMask() << " fp spills=" - << frame_info.FpSpillMask() << std::dec << " ISA " << isa; - } - static void CheckPCOffset(InstructionSet isa, CalleeSaveType type, size_t pc_offset) NO_THREAD_SAFETY_ANALYSIS { ArtMethod* save_method = CreateCalleeSaveMethod(isa, type); @@ -74,79 +65,36 @@ class QuickTrampolineEntrypointsTest : public CommonRuntimeTest { } }; -// Note: these tests are all runtime tests. They let the Runtime create the corresponding ArtMethod -// and check against it. Technically we know and expect certain values, but the Runtime code is -// not constexpr, so we cannot make this compile-time checks (and I want the Runtime code tested). - -// This test ensures that kQuickCalleeSaveFrame_RefAndArgs_FrameSize is correct. -TEST_F(QuickTrampolineEntrypointsTest, FrameSize) { - // We have to use a define here as the callee_save_frame.h functions are constexpr. -#define CHECK_FRAME_SIZE(isa) \ - CheckFrameSize(isa, \ - CalleeSaveType::kSaveRefsAndArgs, \ - GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveRefsAndArgs)); \ - CheckFrameSize(isa, \ - CalleeSaveType::kSaveRefsOnly, \ - GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveRefsOnly)); \ - CheckFrameSize(isa, \ - CalleeSaveType::kSaveAllCalleeSaves, \ - GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveAllCalleeSaves)); \ - CheckFrameSize(isa, \ - CalleeSaveType::kSaveEverything, \ - GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveEverything)); \ - CheckFrameSize(isa, \ - CalleeSaveType::kSaveEverythingForClinit, \ - GetCalleeSaveFrameSize(isa, \ - CalleeSaveType::kSaveEverythingForClinit)); \ - CheckFrameSize(isa, \ - CalleeSaveType::kSaveEverythingForSuspendCheck, \ - GetCalleeSaveFrameSize( \ - isa, CalleeSaveType::kSaveEverythingForSuspendCheck)) - - CHECK_FRAME_SIZE(InstructionSet::kArm); - CHECK_FRAME_SIZE(InstructionSet::kArm64); - CHECK_FRAME_SIZE(InstructionSet::kMips); - CHECK_FRAME_SIZE(InstructionSet::kMips64); - CHECK_FRAME_SIZE(InstructionSet::kX86); - CHECK_FRAME_SIZE(InstructionSet::kX86_64); -} - -// This test ensures that GetConstExprPointerSize is correct with respect to -// GetInstructionSetPointerSize. -TEST_F(QuickTrampolineEntrypointsTest, PointerSize) { - EXPECT_EQ(GetInstructionSetPointerSize(InstructionSet::kArm), - GetConstExprPointerSize(InstructionSet::kArm)); - EXPECT_EQ(GetInstructionSetPointerSize(InstructionSet::kArm64), - GetConstExprPointerSize(InstructionSet::kArm64)); - EXPECT_EQ(GetInstructionSetPointerSize(InstructionSet::kMips), - GetConstExprPointerSize(InstructionSet::kMips)); - EXPECT_EQ(GetInstructionSetPointerSize(InstructionSet::kMips64), - GetConstExprPointerSize(InstructionSet::kMips64)); - EXPECT_EQ(GetInstructionSetPointerSize(InstructionSet::kX86), - GetConstExprPointerSize(InstructionSet::kX86)); - EXPECT_EQ(GetInstructionSetPointerSize(InstructionSet::kX86_64), - GetConstExprPointerSize(InstructionSet::kX86_64)); -} - // This test ensures that the constexpr specialization of the return PC offset computation in // GetCalleeSavePCOffset is correct. TEST_F(QuickTrampolineEntrypointsTest, ReturnPC) { // Ensure that the computation in callee_save_frame.h correct. // Note: we can only check against the kRuntimeISA, because the ArtMethod computation uses // sizeof(void*), which is wrong when the target bitwidth is not the same as the host's. - CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveRefsAndArgs, - GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveRefsAndArgs)); - CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveRefsOnly, - GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveRefsOnly)); - CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveAllCalleeSaves, - GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveAllCalleeSaves)); - CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveEverything, - GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveEverything)); - CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveEverythingForClinit, - GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveEverythingForClinit)); - CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveEverythingForSuspendCheck, - GetCalleeSaveReturnPcOffset(kRuntimeISA, - CalleeSaveType::kSaveEverythingForSuspendCheck)); + CheckPCOffset( + kRuntimeISA, + CalleeSaveType::kSaveRefsAndArgs, + RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveRefsAndArgs)); + CheckPCOffset( + kRuntimeISA, + CalleeSaveType::kSaveRefsOnly, + RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveRefsOnly)); + CheckPCOffset( + kRuntimeISA, + CalleeSaveType::kSaveAllCalleeSaves, + RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveAllCalleeSaves)); + CheckPCOffset( + kRuntimeISA, + CalleeSaveType::kSaveEverything, + RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveEverything)); + CheckPCOffset( + kRuntimeISA, + CalleeSaveType::kSaveEverythingForClinit, + RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveEverythingForClinit)); + CheckPCOffset( + kRuntimeISA, + CalleeSaveType::kSaveEverythingForSuspendCheck, + RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveEverythingForSuspendCheck)); } } // namespace art diff --git a/runtime/runtime-inl.h b/runtime/runtime-inl.h index 4584351a6a..374591eeb0 100644 --- a/runtime/runtime-inl.h +++ b/runtime/runtime-inl.h @@ -19,8 +19,10 @@ #include "runtime.h" +#include "arch/instruction_set.h" #include "art_method.h" #include "base/callee_save_type.h" +#include "entrypoints/quick/callee_save_frame.h" #include "gc_root-inl.h" #include "obj_ptr-inl.h" @@ -38,21 +40,22 @@ inline mirror::Object* Runtime::GetClearedJniWeakGlobal() { inline QuickMethodFrameInfo Runtime::GetRuntimeMethodFrameInfo(ArtMethod* method) { DCHECK(method != nullptr); + DCHECK_EQ(instruction_set_, kRuntimeISA); // Cannot be imt-conflict-method or resolution-method. DCHECK_NE(method, GetImtConflictMethod()); DCHECK_NE(method, GetResolutionMethod()); // Don't use GetCalleeSaveMethod(), some tests don't set all callee save methods. if (method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveRefsAndArgs)) { - return GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); + return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); } else if (method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveAllCalleeSaves)) { - return GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveAllCalleeSaves); + return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveAllCalleeSaves); } else if (method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveRefsOnly)) { - return GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsOnly); + return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsOnly); } else { DCHECK(method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveEverything) || method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveEverythingForClinit) || method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveEverythingForSuspendCheck)); - return GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveEverything); + return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveEverything); } } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index b8775b874f..5d974246ff 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -39,18 +39,12 @@ #include "android-base/strings.h" #include "aot_class_linker.h" -#include "arch/arm/quick_method_frame_info_arm.h" #include "arch/arm/registers_arm.h" -#include "arch/arm64/quick_method_frame_info_arm64.h" #include "arch/arm64/registers_arm64.h" #include "arch/instruction_set_features.h" -#include "arch/mips/quick_method_frame_info_mips.h" #include "arch/mips/registers_mips.h" -#include "arch/mips64/quick_method_frame_info_mips64.h" #include "arch/mips64/registers_mips64.h" -#include "arch/x86/quick_method_frame_info_x86.h" #include "arch/x86/registers_x86.h" -#include "arch/x86_64/quick_method_frame_info_x86_64.h" #include "arch/x86_64/registers_x86_64.h" #include "art_field-inl.h" #include "art_method-inl.h" @@ -2203,38 +2197,21 @@ void Runtime::BroadcastForNewSystemWeaks(bool broadcast_for_checkpoint) { void Runtime::SetInstructionSet(InstructionSet instruction_set) { instruction_set_ = instruction_set; - if ((instruction_set_ == InstructionSet::kThumb2) || (instruction_set_ == InstructionSet::kArm)) { - for (int i = 0; i != kCalleeSaveSize; ++i) { - CalleeSaveType type = static_cast(i); - callee_save_method_frame_infos_[i] = arm::ArmCalleeSaveMethodFrameInfo(type); - } - } else if (instruction_set_ == InstructionSet::kMips) { - for (int i = 0; i != kCalleeSaveSize; ++i) { - CalleeSaveType type = static_cast(i); - callee_save_method_frame_infos_[i] = mips::MipsCalleeSaveMethodFrameInfo(type); - } - } else if (instruction_set_ == InstructionSet::kMips64) { - for (int i = 0; i != kCalleeSaveSize; ++i) { - CalleeSaveType type = static_cast(i); - callee_save_method_frame_infos_[i] = mips64::Mips64CalleeSaveMethodFrameInfo(type); - } - } else if (instruction_set_ == InstructionSet::kX86) { - for (int i = 0; i != kCalleeSaveSize; ++i) { - CalleeSaveType type = static_cast(i); - callee_save_method_frame_infos_[i] = x86::X86CalleeSaveMethodFrameInfo(type); - } - } else if (instruction_set_ == InstructionSet::kX86_64) { - for (int i = 0; i != kCalleeSaveSize; ++i) { - CalleeSaveType type = static_cast(i); - callee_save_method_frame_infos_[i] = x86_64::X86_64CalleeSaveMethodFrameInfo(type); - } - } else if (instruction_set_ == InstructionSet::kArm64) { - for (int i = 0; i != kCalleeSaveSize; ++i) { - CalleeSaveType type = static_cast(i); - callee_save_method_frame_infos_[i] = arm64::Arm64CalleeSaveMethodFrameInfo(type); - } - } else { - UNIMPLEMENTED(FATAL) << instruction_set_; + switch (instruction_set) { + case InstructionSet::kThumb2: + // kThumb2 is the same as kArm, use the canonical value. + instruction_set_ = InstructionSet::kArm; + break; + case InstructionSet::kArm: + case InstructionSet::kArm64: + case InstructionSet::kMips: + case InstructionSet::kMips64: + case InstructionSet::kX86: + case InstructionSet::kX86_64: + break; + default: + UNIMPLEMENTED(FATAL) << instruction_set_; + UNREACHABLE(); } } diff --git a/runtime/runtime.h b/runtime/runtime.h index 953acbb948..10f72e7c5b 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -399,10 +399,6 @@ class Runtime { ArtMethod* GetCalleeSaveMethodUnchecked(CalleeSaveType type) REQUIRES_SHARED(Locks::mutator_lock_); - QuickMethodFrameInfo GetCalleeSaveMethodFrameInfo(CalleeSaveType type) const { - return callee_save_method_frame_infos_[static_cast(type)]; - } - QuickMethodFrameInfo GetRuntimeMethodFrameInfo(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); @@ -831,7 +827,6 @@ class Runtime { GcRoot sentinel_; InstructionSet instruction_set_; - QuickMethodFrameInfo callee_save_method_frame_infos_[kCalleeSaveSize]; CompilerCallbacks* compiler_callbacks_; bool is_zygote_; diff --git a/runtime/stack.cc b/runtime/stack.cc index 229238e0f7..c58380dee2 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -25,6 +25,7 @@ #include "base/hex_dump.h" #include "dex/dex_file_types.h" #include "entrypoints/entrypoint_utils-inl.h" +#include "entrypoints/quick/callee_save_frame.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/space/image_space.h" #include "gc/space/space-inl.h" @@ -718,7 +719,7 @@ QuickMethodFrameInfo StackVisitor::GetCurrentQuickFrameInfo() const { Runtime* runtime = Runtime::Current(); if (method->IsAbstract()) { - return runtime->GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); + return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); } // This goes before IsProxyMethod since runtime methods have a null declaring class. @@ -732,7 +733,7 @@ QuickMethodFrameInfo StackVisitor::GetCurrentQuickFrameInfo() const { // compiled method without any stubs. Therefore the method must have a OatQuickMethodHeader. DCHECK(!method->IsDirect() && !method->IsConstructor()) << "Constructors of proxy classes must have a OatQuickMethodHeader"; - return runtime->GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); + return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); } // The only remaining case is if the method is native and uses the generic JNI stub, @@ -751,8 +752,8 @@ QuickMethodFrameInfo StackVisitor::GetCurrentQuickFrameInfo() const { // Generic JNI frame. uint32_t handle_refs = GetNumberOfReferenceArgsWithoutReceiver(method) + 1; size_t scope_size = HandleScope::SizeOf(handle_refs); - QuickMethodFrameInfo callee_info = - runtime->GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); + constexpr QuickMethodFrameInfo callee_info = + RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); // Callee saves + handle scope + method ref + alignment // Note: -sizeof(void*) since callee-save frame stores a whole method pointer. -- GitLab From 2d3065e6ca0bd707bc998b7d260bb8e8ec07cf87 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 22 May 2018 13:56:09 +0100 Subject: [PATCH 440/749] ObjPtr<>-ify annotation processing. Test: Rely on TreeHugger. Bug: 31113334 Change-Id: Ifd69c15c0df1530d8860cf50e06bde0d356b0c23 --- runtime/dex/dex_file_annotations.cc | 117 ++++++++++-------- runtime/dex/dex_file_annotations.h | 37 +++--- runtime/interpreter/unstarted_runtime.cc | 2 +- runtime/native/java_lang_Class.cc | 8 +- .../native/java_lang_reflect_Constructor.cc | 2 +- runtime/native/java_lang_reflect_Method.cc | 2 +- 6 files changed, 89 insertions(+), 79 deletions(-) diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc index c399b1c7fe..95b42d27d2 100644 --- a/runtime/dex/dex_file_annotations.cc +++ b/runtime/dex/dex_file_annotations.cc @@ -29,6 +29,7 @@ #include "mirror/field.h" #include "mirror/method.h" #include "oat_file.h" +#include "obj_ptr-inl.h" #include "reflection.h" #include "thread.h" #include "well_known_classes.h" @@ -116,9 +117,9 @@ class ClassData { DISALLOW_COPY_AND_ASSIGN(ClassData); }; -mirror::Object* CreateAnnotationMember(const ClassData& klass, - Handle annotation_class, - const uint8_t** annotation) +ObjPtr CreateAnnotationMember(const ClassData& klass, + Handle annotation_class, + const uint8_t** annotation) REQUIRES_SHARED(Locks::mutator_lock_); bool IsVisibilityCompatible(uint32_t actual, uint32_t expected) { @@ -333,7 +334,7 @@ const DexFile::AnnotationSetItem* FindAnnotationSetForClass(const ClassData& kla return dex_file.GetClassAnnotationSet(annotations_dir); } -mirror::Object* ProcessEncodedAnnotation(const ClassData& klass, const uint8_t** annotation) +ObjPtr ProcessEncodedAnnotation(const ClassData& klass, const uint8_t** annotation) REQUIRES_SHARED(Locks::mutator_lock_) { uint32_t type_index = DecodeUnsignedLeb128(annotation); uint32_t size = DecodeUnsignedLeb128(annotation); @@ -355,13 +356,13 @@ mirror::Object* ProcessEncodedAnnotation(const ClassData& klass, const uint8_t** } ObjPtr annotation_member_class = - soa.Decode(WellKnownClasses::libcore_reflect_AnnotationMember).Ptr(); - mirror::Class* annotation_member_array_class = + soa.Decode(WellKnownClasses::libcore_reflect_AnnotationMember); + ObjPtr annotation_member_array_class = class_linker->FindArrayClass(self, &annotation_member_class); if (annotation_member_array_class == nullptr) { return nullptr; } - mirror::ObjectArray* element_array = nullptr; + ObjPtr> element_array = nullptr; if (size > 0) { element_array = mirror::ObjectArray::Alloc(self, annotation_member_array_class, size); @@ -373,7 +374,7 @@ mirror::Object* ProcessEncodedAnnotation(const ClassData& klass, const uint8_t** Handle> h_element_array(hs.NewHandle(element_array)); for (uint32_t i = 0; i < size; ++i) { - mirror::Object* new_member = CreateAnnotationMember(klass, annotation_class, annotation); + ObjPtr new_member = CreateAnnotationMember(klass, annotation_class, annotation); if (new_member == nullptr) { return nullptr; } @@ -605,7 +606,7 @@ bool ProcessAnnotationValue(const ClassData& klass, return false; } if (!component_type->IsPrimitive()) { - mirror::Object* obj = new_annotation_value.value_.GetL(); + ObjPtr obj = new_annotation_value.value_.GetL(); new_array->AsObjectArray()-> SetWithoutChecks(i, obj); } else { @@ -682,20 +683,20 @@ bool ProcessAnnotationValue(const ClassData& klass, *annotation_ptr = annotation; if (result_style == DexFile::kAllObjects && primitive_type != Primitive::kPrimVoid) { - element_object = BoxPrimitive(primitive_type, annotation_value->value_).Ptr(); + element_object = BoxPrimitive(primitive_type, annotation_value->value_); set_object = true; } if (set_object) { - annotation_value->value_.SetL(element_object.Ptr()); + annotation_value->value_.SetL(element_object); } return true; } -mirror::Object* CreateAnnotationMember(const ClassData& klass, - Handle annotation_class, - const uint8_t** annotation) { +ObjPtr CreateAnnotationMember(const ClassData& klass, + Handle annotation_class, + const uint8_t** annotation) { const DexFile& dex_file = klass.GetDexFile(); Thread* self = Thread::Current(); ScopedObjectAccessUnchecked soa(self); @@ -799,7 +800,7 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet( return nullptr; } -mirror::Object* GetAnnotationObjectFromAnnotationSet( +ObjPtr GetAnnotationObjectFromAnnotationSet( const ClassData& klass, const DexFile::AnnotationSetItem* annotation_set, uint32_t visibility, @@ -814,11 +815,11 @@ mirror::Object* GetAnnotationObjectFromAnnotationSet( return ProcessEncodedAnnotation(klass, &annotation); } -mirror::Object* GetAnnotationValue(const ClassData& klass, - const DexFile::AnnotationItem* annotation_item, - const char* annotation_name, - Handle array_class, - uint32_t expected_type) +ObjPtr GetAnnotationValue(const ClassData& klass, + const DexFile::AnnotationItem* annotation_item, + const char* annotation_name, + Handle array_class, + uint32_t expected_type) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile& dex_file = klass.GetDexFile(); const uint8_t* annotation = @@ -864,7 +865,7 @@ mirror::ObjectArray* GetSignatureValue(const ClassData& klass, if (string_array_class == nullptr) { return nullptr; } - mirror::Object* obj = + ObjPtr obj = GetAnnotationValue(klass, annotation_item, "value", string_array_class, DexFile::kDexAnnotationArray); if (obj == nullptr) { @@ -873,8 +874,9 @@ mirror::ObjectArray* GetSignatureValue(const ClassData& klass, return obj->AsObjectArray(); } -mirror::ObjectArray* GetThrowsValue(const ClassData& klass, - const DexFile::AnnotationSetItem* annotation_set) +ObjPtr> GetThrowsValue( + const ClassData& klass, + const DexFile::AnnotationSetItem* annotation_set) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile& dex_file = klass.GetDexFile(); StackHandleScope<1> hs(Thread::Current()); @@ -890,7 +892,7 @@ mirror::ObjectArray* GetThrowsValue(const ClassData& klass, if (class_array_class == nullptr) { return nullptr; } - mirror::Object* obj = + ObjPtr obj = GetAnnotationValue(klass, annotation_item, "value", class_array_class, DexFile::kDexAnnotationArray); if (obj == nullptr) { @@ -899,7 +901,7 @@ mirror::ObjectArray* GetThrowsValue(const ClassData& klass, return obj->AsObjectArray(); } -mirror::ObjectArray* ProcessAnnotationSet( +ObjPtr> ProcessAnnotationSet( const ClassData& klass, const DexFile::AnnotationSetItem* annotation_set, uint32_t visibility) @@ -930,7 +932,7 @@ mirror::ObjectArray* ProcessAnnotationSet( continue; } const uint8_t* annotation = annotation_item->annotation_; - mirror::Object* annotation_obj = ProcessEncodedAnnotation(klass, &annotation); + ObjPtr annotation_obj = ProcessEncodedAnnotation(klass, &annotation); if (annotation_obj != nullptr) { result->SetWithoutChecks(dest_index, annotation_obj); ++dest_index; @@ -943,21 +945,21 @@ mirror::ObjectArray* ProcessAnnotationSet( return result.Get(); } - mirror::ObjectArray* trimmed_result = + ObjPtr> trimmed_result = mirror::ObjectArray::Alloc(self, annotation_array_class.Get(), dest_index); if (trimmed_result == nullptr) { return nullptr; } for (uint32_t i = 0; i < dest_index; ++i) { - mirror::Object* obj = result->GetWithoutChecks(i); + ObjPtr obj = result->GetWithoutChecks(i); trimmed_result->SetWithoutChecks(i, obj); } return trimmed_result; } -mirror::ObjectArray* ProcessAnnotationSetRefList( +ObjPtr> ProcessAnnotationSetRefList( const ClassData& klass, const DexFile::AnnotationSetRefList* set_ref_list, uint32_t size) @@ -968,7 +970,7 @@ mirror::ObjectArray* ProcessAnnotationSetRefList( StackHandleScope<1> hs(self); ObjPtr annotation_array_class = soa.Decode(WellKnownClasses::java_lang_annotation_Annotation__array); - mirror::Class* annotation_array_array_class = + ObjPtr annotation_array_array_class = Runtime::Current()->GetClassLinker()->FindArrayClass(self, &annotation_array_class); if (annotation_array_array_class == nullptr) { return nullptr; @@ -982,8 +984,9 @@ mirror::ObjectArray* ProcessAnnotationSetRefList( for (uint32_t index = 0; index < size; ++index) { const DexFile::AnnotationSetRefItem* set_ref_item = &set_ref_list->list_[index]; const DexFile::AnnotationSetItem* set_item = dex_file.GetSetRefItemItem(set_ref_item); - mirror::Object* annotation_set = ProcessAnnotationSet(klass, set_item, - DexFile::kDexVisibilityRuntime); + ObjPtr annotation_set = ProcessAnnotationSet(klass, + set_item, + DexFile::kDexVisibilityRuntime); if (annotation_set == nullptr) { return nullptr; } @@ -995,7 +998,8 @@ mirror::ObjectArray* ProcessAnnotationSetRefList( namespace annotations { -mirror::Object* GetAnnotationForField(ArtField* field, Handle annotation_class) { +ObjPtr GetAnnotationForField(ArtField* field, + Handle annotation_class) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForField(field); if (annotation_set == nullptr) { return nullptr; @@ -1008,7 +1012,7 @@ mirror::Object* GetAnnotationForField(ArtField* field, Handle ann annotation_class); } -mirror::ObjectArray* GetAnnotationsForField(ArtField* field) { +ObjPtr> GetAnnotationsForField(ArtField* field) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForField(field); StackHandleScope<1> hs(Thread::Current()); const ClassData field_class(hs, field); @@ -1037,7 +1041,7 @@ bool IsFieldAnnotationPresent(ArtField* field, Handle annotation_ return annotation_item != nullptr; } -mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) { +ObjPtr GetAnnotationDefaultValue(ArtMethod* method) { const ClassData klass(method); const DexFile* dex_file = &klass.GetDexFile(); const DexFile::AnnotationsDirectoryItem* annotations_dir = @@ -1081,7 +1085,8 @@ mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) { return annotation_value.value_.GetL(); } -mirror::Object* GetAnnotationForMethod(ArtMethod* method, Handle annotation_class) { +ObjPtr GetAnnotationForMethod(ArtMethod* method, + Handle annotation_class) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method); if (annotation_set == nullptr) { return nullptr; @@ -1090,14 +1095,14 @@ mirror::Object* GetAnnotationForMethod(ArtMethod* method, Handle DexFile::kDexVisibilityRuntime, annotation_class); } -mirror::ObjectArray* GetAnnotationsForMethod(ArtMethod* method) { +ObjPtr> GetAnnotationsForMethod(ArtMethod* method) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method); return ProcessAnnotationSet(ClassData(method), annotation_set, DexFile::kDexVisibilityRuntime); } -mirror::ObjectArray* GetExceptionTypesForMethod(ArtMethod* method) { +ObjPtr> GetExceptionTypesForMethod(ArtMethod* method) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method); if (annotation_set == nullptr) { return nullptr; @@ -1105,7 +1110,7 @@ mirror::ObjectArray* GetExceptionTypesForMethod(ArtMethod* method return GetThrowsValue(ClassData(method), annotation_set); } -mirror::ObjectArray* GetParameterAnnotations(ArtMethod* method) { +ObjPtr> GetParameterAnnotations(ArtMethod* method) { const DexFile* dex_file = method->GetDexFile(); const DexFile::ParameterAnnotationsItem* parameter_annotations = FindAnnotationsItemForMethod(method); @@ -1136,9 +1141,9 @@ uint32_t GetNumberOfAnnotatedMethodParameters(ArtMethod* method) { return set_ref_list->size_; } -mirror::Object* GetAnnotationForMethodParameter(ArtMethod* method, - uint32_t parameter_idx, - Handle annotation_class) { +ObjPtr GetAnnotationForMethodParameter(ArtMethod* method, + uint32_t parameter_idx, + Handle annotation_class) { const DexFile* dex_file = method->GetDexFile(); const DexFile::ParameterAnnotationsItem* parameter_annotations = FindAnnotationsItemForMethod(method); @@ -1307,8 +1312,8 @@ uint32_t GetNativeMethodAnnotationAccessFlags(const DexFile& dex_file, return access_flags; } -mirror::Object* GetAnnotationForClass(Handle klass, - Handle annotation_class) { +ObjPtr GetAnnotationForClass(Handle klass, + Handle annotation_class) { ClassData data(klass); const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { @@ -1320,13 +1325,13 @@ mirror::Object* GetAnnotationForClass(Handle klass, annotation_class); } -mirror::ObjectArray* GetAnnotationsForClass(Handle klass) { +ObjPtr> GetAnnotationsForClass(Handle klass) { ClassData data(klass); const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); return ProcessAnnotationSet(data, annotation_set, DexFile::kDexVisibilityRuntime); } -mirror::ObjectArray* GetDeclaredClasses(Handle klass) { +ObjPtr> GetDeclaredClasses(Handle klass) { ClassData data(klass); const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { @@ -1345,7 +1350,7 @@ mirror::ObjectArray* GetDeclaredClasses(Handle kla if (class_array_class == nullptr) { return nullptr; } - mirror::Object* obj = + ObjPtr obj = GetAnnotationValue(data, annotation_item, "value", class_array_class, DexFile::kDexAnnotationArray); if (obj == nullptr) { @@ -1354,7 +1359,7 @@ mirror::ObjectArray* GetDeclaredClasses(Handle kla return obj->AsObjectArray(); } -mirror::Class* GetDeclaringClass(Handle klass) { +ObjPtr GetDeclaringClass(Handle klass) { ClassData data(klass); const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { @@ -1366,17 +1371,19 @@ mirror::Class* GetDeclaringClass(Handle klass) { if (annotation_item == nullptr) { return nullptr; } - mirror::Object* obj = GetAnnotationValue(data, annotation_item, "value", - ScopedNullHandle(), - DexFile::kDexAnnotationType); + ObjPtr obj = GetAnnotationValue(data, + annotation_item, + "value", + ScopedNullHandle(), + DexFile::kDexAnnotationType); if (obj == nullptr) { return nullptr; } return obj->AsClass(); } -mirror::Class* GetEnclosingClass(Handle klass) { - mirror::Class* declaring_class = GetDeclaringClass(klass); +ObjPtr GetEnclosingClass(Handle klass) { + ObjPtr declaring_class = GetDeclaringClass(klass); if (declaring_class != nullptr) { return declaring_class; } @@ -1420,7 +1427,7 @@ mirror::Class* GetEnclosingClass(Handle klass) { return method->GetDeclaringClass(); } -mirror::Object* GetEnclosingMethod(Handle klass) { +ObjPtr GetEnclosingMethod(Handle klass) { ClassData data(klass); const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { @@ -1438,7 +1445,7 @@ mirror::Object* GetEnclosingMethod(Handle klass) { DexFile::kDexAnnotationMethod); } -bool GetInnerClass(Handle klass, mirror::String** name) { +bool GetInnerClass(Handle klass, ObjPtr* name) { ClassData data(klass); const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { diff --git a/runtime/dex/dex_file_annotations.h b/runtime/dex/dex_file_annotations.h index 4bb0d75a57..9645a7febd 100644 --- a/runtime/dex/dex_file_annotations.h +++ b/runtime/dex/dex_file_annotations.h @@ -22,6 +22,7 @@ #include "handle.h" #include "mirror/dex_cache.h" #include "mirror/object_array.h" +#include "obj_ptr.h" namespace art { @@ -35,9 +36,10 @@ class ClassLinker; namespace annotations { // Field annotations. -mirror::Object* GetAnnotationForField(ArtField* field, Handle annotation_class) +ObjPtr GetAnnotationForField(ArtField* field, + Handle annotation_class) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::ObjectArray* GetAnnotationsForField(ArtField* field) +ObjPtr> GetAnnotationsForField(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_); mirror::ObjectArray* GetSignatureAnnotationForField(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_); @@ -45,21 +47,22 @@ bool IsFieldAnnotationPresent(ArtField* field, Handle annotation_ REQUIRES_SHARED(Locks::mutator_lock_); // Method annotations. -mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) +ObjPtr GetAnnotationDefaultValue(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::Object* GetAnnotationForMethod(ArtMethod* method, Handle annotation_class) +ObjPtr GetAnnotationForMethod(ArtMethod* method, + Handle annotation_class) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::ObjectArray* GetAnnotationsForMethod(ArtMethod* method) +ObjPtr> GetAnnotationsForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::ObjectArray* GetExceptionTypesForMethod(ArtMethod* method) +ObjPtr> GetExceptionTypesForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::ObjectArray* GetParameterAnnotations(ArtMethod* method) +ObjPtr> GetParameterAnnotations(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); uint32_t GetNumberOfAnnotatedMethodParameters(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::Object* GetAnnotationForMethodParameter(ArtMethod* method, - uint32_t parameter_idx, - Handle annotation_class) +ObjPtr GetAnnotationForMethodParameter(ArtMethod* method, + uint32_t parameter_idx, + Handle annotation_class) REQUIRES_SHARED(Locks::mutator_lock_); bool GetParametersMetadataForMethod(ArtMethod* method, MutableHandle>* names, @@ -85,20 +88,20 @@ uint32_t GetNativeMethodAnnotationAccessFlags(const DexFile& dex_file, uint32_t method_index); // Class annotations. -mirror::Object* GetAnnotationForClass(Handle klass, +ObjPtr GetAnnotationForClass(Handle klass, Handle annotation_class) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::ObjectArray* GetAnnotationsForClass(Handle klass) +ObjPtr> GetAnnotationsForClass(Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::ObjectArray* GetDeclaredClasses(Handle klass) +ObjPtr> GetDeclaredClasses(Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::Class* GetDeclaringClass(Handle klass) +ObjPtr GetDeclaringClass(Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::Class* GetEnclosingClass(Handle klass) +ObjPtr GetEnclosingClass(Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::Object* GetEnclosingMethod(Handle klass) +ObjPtr GetEnclosingMethod(Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); -bool GetInnerClass(Handle klass, mirror::String** name) +bool GetInnerClass(Handle klass, ObjPtr* name) REQUIRES_SHARED(Locks::mutator_lock_); bool GetInnerClassFlags(Handle klass, uint32_t* flags) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 791ebf09b7..0e429a63f6 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -510,7 +510,7 @@ void UnstartedRuntime::UnstartedClassIsAnonymousClass( result->SetZ(false); return; } - mirror::String* class_name = nullptr; + ObjPtr class_name = nullptr; if (!annotations::GetInnerClass(klass, &class_name)) { result->SetZ(false); return; diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 68024cd1c2..9f595b1c29 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -648,7 +648,7 @@ static jobjectArray Class_getDeclaredAnnotations(JNIEnv* env, jobject javaThis) // Return an empty array instead of a null pointer. ObjPtr annotation_array_class = soa.Decode(WellKnownClasses::java_lang_annotation_Annotation__array); - mirror::ObjectArray* empty_array = + ObjPtr> empty_array = mirror::ObjectArray::Alloc(soa.Self(), annotation_array_class.Ptr(), 0); @@ -661,7 +661,7 @@ static jobjectArray Class_getDeclaredClasses(JNIEnv* env, jobject javaThis) { ScopedFastNativeObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); Handle klass(hs.NewHandle(DecodeClass(soa, javaThis))); - mirror::ObjectArray* classes = nullptr; + ObjPtr> classes = nullptr; if (!klass->IsProxyClass() && klass->GetDexCache() != nullptr) { classes = annotations::GetDeclaredClasses(klass); } @@ -738,7 +738,7 @@ static jstring Class_getInnerClassName(JNIEnv* env, jobject javaThis) { if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) { return nullptr; } - mirror::String* class_name = nullptr; + ObjPtr class_name = nullptr; if (!annotations::GetInnerClass(klass, &class_name)) { return nullptr; } @@ -763,7 +763,7 @@ static jboolean Class_isAnonymousClass(JNIEnv* env, jobject javaThis) { if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) { return false; } - mirror::String* class_name = nullptr; + ObjPtr class_name = nullptr; if (!annotations::GetInnerClass(klass, &class_name)) { return false; } diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc index a5d6c9704d..13a8d28267 100644 --- a/runtime/native/java_lang_reflect_Constructor.cc +++ b/runtime/native/java_lang_reflect_Constructor.cc @@ -38,7 +38,7 @@ static jobjectArray Constructor_getExceptionTypes(JNIEnv* env, jobject javaMetho ScopedFastNativeObjectAccess soa(env); ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod) ->GetInterfaceMethodIfProxy(kRuntimePointerSize); - mirror::ObjectArray* result_array = + ObjPtr> result_array = annotations::GetExceptionTypesForMethod(method); if (result_array == nullptr) { // Return an empty array instead of a null pointer. diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc index 2503b3cb44..52e04941c6 100644 --- a/runtime/native/java_lang_reflect_Method.cc +++ b/runtime/native/java_lang_reflect_Method.cc @@ -62,7 +62,7 @@ static jobjectArray Method_getExceptionTypes(JNIEnv* env, jobject javaMethod) { klass->GetProxyThrows()->Get(throws_index); return soa.AddLocalReference(declared_exceptions->Clone(soa.Self())); } else { - mirror::ObjectArray* result_array = + ObjPtr> result_array = annotations::GetExceptionTypesForMethod(method); if (result_array == nullptr) { // Return an empty array instead of a null pointer -- GitLab From 8b362a87d52a6668ffd2283ef6ffc274315f41c8 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 22 May 2018 20:54:14 +0000 Subject: [PATCH 441/749] Revert "Remove support for Valgrind in ART." This reverts commit 8268cb677bd92bfbcfec7e803775c29687494e53. Reason for revert: ASAN failures Change-Id: I7e66d3f3fb461ae4f6dea6ec7d506b7dface3402 Test: SANITIZE_HOST=address m test-art-host Bug: 77856586 Bug: 29282211 --- Android.mk | 26 +++ build/Android.bp | 2 + build/Android.gtest.mk | 151 ++++++++++++++++-- build/Android.oat.mk | 108 ++++++++----- cmdline/unit.h | 5 +- compiler/dex/inline_method_analyser.cc | 3 +- compiler/optimizing/instruction_simplifier.cc | 8 +- dex2oat/dex2oat.cc | 8 +- dex2oat/dex2oat_test.cc | 6 +- dex2oat/linker/image_writer.cc | 3 +- libartbase/base/arena_allocator.h | 29 +++- libartbase/base/arena_allocator_test.cc | 8 +- libartbase/base/common_art_test.h | 14 +- libartbase/base/malloc_arena_pool.cc | 6 +- libartbase/base/mem_map.cc | 10 +- libartbase/base/mem_map_test.cc | 46 +++--- libartbase/base/memory_tool.h | 56 +++---- libartbase/base/scoped_arena_containers.h | 2 +- libdexfile/dex/dex_file_tracking_registrar.cc | 3 +- patchoat/patchoat.cc | 4 +- runtime/base/mem_map_arena_pool.cc | 2 +- runtime/exec_utils_test.cc | 24 +-- runtime/gc/allocator/rosalloc.h | 4 +- runtime/gc/heap-inl.h | 4 +- runtime/gc/heap.cc | 3 +- runtime/gc/space/large_object_space.cc | 5 +- .../gc/space/memory_tool_malloc_space-inl.h | 141 +++++++--------- runtime/gc/space/rosalloc_space.cc | 6 +- runtime/gc/space/rosalloc_space.h | 4 +- runtime/interpreter/unstarted_runtime_test.cc | 5 + runtime/jit/jit.cc | 2 +- runtime/native_stack_dump.cc | 6 +- runtime/runtime.cc | 12 +- runtime/runtime_callbacks_test.cc | 4 +- runtime/thread.cc | 13 +- test/137-cfi/cfi.cc | 3 + test/testrunner/target_config.py | 6 +- test/valgrind-suppressions.txt | 87 ++++++++++ test/valgrind-target-suppressions.txt | 76 +++++++++ tools/art | 4 + 40 files changed, 634 insertions(+), 275 deletions(-) create mode 100644 test/valgrind-suppressions.txt create mode 100644 test/valgrind-target-suppressions.txt diff --git a/Android.mk b/Android.mk index b190a3f345..d6472be895 100644 --- a/Android.mk +++ b/Android.mk @@ -245,6 +245,19 @@ endif test-art-host-dexdump: $(addprefix $(HOST_OUT_EXECUTABLES)/, dexdump2 dexlist) ANDROID_HOST_OUT=$(realpath $(HOST_OUT)) art/test/dexdump/run-all-tests +# Valgrind. +.PHONY: valgrind-test-art-host +valgrind-test-art-host: valgrind-test-art-host-gtest + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +.PHONY: valgrind-test-art-host32 +valgrind-test-art-host32: valgrind-test-art-host-gtest32 + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +.PHONY: valgrind-test-art-host64 +valgrind-test-art-host64: valgrind-test-art-host-gtest64 + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + ######################################################################## # target test rules @@ -319,6 +332,19 @@ test-art-target-jit$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) endif +# Valgrind. +.PHONY: valgrind-test-art-target +valgrind-test-art-target: valgrind-test-art-target-gtest + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +.PHONY: valgrind-test-art-target32 +valgrind-test-art-target32: valgrind-test-art-target-gtest32 + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + +.PHONY: valgrind-test-art-target64 +valgrind-test-art-target64: valgrind-test-art-target-gtest64 + $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) + ####################### # Fake packages for ART diff --git a/build/Android.bp b/build/Android.bp index 3a1d5839f5..2a5598fb7a 100644 --- a/build/Android.bp +++ b/build/Android.bp @@ -127,6 +127,8 @@ art_global_defaults { }, include_dirs: [ + "external/valgrind/include", + "external/valgrind", "external/vixl/src", ], diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index cdb39b93b8..3daaf0156e 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -398,9 +398,15 @@ endif ART_TEST_HOST_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_GTEST_RULES := +ART_TEST_HOST_VALGRIND_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_VALGRIND_GTEST_RULES := ART_TEST_TARGET_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := ART_TEST_TARGET_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := ART_TEST_TARGET_GTEST_RULES := +ART_TEST_TARGET_VALGRIND_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_VALGRIND_GTEST_RULES := ART_TEST_HOST_GTEST_DEPENDENCIES := ART_GTEST_TARGET_ANDROID_ROOT := '/system' @@ -408,6 +414,40 @@ ifneq ($(ART_TEST_ANDROID_ROOT),) ART_GTEST_TARGET_ANDROID_ROOT := $(ART_TEST_ANDROID_ROOT) endif +ART_VALGRIND_TARGET_DEPENDENCIES := + +# Has to match list in external/valgrind/Android.build_one.mk +ART_VALGRIND_SUPPORTED_ARCH := arm arm64 x86_64 + +# Valgrind is not supported for x86 +ifneq (,$(filter $(ART_VALGRIND_SUPPORTED_ARCH),$(TARGET_ARCH))) +art_vg_arch := $(if $(filter x86_64,$(TARGET_ARCH)),amd64,$(TARGET_ARCH)) +ART_VALGRIND_TARGET_DEPENDENCIES += \ + $(TARGET_OUT_EXECUTABLES)/valgrind \ + $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/memcheck-$(art_vg_arch)-linux \ + $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_core-$(art_vg_arch)-linux.so \ + $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_memcheck-$(art_vg_arch)-linux.so \ + $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/default.supp +art_vg_arch := +endif + +ifdef TARGET_2ND_ARCH +ifneq (,$(filter $(ART_VALGRIND_SUPPORTED_ARCH),$(TARGET_2ND_ARCH))) +ART_VALGRIND_TARGET_DEPENDENCIES += \ + $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/memcheck-$(TARGET_2ND_ARCH)-linux \ + $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_core-$(TARGET_2ND_ARCH)-linux.so \ + $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_memcheck-$(TARGET_2ND_ARCH)-linux.so +endif +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := valgrind-target-suppressions.txt +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_TAGS := optional +LOCAL_SRC_FILES := test/valgrind-target-suppressions.txt +LOCAL_MODULE_PATH := $(ART_TARGET_TEST_OUT) +include $(BUILD_PREBUILT) + # Define a make rule for a target device gtest. # $(1): gtest name - the name of the test we're building such as leb128_test. # $(2): path relative to $OUT to the test binary @@ -427,9 +467,10 @@ define define-art-gtest-rule-target $$($(3)TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so \ $$($(3)TARGET_OUT_SHARED_LIBRARIES)/libopenjdkd.so \ $$(TARGET_OUT_JAVA_LIBRARIES)/core-libart-testdex.jar \ - $$(TARGET_OUT_JAVA_LIBRARIES)/core-oj-testdex.jar + $$(TARGET_OUT_JAVA_LIBRARIES)/core-oj-testdex.jar \ + $$(ART_TARGET_TEST_OUT)/valgrind-target-suppressions.txt -$$(gtest_rule): PRIVATE_TARGET_EXE := $$(gtest_target_exe) +$$(gtest_rule) valgrind-$$(gtest_rule): PRIVATE_TARGET_EXE := $$(gtest_target_exe) ifeq ($(ART_TEST_CHROOT),) # Non-chroot configuration. @@ -464,7 +505,37 @@ $$(gtest_rule): test-art-target-sync ART_TEST_TARGET_GTEST_RULES += $$(gtest_rule) ART_TEST_TARGET_GTEST_$(1)_RULES += $$(gtest_rule) +# File witnessing the success of the Valgrind gtest, the presence of which means the gtest's +# success. +valgrind_gtest_witness := \ + $(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/valgrind-$$(gtest_rule)-$$$$PPID + +valgrind-$$(gtest_rule): VALGRIND_GTEST_WITNESS := $$(valgrind_gtest_witness) + +.PHONY: valgrind-$$(gtest_rule) +valgrind-$$(gtest_rule): $(ART_VALGRIND_TARGET_DEPENDENCIES) test-art-target-sync + $(hide) adb shell touch $$(VALGRIND_GTEST_WITNESS) + $(hide) adb shell rm $$(VALGRIND_GTEST_WITNESS) + $(hide) adb shell chmod 755 $(maybe_art_test_chroot)$$(PRIVATE_TARGET_EXE) + $(hide) $$(call ART_TEST_SKIP,$$@) && \ + (adb shell "$(maybe_chroot_command) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ + ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \ + $(ART_GTEST_TARGET_ANDROID_ROOT)/bin/valgrind \ + --leak-check=full --error-exitcode=1 --workaround-gcc296-bugs=yes \ + --suppressions=$(ART_TARGET_TEST_DIR)/valgrind-target-suppressions.txt \ + --num-callers=50 --show-mismatched-frees=no $$(PRIVATE_TARGET_EXE) \ + && touch $$(VALGRIND_GTEST_WITNESS)" \ + && (adb pull $$(VALGRIND_GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ + || $$(call ART_TEST_FAILED,$$@)) + $(hide) rm -f /tmp/$$@-$$$$PPID + + ART_TEST_TARGET_VALGRIND_GTEST$$($(3)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += \ + valgrind-$$(gtest_rule) + ART_TEST_TARGET_VALGRIND_GTEST_RULES += valgrind-$$(gtest_rule) + ART_TEST_TARGET_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule) + # Clear locally defined variables. + valgrind_gtest_witness := gtest_witness := maybe_chroot_command := maybe_art_test_chroot := @@ -473,6 +544,16 @@ $$(gtest_rule): test-art-target-sync gtest_rule := endef # define-art-gtest-rule-target +ART_VALGRIND_DEPENDENCIES := \ + $(HOST_OUT_EXECUTABLES)/valgrind \ + $(HOST_OUT)/lib64/valgrind/memcheck-amd64-linux \ + $(HOST_OUT)/lib64/valgrind/memcheck-x86-linux \ + $(HOST_OUT)/lib64/valgrind/default.supp \ + $(HOST_OUT)/lib64/valgrind/vgpreload_core-amd64-linux.so \ + $(HOST_OUT)/lib64/valgrind/vgpreload_core-x86-linux.so \ + $(HOST_OUT)/lib64/valgrind/vgpreload_memcheck-amd64-linux.so \ + $(HOST_OUT)/lib64/valgrind/vgpreload_memcheck-x86-linux.so + # Define make rules for a host gtests. # $(1): gtest name - the name of the test we're building such as leb128_test. # $(2): path relative to $OUT to the test binary @@ -524,6 +605,19 @@ endif ART_TEST_HOST_GTEST_$(1)_RULES += $$(gtest_rule) +.PHONY: valgrind-$$(gtest_rule) +valgrind-$$(gtest_rule): $$(gtest_exe) $$(gtest_deps) $(ART_VALGRIND_DEPENDENCIES) + $(hide) $$(call ART_TEST_SKIP,$$@) && \ + VALGRIND_LIB=$(HOST_OUT)/lib64/valgrind \ + $(HOST_OUT_EXECUTABLES)/valgrind --leak-check=full --error-exitcode=1 \ + --suppressions=art/test/valgrind-suppressions.txt --num-callers=50 \ + $$< && \ + $$(call ART_TEST_PASSED,$$@) || $$(call ART_TEST_FAILED,$$@) + + ART_TEST_HOST_VALGRIND_GTEST$$($(3)ART_PHONY_TEST_HOST_SUFFIX)_RULES += valgrind-$$(gtest_rule) + ART_TEST_HOST_VALGRIND_GTEST_RULES += valgrind-$$(gtest_rule) + ART_TEST_HOST_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule) + # Clear locally defined variables. gtest_deps := gtest_exe := @@ -556,6 +650,7 @@ define define-art-gtest-target ifndef ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES := + ART_TEST_TARGET_VALGRIND_GTEST_$$(art_gtest_name)_RULES := endif $$(eval $$(call define-art-gtest-rule-target,$$(art_gtest_name),$$(art_gtest_filename),$(2),$$($(2)library_path))) @@ -575,6 +670,7 @@ define define-art-gtest-host art_gtest_name := $$(notdir $$(basename $$(art_gtest_filename))) ifndef ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES := + ART_TEST_HOST_VALGRIND_GTEST_$$(art_gtest_name)_RULES := endif $$(eval $$(call define-art-gtest-rule-host,$$(art_gtest_name),$$(art_gtest_filename),$(2))) @@ -593,8 +689,13 @@ define define-art-gtest-target-both test-art-target-gtest-$$(art_gtest_name): $$(ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES) $$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) +.PHONY: valgrind-test-art-target-gtest-$$(art_gtest_name) +valgrind-test-art-target-gtest-$$(art_gtest_name): $$(ART_TEST_TARGET_VALGRIND_GTEST_$$(art_gtest_name)_RULES) + $$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) + # Clear now unused variables. ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES := + ART_TEST_TARGET_VALGRIND_GTEST_$$(art_gtest_name)_RULES := art_gtest_name := endef # define-art-gtest-target-both @@ -607,8 +708,13 @@ define define-art-gtest-host-both test-art-host-gtest-$$(art_gtest_name): $$(ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES) $$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) +.PHONY: valgrind-test-art-host-gtest-$$(art_gtest_name) +valgrind-test-art-host-gtest-$$(art_gtest_name): $$(ART_TEST_HOST_VALGRIND_GTEST_$$(art_gtest_name)_RULES) + $$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) + # Clear now unused variables. ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES := + ART_TEST_HOST_VALGRIND_GTEST_$$(art_gtest_name)_RULES := art_gtest_name := endef # define-art-gtest-host-both @@ -634,11 +740,12 @@ RUNTIME_TARGET_GTEST_MAKE_TARGETS := $(foreach file, $(ART_TARGET_GTEST_FILES), $(eval RUNTIME_TARGET_GTEST_MAKE_TARGETS += $$(notdir $$(patsubst %/,%,$$(dir $$(file))))_$$(notdir $$(basename $$(file))))) COMPILER_TARGET_GTEST_MAKE_TARGETS := -# Define all the combinations of host/target and suffix such as: -# test-art-host-gtest or test-art-host-gtest64 +# Define all the combinations of host/target, valgrind and suffix such as: +# test-art-host-gtest or valgrind-test-art-host-gtest64 # $(1): host or target # $(2): HOST or TARGET -# $(3): undefined, 32 or 64 +# $(3): valgrind- or undefined +# $(4): undefined, 32 or 64 define define-test-art-gtest-combination ifeq ($(1),host) ifneq ($(2),HOST) @@ -653,8 +760,12 @@ define define-test-art-gtest-combination endif endif - rule_name := test-art-$(1)-gtest$(3) - dependencies := $$(ART_TEST_$(2)_GTEST$(3)_RULES) + rule_name := $(3)test-art-$(1)-gtest$(4) + ifeq ($(3),valgrind-) + dependencies := $$(ART_TEST_$(2)_VALGRIND_GTEST$(4)_RULES) + else + dependencies := $$(ART_TEST_$(2)_GTEST$(4)_RULES) + endif .PHONY: $$(rule_name) $$(rule_name): $$(dependencies) dx d8-compat-dx desugar @@ -665,15 +776,21 @@ $$(rule_name): $$(dependencies) dx d8-compat-dx desugar dependencies := endef # define-test-art-gtest-combination -$(eval $(call define-test-art-gtest-combination,target,TARGET,)) -$(eval $(call define-test-art-gtest-combination,target,TARGET,$(ART_PHONY_TEST_TARGET_SUFFIX))) +$(eval $(call define-test-art-gtest-combination,target,TARGET,,)) +$(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,)) +$(eval $(call define-test-art-gtest-combination,target,TARGET,,$(ART_PHONY_TEST_TARGET_SUFFIX))) +$(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,$(ART_PHONY_TEST_TARGET_SUFFIX))) ifdef 2ND_ART_PHONY_TEST_TARGET_SUFFIX -$(eval $(call define-test-art-gtest-combination,target,TARGET,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX))) +$(eval $(call define-test-art-gtest-combination,target,TARGET,,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX))) +$(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX))) endif -$(eval $(call define-test-art-gtest-combination,host,HOST,)) -$(eval $(call define-test-art-gtest-combination,host,HOST,$(ART_PHONY_TEST_HOST_SUFFIX))) +$(eval $(call define-test-art-gtest-combination,host,HOST,,)) +$(eval $(call define-test-art-gtest-combination,host,HOST,valgrind-,)) +$(eval $(call define-test-art-gtest-combination,host,HOST,,$(ART_PHONY_TEST_HOST_SUFFIX))) +$(eval $(call define-test-art-gtest-combination,host,HOST,valgrind-,$(ART_PHONY_TEST_HOST_SUFFIX))) ifneq ($(HOST_PREFER_32_BIT),true) -$(eval $(call define-test-art-gtest-combination,host,HOST,$(2ND_ART_PHONY_TEST_HOST_SUFFIX))) +$(eval $(call define-test-art-gtest-combination,host,HOST,,$(2ND_ART_PHONY_TEST_HOST_SUFFIX))) +$(eval $(call define-test-art-gtest-combination,host,HOST,valgrind-,$(2ND_ART_PHONY_TEST_HOST_SUFFIX))) endif # Clear locally defined variables. @@ -690,9 +807,15 @@ COMPILER_GTEST_HOST_SRC_FILES := ART_TEST_HOST_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_GTEST_RULES := +ART_TEST_HOST_VALGRIND_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_VALGRIND_GTEST_RULES := ART_TEST_TARGET_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := ART_TEST_TARGET_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := ART_TEST_TARGET_GTEST_RULES := +ART_TEST_TARGET_VALGRIND_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := +ART_TEST_TARGET_VALGRIND_GTEST_RULES := ART_GTEST_TARGET_ANDROID_ROOT := ART_GTEST_class_linker_test_DEX_DEPS := ART_GTEST_class_table_test_DEX_DEPS := @@ -731,6 +854,8 @@ ART_GTEST_transaction_test_DEX_DEPS := ART_GTEST_dex2oat_environment_tests_DEX_DEPS := ART_GTEST_heap_verification_test_DEX_DEPS := ART_GTEST_verifier_deps_test_DEX_DEPS := +ART_VALGRIND_DEPENDENCIES := +ART_VALGRIND_TARGET_DEPENDENCIES := $(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_TARGET_GTEST_$(dir)_DEX :=)) $(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_HOST_GTEST_$(dir)_DEX :=)) ART_TEST_HOST_GTEST_MainStripped_DEX := diff --git a/build/Android.oat.mk b/build/Android.oat.mk index ba3ef053de..517ac5c28d 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -37,9 +37,11 @@ else endif # Use dex2oat debug version for better error reporting -# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks). +# $(1): compiler - optimizing, interpreter or interpreter-access-checks. # $(2): 2ND_ or undefined, 2ND_ for 32-bit host builds. -# $(3): multi-image. +# $(3): wrapper, e.g., valgrind. +# $(4): dex2oat suffix, e.g, valgrind requires 32 right now. +# $(5): multi-image. # NB depending on HOST_CORE_DEX_LOCATIONS so we are sure to have the dex files in frameworks for # run-test --no-image define create-core-oat-host-rules @@ -63,11 +65,11 @@ define create-core-oat-host-rules endif ifneq ($(filter-out interpreter interp-ac optimizing,$(1)),) #Technically this test is not precise, but hopefully good enough. - $$(error found $(1) expected interpreter, interp-ac, or optimizing) + $$(error found $(1) expected interpreter, interpreter-access-checks, or optimizing) endif - # If $(3) is true, generate a multi-image. - ifeq ($(3),true) + # If $(5) is true, generate a multi-image. + ifeq ($(5),true) core_multi_infix := -multi core_multi_param := --multi-image --no-inline-from=core-oj-hostdex.jar core_multi_group := _multi @@ -77,18 +79,22 @@ define create-core-oat-host-rules core_multi_group := endif - core_image_name := $($(2)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(CORE_IMG_SUFFIX) - core_oat_name := $($(2)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(CORE_OAT_SUFFIX) + core_image_name := $($(2)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(3)$(CORE_IMG_SUFFIX) + core_oat_name := $($(2)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(3)$(CORE_OAT_SUFFIX) # Using the bitness suffix makes it easier to add as a dependency for the run-test mk. ifeq ($(2),) - HOST_CORE_IMAGE_$(1)$$(core_multi_group)_64 := $$(core_image_name) + $(3)HOST_CORE_IMAGE_$(1)$$(core_multi_group)_64 := $$(core_image_name) else - HOST_CORE_IMAGE_$(1)$$(core_multi_group)_32 := $$(core_image_name) + $(3)HOST_CORE_IMAGE_$(1)$$(core_multi_group)_32 := $$(core_image_name) endif - HOST_CORE_IMG_OUTS += $$(core_image_name) - HOST_CORE_OAT_OUTS += $$(core_oat_name) + $(3)HOST_CORE_IMG_OUTS += $$(core_image_name) + $(3)HOST_CORE_OAT_OUTS += $$(core_oat_name) + # If we have a wrapper, make the target phony. + ifneq ($(3),) +.PHONY: $$(core_image_name) + endif $$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options) $$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name) $$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name) @@ -96,7 +102,7 @@ $$(core_image_name): PRIVATE_CORE_MULTI_PARAM := $$(core_multi_param) $$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency) @echo "host dex2oat: $$@" @mkdir -p $$(dir $$@) - $$(hide) $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \ + $$(hide) $(3) $$(DEX2OAT)$(4) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \ --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \ --image-classes=$$(PRELOADED_CLASSES) $$(addprefix --dex-file=,$$(HOST_CORE_DEX_FILES)) \ $$(addprefix --dex-location=,$$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$$(PRIVATE_CORE_OAT_NAME) \ @@ -118,27 +124,35 @@ $$(core_oat_name): $$(core_image_name) core_infix := endef # create-core-oat-host-rules -# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks). -# $(2): multi-image. +# $(1): compiler - optimizing, interpreter or interpreter-access-checks. +# $(2): wrapper. +# $(3): dex2oat suffix. +# $(4): multi-image. define create-core-oat-host-rule-combination - $(call create-core-oat-host-rules,$(1),,$(2)) + $(call create-core-oat-host-rules,$(1),,$(2),$(3),$(4)) ifneq ($(HOST_PREFER_32_BIT),true) - $(call create-core-oat-host-rules,$(1),2ND_,$(2)) + $(call create-core-oat-host-rules,$(1),2ND_,$(2),$(3),$(4)) endif endef -$(eval $(call create-core-oat-host-rule-combination,optimizing,false)) -$(eval $(call create-core-oat-host-rule-combination,interpreter,false)) -$(eval $(call create-core-oat-host-rule-combination,interp-ac,false)) -$(eval $(call create-core-oat-host-rule-combination,optimizing,true)) -$(eval $(call create-core-oat-host-rule-combination,interpreter,true)) -$(eval $(call create-core-oat-host-rule-combination,interp-ac,true)) +$(eval $(call create-core-oat-host-rule-combination,optimizing,,,false)) +$(eval $(call create-core-oat-host-rule-combination,interpreter,,,false)) +$(eval $(call create-core-oat-host-rule-combination,interp-ac,,,false)) +$(eval $(call create-core-oat-host-rule-combination,optimizing,,,true)) +$(eval $(call create-core-oat-host-rule-combination,interpreter,,,true)) +$(eval $(call create-core-oat-host-rule-combination,interp-ac,,,true)) + +valgrindHOST_CORE_IMG_OUTS := +valgrindHOST_CORE_OAT_OUTS := +$(eval $(call create-core-oat-host-rule-combination,optimizing,valgrind,32,false)) +$(eval $(call create-core-oat-host-rule-combination,interpreter,valgrind,32,false)) +$(eval $(call create-core-oat-host-rule-combination,interp-ac,valgrind,32,false)) + +valgrind-test-art-host-dex2oat-host: $(valgrindHOST_CORE_IMG_OUTS) test-art-host-dex2oat-host: $(HOST_CORE_IMG_OUTS) -# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks). -# $(2): 2ND_ or undefined define create-core-oat-target-rules core_compile_options := core_image_name := @@ -162,32 +176,36 @@ define create-core-oat-target-rules endif ifneq ($(filter-out interpreter interp-ac optimizing,$(1)),) # Technically this test is not precise, but hopefully good enough. - $$(error found $(1) expected interpreter, interp-ac, or optimizing) + $$(error found $(1) expected interpreter, interpreter-access-checks, or optimizing) endif - core_image_name := $($(2)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$(CORE_IMG_SUFFIX) - core_oat_name := $($(2)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$(CORE_OAT_SUFFIX) + core_image_name := $($(2)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$(3)$(CORE_IMG_SUFFIX) + core_oat_name := $($(2)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$(3)$(CORE_OAT_SUFFIX) # Using the bitness suffix makes it easier to add as a dependency for the run-test mk. ifeq ($(2),) ifdef TARGET_2ND_ARCH - TARGET_CORE_IMAGE_$(1)_64 := $$(core_image_name) + $(3)TARGET_CORE_IMAGE_$(1)_64 := $$(core_image_name) else - TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name) + $(3)TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name) endif else - TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name) + $(3)TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name) endif - TARGET_CORE_IMG_OUTS += $$(core_image_name) - TARGET_CORE_OAT_OUTS += $$(core_oat_name) + $(3)TARGET_CORE_IMG_OUTS += $$(core_image_name) + $(3)TARGET_CORE_OAT_OUTS += $$(core_oat_name) + # If we have a wrapper, make the target phony. + ifneq ($(3),) +.PHONY: $$(core_image_name) + endif $$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options) $$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name) $$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name) $$(core_image_name): $$(TARGET_CORE_DEX_FILES) $$(core_dex2oat_dependency) @echo "target dex2oat: $$@" @mkdir -p $$(dir $$@) - $$(hide) $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \ + $$(hide) $(4) $$(DEX2OAT)$(5) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \ --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \ --image-classes=$$(PRELOADED_CLASSES) $$(addprefix --dex-file=,$$(TARGET_CORE_DEX_FILES)) \ $$(addprefix --dex-location=,$$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$$(PRIVATE_CORE_OAT_NAME) \ @@ -210,18 +228,30 @@ $$(core_oat_name): $$(core_image_name) core_infix := endef # create-core-oat-target-rules -# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks). +# $(1): compiler - optimizing, interpreter or interpreter-access-checks. +# $(2): wrapper. +# $(3): dex2oat suffix. define create-core-oat-target-rule-combination - $(call create-core-oat-target-rules,$(1),) + $(call create-core-oat-target-rules,$(1),,$(2),$(3)) ifdef TARGET_2ND_ARCH - $(call create-core-oat-target-rules,$(1),2ND_) + $(call create-core-oat-target-rules,$(1),2ND_,$(2),$(3)) endif endef -$(eval $(call create-core-oat-target-rule-combination,optimizing)) -$(eval $(call create-core-oat-target-rule-combination,interpreter)) -$(eval $(call create-core-oat-target-rule-combination,interp-ac)) +$(eval $(call create-core-oat-target-rule-combination,optimizing,,)) +$(eval $(call create-core-oat-target-rule-combination,interpreter,,)) +$(eval $(call create-core-oat-target-rule-combination,interp-ac,,)) + +valgrindTARGET_CORE_IMG_OUTS := +valgrindTARGET_CORE_OAT_OUTS := +$(eval $(call create-core-oat-target-rule-combination,optimizing,valgrind,32)) +$(eval $(call create-core-oat-target-rule-combination,interpreter,valgrind,32)) +$(eval $(call create-core-oat-target-rule-combination,interp-ac,valgrind,32)) + +valgrind-test-art-host-dex2oat-target: $(valgrindTARGET_CORE_IMG_OUTS) + +valgrind-test-art-host-dex2oat: valgrind-test-art-host-dex2oat-host valgrind-test-art-host-dex2oat-target # Define a default core image that can be used for things like gtests that # need some image to run, but don't otherwise care which image is used. diff --git a/cmdline/unit.h b/cmdline/unit.h index f73981fbd3..ad6a03d12f 100644 --- a/cmdline/unit.h +++ b/cmdline/unit.h @@ -21,9 +21,8 @@ namespace art { // Used for arguments that simply indicate presence (e.g. "-help") without any values. struct Unit { - // Historical note: We specified a user-defined constructor to avoid - // 'Conditional jump or move depends on uninitialised value(s)' errors - // when running Valgrind. + // Avoid 'Conditional jump or move depends on uninitialised value(s)' errors + // when running valgrind by specifying a user-defined constructor. Unit() {} Unit(const Unit&) = default; ~Unit() {} diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc index fe8b766d0f..dc044c1210 100644 --- a/compiler/dex/inline_method_analyser.cc +++ b/compiler/dex/inline_method_analyser.cc @@ -724,8 +724,7 @@ bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(ArtMethod* method, return false; } DCHECK_GE(field->GetOffset().Int32Value(), 0); - // Historical note: We made sure not to interleave function calls with bit field writes to - // placate Valgrind. Bug: 27552451. + // Do not interleave function calls with bit field writes to placate valgrind. Bug: 27552451. uint32_t field_offset = field->GetOffset().Uint32Value(); bool is_volatile = field->IsVolatile(); result->field_idx = field_idx; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 6e618f4d02..ca84d421a7 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -636,8 +636,8 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { return; } - // Historical note: The `outcome` was initialized to please Valgrind - the compiler can reorder - // the return value check with the `outcome` check, b/27651442. + // Note: The `outcome` is initialized to please valgrind - the compiler can reorder + // the return value check with the `outcome` check, b/27651442 . bool outcome = false; if (TypeCheckHasKnownOutcome(check_cast->GetTargetClassRTI(), object, &outcome)) { if (outcome) { @@ -682,8 +682,8 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { return; } - // Historical note: The `outcome` was initialized to please Valgrind - the compiler can reorder - // the return value check with the `outcome` check, b/27651442. + // Note: The `outcome` is initialized to please valgrind - the compiler can reorder + // the return value check with the `outcome` check, b/27651442 . bool outcome = false; if (TypeCheckHasKnownOutcome(instruction->GetTargetClassRTI(), object, &outcome)) { MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf); diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 00c893a8b5..6b65aca943 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -657,7 +657,7 @@ class Dex2Oat FINAL { // the runtime. LogCompletionTime(); - if (!kIsDebugBuild && !(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { + if (!kIsDebugBuild && !(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { // We want to just exit on non-debug builds, not bringing the runtime down // in an orderly fashion. So release the following fields. driver_.release(); @@ -3119,9 +3119,9 @@ static dex2oat::ReturnCode Dex2oat(int argc, char** argv) { int main(int argc, char** argv) { int result = static_cast(art::Dex2oat(argc, argv)); // Everything was done, do an explicit exit here to avoid running Runtime destructors that take - // time (bug 10645725) unless we're a debug or instrumented build or running on a memory tool. - // Note: The Dex2Oat class should not destruct the runtime in this case. - if (!art::kIsDebugBuild && !art::kIsPGOInstrumentation && !art::kRunningOnMemoryTool) { + // time (bug 10645725) unless we're a debug or instrumented build or running on valgrind. Note: + // The Dex2Oat class should not destruct the runtime in this case. + if (!art::kIsDebugBuild && !art::kIsPGOInstrumentation && (RUNNING_ON_MEMORY_TOOL == 0)) { _exit(result); } return result; diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 1d0735d807..2fe16f7cb7 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -472,8 +472,8 @@ class Dex2oatSwapUseTest : public Dex2oatSwapTest { }; TEST_F(Dex2oatSwapUseTest, CheckSwapUsage) { - // Native memory usage isn't correctly tracked when running under ASan. - TEST_DISABLED_FOR_MEMORY_TOOL(); + // Native memory usage isn't correctly tracked under sanitization. + TEST_DISABLED_FOR_MEMORY_TOOL_ASAN(); // The `native_alloc_2_ >= native_alloc_1_` assertion below may not // hold true on some x86 systems; disable this test while we @@ -1054,6 +1054,8 @@ TEST_F(Dex2oatWatchdogTest, TestWatchdogOK) { } TEST_F(Dex2oatWatchdogTest, TestWatchdogTrigger) { + TEST_DISABLED_FOR_MEMORY_TOOL_VALGRIND(); // b/63052624 + // The watchdog is independent of dex2oat and will not delete intermediates. It is possible // that the compilation succeeds and the file is completely written by the time the watchdog // kills dex2oat (but the dex2oat threads must have been scheduled pretty badly). diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index b24cc38100..9e5cd8035c 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -2094,8 +2094,7 @@ void ImageWriter::CopyAndFixupNativeData(size_t oat_index) { size_t size = ArtMethod::Size(target_ptr_size_); size_t alignment = ArtMethod::Alignment(target_ptr_size_); memcpy(dest, pair.first, LengthPrefixedArray::ComputeSize(0, size, alignment)); - // Clear padding to avoid non-deterministic data in the image. - // Historical note: We also did that to placate Valgrind. + // Clear padding to avoid non-deterministic data in the image (and placate valgrind). reinterpret_cast*>(dest)->ClearPadding(size, alignment); break; } diff --git a/libartbase/base/arena_allocator.h b/libartbase/base/arena_allocator.h index 4ad77baa24..211ff4f6ad 100644 --- a/libartbase/base/arena_allocator.h +++ b/libartbase/base/arena_allocator.h @@ -147,9 +147,34 @@ class ArenaAllocatorStatsImpl { typedef ArenaAllocatorStatsImpl ArenaAllocatorStats; -class ArenaAllocatorMemoryTool { +template +class ArenaAllocatorMemoryToolCheckImpl { + // This is the generic template but since there is a partial specialization + // for kValgrind == false, this can be instantiated only for kValgrind == true. + static_assert(kValgrind, "This template can be instantiated only for Valgrind."); + static_assert(kAvailable, "Valgrind implies memory tool availability."); + + public: + ArenaAllocatorMemoryToolCheckImpl() : is_running_on_valgrind_(RUNNING_ON_MEMORY_TOOL) { } + bool IsRunningOnMemoryTool() { return is_running_on_valgrind_; } + + private: + const bool is_running_on_valgrind_; +}; + +template +class ArenaAllocatorMemoryToolCheckImpl { + public: + ArenaAllocatorMemoryToolCheckImpl() { } + bool IsRunningOnMemoryTool() { return kAvailable; } +}; + +typedef ArenaAllocatorMemoryToolCheckImpl + ArenaAllocatorMemoryToolCheck; + +class ArenaAllocatorMemoryTool : private ArenaAllocatorMemoryToolCheck { public: - bool IsRunningOnMemoryTool() { return kMemoryToolIsAvailable; } + using ArenaAllocatorMemoryToolCheck::IsRunningOnMemoryTool; void MakeDefined(void* ptr, size_t size) { if (UNLIKELY(IsRunningOnMemoryTool())) { diff --git a/libartbase/base/arena_allocator_test.cc b/libartbase/base/arena_allocator_test.cc index 6323a2b97c..e358710ca6 100644 --- a/libartbase/base/arena_allocator_test.cc +++ b/libartbase/base/arena_allocator_test.cc @@ -16,7 +16,6 @@ #include "arena_allocator-inl.h" #include "arena_bit_vector.h" -#include "base/common_art_test.h" #include "gtest/gtest.h" #include "malloc_arena_pool.h" #include "memory_tool.h" @@ -147,8 +146,11 @@ TEST_F(ArenaAllocatorTest, AllocAlignment) { } TEST_F(ArenaAllocatorTest, ReallocReuse) { - // Realloc does not reuse arenas when running under sanitization. - TEST_DISABLED_FOR_MEMORY_TOOL(); + // Realloc does not reuse arenas when running under sanitization. So we cannot do those + if (RUNNING_ON_MEMORY_TOOL != 0) { + printf("WARNING: TEST DISABLED FOR MEMORY_TOOL\n"); + return; + } { // Case 1: small aligned allocation, aligned extend inside arena. diff --git a/libartbase/base/common_art_test.h b/libartbase/base/common_art_test.h index fe988a478a..a4764c275d 100644 --- a/libartbase/base/common_art_test.h +++ b/libartbase/base/common_art_test.h @@ -207,11 +207,23 @@ using CommonArtTestWithParam = CommonArtTestBase>; } #define TEST_DISABLED_FOR_MEMORY_TOOL() \ - if (kRunningOnMemoryTool) { \ + if (RUNNING_ON_MEMORY_TOOL > 0) { \ printf("WARNING: TEST DISABLED FOR MEMORY TOOL\n"); \ return; \ } +#define TEST_DISABLED_FOR_MEMORY_TOOL_VALGRIND() \ + if (RUNNING_ON_MEMORY_TOOL > 0 && kMemoryToolIsValgrind) { \ + printf("WARNING: TEST DISABLED FOR MEMORY TOOL VALGRIND\n"); \ + return; \ + } + +#define TEST_DISABLED_FOR_MEMORY_TOOL_ASAN() \ + if (RUNNING_ON_MEMORY_TOOL > 0 && !kMemoryToolIsValgrind) { \ + printf("WARNING: TEST DISABLED FOR MEMORY TOOL ASAN\n"); \ + return; \ + } + #define TEST_DISABLED_FOR_HEAP_POISONING() \ if (kPoisonHeapReferences) { \ printf("WARNING: TEST DISABLED FOR HEAP POISONING\n"); \ diff --git a/libartbase/base/malloc_arena_pool.cc b/libartbase/base/malloc_arena_pool.cc index 15a5d71a6b..144b06ceb9 100644 --- a/libartbase/base/malloc_arena_pool.cc +++ b/libartbase/base/malloc_arena_pool.cc @@ -53,7 +53,7 @@ MallocArena::MallocArena(size_t size) { memory_ = unaligned_memory_; } else { memory_ = AlignUp(unaligned_memory_, ArenaAllocator::kArenaAlignment); - if (kRunningOnMemoryTool) { + if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { size_t head = memory_ - unaligned_memory_; size_t tail = overallocation - head; MEMORY_TOOL_MAKE_NOACCESS(unaligned_memory_, head); @@ -66,7 +66,7 @@ MallocArena::MallocArena(size_t size) { MallocArena::~MallocArena() { constexpr size_t overallocation = RequiredOverallocation(); - if (overallocation != 0u && kRunningOnMemoryTool) { + if (overallocation != 0u && UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { size_t head = memory_ - unaligned_memory_; size_t tail = overallocation - head; MEMORY_TOOL_MAKE_UNDEFINED(unaligned_memory_, head); @@ -132,7 +132,7 @@ size_t MallocArenaPool::GetBytesAllocated() const { } void MallocArenaPool::FreeArenaChain(Arena* first) { - if (kRunningOnMemoryTool) { + if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { for (Arena* arena = first; arena != nullptr; arena = arena->next_) { MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_); } diff --git a/libartbase/base/mem_map.cc b/libartbase/base/mem_map.cc index 4e799bf491..9a1392ceee 100644 --- a/libartbase/base/mem_map.cc +++ b/libartbase/base/mem_map.cc @@ -524,7 +524,7 @@ MemMap* MemMap::MapFileAtAddress(uint8_t* expected_ptr, (expected_ptr == nullptr) ? nullptr : (expected_ptr - page_offset); size_t redzone_size = 0; - if (kRunningOnMemoryTool && kMemoryToolAddsRedzones && expected_ptr == nullptr) { + if (RUNNING_ON_MEMORY_TOOL && kMemoryToolAddsRedzones && expected_ptr == nullptr) { redzone_size = kPageSize; page_aligned_byte_count += redzone_size; } @@ -713,11 +713,9 @@ void MemMap::MadviseDontNeedAndZero() { bool MemMap::Sync() { bool result; if (redzone_size_ != 0) { - // To avoid errors when running on a memory tool, temporarily lift the lower-end noaccess - // protection before passing it to msync() as it only accepts page-aligned base address, - // and exclude the higher-end noaccess protection from the msync range. b/27552451. - // TODO: Valgrind is no longer supported, but Address Sanitizer is: - // check whether this special case is needed for ASan. + // To avoid valgrind errors, temporarily lift the lower-end noaccess protection before passing + // it to msync() as it only accepts page-aligned base address, and exclude the higher-end + // noaccess protection from the msync range. b/27552451. uint8_t* base_begin = reinterpret_cast(base_begin_); MEMORY_TOOL_MAKE_DEFINED(base_begin, begin_ - base_begin); result = msync(BaseBegin(), End() - base_begin, MS_SYNC) == 0; diff --git a/libartbase/base/mem_map_test.cc b/libartbase/base/mem_map_test.cc index 4a78bdcabe..d956126df1 100644 --- a/libartbase/base/mem_map_test.cc +++ b/libartbase/base/mem_map_test.cc @@ -471,33 +471,31 @@ TEST_F(MemMapTest, MapAnonymousExactAddr32bitHighAddr) { // cannot allocate in the 2GB-4GB region. TEST_DISABLED_FOR_MIPS(); - // This test may not work under Valgrind. - // TODO: Valgrind is no longer supported, but Address Sanitizer is: - // check whether this test works with ASan. - TEST_DISABLED_FOR_MEMORY_TOOL(); - CommonInit(); - constexpr size_t size = 0x100000; - // Try all addresses starting from 2GB to 4GB. - size_t start_addr = 2 * GB; - std::string error_msg; - std::unique_ptr map; - for (; start_addr <= std::numeric_limits::max() - size; start_addr += size) { - map.reset(MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr", - reinterpret_cast(start_addr), - size, - PROT_READ | PROT_WRITE, - /*low_4gb*/true, - false, - &error_msg)); - if (map != nullptr) { - break; + // This test may not work under valgrind. + if (RUNNING_ON_MEMORY_TOOL == 0) { + constexpr size_t size = 0x100000; + // Try all addresses starting from 2GB to 4GB. + size_t start_addr = 2 * GB; + std::string error_msg; + std::unique_ptr map; + for (; start_addr <= std::numeric_limits::max() - size; start_addr += size) { + map.reset(MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr", + reinterpret_cast(start_addr), + size, + PROT_READ | PROT_WRITE, + /*low_4gb*/true, + false, + &error_msg)); + if (map != nullptr) { + break; + } } + ASSERT_TRUE(map.get() != nullptr) << error_msg; + ASSERT_GE(reinterpret_cast(map->End()), 2u * GB); + ASSERT_TRUE(error_msg.empty()); + ASSERT_EQ(BaseBegin(map.get()), reinterpret_cast(start_addr)); } - ASSERT_TRUE(map.get() != nullptr) << error_msg; - ASSERT_GE(reinterpret_cast(map->End()), 2u * GB); - ASSERT_TRUE(error_msg.empty()); - ASSERT_EQ(BaseBegin(map.get()), reinterpret_cast(start_addr)); } TEST_F(MemMapTest, MapAnonymousOverflow) { diff --git a/libartbase/base/memory_tool.h b/libartbase/base/memory_tool.h index d381f010f5..e1df99fed4 100644 --- a/libartbase/base/memory_tool.h +++ b/libartbase/base/memory_tool.h @@ -19,53 +19,53 @@ #include -namespace art { - #if !defined(__has_feature) -# define __has_feature(x) 0 +#define __has_feature(x) 0 #endif #if __has_feature(address_sanitizer) -# include -# define ADDRESS_SANITIZER +#include +#define ADDRESS_SANITIZER -# ifdef ART_ENABLE_ADDRESS_SANITIZER -# define MEMORY_TOOL_MAKE_NOACCESS(p, s) __asan_poison_memory_region(p, s) -# define MEMORY_TOOL_MAKE_UNDEFINED(p, s) __asan_unpoison_memory_region(p, s) -# define MEMORY_TOOL_MAKE_DEFINED(p, s) __asan_unpoison_memory_region(p, s) +#ifdef ART_ENABLE_ADDRESS_SANITIZER +#define MEMORY_TOOL_MAKE_NOACCESS(p, s) __asan_poison_memory_region(p, s) +#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) __asan_unpoison_memory_region(p, s) +#define MEMORY_TOOL_MAKE_DEFINED(p, s) __asan_unpoison_memory_region(p, s) constexpr bool kMemoryToolIsAvailable = true; -# else -# define MEMORY_TOOL_MAKE_NOACCESS(p, s) do { (void)(p); (void)(s); } while (0) -# define MEMORY_TOOL_MAKE_UNDEFINED(p, s) do { (void)(p); (void)(s); } while (0) -# define MEMORY_TOOL_MAKE_DEFINED(p, s) do { (void)(p); (void)(s); } while (0) +#else +#define MEMORY_TOOL_MAKE_NOACCESS(p, s) do { (void)(p); (void)(s); } while (0) +#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) do { (void)(p); (void)(s); } while (0) +#define MEMORY_TOOL_MAKE_DEFINED(p, s) do { (void)(p); (void)(s); } while (0) constexpr bool kMemoryToolIsAvailable = false; -# endif +#endif extern "C" void __asan_handle_no_return(); -# define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) -# define MEMORY_TOOL_HANDLE_NO_RETURN __asan_handle_no_return() -constexpr bool kRunningOnMemoryTool = true; +#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) +#define MEMORY_TOOL_HANDLE_NO_RETURN __asan_handle_no_return() +#define RUNNING_ON_MEMORY_TOOL 1U +constexpr bool kMemoryToolIsValgrind = false; constexpr bool kMemoryToolDetectsLeaks = true; constexpr bool kMemoryToolAddsRedzones = true; constexpr size_t kMemoryToolStackGuardSizeScale = 2; #else -# define MEMORY_TOOL_MAKE_NOACCESS(p, s) do { (void)(p); (void)(s); } while (0) -# define MEMORY_TOOL_MAKE_UNDEFINED(p, s) do { (void)(p); (void)(s); } while (0) -# define MEMORY_TOOL_MAKE_DEFINED(p, s) do { (void)(p); (void)(s); } while (0) -# define ATTRIBUTE_NO_SANITIZE_ADDRESS -# define MEMORY_TOOL_HANDLE_NO_RETURN do { } while (0) -constexpr bool kRunningOnMemoryTool = false; -constexpr bool kMemoryToolIsAvailable = false; -constexpr bool kMemoryToolDetectsLeaks = false; -constexpr bool kMemoryToolAddsRedzones = false; +#include +#include +#define MEMORY_TOOL_MAKE_NOACCESS(p, s) VALGRIND_MAKE_MEM_NOACCESS(p, s) +#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) VALGRIND_MAKE_MEM_UNDEFINED(p, s) +#define MEMORY_TOOL_MAKE_DEFINED(p, s) VALGRIND_MAKE_MEM_DEFINED(p, s) +#define ATTRIBUTE_NO_SANITIZE_ADDRESS +#define MEMORY_TOOL_HANDLE_NO_RETURN do { } while (0) +#define RUNNING_ON_MEMORY_TOOL RUNNING_ON_VALGRIND +constexpr bool kMemoryToolIsAvailable = true; +constexpr bool kMemoryToolIsValgrind = true; +constexpr bool kMemoryToolDetectsLeaks = true; +constexpr bool kMemoryToolAddsRedzones = true; constexpr size_t kMemoryToolStackGuardSizeScale = 1; #endif -} // namespace art - #endif // ART_LIBARTBASE_BASE_MEMORY_TOOL_H_ diff --git a/libartbase/base/scoped_arena_containers.h b/libartbase/base/scoped_arena_containers.h index 679bcc0e26..41939816f5 100644 --- a/libartbase/base/scoped_arena_containers.h +++ b/libartbase/base/scoped_arena_containers.h @@ -228,7 +228,7 @@ class ArenaDelete { protected: // Used for variable sized objects such as RegisterLine. ALWAYS_INLINE void ProtectMemory(T* ptr, size_t size) const { - if (kRunningOnMemoryTool) { + if (RUNNING_ON_MEMORY_TOOL > 0) { // Writing to the memory will fail ift we already destroyed the pointer with // DestroyOnlyDelete since we make it no access. memset(ptr, kMagicFill, size); diff --git a/libdexfile/dex/dex_file_tracking_registrar.cc b/libdexfile/dex/dex_file_tracking_registrar.cc index 551bea108c..78ea9c16cb 100644 --- a/libdexfile/dex/dex_file_tracking_registrar.cc +++ b/libdexfile/dex/dex_file_tracking_registrar.cc @@ -130,8 +130,7 @@ inline void SetRegistrationRange(const void* begin, size_t size, bool should_poi MEMORY_TOOL_MAKE_NOACCESS(begin, size); } else { // Note: MEMORY_TOOL_MAKE_UNDEFINED has the same functionality with Address - // Sanitizer. - // Historical note: The difference has not been tested with Valgrind. + // Sanitizer. The difference has not been tested with Valgrind MEMORY_TOOL_MAKE_DEFINED(begin, size); } } diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 0889a8e6ae..dc9d990e29 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -610,7 +610,7 @@ bool PatchOat::Patch(const std::string& image_location, } } - if (!kIsDebugBuild && !(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { + if (!kIsDebugBuild && !(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { // We want to just exit on non-debug builds, not bringing the runtime down // in an orderly fashion. So release the following fields. runtime.release(); @@ -690,7 +690,7 @@ bool PatchOat::Verify(const std::string& image_location, } } - if (!kIsDebugBuild && !(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { + if (!kIsDebugBuild && !(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { // We want to just exit on non-debug builds, not bringing the runtime down // in an orderly fashion. So release the following fields. runtime.release(); diff --git a/runtime/base/mem_map_arena_pool.cc b/runtime/base/mem_map_arena_pool.cc index 702f0e453b..9ac7886e5d 100644 --- a/runtime/base/mem_map_arena_pool.cc +++ b/runtime/base/mem_map_arena_pool.cc @@ -125,7 +125,7 @@ size_t MemMapArenaPool::GetBytesAllocated() const { } void MemMapArenaPool::FreeArenaChain(Arena* first) { - if (kRunningOnMemoryTool) { + if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { for (Arena* arena = first; arena != nullptr; arena = arena->next_) { MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_); } diff --git a/runtime/exec_utils_test.cc b/runtime/exec_utils_test.cc index a9c1ea2ae0..68edfa8b72 100644 --- a/runtime/exec_utils_test.cc +++ b/runtime/exec_utils_test.cc @@ -36,10 +36,8 @@ TEST_F(ExecUtilsTest, ExecSuccess) { command.push_back("/usr/bin/id"); } std::string error_msg; - if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { - // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks. - // TODO: Valgrind is no longer supported, but Address Sanitizer is: - // check whether the following code works with ASan. + if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { + // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks. EXPECT_TRUE(Exec(command, &error_msg)); } EXPECT_EQ(0U, error_msg.size()) << error_msg; @@ -52,10 +50,8 @@ TEST_F(ExecUtilsTest, ExecError) { std::vector command; command.push_back("bogus"); std::string error_msg; - if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { - // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks. - // TODO: Valgrind is no longer supported, but Address Sanitizer is: - // check whether the following code works with ASan. + if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { + // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks. EXPECT_FALSE(Exec(command, &error_msg)); EXPECT_FALSE(error_msg.empty()); } @@ -76,10 +72,8 @@ TEST_F(ExecUtilsTest, EnvSnapshotAdditionsAreNotVisible) { } command.push_back(kModifiedVariable); std::string error_msg; - if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { - // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks. - // TODO: Valgrind is no longer supported, but Address Sanitizer is: - // check whether the following code works with ASan. + if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { + // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks. EXPECT_FALSE(Exec(command, &error_msg)); EXPECT_NE(0U, error_msg.size()) << error_msg; } @@ -103,10 +97,8 @@ TEST_F(ExecUtilsTest, EnvSnapshotDeletionsAreNotVisible) { } command.push_back(kDeletedVariable); std::string error_msg; - if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { - // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks. - // TODO: Valgrind is no longer supported, but Address Sanitizer is: - // check whether the following code works with ASan. + if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { + // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks. EXPECT_TRUE(Exec(command, &error_msg)); EXPECT_EQ(0U, error_msg.size()) << error_msg; } diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h index 30213d55c5..150fe956ae 100644 --- a/runtime/gc/allocator/rosalloc.h +++ b/runtime/gc/allocator/rosalloc.h @@ -625,7 +625,7 @@ class RosAlloc { // If true, check that the returned memory is actually zero. static constexpr bool kCheckZeroMemory = kIsDebugBuild; - // Do not check memory when running under a memory tool. In a normal + // Valgrind protects memory, so do not check memory when running under valgrind. In a normal // build with kCheckZeroMemory the whole test should be optimized away. // TODO: Unprotect before checks. ALWAYS_INLINE bool ShouldCheckZeroMemory(); @@ -768,7 +768,7 @@ class RosAlloc { // greater than or equal to this value, release pages. const size_t page_release_size_threshold_; - // Whether this allocator is running on a memory tool. + // Whether this allocator is running under Valgrind. bool is_running_on_memory_tool_; // The base address of the memory region that's managed by this allocator. diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 675686830e..948d23303c 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -272,7 +272,7 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, } case kAllocatorTypeRosAlloc: { if (kInstrumented && UNLIKELY(is_running_on_memory_tool_)) { - // If running on ASan, we should be using the instrumented path. + // If running on valgrind or asan, we should be using the instrumented path. size_t max_bytes_tl_bulk_allocated = rosalloc_space_->MaxBytesBulkAllocatedFor(alloc_size); if (UNLIKELY(IsOutOfMemoryOnAllocation(allocator_type, max_bytes_tl_bulk_allocated, @@ -303,7 +303,7 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, } case kAllocatorTypeDlMalloc: { if (kInstrumented && UNLIKELY(is_running_on_memory_tool_)) { - // If running on ASan, we should be using the instrumented path. + // If running on valgrind, we should be using the instrumented path. ret = dlmalloc_space_->Alloc(self, alloc_size, bytes_allocated, diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 12021b7f99..b004566ed1 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -2248,8 +2248,7 @@ class ZygoteCompactingCollector FINAL : public collector::SemiSpace { // Add a new bin with the remaining space. AddBin(size - alloc_size, pos + alloc_size); } - // Copy the object over to its new location. - // Historical note: We did not use `alloc_size` to avoid a Valgrind error. + // Copy the object over to its new location. Don't use alloc_size to avoid valgrind error. memcpy(reinterpret_cast(forward_address), obj, obj_size); if (kUseBakerReadBarrier) { obj->AssertReadBarrierState(); diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc index a24ca32314..512cde484d 100644 --- a/runtime/gc/space/large_object_space.cc +++ b/runtime/gc/space/large_object_space.cc @@ -45,9 +45,8 @@ class MemoryToolLargeObjectMapSpace FINAL : public LargeObjectMapSpace { } ~MemoryToolLargeObjectMapSpace() OVERRIDE { - // Historical note: We were deleting large objects to keep Valgrind happy if there were - // any large objects such as Dex cache arrays which aren't freed since they are held live - // by the class linker. + // Keep valgrind happy if there is any large objects such as dex cache arrays which aren't + // freed since they are held live by the class linker. MutexLock mu(Thread::Current(), lock_); for (auto& m : large_objects_) { delete m.second.mem_map; diff --git a/runtime/gc/space/memory_tool_malloc_space-inl.h b/runtime/gc/space/memory_tool_malloc_space-inl.h index c022171082..8282f3dda7 100644 --- a/runtime/gc/space/memory_tool_malloc_space-inl.h +++ b/runtime/gc/space/memory_tool_malloc_space-inl.h @@ -30,14 +30,11 @@ namespace space { namespace memory_tool_details { template -inline mirror::Object* AdjustForMemoryTool(void* obj_with_rdz, - size_t num_bytes, - size_t bytes_allocated, - size_t usable_size, - size_t bytes_tl_bulk_allocated, - size_t* bytes_allocated_out, - size_t* usable_size_out, - size_t* bytes_tl_bulk_allocated_out) { +inline mirror::Object* AdjustForValgrind(void* obj_with_rdz, size_t num_bytes, + size_t bytes_allocated, size_t usable_size, + size_t bytes_tl_bulk_allocated, + size_t* bytes_allocated_out, size_t* usable_size_out, + size_t* bytes_tl_bulk_allocated_out) { if (bytes_allocated_out != nullptr) { *bytes_allocated_out = bytes_allocated; } @@ -87,31 +84,24 @@ template mirror::Object* MemoryToolMallocSpace::AllocWithGrowth( - Thread* self, - size_t num_bytes, - size_t* bytes_allocated_out, - size_t* usable_size_out, + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::AllocWithGrowth( + Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out, size_t* bytes_tl_bulk_allocated_out) { size_t bytes_allocated; size_t usable_size; size_t bytes_tl_bulk_allocated; - void* obj_with_rdz = S::AllocWithGrowth(self, - num_bytes + 2 * kMemoryToolRedZoneBytes, - &bytes_allocated, - &usable_size, + void* obj_with_rdz = S::AllocWithGrowth(self, num_bytes + 2 * kMemoryToolRedZoneBytes, + &bytes_allocated, &usable_size, &bytes_tl_bulk_allocated); if (obj_with_rdz == nullptr) { return nullptr; } - return memory_tool_details::AdjustForMemoryTool( - obj_with_rdz, - num_bytes, - bytes_allocated, - usable_size, + return memory_tool_details::AdjustForValgrind( + obj_with_rdz, num_bytes, + bytes_allocated, usable_size, bytes_tl_bulk_allocated, bytes_allocated_out, usable_size_out, @@ -123,35 +113,27 @@ template mirror::Object* MemoryToolMallocSpace::Alloc( - Thread* self, - size_t num_bytes, - size_t* bytes_allocated_out, - size_t* usable_size_out, + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::Alloc( + Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out, size_t* bytes_tl_bulk_allocated_out) { size_t bytes_allocated; size_t usable_size; size_t bytes_tl_bulk_allocated; - void* obj_with_rdz = S::Alloc(self, - num_bytes + 2 * kMemoryToolRedZoneBytes, - &bytes_allocated, - &usable_size, - &bytes_tl_bulk_allocated); + void* obj_with_rdz = S::Alloc(self, num_bytes + 2 * kMemoryToolRedZoneBytes, + &bytes_allocated, &usable_size, &bytes_tl_bulk_allocated); if (obj_with_rdz == nullptr) { return nullptr; } - return memory_tool_details::AdjustForMemoryTool( - obj_with_rdz, - num_bytes, - bytes_allocated, - usable_size, - bytes_tl_bulk_allocated, - bytes_allocated_out, - usable_size_out, - bytes_tl_bulk_allocated_out); + return memory_tool_details::AdjustForValgrind(obj_with_rdz, num_bytes, + bytes_allocated, usable_size, + bytes_tl_bulk_allocated, + bytes_allocated_out, + usable_size_out, + bytes_tl_bulk_allocated_out); } template mirror::Object* MemoryToolMallocSpace::AllocThreadUnsafe( - Thread* self, - size_t num_bytes, - size_t* bytes_allocated_out, - size_t* usable_size_out, + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::AllocThreadUnsafe( + Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out, size_t* bytes_tl_bulk_allocated_out) { size_t bytes_allocated; size_t usable_size; size_t bytes_tl_bulk_allocated; - void* obj_with_rdz = S::AllocThreadUnsafe(self, - num_bytes + 2 * kMemoryToolRedZoneBytes, - &bytes_allocated, - &usable_size, + void* obj_with_rdz = S::AllocThreadUnsafe(self, num_bytes + 2 * kMemoryToolRedZoneBytes, + &bytes_allocated, &usable_size, &bytes_tl_bulk_allocated); if (obj_with_rdz == nullptr) { return nullptr; } - return memory_tool_details::AdjustForMemoryTool( - obj_with_rdz, - num_bytes, - bytes_allocated, - usable_size, + return memory_tool_details::AdjustForValgrind( + obj_with_rdz, num_bytes, + bytes_allocated, usable_size, bytes_tl_bulk_allocated, bytes_allocated_out, usable_size_out, @@ -195,14 +170,12 @@ template size_t MemoryToolMallocSpace::AllocationSize( + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::AllocationSize( mirror::Object* obj, size_t* usable_size) { - size_t result = S::AllocationSize( - reinterpret_cast( - reinterpret_cast(obj) - - (kAdjustForRedzoneInAllocSize ? kMemoryToolRedZoneBytes : 0)), + size_t result = S::AllocationSize(reinterpret_cast( + reinterpret_cast(obj) - (kAdjustForRedzoneInAllocSize ? kMemoryToolRedZoneBytes : 0)), usable_size); if (usable_size != nullptr) { if (kUseObjSizeForUsable) { @@ -219,9 +192,10 @@ template size_t MemoryToolMallocSpace::Free(Thread* self, mirror::Object* ptr) { + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::Free( + Thread* self, mirror::Object* ptr) { void* obj_after_rdz = reinterpret_cast(ptr); uint8_t* obj_with_rdz = reinterpret_cast(obj_after_rdz) - kMemoryToolRedZoneBytes; @@ -246,10 +220,10 @@ template size_t MemoryToolMallocSpace::FreeList( - Thread* self, size_t num_ptrs, mirror::Object** ptrs) { + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::FreeList( + Thread* self, size_t num_ptrs, mirror::Object** ptrs) { size_t freed = 0; for (size_t i = 0; i < num_ptrs; i++) { freed += Free(self, ptrs[i]); @@ -264,12 +238,11 @@ template template MemoryToolMallocSpace::MemoryToolMallocSpace( - MemMap* mem_map, size_t initial_size, Params... params) - : S(mem_map, initial_size, params...) { - // Don't want to change the memory tool states of the mem map here as the allocator is already + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::MemoryToolMallocSpace( + MemMap* mem_map, size_t initial_size, Params... params) : S(mem_map, initial_size, params...) { + // Don't want to change the valgrind states of the mem map here as the allocator is already // initialized at this point and that may interfere with what the allocator does internally. Note // that the tail beyond the initial size is mprotected. } @@ -279,9 +252,9 @@ template size_t MemoryToolMallocSpace::MaxBytesBulkAllocatedFor(size_t num_bytes) { + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::MaxBytesBulkAllocatedFor(size_t num_bytes) { return S::MaxBytesBulkAllocatedFor(num_bytes + 2 * kMemoryToolRedZoneBytes); } diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc index d698cf20ae..e7865363a1 100644 --- a/runtime/gc/space/rosalloc_space.cc +++ b/runtime/gc/space/rosalloc_space.cc @@ -77,7 +77,7 @@ RosAllocSpace* RosAllocSpace::CreateFromMemMap(MemMap* mem_map, const std::strin // Everything is set so record in immutable structure and leave uint8_t* begin = mem_map->Begin(); - // TODO: Fix RosAllocSpace to support ASan. There is currently some issues with + // TODO: Fix RosAllocSpace to support Valgrind/ASan. There is currently some issues with // AllocationSize caused by redzones. b/12944686 if (running_on_memory_tool) { return new MemoryToolMallocSpace( @@ -382,12 +382,12 @@ size_t RosAllocSpace::AllocationSizeNonvirtual(mirror::Object* obj, size_t* usab size_t size = obj->SizeOf(); bool add_redzones = false; if (kMaybeIsRunningOnMemoryTool) { - add_redzones = kRunningOnMemoryTool ? kMemoryToolAddsRedzones : 0; + add_redzones = RUNNING_ON_MEMORY_TOOL ? kMemoryToolAddsRedzones : 0; if (add_redzones) { size += 2 * kDefaultMemoryToolRedZoneBytes; } } else { - DCHECK(!kRunningOnMemoryTool); + DCHECK_EQ(RUNNING_ON_MEMORY_TOOL, 0U); } size_t size_by_size = rosalloc_->UsableSize(size); if (kIsDebugBuild) { diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h index 4c17233360..9d16b87b7d 100644 --- a/runtime/gc/space/rosalloc_space.h +++ b/runtime/gc/space/rosalloc_space.h @@ -159,8 +159,8 @@ class RosAllocSpace : public MallocSpace { void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size, size_t maximum_size, bool low_memory_mode) OVERRIDE { - return CreateRosAlloc( - base, morecore_start, initial_size, maximum_size, low_memory_mode, kRunningOnMemoryTool); + return CreateRosAlloc(base, morecore_start, initial_size, maximum_size, low_memory_mode, + RUNNING_ON_MEMORY_TOOL != 0); } static allocator::RosAlloc* CreateRosAlloc(void* base, size_t morecore_start, size_t initial_size, size_t maximum_size, bool low_memory_mode, diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc index aeb5f4b1ff..fd435627bf 100644 --- a/runtime/interpreter/unstarted_runtime_test.cc +++ b/runtime/interpreter/unstarted_runtime_test.cc @@ -864,6 +864,11 @@ TEST_F(UnstartedRuntimeTest, Cos) { } TEST_F(UnstartedRuntimeTest, Pow) { + // Valgrind seems to get this wrong, actually. Disable for valgrind. + if (RUNNING_ON_MEMORY_TOOL != 0 && kMemoryToolIsValgrind) { + return; + } + Thread* self = Thread::Current(); ScopedObjectAccess soa(self); diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 42a4a8d204..0684b461ae 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -347,7 +347,7 @@ void Jit::DeleteThreadPool() { } // When running sanitized, let all tasks finish to not leak. Otherwise just clear the queue. - if (!kRunningOnMemoryTool) { + if (!RUNNING_ON_MEMORY_TOOL) { pool->StopWorkers(self); pool->RemoveAllTasks(self); } diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc index b3a47c3053..14f3f45f9e 100644 --- a/runtime/native_stack_dump.cc +++ b/runtime/native_stack_dump.cc @@ -289,10 +289,8 @@ void DumpNativeStack(std::ostream& os, ArtMethod* current_method, void* ucontext_ptr, bool skip_frames) { - // Historical note: This was disabled when running under Valgrind (b/18119146). - // TODO: Valgrind is no longer supported, but Address Sanitizer is: - // check whether this test works with ASan. - if (kRunningOnMemoryTool) { + // b/18119146 + if (RUNNING_ON_MEMORY_TOOL != 0) { return; } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index d0f365e2da..b8775b874f 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -245,7 +245,7 @@ Runtime::Runtime() exit_(nullptr), abort_(nullptr), stats_enabled_(false), - is_running_on_memory_tool_(kRunningOnMemoryTool), + is_running_on_memory_tool_(RUNNING_ON_MEMORY_TOOL), instrumentation_(), main_thread_group_(nullptr), system_thread_group_(nullptr), @@ -1355,10 +1355,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { case InstructionSet::kMips: case InstructionSet::kMips64: implicit_null_checks_ = true; - // Installing stack protection does not play well with Valgrind. - // TODO: Valgrind is no longer supported, but Address Sanitizer is: - // check whether setting `implicit_so_checks_` to `true` works with ASan. - implicit_so_checks_ = !kRunningOnMemoryTool; + // Installing stack protection does not play well with valgrind. + implicit_so_checks_ = !(RUNNING_ON_MEMORY_TOOL && kMemoryToolIsValgrind); break; default: // Keep the defaults. @@ -1373,8 +1371,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // These need to be in a specific order. The null point check handler must be // after the suspend check and stack overflow check handlers. // - // Note: the instances attach themselves to the fault manager and are handled by it. The - // manager will delete the instance on Shutdown(). + // Note: the instances attach themselves to the fault manager and are handled by it. The manager + // will delete the instance on Shutdown(). if (implicit_suspend_checks_) { new SuspensionHandler(&fault_manager); } diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc index 54769f9c49..72d9919971 100644 --- a/runtime/runtime_callbacks_test.cc +++ b/runtime/runtime_callbacks_test.cc @@ -339,8 +339,8 @@ class RuntimeSigQuitCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest { }; TEST_F(RuntimeSigQuitCallbackRuntimeCallbacksTest, SigQuit) { - // SigQuit induces a dump. ASan isn't happy with libunwind reading memory. - TEST_DISABLED_FOR_MEMORY_TOOL(); + // SigQuit induces a dump. ASAN isn't happy with libunwind reading memory. + TEST_DISABLED_FOR_MEMORY_TOOL_ASAN(); // The runtime needs to be started for the signal handler. Thread* self = Thread::Current(); diff --git a/runtime/thread.cc b/runtime/thread.cc index 73701a318b..eada24d257 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1115,10 +1115,21 @@ bool Thread::InitStackHwm() { Runtime* runtime = Runtime::Current(); bool implicit_stack_check = !runtime->ExplicitStackOverflowChecks() && !runtime->IsAotCompiler(); + // Valgrind on arm doesn't give the right values here. Do not install the guard page, and + // effectively disable stack overflow checks (we'll get segfaults, potentially) by setting + // stack_begin to 0. + const bool valgrind_on_arm = + (kRuntimeISA == InstructionSet::kArm || kRuntimeISA == InstructionSet::kArm64) && + kMemoryToolIsValgrind && + RUNNING_ON_MEMORY_TOOL != 0; + if (valgrind_on_arm) { + tlsPtr_.stack_begin = nullptr; + } + ResetDefaultStackEnd(); // Install the protected region if we are doing implicit overflow checks. - if (implicit_stack_check) { + if (implicit_stack_check && !valgrind_on_arm) { // The thread might have protected region at the bottom. We need // to install our own region so we need to move the limits // of the stack to make room for it. diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc index a4d0d0cbe4..49db0c82b5 100644 --- a/test/137-cfi/cfi.cc +++ b/test/137-cfi/cfi.cc @@ -111,6 +111,8 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess( jint, jboolean) { #if __linux__ + // TODO: What to do on Valgrind? + std::unique_ptr bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid())); if (!bt->Unwind(0, nullptr)) { printf("Cannot unwind in process.\n"); @@ -186,6 +188,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess( jboolean, jint pid_int) { #if __linux__ + // TODO: What to do on Valgrind? pid_t pid = static_cast(pid_int); // OK, this is painful. debuggerd uses ptrace to unwind other processes. diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index faa4d91f58..e0757abbe0 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -266,16 +266,14 @@ target_config = { } }, 'art-gtest-valgrind32': { - # Disabled: Valgrind is no longer supported. - # Historical note: This was already disabled, as x86 valgrind did not understand SSE4.x + # Disabled: x86 valgrind does not understand SSE4.x # 'make' : 'valgrind-test-art-host32', 'env': { 'ART_USE_READ_BARRIER' : 'false' } }, 'art-gtest-valgrind64': { - # Disabled: Valgrind is no longer supported. - # 'make' : 'valgrind-test-art-host64', + 'make' : 'valgrind-test-art-host64', 'env': { 'ART_USE_READ_BARRIER' : 'false' } diff --git a/test/valgrind-suppressions.txt b/test/valgrind-suppressions.txt new file mode 100644 index 0000000000..a97d03c2d4 --- /dev/null +++ b/test/valgrind-suppressions.txt @@ -0,0 +1,87 @@ +{ + b/27596582 + Memcheck:Cond + fun:index + fun:expand_dynamic_string_token + fun:_dl_map_object + fun:map_doit + fun:_dl_catch_error + fun:do_preload + fun:dl_main + fun:_dl_sysdep_start + fun:_dl_start_final + fun:_dl_start + obj:/lib/x86_64-linux-gnu/ld-2.19.so +} + +{ + b/31275764 + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + ... + fun:_ZN3art7Runtime17InitNativeMethodsEv +} + +# SigQuit runs libbacktrace +{ + BackTraceReading64 + Memcheck:Addr8 + fun:access_mem_unrestricted + fun:_Uelf64_memory_read + fun:_Uelf64_valid_object_memory + fun:map_create_list + fun:unw_map_local_create + fun:_ZN14UnwindMapLocal5BuildEv + fun:_ZN12BacktraceMap6CreateEib +} +{ + BackTraceReading32 + Memcheck:Addr4 + fun:access_mem_unrestricted + fun:_Uelf32_memory_read + fun:_Uelf32_valid_object_memory + fun:map_create_list + fun:unw_map_local_create + fun:_ZN14UnwindMapLocal5BuildEv + fun:_ZN12BacktraceMap6CreateEib +} +{ + BackTraceReading64 + Memcheck:Addr8 + fun:access_mem_unrestricted + fun:_Uelf64_memory_read + fun:_Uelf64_get_load_base + fun:map_create_list + fun:unw_map_local_create + fun:_ZN14UnwindMapLocal5BuildEv + fun:_ZN12BacktraceMap6CreateEib +} +{ + BackTraceReading32 + Memcheck:Addr4 + fun:access_mem_unrestricted + fun:_Uelf32_memory_read + fun:_Uelf32_get_load_base + fun:map_create_list + fun:unw_map_local_create + fun:_ZN14UnwindMapLocal5BuildEv + fun:_ZN12BacktraceMap6CreateEib +} + +{ + process_vm_readv + Memcheck:Param + process_vm_readv(lvec[...]) + fun:process_vm_readv +} + +# Suppressions for IsAddressMapped check in MemMapTest +{ + MemMapTest_IsAddressMapped + Memcheck:Param + msync(start) + ... + fun:_ZN3art10MemMapTest15IsAddressMappedEPv + ... +} diff --git a/test/valgrind-target-suppressions.txt b/test/valgrind-target-suppressions.txt new file mode 100644 index 0000000000..0d63a1c7aa --- /dev/null +++ b/test/valgrind-target-suppressions.txt @@ -0,0 +1,76 @@ +# Valgrind does not recognize the ashmen ioctl() calls on ARM64, so it assumes that a size +# parameter is a pointer. +{ + ashmem ioctl + Memcheck:Param + ioctl(generic) + ... + fun:ioctl + fun:ashmem_create_region +} + +# It seems that on ARM64 Valgrind considers the canary value used by the Clang stack protector to +# be an uninitialized value. +{ + jemalloc chunk_alloc_cache + Memcheck:Cond + fun:je_chunk_alloc_cache +} + +# The VectorImpl class does not hold a pointer to the allocated SharedBuffer structure, but to the +# beginning of the data, which is effectively an interior pointer. Valgrind has limitations when +# dealing with interior pointers. +{ + VectorImpl + Memcheck:Leak + match-leak-kinds:possible + fun:malloc + # The wildcards make this rule work both for 32-bit and 64-bit environments. + fun:_ZN7android12SharedBuffer5allocE? + fun:_ZN7android10VectorImpl5_growE?? +} + +# Clang/LLVM uses memcpy for *x = *y, even though x == y (which is undefined behavior). Ignore. +# b/29279679, https://llvm.org/bugs/show_bug.cgi?id=11763 +{ + MemCpySelfAssign + Memcheck:Overlap + fun:memcpy + ... + fun:je_malloc_tsd_boot0 +} + +# Setenv is known-leaking when overwriting mappings. This is triggered by re-initializing +# ANDROID_DATA. Ignore all setenv leaks. +{ + SetenvAndroidDataReinit + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:setenv +} + +{ + b/31275764 + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + ... + fun:_ZN3art7Runtime17InitNativeMethodsEv +} + +# art::MemMap::MapInternal() uses msync() to check for the existence of memory mappings. +{ + art::MemMap::MapInternal() + Memcheck:Param + msync(start) + fun:msync + fun:_ZN3art6MemMap11MapInternalEPvmiiilb +} + +{ + process_vm_readv + Memcheck:Param + process_vm_readv(lvec[...]) + fun:process_vm_readv +} diff --git a/tools/art b/tools/art index 781ee2f9cb..1c603d4fa7 100644 --- a/tools/art +++ b/tools/art @@ -77,6 +77,7 @@ Usage: art [OPTIONS] [--] [ART_OPTIONS] CLASS Supported OPTIONS include: --32 Use the 32-bit Android Runtime. --64 Use the 64-bit Android Runtime. + --callgrind Launch the Android Runtime in callgrind. -d Use the debug ART library (libartd.so). --debug Equivalent to -d. --gdb Launch the Android Runtime in gdb. @@ -268,6 +269,9 @@ while [[ "$1" = "-"* ]]; do --64) ART_BINARY=dalvikvm64 ;; + --callgrind) + LAUNCH_WRAPPER="valgrind --tool=callgrind" + ;; -d) ;& # Fallthrough --debug) -- GitLab From 6ec2a1bf1cbecf17546df780dd0ad769042e1874 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 22 May 2018 15:33:48 +0100 Subject: [PATCH 442/749] ObjPtr<>-ify UnstartedRuntime, fix 2 stale reference uses. Test: Rely on TreeHugger. Bug: 31113334 Change-Id: I35f76c3e3b94dfca18dbe67aba065a1270f4e5ee --- .../quick/quick_trampoline_entrypoints.cc | 1 + runtime/interpreter/interpreter.cc | 5 +- runtime/interpreter/interpreter_common.cc | 21 ++++- runtime/interpreter/interpreter_common.h | 18 ---- .../interpreter/interpreter_switch_impl.cc | 17 ++-- runtime/interpreter/mterp/mterp.cc | 9 +- runtime/interpreter/shadow_frame-inl.h | 43 ++++++++++ runtime/interpreter/shadow_frame.h | 15 +--- runtime/interpreter/unstarted_runtime.cc | 31 ++++--- runtime/interpreter/unstarted_runtime_test.cc | 83 +++++++++++-------- runtime/method_handles-inl.h | 74 +++++++++++++++++ runtime/method_handles.cc | 1 + runtime/method_handles.h | 76 +---------------- runtime/mirror/var_handle.cc | 2 +- runtime/quick_exception_handler.cc | 1 + runtime/stack.cc | 2 +- runtime/thread.cc | 2 +- 17 files changed, 229 insertions(+), 172 deletions(-) create mode 100644 runtime/interpreter/shadow_frame-inl.h diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 0a186f4dc5..4ca12fe472 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -33,6 +33,7 @@ #include "index_bss_mapping.h" #include "instrumentation.h" #include "interpreter/interpreter.h" +#include "interpreter/shadow_frame-inl.h" #include "jit/jit.h" #include "linear_alloc.h" #include "method_handles.h" diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index f23304c391..048c6e4d66 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -31,6 +31,7 @@ #include "mterp/mterp.h" #include "nativehelper/scoped_local_ref.h" #include "scoped_thread_state_change-inl.h" +#include "shadow_frame-inl.h" #include "stack.h" #include "thread-inl.h" #include "unstarted_runtime.h" @@ -419,7 +420,7 @@ void EnterInterpreterFromInvoke(Thread* self, size_t cur_reg = num_regs - num_ins; if (!method->IsStatic()) { CHECK(receiver != nullptr); - shadow_frame->SetVRegReference(cur_reg, receiver.Ptr()); + shadow_frame->SetVRegReference(cur_reg, receiver); ++cur_reg; } uint32_t shorty_len = 0; @@ -430,7 +431,7 @@ void EnterInterpreterFromInvoke(Thread* self, case 'L': { ObjPtr o = reinterpret_cast*>(&args[arg_pos])->AsMirrorPtr(); - shadow_frame->SetVRegReference(cur_reg, o.Ptr()); + shadow_frame->SetVRegReference(cur_reg, o); break; } case 'J': case 'D': { diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 5a50ec5586..708a7884fa 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -34,6 +34,7 @@ #include "mirror/var_handle.h" #include "reflection-inl.h" #include "reflection.h" +#include "shadow_frame-inl.h" #include "stack.h" #include "thread-inl.h" #include "transaction.h" @@ -1428,6 +1429,24 @@ bool DoInvokeCustom(Thread* self, } } +// Assign register 'src_reg' from shadow_frame to register 'dest_reg' into new_shadow_frame. +static inline void AssignRegister(ShadowFrame* new_shadow_frame, const ShadowFrame& shadow_frame, + size_t dest_reg, size_t src_reg) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Uint required, so that sign extension does not make this wrong on 64b systems + uint32_t src_value = shadow_frame.GetVReg(src_reg); + ObjPtr o = shadow_frame.GetVRegReference(src_reg); + + // If both register locations contains the same value, the register probably holds a reference. + // Note: As an optimization, non-moving collectors leave a stale reference value + // in the references array even after the original vreg was overwritten to a non-reference. + if (src_value == reinterpret_cast(o.Ptr())) { + new_shadow_frame->SetVRegReference(dest_reg, o); + } else { + new_shadow_frame->SetVReg(dest_reg, src_value); + } +} + template inline void CopyRegisters(ShadowFrame& caller_frame, ShadowFrame* callee_frame, @@ -1612,7 +1631,7 @@ static inline bool DoCallCommon(ArtMethod* called_method, return false; } } - new_shadow_frame->SetVRegReference(dest_reg, o.Ptr()); + new_shadow_frame->SetVRegReference(dest_reg, o); break; } // Handle doubles and longs. 2 consecutive virtual register slots. diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 67a0349d7a..37234e1462 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -543,24 +543,6 @@ static inline bool IsBackwardBranch(int32_t branch_offset) { return branch_offset <= 0; } -// Assign register 'src_reg' from shadow_frame to register 'dest_reg' into new_shadow_frame. -static inline void AssignRegister(ShadowFrame* new_shadow_frame, const ShadowFrame& shadow_frame, - size_t dest_reg, size_t src_reg) - REQUIRES_SHARED(Locks::mutator_lock_) { - // Uint required, so that sign extension does not make this wrong on 64b systems - uint32_t src_value = shadow_frame.GetVReg(src_reg); - ObjPtr o = shadow_frame.GetVRegReference(src_reg); - - // If both register locations contains the same value, the register probably holds a reference. - // Note: As an optimization, non-moving collectors leave a stale reference value - // in the references array even after the original vreg was overwritten to a non-reference. - if (src_value == reinterpret_cast(o.Ptr())) { - new_shadow_frame->SetVRegReference(dest_reg, o.Ptr()); - } else { - new_shadow_frame->SetVReg(dest_reg, src_value); - } -} - // The arg_offset is the offset to the first input register in the frame. void ArtInterpreterToCompiledCodeBridge(Thread* self, ArtMethod* caller, diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 5c7838cd66..27626295c1 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -24,6 +24,7 @@ #include "jit/jit.h" #include "jvalue-inl.h" #include "safe_math.h" +#include "shadow_frame-inl.h" namespace art { namespace interpreter { @@ -299,7 +300,7 @@ void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { PREAMBLE(); ObjPtr exception = self->GetException(); DCHECK(exception != nullptr) << "No pending exception on MOVE_EXCEPTION instruction"; - shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception.Ptr()); + shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception); self->ClearException(); inst = inst->Next_1xx(); break; @@ -514,7 +515,7 @@ void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { if (UNLIKELY(s == nullptr)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), s.Ptr()); + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), s); inst = inst->Next_2xx(); } break; @@ -527,7 +528,7 @@ void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { if (UNLIKELY(s == nullptr)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_31c(inst_data), s.Ptr()); + shadow_frame.SetVRegReference(inst->VRegA_31c(inst_data), s); inst = inst->Next_3xx(); } break; @@ -542,7 +543,7 @@ void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { if (UNLIKELY(c == nullptr)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), c.Ptr()); + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), c); inst = inst->Next_2xx(); } break; @@ -556,7 +557,7 @@ void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { if (UNLIKELY(mh == nullptr)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mh.Ptr()); + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mh); inst = inst->Next_2xx(); } break; @@ -570,7 +571,7 @@ void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { if (UNLIKELY(mt == nullptr)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mt.Ptr()); + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mt); inst = inst->Next_2xx(); } break; @@ -681,7 +682,7 @@ void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { HANDLE_PENDING_EXCEPTION(); break; } - shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), obj.Ptr()); + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), obj); inst = inst->Next_2xx(); } break; @@ -698,7 +699,7 @@ void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { if (UNLIKELY(obj == nullptr)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_22c(inst_data), obj.Ptr()); + shadow_frame.SetVRegReference(inst->VRegA_22c(inst_data), obj); inst = inst->Next_2xx(); } break; diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 1b39a7422d..d62f511ad5 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -24,6 +24,7 @@ #include "entrypoints/entrypoint_utils-inl.h" #include "interpreter/interpreter_common.h" #include "interpreter/interpreter_intrinsics.h" +#include "interpreter/shadow_frame-inl.h" namespace art { namespace interpreter { @@ -369,7 +370,7 @@ extern "C" size_t MterpConstString(uint32_t index, if (UNLIKELY(s == nullptr)) { return true; } - shadow_frame->SetVRegReference(tgt_vreg, s.Ptr()); + shadow_frame->SetVRegReference(tgt_vreg, s); return false; } @@ -386,7 +387,7 @@ extern "C" size_t MterpConstClass(uint32_t index, if (UNLIKELY(c == nullptr)) { return true; } - shadow_frame->SetVRegReference(tgt_vreg, c.Ptr()); + shadow_frame->SetVRegReference(tgt_vreg, c); return false; } @@ -399,7 +400,7 @@ extern "C" size_t MterpConstMethodHandle(uint32_t index, if (UNLIKELY(mh == nullptr)) { return true; } - shadow_frame->SetVRegReference(tgt_vreg, mh.Ptr()); + shadow_frame->SetVRegReference(tgt_vreg, mh); return false; } @@ -413,7 +414,7 @@ extern "C" size_t MterpConstMethodType(uint32_t index, if (UNLIKELY(mt == nullptr)) { return true; } - shadow_frame->SetVRegReference(tgt_vreg, mt.Ptr()); + shadow_frame->SetVRegReference(tgt_vreg, mt); return false; } diff --git a/runtime/interpreter/shadow_frame-inl.h b/runtime/interpreter/shadow_frame-inl.h new file mode 100644 index 0000000000..7eaad597d2 --- /dev/null +++ b/runtime/interpreter/shadow_frame-inl.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#ifndef ART_RUNTIME_INTERPRETER_SHADOW_FRAME_INL_H_ +#define ART_RUNTIME_INTERPRETER_SHADOW_FRAME_INL_H_ + +#include "shadow_frame.h" + +#include "obj_ptr-inl.h" + +namespace art { + +template +inline void ShadowFrame::SetVRegReference(size_t i, ObjPtr val) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_LT(i, NumberOfVRegs()); + if (kVerifyFlags & kVerifyWrites) { + VerifyObject(val); + } + ReadBarrier::MaybeAssertToSpaceInvariant(val.Ptr()); + uint32_t* vreg = &vregs_[i]; + reinterpret_cast*>(vreg)->Assign(val); + if (HasReferenceArray()) { + References()[i].Assign(val); + } +} + +} // namespace art + +#endif // ART_RUNTIME_INTERPRETER_SHADOW_FRAME_INL_H_ diff --git a/runtime/interpreter/shadow_frame.h b/runtime/interpreter/shadow_frame.h index d5451ffded..f76b86c94f 100644 --- a/runtime/interpreter/shadow_frame.h +++ b/runtime/interpreter/shadow_frame.h @@ -37,6 +37,7 @@ class Object; class ArtMethod; class ShadowFrame; +template class ObjPtr; class Thread; union JValue; @@ -245,18 +246,8 @@ class ShadowFrame { } template - void SetVRegReference(size_t i, mirror::Object* val) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK_LT(i, NumberOfVRegs()); - if (kVerifyFlags & kVerifyWrites) { - VerifyObject(val); - } - ReadBarrier::MaybeAssertToSpaceInvariant(val); - uint32_t* vreg = &vregs_[i]; - reinterpret_cast*>(vreg)->Assign(val); - if (HasReferenceArray()) { - References()[i].Assign(val); - } - } + void SetVRegReference(size_t i, ObjPtr val) + REQUIRES_SHARED(Locks::mutator_lock_); void SetMethod(ArtMethod* method) REQUIRES(Locks::mutator_lock_) { DCHECK(method != nullptr); diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 0e429a63f6..7abb007838 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -131,7 +131,7 @@ static void UnstartedRuntimeFindClass(Thread* self, Handle class std::string descriptor(DotToDescriptor(className->ToModifiedUtf8().c_str())); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - mirror::Class* found = class_linker->FindClass(self, descriptor.c_str(), class_loader); + ObjPtr found = class_linker->FindClass(self, descriptor.c_str(), class_loader); if (found == nullptr && abort_if_not_found) { if (!self->IsExceptionPending()) { AbortTransactionOrFail(self, "%s failed in un-started runtime for class: %s", @@ -142,7 +142,7 @@ static void UnstartedRuntimeFindClass(Thread* self, Handle class } if (found != nullptr && initialize_class) { StackHandleScope<1> hs(self); - Handle h_class(hs.NewHandle(found)); + HandleWrapperObjPtr h_class = hs.NewHandleWrapper(&found); if (!class_linker->EnsureInitialized(self, h_class, true, true)) { CHECK(self->IsExceptionPending()); return; @@ -269,8 +269,7 @@ void UnstartedRuntime::UnstartedClassNewInstance( AbortTransactionOrFail(self, "Null-pointer in Class.newInstance."); return; } - mirror::Class* klass = param->AsClass(); - Handle h_klass(hs.NewHandle(klass)); + Handle h_klass(hs.NewHandle(param->AsClass())); // Check that it's not null. if (h_klass == nullptr) { @@ -280,7 +279,7 @@ void UnstartedRuntime::UnstartedClassNewInstance( // If we're in a transaction, class must not be finalizable (it or a superclass has a finalizer). if (Runtime::Current()->IsActiveTransaction()) { - if (h_klass.Get()->IsFinalizable()) { + if (h_klass->IsFinalizable()) { AbortTransactionF(self, "Class for newInstance is finalizable: '%s'", h_klass->PrettyClass().c_str()); return; @@ -299,7 +298,7 @@ void UnstartedRuntime::UnstartedClassNewInstance( cons = nullptr; } if (cons != nullptr) { - Handle h_obj(hs.NewHandle(klass->AllocObject(self))); + Handle h_obj(hs.NewHandle(h_klass->AllocObject(self))); CHECK(h_obj != nullptr); // We don't expect OOM at compile-time. EnterInterpreterFromInvoke(self, cons, h_obj.Get(), nullptr, nullptr); if (!self->IsExceptionPending()) { @@ -323,8 +322,8 @@ void UnstartedRuntime::UnstartedClassGetDeclaredField( Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { // Special managed code cut-out to allow field lookup in a un-started runtime that'd fail // going the reflective Dex way. - mirror::Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); - mirror::String* name2 = shadow_frame->GetVRegReference(arg_offset + 1)->AsString(); + ObjPtr klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); + ObjPtr name2 = shadow_frame->GetVRegReference(arg_offset + 1)->AsString(); ArtField* found = nullptr; for (ArtField& field : klass->GetIFields()) { if (name2->Equals(field.GetName())) { @@ -376,13 +375,13 @@ void UnstartedRuntime::UnstartedClassGetDeclaredField( void UnstartedRuntime::UnstartedClassGetDeclaredMethod( Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { // Special managed code cut-out to allow method lookup in a un-started runtime. - mirror::Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); + ObjPtr klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); if (klass == nullptr) { ThrowNullPointerExceptionForMethodAccess(shadow_frame->GetMethod(), InvokeType::kVirtual); return; } - mirror::String* name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString(); - mirror::ObjectArray* args = + ObjPtr name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString(); + ObjPtr> args = shadow_frame->GetVRegReference(arg_offset + 2)->AsObjectArray(); Runtime* runtime = Runtime::Current(); bool transaction = runtime->IsActiveTransaction(); @@ -414,7 +413,7 @@ void UnstartedRuntime::UnstartedClassGetDeclaredMethod( // Special managed code cut-out to allow constructor lookup in a un-started runtime. void UnstartedRuntime::UnstartedClassGetDeclaredConstructor( Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { - mirror::Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); + ObjPtr klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); if (klass == nullptr) { ThrowNullPointerExceptionForMethodAccess(shadow_frame->GetMethod(), InvokeType::kVirtual); return; @@ -830,12 +829,12 @@ void UnstartedRuntime::UnstartedSystemArraycopy( } // Type checking. - mirror::Class* src_type = shadow_frame->GetVRegReference(arg_offset)->GetClass()-> + ObjPtr src_type = shadow_frame->GetVRegReference(arg_offset)->GetClass()-> GetComponentType(); if (!src_type->IsPrimitive()) { // Check that the second type is not primitive. - mirror::Class* trg_type = shadow_frame->GetVRegReference(arg_offset + 2)->GetClass()-> + ObjPtr trg_type = shadow_frame->GetVRegReference(arg_offset + 2)->GetClass()-> GetComponentType(); if (trg_type->IsPrimitiveInt()) { AbortTransactionOrFail(self, "Type mismatch in arraycopy: %s vs %s", @@ -1894,7 +1893,7 @@ void UnstartedRuntime::UnstartedJNIUnsafePutObject( void UnstartedRuntime::UnstartedJNIUnsafeGetArrayBaseOffsetForComponentType( Thread* self ATTRIBUTE_UNUSED, ArtMethod* method ATTRIBUTE_UNUSED, mirror::Object* receiver ATTRIBUTE_UNUSED, uint32_t* args, JValue* result) { - mirror::Class* component = reinterpret_cast(args[0])->AsClass(); + ObjPtr component = reinterpret_cast(args[0])->AsClass(); Primitive::Type primitive_type = component->GetPrimitiveType(); result->SetI(mirror::Array::DataOffset(Primitive::ComponentSize(primitive_type)).Int32Value()); } @@ -1902,7 +1901,7 @@ void UnstartedRuntime::UnstartedJNIUnsafeGetArrayBaseOffsetForComponentType( void UnstartedRuntime::UnstartedJNIUnsafeGetArrayIndexScaleForComponentType( Thread* self ATTRIBUTE_UNUSED, ArtMethod* method ATTRIBUTE_UNUSED, mirror::Object* receiver ATTRIBUTE_UNUSED, uint32_t* args, JValue* result) { - mirror::Class* component = reinterpret_cast(args[0])->AsClass(); + ObjPtr component = reinterpret_cast(args[0])->AsClass(); Primitive::Type primitive_type = component->GetPrimitiveType(); result->SetI(Primitive::ComponentSize(primitive_type)); } diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc index fd435627bf..860de2c28b 100644 --- a/runtime/interpreter/unstarted_runtime_test.cc +++ b/runtime/interpreter/unstarted_runtime_test.cc @@ -35,6 +35,7 @@ #include "mirror/string-inl.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" +#include "shadow_frame-inl.h" #include "thread.h" #include "transaction.h" @@ -82,7 +83,7 @@ class UnstartedRuntimeTest : public CommonRuntimeTest { // Note: as we have to use handles, we use StackHandleScope to transfer data. Hardcode a size // of three everywhere. That is enough to test all cases. - static mirror::ObjectArray* CreateObjectArray( + static ObjPtr> CreateObjectArray( Thread* self, ObjPtr component_type, const StackHandleScope<3>& data) @@ -98,10 +99,10 @@ class UnstartedRuntimeTest : public CommonRuntimeTest { result->Set(static_cast(i), data.GetReference(i)); CHECK(!self->IsExceptionPending()); } - return result.Ptr(); + return result; } - static void CheckObjectArray(mirror::ObjectArray* array, + static void CheckObjectArray(ObjPtr> array, const StackHandleScope<3>& data) REQUIRES_SHARED(Locks::mutator_lock_) { CHECK_EQ(array->GetLength(), 3); @@ -114,9 +115,9 @@ class UnstartedRuntimeTest : public CommonRuntimeTest { void RunArrayCopy(Thread* self, ShadowFrame* tmp, bool expect_exception, - mirror::ObjectArray* src, + ObjPtr> src, int32_t src_pos, - mirror::ObjectArray* dst, + ObjPtr> dst, int32_t dst_pos, int32_t length) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -137,8 +138,8 @@ class UnstartedRuntimeTest : public CommonRuntimeTest { void RunArrayCopy(Thread* self, ShadowFrame* tmp, bool expect_exception, - mirror::Class* src_component_class, - mirror::Class* dst_component_class, + ObjPtr src_component_class, + ObjPtr dst_component_class, const StackHandleScope<3>& src_data, int32_t src_pos, const StackHandleScope<3>& dst_data, @@ -366,7 +367,7 @@ TEST_F(UnstartedRuntimeTest, StringCharAt) { // TODO: Actual UTF. constexpr const char* base_string = "abcdefghijklmnop"; int32_t base_len = static_cast(strlen(base_string)); - mirror::String* test_string = mirror::String::AllocFromModifiedUtf8(self, base_string); + ObjPtr test_string = mirror::String::AllocFromModifiedUtf8(self, base_string); JValue result; ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); @@ -386,7 +387,7 @@ TEST_F(UnstartedRuntimeTest, StringCharAt) { TEST_F(UnstartedRuntimeTest, StringInit) { Thread* self = Thread::Current(); ScopedObjectAccess soa(self); - mirror::Class* klass = mirror::String::GetJavaLangString(); + ObjPtr klass = mirror::String::GetJavaLangString(); ArtMethod* method = klass->FindConstructor("(Ljava/lang/String;)V", Runtime::Current()->GetClassLinker()->GetImagePointerSize()); @@ -398,10 +399,13 @@ TEST_F(UnstartedRuntimeTest, StringInit) { JValue result; ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, method, 0); const char* base_string = "hello_world"; - mirror::String* string_arg = mirror::String::AllocFromModifiedUtf8(self, base_string); - mirror::String* reference_empty_string = mirror::String::AllocFromModifiedUtf8(self, ""); - shadow_frame->SetVRegReference(0, reference_empty_string); - shadow_frame->SetVRegReference(1, string_arg); + StackHandleScope<2> hs(self); + Handle string_arg = + hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, base_string)); + Handle reference_empty_string = + hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "")); + shadow_frame->SetVRegReference(0, reference_empty_string.Get()); + shadow_frame->SetVRegReference(1, string_arg.Get()); interpreter::DoCall(method, self, @@ -409,7 +413,7 @@ TEST_F(UnstartedRuntimeTest, StringInit) { Instruction::At(inst_data), inst_data[0], &result); - mirror::String* string_result = reinterpret_cast(result.GetL()); + ObjPtr string_result = down_cast(result.GetL()); EXPECT_EQ(string_arg->GetLength(), string_result->GetLength()); if (string_arg->IsCompressed() && string_result->IsCompressed()) { @@ -442,7 +446,7 @@ TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTestExceptions) { // Note: all tests are not GC safe. Assume there's no GC running here with the few objects we // allocate. - StackHandleScope<2> hs_misc(self); + StackHandleScope<3> hs_misc(self); Handle object_class( hs_misc.NewHandle(mirror::Class::GetJavaLangClass()->GetSuperClass())); @@ -461,10 +465,10 @@ TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTestExceptions) { RunArrayCopy(self, tmp, true, array.Get(), 0, array.Get(), 1, 3); RunArrayCopy(self, tmp, true, array.Get(), 1, array.Get(), 0, 3); - mirror::ObjectArray* class_as_array = - reinterpret_cast*>(object_class.Get()); - RunArrayCopy(self, tmp, true, class_as_array, 0, array.Get(), 0, 0); - RunArrayCopy(self, tmp, true, array.Get(), 0, class_as_array, 0, 0); + Handle> class_as_array = + hs_misc.NewHandle(reinterpret_cast*>(object_class.Get())); + RunArrayCopy(self, tmp, true, class_as_array.Get(), 0, array.Get(), 0, 0); + RunArrayCopy(self, tmp, true, array.Get(), 0, class_as_array.Get(), 0, 0); ShadowFrame::DeleteDeoptimizedFrame(tmp); } @@ -897,7 +901,7 @@ TEST_F(UnstartedRuntimeTest, IsAnonymousClass) { JValue result; ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); - mirror::Class* class_klass = mirror::Class::GetJavaLangClass(); + ObjPtr class_klass = mirror::Class::GetJavaLangClass(); shadow_frame->SetVRegReference(0, class_klass); UnstartedClassIsAnonymousClass(self, shadow_frame, &result, 0); EXPECT_EQ(result.GetZ(), 0); @@ -906,7 +910,7 @@ TEST_F(UnstartedRuntimeTest, IsAnonymousClass) { StackHandleScope<1> hs(soa.Self()); Handle loader( hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* c = class_linker_->FindClass(soa.Self(), "LNested$1;", loader); + ObjPtr c = class_linker_->FindClass(soa.Self(), "LNested$1;", loader); ASSERT_TRUE(c != nullptr); shadow_frame->SetVRegReference(0, c); UnstartedClassIsAnonymousClass(self, shadow_frame, &result, 0); @@ -1042,7 +1046,7 @@ TEST_F(UnstartedRuntimeTest, FloatConversion) { Instruction::At(inst_data), inst_data[0], &result); - ObjPtr string_result = reinterpret_cast(result.GetL()); + ObjPtr string_result = down_cast(result.GetL()); ASSERT_TRUE(string_result != nullptr); std::string mod_utf = string_result->ToModifiedUtf8(); @@ -1138,7 +1142,7 @@ class UnstartedClassForNameTest : public UnstartedRuntimeTest { ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); for (const char* name : kTestCases) { - mirror::String* name_string = mirror::String::AllocFromModifiedUtf8(self, name); + ObjPtr name_string = mirror::String::AllocFromModifiedUtf8(self, name); CHECK(name_string != nullptr); if (in_transaction) { @@ -1213,8 +1217,10 @@ class UnstartedClassForNameTest : public UnstartedRuntimeTest { }; TEST_F(UnstartedClassForNameTest, ClassForName) { - auto runner = [](Thread* self, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) - REQUIRES_SHARED(Locks::mutator_lock_) { + auto runner = [](Thread* self, + ShadowFrame* shadow_frame, + ObjPtr name, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { shadow_frame->SetVRegReference(0, name); UnstartedClassForName(self, shadow_frame, result, 0); }; @@ -1222,8 +1228,10 @@ TEST_F(UnstartedClassForNameTest, ClassForName) { } TEST_F(UnstartedClassForNameTest, ClassForNameLong) { - auto runner = [](Thread* self, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) - REQUIRES_SHARED(Locks::mutator_lock_) { + auto runner = [](Thread* self, + ShadowFrame* shadow_frame, + ObjPtr name, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { shadow_frame->SetVRegReference(0, name); shadow_frame->SetVReg(1, 0); shadow_frame->SetVRegReference(2, nullptr); @@ -1239,8 +1247,10 @@ TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoader) { StackHandleScope<1> hs(self); Handle boot_cp = hs.NewHandle(GetBootClassLoader()); - auto runner = [&](Thread* th, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) - REQUIRES_SHARED(Locks::mutator_lock_) { + auto runner = [&](Thread* th, + ShadowFrame* shadow_frame, + ObjPtr name, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { shadow_frame->SetVRegReference(0, name); shadow_frame->SetVReg(1, 0); shadow_frame->SetVRegReference(2, boot_cp.Get()); @@ -1256,7 +1266,10 @@ TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoaderTransaction) { StackHandleScope<1> hs(self); Handle boot_cp = hs.NewHandle(GetBootClassLoader()); - auto runner = [&](Thread* th, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) + auto runner = [&](Thread* th, + ShadowFrame* shadow_frame, + ObjPtr name, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { shadow_frame->SetVRegReference(0, name); shadow_frame->SetVReg(1, 0); @@ -1277,8 +1290,10 @@ TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoaderFail) { Handle path_cp = hs.NewHandle( self->DecodeJObject(path_jobj)->AsClassLoader()); - auto runner = [&](Thread* th, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) - REQUIRES_SHARED(Locks::mutator_lock_) { + auto runner = [&](Thread* th, + ShadowFrame* shadow_frame, + ObjPtr name, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { shadow_frame->SetVRegReference(0, name); shadow_frame->SetVReg(1, 0); shadow_frame->SetVRegReference(2, path_cp.Get()); @@ -1376,7 +1391,7 @@ TEST_F(UnstartedRuntimeTest, ConstructorNewInstance0) { // Should have the right string. ObjPtr result_msg = reinterpret_cast(result.GetL())->GetDetailMessage(); - EXPECT_EQ(input.Get(), result_msg.Ptr()); + EXPECT_OBJ_PTR_EQ(input.Get(), result_msg); ShadowFrame::DeleteDeoptimizedFrame(shadow_frame); } @@ -1393,7 +1408,7 @@ TEST_F(UnstartedRuntimeTest, IdentityHashCode) { ASSERT_FALSE(self->IsExceptionPending()); ObjPtr str = mirror::String::AllocFromModifiedUtf8(self, "abd"); - tmp->SetVRegReference(0, str.Ptr()); + tmp->SetVRegReference(0, str); UnstartedSystemIdentityHashCode(self, tmp, &result, 0); EXPECT_NE(0, result.GetI()); EXPECT_EQ(str->IdentityHashCode(), result.GetI()); diff --git a/runtime/method_handles-inl.h b/runtime/method_handles-inl.h index 00a8c00880..c4ac982576 100644 --- a/runtime/method_handles-inl.h +++ b/runtime/method_handles-inl.h @@ -22,6 +22,7 @@ #include "common_throws.h" #include "dex/dex_instruction.h" #include "interpreter/interpreter_common.h" +#include "interpreter/shadow_frame-inl.h" #include "jvalue-inl.h" #include "mirror/class.h" #include "mirror/method_type.h" @@ -31,6 +32,79 @@ namespace art { +// A convenience class that allows for iteration through a list of +// input argument registers. This is used to iterate over input +// arguments while performing standard argument conversions. +class ShadowFrameGetter { + public: + ShadowFrameGetter(const ShadowFrame& shadow_frame, + const InstructionOperands* const operands, + size_t operand_index = 0u) + : shadow_frame_(shadow_frame), operands_(operands), operand_index_(operand_index) {} + + ALWAYS_INLINE uint32_t Get() REQUIRES_SHARED(Locks::mutator_lock_) { + return shadow_frame_.GetVReg(Next()); + } + + ALWAYS_INLINE int64_t GetLong() REQUIRES_SHARED(Locks::mutator_lock_) { + return shadow_frame_.GetVRegLong(NextLong()); + } + + ALWAYS_INLINE ObjPtr GetReference() REQUIRES_SHARED(Locks::mutator_lock_) { + return shadow_frame_.GetVRegReference(Next()); + } + + private: + uint32_t Next() { + const uint32_t next = operands_->GetOperand(operand_index_); + operand_index_ += 1; + return next; + } + + uint32_t NextLong() { + const uint32_t next = operands_->GetOperand(operand_index_); + operand_index_ += 2; + return next; + } + + const ShadowFrame& shadow_frame_; + const InstructionOperands* const operands_; // the set of register operands to read + size_t operand_index_; // the next register operand to read from frame +}; + +// A convenience class that allows values to be written to a given shadow frame, +// starting at location |first_dst_reg|. +class ShadowFrameSetter { + public: + ShadowFrameSetter(ShadowFrame* shadow_frame, size_t first_dst_reg) + : shadow_frame_(shadow_frame), arg_index_(first_dst_reg) {} + + ALWAYS_INLINE void Set(uint32_t value) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_LT(arg_index_, shadow_frame_->NumberOfVRegs()); + shadow_frame_->SetVReg(arg_index_++, value); + } + + ALWAYS_INLINE void SetReference(ObjPtr value) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_LT(arg_index_, shadow_frame_->NumberOfVRegs()); + shadow_frame_->SetVRegReference(arg_index_++, value); + } + + ALWAYS_INLINE void SetLong(int64_t value) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_LT(arg_index_, shadow_frame_->NumberOfVRegs()); + shadow_frame_->SetVRegLong(arg_index_, value); + arg_index_ += 2; + } + + ALWAYS_INLINE bool Done() const { + return arg_index_ == shadow_frame_->NumberOfVRegs(); + } + + private: + ShadowFrame* shadow_frame_; + size_t arg_index_; +}; + inline bool ConvertArgumentValue(Handle callsite_type, Handle callee_type, ObjPtr from_class, diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 1d45aaeb2e..27de72533c 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -19,6 +19,7 @@ #include "android-base/stringprintf.h" #include "common_dex_operations.h" +#include "interpreter/shadow_frame-inl.h" #include "jvalue-inl.h" #include "mirror/emulated_stack_frame.h" #include "mirror/method_handle_impl-inl.h" diff --git a/runtime/method_handles.h b/runtime/method_handles.h index fce3d06639..b6e31345e1 100644 --- a/runtime/method_handles.h +++ b/runtime/method_handles.h @@ -21,12 +21,13 @@ #include "dex/dex_instruction.h" #include "handle.h" -#include "interpreter/shadow_frame.h" #include "jvalue.h" #include "mirror/class.h" namespace art { +class ShadowFrame; + namespace mirror { class MethodHandle; class MethodType; @@ -126,79 +127,6 @@ bool PerformConversions(Thread* self, int32_t start_index, int32_t end_index) REQUIRES_SHARED(Locks::mutator_lock_); -// A convenience class that allows for iteration through a list of -// input argument registers. This is used to iterate over input -// arguments while performing standard argument conversions. -class ShadowFrameGetter { - public: - ShadowFrameGetter(const ShadowFrame& shadow_frame, - const InstructionOperands* const operands, - size_t operand_index = 0u) - : shadow_frame_(shadow_frame), operands_(operands), operand_index_(operand_index) {} - - ALWAYS_INLINE uint32_t Get() REQUIRES_SHARED(Locks::mutator_lock_) { - return shadow_frame_.GetVReg(Next()); - } - - ALWAYS_INLINE int64_t GetLong() REQUIRES_SHARED(Locks::mutator_lock_) { - return shadow_frame_.GetVRegLong(NextLong()); - } - - ALWAYS_INLINE ObjPtr GetReference() REQUIRES_SHARED(Locks::mutator_lock_) { - return shadow_frame_.GetVRegReference(Next()); - } - - private: - uint32_t Next() { - const uint32_t next = operands_->GetOperand(operand_index_); - operand_index_ += 1; - return next; - } - - uint32_t NextLong() { - const uint32_t next = operands_->GetOperand(operand_index_); - operand_index_ += 2; - return next; - } - - const ShadowFrame& shadow_frame_; - const InstructionOperands* const operands_; // the set of register operands to read - size_t operand_index_; // the next register operand to read from frame -}; - -// A convenience class that allows values to be written to a given shadow frame, -// starting at location |first_dst_reg|. -class ShadowFrameSetter { - public: - ShadowFrameSetter(ShadowFrame* shadow_frame, size_t first_dst_reg) - : shadow_frame_(shadow_frame), arg_index_(first_dst_reg) {} - - ALWAYS_INLINE void Set(uint32_t value) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK_LT(arg_index_, shadow_frame_->NumberOfVRegs()); - shadow_frame_->SetVReg(arg_index_++, value); - } - - ALWAYS_INLINE void SetReference(ObjPtr value) - REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK_LT(arg_index_, shadow_frame_->NumberOfVRegs()); - shadow_frame_->SetVRegReference(arg_index_++, value.Ptr()); - } - - ALWAYS_INLINE void SetLong(int64_t value) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK_LT(arg_index_, shadow_frame_->NumberOfVRegs()); - shadow_frame_->SetVRegLong(arg_index_, value); - arg_index_ += 2; - } - - ALWAYS_INLINE bool Done() const { - return arg_index_ == shadow_frame_->NumberOfVRegs(); - } - - private: - ShadowFrame* shadow_frame_; - size_t arg_index_; -}; - bool MethodHandleInvoke(Thread* self, ShadowFrame& shadow_frame, Handle method_handle, diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc index 44c819aaf7..c755299a79 100644 --- a/runtime/mirror/var_handle.cc +++ b/runtime/mirror/var_handle.cc @@ -24,7 +24,7 @@ #include "intrinsics_enum.h" #include "jni/jni_internal.h" #include "jvalue-inl.h" -#include "method_handles.h" +#include "method_handles-inl.h" #include "method_type.h" #include "well_known_classes.h" diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 077aa33925..ad08fa0fa9 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -26,6 +26,7 @@ #include "entrypoints/quick/quick_entrypoints_enum.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "handle_scope-inl.h" +#include "interpreter/shadow_frame-inl.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "mirror/class-inl.h" diff --git a/runtime/stack.cc b/runtime/stack.cc index 229238e0f7..93127bea29 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -28,7 +28,7 @@ #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/space/image_space.h" #include "gc/space/space-inl.h" -#include "interpreter/shadow_frame.h" +#include "interpreter/shadow_frame-inl.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "linear_alloc.h" diff --git a/runtime/thread.cc b/runtime/thread.cc index eada24d257..51b37527f7 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -63,7 +63,7 @@ #include "handle_scope-inl.h" #include "indirect_reference_table-inl.h" #include "interpreter/interpreter.h" -#include "interpreter/shadow_frame.h" +#include "interpreter/shadow_frame-inl.h" #include "java_frame_root_info.h" #include "jni/java_vm_ext.h" #include "jni/jni_internal.h" -- GitLab From e9987b0831403858d95bdd7a5ef9e56665a9702d Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 22 May 2018 16:26:43 +0100 Subject: [PATCH 443/749] ObjPtr<>-ify tests using ClassLinker::FindClass(). ClassLinker::FindClass() returns a non-ObjPtr<> reference but it has a lot of uses, so we shall change the uses in a few steps. This change deals with several tests. Test: Rely on TreeHugger. Bug: 31113334 Change-Id: Ib75e20e7ebaff01fb607a09f96675f8cf397ae52 --- compiler/common_compiler_test.cc | 10 +- compiler/jni/jni_compiler_test.cc | 2 +- dex2oat/linker/image_test.h | 13 +- dex2oat/linker/oat_writer_test.cc | 6 +- .../profile/profile_compilation_info_test.cc | 2 +- runtime/class_linker_test.cc | 118 +++++---- runtime/indirect_reference_table_test.cc | 15 +- runtime/instrumentation_test.cc | 14 +- runtime/jni/jni_internal_test.cc | 4 +- runtime/mirror/dex_cache_test.cc | 2 +- runtime/mirror/method_type_test.cc | 2 +- runtime/mirror/object_test.cc | 246 ++++++++++-------- runtime/proxy_test.h | 10 +- 13 files changed, 238 insertions(+), 206 deletions(-) diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index 96a0c1be4d..a7f16d394e 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -123,7 +123,7 @@ void CommonCompilerTest::MakeExecutable(ObjPtr class_loader Thread* self = Thread::Current(); StackHandleScope<1> hs(self); Handle loader(hs.NewHandle(class_loader)); - mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader); + ObjPtr klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader); CHECK(klass != nullptr) << "Class not found " << class_name; PointerSize pointer_size = class_linker_->GetImagePointerSize(); for (auto& m : klass->GetMethods(pointer_size)) { @@ -222,7 +222,7 @@ void CommonCompilerTest::CompileClass(mirror::ClassLoader* class_loader, const c Thread* self = Thread::Current(); StackHandleScope<1> hs(self); Handle loader(hs.NewHandle(class_loader)); - mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader); + ObjPtr klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader); CHECK(klass != nullptr) << "Class not found " << class_name; auto pointer_size = class_linker_->GetImagePointerSize(); for (auto& m : klass->GetMethods(pointer_size)) { @@ -244,7 +244,8 @@ void CommonCompilerTest::CompileDirectMethod(Handle class_l const char* signature) { std::string class_descriptor(DotToDescriptor(class_name)); Thread* self = Thread::Current(); - mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader); + ObjPtr klass = + class_linker_->FindClass(self, class_descriptor.c_str(), class_loader); CHECK(klass != nullptr) << "Class not found " << class_name; auto pointer_size = class_linker_->GetImagePointerSize(); ArtMethod* method = klass->FindClassMethod(method_name, signature, pointer_size); @@ -258,7 +259,8 @@ void CommonCompilerTest::CompileVirtualMethod(Handle class_ const char* signature) { std::string class_descriptor(DotToDescriptor(class_name)); Thread* self = Thread::Current(); - mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader); + ObjPtr klass = + class_linker_->FindClass(self, class_descriptor.c_str(), class_loader); CHECK(klass != nullptr) << "Class not found " << class_name; auto pointer_size = class_linker_->GetImagePointerSize(); ArtMethod* method = klass->FindClassMethod(method_name, signature, pointer_size); diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index c643af787d..3cb4a652ad 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -245,7 +245,7 @@ class JniCompilerTest : public CommonCompilerTest { Handle loader( hs.NewHandle(soa.Decode(class_loader))); // Compile the native method before starting the runtime - mirror::Class* c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader); + ObjPtr c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader); const auto pointer_size = class_linker_->GetImagePointerSize(); ArtMethod* method = c->FindClassMethod(method_name, method_sig, pointer_size); ASSERT_TRUE(method != nullptr) << method_name << " " << method_sig; diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index a95252d3ed..4b231ed35c 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -394,7 +394,8 @@ inline void ImageTest::Compile(ImageHeader::StorageMode storage_mode, ScopedObjectAccess soa(Thread::Current()); ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); for (const std::string& image_class : image_classes) { - mirror::Class* klass = class_linker->FindSystemClass(Thread::Current(), image_class.c_str()); + ObjPtr klass = + class_linker->FindSystemClass(Thread::Current(), image_class.c_str()); EXPECT_TRUE(klass != nullptr); EXPECT_TRUE(klass->IsInitialized()); } @@ -492,15 +493,15 @@ inline void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { for (size_t j = 0; j < dex->NumClassDefs(); ++j) { const DexFile::ClassDef& class_def = dex->GetClassDef(j); const char* descriptor = dex->GetClassDescriptor(class_def); - mirror::Class* klass = class_linker_->FindSystemClass(soa.Self(), descriptor); + ObjPtr klass = class_linker_->FindSystemClass(soa.Self(), descriptor); EXPECT_TRUE(klass != nullptr) << descriptor; + uint8_t* raw_klass = reinterpret_cast(klass.Ptr()); if (image_classes.find(descriptor) == image_classes.end()) { - EXPECT_TRUE(reinterpret_cast(klass) >= image_end || - reinterpret_cast(klass) < image_begin) << descriptor; + EXPECT_TRUE(raw_klass >= image_end || raw_klass < image_begin) << descriptor; } else { // Image classes should be located inside the image. - EXPECT_LT(image_begin, reinterpret_cast(klass)) << descriptor; - EXPECT_LT(reinterpret_cast(klass), image_end) << descriptor; + EXPECT_LT(image_begin, raw_klass) << descriptor; + EXPECT_LT(raw_klass, image_end) << descriptor; } EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord(false))); } diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index df0641cc7f..0694c4ff9f 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -463,9 +463,9 @@ TEST_F(OatTest, WriteRead) { } const char* descriptor = dex_file.GetClassDescriptor(class_def); - mirror::Class* klass = class_linker->FindClass(soa.Self(), - descriptor, - ScopedNullHandle()); + ObjPtr klass = class_linker->FindClass(soa.Self(), + descriptor, + ScopedNullHandle()); const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(i); CHECK_EQ(ClassStatus::kNotReady, oat_class.GetStatus()) << descriptor; diff --git a/libprofile/profile/profile_compilation_info_test.cc b/libprofile/profile/profile_compilation_info_test.cc index b3262a7a14..ead7cba60b 100644 --- a/libprofile/profile/profile_compilation_info_test.cc +++ b/libprofile/profile/profile_compilation_info_test.cc @@ -54,7 +54,7 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { StackHandleScope<1> hs(self); Handle h_loader( hs.NewHandle(self->DecodeJObject(class_loader)->AsClassLoader())); - mirror::Class* klass = class_linker->FindClass(self, clazz.c_str(), h_loader); + ObjPtr klass = class_linker->FindClass(self, clazz.c_str(), h_loader); const auto pointer_size = class_linker->GetImagePointerSize(); std::vector methods; diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 8cd0604ac3..6ed029ceb1 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -62,9 +62,10 @@ class ClassLinkerTest : public CommonRuntimeTest { Thread* self = Thread::Current(); EXPECT_TRUE(class_linker_->FindSystemClass(self, descriptor.c_str()) == nullptr); EXPECT_TRUE(self->IsExceptionPending()); - mirror::Object* exception = self->GetException(); + StackHandleScope<1> hs(self); + Handle exception = hs.NewHandle(self->GetException()); self->ClearException(); - mirror::Class* exception_class = + ObjPtr exception_class = class_linker_->FindSystemClass(self, "Ljava/lang/NoClassDefFoundError;"); EXPECT_TRUE(exception->InstanceOf(exception_class)); } @@ -75,7 +76,7 @@ class ClassLinkerTest : public CommonRuntimeTest { AssertPrimitiveClass(descriptor, class_linker_->FindSystemClass(self, descriptor.c_str())); } - void AssertPrimitiveClass(const std::string& descriptor, mirror::Class* primitive) + void AssertPrimitiveClass(const std::string& descriptor, ObjPtr primitive) REQUIRES_SHARED(Locks::mutator_lock_) { ASSERT_TRUE(primitive != nullptr); ASSERT_TRUE(primitive->GetClass() != nullptr); @@ -113,13 +114,13 @@ class ClassLinkerTest : public CommonRuntimeTest { EXPECT_EQ(kAccPublic | kAccFinal | kAccAbstract, primitive->GetAccessFlags()); } - void AssertObjectClass(mirror::Class* JavaLangObject) + void AssertObjectClass(ObjPtr JavaLangObject) REQUIRES_SHARED(Locks::mutator_lock_) { ASSERT_TRUE(JavaLangObject != nullptr); ASSERT_TRUE(JavaLangObject->GetClass() != nullptr); ASSERT_EQ(JavaLangObject->GetClass(), JavaLangObject->GetClass()->GetClass()); - EXPECT_EQ(JavaLangObject, JavaLangObject->GetClass()->GetSuperClass()); + EXPECT_OBJ_PTR_EQ(JavaLangObject, JavaLangObject->GetClass()->GetSuperClass()); std::string temp; ASSERT_STREQ(JavaLangObject->GetDescriptor(&temp), "Ljava/lang/Object;"); EXPECT_TRUE(JavaLangObject->GetSuperClass() == nullptr); @@ -172,7 +173,7 @@ class ClassLinkerTest : public CommonRuntimeTest { void AssertArrayClass(const std::string& array_descriptor, const std::string& component_type, - mirror::ClassLoader* class_loader) + ObjPtr class_loader) REQUIRES_SHARED(Locks::mutator_lock_) { Thread* self = Thread::Current(); StackHandleScope<2> hs(self); @@ -181,7 +182,7 @@ class ClassLinkerTest : public CommonRuntimeTest { hs.NewHandle(class_linker_->FindClass(self, array_descriptor.c_str(), loader))); std::string temp; EXPECT_STREQ(component_type.c_str(), array->GetComponentType()->GetDescriptor(&temp)); - EXPECT_EQ(class_loader, array->GetClassLoader()); + EXPECT_OBJ_PTR_EQ(loader.Get(), array->GetClassLoader()); EXPECT_EQ(kAccFinal | kAccAbstract, (array->GetAccessFlags() & (kAccFinal | kAccAbstract))); AssertArrayClass(array_descriptor, array); } @@ -234,7 +235,7 @@ class ClassLinkerTest : public CommonRuntimeTest { EXPECT_OBJ_PTR_EQ(class_linker_->FindArrayClass(self, &array_ptr), array.Get()); PointerSize pointer_size = class_linker_->GetImagePointerSize(); - mirror::Class* JavaLangObject = + ObjPtr JavaLangObject = class_linker_->FindSystemClass(self, "Ljava/lang/Object;"); ImTable* JavaLangObject_imt = JavaLangObject->GetImt(pointer_size); // IMT of a array class should be shared with the IMT of the java.lag.Object @@ -292,9 +293,9 @@ class ClassLinkerTest : public CommonRuntimeTest { } } EXPECT_EQ(klass->IsInterface(), !klass->HasVTable()); - mirror::IfTable* iftable = klass->GetIfTable(); + ObjPtr iftable = klass->GetIfTable(); for (int i = 0; i < klass->GetIfTableCount(); i++) { - mirror::Class* interface = iftable->GetInterface(i); + ObjPtr interface = iftable->GetInterface(i); ASSERT_TRUE(interface != nullptr); if (klass->IsInterface()) { EXPECT_EQ(0U, iftable->GetMethodArrayCount(i)); @@ -322,13 +323,13 @@ class ClassLinkerTest : public CommonRuntimeTest { for (ArtMethod& method : klass->GetDirectMethods(kRuntimePointerSize)) { AssertMethod(&method); EXPECT_TRUE(method.IsDirect()); - EXPECT_EQ(klass.Get(), method.GetDeclaringClass()); + EXPECT_OBJ_PTR_EQ(klass.Get(), method.GetDeclaringClass()); } for (ArtMethod& method : klass->GetDeclaredVirtualMethods(kRuntimePointerSize)) { AssertMethod(&method); EXPECT_FALSE(method.IsDirect()); - EXPECT_EQ(klass.Get(), method.GetDeclaringClass()); + EXPECT_OBJ_PTR_EQ(klass.Get(), method.GetDeclaringClass()); } for (ArtMethod& method : klass->GetCopiedMethods(kRuntimePointerSize)) { @@ -386,7 +387,7 @@ class ClassLinkerTest : public CommonRuntimeTest { ASSERT_EQ(end_ref_offset.Uint32Value(), current_ref_offset.Uint32Value()); uint32_t total_num_reference_instance_fields = 0; - mirror::Class* k = klass.Get(); + ObjPtr k = klass.Get(); while (k != nullptr) { total_num_reference_instance_fields += k->NumReferenceInstanceFields(); k = k->GetSuperClass(); @@ -400,7 +401,7 @@ class ClassLinkerTest : public CommonRuntimeTest { } } - void AssertDexFileClass(mirror::ClassLoader* class_loader, const std::string& descriptor) + void AssertDexFileClass(ObjPtr class_loader, const std::string& descriptor) REQUIRES_SHARED(Locks::mutator_lock_) { ASSERT_TRUE(descriptor != nullptr); Thread* self = Thread::Current(); @@ -409,8 +410,8 @@ class ClassLinkerTest : public CommonRuntimeTest { hs.NewHandle(class_linker_->FindSystemClass(self, descriptor.c_str()))); ASSERT_TRUE(klass != nullptr); std::string temp; - EXPECT_STREQ(descriptor.c_str(), klass.Get()->GetDescriptor(&temp)); - EXPECT_EQ(class_loader, klass->GetClassLoader()); + EXPECT_STREQ(descriptor.c_str(), klass->GetDescriptor(&temp)); + EXPECT_OBJ_PTR_EQ(class_loader, klass->GetClassLoader()); if (klass->IsPrimitive()) { AssertPrimitiveClass(descriptor, klass.Get()); } else if (klass->IsArrayClass()) { @@ -420,7 +421,7 @@ class ClassLinkerTest : public CommonRuntimeTest { } } - void AssertDexFile(const DexFile& dex, mirror::ClassLoader* class_loader) + void AssertDexFile(const DexFile& dex, ObjPtr class_loader) REQUIRES_SHARED(Locks::mutator_lock_) { // Verify all the classes defined in this file for (size_t i = 0; i < dex.NumClassDefs(); i++) { @@ -469,7 +470,7 @@ struct CheckOffsets { bool Check() REQUIRES_SHARED(Locks::mutator_lock_) { Thread* self = Thread::Current(); - mirror::Class* klass = + ObjPtr klass = Runtime::Current()->GetClassLinker()->FindSystemClass(self, class_descriptor.c_str()); CHECK(klass != nullptr) << class_descriptor; @@ -875,12 +876,13 @@ TEST_F(ClassLinkerTest, FindClassNested) { Handle class_loader( hs.NewHandle(soa.Decode(LoadDex("Nested")))); - mirror::Class* outer = class_linker_->FindClass(soa.Self(), "LNested;", class_loader); + ObjPtr outer = class_linker_->FindClass(soa.Self(), "LNested;", class_loader); ASSERT_TRUE(outer != nullptr); EXPECT_EQ(0U, outer->NumVirtualMethods()); EXPECT_EQ(1U, outer->NumDirectMethods()); - mirror::Class* inner = class_linker_->FindClass(soa.Self(), "LNested$Inner;", class_loader); + ObjPtr inner = + class_linker_->FindClass(soa.Self(), "LNested$Inner;", class_loader); ASSERT_TRUE(inner != nullptr); EXPECT_EQ(0U, inner->NumVirtualMethods()); EXPECT_EQ(1U, inner->NumDirectMethods()); @@ -902,23 +904,24 @@ TEST_F(ClassLinkerTest, FindClass_Primitives) { TEST_F(ClassLinkerTest, FindClass) { ScopedObjectAccess soa(Thread::Current()); - mirror::Class* JavaLangObject = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); - AssertObjectClass(JavaLangObject); + StackHandleScope<2> hs(soa.Self()); + Handle JavaLangObject = hs.NewHandle( + class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")); + AssertObjectClass(JavaLangObject.Get()); - StackHandleScope<1> hs(soa.Self()); Handle class_loader( hs.NewHandle(soa.Decode(LoadDex("MyClass")))); AssertNonExistentClass("LMyClass;"); - mirror::Class* MyClass = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader); + ObjPtr MyClass = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader); ASSERT_TRUE(MyClass != nullptr); ASSERT_TRUE(MyClass->GetClass() != nullptr); ASSERT_EQ(MyClass->GetClass(), MyClass->GetClass()->GetClass()); - EXPECT_EQ(JavaLangObject, MyClass->GetClass()->GetSuperClass()); + EXPECT_OBJ_PTR_EQ(JavaLangObject.Get(), MyClass->GetClass()->GetSuperClass()); std::string temp; ASSERT_STREQ(MyClass->GetDescriptor(&temp), "LMyClass;"); - EXPECT_TRUE(MyClass->GetSuperClass() == JavaLangObject); + EXPECT_OBJ_PTR_EQ(MyClass->GetSuperClass(), JavaLangObject.Get()); EXPECT_TRUE(MyClass->HasSuperClass()); - EXPECT_EQ(class_loader.Get(), MyClass->GetClassLoader()); + EXPECT_OBJ_PTR_EQ(class_loader.Get(), MyClass->GetClassLoader()); EXPECT_EQ(ClassStatus::kResolved, MyClass->GetStatus()); EXPECT_FALSE(MyClass->IsErroneous()); EXPECT_TRUE(MyClass->IsLoaded()); @@ -1057,8 +1060,9 @@ TEST_F(ClassLinkerTest, LibCore) { // start of the object TEST_F(ClassLinkerTest, ValidateObjectArrayElementsOffset) { ScopedObjectAccess soa(Thread::Current()); - mirror::Class* array_class = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); - mirror::ObjectArray* array = + ObjPtr array_class = + class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); + ObjPtr> array = mirror::ObjectArray::Alloc(soa.Self(), array_class, 0); uintptr_t data_offset = reinterpret_cast(array->GetRawData(sizeof(mirror::HeapReference), @@ -1106,7 +1110,7 @@ TEST_F(ClassLinkerTest, ValidateBoxedTypes) { // This lets UnboxPrimitive avoid searching for the field by name at runtime. ScopedObjectAccess soa(Thread::Current()); ScopedNullHandle class_loader; - mirror::Class* c; + ObjPtr c; c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Boolean;", class_loader); EXPECT_STREQ("value", c->GetIFieldsPtr()->At(0).GetName()); c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Byte;", class_loader); @@ -1127,16 +1131,18 @@ TEST_F(ClassLinkerTest, ValidateBoxedTypes) { TEST_F(ClassLinkerTest, TwoClassLoadersOneClass) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); + StackHandleScope<3> hs(soa.Self()); Handle class_loader_1( hs.NewHandle(soa.Decode(LoadDex("MyClass")))); Handle class_loader_2( hs.NewHandle(soa.Decode(LoadDex("MyClass")))); - mirror::Class* MyClass_1 = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader_1); - mirror::Class* MyClass_2 = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader_2); + Handle MyClass_1 = hs.NewHandle( + class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader_1)); + ObjPtr MyClass_2 = + class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader_2); EXPECT_TRUE(MyClass_1 != nullptr); EXPECT_TRUE(MyClass_2 != nullptr); - EXPECT_NE(MyClass_1, MyClass_2); + EXPECT_OBJ_PTR_NE(MyClass_1.Get(), MyClass_2); } TEST_F(ClassLinkerTest, StaticFields) { @@ -1200,7 +1206,7 @@ TEST_F(ClassLinkerTest, StaticFields) { soa.Self(), statics.Get(), "s8", "Ljava/lang/String;"); EXPECT_EQ(s8->GetTypeAsPrimitiveType(), Primitive::kPrimNot); EXPECT_TRUE(s8->GetObject(statics.Get())->AsString()->Equals("android")); - mirror::String* str_value = mirror::String::AllocFromModifiedUtf8(soa.Self(), "robot"); + ObjPtr str_value = mirror::String::AllocFromModifiedUtf8(soa.Self(), "robot"); s8->SetObject(s8->GetDeclaringClass(), str_value); // TODO: Remove EXPECT_FALSE when GCC can handle EXPECT_EQ @@ -1300,7 +1306,8 @@ TEST_F(ClassLinkerTest, ResolveVerifyAndClinit) { StackHandleScope<1> hs(soa.Self()); Handle class_loader( hs.NewHandle(soa.Decode(jclass_loader))); - mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", class_loader); + ObjPtr klass = + class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", class_loader); ArtMethod* clinit = klass->FindClassInitializer(kRuntimePointerSize); ArtMethod* getS0 = klass->FindClassMethod("getS0", "()Ljava/lang/Object;", kRuntimePointerSize); @@ -1345,7 +1352,7 @@ TEST_F(ClassLinkerTest, ErroneousClass) { TEST_F(ClassLinkerTest, FinalizableBit) { ScopedObjectAccess soa(Thread::Current()); - mirror::Class* c; + ObjPtr c; // Object has a finalize method, but we know it's empty. c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); @@ -1381,7 +1388,7 @@ TEST_F(ClassLinkerTest, ClassRootDescriptors) { ScopedObjectAccess soa(Thread::Current()); std::string temp; for (int i = 0; i < ClassLinker::kClassRootsMax; i++) { - mirror::Class* klass = class_linker_->GetClassRoot(ClassLinker::ClassRoot(i)); + ObjPtr klass = class_linker_->GetClassRoot(ClassLinker::ClassRoot(i)); EXPECT_GT(strlen(klass->GetDescriptor(&temp)), 0U); EXPECT_STREQ(klass->GetDescriptor(&temp), class_linker_->GetClassRootDescriptor(ClassLinker::ClassRoot(i))) << " i = " << i; @@ -1391,7 +1398,7 @@ TEST_F(ClassLinkerTest, ClassRootDescriptors) { TEST_F(ClassLinkerTest, ValidatePredefinedClassSizes) { ScopedObjectAccess soa(Thread::Current()); ScopedNullHandle class_loader; - mirror::Class* c; + ObjPtr c; c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Class;", class_loader); ASSERT_TRUE(c != nullptr); @@ -1418,7 +1425,7 @@ static void CheckMethod(ArtMethod* method, bool verified) } } -static void CheckVerificationAttempted(mirror::Class* c, bool preverified) +static void CheckVerificationAttempted(ObjPtr c, bool preverified) REQUIRES_SHARED(Locks::mutator_lock_) { EXPECT_EQ((c->GetAccessFlags() & kAccVerificationAttempted) != 0U, preverified) << "Class " << mirror::Class::PrettyClass(c) << " not as expected"; @@ -1430,7 +1437,8 @@ static void CheckVerificationAttempted(mirror::Class* c, bool preverified) TEST_F(ClassLinkerTest, Preverified_InitializedBoot) { ScopedObjectAccess soa(Thread::Current()); - mirror::Class* JavaLangObject = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); + ObjPtr JavaLangObject = + class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); ASSERT_TRUE(JavaLangObject != nullptr); EXPECT_TRUE(JavaLangObject->IsInitialized()) << "Not testing already initialized class from the " "core"; @@ -1479,13 +1487,13 @@ TEST_F(ClassLinkerTest, IsBootStrapClassLoaded) { Handle jlo_class( hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"))); ASSERT_TRUE(jlo_class != nullptr); - EXPECT_TRUE(jlo_class.Get()->IsBootStrapClassLoaded()); + EXPECT_TRUE(jlo_class->IsBootStrapClassLoaded()); // Statics is not a bootstrap class. Handle statics( hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStatics;", class_loader))); ASSERT_TRUE(statics != nullptr); - EXPECT_FALSE(statics.Get()->IsBootStrapClassLoaded()); + EXPECT_FALSE(statics->IsBootStrapClassLoaded()); } // Regression test for b/26799552. @@ -1563,13 +1571,13 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) { Handle string_class(hs.NewHandle(class_linker_->FindClass(soa.Self(), "Ljava/lang/String;", class_loader))); - ASSERT_EQ(string_class.Get(), method1_type->GetRType()); - ASSERT_EQ(string_class.Get(), method1_type->GetPTypes()->Get(0)); + ASSERT_OBJ_PTR_EQ(string_class.Get(), method1_type->GetRType()); + ASSERT_OBJ_PTR_EQ(string_class.Get(), method1_type->GetPTypes()->Get(0)); // Resolve the method type again and assert that we get back the same value. Handle method1_type2 = hs.NewHandle( class_linker_->ResolveMethodType(soa.Self(), method1_id.proto_idx_, dex_cache, class_loader)); - ASSERT_EQ(method1_type.Get(), method1_type2.Get()); + ASSERT_OBJ_PTR_EQ(method1_type.Get(), method1_type2.Get()); // Resolve the MethodType associated with a different method signature // and assert it's different. @@ -1582,7 +1590,7 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) { const DexFile::MethodId& method2_id = dex_file.GetMethodId(method2->GetDexMethodIndex()); Handle method2_type = hs.NewHandle( class_linker_->ResolveMethodType(soa.Self(), method2_id.proto_idx_, dex_cache, class_loader)); - ASSERT_TRUE(method1_type.Get() != method2_type.Get()); + ASSERT_OBJ_PTR_NE(method1_type.Get(), method2_type.Get()); } // Verify that ClassLinker's CreateWellknownClassLoader works as expected @@ -1599,18 +1607,18 @@ TEST_F(ClassLinkerTest, CreateWellKnownClassLoader) { TEST_F(ClassLinkerTest, PrettyClass) { ScopedObjectAccess soa(Thread::Current()); EXPECT_EQ("null", mirror::Class::PrettyClass(nullptr)); - mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); + ObjPtr c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); ASSERT_TRUE(c != nullptr); - mirror::Object* o = mirror::ObjectArray::Alloc(soa.Self(), c, 0); + ObjPtr o = mirror::ObjectArray::Alloc(soa.Self(), c, 0); EXPECT_EQ("java.lang.Class", mirror::Class::PrettyClass(o->GetClass())); } TEST_F(ClassLinkerTest, PrettyClassAndClassLoader) { ScopedObjectAccess soa(Thread::Current()); EXPECT_EQ("null", mirror::Class::PrettyClassAndClassLoader(nullptr)); - mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); + ObjPtr c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); ASSERT_TRUE(c != nullptr); - mirror::Object* o = mirror::ObjectArray::Alloc(soa.Self(), c, 0); + ObjPtr o = mirror::ObjectArray::Alloc(soa.Self(), c, 0); EXPECT_EQ("java.lang.Class", mirror::Class::PrettyClassAndClassLoader(o->GetClass())); } @@ -1619,8 +1627,8 @@ TEST_F(ClassLinkerTest, PrettyField) { ScopedObjectAccess soa(Thread::Current()); EXPECT_EQ("null", ArtField::PrettyField(nullptr)); - mirror::Class* java_lang_String = class_linker_->FindSystemClass(soa.Self(), - "Ljava/lang/String;"); + ObjPtr java_lang_String = class_linker_->FindSystemClass(soa.Self(), + "Ljava/lang/String;"); ArtField* f; f = java_lang_String->FindDeclaredInstanceField("count", "I"); @@ -1630,7 +1638,7 @@ TEST_F(ClassLinkerTest, PrettyField) { TEST_F(ClassLinkerTest, JniShortName_JniLongName) { ScopedObjectAccess soa(Thread::Current()); - mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/String;"); + ObjPtr c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/String;"); ASSERT_TRUE(c != nullptr); ArtMethod* m; @@ -1682,7 +1690,7 @@ class ClassLinkerClassLoaderTest : public ClassLinkerTest { ASSERT_TRUE(klass != nullptr) << descriptor; Handle expected_class_loader( hs.NewHandle(soa.Decode(expected_class_loader_obj))); - ASSERT_EQ(klass->GetClassLoader(), expected_class_loader.Get()); + ASSERT_OBJ_PTR_EQ(klass->GetClassLoader(), expected_class_loader.Get()); } } }; diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc index 92785090f5..141feb434f 100644 --- a/runtime/indirect_reference_table_test.cc +++ b/runtime/indirect_reference_table_test.cc @@ -60,8 +60,9 @@ TEST_F(IndirectReferenceTableTest, BasicTest) { &error_msg); ASSERT_TRUE(irt.IsValid()) << error_msg; - mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); - StackHandleScope<4> hs(soa.Self()); + StackHandleScope<5> hs(soa.Self()); + Handle c = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")); ASSERT_TRUE(c != nullptr); Handle obj0 = hs.NewHandle(c->AllocObject(soa.Self())); ASSERT_TRUE(obj0 != nullptr); @@ -278,8 +279,9 @@ TEST_F(IndirectReferenceTableTest, Holes) { ScopedObjectAccess soa(Thread::Current()); static const size_t kTableMax = 10; - mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); - StackHandleScope<5> hs(soa.Self()); + StackHandleScope<6> hs(soa.Self()); + Handle c = hs.NewHandle( + class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")); ASSERT_TRUE(c != nullptr); Handle obj0 = hs.NewHandle(c->AllocObject(soa.Self())); ASSERT_TRUE(obj0 != nullptr); @@ -487,8 +489,9 @@ TEST_F(IndirectReferenceTableTest, Resize) { ScopedObjectAccess soa(Thread::Current()); static const size_t kTableMax = 512; - mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); - StackHandleScope<1> hs(soa.Self()); + StackHandleScope<2> hs(soa.Self()); + Handle c = hs.NewHandle( + class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")); ASSERT_TRUE(c != nullptr); Handle obj0 = hs.NewHandle(c->AllocObject(soa.Self())); ASSERT_TRUE(obj0 != nullptr); diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc index 3171eeb861..8ac26afe9f 100644 --- a/runtime/instrumentation_test.cc +++ b/runtime/instrumentation_test.cc @@ -520,7 +520,7 @@ TEST_F(InstrumentationTest, MethodEntryEvent) { ClassLinker* class_linker = runtime->GetClassLinker(); StackHandleScope<1> hs(soa.Self()); Handle loader(hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); + ObjPtr klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); ASSERT_TRUE(klass != nullptr); ArtMethod* method = klass->FindClassMethod("returnReference", "()Ljava/lang/Object;", kRuntimePointerSize); @@ -540,7 +540,7 @@ TEST_F(InstrumentationTest, MethodExitObjectEvent) { ClassLinker* class_linker = runtime->GetClassLinker(); StackHandleScope<1> hs(soa.Self()); Handle loader(hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); + ObjPtr klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); ASSERT_TRUE(klass != nullptr); ArtMethod* method = klass->FindClassMethod("returnReference", "()Ljava/lang/Object;", kRuntimePointerSize); @@ -560,7 +560,7 @@ TEST_F(InstrumentationTest, MethodExitPrimEvent) { ClassLinker* class_linker = runtime->GetClassLinker(); StackHandleScope<1> hs(soa.Self()); Handle loader(hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); + ObjPtr klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); ASSERT_TRUE(klass != nullptr); ArtMethod* method = klass->FindClassMethod("returnPrimitive", "()I", kRuntimePointerSize); ASSERT_TRUE(method != nullptr); @@ -595,7 +595,7 @@ TEST_F(InstrumentationTest, FieldWriteObjectEvent) { ClassLinker* class_linker = runtime->GetClassLinker(); StackHandleScope<1> hs(soa.Self()); Handle loader(hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); + ObjPtr klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); ASSERT_TRUE(klass != nullptr); ArtField* field = klass->FindDeclaredStaticField("referenceField", "Ljava/lang/Object;"); ASSERT_TRUE(field != nullptr); @@ -613,7 +613,7 @@ TEST_F(InstrumentationTest, FieldWritePrimEvent) { ClassLinker* class_linker = runtime->GetClassLinker(); StackHandleScope<1> hs(soa.Self()); Handle loader(hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); + ObjPtr klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); ASSERT_TRUE(klass != nullptr); ArtField* field = klass->FindDeclaredStaticField("primitiveField", "I"); ASSERT_TRUE(field != nullptr); @@ -648,7 +648,7 @@ TEST_F(InstrumentationTest, DeoptimizeDirectMethod) { ClassLinker* class_linker = runtime->GetClassLinker(); StackHandleScope<1> hs(soa.Self()); Handle loader(hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); + ObjPtr klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); ASSERT_TRUE(klass != nullptr); ArtMethod* method_to_deoptimize = klass->FindClassMethod("instanceMethod", "()V", kRuntimePointerSize); @@ -697,7 +697,7 @@ TEST_F(InstrumentationTest, MixedDeoptimization) { ClassLinker* class_linker = runtime->GetClassLinker(); StackHandleScope<1> hs(soa.Self()); Handle loader(hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); + ObjPtr klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); ASSERT_TRUE(klass != nullptr); ArtMethod* method_to_deoptimize = klass->FindClassMethod("instanceMethod", "()V", kRuntimePointerSize); diff --git a/runtime/jni/jni_internal_test.cc b/runtime/jni/jni_internal_test.cc index 5d74181cef..a25049e681 100644 --- a/runtime/jni/jni_internal_test.cc +++ b/runtime/jni/jni_internal_test.cc @@ -91,7 +91,7 @@ class JniInternalTest : public CommonCompilerTest { jclass GetPrimitiveClass(char descriptor) { ScopedObjectAccess soa(env_); - mirror::Class* c = class_linker_->FindPrimitiveClass(descriptor); + ObjPtr c = class_linker_->FindPrimitiveClass(descriptor); CHECK(c != nullptr); return soa.AddLocalReference(c); } @@ -624,7 +624,7 @@ class JniInternalTest : public CommonCompilerTest { StackHandleScope<1> hs(soa.Self()); Handle loader( hs.NewHandle(soa.Decode(class_loader_))); - mirror::Class* c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader); + ObjPtr c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader); const auto pointer_size = class_linker_->GetImagePointerSize(); ArtMethod* method = c->FindClassMethod(method_name, method_sig, pointer_size); ASSERT_TRUE(method != nullptr) << method_name << " " << method_sig; diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc index 97e0ce6684..7a70cae1ef 100644 --- a/runtime/mirror/dex_cache_test.cc +++ b/runtime/mirror/dex_cache_test.cc @@ -83,7 +83,7 @@ TEST_F(DexCacheTest, LinearAlloc) { StackHandleScope<1> hs(soa.Self()); Handle class_loader(hs.NewHandle( soa.Decode(jclass_loader))); - mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader); + ObjPtr klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader); ASSERT_TRUE(klass != nullptr); LinearAlloc* const linear_alloc = klass->GetClassLoader()->GetAllocator(); EXPECT_NE(linear_alloc, runtime_->GetLinearAlloc()); diff --git a/runtime/mirror/method_type_test.cc b/runtime/mirror/method_type_test.cc index a361772c58..16bfc73e04 100644 --- a/runtime/mirror/method_type_test.cc +++ b/runtime/mirror/method_type_test.cc @@ -54,7 +54,7 @@ static mirror::MethodType* CreateMethodType(const std::string& return_type, CHECK(return_clazz != nullptr); ObjPtr class_type = mirror::Class::GetJavaLangClass(); - mirror::Class* class_array_type = class_linker->FindArrayClass(self, &class_type); + ObjPtr class_array_type = class_linker->FindArrayClass(self, &class_type); Handle> param_classes = hs.NewHandle( mirror::ObjectArray::Alloc(self, class_array_type, param_types.size())); diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index 5306eac7f6..69ba4b98cf 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -116,7 +116,7 @@ TEST_F(ObjectTest, Clone) { TEST_F(ObjectTest, AllocObjectArray) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); + StackHandleScope<3> hs(soa.Self()); Handle> oa(hs.NewHandle(AllocObjectArray(soa.Self(), 2))); EXPECT_EQ(2, oa->GetLength()); EXPECT_TRUE(oa->Get(0) == nullptr); @@ -128,17 +128,17 @@ TEST_F(ObjectTest, AllocObjectArray) { EXPECT_TRUE(oa->Get(0) == oa.Get()); EXPECT_TRUE(oa->Get(1) == oa.Get()); - Class* aioobe = class_linker_->FindSystemClass(soa.Self(), - "Ljava/lang/ArrayIndexOutOfBoundsException;"); + Handle aioobe = hs.NewHandle( + class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/ArrayIndexOutOfBoundsException;")); EXPECT_TRUE(oa->Get(-1) == nullptr); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_OBJ_PTR_EQ(aioobe.Get(), soa.Self()->GetException()->GetClass()); soa.Self()->ClearException(); EXPECT_TRUE(oa->Get(2) == nullptr); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_OBJ_PTR_EQ(aioobe.Get(), soa.Self()->GetException()->GetClass()); soa.Self()->ClearException(); ASSERT_TRUE(oa->GetClass() != nullptr); @@ -152,53 +152,51 @@ TEST_F(ObjectTest, AllocObjectArray) { TEST_F(ObjectTest, AllocArray) { ScopedObjectAccess soa(Thread::Current()); - Class* c = class_linker_->FindSystemClass(soa.Self(), "[I"); - StackHandleScope<1> hs(soa.Self()); - MutableHandle a( - hs.NewHandle(Array::Alloc(soa.Self(), c, 1, c->GetComponentSizeShift(), - Runtime::Current()->GetHeap()->GetCurrentAllocator()))); - EXPECT_TRUE(c == a->GetClass()); + StackHandleScope<2> hs(soa.Self()); + MutableHandle c = hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[I")); + gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); + MutableHandle a = hs.NewHandle( + Array::Alloc(soa.Self(), c.Get(), 1, c->GetComponentSizeShift(), allocator_type)); + EXPECT_TRUE(c.Get() == a->GetClass()); EXPECT_EQ(1, a->GetLength()); - c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;"); - a.Assign(Array::Alloc(soa.Self(), c, 1, c->GetComponentSizeShift(), - Runtime::Current()->GetHeap()->GetCurrentAllocator())); - EXPECT_TRUE(c == a->GetClass()); + c.Assign(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;")); + a.Assign(Array::Alloc(soa.Self(), c.Get(), 1, c->GetComponentSizeShift(), allocator_type)); + EXPECT_TRUE(c.Get() == a->GetClass()); EXPECT_EQ(1, a->GetLength()); - c = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;"); - a.Assign(Array::Alloc(soa.Self(), c, 1, c->GetComponentSizeShift(), - Runtime::Current()->GetHeap()->GetCurrentAllocator())); - EXPECT_TRUE(c == a->GetClass()); + c.Assign(class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;")); + a.Assign(Array::Alloc(soa.Self(), c.Get(), 1, c->GetComponentSizeShift(), allocator_type)); + EXPECT_TRUE(c.Get() == a->GetClass()); EXPECT_EQ(1, a->GetLength()); } TEST_F(ObjectTest, AllocArray_FillUsable) { ScopedObjectAccess soa(Thread::Current()); - Class* c = class_linker_->FindSystemClass(soa.Self(), "[B"); - StackHandleScope<1> hs(soa.Self()); - MutableHandle a( - hs.NewHandle(Array::Alloc(soa.Self(), c, 1, c->GetComponentSizeShift(), - Runtime::Current()->GetHeap()->GetCurrentAllocator()))); - EXPECT_TRUE(c == a->GetClass()); + StackHandleScope<2> hs(soa.Self()); + MutableHandle c = hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[B")); + gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); + MutableHandle a = hs.NewHandle( + Array::Alloc(soa.Self(), c.Get(), 1, c->GetComponentSizeShift(), allocator_type)); + EXPECT_TRUE(c.Get() == a->GetClass()); EXPECT_LE(1, a->GetLength()); - c = class_linker_->FindSystemClass(soa.Self(), "[I"); - a.Assign(Array::Alloc(soa.Self(), c, 2, c->GetComponentSizeShift(), - Runtime::Current()->GetHeap()->GetCurrentAllocator())); - EXPECT_TRUE(c == a->GetClass()); + c.Assign(class_linker_->FindSystemClass(soa.Self(), "[I")); + a.Assign( + Array::Alloc(soa.Self(), c.Get(), 2, c->GetComponentSizeShift(), allocator_type)); + EXPECT_TRUE(c.Get() == a->GetClass()); EXPECT_LE(2, a->GetLength()); - c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;"); - a.Assign(Array::Alloc(soa.Self(), c, 2, c->GetComponentSizeShift(), - Runtime::Current()->GetHeap()->GetCurrentAllocator())); - EXPECT_TRUE(c == a->GetClass()); + c.Assign(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;")); + a.Assign( + Array::Alloc(soa.Self(), c.Get(), 2, c->GetComponentSizeShift(), allocator_type)); + EXPECT_TRUE(c.Get() == a->GetClass()); EXPECT_LE(2, a->GetLength()); - c = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;"); - a.Assign(Array::Alloc(soa.Self(), c, 2, c->GetComponentSizeShift(), - Runtime::Current()->GetHeap()->GetCurrentAllocator())); - EXPECT_TRUE(c == a->GetClass()); + c.Assign(class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;")); + a.Assign( + Array::Alloc(soa.Self(), c.Get(), 2, c->GetComponentSizeShift(), allocator_type)); + EXPECT_TRUE(c.Get() == a->GetClass()); EXPECT_LE(2, a->GetLength()); } @@ -218,16 +216,18 @@ void TestPrimitiveArray(ClassLinker* cl) { EXPECT_EQ(T(123), a->Get(0)); EXPECT_EQ(T(321), a->Get(1)); - Class* aioobe = cl->FindSystemClass(soa.Self(), "Ljava/lang/ArrayIndexOutOfBoundsException;"); + StackHandleScope<1> hs(soa.Self()); + Handle aioobe = hs.NewHandle( + cl->FindSystemClass(soa.Self(), "Ljava/lang/ArrayIndexOutOfBoundsException;")); EXPECT_EQ(0, a->Get(-1)); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_OBJ_PTR_EQ(aioobe.Get(), soa.Self()->GetException()->GetClass()); soa.Self()->ClearException(); EXPECT_EQ(0, a->Get(2)); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_OBJ_PTR_EQ(aioobe.Get(), soa.Self()->GetException()->GetClass()); soa.Self()->ClearException(); } @@ -266,17 +266,18 @@ TEST_F(ObjectTest, PrimitiveArray_Double_Alloc) { EXPECT_DOUBLE_EQ(T(123), a->Get(0)); EXPECT_DOUBLE_EQ(T(321), a->Get(1)); - Class* aioobe = class_linker_->FindSystemClass(soa.Self(), - "Ljava/lang/ArrayIndexOutOfBoundsException;"); + StackHandleScope<1> hs(soa.Self()); + Handle aioobe = hs.NewHandle( + class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/ArrayIndexOutOfBoundsException;")); EXPECT_DOUBLE_EQ(0, a->Get(-1)); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_OBJ_PTR_EQ(aioobe.Get(), soa.Self()->GetException()->GetClass()); soa.Self()->ClearException(); EXPECT_DOUBLE_EQ(0, a->Get(2)); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_OBJ_PTR_EQ(aioobe.Get(), soa.Self()->GetException()->GetClass()); soa.Self()->ClearException(); } @@ -296,17 +297,18 @@ TEST_F(ObjectTest, PrimitiveArray_Float_Alloc) { EXPECT_FLOAT_EQ(T(123), a->Get(0)); EXPECT_FLOAT_EQ(T(321), a->Get(1)); - Class* aioobe = class_linker_->FindSystemClass(soa.Self(), - "Ljava/lang/ArrayIndexOutOfBoundsException;"); + StackHandleScope<1> hs(soa.Self()); + Handle aioobe = hs.NewHandle( + class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/ArrayIndexOutOfBoundsException;")); EXPECT_FLOAT_EQ(0, a->Get(-1)); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_OBJ_PTR_EQ(aioobe.Get(), soa.Self()->GetException()->GetClass()); soa.Self()->ClearException(); EXPECT_FLOAT_EQ(0, a->Get(2)); EXPECT_TRUE(soa.Self()->IsExceptionPending()); - EXPECT_EQ(aioobe, soa.Self()->GetException()->GetClass()); + EXPECT_OBJ_PTR_EQ(aioobe.Get(), soa.Self()->GetException()->GetClass()); soa.Self()->ClearException(); } @@ -352,9 +354,10 @@ TEST_F(ObjectTest, StaticFieldFromCode) { jobject class_loader = LoadDex("StaticsFromCode"); const DexFile* dex_file = GetFirstDexFile(class_loader); - StackHandleScope<2> hs(soa.Self()); + StackHandleScope<3> hs(soa.Self()); Handle loader(hs.NewHandle(soa.Decode(class_loader))); - Class* klass = class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", loader); + Handle klass = + hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", loader)); ArtMethod* clinit = klass->FindClassInitializer(kRuntimePointerSize); const DexFile::TypeId* klass_type_id = dex_file->FindTypeId("LStaticsFromCode;"); ASSERT_TRUE(klass_type_id != nullptr); @@ -372,15 +375,15 @@ TEST_F(ObjectTest, StaticFieldFromCode) { ArtField* field = FindFieldFromCode(field_idx, clinit, Thread::Current(), sizeof(HeapReference)); - ObjPtr s0 = field->GetObj(klass); + ObjPtr s0 = field->GetObj(klass.Get()); EXPECT_TRUE(s0 != nullptr); Handle char_array(hs.NewHandle(CharArray::Alloc(soa.Self(), 0))); field->SetObj(field->GetDeclaringClass(), char_array.Get()); - EXPECT_OBJ_PTR_EQ(char_array.Get(), field->GetObj(klass)); + EXPECT_OBJ_PTR_EQ(char_array.Get(), field->GetObj(klass.Get())); field->SetObj(field->GetDeclaringClass(), nullptr); - EXPECT_EQ(nullptr, field->GetObj(klass)); + EXPECT_EQ(nullptr, field->GetObj(klass.Get())); // TODO: more exhaustive tests of all 6 cases of ArtField::*FromCode } @@ -486,9 +489,11 @@ TEST_F(ObjectTest, DescriptorCompare) { Handle class_loader_1(hs.NewHandle(soa.Decode(jclass_loader_1))); Handle class_loader_2(hs.NewHandle(soa.Decode(jclass_loader_2))); - Class* klass1 = linker->FindClass(soa.Self(), "LProtoCompare;", class_loader_1); + Handle klass1 = + hs.NewHandle(linker->FindClass(soa.Self(), "LProtoCompare;", class_loader_1)); ASSERT_TRUE(klass1 != nullptr); - Class* klass2 = linker->FindClass(soa.Self(), "LProtoCompare2;", class_loader_2); + Handle klass2 = + hs.NewHandle(linker->FindClass(soa.Self(), "LProtoCompare2;", class_loader_2)); ASSERT_TRUE(klass2 != nullptr); ArtMethod* m1_1 = klass1->GetVirtualMethod(0, kRuntimePointerSize); @@ -525,11 +530,11 @@ TEST_F(ObjectTest, StringHashCode) { TEST_F(ObjectTest, InstanceOf) { ScopedObjectAccess soa(Thread::Current()); jobject jclass_loader = LoadDex("XandY"); - StackHandleScope<3> hs(soa.Self()); + StackHandleScope<10> hs(soa.Self()); Handle class_loader(hs.NewHandle(soa.Decode(jclass_loader))); - Class* X = class_linker_->FindClass(soa.Self(), "LX;", class_loader); - Class* Y = class_linker_->FindClass(soa.Self(), "LY;", class_loader); + Handle X = hs.NewHandle(class_linker_->FindClass(soa.Self(), "LX;", class_loader)); + Handle Y = hs.NewHandle(class_linker_->FindClass(soa.Self(), "LY;", class_loader)); ASSERT_TRUE(X != nullptr); ASSERT_TRUE(Y != nullptr); @@ -538,51 +543,56 @@ TEST_F(ObjectTest, InstanceOf) { ASSERT_TRUE(x != nullptr); ASSERT_TRUE(y != nullptr); - EXPECT_TRUE(x->InstanceOf(X)); - EXPECT_FALSE(x->InstanceOf(Y)); - EXPECT_TRUE(y->InstanceOf(X)); - EXPECT_TRUE(y->InstanceOf(Y)); + EXPECT_TRUE(x->InstanceOf(X.Get())); + EXPECT_FALSE(x->InstanceOf(Y.Get())); + EXPECT_TRUE(y->InstanceOf(X.Get())); + EXPECT_TRUE(y->InstanceOf(Y.Get())); - Class* java_lang_Class = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Class;"); - Class* Object_array_class = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;"); + Handle java_lang_Class = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Class;")); + Handle Object_array_class = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;")); - EXPECT_FALSE(java_lang_Class->InstanceOf(Object_array_class)); - EXPECT_TRUE(Object_array_class->InstanceOf(java_lang_Class)); + EXPECT_FALSE(java_lang_Class->InstanceOf(Object_array_class.Get())); + EXPECT_TRUE(Object_array_class->InstanceOf(java_lang_Class.Get())); // All array classes implement Cloneable and Serializable. - Object* array = ObjectArray::Alloc(soa.Self(), Object_array_class, 1); - Class* java_lang_Cloneable = - class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Cloneable;"); - Class* java_io_Serializable = - class_linker_->FindSystemClass(soa.Self(), "Ljava/io/Serializable;"); - EXPECT_TRUE(array->InstanceOf(java_lang_Cloneable)); - EXPECT_TRUE(array->InstanceOf(java_io_Serializable)); + Handle array = + hs.NewHandle(ObjectArray::Alloc(soa.Self(), Object_array_class.Get(), 1)); + Handle java_lang_Cloneable = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Cloneable;")); + Handle java_io_Serializable = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/io/Serializable;")); + EXPECT_TRUE(array->InstanceOf(java_lang_Cloneable.Get())); + EXPECT_TRUE(array->InstanceOf(java_io_Serializable.Get())); } TEST_F(ObjectTest, IsAssignableFrom) { ScopedObjectAccess soa(Thread::Current()); jobject jclass_loader = LoadDex("XandY"); - StackHandleScope<1> hs(soa.Self()); + StackHandleScope<5> hs(soa.Self()); Handle class_loader(hs.NewHandle(soa.Decode(jclass_loader))); - Class* X = class_linker_->FindClass(soa.Self(), "LX;", class_loader); - Class* Y = class_linker_->FindClass(soa.Self(), "LY;", class_loader); + Handle X = hs.NewHandle(class_linker_->FindClass(soa.Self(), "LX;", class_loader)); + Handle Y = hs.NewHandle(class_linker_->FindClass(soa.Self(), "LY;", class_loader)); - EXPECT_TRUE(X->IsAssignableFrom(X)); - EXPECT_TRUE(X->IsAssignableFrom(Y)); - EXPECT_FALSE(Y->IsAssignableFrom(X)); - EXPECT_TRUE(Y->IsAssignableFrom(Y)); + EXPECT_TRUE(X->IsAssignableFrom(X.Get())); + EXPECT_TRUE(X->IsAssignableFrom(Y.Get())); + EXPECT_FALSE(Y->IsAssignableFrom(X.Get())); + EXPECT_TRUE(Y->IsAssignableFrom(Y.Get())); // class final String implements CharSequence, .. - Class* string = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/String;"); - Class* charseq = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/CharSequence;"); + Handle string = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/String;")); + Handle charseq = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/CharSequence;")); // Can String be assigned to CharSequence without a cast? - EXPECT_TRUE(charseq->IsAssignableFrom(string)); + EXPECT_TRUE(charseq->IsAssignableFrom(string.Get())); // Can CharSequence be assigned to String without a cast? - EXPECT_FALSE(string->IsAssignableFrom(charseq)); + EXPECT_FALSE(string->IsAssignableFrom(charseq.Get())); // Primitive types are only assignable to themselves const char* prims = "ZBCSIJFD"; - std::vector prim_types(strlen(prims)); + std::vector> prim_types(strlen(prims)); for (size_t i = 0; i < strlen(prims); i++) { prim_types[i] = class_linker_->FindPrimitiveClass(prims[i]); } @@ -600,55 +610,61 @@ TEST_F(ObjectTest, IsAssignableFrom) { TEST_F(ObjectTest, IsAssignableFromArray) { ScopedObjectAccess soa(Thread::Current()); jobject jclass_loader = LoadDex("XandY"); - StackHandleScope<1> hs(soa.Self()); + StackHandleScope<14> hs(soa.Self()); Handle class_loader(hs.NewHandle(soa.Decode(jclass_loader))); - Class* X = class_linker_->FindClass(soa.Self(), "LX;", class_loader); - Class* Y = class_linker_->FindClass(soa.Self(), "LY;", class_loader); + Handle X = hs.NewHandle(class_linker_->FindClass(soa.Self(), "LX;", class_loader)); + Handle Y = hs.NewHandle(class_linker_->FindClass(soa.Self(), "LY;", class_loader)); ASSERT_TRUE(X != nullptr); ASSERT_TRUE(Y != nullptr); - Class* YA = class_linker_->FindClass(soa.Self(), "[LY;", class_loader); - Class* YAA = class_linker_->FindClass(soa.Self(), "[[LY;", class_loader); + Handle YA = hs.NewHandle(class_linker_->FindClass(soa.Self(), "[LY;", class_loader)); + Handle YAA = hs.NewHandle(class_linker_->FindClass(soa.Self(), "[[LY;", class_loader)); ASSERT_TRUE(YA != nullptr); ASSERT_TRUE(YAA != nullptr); - Class* XAA = class_linker_->FindClass(soa.Self(), "[[LX;", class_loader); + Handle XAA = hs.NewHandle(class_linker_->FindClass(soa.Self(), "[[LX;", class_loader)); ASSERT_TRUE(XAA != nullptr); - Class* O = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); - Class* OA = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;"); - Class* OAA = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;"); - Class* OAAA = class_linker_->FindSystemClass(soa.Self(), "[[[Ljava/lang/Object;"); + Handle O = hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")); + Handle OA = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;")); + Handle OAA = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;")); + Handle OAAA = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[[[Ljava/lang/Object;")); ASSERT_TRUE(O != nullptr); ASSERT_TRUE(OA != nullptr); ASSERT_TRUE(OAA != nullptr); ASSERT_TRUE(OAAA != nullptr); - Class* S = class_linker_->FindSystemClass(soa.Self(), "Ljava/io/Serializable;"); - Class* SA = class_linker_->FindSystemClass(soa.Self(), "[Ljava/io/Serializable;"); - Class* SAA = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/io/Serializable;"); + Handle S = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/io/Serializable;")); + Handle SA = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/io/Serializable;")); + Handle SAA = + hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[[Ljava/io/Serializable;")); ASSERT_TRUE(S != nullptr); ASSERT_TRUE(SA != nullptr); ASSERT_TRUE(SAA != nullptr); - Class* IA = class_linker_->FindSystemClass(soa.Self(), "[I"); + Handle IA = hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[I")); ASSERT_TRUE(IA != nullptr); - EXPECT_TRUE(YAA->IsAssignableFrom(YAA)); // identity - EXPECT_TRUE(XAA->IsAssignableFrom(YAA)); // element superclass - EXPECT_FALSE(YAA->IsAssignableFrom(XAA)); - EXPECT_FALSE(Y->IsAssignableFrom(YAA)); - EXPECT_FALSE(YA->IsAssignableFrom(YAA)); - EXPECT_TRUE(O->IsAssignableFrom(YAA)); // everything is an Object - EXPECT_TRUE(OA->IsAssignableFrom(YAA)); - EXPECT_TRUE(OAA->IsAssignableFrom(YAA)); - EXPECT_TRUE(S->IsAssignableFrom(YAA)); // all arrays are Serializable - EXPECT_TRUE(SA->IsAssignableFrom(YAA)); - EXPECT_FALSE(SAA->IsAssignableFrom(YAA)); // unless Y was Serializable + EXPECT_TRUE(YAA->IsAssignableFrom(YAA.Get())); // identity + EXPECT_TRUE(XAA->IsAssignableFrom(YAA.Get())); // element superclass + EXPECT_FALSE(YAA->IsAssignableFrom(XAA.Get())); + EXPECT_FALSE(Y->IsAssignableFrom(YAA.Get())); + EXPECT_FALSE(YA->IsAssignableFrom(YAA.Get())); + EXPECT_TRUE(O->IsAssignableFrom(YAA.Get())); // everything is an Object + EXPECT_TRUE(OA->IsAssignableFrom(YAA.Get())); + EXPECT_TRUE(OAA->IsAssignableFrom(YAA.Get())); + EXPECT_TRUE(S->IsAssignableFrom(YAA.Get())); // all arrays are Serializable + EXPECT_TRUE(SA->IsAssignableFrom(YAA.Get())); + EXPECT_FALSE(SAA->IsAssignableFrom(YAA.Get())); // unless Y was Serializable - EXPECT_FALSE(IA->IsAssignableFrom(OA)); - EXPECT_FALSE(OA->IsAssignableFrom(IA)); - EXPECT_TRUE(O->IsAssignableFrom(IA)); + EXPECT_FALSE(IA->IsAssignableFrom(OA.Get())); + EXPECT_FALSE(OA->IsAssignableFrom(IA.Get())); + EXPECT_TRUE(O->IsAssignableFrom(IA.Get())); } TEST_F(ObjectTest, FindInstanceField) { @@ -656,7 +672,7 @@ TEST_F(ObjectTest, FindInstanceField) { StackHandleScope<1> hs(soa.Self()); Handle s(hs.NewHandle(String::AllocFromModifiedUtf8(soa.Self(), "ABC"))); ASSERT_TRUE(s != nullptr); - Class* c = s->GetClass(); + ObjPtr c = s->GetClass(); ASSERT_TRUE(c != nullptr); // Wrong type. @@ -798,7 +814,7 @@ TEST_F(ObjectTest, PrettyTypeOf) { Handle a(hs.NewHandle(mirror::ShortArray::Alloc(soa.Self(), 2))); EXPECT_EQ("short[]", mirror::Object::PrettyTypeOf(a.Get())); - mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); + ObjPtr c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); ASSERT_TRUE(c != nullptr); mirror::Object* o = mirror::ObjectArray::Alloc(soa.Self(), c, 0); EXPECT_EQ("java.lang.String[]", mirror::Object::PrettyTypeOf(o)); diff --git a/runtime/proxy_test.h b/runtime/proxy_test.h index b559823257..98362649f5 100644 --- a/runtime/proxy_test.h +++ b/runtime/proxy_test.h @@ -37,7 +37,9 @@ mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, const char* className, const std::vector& interfaces) REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::Class* javaLangObject = class_linker->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); + StackHandleScope<1> hs(soa.Self()); + Handle javaLangObject = hs.NewHandle( + class_linker->FindSystemClass(soa.Self(), "Ljava/lang/Object;")); CHECK(javaLangObject != nullptr); jclass javaLangClass = soa.AddLocalReference(mirror::Class::GetJavaLangClass()); @@ -67,7 +69,7 @@ mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, "equals", "(Ljava/lang/Object;)Z", kRuntimePointerSize); CHECK(method != nullptr); CHECK(!method->IsDirect()); - CHECK(method->GetDeclaringClass() == javaLangObject); + CHECK(method->GetDeclaringClass() == javaLangObject.Get()); DCHECK(!Runtime::Current()->IsActiveTransaction()); soa.Env()->SetObjectArrayElement( proxyClassMethods, array_index++, soa.AddLocalReference( @@ -75,7 +77,7 @@ mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, method = javaLangObject->FindClassMethod("hashCode", "()I", kRuntimePointerSize); CHECK(method != nullptr); CHECK(!method->IsDirect()); - CHECK(method->GetDeclaringClass() == javaLangObject); + CHECK(method->GetDeclaringClass() == javaLangObject.Get()); soa.Env()->SetObjectArrayElement( proxyClassMethods, array_index++, soa.AddLocalReference( mirror::Method::CreateFromArtMethod(soa.Self(), method))); @@ -83,7 +85,7 @@ mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, "toString", "()Ljava/lang/String;", kRuntimePointerSize); CHECK(method != nullptr); CHECK(!method->IsDirect()); - CHECK(method->GetDeclaringClass() == javaLangObject); + CHECK(method->GetDeclaringClass() == javaLangObject.Get()); soa.Env()->SetObjectArrayElement( proxyClassMethods, array_index++, soa.AddLocalReference( mirror::Method::CreateFromArtMethod(soa.Self(), method))); -- GitLab From 1500bc6bdbdad3b0f88eaafda82d017c33525c1c Mon Sep 17 00:00:00 2001 From: Tamas Kenez Date: Fri, 18 May 2018 14:51:20 +0200 Subject: [PATCH 444/749] ART-tests: Remove DX dependency from 450-checker-types. Test: art/test.py -r --host -t 450 Change-Id: I6f319c7b9d8f2c72f9c739890acd6499f7ffcacd --- test/450-checker-types/build | 3 -- test/450-checker-types/smali/Main2.smali | 55 ++++++++++++++++++++++++ test/450-checker-types/src/Main.java | 10 ----- 3 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 test/450-checker-types/smali/Main2.smali diff --git a/test/450-checker-types/build b/test/450-checker-types/build index 3721955670..49292c9ac1 100755 --- a/test/450-checker-types/build +++ b/test/450-checker-types/build @@ -20,7 +20,4 @@ export USE_JACK=false # Also disable desugar because it is missing in jack platform builds. export DESUGAR=false -# See b/65168732 -export USE_D8=false - ./default-build "$@" diff --git a/test/450-checker-types/smali/Main2.smali b/test/450-checker-types/smali/Main2.smali new file mode 100644 index 0000000000..5f11be3d29 --- /dev/null +++ b/test/450-checker-types/smali/Main2.smali @@ -0,0 +1,55 @@ +# 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 +# +# 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. + +.class LMain2; +.super Ljava/lang/Object; +.source "Main2.java" + + +# direct methods +.method constructor ()V + .registers 1 + + .prologue + .line 17 + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + +## CHECK-START: void Main2.testArraySimpleRemove() instruction_simplifier (before) +## CHECK: CheckCast + +## CHECK-START: void Main2.testArraySimpleRemove() instruction_simplifier (after) +## CHECK-NOT: CheckCast + +.method static testArraySimpleRemove()V + .registers 3 + + .prologue + .line 19 + const/16 v2, 0xa + + new-array v0, v2, [LSubclassA; + + .local v0, "b":[LSuper; + move-object v1, v0 + + .line 20 + check-cast v1, [LSubclassA; + + .line 21 + .local v1, "c":[LSubclassA; + return-void +.end method diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java index ae0fdbe576..f6561cd046 100644 --- a/test/450-checker-types/src/Main.java +++ b/test/450-checker-types/src/Main.java @@ -463,16 +463,6 @@ public class Main { public SubclassA $noinline$getSubclass() { throw new RuntimeException(); } - /// CHECK-START: void Main.testArraySimpleRemove() instruction_simplifier (before) - /// CHECK: CheckCast - - /// CHECK-START: void Main.testArraySimpleRemove() instruction_simplifier (after) - /// CHECK-NOT: CheckCast - public void testArraySimpleRemove() { - Super[] b = new SubclassA[10]; - SubclassA[] c = (SubclassA[])b; - } - /// CHECK-START: void Main.testInvokeSimpleRemove() instruction_simplifier (before) /// CHECK: CheckCast -- GitLab From 9fa1bab5f0d88083f2a39f72825edc6ba2e3c113 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Wed, 23 May 2018 15:21:35 +0100 Subject: [PATCH 445/749] ART: Fix missing field type crasher Avoids nullptr dereference when a field with a missing field type is assigned. Bug: b/79751666 Test: art/test.py --host -r -t 173 Change-Id: I5868793f57802ed3de60264eefa7eb98845ae126 --- runtime/common_dex_operations.h | 7 +++- test/173-missing-field-type/expected.txt | 1 + test/173-missing-field-type/info.txt | 1 + .../smali/BadField.smali | 34 +++++++++++++++++++ test/173-missing-field-type/src-art/Main.java | 34 +++++++++++++++++++ test/173-missing-field-type/src/Main.java | 22 ++++++++++++ test/etc/default-build | 5 ++- 7 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 test/173-missing-field-type/expected.txt create mode 100644 test/173-missing-field-type/info.txt create mode 100644 test/173-missing-field-type/smali/BadField.smali create mode 100644 test/173-missing-field-type/src-art/Main.java create mode 100644 test/173-missing-field-type/src/Main.java diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h index 9c2a40b50d..c29043e7c6 100644 --- a/runtime/common_dex_operations.h +++ b/runtime/common_dex_operations.h @@ -205,7 +205,12 @@ ALWAYS_INLINE bool DoFieldPutCommon(Thread* self, HandleWrapperObjPtr h_obj(hs.NewHandleWrapper(&obj)); field_class = field->ResolveType(); } - if (!reg->VerifierInstanceOf(field_class.Ptr())) { + // ArtField::ResolveType() may fail as evidenced with a dexing bug (b/78788577). + if (UNLIKELY(field_class.IsNull())) { + Thread::Current()->AssertPendingException(); + return false; + } + if (UNLIKELY(!reg->VerifierInstanceOf(field_class.Ptr()))) { // This should never happen. std::string temp1, temp2, temp3; self->ThrowNewExceptionF("Ljava/lang/InternalError;", diff --git a/test/173-missing-field-type/expected.txt b/test/173-missing-field-type/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/173-missing-field-type/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/173-missing-field-type/info.txt b/test/173-missing-field-type/info.txt new file mode 100644 index 0000000000..4e20b4c8e0 --- /dev/null +++ b/test/173-missing-field-type/info.txt @@ -0,0 +1 @@ +Tests handling of fields where the field type is missing (b/79751666). diff --git a/test/173-missing-field-type/smali/BadField.smali b/test/173-missing-field-type/smali/BadField.smali new file mode 100644 index 0000000000..851e8fe9c6 --- /dev/null +++ b/test/173-missing-field-type/smali/BadField.smali @@ -0,0 +1,34 @@ +# +# 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. + +.class public LBadField; +.super Ljava/lang/Object; + +# This is a bad field since there is no class Widget in this test. +.field public static widget:LWidget; + +.method public constructor ()V + .registers 2 + invoke-direct {v1}, Ljava/lang/Object;->()V + return-void +.end method + +.method public static constructor ()V + .registers 1 + new-instance v0, Ljava/lang/Object; + invoke-direct {v0}, Ljava/lang/Object;->()V + sput-object v0, LBadField;->widget:LWidget; + return-void +.end method \ No newline at end of file diff --git a/test/173-missing-field-type/src-art/Main.java b/test/173-missing-field-type/src-art/Main.java new file mode 100644 index 0000000000..61bb918813 --- /dev/null +++ b/test/173-missing-field-type/src-art/Main.java @@ -0,0 +1,34 @@ +/* + * 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 Main { + public static void main(String[] args) throws Throwable { + try { + // Class BadField is defined in BadField.smali. + Class c = Class.forName("BadField"); + System.out.println("Not reached"); + c.newInstance(); + } catch (NoClassDefFoundError expected) { + // The NoClassDefFoundError is for the field widget in class BadField. + if (expected.getMessage().equals("Failed resolution of: LWidget;")) { + System.out.println("passed"); + } else { + System.out.println("failed: " + expected.getMessage()); + } + } + } +} diff --git a/test/173-missing-field-type/src/Main.java b/test/173-missing-field-type/src/Main.java new file mode 100644 index 0000000000..172d6a6214 --- /dev/null +++ b/test/173-missing-field-type/src/Main.java @@ -0,0 +1,22 @@ +/* + * 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 Main { + // Allow test to pass on RI without adding to knownfailures.json file. + public static void main(String args[]) throws Exception { + System.out.println("passed"); + } +} diff --git a/test/etc/default-build b/test/etc/default-build index c61de0ab6e..e06b367f2b 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -500,7 +500,10 @@ fi if [ "${HAS_SMALI}" = "true" -a ${NEED_DEX} = "true" ]; then # Compile Smali classes ${SMALI} -JXmx512m assemble ${SMALI_ARGS} --output smali_classes.dex `find smali -name '*.smali'` - + if [[ ! -s smali_classes.dex ]] ; then + echo ${SMALI} produced no output. >&2 + exit 1 + fi # Merge smali files into classes.dex, this takes priority over any jasmin files. make_dexmerge classes.dex smali_classes.dex fi -- GitLab From c6d02fd9d27429d8940b04b1d60f2497c4eeb776 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Wed, 23 May 2018 14:31:46 +0100 Subject: [PATCH 446/749] Fix gtest Makefile rules with respect to chroot support. Test: make test-art-target-gtest Bug: 34729697 Change-Id: I751b138b39a5f77306940ca2114a30e9c2757dd4 --- build/Android.gtest.mk | 53 +++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index b481352b60..7272661860 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -464,6 +464,16 @@ include $(BUILD_PREBUILT) # $(3): 2ND_ or undefined - used to differentiate between the primary and secondary architecture. # $(4): LD_LIBRARY_PATH or undefined - used in case libartd.so is not in /system/lib/ define define-art-gtest-rule-target + ifeq ($(ART_TEST_CHROOT),) + # Non-chroot configuration. + maybe_art_test_chroot := + maybe_chroot_command := + else + # Chroot configuration. + maybe_art_test_chroot := $(ART_TEST_CHROOT) + maybe_chroot_command := chroot $(ART_TEST_CHROOT) + endif + gtest_rule := test-art-target-gtest-$(1)$$($(3)ART_PHONY_TEST_TARGET_SUFFIX) gtest_exe := $(OUT_DIR)/$(2) gtest_target_exe := $$(patsubst $(PRODUCT_OUT)/%,/%,$$(gtest_exe)) @@ -481,33 +491,24 @@ define define-art-gtest-rule-target $$(ART_TARGET_TEST_OUT)/valgrind-target-suppressions.txt $$(gtest_rule) valgrind-$$(gtest_rule): PRIVATE_TARGET_EXE := $$(gtest_target_exe) - -ifeq ($(ART_TEST_CHROOT),) -# Non-chroot configuration. -maybe_art_test_chroot := -maybe_chroot_command := -else -# Chroot configuration. -maybe_art_test_chroot := $(ART_TEST_CHROOT)/ -maybe_chroot_command := chroot $(ART_TEST_CHROOT) -endif +$$(gtest_rule) valgrind-$$(gtest_rule): PRIVATE_MAYBE_CHROOT_COMMAND := $$(maybe_chroot_command) # File witnessing the success of the gtest, the presence of which means the gtest's success. gtest_witness := \ - $(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$(gtest_rule)-$$$$PPID + $$(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$(gtest_rule)-$$$$PPID -$$(gtest_rule): GTEST_WITNESS := $$(gtest_witness) +$$(gtest_rule): PRIVATE_GTEST_WITNESS := $$(gtest_witness) .PHONY: $$(gtest_rule) $$(gtest_rule): test-art-target-sync - $(hide) adb shell touch $$(GTEST_WITNESS) - $(hide) adb shell rm $$(GTEST_WITNESS) - $(hide) adb shell chmod 755 $(maybe_art_test_chroot)$$(PRIVATE_TARGET_EXE) + $(hide) adb shell touch $$(PRIVATE_GTEST_WITNESS) + $(hide) adb shell rm $$(PRIVATE_GTEST_WITNESS) + $(hide) adb shell $$(PRIVATE_MAYBE_CHROOT_COMMAND) chmod 755 $$(PRIVATE_TARGET_EXE) $(hide) $$(call ART_TEST_SKIP,$$@) && \ - (adb shell "$(maybe_chroot_command) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ + (adb shell "$$(PRIVATE_MAYBE_CHROOT_COMMAND) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) $$(PRIVATE_TARGET_EXE) \ - && touch $$(GTEST_WITNESS)" \ - && (adb pull $$(GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ + && touch $$(PRIVATE_GTEST_WITNESS)" \ + && (adb pull $$(PRIVATE_GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ || $$(call ART_TEST_FAILED,$$@)) $(hide) rm -f /tmp/$$@-$$$$PPID @@ -518,24 +519,24 @@ $$(gtest_rule): test-art-target-sync # File witnessing the success of the Valgrind gtest, the presence of which means the gtest's # success. valgrind_gtest_witness := \ - $(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/valgrind-$$(gtest_rule)-$$$$PPID + $$(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/valgrind-$$(gtest_rule)-$$$$PPID -valgrind-$$(gtest_rule): VALGRIND_GTEST_WITNESS := $$(valgrind_gtest_witness) +valgrind-$$(gtest_rule): PRIVATE_VALGRIND_GTEST_WITNESS := $$(valgrind_gtest_witness) .PHONY: valgrind-$$(gtest_rule) valgrind-$$(gtest_rule): $(ART_VALGRIND_TARGET_DEPENDENCIES) test-art-target-sync - $(hide) adb shell touch $$(VALGRIND_GTEST_WITNESS) - $(hide) adb shell rm $$(VALGRIND_GTEST_WITNESS) - $(hide) adb shell chmod 755 $(maybe_art_test_chroot)$$(PRIVATE_TARGET_EXE) + $(hide) adb shell touch $$(PRIVATE_VALGRIND_GTEST_WITNESS) + $(hide) adb shell rm $$(PRIVATE_VALGRIND_GTEST_WITNESS) + $(hide) adb shell $$(PRIVATE_MAYBE_CHROOT_COMMAND) chmod 755 $$(PRIVATE_TARGET_EXE) $(hide) $$(call ART_TEST_SKIP,$$@) && \ - (adb shell "$(maybe_chroot_command) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ + (adb shell "$$(PRIVATE_MAYBE_CHROOT_COMMAND) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \ $(ART_GTEST_TARGET_ANDROID_ROOT)/bin/valgrind \ --leak-check=full --error-exitcode=1 --workaround-gcc296-bugs=yes \ --suppressions=$(ART_TARGET_TEST_DIR)/valgrind-target-suppressions.txt \ --num-callers=50 --show-mismatched-frees=no $$(PRIVATE_TARGET_EXE) \ - && touch $$(VALGRIND_GTEST_WITNESS)" \ - && (adb pull $$(VALGRIND_GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ + && touch $$(PRIVATE_VALGRIND_GTEST_WITNESS)" \ + && (adb pull $$(PRIVATE_VALGRIND_GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ || $$(call ART_TEST_FAILED,$$@)) $(hide) rm -f /tmp/$$@-$$$$PPID -- GitLab From 59df4f8698cc267e49a6ca5f59abdded89a1f5b1 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Wed, 23 May 2018 18:21:36 +0100 Subject: [PATCH 447/749] Check that /system exists in chroot before cleaning it up. Test: adb shell rm -rf "$ART_TEST_CHROOT/system" && art/tools/cleanup-buildbot-device.sh Bug: 34729697 Change-Id: Icd5a1e0dd35480704c468a4e738f88a86ab85f8f --- tools/cleanup-buildbot-device.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tools/cleanup-buildbot-device.sh b/tools/cleanup-buildbot-device.sh index 53072ae7c5..8c28828261 100755 --- a/tools/cleanup-buildbot-device.sh +++ b/tools/cleanup-buildbot-device.sh @@ -44,10 +44,11 @@ if [[ -n "$ART_TEST_CHROOT" ]]; then # TODO: Also consider adding a "tear down device" step on the ART # Buildbot (at the very end of a build) undoing (some of) the work # done in the "device setup" step. - adb shell find "$ART_TEST_CHROOT/system" \ - ! -path "$ART_TEST_CHROOT/system/etc/selinux/plat_property_contexts" \ - ! -type d \ - -exec rm -f \{\} + + adb shell test -f "$ART_TEST_CHROOT/system" \ + "&&" find "$ART_TEST_CHROOT/system" \ + ! -path "$ART_TEST_CHROOT/system/etc/selinux/plat_property_contexts" \ + ! -type d \ + -exec rm -f \{\} + echo -e "${green}Clean up some subdirs in /data in chroot${nc}" adb shell rm -rf \ -- GitLab From 5604938a767dfb44eae72dc56805f641e16a79cc Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Wed, 23 May 2018 18:26:22 +0100 Subject: [PATCH 448/749] Add a tear-down script for devices used in ART target testing. This script is meant to undo (most of) the set-up work done by tools/setup-buildbot-device.sh. Test: art/tools/setup-buildbot-device.sh && art/tools/teardown-buildbot-device.sh Bug: 34729697 Change-Id: I4a16d8451f5be461121ca9492dd5e732177bd719 --- tools/cleanup-buildbot-device.sh | 4 -- tools/setup-buildbot-device.sh | 5 ++- tools/teardown-buildbot-device.sh | 70 +++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 5 deletions(-) create mode 100755 tools/teardown-buildbot-device.sh diff --git a/tools/cleanup-buildbot-device.sh b/tools/cleanup-buildbot-device.sh index 8c28828261..2144b02c2f 100755 --- a/tools/cleanup-buildbot-device.sh +++ b/tools/cleanup-buildbot-device.sh @@ -40,10 +40,6 @@ if [[ -n "$ART_TEST_CHROOT" ]]; then # # TODO: Reorder ART Buildbot steps so that "device cleanup" happens # before "setup device" and remove this special case. - # - # TODO: Also consider adding a "tear down device" step on the ART - # Buildbot (at the very end of a build) undoing (some of) the work - # done in the "device setup" step. adb shell test -f "$ART_TEST_CHROOT/system" \ "&&" find "$ART_TEST_CHROOT/system" \ ! -path "$ART_TEST_CHROOT/system/etc/selinux/plat_property_contexts" \ diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh index f71d973925..f89197580d 100755 --- a/tools/setup-buildbot-device.sh +++ b/tools/setup-buildbot-device.sh @@ -14,10 +14,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +# The work does by this script is (mostly) undone by tools/teardown-buildbot-device.sh. +# Make sure to keep these files in sync. + green='\033[0;32m' nc='\033[0m' -# Setup as root, as some actions performed here (e.g. setting the date) requires it. +# Setup as root, as some actions performed here require it. adb root adb wait-for-device diff --git a/tools/teardown-buildbot-device.sh b/tools/teardown-buildbot-device.sh new file mode 100755 index 0000000000..df239a28bc --- /dev/null +++ b/tools/teardown-buildbot-device.sh @@ -0,0 +1,70 @@ +#!/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. + +# This script undoes (most of) the work done by tools/setup-buildbot-device.sh. +# Make sure to keep these files in sync. + +green='\033[0;32m' +nc='\033[0m' + +# Setup as root, as some actions performed here require it. +adb root +adb wait-for-device + +if [[ -n "$ART_TEST_CHROOT" ]]; then + # Tear down the chroot dir. + echo -e "${green}Tear down the chroot dir in $ART_TEST_CHROOT${nc}" + + # Check that ART_TEST_CHROOT is correctly defined. + [[ "x$ART_TEST_CHROOT" = x/* ]] || { echo "$ART_TEST_CHROOT is not an absolute path"; exit 1; } + + # Remove /dev from chroot. + adb shell mount | grep -q "^tmpfs on $ART_TEST_CHROOT/dev type tmpfs " \ + && adb shell umount "$ART_TEST_CHROOT/dev" \ + && adb shell rmdir "$ART_TEST_CHROOT/dev" + + # Remove /sys/kernel/debug from chroot. + adb shell mount | grep -q "^debugfs on $ART_TEST_CHROOT/sys/kernel/debug type debugfs " \ + && adb shell umount "$ART_TEST_CHROOT/sys/kernel/debug" + # Remove /sys from chroot. + adb shell mount | grep -q "^sysfs on $ART_TEST_CHROOT/sys type sysfs " \ + && adb shell umount "$ART_TEST_CHROOT/sys" \ + && adb shell rmdir "$ART_TEST_CHROOT/sys" + + # Remove /proc from chroot. + adb shell mount | grep -q "^proc on $ART_TEST_CHROOT/proc type proc " \ + && adb shell umount "$ART_TEST_CHROOT/proc" \ + && adb shell rmdir "$ART_TEST_CHROOT/proc" + + # Remove /etc from chroot. + adb shell rm -f "$ART_TEST_CHROOT/etc" + adb shell rm -rf "$ART_TEST_CHROOT/system/etc" + + # Remove directories used for ART testing in chroot. + adb shell rm -rf "$ART_TEST_CHROOT/data/local/tmp" + adb shell rm -rf "$ART_TEST_CHROOT/data/dalvik-cache" + adb shell rm -rf "$ART_TEST_CHROOT/tmp" + + # Remove property_contexts file(s) from chroot. + property_context_files="/property_contexts \ + /system/etc/selinux/plat_property_contexts \ + /vendor/etc/selinux/nonplat_property_context \ + /plat_property_contexts \ + /nonplat_property_contexts" + for f in $property_context_files; do + adb shell test -f "$f" "&&" rm -f "$ART_TEST_CHROOT$f" + done +fi -- GitLab From 05dc23eab55efcfbd13a3de59864d0491e87b834 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 22 May 2018 11:56:14 -0700 Subject: [PATCH 449/749] Add ClassIterator Add a way to iterate over all of the classes in a Dex file. The iterator returns a ClassAccessor for each class. Added DexFile::GetClasses to return an iteration range for this new iterator type. Sample usage: for (ClassAccessor accessor : dex_file.GetClasses()) { Bug: 79758018 Bug: 77709234 Test: test-art-host Change-Id: I66e000aa11f433e72f6857496f4e89a0b811f5a2 --- libdexfile/dex/class_accessor-inl.h | 59 +++++++----- libdexfile/dex/class_accessor.h | 24 +++-- libdexfile/dex/class_iterator.h | 101 +++++++++++++++++++++ libdexfile/dex/code_item_accessors.h | 5 + libdexfile/dex/dex_file-inl.h | 5 + libdexfile/dex/dex_file.h | 3 + tools/dexanalyze/dexanalyze_experiments.cc | 21 ++--- 7 files changed, 176 insertions(+), 42 deletions(-) create mode 100644 libdexfile/dex/class_iterator.h diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h index bcd0a7b66c..5cfbcaa359 100644 --- a/libdexfile/dex/class_accessor-inl.h +++ b/libdexfile/dex/class_accessor-inl.h @@ -20,19 +20,22 @@ #include "class_accessor.h" #include "base/leb128.h" +#include "class_iterator.h" +#include "code_item_accessors-inl.h" namespace art { -inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const DexFile::ClassDef& class_def) - : ClassAccessor(dex_file, dex_file.GetClassData(class_def)) {} +inline ClassAccessor::ClassAccessor(const ClassIteratorData& data) + : ClassAccessor(data.dex_file_, data.dex_file_.GetClassDef(data.class_def_idx_)) {} -inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const uint8_t* class_data) +inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const DexFile::ClassDef& class_def) : dex_file_(dex_file), - ptr_pos_(class_data), - num_static_fields_(class_data != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u), - num_instance_fields_(class_data != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u), - num_direct_methods_(class_data != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u), - num_virtual_methods_(class_data != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u) {} + descriptor_index_(class_def.class_idx_), + ptr_pos_(dex_file.GetClassData(class_def)), + num_static_fields_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u), + num_instance_fields_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u), + num_direct_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u), + num_virtual_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u) {} inline const uint8_t* ClassAccessor::Method::Read(const uint8_t* ptr) { method_idx_ += DecodeUnsignedLeb128(&ptr); @@ -57,25 +60,33 @@ inline void ClassAccessor::VisitMethodsAndFields( const DirectMethodVisitor& direct_method_visitor, const VirtualMethodVisitor& virtual_method_visitor) const { const uint8_t* ptr = ptr_pos_; - for (size_t i = 0; i < num_static_fields_; ++i) { + { Field data; - ptr = data.Read(ptr); - static_field_visitor(data); + for (size_t i = 0; i < num_static_fields_; ++i) { + ptr = data.Read(ptr); + static_field_visitor(data); + } } - for (size_t i = 0; i < num_instance_fields_; ++i) { + { Field data; - ptr = data.Read(ptr); - instance_field_visitor(data); + for (size_t i = 0; i < num_instance_fields_; ++i) { + ptr = data.Read(ptr); + instance_field_visitor(data); + } } - for (size_t i = 0; i < num_direct_methods_; ++i) { - Method data; - ptr = data.Read(ptr); - direct_method_visitor(data); + { + Method data(dex_file_); + for (size_t i = 0; i < num_direct_methods_; ++i) { + ptr = data.Read(ptr); + direct_method_visitor(data); + } } - for (size_t i = 0; i < num_virtual_methods_; ++i) { - Method data; - ptr = data.Read(ptr); - virtual_method_visitor(data); + { + Method data(dex_file_); + for (size_t i = 0; i < num_virtual_methods_; ++i) { + ptr = data.Read(ptr); + virtual_method_visitor(data); + } } } @@ -99,6 +110,10 @@ inline const DexFile::CodeItem* ClassAccessor::GetCodeItem(const Method& method) return dex_file_.GetCodeItem(method.GetCodeItemOffset()); } +inline CodeItemInstructionAccessor ClassAccessor::Method::GetInstructions() const { + return CodeItemInstructionAccessor(dex_file_, dex_file_.GetCodeItem(GetCodeItemOffset())); +} + } // namespace art #endif // ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_ diff --git a/libdexfile/dex/class_accessor.h b/libdexfile/dex/class_accessor.h index 59a6b5dfa5..835c4e2eb7 100644 --- a/libdexfile/dex/class_accessor.h +++ b/libdexfile/dex/class_accessor.h @@ -18,10 +18,13 @@ #define ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_H_ #include "base/utils.h" +#include "code_item_accessors.h" #include "dex_file.h" namespace art { +class ClassIteratorData; + // Classes to access Dex data. class ClassAccessor { public: @@ -40,10 +43,15 @@ class ClassAccessor { return code_off_; } + CodeItemInstructionAccessor GetInstructions() const; + private: + explicit Method(const DexFile& dex_file) : dex_file_(dex_file) {} + const uint8_t* Read(const uint8_t* ptr); // A decoded version of the method of a class_data_item. + const DexFile& dex_file_; uint32_t method_idx_ = 0u; uint32_t access_flags_ = 0u; uint32_t code_off_ = 0u; @@ -72,7 +80,10 @@ class ClassAccessor { friend class ClassAccessor; }; - ALWAYS_INLINE ClassAccessor(const DexFile& dex_file, const DexFile::ClassDef& class_def); + // Not explicit specifically for range-based loops. + ALWAYS_INLINE ClassAccessor(const ClassIteratorData& data); + + ClassAccessor(const DexFile& dex_file, const DexFile::ClassDef& class_def); // Return the code item for a method. const DexFile::CodeItem* GetCodeItem(const Method& method) const; @@ -112,18 +123,19 @@ class ClassAccessor { return num_virtual_methods_; } - protected: - ALWAYS_INLINE ClassAccessor(const DexFile& dex_file, const uint8_t* class_data); + // TODO: Deprecate + dex::TypeIndex GetDescriptorIndex() const { + return descriptor_index_; + } + protected: const DexFile& dex_file_; + const dex::TypeIndex descriptor_index_ = {}; const uint8_t* ptr_pos_ = nullptr; // Pointer into stream of class_data_item. const uint32_t num_static_fields_ = 0u; const uint32_t num_instance_fields_ = 0u; const uint32_t num_direct_methods_ = 0u; const uint32_t num_virtual_methods_ = 0u; - // Only cache descriptor. - const void* class_def_ = nullptr; - const void* class_data_ = nullptr; }; } // namespace art diff --git a/libdexfile/dex/class_iterator.h b/libdexfile/dex/class_iterator.h new file mode 100644 index 0000000000..477c93b508 --- /dev/null +++ b/libdexfile/dex/class_iterator.h @@ -0,0 +1,101 @@ +/* + * 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. + */ + +#ifndef ART_LIBDEXFILE_DEX_CLASS_ITERATOR_H_ +#define ART_LIBDEXFILE_DEX_CLASS_ITERATOR_H_ + +#include "base/logging.h" + +namespace art { + +class ClassAccessor; +class ClassIterator; +class DexFile; + +// Holder class, used to construct ClassAccessors. +class ClassIteratorData { + public: + ClassIteratorData(const DexFile& dex_file, uint32_t class_def_idx) + : dex_file_(dex_file), + class_def_idx_(class_def_idx) {} + + private: + const DexFile& dex_file_; + uint32_t class_def_idx_ = 0u; + + friend class ClassAccessor; + friend class ClassIterator; +}; + +// Iterator for visiting classes in a Dex file. +class ClassIterator : public std::iterator { + public: + using value_type = std::iterator::value_type; + using difference_type = std::iterator::difference_type; + + ClassIterator(const DexFile& dex_file, uint32_t class_def_idx) + : data_(dex_file, class_def_idx) {} + + // Value after modification. + ClassIterator& operator++() { + ++data_.class_def_idx_; + return *this; + } + + // Value before modification. + ClassIterator operator++(int) { + ClassIterator temp = *this; + ++*this; + return temp; + } + + const value_type& operator*() const { + return data_; + } + + bool operator==(const ClassIterator& rhs) const { + DCHECK_EQ(&data_.dex_file_, &rhs.data_.dex_file_) << "Comparing different dex files."; + return data_.class_def_idx_ == rhs.data_.class_def_idx_; + } + + bool operator!=(const ClassIterator& rhs) const { + return !(*this == rhs); + } + + bool operator<(const ClassIterator& rhs) const { + DCHECK_EQ(&data_.dex_file_, &rhs.data_.dex_file_) << "Comparing different dex files."; + return data_.class_def_idx_ < rhs.data_.class_def_idx_; + } + + bool operator>(const ClassIterator& rhs) const { + return rhs < *this; + } + + bool operator<=(const ClassIterator& rhs) const { + return !(rhs < *this); + } + + bool operator>=(const ClassIterator& rhs) const { + return !(*this < rhs); + } + + protected: + ClassIteratorData data_; +}; + +} // namespace art + +#endif // ART_LIBDEXFILE_DEX_CLASS_ITERATOR_H_ diff --git a/libdexfile/dex/code_item_accessors.h b/libdexfile/dex/code_item_accessors.h index ba7c126ed8..5786d3f611 100644 --- a/libdexfile/dex/code_item_accessors.h +++ b/libdexfile/dex/code_item_accessors.h @@ -47,6 +47,11 @@ class CodeItemInstructionAccessor { return insns_size_in_code_units_; } + uint32_t InsnsSizeInBytes() const { + static constexpr uint32_t kCodeUnitSizeInBytes = 2u; + return insns_size_in_code_units_ * kCodeUnitSizeInBytes; + } + const uint16_t* Insns() const { return insns_; } diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h index e78e8d7a44..f5dd374253 100644 --- a/libdexfile/dex/dex_file-inl.h +++ b/libdexfile/dex/dex_file-inl.h @@ -20,6 +20,7 @@ #include "base/casts.h" #include "base/leb128.h" #include "base/stringpiece.h" +#include "class_iterator.h" #include "compact_dex_file.h" #include "dex_file.h" #include "invoke_type.h" @@ -527,6 +528,10 @@ inline void DexFile::ClassDef::VisitMethods(const DexFile* dex_file, const Visit } } +inline IterationRange DexFile::GetClasses() const { + return { ClassIterator(*this, 0u), ClassIterator(*this, NumClassDefs()) }; +} + } // 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 87d2c48ff1..f1f8b505bd 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -27,6 +27,7 @@ #include "base/iteration_range.h" #include "base/macros.h" #include "base/value_object.h" +#include "class_iterator.h" #include "dex_file_types.h" #include "dex_instruction_iterator.h" #include "hidden_api_access_flags.h" @@ -1012,6 +1013,8 @@ class DexFile { // Changes the dex file pointed to by class_it to not have any hiddenapi flags. static void UnHideAccessFlags(ClassDataItemIterator& class_it); + inline IterationRange GetClasses() const; + protected: // First Dex format version supporting default methods. static const uint32_t kDefaultMethodsVersion = 37; diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc index f9bf45da4f..427a465caf 100644 --- a/tools/dexanalyze/dexanalyze_experiments.cc +++ b/tools/dexanalyze/dexanalyze_experiments.cc @@ -24,6 +24,7 @@ #include "android-base/stringprintf.h" #include "dex/class_accessor-inl.h" +#include "dex/class_iterator.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_instruction-inl.h" #include "dex/standard_dex_file.h" @@ -125,20 +126,12 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { num_field_ids_ += dex_file.NumFieldIds(); num_type_ids_ += dex_file.NumTypeIds(); num_class_defs_ += dex_file.NumClassDefs(); - for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); ++class_def_index) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); - ClassAccessor accessor(dex_file, class_def); + for (ClassAccessor accessor : dex_file.GetClasses()) { std::set unique_method_ids; std::set unique_string_ids; accessor.VisitMethods([&](const ClassAccessor::Method& method) { - const DexFile::CodeItem* code_item = accessor.GetCodeItem(method); - if (code_item == nullptr) { - return; - } - CodeItemInstructionAccessor instructions(dex_file, code_item); - const uint16_t* code_ptr = instructions.Insns(); - dex_code_bytes_ += instructions.InsnsSizeInCodeUnits() * sizeof(code_ptr[0]); - for (const DexInstructionPcPair& inst : instructions) { + dex_code_bytes_ += method.GetInstructions().InsnsSizeInBytes(); + for (const DexInstructionPcPair& inst : method.GetInstructions()) { switch (inst->Opcode()) { case Instruction::CONST_STRING: { const dex::StringIndex string_index(inst->VRegB_21c()); @@ -157,7 +150,7 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { case Instruction::INVOKE_VIRTUAL_RANGE: { bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE); uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); - if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) { + if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetDescriptorIndex()) { ++same_class_virtual_; } else { ++other_class_virtual_; @@ -169,7 +162,7 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { case Instruction::INVOKE_DIRECT_RANGE: { bool is_range = (inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE); uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) { + if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetDescriptorIndex()) { ++same_class_direct_; } else { ++other_class_direct_; @@ -181,7 +174,7 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { case Instruction::INVOKE_STATIC_RANGE: { bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE); uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - if (dex_file.GetMethodId(method_idx).class_idx_ == class_def.class_idx_) { + if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetDescriptorIndex()) { ++same_class_static_; } else { ++other_class_static_; -- GitLab From 7c3a8c1679c9356d80e0fc1afa7471b056a5b779 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 23 May 2018 18:09:45 -0700 Subject: [PATCH 450/749] Add measurement of Dex code bytes and number of unique code items Also modified -i option to also ignore Dex verificaiton, this allows dumping jars that have gone through hidden API tool. Bug: 77721545 Test: test-art-host Change-Id: If608c0cddd232be465c5df9cdf9b06025e68ea83 --- tools/dexanalyze/dexanalyze.cc | 3 ++- tools/dexanalyze/dexanalyze_experiments.cc | 7 ++++++- tools/dexanalyze/dexanalyze_experiments.h | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/dexanalyze/dexanalyze.cc b/tools/dexanalyze/dexanalyze.cc index 46c48520e3..083de7066d 100644 --- a/tools/dexanalyze/dexanalyze.cc +++ b/tools/dexanalyze/dexanalyze.cc @@ -49,7 +49,7 @@ class DexAnalyze { << "Usage " << argv[0] << " [options] \n" << " [options] is a combination of the following\n" << " -count_indices (Count dex indices accessed from code items)\n" - << " -i (Ignore DEX checksum failure)\n" + << " -i (Ignore Dex checksum and verification failures)\n" << " -a (Run all experiments)\n" << " -d (Dump on per DEX basis)\n"; return kExitCodeUsageError; @@ -62,6 +62,7 @@ class DexAnalyze { const std::string arg = argv[i]; if (arg == "-i") { verify_checksum_ = false; + run_dex_file_verifier_ = false; } else if (arg == "-a") { run_all_experiments_ = true; } else if (arg == "-count-indices") { diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc index 427a465caf..2e95286777 100644 --- a/tools/dexanalyze/dexanalyze_experiments.cc +++ b/tools/dexanalyze/dexanalyze_experiments.cc @@ -126,11 +126,13 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { num_field_ids_ += dex_file.NumFieldIds(); num_type_ids_ += dex_file.NumTypeIds(); num_class_defs_ += dex_file.NumClassDefs(); + std::set unique_code_items; for (ClassAccessor accessor : dex_file.GetClasses()) { std::set unique_method_ids; std::set unique_string_ids; accessor.VisitMethods([&](const ClassAccessor::Method& method) { dex_code_bytes_ += method.GetInstructions().InsnsSizeInBytes(); + unique_code_items.insert(method.GetCodeItemOffset()); for (const DexInstructionPcPair& inst : method.GetInstructions()) { switch (inst->Opcode()) { case Instruction::CONST_STRING: { @@ -190,6 +192,7 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { total_unique_method_idx_ += unique_method_ids.size(); total_unique_string_ids_ += unique_string_ids.size(); } + total_unique_code_items_ += unique_code_items.size(); } void CountDexIndices::Dump(std::ostream& os, uint64_t total_size) const { @@ -212,7 +215,9 @@ void CountDexIndices::Dump(std::ostream& os, uint64_t total_size) const { os << "Same class invoke: " << same_class_total << "\n"; os << "Other class invoke: " << other_class_total << "\n"; os << "Invokes from code: " << (same_class_total + other_class_total) << "\n"; - os << "Total dex size: " << total_size << "\n"; + os << "Total Dex code bytes: " << Percent(dex_code_bytes_, total_size) << "\n"; + os << "Total unique code items: " << total_unique_code_items_ << "\n"; + os << "Total Dex size: " << total_size << "\n"; } } // namespace art diff --git a/tools/dexanalyze/dexanalyze_experiments.h b/tools/dexanalyze/dexanalyze_experiments.h index 0fb4d32005..c84b082955 100644 --- a/tools/dexanalyze/dexanalyze_experiments.h +++ b/tools/dexanalyze/dexanalyze_experiments.h @@ -63,6 +63,7 @@ class CountDexIndices : public Experiment { size_t num_string_ids_from_code_ = 0; size_t total_unique_method_idx_ = 0; size_t total_unique_string_ids_ = 0; + uint64_t total_unique_code_items_ = 0u; // Other dex ids. size_t dex_code_bytes_ = 0; -- GitLab From 4a1cac420b34cc23458742e0deb67116b69b93eb Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Wed, 23 May 2018 16:23:27 +0100 Subject: [PATCH 451/749] ART: Remove jack from test Test: art/test.py --host Change-Id: I31b0c0d57d344f54a8c0545fd32c81a893b4ec75 --- test.py | 3 - test/003-omnibus-opcodes/build | 10 +- test/005-annotations/build | 11 +- test/022-interface/build | 9 +- test/023-many-interfaces/build | 14 +- test/056-const-string-jumbo/build | 13 +- .../091-override-package-private-method/build | 16 +- test/111-unresolvable-exception/build | 10 +- test/113-multidex/build | 24 +-- test/124-missing-classes/build | 10 +- test/126-miranda-multidex/build | 24 +-- test/127-checker-secondarydex/build | 16 +- test/138-duplicate-classes-check2/build | 10 +- .../smali/art/Test1929$Impl.smali | 2 +- .../src/art/Test1929.java | 2 +- test/303-verification-stress/build | 17 +- test/442-checker-constant-folding/build | 6 - test/450-checker-types/build | 7 +- .../458-checker-instruct-simplification/build | 6 - test/463-checker-boolean-simplifier/build | 6 - test/536-checker-intrinsic-optimization/build | 23 --- test/537-checker-inline-and-unverified/build | 23 --- test/565-checker-doublenegbitwise/build | 6 - test/586-checker-null-array-get/build | 23 --- .../593-checker-boolean-2-integral-conv/build | 23 --- test/633-checker-rtp-getclass/build | 23 --- test/636-arm64-veneer-pool/build | 27 ---- test/641-checker-arraycopy/build | 28 ---- test/910-methods/check | 5 - test/910-methods/expected_jack.diff | 4 - test/911-get-stack-trace/check | 5 - test/911-get-stack-trace/expected_jack.diff | 32 ---- test/913-heaps/check | 5 +- test/913-heaps/expected_jack.diff | 50 ------ test/968-default-partial-compile-gen/build | 2 - test/970-iface-super-resolution-gen/build | 10 -- test/971-iface-super/build | 2 - test/etc/default-build | 146 +++++------------- test/run-test | 25 +-- test/testrunner/env.py | 7 +- test/testrunner/run_build_test_target.py | 2 - test/testrunner/testrunner.py | 8 - 42 files changed, 79 insertions(+), 616 deletions(-) delete mode 100755 test/536-checker-intrinsic-optimization/build delete mode 100755 test/537-checker-inline-and-unverified/build delete mode 100755 test/586-checker-null-array-get/build delete mode 100755 test/593-checker-boolean-2-integral-conv/build delete mode 100755 test/633-checker-rtp-getclass/build delete mode 100755 test/636-arm64-veneer-pool/build delete mode 100644 test/641-checker-arraycopy/build delete mode 100644 test/910-methods/expected_jack.diff delete mode 100644 test/911-get-stack-trace/expected_jack.diff delete mode 100644 test/913-heaps/expected_jack.diff diff --git a/test.py b/test.py index 047d8125fb..bc15736bd8 100755 --- a/test.py +++ b/test.py @@ -64,11 +64,8 @@ if options.gtest or not options.run_test: build_command = 'make' build_command += ' -j' + str(options.n_threads) - build_command += ' -C ' + ANDROID_BUILD_TOP build_command += ' ' + build_target - # Add 'dist' to avoid Jack issues b/36169180. - build_command += ' dist' print build_command diff --git a/test/003-omnibus-opcodes/build b/test/003-omnibus-opcodes/build index dba3549b1a..4d3fb37d1d 100644 --- a/test/003-omnibus-opcodes/build +++ b/test/003-omnibus-opcodes/build @@ -22,15 +22,7 @@ ${JAVAC} -d classes `find src -name '*.java'` rm classes/UnresClass.class ${JAVAC} -d classes `find src2 -name '*.java'` -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - ${JACK} --import classes.jill.jar --output-dex . -else - if [ ${NEED_DEX} = "true" ]; then - ${DX} -JXmx256m --debug --dex --output=classes.dex classes - fi -fi - if [ ${NEED_DEX} = "true" ]; then + ${DX} -JXmx256m --debug --dex --output=classes.dex classes zip $TEST_NAME.jar classes.dex fi diff --git a/test/005-annotations/build b/test/005-annotations/build index 8b9f55065e..8eb07a9bf5 100644 --- a/test/005-annotations/build +++ b/test/005-annotations/build @@ -28,16 +28,7 @@ ${JAVAC} -d classes `find src2 -name '*.java'` rm 'classes/android/test/anno/MissingAnnotation.class' rm 'classes/android/test/anno/ClassWithInnerAnnotationClass$MissingInnerAnnotationClass.class' -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - # Jack needs to emit annotations with CLASS retention. - ${JACK} -D jack.dex.annotation.class-retention=true --import classes.jill.jar --output-dex . -else - if [ ${NEED_DEX} = "true" ]; then - ${DX} -JXmx256m --debug --dex --output=classes.dex classes - fi -fi - if [ ${NEED_DEX} = "true" ]; then + ${DX} -JXmx256m --debug --dex --output=classes.dex classes zip $TEST_NAME.jar classes.dex fi diff --git a/test/022-interface/build b/test/022-interface/build index 5cfc7f25b7..f6aad91e97 100644 --- a/test/022-interface/build +++ b/test/022-interface/build @@ -17,13 +17,6 @@ # Stop if something fails. set -e -# Use classes that are compiled with ecj that exposes an invokeinterface -# issue when interfaces override methods in Object -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - ${JACK} --import classes.jill.jar --output-dex . -else - ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes -fi +${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes zip $TEST_NAME.jar classes.dex diff --git a/test/023-many-interfaces/build b/test/023-many-interfaces/build index b4b5bd4c4a..faa3ce7178 100644 --- a/test/023-many-interfaces/build +++ b/test/023-many-interfaces/build @@ -21,16 +21,4 @@ set -e gcc -Wall -Werror -o iface-gen iface-gen.c ./iface-gen -if [ ${USE_JACK} = "true" ]; then - # Use the default Jack commands - ./default-build -else - mkdir classes - ${JAVAC} -d classes src/*.java - - # dx needs more memory for that test so do not pass Xmx option here. - if [ ${NEED_DEX} = "true" ]; then - ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes - zip $TEST_NAME.jar classes.dex - fi -fi +./default-build "$@" diff --git a/test/056-const-string-jumbo/build b/test/056-const-string-jumbo/build index 5344ac38eb..47641d5891 100644 --- a/test/056-const-string-jumbo/build +++ b/test/056-const-string-jumbo/build @@ -39,17 +39,10 @@ function writeFile(name, start, end) { printf("}\n") > fileName; }' -if [ ${USE_JACK} = "true" ]; then - ${JACK} --output-dex . src -else - mkdir classes - ${JAVAC} -d classes src/*.java - - if [ ${NEED_DEX} = "true" ]; then - ${DX} -JXmx500m --debug --dex --no-optimize --positions=none --no-locals --output=classes.dex classes - fi -fi +mkdir classes +${JAVAC} -d classes src/*.java if [ ${NEED_DEX} = "true" ]; then + ${DX} -JXmx500m --debug --dex --no-optimize --positions=none --no-locals --output=classes.dex classes zip $TEST_NAME.jar classes.dex fi diff --git a/test/091-override-package-private-method/build b/test/091-override-package-private-method/build index 073a4ba9bc..ea12b3a540 100755 --- a/test/091-override-package-private-method/build +++ b/test/091-override-package-private-method/build @@ -23,19 +23,9 @@ ${JAVAC} -d classes `find src -name '*.java'` mkdir classes-ex mv classes/OverridePackagePrivateMethodSuper.class classes-ex -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - jar cf classes-ex.jill.jar -C classes-ex . - - ${JACK} --import classes.jill.jar --output-dex . +if [ ${NEED_DEX} = "true" ]; then + ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes zip $TEST_NAME.jar classes.dex - ${JACK} --import classes-ex.jill.jar --output-dex . + ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex zip ${TEST_NAME}-ex.jar classes.dex -else - if [ ${NEED_DEX} = "true" ]; then - ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes - zip $TEST_NAME.jar classes.dex - ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex - zip ${TEST_NAME}-ex.jar classes.dex - fi fi diff --git a/test/111-unresolvable-exception/build b/test/111-unresolvable-exception/build index cf19f60d51..6fe73af8d8 100644 --- a/test/111-unresolvable-exception/build +++ b/test/111-unresolvable-exception/build @@ -21,15 +21,7 @@ mkdir classes ${JAVAC} -d classes `find src -name '*.java'` rm classes/TestException.class -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - ${JACK} --import classes.jill.jar --output-dex . -else - if [ ${NEED_DEX} = "true" ]; then - ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes - fi -fi - if [ ${NEED_DEX} = "true" ]; then + ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes zip $TEST_NAME.jar classes.dex fi diff --git a/test/113-multidex/build b/test/113-multidex/build index b980e501be..f945563939 100644 --- a/test/113-multidex/build +++ b/test/113-multidex/build @@ -27,25 +27,11 @@ mkdir classes2 ${JAVAC} -d classes2 `find src -name '*.java'` rm classes2/Second.class classes2/FillerA.class classes2/FillerB.class classes2/Inf*.class -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - jar cf classes2.jill.jar -C classes2 . - - ${JACK} --import classes.jill.jar --output-dex . - mv classes.dex classes-1.dex - ${JACK} --import classes2.jill.jar --output-dex . - mv classes.dex classes2.dex - mv classes-1.dex classes.dex -else - if [ ${NEED_DEX} = "true" ]; then - # All except Main - ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes - - # Only Main - ${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex classes2 - fi -fi - if [ ${NEED_DEX} = "true" ]; then + # All except Main + ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes + + # Only Main + ${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex classes2 zip $TEST_NAME.jar classes.dex classes2.dex fi diff --git a/test/124-missing-classes/build b/test/124-missing-classes/build index ea45cd27e5..b13aa6e851 100644 --- a/test/124-missing-classes/build +++ b/test/124-missing-classes/build @@ -26,15 +26,7 @@ ${JAVAC} -d classes `find src -name '*.java'` rm 'classes/MissingClass.class' rm 'classes/Main$MissingInnerClass.class' -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - ${JACK} --import classes.jill.jar --output-dex . -else - if [ ${NEED_DEX} = "true" ]; then - ${DX} -JXmx256m --debug --dex --output=classes.dex classes - fi -fi - if [ ${NEED_DEX} = "true" ]; then + ${DX} -JXmx256m --debug --dex --output=classes.dex classes zip $TEST_NAME.jar classes.dex fi diff --git a/test/126-miranda-multidex/build b/test/126-miranda-multidex/build index 2a5e7daa12..cf19855316 100644 --- a/test/126-miranda-multidex/build +++ b/test/126-miranda-multidex/build @@ -27,25 +27,11 @@ mkdir classes2 ${JAVAC} -d classes2 `find src -name '*.java'` rm classes2/Main.class classes2/MirandaAbstract.class classes2/MirandaClass*.class classes2/MirandaInterface2*.class -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - jar cf classes2.jill.jar -C classes2 . - - ${JACK} --import classes.jill.jar --output-dex . - mv classes.dex classes-1.dex - ${JACK} --import classes2.jill.jar --output-dex . - mv classes.dex classes2.dex - mv classes-1.dex classes.dex -else - if [ ${NEED_DEX} = "true" ]; then - # All except Main - ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes - - # Only Main - ${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex classes2 - fi -fi - if [ ${NEED_DEX} = "true" ]; then + # All except Main + ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes + + # Only Main + ${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex classes2 zip $TEST_NAME.jar classes.dex classes2.dex fi diff --git a/test/127-checker-secondarydex/build b/test/127-checker-secondarydex/build index 7ce46acfed..712774f7ef 100755 --- a/test/127-checker-secondarydex/build +++ b/test/127-checker-secondarydex/build @@ -23,19 +23,9 @@ ${JAVAC} -d classes `find src -name '*.java'` mkdir classes-ex mv classes/Super.class classes-ex -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - jar cf classes-ex.jill.jar -C classes-ex . - - ${JACK} --import classes.jill.jar --output-dex . +if [ ${NEED_DEX} = "true" ]; then + ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes zip $TEST_NAME.jar classes.dex - ${JACK} --import classes-ex.jill.jar --output-dex . + ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex zip ${TEST_NAME}-ex.jar classes.dex -else - if [ ${NEED_DEX} = "true" ]; then - ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes - zip $TEST_NAME.jar classes.dex - ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex - zip ${TEST_NAME}-ex.jar classes.dex - fi fi diff --git a/test/138-duplicate-classes-check2/build b/test/138-duplicate-classes-check2/build index d346251edb..76d535abf1 100755 --- a/test/138-duplicate-classes-check2/build +++ b/test/138-duplicate-classes-check2/build @@ -24,15 +24,7 @@ mkdir classes-ex ${JAVAC} -d classes-ex `find src-ex -name '*.java'` rm classes-ex/A.class -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - ${JACK} --import classes.jill.jar --output-dex . - zip ${TEST_NAME}.jar classes.dex - - jar cf classes-ex.jill.jar -C classes-ex . - ${JACK} --import classes-ex.jill.jar --output-dex . - zip ${TEST_NAME}-ex.jar classes.dex -elif [ ${NEED_DEX} = "true" ]; then +if [ ${NEED_DEX} = "true" ]; then ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes zip ${TEST_NAME}.jar classes.dex ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex diff --git a/test/1929-exception-catch-exception/smali/art/Test1929$Impl.smali b/test/1929-exception-catch-exception/smali/art/Test1929$Impl.smali index 4edd56f690..605b408d91 100644 --- a/test/1929-exception-catch-exception/smali/art/Test1929$Impl.smali +++ b/test/1929-exception-catch-exception/smali/art/Test1929$Impl.smali @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# The standard dx/jack/d8 all would leave the move-exception instructions outside of either catch +# The standard dx/d8 would leave the move-exception instructions outside of either catch # block. This is different from the RI which will leave the corresponding aload. # # See b/65203529 for more information. diff --git a/test/1929-exception-catch-exception/src/art/Test1929.java b/test/1929-exception-catch-exception/src/art/Test1929.java index e2deb3f85f..c0995a8468 100644 --- a/test/1929-exception-catch-exception/src/art/Test1929.java +++ b/test/1929-exception-catch-exception/src/art/Test1929.java @@ -149,7 +149,7 @@ public class Test1929 { public void run() { throwCatchBaseTestException(); } } - // dx/d8/jack all do an optimization around catch blocks that (while legal) breaks assumptions + // dx and d8 do an optimization around catch blocks that (while legal) breaks assumptions // this test relies on so we have the actual implementation be corrected smali. This does work // for RI however. diff --git a/test/303-verification-stress/build b/test/303-verification-stress/build index b67eaf2261..ba79541478 100644 --- a/test/303-verification-stress/build +++ b/test/303-verification-stress/build @@ -21,16 +21,11 @@ set -e gcc -Wall -Werror -o classes-gen classes-gen.c ./classes-gen -if [ ${USE_JACK} = "true" ]; then - # Use the default Jack commands - ./default-build -else - mkdir classes - ${JAVAC} -d classes src/*.java +mkdir classes +${JAVAC} -d classes src/*.java - # dx needs more memory for that test so do not pass Xmx option here. - if [ ${NEED_DEX} = "true" ]; then - ${DX} --debug --dex --output=classes.dex classes - zip $TEST_NAME.jar classes.dex - fi +# dx needs more memory for that test so do not pass Xmx option here. +if [ ${NEED_DEX} = "true" ]; then + ${DX} --debug --dex --output=classes.dex classes + zip $TEST_NAME.jar classes.dex fi diff --git a/test/442-checker-constant-folding/build b/test/442-checker-constant-folding/build index 947ec9a560..42b99ad9f8 100755 --- a/test/442-checker-constant-folding/build +++ b/test/442-checker-constant-folding/build @@ -14,12 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This checker test is incompatible with jack bytecode output, -# so force it to use javac/dx. -export USE_JACK=false -# Also disable desugar because it is missing in jack platform builds. -export DESUGAR=false - # See b/65168732 export DX=$ANDROID_HOST_OUT/bin/dx diff --git a/test/450-checker-types/build b/test/450-checker-types/build index 49292c9ac1..10ffcc537d 100755 --- a/test/450-checker-types/build +++ b/test/450-checker-types/build @@ -14,10 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This checker test is incompatible with jack bytecode output, -# so force it to use javac/dx. -export USE_JACK=false -# Also disable desugar because it is missing in jack platform builds. -export DESUGAR=false +# See b/65168732 +export USE_D8=false ./default-build "$@" diff --git a/test/458-checker-instruct-simplification/build b/test/458-checker-instruct-simplification/build index 3721955670..10ffcc537d 100755 --- a/test/458-checker-instruct-simplification/build +++ b/test/458-checker-instruct-simplification/build @@ -14,12 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This checker test is incompatible with jack bytecode output, -# so force it to use javac/dx. -export USE_JACK=false -# Also disable desugar because it is missing in jack platform builds. -export DESUGAR=false - # See b/65168732 export USE_D8=false diff --git a/test/463-checker-boolean-simplifier/build b/test/463-checker-boolean-simplifier/build index 3721955670..10ffcc537d 100755 --- a/test/463-checker-boolean-simplifier/build +++ b/test/463-checker-boolean-simplifier/build @@ -14,12 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This checker test is incompatible with jack bytecode output, -# so force it to use javac/dx. -export USE_JACK=false -# Also disable desugar because it is missing in jack platform builds. -export DESUGAR=false - # See b/65168732 export USE_D8=false diff --git a/test/536-checker-intrinsic-optimization/build b/test/536-checker-intrinsic-optimization/build deleted file mode 100755 index 49292c9ac1..0000000000 --- a/test/536-checker-intrinsic-optimization/build +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -# -# Copyright 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. - -# This checker test is incompatible with jack bytecode output, -# so force it to use javac/dx. -export USE_JACK=false -# Also disable desugar because it is missing in jack platform builds. -export DESUGAR=false - -./default-build "$@" diff --git a/test/537-checker-inline-and-unverified/build b/test/537-checker-inline-and-unverified/build deleted file mode 100755 index 49292c9ac1..0000000000 --- a/test/537-checker-inline-and-unverified/build +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -# -# Copyright 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. - -# This checker test is incompatible with jack bytecode output, -# so force it to use javac/dx. -export USE_JACK=false -# Also disable desugar because it is missing in jack platform builds. -export DESUGAR=false - -./default-build "$@" diff --git a/test/565-checker-doublenegbitwise/build b/test/565-checker-doublenegbitwise/build index 3721955670..10ffcc537d 100755 --- a/test/565-checker-doublenegbitwise/build +++ b/test/565-checker-doublenegbitwise/build @@ -14,12 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This checker test is incompatible with jack bytecode output, -# so force it to use javac/dx. -export USE_JACK=false -# Also disable desugar because it is missing in jack platform builds. -export DESUGAR=false - # See b/65168732 export USE_D8=false diff --git a/test/586-checker-null-array-get/build b/test/586-checker-null-array-get/build deleted file mode 100755 index 49292c9ac1..0000000000 --- a/test/586-checker-null-array-get/build +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -# -# Copyright 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. - -# This checker test is incompatible with jack bytecode output, -# so force it to use javac/dx. -export USE_JACK=false -# Also disable desugar because it is missing in jack platform builds. -export DESUGAR=false - -./default-build "$@" diff --git a/test/593-checker-boolean-2-integral-conv/build b/test/593-checker-boolean-2-integral-conv/build deleted file mode 100755 index 49292c9ac1..0000000000 --- a/test/593-checker-boolean-2-integral-conv/build +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -# -# Copyright 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. - -# This checker test is incompatible with jack bytecode output, -# so force it to use javac/dx. -export USE_JACK=false -# Also disable desugar because it is missing in jack platform builds. -export DESUGAR=false - -./default-build "$@" diff --git a/test/633-checker-rtp-getclass/build b/test/633-checker-rtp-getclass/build deleted file mode 100755 index 49292c9ac1..0000000000 --- a/test/633-checker-rtp-getclass/build +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -# -# Copyright 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. - -# This checker test is incompatible with jack bytecode output, -# so force it to use javac/dx. -export USE_JACK=false -# Also disable desugar because it is missing in jack platform builds. -export DESUGAR=false - -./default-build "$@" diff --git a/test/636-arm64-veneer-pool/build b/test/636-arm64-veneer-pool/build deleted file mode 100755 index 43f9018f13..0000000000 --- a/test/636-arm64-veneer-pool/build +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -# -# Copyright 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. -# Make us exit on a failure. - -set -e - -# Use javac+dx instead of jack. -export USE_JACK=false - -# Don't use desugar because the bootclasspath jars will be missing -# on a platform build compiled with ANDROID_COMPILE_WITH_JACK=true. -export USE_DESUGAR=false - -./default-build "$@" diff --git a/test/641-checker-arraycopy/build b/test/641-checker-arraycopy/build deleted file mode 100644 index 12e4423b56..0000000000 --- a/test/641-checker-arraycopy/build +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -# -# Copyright 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. - -# make us exit on a failure -set -e - -# Don't use jack for this test, to ensure we don't use -# the typed System.arraycopy versions directly. -export USE_JACK=false - -# Don't use desugar because the bootclasspath jars will be missing -# on a platform build compiled with ANDROID_COMPILE_WITH_JACK=true. -export USE_DESUGAR=false - -./default-build "$@" diff --git a/test/910-methods/check b/test/910-methods/check index 76b23cb906..61846adf9b 100644 --- a/test/910-methods/check +++ b/test/910-methods/check @@ -14,11 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Jack has a different set of bytecode offsets/method IDs in the expected.txt -if [[ "$USE_JACK" == true ]]; then - patch -p0 expected.txt < expected_jack.diff -fi - ./default-check "$@" if [[ "$?" == "0" ]]; then exit 0; diff --git a/test/910-methods/expected_jack.diff b/test/910-methods/expected_jack.diff deleted file mode 100644 index 2fe6953f6c..0000000000 --- a/test/910-methods/expected_jack.diff +++ /dev/null @@ -1,4 +0,0 @@ -7c7 -< Location end: 39 ---- -> Location end: 40 diff --git a/test/911-get-stack-trace/check b/test/911-get-stack-trace/check index a46ea9e54a..ee00266b36 100644 --- a/test/911-get-stack-trace/check +++ b/test/911-get-stack-trace/check @@ -14,11 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Jack has a different set of bytecode offsets/method IDs in the expected.txt -if [[ "$USE_JACK" == true ]]; then - patch -p0 expected.txt < expected_jack.diff -fi - if [[ "$DX" == 'd8' ]]; then patch -p0 expected.txt < expected_d8.diff fi diff --git a/test/911-get-stack-trace/expected_jack.diff b/test/911-get-stack-trace/expected_jack.diff deleted file mode 100644 index b7481941c7..0000000000 --- a/test/911-get-stack-trace/expected_jack.diff +++ /dev/null @@ -1,32 +0,0 @@ -24c24 -< doTest ()V 34 25 ---- -> doTest ()V 38 25 -44c44 -< doTest ()V 38 26 ---- -> doTest ()V 42 26 -65c65 -< doTest ()V 60 32 ---- -> doTest ()V 65 32 -363c363 -< doTest ()V 122 59 ---- -> doTest ()V 128 59 -598c598 -< doTest ()V 127 61 ---- -> doTest ()V 133 61 -630c630 -< doTest ()V 112 54 ---- -> doTest ()V 116 54 -677c677 -< doTest ()V 117 56 ---- -> doTest ()V 121 56 -792c792 -< [public static void art.Frames.doTestSameThread(), 35] ---- -> [public static void art.Frames.doTestSameThread(), 38] diff --git a/test/913-heaps/check b/test/913-heaps/check index f4892d0a07..c3b47f56ac 100644 --- a/test/913-heaps/check +++ b/test/913-heaps/check @@ -15,10 +15,7 @@ # limitations under the License. # Jack/D8 has a different set of bytecode offsets/method IDs in the expected.txt - -if [[ "$USE_JACK" == true ]]; then - patch -p0 expected.txt < expected_jack.diff -elif [[ "$USE_D8" == true ]]; then +if [[ "$USE_D8" == true ]]; then patch -p0 expected.txt < expected_d8.diff fi diff --git a/test/913-heaps/expected_jack.diff b/test/913-heaps/expected_jack.diff deleted file mode 100644 index 32fef02234..0000000000 --- a/test/913-heaps/expected_jack.diff +++ /dev/null @@ -1,50 +0,0 @@ -4c4 -< root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1] ---- -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 32])--> 1@1000 [size=16, length=-1] -49,50c49,50 -< root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=11,location= 8])--> 1@1000 [size=16, length=-1] -< root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1] ---- -> root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=13,location= 10])--> 1@1000 [size=16, length=-1] -> root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 10])--> 1@1000 [size=16, length=-1] -102c102 -< root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1] ---- -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 32])--> 1@1000 [size=16, length=-1] -115,116c115,116 -< root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=11,location= 8])--> 1@1000 [size=16, length=-1] -< root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1] ---- -> root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=13,location= 10])--> 1@1000 [size=16, length=-1] -> root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 10])--> 1@1000 [size=16, length=-1] -162c162 -< root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1] ---- -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 32])--> 1@1000 [size=16, length=-1] -177,178c177,178 -< root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=11,location= 8])--> 1@1000 [size=16, length=-1] -< root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1] ---- -> root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=13,location= 10])--> 1@1000 [size=16, length=-1] -> root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 10])--> 1@1000 [size=16, length=-1] -201c201 -< root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1] ---- -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 32])--> 1@1000 [size=16, length=-1] -246,247c246,247 -< root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=11,location= 8])--> 1@1000 [size=16, length=-1] -< root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1] ---- -> root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=13,location= 10])--> 1@1000 [size=16, length=-1] -> root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 10])--> 1@1000 [size=16, length=-1] -347c347 -< root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1] ---- -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 32])--> 1@1000 [size=16, length=-1] -366,367c366,367 -< root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=11,location= 8])--> 1@1000 [size=16, length=-1] -< root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1] ---- -> root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=13,location= 10])--> 1@1000 [size=16, length=-1] -> root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 10])--> 1@1000 [size=16, length=-1] diff --git a/test/968-default-partial-compile-gen/build b/test/968-default-partial-compile-gen/build index 00ccb89faf..c8f0384645 100755 --- a/test/968-default-partial-compile-gen/build +++ b/test/968-default-partial-compile-gen/build @@ -17,8 +17,6 @@ # make us exit on a failure set -e -# TODO: Support running with jack. - if [[ $@ == *"--jvm"* ]]; then # Build the Java files if we are running a --jvm test mkdir -p classes diff --git a/test/970-iface-super-resolution-gen/build b/test/970-iface-super-resolution-gen/build index 7217fac601..0594501c91 100755 --- a/test/970-iface-super-resolution-gen/build +++ b/test/970-iface-super-resolution-gen/build @@ -20,16 +20,6 @@ set -e # Should we compile with Java source code. By default we will use Smali. USES_JAVA_SOURCE="false" if [[ $@ == *"--jvm"* ]]; then - USES_JAVA_SOURCE="true" -elif [[ "$USE_JACK" == "true" ]]; then - if $JACK -D jack.java.source.version=1.8 -D jack.android.min-api-level=24 2>/dev/null; then - USES_JAVA_SOURCE="true" - else - echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2 - fi -fi - -if [[ "$USES_JAVA_SOURCE" == "true" ]]; then # Build the Java files mkdir -p src mkdir -p src2 diff --git a/test/971-iface-super/build b/test/971-iface-super/build index 00ccb89faf..c8f0384645 100755 --- a/test/971-iface-super/build +++ b/test/971-iface-super/build @@ -17,8 +17,6 @@ # make us exit on a failure set -e -# TODO: Support running with jack. - if [[ $@ == *"--jvm"* ]]; then # Build the Java files if we are running a --jvm test mkdir -p classes diff --git a/test/etc/default-build b/test/etc/default-build index e06b367f2b..d0ebe80e68 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -109,7 +109,7 @@ ZIP_COMPRESSION_METHOD="deflate" WITH_ZIP_ALIGN=false ZIP_ALIGN_BYTES="-1" -DX_FLAGS="--min-sdk-version=24" +DX_FLAGS="--min-sdk-version=26" DX_VM_FLAGS="" EXPERIMENTAL="" @@ -120,13 +120,6 @@ DEV_MODE="no" DEFAULT_EXPERIMENT="no-experiment" # Setup experimental flag mappings in a bash associative array. -declare -A JACK_EXPERIMENTAL_ARGS -JACK_EXPERIMENTAL_ARGS["agents"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24" -JACK_EXPERIMENTAL_ARGS["default-methods"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24" -JACK_EXPERIMENTAL_ARGS["lambdas"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24" -JACK_EXPERIMENTAL_ARGS["method-handles"]="-D jack.java.source.version=1.7 -D jack.android.min-api-level=o-b1" -JACK_EXPERIMENTAL_ARGS[${DEFAULT_EXPERIMENT}]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24" - declare -A SMALI_EXPERIMENTAL_ARGS SMALI_EXPERIMENTAL_ARGS["default-methods"]="--api 24" SMALI_EXPERIMENTAL_ARGS["method-handles"]="--api 26" @@ -223,19 +216,8 @@ fi # Be sure to get any default arguments if not doing any experiments. EXPERIMENTAL="${EXPERIMENTAL} ${DEFAULT_EXPERIMENT}" -if [ "${JACK_SERVER}" = "false" ]; then - # Run in single-threaded mode for the continuous buildbot. - JACK_ARGS="${JACK_ARGS} -D sched.runner=single-threaded" -else - # Run with 4 threads to reduce memory footprint and thread contention. - JACK_ARGS="${JACK_ARGS} -D sched.runner=multi-threaded" - JACK_ARGS="${JACK_ARGS} -D sched.runner.thread.kind=fixed" - JACK_ARGS="${JACK_ARGS} -D sched.runner.thread.fixed.count=4" -fi - # Add args from the experimental mappings. for experiment in ${EXPERIMENTAL}; do - JACK_ARGS="${JACK_ARGS} ${JACK_EXPERIMENTAL_ARGS[${experiment}]}" SMALI_ARGS="${SMALI_ARGS} ${SMALI_EXPERIMENTAL_ARGS[${experiment}]}" JAVAC_ARGS="${JAVAC_ARGS} ${JAVAC_EXPERIMENTAL_ARGS[${experiment}]}" DX_FLAGS="${DX_FLAGS} ${DX_EXPERIMENTAL_ARGS[${experiment}]}" @@ -406,79 +388,39 @@ if [ ${HAS_SRC_DEX2OAT_UNRESOLVED} = "true" ]; then mkdir classes-ex javac_with_bootclasspath -implicit:none -sourcepath src-dex2oat-unresolved -d classes `find src -name '*.java'` javac_with_bootclasspath -implicit:none -sourcepath src -d classes-ex `find src-dex2oat-unresolved -name '*.java'` - if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - jar cf classes-ex.jill.jar -C classes-ex . - - ${JACK} --import classes-ex.jill.jar --output-dex . + if [ ${NEED_DEX} = "true" ]; then + make_dex classes-ex + mv classes-ex.dex classes.dex # rename it so it shows up as "classes.dex" in the zip file. zip ${TEST_NAME}-ex.jar classes.dex - ${JACK} --import classes.jill.jar --output-dex . - else - if [ ${NEED_DEX} = "true" ]; then - make_dex classes-ex - mv classes-ex.dex classes.dex # rename it so it shows up as "classes.dex" in the zip file. - zip ${TEST_NAME}-ex.jar classes.dex - make_dex classes - fi + make_dex classes fi else - if [ ${USE_JACK} = "true" ]; then - # Jack toolchain - if [[ "$HAS_SRC" == true || "$HAS_SRC_ART" == true ]]; then - if [ "${HAS_SRC_MULTIDEX}" = "true" ]; then - # Compile src and src-multidex in the same .jack file. We will apply multidex partitioning - # when creating the output .dex file. - ${JACK} ${JACK_ARGS} --output-jack src.jack $(maybe_dir src) src-multidex $(maybe_dir src-art) - jack_extra_args="${jack_extra_args} -D jack.dex.output.policy=minimal-multidex" - jack_extra_args="${jack_extra_args} -D jack.preprocessor=true" - jack_extra_args="${jack_extra_args} -D jack.preprocessor.file=multidex.jpp" - else - ${JACK} ${JACK_ARGS} --output-jack src.jack $(maybe_dir src) $(maybe_dir src-art) - fi - jack_extra_args="${jack_extra_args} --import src.jack" - fi - - if [ "${HAS_SRC2}" = "true" ]; then - ${JACK} ${JACK_ARGS} --output-jack src2.jack src2 - # In case of duplicate classes, we want to take into account the classes from src2. Therefore - # we apply the 'keep-first' policy and import src2.jack file *before* the src.jack file. - jack_extra_args="${jack_extra_args} -D jack.import.type.policy=keep-first" - jack_extra_args="--import src2.jack ${jack_extra_args}" - fi - - # Compile jack files into a DEX file. - if [ "${HAS_SRC}" = "true" ] || [ "${HAS_SRC2}" = "true" ] || [ "${HAS_SRC_ART}" = "true" ]; then - ${JACK} ${JACK_ARGS} ${jack_extra_args} --output-dex . - fi - else - # Legacy toolchain with javac+dx - if [ "${HAS_SRC}" = "true" ]; then - mkdir -p classes - javac_with_bootclasspath -implicit:none -classpath src-multidex -d classes `find src -name '*.java'` - fi + if [ "${HAS_SRC}" = "true" ]; then + mkdir -p classes + javac_with_bootclasspath -implicit:none -classpath src-multidex -d classes `find src -name '*.java'` + fi - if [ "${HAS_SRC_ART}" = "true" ]; then - mkdir -p classes - javac_with_bootclasspath -implicit:none -classpath src-multidex -d classes `find src-art -name '*.java'` - fi + if [ "${HAS_SRC_ART}" = "true" ]; then + mkdir -p classes + javac_with_bootclasspath -implicit:none -classpath src-multidex -d classes `find src-art -name '*.java'` + fi - if [ "${HAS_SRC_MULTIDEX}" = "true" ]; then - mkdir classes2 - javac_with_bootclasspath -implicit:none -classpath src -d classes2 `find src-multidex -name '*.java'` - if [ ${NEED_DEX} = "true" ]; then - make_dex classes2 - fi + if [ "${HAS_SRC_MULTIDEX}" = "true" ]; then + mkdir classes2 + javac_with_bootclasspath -implicit:none -classpath src -d classes2 `find src-multidex -name '*.java'` + if [ ${NEED_DEX} = "true" ]; then + make_dex classes2 fi + fi - if [ "${HAS_SRC2}" = "true" ]; then - mkdir -p classes - javac_with_bootclasspath -classpath classes -d classes `find src2 -name '*.java'` - fi + if [ "${HAS_SRC2}" = "true" ]; then + mkdir -p classes + javac_with_bootclasspath -classpath classes -d classes `find src2 -name '*.java'` + fi - if [[ "${HAS_SRC}" == "true" || "${HAS_SRC2}" == "true" || "${HAS_SRC_ART}" == "true" ]]; then - if [ ${NEED_DEX} = "true" ]; then - make_dex classes - fi + if [[ "${HAS_SRC}" == "true" || "${HAS_SRC2}" == "true" || "${HAS_SRC_ART}" == "true" ]]; then + if [ ${NEED_DEX} = "true" ]; then + make_dex classes fi fi fi @@ -535,30 +477,20 @@ fi if [ ${HAS_SRC_EX} = "true" ]; then - if [ ${USE_JACK} = "true" ]; then - # Rename previous "classes.dex" so it is not overwritten. - mv classes.dex classes-1.dex - #TODO find another way to append src.jack to the jack classpath - ${JACK}:src.jack ${JACK_ARGS} --output-dex . src-ex - zip $TEST_NAME-ex.jar classes.dex - # Restore previous "classes.dex" so it can be zipped. - mv classes-1.dex classes.dex - else - # Build src-ex into classes-ex. - # Includes 'src', 'src-art' source when compiling classes-ex, but exclude their .class files. - if [[ "${HAS_SRC}" == "true" ]]; then - mkdir -p classes-tmp-for-ex - javac_with_bootclasspath -d classes-tmp-for-ex `find src -name '*.java'` - src_tmp_for_ex="-cp classes-tmp-for-ex" - fi - if [[ "${HAS_SRC_ART}" == "true" ]]; then - mkdir -p classes-tmp-for-ex - javac_with_bootclasspath -d classes-tmp-for-ex `find src-art -name '*.java'` - src_tmp_for_ex="-cp classes-tmp-for-ex" - fi - mkdir classes-ex - javac_with_bootclasspath -d classes-ex $src_tmp_for_ex `find src-ex -name '*.java'` + # Build src-ex into classes-ex. + # Includes 'src', 'src-art' source when compiling classes-ex, but exclude their .class files. + if [[ "${HAS_SRC}" == "true" ]]; then + mkdir -p classes-tmp-for-ex + javac_with_bootclasspath -d classes-tmp-for-ex `find src -name '*.java'` + src_tmp_for_ex="-cp classes-tmp-for-ex" fi + if [[ "${HAS_SRC_ART}" == "true" ]]; then + mkdir -p classes-tmp-for-ex + javac_with_bootclasspath -d classes-tmp-for-ex `find src-art -name '*.java'` + src_tmp_for_ex="-cp classes-tmp-for-ex" + fi + mkdir classes-ex + javac_with_bootclasspath -d classes-ex $src_tmp_for_ex `find src-ex -name '*.java'` fi if [[ -d classes-ex ]] && [ ${NEED_DEX} = "true" ]; then diff --git a/test/run-test b/test/run-test index be0a88d1f9..8e012d13fb 100755 --- a/test/run-test +++ b/test/run-test @@ -46,7 +46,6 @@ export RUN="${progdir}/etc/run-test-jar" export DEX_LOCATION=/data/run-test/${test_dir} export NEED_DEX="true" export USE_D8="true" -export USE_JACK="false" export USE_DESUGAR="true" export SMALI_ARGS="" @@ -72,11 +71,6 @@ if [ -z "$SMALI" ]; then export SMALI="smali" fi -# If jack was not set by the environment variable, assume it is in the path. -if [ -z "$JACK" ]; then - export JACK="jack" -fi - # ANDROID_BUILD_TOP is not set in a build environment. if [ -z "$ANDROID_BUILD_TOP" ]; then export ANDROID_BUILD_TOP=$oldwd @@ -87,13 +81,6 @@ if [ -z "$ANDROID_HOST_OUT" ]; then export ANDROID_HOST_OUT=${OUT_DIR:-$ANDROID_BUILD_TOP/out}/host/linux-x86 fi -# If JACK_CLASSPATH is not set, assume it only contains core-libart. -if [ -z "$JACK_CLASSPATH" ]; then - export JACK_CLASSPATH="${ANDROID_HOST_OUT}/../common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack:${ANDROID_HOST_OUT}/../common/obj/JAVA_LIBRARIES/core-oj-hostdex_intermediates/classes.jack" -fi - -export JACK="$JACK -g -cp $JACK_CLASSPATH" - # Allow changing DESUGAR script to something else, or to disable it with DESUGAR=false. if [ -z "$DESUGAR" ]; then export DESUGAR="$ANDROID_BUILD_TOP/art/tools/desugar.sh" @@ -197,7 +184,6 @@ while true; do image_args="" prebuild_mode="no" NEED_DEX="false" - USE_JACK="false" run_args="${run_args} --jvm" shift elif [ "x$1" = "x-O" ]; then @@ -367,12 +353,6 @@ while true; do elif [ "x$1" = "x--build-only" ]; then build_only="yes" shift - elif [ "x$1" = "x--build-with-javac-dx" ]; then - USE_JACK="false" - shift - elif [ "x$1" = "x--build-with-jack" ]; then - USE_JACK="true" - shift elif [ "x$1" = "x--output-path" ]; then shift tmp_dir=$1 @@ -678,7 +658,7 @@ fi # For building with javac and dx always use Java 7. The dx compiler # only support byte codes from Java 7 or earlier (class file major # version 51 or lower). -if [ "$USE_JACK" != "true" ] && [ "$NEED_DEX" = "true" ]; then +if [ "$NEED_DEX" = "true" ]; then export JAVAC="${JAVAC} -source 1.7 -target 1.7" fi @@ -711,9 +691,6 @@ if [ "$usage" = "yes" ]; then echo " --gdb Run under gdb; incompatible with some tests." echo " --gdb-arg Pass an option to gdb." echo " --build-only Build test files only (off by default)." - echo " --build-with-d8 Build test files with javac and d8 (off by default)." - echo " --build-with-javac-dx Build test files with javac and dx (off by default)." - echo " --build-with-jack Build test files with jack and jill (on by default)." echo " --interpreter Enable interpreter only mode (off by default)." echo " --jit Enable jit (off by default)." echo " --optimizing Enable optimizing compiler (default)." diff --git a/test/testrunner/env.py b/test/testrunner/env.py index 0c1c308218..66ed0d0004 100644 --- a/test/testrunner/env.py +++ b/test/testrunner/env.py @@ -68,9 +68,6 @@ def _get_android_build_top(): ANDROID_BUILD_TOP = _get_android_build_top() -# Compiling with jack? Possible values in (True, False, 'default') -ANDROID_COMPILE_WITH_JACK = _get_build_var_boolean('ANDROID_COMPILE_WITH_JACK', 'default') - # Directory used for temporary test files on the host. ART_HOST_TEST_DIR = tempfile.mkdtemp(prefix = 'test-art-') @@ -134,8 +131,8 @@ else: HOST_OUT_EXECUTABLES = os.path.join(ANDROID_BUILD_TOP, _get_build_var("HOST_OUT_EXECUTABLES")) -# Set up default values for $JACK, $DX, $SMALI, etc to the $HOST_OUT_EXECUTABLES/$name path. -for tool in ['jack', 'dx', 'smali', 'jasmin', 'd8']: +# Set up default values for $DX, $SMALI, etc to the $HOST_OUT_EXECUTABLES/$name path. +for tool in ['dx', 'smali', 'jasmin', 'd8']: os.environ.setdefault(tool.upper(), HOST_OUT_EXECUTABLES + '/' + tool) ANDROID_JAVA_TOOLCHAIN = os.path.join(ANDROID_BUILD_TOP, diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py index e0ccc3e14c..316007933e 100755 --- a/test/testrunner/run_build_test_target.py +++ b/test/testrunner/run_build_test_target.py @@ -68,8 +68,6 @@ if 'make' in target: build_command += ' -j' + str(n_threads) build_command += ' -C ' + env.ANDROID_BUILD_TOP build_command += ' ' + target.get('make') - # Add 'dist' to avoid Jack issues b/36169180. - build_command += ' dist' sys.stdout.write(str(build_command) + '\n') sys.stdout.flush() if subprocess.call(build_command.split()): diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 254ffc9db1..09b9b210fc 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -482,12 +482,6 @@ def run_tests(tests): options_test += ' --instruction-set-features ' + \ env.HOST_2ND_ARCH_PREFIX_DEX2OAT_HOST_INSTRUCTION_SET_FEATURES - # Use the default run-test behavior unless ANDROID_COMPILE_WITH_JACK is explicitly set. - if env.ANDROID_COMPILE_WITH_JACK == True: - options_test += ' --build-with-jack' - elif env.ANDROID_COMPILE_WITH_JACK == False: - options_test += ' --build-with-javac-dx' - # TODO(http://36039166): This is a temporary solution to # fix build breakages. options_test = (' --output-path %s') % ( @@ -1010,8 +1004,6 @@ def main(): build_command += ' -j' build_command += ' -C ' + env.ANDROID_BUILD_TOP build_command += ' ' + build_targets - # Add 'dist' to avoid Jack issues b/36169180. - build_command += ' dist' if subprocess.call(build_command.split()): sys.exit(1) if user_requested_tests: -- GitLab From f83f84a20e83f58ef49b3ada35d2f717927d9625 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Thu, 24 May 2018 09:39:54 +0100 Subject: [PATCH 452/749] ART: Remove jack from tools Test: art/tools/buildbot.sh --host -j32 Test: manual Change-Id: I54d37cca8be92ac007f5f83bf47e339fde5b2abc --- test/913-heaps/check | 2 +- tools/build/var_list | 1 - tools/buildbot-build.sh | 6 ------ tools/common/common.py | 8 -------- tools/golem/build-target.sh | 2 -- tools/jfuzz/README.md | 10 ++++++---- tools/jfuzz/run_dex_fuzz_test.py | 14 ++------------ tools/jfuzz/run_jfuzz_test.py | 8 +------- 8 files changed, 10 insertions(+), 41 deletions(-) diff --git a/test/913-heaps/check b/test/913-heaps/check index c3b47f56ac..f7f8dab8cd 100644 --- a/test/913-heaps/check +++ b/test/913-heaps/check @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Jack/D8 has a different set of bytecode offsets/method IDs in the expected.txt +# D8 has a different set of bytecode offsets/method IDs in the expected.txt if [[ "$USE_D8" == true ]]; then patch -p0 expected.txt < expected_d8.diff fi diff --git a/tools/build/var_list b/tools/build/var_list index adcb066f7c..bb005cf77c 100644 --- a/tools/build/var_list +++ b/tools/build/var_list @@ -33,5 +33,4 @@ TARGET_ARCH HOST_PREFER_32_BIT HOST_OUT_EXECUTABLES ANDROID_JAVA_TOOLCHAIN -ANDROID_COMPILE_WITH_JACK diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh index 10eb9360af..830505124e 100755 --- a/tools/buildbot-build.sh +++ b/tools/buildbot-build.sh @@ -32,8 +32,6 @@ else out_dir=${OUT_DIR} fi -using_jack=$(get_build_var ANDROID_COMPILE_WITH_JACK) - java_libraries_dir=${out_dir}/target/common/obj/JAVA_LIBRARIES common_targets="vogar core-tests apache-harmony-jdwp-tests-hostdex jsr166-tests mockito-target" mode="target" @@ -62,10 +60,6 @@ while true; do fi done -if [[ $using_jack == "true" ]]; then - common_targets="$common_targets ${out_dir}/host/linux-x86/bin/jack" -fi - # Allow to build successfully in master-art. extra_args=SOONG_ALLOW_MISSING_DEPENDENCIES=true diff --git a/tools/common/common.py b/tools/common/common.py index 735bbaa4a4..b728e8d927 100755 --- a/tools/common/common.py +++ b/tools/common/common.py @@ -116,14 +116,6 @@ def GetEnvVariableOrError(variable_name): return top -def GetJackClassPath(): - """Returns Jack's classpath.""" - top = GetEnvVariableOrError('ANDROID_BUILD_TOP') - libdir = top + '/out/host/common/obj/JAVA_LIBRARIES' - return libdir + '/core-libart-hostdex_intermediates/classes.jack:' \ - + libdir + '/core-oj-hostdex_intermediates/classes.jack' - - def _DexArchCachePaths(android_data_path): """Returns paths to architecture specific caches. diff --git a/tools/golem/build-target.sh b/tools/golem/build-target.sh index 4ca2722ac9..921a8cbe36 100755 --- a/tools/golem/build-target.sh +++ b/tools/golem/build-target.sh @@ -267,8 +267,6 @@ if [[ $mode == "golem" ]]; then execute 'source' build/envsetup.sh # Build generic targets (as opposed to something specific like aosp_angler-eng). execute lunch "$lunch_target" - setenv JACK_SERVER false - setenv_escape JACK_REPOSITORY "$PWD/prebuilts/sdk/tools/jacks" '$PWD/prebuilts/sdk/tools/jacks' # Golem uses master-art repository which is missing a lot of other libraries. setenv SOONG_ALLOW_MISSING_DEPENDENCIES true # Golem may be missing tools such as javac from its path. diff --git a/tools/jfuzz/README.md b/tools/jfuzz/README.md index eb0e71f53d..f32cf561d6 100644 --- a/tools/jfuzz/README.md +++ b/tools/jfuzz/README.md @@ -37,8 +37,10 @@ The current version of JFuzz sends all output to stdout, and uses a fixed testing class named Test. So a typical test run looks as follows. jfuzz > Test.java - jack -cp ${JACK_CLASSPATH} --output-dex . Test.java - art -classpath classes.dex Test + mkdir classes + javac -d classes Test.java + dx --dex --output=classes.dex classes + art -cp classes.dex Test How to start JFuzz testing ========================== @@ -67,7 +69,7 @@ where --report_script : path to script called for each divergence --jfuzz_arg : argument for jfuzz --true_divergence : don't bisect timeout divergences - --dexer=DEXER : use either dx, d8, or jack to obtain dex files + --dexer=DEXER : use either dx or d8 to obtain dex files --debug_info : include debugging info How to start JFuzz nightly testing @@ -97,7 +99,7 @@ where --num_tests : number of tests to run (10000 by default) --num_inputs : number of JFuzz programs to generate --device : target device serial number (passed to adb -s) - --dexer=DEXER : use either dx, d8, or jack to obtain dex files + --dexer=DEXER : use either dx or d8 to obtain dex files --debug_info : include debugging info Background diff --git a/tools/jfuzz/run_dex_fuzz_test.py b/tools/jfuzz/run_dex_fuzz_test.py index 47fe072b6d..64bf23405b 100755 --- a/tools/jfuzz/run_dex_fuzz_test.py +++ b/tools/jfuzz/run_dex_fuzz_test.py @@ -28,7 +28,6 @@ sys.path.append(os.path.dirname(os.path.dirname( from common.common import FatalError from common.common import GetEnvVariableOrError -from common.common import GetJackClassPath from common.common import RetCode from common.common import RunCommand @@ -106,7 +105,7 @@ class DexFuzzTester(object): self.RunDexFuzz() def CompileOnHost(self): - """Compiles Test.java into classes.dex using either javac/dx,d8 or jack. + """Compiles Test.java into classes.dex using either javac/dx or d8. Raises: FatalError: error when compilation fails @@ -128,15 +127,6 @@ class DexFuzzTester(object): os.unlink(cfile) os.unlink('jerr.txt') os.unlink('dxerr.txt') - - elif self._dexer == 'jack': - jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', 'Test.java'] - if RunCommand(['jack'] + jack_args, out=None, err='jackerr.txt', - timeout=30) != RetCode.SUCCESS: - print('Unexpected error while running Jack') - raise FatalError('Unexpected error while running Jack') - # Cleanup on success (nothing to see). - os.unlink('jackerr.txt') else: raise FatalError('Unknown dexer: ' + self._dexer) @@ -188,7 +178,7 @@ def main(): help='number of JFuzz program to generate (default: 10)') parser.add_argument('--device', help='target device serial number') parser.add_argument('--dexer', default='dx', type=str, - help='defines dexer as dx, d8, or jack (default: dx)') + help='defines dexer as dx or d8 (default: dx)') parser.add_argument('--debug_info', default=False, action='store_true', help='include debugging info') args = parser.parse_args() diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py index 3ff9f450a1..f8bfd8dda7 100755 --- a/tools/jfuzz/run_jfuzz_test.py +++ b/tools/jfuzz/run_jfuzz_test.py @@ -33,7 +33,6 @@ sys.path.append(os.path.dirname(os.path.dirname( from common.common import RetCode from common.common import CommandListToCommandString from common.common import FatalError -from common.common import GetJackClassPath from common.common import GetEnvVariableOrError from common.common import RunCommand from common.common import RunCommandForOutput @@ -127,8 +126,6 @@ class TestRunnerWithHostCompilation(TestRunner): """ self._dexer = dexer self._debug_info = debug_info - self._jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', - 'Test.java'] def CompileOnHost(self): if self._dexer == 'dx' or self._dexer == 'd8': @@ -140,9 +137,6 @@ class TestRunnerWithHostCompilation(TestRunner): out=None, err='dxerr.txt', timeout=30) else: retc = RetCode.NOTCOMPILED - elif self._dexer == 'jack': - retc = RunCommand(['jack'] + self._jack_args, - out=None, err='jackerr.txt', timeout=30) else: raise FatalError('Unknown dexer: ' + self._dexer) return retc @@ -632,7 +626,7 @@ def main(): parser.add_argument('--true_divergence', default=False, action='store_true', help='do not bisect timeout divergences') parser.add_argument('--dexer', default='dx', type=str, - help='defines dexer as dx, d8, or jack (default: dx)') + help='defines dexer as dx or d8 (default: dx)') parser.add_argument('--debug_info', default=False, action='store_true', help='include debugging info') args = parser.parse_args() -- GitLab From b0633b2d69b5bae42c4cf97696aa227e2a8750ef Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Thu, 24 May 2018 15:59:18 +0100 Subject: [PATCH 453/749] Have testrunner honor ART_TEST_CHROOT only for target tests. Chroot-based testing is only supported on devices, and art/test/run-test would complain if `--host` and `--chroot` were to be used together. Also have the same behavior for ART_TEST_ANDROID_ROOT, as it only makes sense for target tests. Test: ART_TEST_CHROOT=/data/local/art-test-chroot art/test/testrunner/testrunner.py --host Bug: 34729697 Change-Id: I617fbcf719d3c59dbcbd632e69bf52e3498b4cff --- test/testrunner/testrunner.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 09b9b210fc..540acd8b4e 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -320,12 +320,6 @@ def run_tests(tests): if env.ART_TEST_BISECTION: options_all += ' --bisection-search' - if env.ART_TEST_CHROOT: - options_all += ' --chroot ' + env.ART_TEST_CHROOT - - if env.ART_TEST_ANDROID_ROOT: - options_all += ' --android-root ' + env.ART_TEST_ANDROID_ROOT - if gdb: options_all += ' --gdb' if gdb_arg: @@ -401,6 +395,13 @@ def run_tests(tests): elif target == 'jvm': options_test += ' --jvm' + # Honor ART_TEST_CHROOT and ART_TEST_ANDROID_ROOT, but only for target tests. + if target == 'target': + if env.ART_TEST_CHROOT: + options_test += ' --chroot ' + env.ART_TEST_CHROOT + if env.ART_TEST_ANDROID_ROOT: + options_test += ' --android-root ' + env.ART_TEST_ANDROID_ROOT + if run == 'ndebug': options_test += ' -O' -- GitLab From ca210e34eac5b262a85c05e8815ab266f7f983d2 Mon Sep 17 00:00:00 2001 From: Artem Serov Date: Fri, 15 Dec 2017 13:43:20 +0000 Subject: [PATCH 454/749] ART: SBC: Support single exit loops with live_outs. Support copying subgraphs with a single exit and live-outs - values which are defined inside the subgraph and have uses outside. Test: test-art-target, test-art-host. Test: 530-checker-peel-unroll. Change-Id: Id06a6f08d14c6e86f6437d662e24489f955e9edf --- compiler/optimizing/nodes.cc | 17 ++ compiler/optimizing/nodes.h | 5 + compiler/optimizing/superblock_cloner.cc | 154 ++++++++++++-- compiler/optimizing/superblock_cloner.h | 31 ++- test/530-checker-peel-unroll/src/Main.java | 235 ++++++++++++++++++++- 5 files changed, 418 insertions(+), 24 deletions(-) diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 7f78dc257e..55496602d2 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1121,6 +1121,23 @@ void HEnvironment::RemoveAsUserOfInput(size_t index) const { user->FixUpUserRecordsAfterEnvUseRemoval(before_env_use_node); } +void HEnvironment::ReplaceInput(HInstruction* replacement, size_t index) { + const HUserRecord& env_use_record = vregs_[index]; + HInstruction* orig_instr = env_use_record.GetInstruction(); + + DCHECK(orig_instr != replacement); + + HUseList::iterator before_use_node = env_use_record.GetBeforeUseNode(); + // Note: fixup_end remains valid across splice_after(). + auto fixup_end = replacement->env_uses_.empty() ? replacement->env_uses_.begin() + : ++replacement->env_uses_.begin(); + replacement->env_uses_.splice_after(replacement->env_uses_.before_begin(), + env_use_record.GetInstruction()->env_uses_, + before_use_node); + replacement->FixUpUserRecordsAfterEnvUseInsertion(fixup_end); + orig_instr->FixUpUserRecordsAfterEnvUseRemoval(before_use_node); +} + HInstruction* HInstruction::GetNextDisregardingMoves() const { HInstruction* next = GetNext(); while (next != nullptr && next->IsParallelMove()) { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 09d9c57a33..3fd5b6b02d 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1909,6 +1909,11 @@ class HEnvironment : public ArenaObject { void RemoveAsUserOfInput(size_t index) const; + // Replaces the input at the position 'index' with the replacement; the replacement and old + // input instructions' env_uses_ lists are adjusted. The function works similar to + // HInstruction::ReplaceInput. + void ReplaceInput(HInstruction* replacement, size_t index); + size_t Size() const { return vregs_.size(); } HEnvironment* GetParent() const { return parent_; } diff --git a/compiler/optimizing/superblock_cloner.cc b/compiler/optimizing/superblock_cloner.cc index fad7729956..1b43618538 100644 --- a/compiler/optimizing/superblock_cloner.cc +++ b/compiler/optimizing/superblock_cloner.cc @@ -409,7 +409,7 @@ void SuperblockCloner::ResolvePhi(HPhi* phi) { // Main algorithm methods. // -void SuperblockCloner::SearchForSubgraphExits(ArenaVector* exits) { +void SuperblockCloner::SearchForSubgraphExits(ArenaVector* exits) const { DCHECK(exits->empty()); for (uint32_t block_id : orig_bb_set_.Indexes()) { HBasicBlock* block = GetBlockById(block_id); @@ -520,6 +520,113 @@ void SuperblockCloner::ResolveDataFlow() { } } +// +// Helpers for live-outs processing and Subgraph-closed SSA. +// + +bool SuperblockCloner::CollectLiveOutsAndCheckClonable(HInstructionMap* live_outs) const { + DCHECK(live_outs->empty()); + for (uint32_t idx : orig_bb_set_.Indexes()) { + HBasicBlock* block = GetBlockById(idx); + + for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { + HInstruction* instr = it.Current(); + DCHECK(instr->IsClonable()); + + if (IsUsedOutsideRegion(instr, orig_bb_set_)) { + live_outs->FindOrAdd(instr, instr); + } + } + + for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { + HInstruction* instr = it.Current(); + if (!instr->IsClonable()) { + return false; + } + + if (IsUsedOutsideRegion(instr, orig_bb_set_)) { + // TODO: Investigate why HNewInstance, HCheckCast has a requirement for the input. + if (instr->IsLoadClass()) { + return false; + } + live_outs->FindOrAdd(instr, instr); + } + } + } + return true; +} + +void SuperblockCloner::ConstructSubgraphClosedSSA() { + if (live_outs_.empty()) { + return; + } + + ArenaVector exits(arena_->Adapter(kArenaAllocSuperblockCloner)); + SearchForSubgraphExits(&exits); + if (exits.empty()) { + DCHECK(live_outs_.empty()); + return; + } + + DCHECK_EQ(exits.size(), 1u); + HBasicBlock* exit_block = exits[0]; + // There should be no critical edges. + DCHECK_EQ(exit_block->GetPredecessors().size(), 1u); + DCHECK(exit_block->GetPhis().IsEmpty()); + + // For each live-out value insert a phi into the loop exit and replace all the value's uses + // external to the loop with this phi. The phi will have the original value as its only input; + // after copying is done FixSubgraphClosedSSAAfterCloning will add a corresponding copy of the + // original value as the second input thus merging data flow from the original and copy parts of + // the subgraph. Also update the record in the live_outs_ map from (value, value) to + // (value, new_phi). + for (auto live_out_it = live_outs_.begin(); live_out_it != live_outs_.end(); ++live_out_it) { + HInstruction* value = live_out_it->first; + HPhi* phi = new (arena_) HPhi(arena_, kNoRegNumber, 0, value->GetType()); + + if (value->GetType() == DataType::Type::kReference) { + phi->SetReferenceTypeInfo(value->GetReferenceTypeInfo()); + } + + exit_block->AddPhi(phi); + live_out_it->second = phi; + + const HUseList& uses = value->GetUses(); + for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) { + HInstruction* user = it->GetUser(); + size_t index = it->GetIndex(); + // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput(). + ++it; + if (!IsInOrigBBSet(user->GetBlock())) { + user->ReplaceInput(phi, index); + } + } + + const HUseList& env_uses = value->GetEnvUses(); + for (auto it = env_uses.begin(), e = env_uses.end(); it != e; /* ++it below */) { + HEnvironment* env = it->GetUser(); + size_t index = it->GetIndex(); + ++it; + if (!IsInOrigBBSet(env->GetHolder()->GetBlock())) { + env->ReplaceInput(phi, index); + } + } + + phi->AddInput(value); + } +} + +void SuperblockCloner::FixSubgraphClosedSSAAfterCloning() { + for (auto it : live_outs_) { + DCHECK(it.first != it.second); + HInstruction* orig_value = it.first; + HPhi* phi = it.second->AsPhi(); + HInstruction* copy_value = GetInstrCopy(orig_value); + // Copy edges are inserted after the original so we can just add new input to the phi. + phi->AddInput(copy_value); + } +} + // // Debug and logging methods. // @@ -644,7 +751,6 @@ void DumpBBSet(const ArenaBitVector* set) { } void SuperblockCloner::DumpInputSets() { - std::cout << graph_->PrettyMethod() << "\n"; std::cout << "orig_bb_set:\n"; for (uint32_t idx : orig_bb_set_.Indexes()) { std::cout << idx << "\n"; @@ -680,7 +786,9 @@ SuperblockCloner::SuperblockCloner(HGraph* graph, bb_map_(bb_map), hir_map_(hir_map), outer_loop_(nullptr), - outer_loop_bb_set_(arena_, orig_bb_set->GetSizeOf(), true, kArenaAllocSuperblockCloner) { + outer_loop_bb_set_(arena_, orig_bb_set->GetSizeOf(), true, kArenaAllocSuperblockCloner), + live_outs_(std::less(), + graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)) { orig_bb_set_.Copy(orig_bb_set); } @@ -699,26 +807,19 @@ bool SuperblockCloner::IsSubgraphClonable() const { return false; } - // Check that there are no instructions defined in the subgraph and used outside. - // TODO: Improve this by accepting graph with such uses but only one exit. - for (uint32_t idx : orig_bb_set_.Indexes()) { - HBasicBlock* block = GetBlockById(idx); + HInstructionMap live_outs( + std::less(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); - for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { - HInstruction* instr = it.Current(); - if (!instr->IsClonable() || - IsUsedOutsideRegion(instr, orig_bb_set_)) { - return false; - } - } + if (!CollectLiveOutsAndCheckClonable(&live_outs)) { + return false; + } - for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { - HInstruction* instr = it.Current(); - if (!instr->IsClonable() || - IsUsedOutsideRegion(instr, orig_bb_set_)) { - return false; - } - } + ArenaVector exits(arena_->Adapter(kArenaAllocSuperblockCloner)); + SearchForSubgraphExits(&exits); + + // The only loops with live-outs which are currently supported are loops with a single exit. + if (!live_outs.empty() && exits.size() != 1) { + return false; } return true; @@ -794,8 +895,10 @@ void SuperblockCloner::Run() { DumpInputSets(); } + CollectLiveOutsAndCheckClonable(&live_outs_); // Find an area in the graph for which control flow information should be adjusted. FindAndSetLocalAreaForAdjustments(); + ConstructSubgraphClosedSSA(); // Clone the basic blocks from the orig_bb_set_; data flow is invalid after the call and is to be // adjusted. CloneBasicBlocks(); @@ -819,6 +922,7 @@ void SuperblockCloner::Run() { AdjustControlFlowInfo(); // Fix data flow of the graph. ResolveDataFlow(); + FixSubgraphClosedSSAAfterCloning(); } void SuperblockCloner::CleanUp() { @@ -985,8 +1089,14 @@ HBasicBlock* PeelUnrollHelper::DoPeelUnrollImpl(bool to_unroll) { HBasicBlock* loop_header = loop_info_->GetHeader(); // Check that loop info is up-to-date. DCHECK(loop_info_ == loop_header->GetLoopInformation()); - HGraph* graph = loop_header->GetGraph(); + + if (kSuperblockClonerLogging) { + std::cout << "Method: " << graph->PrettyMethod() << std::endl; + std::cout << "Scalar loop " << (to_unroll ? "unrolling" : "peeling") << + " was applied to the loop <" << loop_header->GetBlockId() << ">." << std::endl; + } + ArenaAllocator allocator(graph->GetAllocator()->GetArenaPool()); HEdgeSet remap_orig_internal(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); diff --git a/compiler/optimizing/superblock_cloner.h b/compiler/optimizing/superblock_cloner.h index e0931674cb..f21172131b 100644 --- a/compiler/optimizing/superblock_cloner.h +++ b/compiler/optimizing/superblock_cloner.h @@ -218,7 +218,7 @@ class SuperblockCloner : public ValueObject { private: // Fills the 'exits' vector with the subgraph exits. - void SearchForSubgraphExits(ArenaVector* exits); + void SearchForSubgraphExits(ArenaVector* exits) const; // Finds and records information about the area in the graph for which control flow (back edges, // loops, dominators) needs to be adjusted. @@ -239,6 +239,33 @@ class SuperblockCloner : public ValueObject { // the SSA form. void ResolveDataFlow(); + // + // Helpers for live-outs processing and Subgraph-closed SSA. + // + // - live-outs - values which are defined inside the subgraph and have uses outside. + // - Subgraph-closed SSA - SSA form for which all the values defined inside the subgraph + // have no outside uses except for the phi-nodes in the subgraph exits. + // + // Note: now if the subgraph has live-outs it is only clonable if it has a single exit; this + // makes the subgraph-closed SSA form construction much easier. + // + // TODO: Support subgraphs with live-outs and multiple exits. + // + + // For each live-out value 'val' in the region puts a record into the map. + // Returns whether all of the instructions in the subgraph are clonable. + bool CollectLiveOutsAndCheckClonable(HInstructionMap* live_outs_) const; + + // Constructs Subgraph-closed SSA; precondition - a subgraph has a single exit. + // + // For each live-out 'val' in 'live_outs_' map inserts a HPhi 'phi' into the exit node, updates + // the record in the map to and replaces all outside uses with this phi. + void ConstructSubgraphClosedSSA(); + + // Fixes the data flow for the live-out 'val' by adding a 'copy_val' input to the corresponding + // () phi after the cloning is done. + void FixSubgraphClosedSSAAfterCloning(); + // // Helpers for CloneBasicBlock. // @@ -316,6 +343,8 @@ class SuperblockCloner : public ValueObject { HLoopInformation* outer_loop_; HBasicBlockSet outer_loop_bb_set_; + HInstructionMap live_outs_; + ART_FRIEND_TEST(SuperblockClonerTest, AdjustControlFlowInfo); ART_FRIEND_TEST(SuperblockClonerTest, IsGraphConnected); diff --git a/test/530-checker-peel-unroll/src/Main.java b/test/530-checker-peel-unroll/src/Main.java index 2051b47afe..804c9fe916 100644 --- a/test/530-checker-peel-unroll/src/Main.java +++ b/test/530-checker-peel-unroll/src/Main.java @@ -455,6 +455,235 @@ public class Main { } } + /// CHECK-START: int Main.unrollingSimpleLiveOuts(int[]) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Mul [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Add [<>,<>] loop:none + /// CHECK-DAG: <> DivZeroCheck [<>] env:[[<>,<>,<>,<>,_,<>]] loop:none + /// CHECK-DAG: <> Div [<>,<>] loop:none + /// CHECK-DAG: Return [<
>] loop:none + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + + /// CHECK-START: int Main.unrollingSimpleLiveOuts(int[]) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Mul [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Mul [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Phi [<>,<>] loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:none + /// CHECK-DAG: <> DivZeroCheck [<>] env:[[<>,<>,<>,<>,_,<>]] loop:none + /// CHECK-DAG: <> Div [<>,<>] loop:none + /// CHECK-DAG: Return [<
>] loop:none + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static final int unrollingSimpleLiveOuts(int[] a) { + int s = 1; + int t = 2; + for (int i = 0; i < LENGTH - 2; i++) { + int temp = a[i + 1]; + s += temp; + t *= temp; + a[i] += s; + } + + return 1 / (s + t); + } + + /// CHECK-START: int Main.unrollingWhileLiveOuts(int[]) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> IntConstant 128 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Rem [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet loop:<> outer_loop:none + /// CHECK-DAG: Phi [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + + /// CHECK-START: int Main.unrollingWhileLiveOuts(int[]) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> IntConstant 128 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Rem [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Rem [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: Phi [<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Phi [<>,<>] loop:none + /// CHECK-DAG: Return [<>] loop:none + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static final int unrollingWhileLiveOuts(int[] a) { + int i = 0; + int s = 128; + while (i++ < LENGTH - 2) { + if (i % 2 == 0) { + a[i] = s++; + } + } + return s; + } + + /// CHECK-START: int Main.unrollingLiveOutsNested(int[]) loop_optimization (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + // + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Mul [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:<> + // + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + + /// CHECK-START: int Main.unrollingLiveOutsNested(int[]) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> IntConstant 4094 loop:none + // + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:<> + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Mul [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:<> + // + /// CHECK-DAG: If [<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Mul [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:<> + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:<> + // + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-DAG: <> Add [<>,<>] loop:none + /// CHECK-DAG: Return [<>] loop:none + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static final int unrollingLiveOutsNested(int[] a) { + int s = 1; + int t = 2; + for (int j = 0; j < 16; j++) { + for (int i = 0; i < LENGTH - 2; i++) { + int temp = a[i + 1]; + s += temp; + t *= temp; + a[i] += s; + } + } + return s + t; + } + /// CHECK-START: void Main.noUnrollingOddTripCount(int[]) loop_optimization (before) /// CHECK-DAG: <> ParameterValue loop:none /// CHECK-DAG: <> IntConstant 1 loop:none @@ -802,7 +1031,11 @@ public class Main { peelingBreakFromNest(a, false); peelingBreakFromNest(a, true); - int expected = 141312; + unrollingSimpleLiveOuts(a); + unrollingWhileLiveOuts(a); + unrollingLiveOutsNested(a); + + int expected = 51565978; int found = 0; for (int i = 0; i < a.length; i++) { found += a[i]; -- GitLab From c8c8d5f484b9e4660de288aa51ce3f831317ee53 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 22 May 2018 11:56:14 -0700 Subject: [PATCH 455/749] Move compiler/ to ClassAccessor Remove usages of ClassDataItemIterator to reduces boiler plate code. Bug: 79758018 Bug: 77709234 Test: test-art-host Change-Id: Id41db3299bba3ea8debcbb0b9c721fa675adc064 --- compiler/dex/dex_to_dex_compiler.cc | 16 +- compiler/driver/compiler_driver.cc | 316 ++++++++------------- compiler/verifier_deps_test.cc | 63 ++-- libdexfile/dex/class_accessor-inl.h | 30 +- libdexfile/dex/class_accessor.h | 95 +++++-- libdexfile/dex/dex_instruction_iterator.h | 2 +- tools/dexanalyze/dexanalyze_experiments.cc | 6 +- 7 files changed, 244 insertions(+), 284 deletions(-) diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index be8641fd86..68155d844a 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -26,6 +26,7 @@ #include "base/mutex.h" #include "compiled_method.h" #include "dex/bytecode_utils.h" +#include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_instruction-inl.h" #include "dex_to_dex_decompiler.h" @@ -633,21 +634,14 @@ void DexToDexCompiler::SetDexFiles(const std::vector& dex_files) // item. std::unordered_set seen_code_items; for (const DexFile* dex_file : dex_files) { - for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - continue; - } - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - for (; it.HasNextMethod(); it.Next()) { - const DexFile::CodeItem* code_item = it.GetMethodCodeItem(); + for (ClassAccessor accessor : dex_file->GetClasses()) { + accessor.VisitMethods([&](const ClassAccessor::Method& method) { + const DexFile::CodeItem* code_item = method.GetCodeItem(); // Detect the shared code items. if (!seen_code_items.insert(code_item).second) { shared_code_items_.insert(code_item); } - } + }); } } VLOG(compiler) << "Shared code items " << shared_code_items_.size(); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 7dc44fa44b..1b809d232a 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -42,6 +42,7 @@ #include "compiler.h" #include "compiler_callbacks.h" #include "compiler_driver-inl.h" +#include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_annotations.h" @@ -771,39 +772,6 @@ void CompilerDriver::Resolve(jobject class_loader, } } -// Resolve const-strings in the code. Done to have deterministic allocation behavior. Right now -// this is single-threaded for simplicity. -// TODO: Collect the relevant string indices in parallel, then allocate them sequentially in a -// stable order. - -static void ResolveConstStrings(ClassLinker* class_linker, - Handle dex_cache, - const DexFile& dex_file, - const DexFile::CodeItem* code_item) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (code_item == nullptr) { - // Abstract or native method. - return; - } - - for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) { - switch (inst->Opcode()) { - case Instruction::CONST_STRING: - case Instruction::CONST_STRING_JUMBO: { - dex::StringIndex string_index((inst->Opcode() == Instruction::CONST_STRING) - ? inst->VRegB_21c() - : inst->VRegB_31c()); - ObjPtr string = class_linker->ResolveString(string_index, dex_cache); - CHECK(string != nullptr) << "Could not allocate a string when forcing determinism"; - break; - } - - default: - break; - } - } -} - static void ResolveConstStrings(CompilerDriver* driver, const std::vector& dex_files, TimingLogger* timings) { @@ -816,33 +784,35 @@ static void ResolveConstStrings(CompilerDriver* driver, dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file)); TimingLogger::ScopedTiming t("Resolve const-string Strings", timings); - size_t class_def_count = dex_file->NumClassDefs(); - for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - // empty class, probably a marker interface - continue; - } - - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - - bool compilation_enabled = driver->IsClassToCompile( - dex_file->StringByTypeIdx(class_def.class_idx_)); - if (!compilation_enabled) { + for (ClassAccessor accessor : dex_file->GetClasses()) { + if (!driver->IsClassToCompile(accessor.GetDescriptor())) { // Compilation is skipped, do not resolve const-string in code of this class. // FIXME: Make sure that inlining honors this. b/26687569 continue; } + accessor.VisitMethods([&](const ClassAccessor::Method& method) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Resolve const-strings in the code. Done to have deterministic allocation behavior. Right + // now this is single-threaded for simplicity. + // TODO: Collect the relevant string indices in parallel, then allocate them sequentially + // in a stable order. + for (const DexInstructionPcPair& inst : method.GetInstructions()) { + switch (inst->Opcode()) { + case Instruction::CONST_STRING: + case Instruction::CONST_STRING_JUMBO: { + dex::StringIndex string_index((inst->Opcode() == Instruction::CONST_STRING) + ? inst->VRegB_21c() + : inst->VRegB_31c()); + ObjPtr string = class_linker->ResolveString(string_index, dex_cache); + CHECK(string != nullptr) << "Could not allocate a string when forcing determinism"; + break; + } - // Direct and virtual methods. - while (it.HasNextMethod()) { - ResolveConstStrings(class_linker, dex_cache, *dex_file, it.GetMethodCodeItem()); - it.Next(); - } - DCHECK(!it.HasNext()); + default: + break; + } + } + }); } } } @@ -856,14 +826,9 @@ static void InitializeTypeCheckBitstrings(CompilerDriver* driver, ClassLinker* class_linker, Handle dex_cache, const DexFile& dex_file, - const DexFile::CodeItem* code_item) + const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) { - if (code_item == nullptr) { - // Abstract or native method. - return; - } - - for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) { + for (const DexInstructionPcPair& inst : method.GetInstructions()) { switch (inst->Opcode()) { case Instruction::CHECK_CAST: case Instruction::INSTANCE_OF: { @@ -907,34 +872,18 @@ static void InitializeTypeCheckBitstrings(CompilerDriver* driver, dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file)); TimingLogger::ScopedTiming t("Initialize type check bitstrings", timings); - size_t class_def_count = dex_file->NumClassDefs(); - for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - // empty class, probably a marker interface - continue; - } - - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - - bool compilation_enabled = driver->IsClassToCompile( - dex_file->StringByTypeIdx(class_def.class_idx_)); - if (!compilation_enabled) { + for (ClassAccessor accessor : dex_file->GetClasses()) { + if (!driver->IsClassToCompile(accessor.GetDescriptor())) { // Compilation is skipped, do not look for type checks in code of this class. // FIXME: Make sure that inlining honors this. b/26687569 continue; } // Direct and virtual methods. - while (it.HasNextMethod()) { - InitializeTypeCheckBitstrings( - driver, class_linker, dex_cache, *dex_file, it.GetMethodCodeItem()); - it.Next(); - } - DCHECK(!it.HasNext()); + accessor.VisitMethods([&](const ClassAccessor::Method& method) + REQUIRES_SHARED(Locks::mutator_lock_) { + InitializeTypeCheckBitstrings(driver, class_linker, dex_cache, *dex_file, method); + }); } } } @@ -954,10 +903,8 @@ static void EnsureVerifiedOrVerifyAtRuntime(jobject jclass_loader, ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); for (const DexFile* dex_file : dex_files) { - for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); - const char* descriptor = dex_file->GetClassDescriptor(class_def); - cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader)); + for (ClassAccessor accessor : dex_file->GetClasses()) { + cls.Assign(class_linker->FindClass(soa.Self(), accessor.GetDescriptor(), class_loader)); if (cls == nullptr) { soa.Self()->ClearException(); } else if (&cls->GetDexFile() == dex_file) { @@ -1740,22 +1687,16 @@ static void CheckAndClearResolveException(Thread* self) bool CompilerDriver::RequiresConstructorBarrier(const DexFile& dex_file, uint16_t class_def_idx) const { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx); - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { - // Empty class such as a marker interface. - return false; - } - ClassDataItemIterator it(dex_file, class_data); - it.SkipStaticFields(); + ClassAccessor accessor(dex_file, dex_file.GetClassDef(class_def_idx)); + bool has_is_final = false; // We require a constructor barrier if there are final instance fields. - while (it.HasNextInstanceField()) { - if (it.MemberIsFinal()) { - return true; + accessor.VisitFields(/*static*/ VoidFunctor(), + [&](const ClassAccessor::Field& field) { + if (field.IsFinal()) { + has_is_final = true; } - it.Next(); - } - return false; + }); + return has_is_final; } class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { @@ -1770,11 +1711,6 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { const DexFile& dex_file = *manager_->GetDexFile(); ClassLinker* class_linker = manager_->GetClassLinker(); - // If an instance field is final then we need to have a barrier on the return, static final - // fields are assigned within the lock held for class initialization. Conservatively assume - // constructor barriers are always required. - bool requires_constructor_barrier = true; - // Method and Field are the worst. We can't resolve without either // context from the code use (to disambiguate virtual vs direct // method and instance vs static field) or from class @@ -1806,56 +1742,53 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { // We want to resolve the methods and fields eagerly. resolve_fields_and_methods = true; } - // Note the class_data pointer advances through the headers, - // static fields, instance fields, direct methods, and virtual - // methods. - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { - // Empty class such as a marker interface. - requires_constructor_barrier = false; - } else { - ClassDataItemIterator it(dex_file, class_data); - while (it.HasNextStaticField()) { - if (resolve_fields_and_methods) { - ArtField* field = class_linker->ResolveField( - it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ true); - if (field == nullptr) { - CheckAndClearResolveException(soa.Self()); - } + // If an instance field is final then we need to have a barrier on the return, static final + // fields are assigned within the lock held for class initialization. + bool requires_constructor_barrier = false; + + ClassAccessor accessor(dex_file, class_def); + // Optionally resolve fields and methods and figure out if we need a constructor barrier. + auto method_visitor = [&](const ClassAccessor::Method& method) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (resolve_fields_and_methods) { + ArtMethod* resolved = class_linker->ResolveMethod( + method.GetIndex(), + dex_cache, + class_loader, + /* referrer */ nullptr, + method.GetInvokeType(class_def.access_flags_)); + if (resolved == nullptr) { + CheckAndClearResolveException(soa.Self()); } - it.Next(); } - // We require a constructor barrier if there are final instance fields. - requires_constructor_barrier = false; - while (it.HasNextInstanceField()) { - if (it.MemberIsFinal()) { - requires_constructor_barrier = true; - } - if (resolve_fields_and_methods) { - ArtField* field = class_linker->ResolveField( - it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ false); - if (field == nullptr) { - CheckAndClearResolveException(soa.Self()); + }; + accessor.VisitFieldsAndMethods( + // static fields + [&](ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { + if (resolve_fields_and_methods) { + ArtField* resolved = class_linker->ResolveField( + field.GetIndex(), dex_cache, class_loader, /* is_static */ true); + if (resolved == nullptr) { + CheckAndClearResolveException(soa.Self()); + } } - } - it.Next(); - } - if (resolve_fields_and_methods) { - while (it.HasNextMethod()) { - ArtMethod* method = class_linker->ResolveMethod( - it.GetMemberIndex(), - dex_cache, - class_loader, - /* referrer */ nullptr, - it.GetMethodInvokeType(class_def)); - if (method == nullptr) { - CheckAndClearResolveException(soa.Self()); + }, + // instance fields + [&](ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { + if (field.IsFinal()) { + // We require a constructor barrier if there are final instance fields. + requires_constructor_barrier = true; } - it.Next(); - } - DCHECK(!it.HasNext()); - } - } + if (resolve_fields_and_methods) { + ArtField* resolved = class_linker->ResolveField( + field.GetIndex(), dex_cache, class_loader, /* is_static */ false); + if (resolved == nullptr) { + CheckAndClearResolveException(soa.Self()); + } + } + }, + /*direct methods*/ method_visitor, + /*virtual methods*/ method_visitor); manager_->GetCompiler()->SetRequiresConstructorBarrier(self, &dex_file, class_def_index, @@ -1942,32 +1875,13 @@ void CompilerDriver::SetVerified(jobject class_loader, } } -static void PopulateVerifiedMethods(const DexFile& dex_file, - uint32_t class_def_index, - VerificationResults* verification_results) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { - return; - } - ClassDataItemIterator it(dex_file, class_data); - it.SkipAllFields(); - - while (it.HasNextMethod()) { - verification_results->CreateVerifiedMethodFor(MethodReference(&dex_file, it.GetMemberIndex())); - it.Next(); - } - DCHECK(!it.HasNext()); -} - -static void LoadAndUpdateStatus(const DexFile& dex_file, - const DexFile::ClassDef& class_def, +static void LoadAndUpdateStatus(const ClassAccessor& accessor, ClassStatus status, Handle class_loader, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<1> hs(self); - const char* descriptor = dex_file.GetClassDescriptor(class_def); + const char* descriptor = accessor.GetDescriptor(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle cls(hs.NewHandle( class_linker->FindClass(self, descriptor, class_loader))); @@ -1975,7 +1889,7 @@ static void LoadAndUpdateStatus(const DexFile& dex_file, // Check that the class is resolved with the current dex file. We might get // a boot image class, or a class in a different dex file for multidex, and // we should not update the status in that case. - if (&cls->GetDexFile() == &dex_file) { + if (&cls->GetDexFile() == &accessor.GetDexFile()) { ObjectLock lock(self, cls); mirror::Class::SetStatus(cls, status, self); } @@ -2014,13 +1928,13 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, // Fetch the list of unverified classes. const std::set& unverified_classes = verifier_deps->GetUnverifiedClasses(*dex_file); - for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); - if (unverified_classes.find(class_def.class_idx_) == unverified_classes.end()) { + uint32_t class_def_idx = 0u; + for (ClassAccessor accessor : dex_file->GetClasses()) { + if (unverified_classes.find(accessor.GetClassIdx()) == unverified_classes.end()) { if (compiler_only_verifies) { // Just update the compiled_classes_ map. The compiler doesn't need to resolve // the type. - ClassReference ref(dex_file, i); + ClassReference ref(dex_file, class_def_idx); ClassStatus existing = ClassStatus::kNotReady; DCHECK(compiled_classes_.Get(ref, &existing)) << ref.dex_file->GetLocation(); ClassStateTable::InsertResult result = @@ -2029,26 +1943,27 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, } else { // Update the class status, so later compilation stages know they don't need to verify // the class. - LoadAndUpdateStatus( - *dex_file, class_def, ClassStatus::kVerified, class_loader, soa.Self()); + LoadAndUpdateStatus(accessor, ClassStatus::kVerified, class_loader, soa.Self()); // Create `VerifiedMethod`s for each methods, the compiler expects one for // quickening or compiling. // Note that this means: // - We're only going to compile methods that did verify. // - Quickening will not do checkcast ellision. // TODO(ngeoffray): Reconsider this once we refactor compiler filters. - PopulateVerifiedMethods(*dex_file, i, verification_results_); + accessor.VisitMethods([&](const ClassAccessor::Method& method) { + verification_results_->CreateVerifiedMethodFor(method.GetReference()); + }); } } else if (!compiler_only_verifies) { // Make sure later compilation stages know they should not try to verify // this class again. - LoadAndUpdateStatus(*dex_file, - class_def, + LoadAndUpdateStatus(accessor, ClassStatus::kRetryVerificationAtRuntime, class_loader, soa.Self()); } } + ++class_def_idx; } return true; } @@ -2784,22 +2699,22 @@ static void CompileDexFile(CompilerDriver* driver, auto compile = [&context, &compile_fn](size_t class_def_index) { ScopedTrace trace(__FUNCTION__); const DexFile& dex_file = *context.GetDexFile(); - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); ClassLinker* class_linker = context.GetClassLinker(); jobject jclass_loader = context.GetClassLoader(); ClassReference ref(&dex_file, class_def_index); + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); + ClassAccessor accessor(dex_file, class_def); // Skip compiling classes with generic verifier failures since they will still fail at runtime if (context.GetCompiler()->GetVerificationResults()->IsClassRejected(ref)) { return; } // Use a scoped object access to perform to the quick SkipClass check. - const char* descriptor = dex_file.GetClassDescriptor(class_def); ScopedObjectAccess soa(Thread::Current()); StackHandleScope<3> hs(soa.Self()); Handle class_loader( hs.NewHandle(soa.Decode(jclass_loader))); Handle klass( - hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader))); + hs.NewHandle(class_linker->FindClass(soa.Self(), accessor.GetDescriptor(), class_loader))); Handle dex_cache; if (klass == nullptr) { soa.Self()->AssertPendingException(); @@ -2814,9 +2729,8 @@ static void CompileDexFile(CompilerDriver* driver, dex_cache = hs.NewHandle(klass->GetDexCache()); } - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { - // empty class, probably a marker interface + // Avoid suspension if there are no methods to compile. + if (accessor.NumDirectMethods() + accessor.NumVirtualMethods() == 0) { return; } @@ -2829,28 +2743,24 @@ static void CompileDexFile(CompilerDriver* driver, optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level = GetDexToDexCompilationLevel(soa.Self(), *driver, jclass_loader, dex_file, class_def); - ClassDataItemIterator it(dex_file, class_data); - it.SkipAllFields(); - bool compilation_enabled = driver->IsClassToCompile( - dex_file.StringByTypeIdx(class_def.class_idx_)); + const bool compilation_enabled = driver->IsClassToCompile(accessor.GetDescriptor()); // Compile direct and virtual methods. int64_t previous_method_idx = -1; - while (it.HasNextMethod()) { - uint32_t method_idx = it.GetMemberIndex(); + accessor.VisitMethods([&](const ClassAccessor::Method& method) { + const uint32_t method_idx = method.GetIndex(); if (method_idx == previous_method_idx) { // smali can create dex files with two encoded_methods sharing the same method_idx // http://code.google.com/p/smali/issues/detail?id=119 - it.Next(); - continue; + return; } previous_method_idx = method_idx; compile_fn(soa.Self(), driver, - it.GetMethodCodeItem(), - it.GetMethodAccessFlags(), - it.GetMethodInvokeType(class_def), + method.GetCodeItem(), + method.GetAccessFlags(), + method.GetInvokeType(class_def.access_flags_), class_def_index, method_idx, class_loader, @@ -2858,9 +2768,7 @@ static void CompileDexFile(CompilerDriver* driver, dex_to_dex_compilation_level, compilation_enabled, dex_cache); - it.Next(); - } - DCHECK(!it.HasNext()); + }); }; context.ForAllLambda(0, dex_file.NumClassDefs(), compile, thread_count); } diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 06f0bcdec7..103862beff 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -22,6 +22,8 @@ #include "class_linker.h" #include "common_compiler_test.h" #include "compiler_callbacks.h" +#include "dex/class_accessor-inl.h" +#include "dex/class_iterator.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_types.h" #include "dex/verification_results.h" @@ -148,48 +150,45 @@ class VerifierDepsTest : public CommonCompilerTest { Handle dex_cache_handle(hs.NewHandle(klass_Main_->GetDexCache())); const DexFile::ClassDef* class_def = klass_Main_->GetClassDef(); - const uint8_t* class_data = primary_dex_file_->GetClassData(*class_def); - CHECK(class_data != nullptr); + ClassAccessor accessor(*primary_dex_file_, *class_def); - ClassDataItemIterator it(*primary_dex_file_, class_data); - it.SkipAllFields(); + bool has_failures = true; + bool found_method = false; - ArtMethod* method = nullptr; - while (it.HasNextDirectMethod()) { + accessor.VisitMethods([&](const ClassAccessor::Method& method) + REQUIRES_SHARED(Locks::mutator_lock_) { ArtMethod* resolved_method = class_linker_->ResolveMethod( - it.GetMemberIndex(), + method.GetIndex(), dex_cache_handle, class_loader_handle, /* referrer */ nullptr, - it.GetMethodInvokeType(*class_def)); + method.GetInvokeType(class_def->access_flags_)); CHECK(resolved_method != nullptr); if (method_name == resolved_method->GetName()) { - method = resolved_method; - break; + soa.Self()->SetVerifierDeps(callbacks_->GetVerifierDeps()); + MethodVerifier verifier(soa.Self(), + primary_dex_file_, + dex_cache_handle, + class_loader_handle, + *class_def, + method.GetCodeItem(), + method.GetIndex(), + resolved_method, + method.GetAccessFlags(), + true /* can_load_classes */, + true /* allow_soft_failures */, + true /* need_precise_constants */, + false /* verify to dump */, + true /* allow_thread_suspension */); + verifier.Verify(); + soa.Self()->SetVerifierDeps(nullptr); + has_failures = verifier.HasFailures(); + found_method = true; } - it.Next(); - } - CHECK(method != nullptr); - - Thread::Current()->SetVerifierDeps(callbacks_->GetVerifierDeps()); - MethodVerifier verifier(Thread::Current(), - primary_dex_file_, - dex_cache_handle, - class_loader_handle, - *class_def, - it.GetMethodCodeItem(), - it.GetMemberIndex(), - method, - it.GetMethodAccessFlags(), - true /* can_load_classes */, - true /* allow_soft_failures */, - true /* need_precise_constants */, - false /* verify to dump */, - true /* allow_thread_suspension */); - verifier.Verify(); - Thread::Current()->SetVerifierDeps(nullptr); - return !verifier.HasFailures(); + }); + CHECK(found_method) << "Expected to find method " << method_name; + return !has_failures; } void VerifyDexFile(const char* multidex = nullptr) { diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h index 5cfbcaa359..a082142366 100644 --- a/libdexfile/dex/class_accessor-inl.h +++ b/libdexfile/dex/class_accessor-inl.h @@ -38,14 +38,14 @@ inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const DexFile::Clas num_virtual_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u) {} inline const uint8_t* ClassAccessor::Method::Read(const uint8_t* ptr) { - method_idx_ += DecodeUnsignedLeb128(&ptr); + index_ += DecodeUnsignedLeb128(&ptr); access_flags_ = DecodeUnsignedLeb128(&ptr); code_off_ = DecodeUnsignedLeb128(&ptr); return ptr; } inline const uint8_t* ClassAccessor::Field::Read(const uint8_t* ptr) { - field_idx_ += DecodeUnsignedLeb128(&ptr); + index_ += DecodeUnsignedLeb128(&ptr); access_flags_ = DecodeUnsignedLeb128(&ptr); return ptr; } @@ -54,7 +54,7 @@ template -inline void ClassAccessor::VisitMethodsAndFields( +inline void ClassAccessor::VisitFieldsAndMethods( const StaticFieldVisitor& static_field_visitor, const InstanceFieldVisitor& instance_field_visitor, const DirectMethodVisitor& direct_method_visitor, @@ -75,14 +75,14 @@ inline void ClassAccessor::VisitMethodsAndFields( } } { - Method data(dex_file_); + Method data(dex_file_, /*is_static_or_direct*/ true); for (size_t i = 0; i < num_direct_methods_; ++i) { ptr = data.Read(ptr); direct_method_visitor(data); } } { - Method data(dex_file_); + Method data(dex_file_, /*is_static_or_direct*/ false); for (size_t i = 0; i < num_virtual_methods_; ++i) { ptr = data.Read(ptr); virtual_method_visitor(data); @@ -94,12 +94,22 @@ template inline void ClassAccessor::VisitMethods(const DirectMethodVisitor& direct_method_visitor, const VirtualMethodVisitor& virtual_method_visitor) const { - VisitMethodsAndFields(VoidFunctor(), + VisitFieldsAndMethods(VoidFunctor(), VoidFunctor(), direct_method_visitor, virtual_method_visitor); } +template +inline void ClassAccessor::VisitFields(const StaticFieldVisitor& static_field_visitor, + const InstanceFieldVisitor& instance_field_visitor) const { + VisitFieldsAndMethods(static_field_visitor, + instance_field_visitor, + VoidFunctor(), + VoidFunctor()); +} + // Visit direct and virtual methods. template inline void ClassAccessor::VisitMethods(const MethodVisitor& method_visitor) const { @@ -114,6 +124,14 @@ inline CodeItemInstructionAccessor ClassAccessor::Method::GetInstructions() cons return CodeItemInstructionAccessor(dex_file_, dex_file_.GetCodeItem(GetCodeItemOffset())); } +inline const char* ClassAccessor::GetDescriptor() const { + return dex_file_.StringByTypeIdx(descriptor_index_); +} + +inline const DexFile::CodeItem* ClassAccessor::Method::GetCodeItem() const { + return dex_file_.GetCodeItem(code_off_); +} + } // namespace art #endif // ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_ diff --git a/libdexfile/dex/class_accessor.h b/libdexfile/dex/class_accessor.h index 835c4e2eb7..72bc50b98c 100644 --- a/libdexfile/dex/class_accessor.h +++ b/libdexfile/dex/class_accessor.h @@ -20,6 +20,9 @@ #include "base/utils.h" #include "code_item_accessors.h" #include "dex_file.h" +#include "invoke_type.h" +#include "method_reference.h" +#include "modifiers.h" namespace art { @@ -27,56 +30,82 @@ class ClassIteratorData; // Classes to access Dex data. class ClassAccessor { - public: - // Class method data. - class Method { + private: + class BaseItem { public: uint32_t GetIndex() const { - return method_idx_; + return index_; } uint32_t GetAccessFlags() const { return access_flags_; } + bool IsFinal() const { + return (GetAccessFlags() & kAccFinal) != 0; + } + + public: + uint32_t index_ = 0u; + uint32_t access_flags_ = 0u; + }; + + public: + // A decoded version of the method of a class_data_item. + class Method : public BaseItem { + public: uint32_t GetCodeItemOffset() const { return code_off_; } + InvokeType GetInvokeType(uint32_t class_access_flags) const { + return is_static_or_direct_ + ? GetDirectMethodInvokeType() + : GetVirtualMethodInvokeType(class_access_flags); + } + + MethodReference GetReference() const { + return MethodReference(&dex_file_, GetIndex()); + } + CodeItemInstructionAccessor GetInstructions() const; + const DexFile::CodeItem* GetCodeItem() const; + private: - explicit Method(const DexFile& dex_file) : dex_file_(dex_file) {} + explicit Method(const DexFile& dex_file, bool is_static_or_direct) + : dex_file_(dex_file), + is_static_or_direct_(is_static_or_direct) {} const uint8_t* Read(const uint8_t* ptr); - // A decoded version of the method of a class_data_item. + InvokeType GetDirectMethodInvokeType() const { + return (GetAccessFlags() & kAccStatic) != 0 ? kStatic : kDirect; + } + + InvokeType GetVirtualMethodInvokeType(uint32_t class_access_flags) const { + DCHECK_EQ(GetAccessFlags() & kAccStatic, 0U); + if ((class_access_flags & kAccInterface) != 0) { + return kInterface; + } else if ((GetAccessFlags() & kAccConstructor) != 0) { + return kSuper; + } else { + return kVirtual; + } + } + const DexFile& dex_file_; - uint32_t method_idx_ = 0u; - uint32_t access_flags_ = 0u; + const bool is_static_or_direct_; uint32_t code_off_ = 0u; friend class ClassAccessor; }; - // Class field data. - class Field { - public: - uint32_t GetIndex() const { - return field_idx_; - } - - uint32_t GetAccessFlags() const { - return access_flags_; - } - + // A decoded version of the field of a class_data_item. + class Field : public BaseItem { private: const uint8_t* Read(const uint8_t* ptr); - // A decoded version of the field of a class_data_item. - uint32_t field_idx_ = 0u; - uint32_t access_flags_ = 0u; - friend class ClassAccessor; }; @@ -89,20 +118,27 @@ class ClassAccessor { const DexFile::CodeItem* GetCodeItem(const Method& method) const; // Iterator data is not very iterator friendly, use visitors to get around this. + // No thread safety analysis since the visitor may require capabilities. template - void VisitMethodsAndFields(const StaticFieldVisitor& static_field_visitor, + void VisitFieldsAndMethods(const StaticFieldVisitor& static_field_visitor, const InstanceFieldVisitor& instance_field_visitor, const DirectMethodVisitor& direct_method_visitor, - const VirtualMethodVisitor& virtual_method_visitor) const; + const VirtualMethodVisitor& virtual_method_visitor) const + NO_THREAD_SAFETY_ANALYSIS; template void VisitMethods(const DirectMethodVisitor& direct_method_visitor, const VirtualMethodVisitor& virtual_method_visitor) const; + template + void VisitFields(const StaticFieldVisitor& static_field_visitor, + const InstanceFieldVisitor& instance_field_visitor) const; + // Visit direct and virtual methods. template void VisitMethods(const MethodVisitor& method_visitor) const; @@ -123,11 +159,16 @@ class ClassAccessor { return num_virtual_methods_; } - // TODO: Deprecate - dex::TypeIndex GetDescriptorIndex() const { + const char* GetDescriptor() const; + + dex::TypeIndex GetClassIdx() const { return descriptor_index_; } + const DexFile& GetDexFile() const { + return dex_file_; + } + protected: const DexFile& dex_file_; const dex::TypeIndex descriptor_index_ = {}; diff --git a/libdexfile/dex/dex_instruction_iterator.h b/libdexfile/dex/dex_instruction_iterator.h index db3ff95e02..b75a95bf5c 100644 --- a/libdexfile/dex/dex_instruction_iterator.h +++ b/libdexfile/dex/dex_instruction_iterator.h @@ -123,7 +123,7 @@ class DexInstructionIterator : public DexInstructionIteratorBase { using DexInstructionIteratorBase::DexInstructionIteratorBase; explicit DexInstructionIterator(const uint16_t* inst, uint32_t dex_pc) - : DexInstructionIteratorBase(Instruction::At(inst), dex_pc) {} + : DexInstructionIteratorBase(inst != nullptr ? Instruction::At(inst) : nullptr, dex_pc) {} explicit DexInstructionIterator(const DexInstructionPcPair& pair) : DexInstructionIterator(pair.Instructions(), pair.DexPc()) {} diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc index 427a465caf..312a7b3159 100644 --- a/tools/dexanalyze/dexanalyze_experiments.cc +++ b/tools/dexanalyze/dexanalyze_experiments.cc @@ -150,7 +150,7 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { case Instruction::INVOKE_VIRTUAL_RANGE: { bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE); uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); - if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetDescriptorIndex()) { + if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) { ++same_class_virtual_; } else { ++other_class_virtual_; @@ -162,7 +162,7 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { case Instruction::INVOKE_DIRECT_RANGE: { bool is_range = (inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE); uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetDescriptorIndex()) { + if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) { ++same_class_direct_; } else { ++other_class_direct_; @@ -174,7 +174,7 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { case Instruction::INVOKE_STATIC_RANGE: { bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE); uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetDescriptorIndex()) { + if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) { ++same_class_static_; } else { ++other_class_static_; -- GitLab From fbee398d00ab994eb1d7609d7f4e1d0c698d4aa6 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Thu, 24 May 2018 10:28:46 -0700 Subject: [PATCH 456/749] Remove hard-coded Object class from test 980 We redefined Object to a hard-coded dex-file in test 980. This led to problems whenever anything tried to change the Object class. This change makes us use slicer to modify the Object class instead. Test: ./test.py --host Change-Id: Ic888281f57f5796ec4a284ce79fa322669266899 --- test/980-redefine-object/redef_object.cc | 143 +++++++++ test/980-redefine-object/src/Main.java | 273 +----------------- .../src/art/Redefinition.java | 91 ------ test/Android.bp | 5 + 4 files changed, 156 insertions(+), 356 deletions(-) create mode 100644 test/980-redefine-object/redef_object.cc delete mode 100644 test/980-redefine-object/src/art/Redefinition.java diff --git a/test/980-redefine-object/redef_object.cc b/test/980-redefine-object/redef_object.cc new file mode 100644 index 0000000000..b4d82ad76d --- /dev/null +++ b/test/980-redefine-object/redef_object.cc @@ -0,0 +1,143 @@ +/* + * 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. + */ + +#include +#include + +#include "jni.h" +#include "jvmti.h" + +// Test infrastructure +#include "jvmti_helper.h" +#include "test_env.h" + +// Slicer's headers have code that triggers these warnings. b/65298177 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-compare" +#pragma clang diagnostic ignored "-Wunused-parameter" +#include "slicer/instrumentation.h" +#include "slicer/reader.h" +#include "slicer/writer.h" +#pragma clang diagnostic pop + +namespace art { +namespace Test980RedefineObject { + +static void JNICALL RedefineObjectHook(jvmtiEnv *jvmti_env, + JNIEnv* env, + jclass class_being_redefined ATTRIBUTE_UNUSED, + jobject loader ATTRIBUTE_UNUSED, + const char* name, + jobject protection_domain ATTRIBUTE_UNUSED, + jint class_data_len, + const unsigned char* class_data, + jint* new_class_data_len, + unsigned char** new_class_data) { + if (strcmp(name, "java/lang/Object") != 0) { + return; + } + + dex::Reader reader(class_data, class_data_len); + dex::u4 class_index = reader.FindClassIndex("Ljava/lang/Object;"); + if (class_index == dex::kNoIndex) { + env->ThrowNew(env->FindClass("java/lang/RuntimeException"), + "Failed to find object in dex file!"); + return; + } + + reader.CreateClassIr(class_index); + auto dex_ir = reader.GetIr(); + + slicer::MethodInstrumenter mi(dex_ir); + mi.AddTransformation(ir::MethodId("Lart/test/TestWatcher;", + "NotifyConstructed"), + /*this_as_object*/ true); + if (!mi.InstrumentMethod(ir::MethodId("Ljava/lang/Object;", + "", + "()V"))) { + env->ThrowNew(env->FindClass("java/lang/RuntimeException"), + "Failed to find Object;->()V in dex file!"); + return; + } + + + dex::Writer writer(dex_ir); + + class JvmtiAllocator : public dex::Writer::Allocator { + public: + explicit JvmtiAllocator(jvmtiEnv* jvmti) : jvmti_(jvmti) {} + + void* Allocate(size_t size) { + unsigned char* res = nullptr; + jvmti_->Allocate(size, &res); + return res; + } + + void Free(void* ptr) { + jvmti_->Deallocate(reinterpret_cast(ptr)); + } + + private: + jvmtiEnv* jvmti_; + }; + JvmtiAllocator allocator(jvmti_env); + size_t new_size; + *new_class_data = writer.CreateImage(&allocator, &new_size); + if (new_size > std::numeric_limits::max()) { + *new_class_data = nullptr; + env->ThrowNew(env->FindClass("java/lang/RuntimeException"), + "transform result is too large!"); + return; + } + *new_class_data_len = static_cast(new_size); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_addMemoryTrackingCall(JNIEnv* env, + jclass klass ATTRIBUTE_UNUSED, + jclass obj_class, + jthread thr) { + jvmtiCapabilities caps {.can_retransform_classes = 1}; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps))) { + return; + } + jvmtiEventCallbacks cb {.ClassFileLoadHook = RedefineObjectHook }; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) { + return; + } + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, + JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, + thr))) { + return; + } + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->RetransformClasses(1, &obj_class))) { + return; + } + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, + JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, + thr))) { + return; + } +} + +} // namespace Test980RedefineObject +} // namespace art + diff --git a/test/980-redefine-object/src/Main.java b/test/980-redefine-object/src/Main.java index 2428b55a4e..efbc75f6c5 100644 --- a/test/980-redefine-object/src/Main.java +++ b/test/980-redefine-object/src/Main.java @@ -14,278 +14,16 @@ * limitations under the License. */ -import static art.Redefinition.doCommonClassRedefinition; - import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Base64; import java.util.LinkedList; public class Main { - - // TODO We should make this run on the RI. - /** - * This test cannot be run on the RI. - */ - private static final byte[] CLASS_BYTES = new byte[0]; - - // TODO It might be a good idea to replace this hard-coded Object definition with a - // retransformation based test. /** - * Base64 encoding of the following smali file. - * - * .class public Ljava/lang/Object; - * .source "Object.java" - * # instance fields - * .field private transient shadow$_klass_:Ljava/lang/Class; - * .annotation system Ldalvik/annotation/Signature; - * value = { - * "Ljava/lang/Class", - * "<*>;" - * } - * .end annotation - * .end field - * - * .field private transient shadow$_monitor_:I - * # direct methods - * .method public constructor ()V - * .registers 1 - * .prologue - * invoke-static {p0}, Lart/test/TestWatcher;->NotifyConstructed(Ljava/lang/Object;)V - * return-void - * .end method - * - * .method static identityHashCode(Ljava/lang/Object;)I - * .registers 7 - * .prologue - * iget v0, p0, Ljava/lang/Object;->shadow$_monitor_:I - * const/high16 v3, -0x40000000 # -2.0f - * const/high16 v2, -0x80000000 - * const v1, 0xfffffff - * const/high16 v4, -0x40000000 # -2.0f - * and-int/2addr v4, v0 - * const/high16 v5, -0x80000000 - * if-ne v4, v5, :cond_15 - * const v4, 0xfffffff - * and-int/2addr v4, v0 - * return v4 - * :cond_15 - * invoke-static {p0}, Ljava/lang/Object;->identityHashCodeNative(Ljava/lang/Object;)I - * move-result v4 - * return v4 - * .end method - * - * .method private static native identityHashCodeNative(Ljava/lang/Object;)I - * .annotation build Ldalvik/annotation/optimization/FastNative; - * .end annotation - * .end method - * - * .method private native internalClone()Ljava/lang/Object; - * .annotation build Ldalvik/annotation/optimization/FastNative; - * .end annotation - * .end method - * - * - * # virtual methods - * .method protected clone()Ljava/lang/Object; - * .registers 4 - * .annotation system Ldalvik/annotation/Throws; - * value = { - * Ljava/lang/CloneNotSupportedException; - * } - * .end annotation - * - * .prologue - * instance-of v0, p0, Ljava/lang/Cloneable; - * if-nez v0, :cond_2d - * new-instance v0, Ljava/lang/CloneNotSupportedException; - * new-instance v1, Ljava/lang/StringBuilder; - * invoke-direct {v1}, Ljava/lang/StringBuilder;->()V - * const-string/jumbo v2, "Class " - * invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - * move-result-object v1 - * invoke-virtual {p0}, Ljava/lang/Object;->getClass()Ljava/lang/Class; - * move-result-object v2 - * invoke-virtual {v2}, Ljava/lang/Class;->getName()Ljava/lang/String; - * move-result-object v2 - * invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - * move-result-object v1 - * const-string/jumbo v2, " doesn\'t implement Cloneable" - * invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - * move-result-object v1 - * invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; - * move-result-object v1 - * invoke-direct {v0, v1}, Ljava/lang/CloneNotSupportedException;->(Ljava/lang/String;)V - * throw v0 - * :cond_2d - * invoke-direct {p0}, Ljava/lang/Object;->internalClone()Ljava/lang/Object; - * move-result-object v0 - * return-object v0 - * .end method - * - * .method public equals(Ljava/lang/Object;)Z - * .registers 3 - * .prologue - * if-ne p0, p1, :cond_4 - * const/4 v0, 0x1 - * :goto_3 - * return v0 - * :cond_4 - * const/4 v0, 0x0 - * goto :goto_3 - * .end method - * - * .method protected finalize()V - * .registers 1 - * .annotation system Ldalvik/annotation/Throws; - * value = { - * Ljava/lang/Throwable; - * } - * .end annotation - * .prologue - * return-void - * .end method - * - * .method public final getClass()Ljava/lang/Class; - * .registers 2 - * .annotation system Ldalvik/annotation/Signature; - * value = { - * "()", - * "Ljava/lang/Class", - * "<*>;" - * } - * .end annotation - * .prologue - * iget-object v0, p0, Ljava/lang/Object;->shadow$_klass_:Ljava/lang/Class; - * return-object v0 - * .end method - * - * .method public hashCode()I - * .registers 2 - * .prologue - * invoke-static {p0}, Ljava/lang/Object;->identityHashCode(Ljava/lang/Object;)I - * move-result v0 - * return v0 - * .end method - * - * .method public final native notify()V - * .annotation build Ldalvik/annotation/optimization/FastNative; - * .end annotation - * .end method - * - * .method public final native notifyAll()V - * .annotation build Ldalvik/annotation/optimization/FastNative; - * .end annotation - * .end method - * - * .method public toString()Ljava/lang/String; - * .registers 3 - * .prologue - * new-instance v0, Ljava/lang/StringBuilder; - * invoke-direct {v0}, Ljava/lang/StringBuilder;->()V - * invoke-virtual {p0}, Ljava/lang/Object;->getClass()Ljava/lang/Class; - * move-result-object v1 - * invoke-virtual {v1}, Ljava/lang/Class;->getName()Ljava/lang/String; - * move-result-object v1 - * invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - * move-result-object v0 - * const-string/jumbo v1, "@" - * invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - * move-result-object v0 - * invoke-virtual {p0}, Ljava/lang/Object;->hashCode()I - * move-result v1 - * invoke-static {v1}, Ljava/lang/Integer;->toHexString(I)Ljava/lang/String; - * move-result-object v1 - * invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - * move-result-object v0 - * invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; - * move-result-object v0 - * return-object v0 - * .end method - * - * .method public final native wait()V - * .annotation system Ldalvik/annotation/Throws; - * value = { - * Ljava/lang/InterruptedException; - * } - * .end annotation - * - * .annotation build Ldalvik/annotation/optimization/FastNative; - * .end annotation - * .end method - * - * .method public final wait(J)V - * .registers 4 - * .annotation system Ldalvik/annotation/Throws; - * value = { - * Ljava/lang/InterruptedException; - * } - * .end annotation - * .prologue - * const/4 v0, 0x0 - * invoke-virtual {p0, p1, p2, v0}, Ljava/lang/Object;->wait(JI)V - * return-void - * .end method - * - * .method public final native wait(JI)V - * .annotation system Ldalvik/annotation/Throws; - * value = { - * Ljava/lang/InterruptedException; - * } - * .end annotation - * - * .annotation build Ldalvik/annotation/optimization/FastNative; - * .end annotation - * .end method + * NB This test cannot be run on the RI. + * TODO We should make this run on the RI. */ - private static final byte[] DEX_BYTES = Base64.getDecoder().decode( - "ZGV4CjAzNQDUlMR9j03MYuOKekKs2p7zJzu2IfDb7RlMCgAAcAAAAHhWNBIAAAAAAAAAAIgJAAA6" + - "AAAAcAAAABEAAABYAQAADQAAAJwBAAACAAAAOAIAABYAAABIAgAAAQAAAPgCAAA0BwAAGAMAABgD" + - "AAA2AwAAOgMAAEADAABIAwAASwMAAFMDAABWAwAAWgMAAF0DAABgAwAAZAMAAGgDAACAAwAAnwMA" + - "ALsDAADoAwAA+gMAAA0EAAA1BAAATAQAAGEEAACDBAAAlwQAAKsEAADGBAAA3QQAAPAEAAD9BAAA" + - "AAUAAAQFAAAJBQAADQUAABAFAAAUBQAAHAUAACMFAAArBQAANQUAAD8FAABIBQAAUgUAAGQFAAB8" + - "BQAAiwUAAJUFAACnBQAAugUAAM0FAADVBQAA3QUAAOgFAADtBQAA/QUAAA8GAAAcBgAAJgYAAC0G" + - "AAAGAAAACAAAAAwAAAANAAAADgAAAA8AAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAA" + - "ABkAAAAcAAAAIAAAAAYAAAAAAAAAAAAAAAcAAAAAAAAAPAYAAAkAAAAGAAAAAAAAAAkAAAALAAAA" + - "AAAAAAkAAAAMAAAAAAAAAAoAAAAMAAAARAYAAAsAAAANAAAAVAYAABwAAAAPAAAAAAAAAB0AAAAP" + - "AAAATAYAAB4AAAAPAAAANAYAAB8AAAAPAAAAPAYAAB8AAAAPAAAAVAYAACEAAAAQAAAAPAYAAAsA" + - "BgA0AAAACwAAADUAAAACAAoAGgAAAAYABAAnAAAABwALAAMAAAAJAAUANgAAAAsABwADAAAACwAD" + - "ACMAAAALAAwAJAAAAAsABwAlAAAACwACACYAAAALAAAAKAAAAAsAAQApAAAACwABACoAAAALAAMA" + - "KwAAAAsABwAxAAAACwAHADIAAAALAAQANwAAAAsABwA5AAAACwAIADkAAAALAAkAOQAAAA0ABwAD" + - "AAAADQAGACIAAAANAAQANwAAAAsAAAABAAAA/////wAAAAAbAAAA0AYAAD4JAAAAAAAAHCBkb2Vz" + - "bid0IGltcGxlbWVudCBDbG9uZWFibGUAAigpAAQ8Kj47AAY8aW5pdD4AAUAABkNsYXNzIAABSQAC" + - "SUwAAUoAAUwAAkxJAAJMTAAWTGFydC90ZXN0L1Rlc3RXYXRjaGVyOwAdTGRhbHZpay9hbm5vdGF0" + - "aW9uL1NpZ25hdHVyZTsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ACtMZGFsdmlrL2Fubm90" + - "YXRpb24vb3B0aW1pemF0aW9uL0Zhc3ROYXRpdmU7ABBMamF2YS9sYW5nL0NsYXNzABFMamF2YS9s" + - "YW5nL0NsYXNzOwAmTGphdmEvbGFuZy9DbG9uZU5vdFN1cHBvcnRlZEV4Y2VwdGlvbjsAFUxqYXZh" + - "L2xhbmcvQ2xvbmVhYmxlOwATTGphdmEvbGFuZy9JbnRlZ2VyOwAgTGphdmEvbGFuZy9JbnRlcnJ1" + - "cHRlZEV4Y2VwdGlvbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABlM" + - "amF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7ABVMamF2YS9sYW5nL1Rocm93YWJsZTsAEU5vdGlmeUNv" + - "bnN0cnVjdGVkAAtPYmplY3QuamF2YQABVgACVkoAA1ZKSQACVkwAAVoAAlpMAAZhcHBlbmQABWNs" + - "b25lAAZlcXVhbHMACGZpbmFsaXplAAhnZXRDbGFzcwAHZ2V0TmFtZQAIaGFzaENvZGUAEGlkZW50" + - "aXR5SGFzaENvZGUAFmlkZW50aXR5SGFzaENvZGVOYXRpdmUADWludGVybmFsQ2xvbmUACGxvY2tX" + - "b3JkABBsb2NrV29yZEhhc2hNYXNrABFsb2NrV29yZFN0YXRlSGFzaAARbG9ja1dvcmRTdGF0ZU1h" + - "c2sABm1pbGxpcwAGbm90aWZ5AAlub3RpZnlBbGwAA29iagAOc2hhZG93JF9rbGFzc18AEHNoYWRv" + - "dyRfbW9uaXRvcl8AC3RvSGV4U3RyaW5nAAh0b1N0cmluZwAFdmFsdWUABHdhaXQAAAIAAAABAAAA" + - "AQAAAAsAAAABAAAAAAAAAAEAAAABAAAAAQAAAAwAAgQBOBwBGAcCBAE4HAEYCgIDATgcAhcQFwIC" + - "BAE4HAEYDgAFAAIDATgcAxcBFxAXAgAAAAAAAAAAAAEAAABaBgAAAgAAAGIGAAB8BgAAAQAAAGIG" + - "AAABAAAAagYAAAEAAAB0BgAAAQAAAHwGAAABAAAAfwYAAAAAAAABAAAACgAAAAAAAAAAAAAAsAYA" + - "AAUAAACUBgAABwAAALgGAAAIAAAAyAYAAAsAAADABgAADAAAAMAGAAANAAAAwAYAAA4AAADABgAA" + - "EAAAAJwGAAARAAAAqAYAABIAAACcBgAAKAAHDgBwATQHDi0DAC0BLQMDMAEtAwIvATwDAS4BeFsA" + - "7AEABw5LARoPOsYArAEBNAcOAMUEAAcOAEEABw4AaAAHDgCRAgAHDgCmAwExBw5LAAAAAQABAAEA" + - "AAA4BwAABAAAAHEQAAAAAA4ABwABAAEAAAA9BwAAGgAAAFJgAQAVAwDAFQIAgBQB////DxUEAMC1" + - "BBUFAIAzVAcAFAT///8PtQQPBHEQCwAGAAoEDwQEAAEAAgAAAFkHAAAyAAAAIDAIADkAKwAiAAcA" + - "IgENAHAQEwABABsCBQAAAG4gFAAhAAwBbhAIAAMADAJuEAEAAgAMAm4gFAAhAAwBGwIAAAAAbiAU" + - "ACEADAFuEBUAAQAMAXAgAgAQACcAcBAMAAMADAARAAMAAgAAAAAAZQcAAAYAAAAzIQQAEhAPABIA" + - "KP4BAAEAAAAAAGwHAAABAAAADgAAAAIAAQAAAAAAcgcAAAMAAABUEAAAEQAAAAIAAQABAAAAdwcA" + - "AAUAAABxEAoAAQAKAA8AAAADAAEAAgAAAHwHAAApAAAAIgANAHAQEwAAAG4QCAACAAwBbhABAAEA" + - "DAFuIBQAEAAMABsBBAAAAG4gFAAQAAwAbhAJAAIACgFxEAMAAQAMAW4gFAAQAAwAbhAVAAAADAAR" + - "AAAABAADAAQAAACCBwAABQAAABIAbkASACEDDgAAAgQLAIIBAYIBBIGABIwPBgikDwGKAgABggIA" + - "BQToDwEB3BABBPgQARGMEQEBpBEEkQIAAZECAAEBwBEBkQIAARGkEgGRAgAAABAAAAAAAAAAAQAA" + - "AAAAAAABAAAAOgAAAHAAAAACAAAAEQAAAFgBAAADAAAADQAAAJwBAAAEAAAAAgAAADgCAAAFAAAA" + - "FgAAAEgCAAAGAAAAAQAAAPgCAAACIAAAOgAAABgDAAABEAAABQAAADQGAAAEIAAABgAAAFoGAAAD" + - "EAAACQAAAIwGAAAGIAAAAQAAANAGAAADIAAACQAAADgHAAABIAAACQAAAIwHAAAAIAAAAQAAAD4J" + - "AAAAEAAAAQAAAIgJAAA="); private static final String LISTENER_LOCATION = System.getenv("DEX_LOCATION") + "/980-redefine-object-ex.jar"; @@ -361,7 +99,7 @@ public class Main { // Redefine the Object Class. safePrintln("Redefining the Object class to add a hook into the method"); - doCommonClassRedefinition(Object.class, CLASS_BYTES, DEX_BYTES); + addMemoryTrackingCall(Object.class, Thread.currentThread()); safePrintln("Allocating an j.l.Object after redefining Object class"); Object o2 = new Object(); @@ -407,5 +145,10 @@ public class Main { safePrintln("Finishing test!"); } + // This is from 929-search/search.cc private static native void addToBootClassLoader(String s); + // This is from 980-redefine-object/redef_object.cc + // It will add a call to Lart/test/TestWatcher;->NotifyConstructed()V in the Object ()V + // function. + private static native void addMemoryTrackingCall(Class c, Thread thr); } diff --git a/test/980-redefine-object/src/art/Redefinition.java b/test/980-redefine-object/src/art/Redefinition.java deleted file mode 100644 index 56d2938a01..0000000000 --- a/test/980-redefine-object/src/art/Redefinition.java +++ /dev/null @@ -1,91 +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. - */ - -package art; - -import java.util.ArrayList; -// Common Redefinition functions. Placed here for use by CTS -public class Redefinition { - public static final class CommonClassDefinition { - public final Class target; - public final byte[] class_file_bytes; - public final byte[] dex_file_bytes; - - public CommonClassDefinition(Class target, byte[] class_file_bytes, byte[] dex_file_bytes) { - this.target = target; - this.class_file_bytes = class_file_bytes; - this.dex_file_bytes = dex_file_bytes; - } - } - - // A set of possible test configurations. Test should set this if they need to. - // This must be kept in sync with the defines in ti-agent/common_helper.cc - public static enum Config { - COMMON_REDEFINE(0), - COMMON_RETRANSFORM(1), - COMMON_TRANSFORM(2); - - private final int val; - private Config(int val) { - this.val = val; - } - } - - public static void setTestConfiguration(Config type) { - nativeSetTestConfiguration(type.val); - } - - private static native void nativeSetTestConfiguration(int type); - - // Transforms the class - public static native void doCommonClassRedefinition(Class target, - byte[] classfile, - byte[] dexfile); - - public static void doMultiClassRedefinition(CommonClassDefinition... defs) { - ArrayList> classes = new ArrayList<>(); - ArrayList class_files = new ArrayList<>(); - ArrayList dex_files = new ArrayList<>(); - - for (CommonClassDefinition d : defs) { - classes.add(d.target); - class_files.add(d.class_file_bytes); - dex_files.add(d.dex_file_bytes); - } - doCommonMultiClassRedefinition(classes.toArray(new Class[0]), - class_files.toArray(new byte[0][]), - dex_files.toArray(new byte[0][])); - } - - public static void addMultiTransformationResults(CommonClassDefinition... defs) { - for (CommonClassDefinition d : defs) { - addCommonTransformationResult(d.target.getCanonicalName(), - d.class_file_bytes, - d.dex_file_bytes); - } - } - - public static native void doCommonMultiClassRedefinition(Class[] targets, - byte[][] classfiles, - byte[][] dexfiles); - public static native void doCommonClassRetransformation(Class... target); - public static native void setPopRetransformations(boolean pop); - public static native void popTransformationFor(String name); - public static native void enableCommonRetransformation(boolean enable); - public static native void addCommonTransformationResult(String target_name, - byte[] class_bytes, - byte[] dex_bytes); -} diff --git a/test/Android.bp b/test/Android.bp index 7909bf897a..e0ec286314 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -314,10 +314,15 @@ art_cc_defaults { "909-attach-agent/attach.cc", "912-classes/classes_art.cc", "936-search-onload/search_onload.cc", + "980-redefine-object/redef_object.cc", "983-source-transform-verify/source_transform_art.cc", "1940-ddms-ext/ddm_ext.cc", "1944-sudden-exit/sudden_exit.cc", ], + static_libs: [ + "libz", + "slicer", + ], } art_cc_test_library { -- GitLab From ba4c11864db52a316b9ba03017a5ea369039926a Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 24 May 2018 14:05:55 -0700 Subject: [PATCH 457/749] Remove some dex analysis from oatdump This functionality is now in dexanalyze. Test: test-art-host-gtest Change-Id: Ia58a54ec8e8962701d7b7d54db2d125cbec3e965 --- oatdump/oatdump.cc | 125 +---------------------------------------- oatdump/oatdump_test.h | 4 -- 2 files changed, 1 insertion(+), 128 deletions(-) diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 5c20efa3f7..fcd6bfd46c 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -527,7 +527,6 @@ class OatDumper { } // Dumping the dex file overview is compact enough to do even if header only. - DexFileData cumulative; for (size_t i = 0; i < oat_dex_files_.size(); i++) { const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; CHECK(oat_dex_file != nullptr); @@ -538,10 +537,7 @@ class OatDumper { << error_msg; continue; } - DexFileData data(*dex_file); - os << "Dex file data for " << dex_file->GetLocation() << "\n"; - data.Dump(os); - os << "\n"; + const DexLayoutSections* const layout_sections = oat_dex_file->GetDexLayoutSections(); if (layout_sections != nullptr) { os << "Layout data\n"; @@ -549,8 +545,6 @@ class OatDumper { os << "\n"; } - cumulative.Add(data); - // Dump .bss entries. DumpBssEntries( os, @@ -574,9 +568,6 @@ class OatDumper { sizeof(GcRoot), [=](uint32_t index) { return dex_file->StringDataByIdx(dex::StringIndex(index)); }); } - os << "Cumulative dex file data\n"; - cumulative.Dump(os); - os << "\n"; if (!options_.dump_header_only_) { VariableIndentationOutputStream vios(&os); @@ -950,120 +941,6 @@ class OatDumper { offsets_.insert(oat_method.GetVmapTableOffset()); } - // Dex file data, may be for multiple different dex files. - class DexFileData { - public: - DexFileData() {} - - explicit DexFileData(const DexFile& dex_file) - : num_string_ids_(dex_file.NumStringIds()), - num_method_ids_(dex_file.NumMethodIds()), - num_field_ids_(dex_file.NumFieldIds()), - num_type_ids_(dex_file.NumTypeIds()), - num_class_defs_(dex_file.NumClassDefs()) { - for (size_t class_def_index = 0; class_def_index < num_class_defs_; ++class_def_index) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); - WalkClass(dex_file, class_def); - } - } - - void Add(const DexFileData& other) { - AddAll(unique_string_ids_from_code_, other.unique_string_ids_from_code_); - num_string_ids_from_code_ += other.num_string_ids_from_code_; - AddAll(dex_code_item_ptrs_, other.dex_code_item_ptrs_); - dex_code_bytes_ += other.dex_code_bytes_; - num_string_ids_ += other.num_string_ids_; - num_method_ids_ += other.num_method_ids_; - num_field_ids_ += other.num_field_ids_; - num_type_ids_ += other.num_type_ids_; - num_class_defs_ += other.num_class_defs_; - } - - void Dump(std::ostream& os) { - os << "Num string ids: " << num_string_ids_ << "\n"; - os << "Num method ids: " << num_method_ids_ << "\n"; - os << "Num field ids: " << num_field_ids_ << "\n"; - os << "Num type ids: " << num_type_ids_ << "\n"; - os << "Num class defs: " << num_class_defs_ << "\n"; - os << "Unique strings loaded from dex code: " << unique_string_ids_from_code_.size() << "\n"; - os << "Total strings loaded from dex code: " << num_string_ids_from_code_ << "\n"; - os << "Number of unique dex code items: " << dex_code_item_ptrs_.size() << "\n"; - os << "Total number of dex code bytes: " << dex_code_bytes_ << "\n"; - } - - private: - // All of the elements from one container to another. - template - static void AddAll(Dest& dest, const Src& src) { - dest.insert(src.begin(), src.end()); - } - - void WalkClass(const DexFile& dex_file, const DexFile::ClassDef& class_def) { - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { // empty class such as a marker interface? - return; - } - ClassDataItemIterator it(dex_file, class_data); - it.SkipAllFields(); - while (it.HasNextMethod()) { - WalkCodeItem(dex_file, it.GetMethodCodeItem()); - it.Next(); - } - DCHECK(!it.HasNext()); - } - - void WalkCodeItem(const DexFile& dex_file, const DexFile::CodeItem* code_item) { - if (code_item == nullptr) { - return; - } - CodeItemInstructionAccessor instructions(dex_file, code_item); - - // If we inserted a new dex code item pointer, add to total code bytes. - const uint16_t* code_ptr = instructions.Insns(); - if (dex_code_item_ptrs_.insert(code_ptr).second) { - dex_code_bytes_ += instructions.InsnsSizeInCodeUnits() * sizeof(code_ptr[0]); - } - - for (const DexInstructionPcPair& inst : instructions) { - switch (inst->Opcode()) { - case Instruction::CONST_STRING: { - const dex::StringIndex string_index(inst->VRegB_21c()); - unique_string_ids_from_code_.insert(StringReference(&dex_file, string_index)); - ++num_string_ids_from_code_; - break; - } - case Instruction::CONST_STRING_JUMBO: { - const dex::StringIndex string_index(inst->VRegB_31c()); - unique_string_ids_from_code_.insert(StringReference(&dex_file, string_index)); - ++num_string_ids_from_code_; - break; - } - default: - break; - } - } - } - - // Unique string ids loaded from dex code. - std::set unique_string_ids_from_code_; - - // Total string ids loaded from dex code. - size_t num_string_ids_from_code_ = 0; - - // Unique code pointers. - std::set dex_code_item_ptrs_; - - // Total "unique" dex code bytes. - size_t dex_code_bytes_ = 0; - - // Other dex ids. - size_t num_string_ids_ = 0; - size_t num_method_ids_ = 0; - size_t num_field_ids_ = 0; - size_t num_type_ids_ = 0; - size_t num_class_defs_ = 0; - }; - bool DumpOatDexFile(std::ostream& os, const OatFile::OatDexFile& oat_dex_file) { bool success = true; bool stop_analysis = false; diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h index bbe89ca33c..293acdc3a6 100644 --- a/oatdump/oatdump_test.h +++ b/oatdump/oatdump_test.h @@ -151,10 +151,6 @@ class OatDumpTest : public CommonRuntimeTest { exec_argv.push_back("--symbolize=" + core_oat_location_); exec_argv.push_back("--output=" + core_oat_location_ + ".symbolize"); } else { - expected_prefixes.push_back("Dex file data for"); - expected_prefixes.push_back("Num string ids:"); - expected_prefixes.push_back("Num field ids:"); - expected_prefixes.push_back("Num method ids:"); expected_prefixes.push_back("LOCATION:"); expected_prefixes.push_back("MAGIC:"); expected_prefixes.push_back("DEX FILE COUNT:"); -- GitLab From 20f49922302d30c452eaf5588a6cf66946866922 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 24 May 2018 16:04:17 -0700 Subject: [PATCH 458/749] Move profman/ to ClassAccessor Bug: 77709234 Bug: 79758018 Test: test-art-host Change-Id: I97111ef61a6735ef8719c1a6d7c80ad7c553af51 --- profman/boot_image_profile.cc | 52 ++++++++++++++++------------------- profman/profman.cc | 21 +++++--------- 2 files changed, 30 insertions(+), 43 deletions(-) diff --git a/profman/boot_image_profile.cc b/profman/boot_image_profile.cc index 89c9eb8b03..6715680361 100644 --- a/profman/boot_image_profile.cc +++ b/profman/boot_image_profile.cc @@ -18,6 +18,7 @@ #include #include "boot_image_profile.h" +#include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" #include "dex/method_reference.h" #include "dex/type_reference.h" @@ -74,38 +75,31 @@ void GenerateBootImageProfile( } } // Walk all of the classes and add them to the profile if they meet the requirements. - for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); - TypeReference ref(dex_file.get(), class_def.class_idx_); + for (ClassAccessor accessor : dex_file->GetClasses()) { + TypeReference ref(dex_file.get(), accessor.GetClassIdx()); bool is_clean = true; - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data != nullptr) { - ClassDataItemIterator it(*dex_file, class_data); - while (it.HasNextStaticField()) { - const uint32_t flags = it.GetFieldAccessFlags(); - if ((flags & kAccFinal) == 0) { - // Not final static field will probably dirty the class. - is_clean = false; - break; - } - it.Next(); + auto method_visitor = [&](const ClassAccessor::Method& method) { + const uint32_t flags = method.GetAccessFlags(); + if ((flags & kAccNative) != 0) { + // Native method will get dirtied. + is_clean = false; } - it.SkipInstanceFields(); - while (it.HasNextMethod()) { - const uint32_t flags = it.GetMethodAccessFlags(); - if ((flags & kAccNative) != 0) { - // Native method will get dirtied. - is_clean = false; - break; - } - if ((flags & kAccConstructor) != 0 && (flags & kAccStatic) != 0) { - // Class initializer, may get dirtied (not sure). - is_clean = false; - break; - } - it.Next(); + if ((flags & kAccConstructor) != 0 && (flags & kAccStatic) != 0) { + // Class initializer, may get dirtied (not sure). + is_clean = false; } - } + }; + accessor.VisitFieldsAndMethods( + [&](const ClassAccessor::Field& field) { + if (!field.IsFinal()) { + // Not final static field will probably dirty the class. + is_clean = false; + } + }, + /*instance_fields*/ VoidFunctor(), + method_visitor, + method_visitor); + ++(is_clean ? clean_count : dirty_count); // This counter is how many profiles contain the class. size_t counter = 0; diff --git a/profman/profman.cc b/profman/profman.cc index 1f7723946c..661132d94f 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -43,6 +43,7 @@ #include "boot_image_profile.h" #include "dex/art_dex_file_loader.h" #include "dex/bytecode_utils.h" +#include "dex/class_accessor-inl.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" @@ -929,21 +930,13 @@ class ProfMan FINAL { dex_resolved_classes.first->AddClass(class_ref.TypeIndex()); std::vector methods; if (method_str == kClassAllMethods) { - // Add all of the methods. - const DexFile::ClassDef* class_def = dex_file->FindClassDef(class_ref.TypeIndex()); - const uint8_t* class_data = dex_file->GetClassData(*class_def); - if (class_data != nullptr) { - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - while (it.HasNextMethod()) { - if (it.GetMethodCodeItemOffset() != 0) { - // Add all of the methods that have code to the profile. - const uint32_t method_idx = it.GetMemberIndex(); - methods.push_back(ProfileMethodInfo(MethodReference(dex_file, method_idx))); - } - it.Next(); + ClassAccessor accessor(*dex_file, *dex_file->FindClassDef(class_ref.TypeIndex())); + accessor.VisitMethods([&](const ClassAccessor::Method& method) { + if (method.GetCodeItemOffset() != 0) { + // Add all of the methods that have code to the profile. + methods.push_back(ProfileMethodInfo(method.GetReference())); } - } + }); } // TODO: Check return values? profile->AddMethods(methods, static_cast(flags)); -- GitLab From 6aa4e0d6d54fd061387f1ffc88ea2f96daad9425 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Fri, 25 May 2018 09:39:16 +0100 Subject: [PATCH 459/749] ART: Fix run-test invocation for jvm tests Add check in testrunner.py to conditionally add "--compact-dex-level". Add check in run-test to warn if the test argument is missing to avoid cryptic failure. Test: art/test.py --host --jvm -r -t 526 Change-Id: I73c6f9654656ada79553e536546464e267c66053 --- test/run-test | 5 +++++ test/testrunner/testrunner.py | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/test/run-test b/test/run-test index 8e012d13fb..5bd8b3b348 100755 --- a/test/run-test +++ b/test/run-test @@ -441,6 +441,11 @@ while true; do fi done +if [ "$usage" = "no" -a "x$1" = "x" ]; then + echo "missing test to run" 1>&2 + usage="yes" +fi + # The DEX_LOCATION with the chroot prefix, if any. chroot_dex_location="$chroot$DEX_LOCATION" diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 09b9b210fc..be7e225e05 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -411,8 +411,9 @@ def run_tests(tests): elif prebuild == 'no-dex2oat': options_test += ' --no-prebuild --no-dex2oat' - # Add option and remove the cdex- prefix. - options_test += ' --compact-dex-level ' + cdex_level.replace('cdex-','') + if cdex_level: + # Add option and remove the cdex- prefix. + options_test += ' --compact-dex-level ' + cdex_level.replace('cdex-','') if compiler == 'optimizing': options_test += ' --optimizing' -- GitLab From 644edf1b8a9524d60c1666230490fc41779f6d63 Mon Sep 17 00:00:00 2001 From: Tamas Kenez Date: Fri, 25 May 2018 11:26:37 +0200 Subject: [PATCH 460/749] ART-tests: Remove DX-dependency from 458-checker-instruct-simplification Bug: 65168732 Test: art/test.py -r --host -t 458-checker Change-Id: Id8cbdb80f3e643d4dbf0c32e01babe38e6e1a5dd --- .../458-checker-instruct-simplification/build | 20 -- .../smali/SmaliTests2.smali | 305 ++++++++++++++++++ .../src/Main.java | 200 ++---------- 3 files changed, 338 insertions(+), 187 deletions(-) delete mode 100755 test/458-checker-instruct-simplification/build create mode 100644 test/458-checker-instruct-simplification/smali/SmaliTests2.smali diff --git a/test/458-checker-instruct-simplification/build b/test/458-checker-instruct-simplification/build deleted file mode 100755 index 10ffcc537d..0000000000 --- a/test/458-checker-instruct-simplification/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/458-checker-instruct-simplification/smali/SmaliTests2.smali b/test/458-checker-instruct-simplification/smali/SmaliTests2.smali new file mode 100644 index 0000000000..99fb049510 --- /dev/null +++ b/test/458-checker-instruct-simplification/smali/SmaliTests2.smali @@ -0,0 +1,305 @@ +# 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. + +.class public LSmaliTests2; +.super Ljava/lang/Object; + +## CHECK-START: int SmaliTests2.$noinline$XorAllOnes(int) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant -1 +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests2.$noinline$XorAllOnes(int) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> Not [<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests2.$noinline$XorAllOnes(int) instruction_simplifier (after) +## CHECK-NOT: Xor + +# Original java source: +# +# return arg ^ -1; +# +.method public static $noinline$XorAllOnes(I)I + .registers 2 + .param p0, "arg" # I + + .prologue + .line 658 + xor-int/lit8 v0, p0, -0x1 + + return v0 +.end method + +# Test simplification of the `~~var` pattern. +# The transformation tested is implemented in `InstructionSimplifierVisitor::VisitNot`. + +## CHECK-START: long SmaliTests2.$noinline$NotNot1(long) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> LongConstant -1 +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: long SmaliTests2.$noinline$NotNot1(long) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: Return [<>] + +## CHECK-START: long SmaliTests2.$noinline$NotNot1(long) instruction_simplifier (after) +## CHECK-NOT: Xor + +# Original java source: +# +# return ~~arg; +.method public static $noinline$NotNot1(J)J + .registers 6 + .param p0, "arg" # J + + .prologue + const-wide/16 v2, -0x1 + + .line 1001 + xor-long v0, p0, v2 + + xor-long/2addr v0, v2 + + return-wide v0 +.end method + +## CHECK-START: int SmaliTests2.$noinline$NotNot2(int) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant -1 +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: <> Add [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests2.$noinline$NotNot2(int) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> Not [<>] +## CHECK-DAG: <> Add [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests2.$noinline$NotNot2(int) instruction_simplifier (after) +## CHECK: Not +## CHECK-NOT: Not + +## CHECK-START: int SmaliTests2.$noinline$NotNot2(int) instruction_simplifier (after) +## CHECK-NOT: Xor + +# Original java source: +# +# int temp = ~arg; +# return temp + ~temp; +# +.method public static $noinline$NotNot2(I)I + .registers 3 + .param p0, "arg" # I + + .prologue + .line 1026 + xor-int/lit8 v0, p0, -0x1 + + .line 1027 + .local v0, "temp":I + xor-int/lit8 v1, v0, -0x1 + + add-int/2addr v1, v0 + + return v1 +.end method + +# Original java source: +# +# return !arg; +# +.method public static NegateValue(Z)Z + .registers 2 + .param p0, "arg" # Z + + .prologue + .line 1216 + if-nez p0, :cond_4 + + const/4 v0, 0x1 + + :goto_3 + return v0 + + :cond_4 + const/4 v0, 0x0 + + goto :goto_3 +.end method + +# Test simplification of double Boolean negation. Note that sometimes +# both negations can be removed but we only expect the simplifier to +# remove the second. + +## CHECK-START: boolean SmaliTests2.$noinline$NotNotBool(boolean) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> InvokeStaticOrDirect method_name:SmaliTests2.NegateValue +## CHECK-DAG: <> NotEqual [<>,<>] +## CHECK-DAG: If [<>] + +## CHECK-START: boolean SmaliTests2.$noinline$NotNotBool(boolean) instruction_simplifier (after) +## CHECK-NOT: NotEqual + +## CHECK-START: boolean SmaliTests2.$noinline$NotNotBool(boolean) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> InvokeStaticOrDirect method_name:SmaliTests2.NegateValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> Phi [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: boolean SmaliTests2.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: If [<>] +## CHECK-DAG: <> Phi [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: boolean SmaliTests2.$noinline$NotNotBool(boolean) instruction_simplifier$after_gvn (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: Return [<>] + +# Original java source: +# +# return !(NegateValue(arg)); +# +.method public static $noinline$NotNotBool(Z)Z + .registers 2 + .param p0, "arg" # Z + + .prologue + .line 1220 + invoke-static {p0}, LSmaliTests2;->NegateValue(Z)Z + + move-result v0 + + if-nez v0, :cond_8 + + const/4 v0, 0x1 + + :goto_7 + return v0 + + :cond_8 + const/4 v0, 0x0 + + goto :goto_7 +.end method + +## CHECK-START: int SmaliTests2.$noinline$bug68142795Short(short) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 65535 +## CHECK-DAG: <> And [<>,<>] +## CHECK-DAG: <> And [<>,<>] +## CHECK-DAG: <> TypeConversion [<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests2.$noinline$bug68142795Short(short) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: Return [<>] + +# Original java source +# +# return (short)(0xffff & (s & 0xffff)); +# +.method public static $noinline$bug68142795Short(S)I + .registers 3 + .param p0, "s" # S + + .prologue + const v1, 0xffff + + .line 2562 + and-int v0, p0, v1 + + and-int/2addr v0, v1 + + int-to-short v0, v0 + + return v0 +.end method + +# Original java source +# +# return 255; +# +.method private static $inline$get255()I + .registers 1 + + .prologue + .line 2849 + const/16 v0, 0xff + + return v0 +.end method + +## CHECK-START: int SmaliTests2.$noinline$bug68142795Boolean(boolean) instruction_simplifier$after_inlining (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> IntConstant 255 +## CHECK-DAG: If [<>] +## CHECK-DAG: <> Phi [<>,<>] +## CHECK-DAG: <> And [<>,<>] +## CHECK-DAG: <> TypeConversion [<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests2.$noinline$bug68142795Boolean(boolean) instruction_simplifier$after_gvn (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: Return [<>] + +# Original java source +# +# int v = b ? 1 : 0; // Should be simplified to "b" after inlining. +# return (byte)($inline$get255() & v); +# +.method public static $noinline$bug68142795Boolean(Z)I + .registers 3 + .param p0, "b" # Z + + .prologue + .line 2580 + if-eqz p0, :cond_a + + const/4 v0, 0x1 + + .line 2581 + .local v0, "v":I + :goto_3 + invoke-static {}, LSmaliTests2;->$inline$get255()I + + move-result v1 + + and-int/2addr v1, v0 + + int-to-byte v1, v1 + + return v1 + + .line 2580 + .end local v0 # "v":I + :cond_a + const/4 v0, 0x0 + + goto :goto_3 +.end method diff --git a/test/458-checker-instruct-simplification/src/Main.java b/test/458-checker-instruct-simplification/src/Main.java index b24cfcb775..40e3778109 100644 --- a/test/458-checker-instruct-simplification/src/Main.java +++ b/test/458-checker-instruct-simplification/src/Main.java @@ -640,24 +640,6 @@ public class Main { return arg ^ 0; } - /// CHECK-START: int Main.$noinline$XorAllOnes(int) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant -1 - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$noinline$XorAllOnes(int) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> Not [<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$noinline$XorAllOnes(int) instruction_simplifier (after) - /// CHECK-NOT: Xor - - public static int $noinline$XorAllOnes(int arg) { - return arg ^ -1; - } - /** * Test that addition or subtraction operation with both inputs negated are * optimized to use a single negation after the operation. @@ -978,56 +960,7 @@ public class Main { return -temp | -temp; } - /** - * Test simplification of the `~~var` pattern. - * The transformation tested is implemented in `InstructionSimplifierVisitor::VisitNot`. - */ - - /// CHECK-START: long Main.$noinline$NotNot1(long) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> LongConstant -1 - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: long Main.$noinline$NotNot1(long) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: Return [<>] - - /// CHECK-START: long Main.$noinline$NotNot1(long) instruction_simplifier (after) - /// CHECK-NOT: Xor - - public static long $noinline$NotNot1(long arg) { - return ~~arg; - } - - /// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant -1 - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> Not [<>] - /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (after) - /// CHECK: Not - /// CHECK-NOT: Not - - /// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (after) - /// CHECK-NOT: Xor - - public static int $noinline$NotNot2(int arg) { - int temp = ~arg; - return temp + ~temp; - } - - /** + /** * Test the simplification of a subtraction with a negated argument. * The transformation tested is implemented in `InstructionSimplifierVisitor::VisitSub`. */ @@ -1176,50 +1109,6 @@ public class Main { return (arg ? $inline$ReturnArg(0) : $inline$ReturnArg(1)) == 2; } - /* - * Test simplification of double Boolean negation. Note that sometimes - * both negations can be removed but we only expect the simplifier to - * remove the second. - */ - - /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> InvokeStaticOrDirect method_name:Main.NegateValue - /// CHECK-DAG: <> NotEqual [<>,<>] - /// CHECK-DAG: If [<>] - - /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier (after) - /// CHECK-NOT: NotEqual - - /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> InvokeStaticOrDirect method_name:Main.NegateValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> Phi [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: If [<>] - /// CHECK-DAG: <> Phi [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_gvn (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: Return [<>] - - public static boolean NegateValue(boolean arg) { - return !arg; - } - - public static boolean $noinline$NotNotBool(boolean arg) { - return !(NegateValue(arg)); - } - /// CHECK-START: float Main.$noinline$Div2(float) instruction_simplifier (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> FloatConstant 2 @@ -1911,9 +1800,9 @@ public class Main { } } - public static boolean $noinline$runSmaliTestBoolean(String name, boolean input) { + public static boolean $noinline$runSmaliTest2Boolean(String name, boolean input) { try { - Class c = Class.forName("SmaliTests"); + Class c = Class.forName("SmaliTests2"); Method m = c.getMethod(name, boolean.class); return (Boolean) m.invoke(null, input); } catch (Exception ex) { @@ -1921,9 +1810,9 @@ public class Main { } } - public static int $noinline$runSmaliTestInt(String name, int arg) { + public static int $noinline$runSmaliTestInt(String postfix, String name, int arg) { try { - Class c = Class.forName("SmaliTests"); + Class c = Class.forName("SmaliTests" + postfix); Method m = c.getMethod(name, int.class); return (Integer) m.invoke(null, arg); } catch (Exception ex) { @@ -1931,9 +1820,13 @@ public class Main { } } - public static long $noinline$runSmaliTestLong(String name, long arg) { + public static int $noinline$runSmaliTestInt(String name, int arg) { + return $noinline$runSmaliTestInt("", name, arg); + } + + public static long $noinline$runSmaliTest2Long(String name, long arg) { try { - Class c = Class.forName("SmaliTests"); + Class c = Class.forName("SmaliTests2"); Method m = c.getMethod(name, long.class); return (Long) m.invoke(null, arg); } catch (Exception ex) { @@ -2547,40 +2440,6 @@ public class Main { return (byte)(0xff & (b & 0xff)); } - /// CHECK-START: int Main.$noinline$bug68142795Short(short) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 65535 - /// CHECK-DAG: <> And [<>,<>] - /// CHECK-DAG: <> And [<>,<>] - /// CHECK-DAG: <> TypeConversion [<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$noinline$bug68142795Short(short) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: Return [<>] - public static int $noinline$bug68142795Short(short s) { - return (short)(0xffff & (s & 0xffff)); - } - - /// CHECK-START: int Main.$noinline$bug68142795Boolean(boolean) instruction_simplifier$after_inlining (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> IntConstant 255 - /// CHECK-DAG: If [<>] - /// CHECK-DAG: <> Phi [<>,<>] - /// CHECK-DAG: <> And [<>,<>] - /// CHECK-DAG: <> TypeConversion [<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$noinline$bug68142795Boolean(boolean) instruction_simplifier$after_gvn (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: Return [<>] - public static int $noinline$bug68142795Boolean(boolean b) { - int v = b ? 1 : 0; // Should be simplified to "b" after inlining. - return (byte)($inline$get255() & v); - } - /// CHECK-START: int Main.$noinline$bug68142795Elaborate(byte) instruction_simplifier (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 255 @@ -2599,7 +2458,15 @@ public class Main { return (byte)((int)(((long)(b & 0xff)) & 255L)); } - public static void main(String[] args) { + public static void main(String[] args) throws Exception { + Class smaliTests2 = Class.forName("SmaliTests2"); + Method $noinline$XorAllOnes = smaliTests2.getMethod("$noinline$XorAllOnes", int.class); + Method $noinline$NotNot1 = smaliTests2.getMethod("$noinline$NotNot1", long.class); + Method $noinline$NotNot2 = smaliTests2.getMethod("$noinline$NotNot2", int.class); + Method $noinline$NotNotBool = smaliTests2.getMethod("$noinline$NotNotBool", boolean.class); + Method $noinline$bug68142795Short = smaliTests2.getMethod("$noinline$bug68142795Short", short.class); + Method $noinline$bug68142795Boolean = smaliTests2.getMethod("$noinline$bug68142795Boolean", boolean.class); + int arg = 123456; float floatArg = 123456.125f; @@ -2624,7 +2491,7 @@ public class Main { assertLongEquals(3, $noinline$SubSubConst(4)); assertLongEquals(arg, $noinline$UShr0(arg)); assertIntEquals(arg, $noinline$Xor0(arg)); - assertIntEquals(~arg, $noinline$XorAllOnes(arg)); + assertIntEquals(~arg, (int)$noinline$XorAllOnes.invoke(null, arg)); assertIntEquals(-(arg + arg + 1), $noinline$AddNegs1(arg, arg + 1)); assertIntEquals(-(arg + arg + 1), $noinline$AddNegs2(arg, arg + 1)); assertLongEquals(-(2 * arg + 1), $noinline$AddNegs3(arg, arg + 1)); @@ -2635,10 +2502,10 @@ public class Main { assertLongEquals(arg, $noinline$NegNeg3(arg)); assertIntEquals(1, $noinline$NegSub1(arg, arg + 1)); assertIntEquals(1, $noinline$NegSub2(arg, arg + 1)); - assertLongEquals(arg, $noinline$NotNot1(arg)); - assertLongEquals(arg, $noinline$runSmaliTestLong("$noinline$NotNot1", arg)); - assertIntEquals(-1, $noinline$NotNot2(arg)); - assertIntEquals(-1, $noinline$runSmaliTestInt("$noinline$NotNot2", arg)); + assertLongEquals(arg, (long)$noinline$NotNot1.invoke(null, arg)); + assertLongEquals(arg, $noinline$runSmaliTest2Long("$noinline$NotNot1", arg)); + assertIntEquals(-1, (int)$noinline$NotNot2.invoke(null, arg)); + assertIntEquals(-1, $noinline$runSmaliTestInt("2", "$noinline$NotNot2", arg)); assertIntEquals(-(arg + arg + 1), $noinline$SubNeg1(arg, arg + 1)); assertIntEquals(-(arg + arg + 1), $noinline$SubNeg2(arg, arg + 1)); assertLongEquals(-(2 * arg + 1), $noinline$SubNeg3(arg, arg + 1)); @@ -2646,10 +2513,10 @@ public class Main { assertBooleanEquals(true, $noinline$EqualBoolVsIntConst(true)); assertBooleanEquals(false, $noinline$NotEqualBoolVsIntConst(false)); assertBooleanEquals(false, $noinline$NotEqualBoolVsIntConst(false)); - assertBooleanEquals(true, $noinline$NotNotBool(true)); - assertBooleanEquals(true, $noinline$runSmaliTestBoolean("$noinline$NotNotBool", true)); - assertBooleanEquals(false, $noinline$NotNotBool(false)); - assertBooleanEquals(false, $noinline$runSmaliTestBoolean("$noinline$NotNotBool", false)); + assertBooleanEquals(true, (boolean)$noinline$NotNotBool.invoke(null, true)); + assertBooleanEquals(true, $noinline$runSmaliTest2Boolean("$noinline$NotNotBool", true)); + assertBooleanEquals(false, (boolean)$noinline$NotNotBool.invoke(null, false)); + assertBooleanEquals(false, $noinline$runSmaliTest2Boolean("$noinline$NotNotBool", false)); assertFloatEquals(50.0f, $noinline$Div2(100.0f)); assertDoubleEquals(75.0, $noinline$Div2(150.0)); assertFloatEquals(-400.0f, $noinline$DivMP25(100.0f)); @@ -2836,17 +2703,16 @@ public class Main { assertIntEquals(0x7f, $noinline$bug68142795Byte((byte) 0x7f)); assertIntEquals((byte) 0x80, $noinline$bug68142795Byte((byte) 0x80)); - assertIntEquals(0x7fff, $noinline$bug68142795Short((short) 0x7fff)); - assertIntEquals((short) 0x8000, $noinline$bug68142795Short((short) 0x8000)); - assertIntEquals(0, $noinline$bug68142795Boolean(false)); - assertIntEquals(1, $noinline$bug68142795Boolean(true)); + assertIntEquals(0x7fff, (int)$noinline$bug68142795Short.invoke(null, (short) 0x7fff)); + assertIntEquals((short) 0x8000, (int)$noinline$bug68142795Short.invoke(null, (short) 0x8000)); + assertIntEquals(0, (int)$noinline$bug68142795Boolean.invoke(null, false)); + assertIntEquals(1, (int)$noinline$bug68142795Boolean.invoke(null, true)); assertIntEquals(0x7f, $noinline$bug68142795Elaborate((byte) 0x7f)); assertIntEquals((byte) 0x80, $noinline$bug68142795Elaborate((byte) 0x80)); } private static boolean $inline$true() { return true; } private static boolean $inline$false() { return false; } - private static int $inline$get255() { return 255; } public static boolean booleanField; -- GitLab From b4eb1b19e1dd35d12a408358656c1421f507d231 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 24 May 2018 11:09:38 +0100 Subject: [PATCH 461/749] Refactor ClassRoot/GetClassRoot(). Move it outside the ClassLinker, into its own header file, and add retrieval based on a mirror class template argument. Keep the SetClassRoot() as a private member of ClassLinker. Make the new GetClassRoot()s return ObjPtr<>. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Bug: 31113334 Change-Id: Icbc6b62b41f6ffd65b437297a21eadbb0454e2b7 --- compiler/optimizing/inliner.cc | 8 +- compiler/optimizing/instruction_simplifier.cc | 4 +- compiler/optimizing/nodes.cc | 4 +- compiler/optimizing/optimizing_compiler.cc | 4 +- .../optimizing/reference_type_propagation.cc | 23 +- dex2oat/linker/image_writer.cc | 7 +- openjdkjvmti/ti_class_definition.cc | 7 +- openjdkjvmti/ti_redefine.cc | 13 +- runtime/Android.bp | 1 + runtime/class_linker-inl.h | 8 - runtime/class_linker.cc | 374 ++++++++---------- runtime/class_linker.h | 61 +-- runtime/class_linker_test.cc | 8 +- runtime/class_root.cc | 36 ++ runtime/class_root.h | 183 +++++++++ runtime/entrypoints/quick/callee_save_frame.h | 6 +- runtime/gc/accounting/mod_union_table_test.cc | 4 +- runtime/gc/collector/concurrent_copying.cc | 5 +- runtime/gc/heap_verification_test.cc | 4 +- runtime/hprof/hprof.cc | 4 +- runtime/interpreter/unstarted_runtime_test.cc | 3 +- runtime/mirror/class.cc | 23 +- runtime/mirror/emulated_stack_frame.cc | 4 +- runtime/mirror/object_test.cc | 3 +- runtime/thread.cc | 4 +- 25 files changed, 460 insertions(+), 341 deletions(-) create mode 100644 runtime/class_root.cc create mode 100644 runtime/class_root.h diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index ffa000e34e..6900cd883a 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -20,6 +20,7 @@ #include "base/enums.h" #include "builder.h" #include "class_linker.h" +#include "class_root.h" #include "constant_folding.h" #include "data_type-inl.h" #include "dead_code_elimination.h" @@ -537,7 +538,7 @@ static Handle> AllocateInlineCacheHolder( Handle> inline_cache = hs->NewHandle( mirror::ObjectArray::Alloc( self, - class_linker->GetClassRoot(ClassLinker::kClassArrayClass), + GetClassRoot>(class_linker), InlineCache::kIndividualCacheSize)); if (inline_cache == nullptr) { // We got an OOME. Just clear the exception, and don't inline. @@ -777,7 +778,7 @@ HInliner::InlineCacheType HInliner::ExtractClassesFromOfflineProfile( HInstanceFieldGet* HInliner::BuildGetReceiverClass(ClassLinker* class_linker, HInstruction* receiver, uint32_t dex_pc) const { - ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0); + ArtField* field = GetClassRoot(class_linker)->GetInstanceField(0); DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_"); HInstanceFieldGet* result = new (graph_->GetAllocator()) HInstanceFieldGet( receiver, @@ -2120,9 +2121,8 @@ bool HInliner::ReturnTypeMoreSpecific(HInvoke* invoke_instruction, return true; } else if (return_replacement->IsInstanceFieldGet()) { HInstanceFieldGet* field_get = return_replacement->AsInstanceFieldGet(); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); if (field_get->GetFieldInfo().GetField() == - class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0)) { + GetClassRoot()->GetInstanceField(0)) { return true; } } diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index ca84d421a7..63704a470e 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -18,6 +18,7 @@ #include "art_method-inl.h" #include "class_linker-inl.h" +#include "class_root.h" #include "data_type-inl.h" #include "escape.h" #include "intrinsics.h" @@ -1563,8 +1564,7 @@ static bool RecognizeAndSimplifyClassCheck(HCondition* condition) { { ScopedObjectAccess soa(Thread::Current()); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0); + ArtField* field = GetClassRoot()->GetInstanceField(0); DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_"); if (field_get->GetFieldInfo().GetField() != field) { return false; diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 7f78dc257e..99b0b186d4 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -22,6 +22,7 @@ #include "base/bit_vector-inl.h" #include "base/stl_util.h" #include "class_linker-inl.h" +#include "class_root.h" #include "code_generator.h" #include "common_dominator.h" #include "intrinsics.h" @@ -40,9 +41,8 @@ static constexpr bool kEnableFloatingPointStaticEvaluation = (FLT_EVAL_METHOD == void HGraph::InitializeInexactObjectRTI(VariableSizedHandleScope* handles) { ScopedObjectAccess soa(Thread::Current()); // Create the inexact Object reference type and store it in the HGraph. - ClassLinker* linker = Runtime::Current()->GetClassLinker(); inexact_object_rti_ = ReferenceTypeInfo::Create( - handles->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangObject)), + handles->NewHandle(GetClassRoot()), /* is_exact */ false); } diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index c4977decd9..79ac6b9b9d 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -31,6 +31,7 @@ #include "base/scoped_arena_allocator.h" #include "base/timing_logger.h" #include "builder.h" +#include "class_root.h" #include "code_generator.h" #include "compiled_method.h" #include "compiler.h" @@ -1309,13 +1310,12 @@ bool OptimizingCompiler::JitCompile(Thread* self, size_t method_info_size = 0; codegen->ComputeStackMapAndMethodInfoSize(&stack_map_size, &method_info_size); size_t number_of_roots = codegen->GetNumberOfJitRoots(); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); // We allocate an object array to ensure the JIT roots that we will collect in EmitJitRoots // will be visible by the GC between EmitLiterals and CommitCode. Once CommitCode is // executed, this array is not needed. Handle> roots( hs.NewHandle(mirror::ObjectArray::Alloc( - self, class_linker->GetClassRoot(ClassLinker::kObjectArrayClass), number_of_roots))); + self, GetClassRoot>(), number_of_roots))); if (roots == nullptr) { // Out of memory, just clear the exception to avoid any Java exception uncaught problems. MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kJitOutOfMemoryForCommit); diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index ecfa790b91..f3fe62561f 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -22,6 +22,7 @@ #include "base/scoped_arena_containers.h" #include "base/enums.h" #include "class_linker-inl.h" +#include "class_root.h" #include "handle_scope-inl.h" #include "mirror/class-inl.h" #include "mirror/dex_cache.h" @@ -40,43 +41,40 @@ static inline ObjPtr FindDexCacheWithHint( } static inline ReferenceTypeInfo::TypeHandle GetRootHandle(VariableSizedHandleScope* handles, - ClassLinker::ClassRoot class_root, + ClassRoot class_root, ReferenceTypeInfo::TypeHandle* cache) { if (!ReferenceTypeInfo::IsValidHandle(*cache)) { // Mutator lock is required for NewHandle. - ClassLinker* linker = Runtime::Current()->GetClassLinker(); ScopedObjectAccess soa(Thread::Current()); - *cache = handles->NewHandle(linker->GetClassRoot(class_root)); + *cache = handles->NewHandle(GetClassRoot(class_root)); } return *cache; } ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetObjectClassHandle() { - return GetRootHandle(handles_, ClassLinker::kJavaLangObject, &object_class_handle_); + return GetRootHandle(handles_, ClassRoot::kJavaLangObject, &object_class_handle_); } ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetClassClassHandle() { - return GetRootHandle(handles_, ClassLinker::kJavaLangClass, &class_class_handle_); + return GetRootHandle(handles_, ClassRoot::kJavaLangClass, &class_class_handle_); } ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetMethodHandleClassHandle() { return GetRootHandle(handles_, - ClassLinker::kJavaLangInvokeMethodHandleImpl, + ClassRoot::kJavaLangInvokeMethodHandleImpl, &method_handle_class_handle_); } ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetMethodTypeClassHandle() { - return GetRootHandle(handles_, - ClassLinker::kJavaLangInvokeMethodType, - &method_type_class_handle_); + return GetRootHandle(handles_, ClassRoot::kJavaLangInvokeMethodType, &method_type_class_handle_); } ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetStringClassHandle() { - return GetRootHandle(handles_, ClassLinker::kJavaLangString, &string_class_handle_); + return GetRootHandle(handles_, ClassRoot::kJavaLangString, &string_class_handle_); } ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetThrowableClassHandle() { - return GetRootHandle(handles_, ClassLinker::kJavaLangThrowable, &throwable_class_handle_); + return GetRootHandle(handles_, ClassRoot::kJavaLangThrowable, &throwable_class_handle_); } class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { @@ -341,8 +339,7 @@ static void BoundTypeForClassCheck(HInstruction* check) { { ScopedObjectAccess soa(Thread::Current()); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0); + ArtField* field = GetClassRoot()->GetInstanceField(0); DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_"); if (field_get->GetFieldInfo().GetField() != field) { return; diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 01726af1d6..058724570d 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -33,6 +33,7 @@ #include "base/logging.h" // For VLOG. #include "base/unix_file/fd_file.h" #include "class_linker-inl.h" +#include "class_root.h" #include "compiled_method.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_types.h" @@ -618,8 +619,7 @@ void ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t oat_index) { } } else if (object->GetClass()->IsStringClass()) { bin = Bin::kString; // Strings are almost always immutable (except for object header). - } else if (object->GetClass() == - Runtime::Current()->GetClassLinker()->GetClassRoot(ClassLinker::kJavaLangObject)) { + } else if (object->GetClass() == GetClassRoot()) { // Instance of java lang object, probably a lock object. This means it will be dirty when we // synchronize on it. bin = Bin::kMiscDirty; @@ -2409,8 +2409,7 @@ void ImageWriter::FixupObject(Object* orig, Object* copy) { ArtMethod* src_method = src->GetArtMethod(); dest->SetArtMethod(GetImageMethodAddress(src_method)); } else if (!klass->IsArrayClass()) { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - if (klass == class_linker->GetClassRoot(ClassLinker::kJavaLangDexCache)) { + if (klass == GetClassRoot()) { FixupDexCache(down_cast(orig), down_cast(copy)); } else if (klass->IsClassLoaderClass()) { mirror::ClassLoader* copy_loader = down_cast(copy); diff --git a/openjdkjvmti/ti_class_definition.cc b/openjdkjvmti/ti_class_definition.cc index 1b641cd905..dce2733e7e 100644 --- a/openjdkjvmti/ti_class_definition.cc +++ b/openjdkjvmti/ti_class_definition.cc @@ -33,6 +33,7 @@ #include "base/array_slice.h" #include "class_linker-inl.h" +#include "class_root.h" #include "dex/dex_file.h" #include "fixed_up_dex_file.h" #include "handle.h" @@ -162,8 +163,7 @@ static void GetDexDataForRetransformation(art::Handle klass, << "Expected java/lang/Long but found object of type " << orig_dex->GetClass()->PrettyClass(); art::ObjPtr prim_long_class( - art::Runtime::Current()->GetClassLinker()->GetClassRoot( - art::ClassLinker::kPrimitiveLong)); + art::GetClassRoot(art::ClassRoot::kPrimitiveLong)); art::JValue val; if (!art::UnboxPrimitiveForResult(orig_dex.Get(), prim_long_class, &val)) { // This should never happen. @@ -226,8 +226,7 @@ static const art::DexFile* GetQuickenedDexFile(art::Handle k << "Expected java/lang/Long but found object of type " << orig_dex->GetClass()->PrettyClass(); art::ObjPtr prim_long_class( - art::Runtime::Current()->GetClassLinker()->GetClassRoot( - art::ClassLinker::kPrimitiveLong)); + art::GetClassRoot(art::ClassRoot::kPrimitiveLong)); art::JValue val; if (!art::UnboxPrimitiveForResult(orig_dex.Ptr(), prim_long_class, &val)) { LOG(FATAL) << "Unable to unwrap a long value!"; diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index 8a726bca14..48e2958773 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -42,6 +42,7 @@ #include "base/array_ref.h" #include "base/stringpiece.h" #include "class_linker-inl.h" +#include "class_root.h" #include "debugger.h" #include "dex/art_dex_file_loader.h" #include "dex/dex_file.h" @@ -485,7 +486,7 @@ art::mirror::DexCache* Redefiner::ClassRedefinition::CreateNewDexCache( art::ClassLinker* cl = driver_->runtime_->GetClassLinker(); art::Handle cache(hs.NewHandle( art::ObjPtr::DownCast( - cl->GetClassRoot(art::ClassLinker::kJavaLangDexCache)->AllocObject(driver_->self_)))); + art::GetClassRoot(cl)->AllocObject(driver_->self_)))); if (cache.IsNull()) { driver_->self_->AssertPendingOOMException(); return nullptr; @@ -859,12 +860,10 @@ class RedefinitionDataHolder { art::Thread* self, std::vector* redefinitions) REQUIRES_SHARED(art::Locks::mutator_lock_) : - arr_( - hs->NewHandle( - art::mirror::ObjectArray::Alloc( - self, - runtime->GetClassLinker()->GetClassRoot(art::ClassLinker::kObjectArrayClass), - redefinitions->size() * kNumSlots))), + arr_(hs->NewHandle(art::mirror::ObjectArray::Alloc( + self, + art::GetClassRoot>(runtime->GetClassLinker()), + redefinitions->size() * kNumSlots))), redefinitions_(redefinitions) {} bool IsNull() const REQUIRES_SHARED(art::Locks::mutator_lock_) { diff --git a/runtime/Android.bp b/runtime/Android.bp index 92607f51a0..9043866e73 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -40,6 +40,7 @@ libart_cc_defaults { "check_jni.cc", "class_linker.cc", "class_loader_context.cc", + "class_root.cc", "class_table.cc", "common_throws.cc", "compiler_filter.cc", diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index ae06f8f9bc..7a99d3dc5e 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -365,14 +365,6 @@ inline ArtField* ClassLinker::ResolveField(uint32_t field_idx, return resolved_field; } -inline mirror::Class* ClassLinker::GetClassRoot(ClassRoot class_root) { - DCHECK(!class_roots_.IsNull()); - mirror::ObjectArray* class_roots = class_roots_.Read(); - ObjPtr klass = class_roots->Get(class_root); - DCHECK(klass != nullptr); - return klass.Ptr(); -} - template inline void ClassLinker::VisitClassTables(const Visitor& visitor) { Thread* const self = Thread::Current(); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index b88aa5e07a..e2449f941c 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -51,6 +51,7 @@ #include "cha.h" #include "class_linker-inl.h" #include "class_loader_utils.h" +#include "class_root.h" #include "class_table-inl.h" #include "compiler_callbacks.h" #include "debug_print.h" @@ -516,29 +517,30 @@ bool ClassLinker::InitWithoutImage(std::vector> b // Create storage for root classes, save away our work so far (requires descriptors). class_roots_ = GcRoot>( - mirror::ObjectArray::Alloc(self, object_array_class.Get(), - kClassRootsMax)); + mirror::ObjectArray::Alloc(self, + object_array_class.Get(), + static_cast(ClassRoot::kMax))); CHECK(!class_roots_.IsNull()); - SetClassRoot(kJavaLangClass, java_lang_Class.Get()); - SetClassRoot(kJavaLangObject, java_lang_Object.Get()); - SetClassRoot(kClassArrayClass, class_array_class.Get()); - SetClassRoot(kObjectArrayClass, object_array_class.Get()); - SetClassRoot(kCharArrayClass, char_array_class.Get()); - SetClassRoot(kJavaLangString, java_lang_String.Get()); - SetClassRoot(kJavaLangRefReference, java_lang_ref_Reference.Get()); + SetClassRoot(ClassRoot::kJavaLangClass, java_lang_Class.Get()); + SetClassRoot(ClassRoot::kJavaLangObject, java_lang_Object.Get()); + SetClassRoot(ClassRoot::kClassArrayClass, class_array_class.Get()); + SetClassRoot(ClassRoot::kObjectArrayClass, object_array_class.Get()); + SetClassRoot(ClassRoot::kCharArrayClass, char_array_class.Get()); + SetClassRoot(ClassRoot::kJavaLangString, java_lang_String.Get()); + SetClassRoot(ClassRoot::kJavaLangRefReference, java_lang_ref_Reference.Get()); // Fill in the empty iftable. Needs to be done after the kObjectArrayClass root is set. java_lang_Object->SetIfTable(AllocIfTable(self, 0)); // Setup the primitive type classes. - SetClassRoot(kPrimitiveBoolean, CreatePrimitiveClass(self, Primitive::kPrimBoolean)); - SetClassRoot(kPrimitiveByte, CreatePrimitiveClass(self, Primitive::kPrimByte)); - SetClassRoot(kPrimitiveShort, CreatePrimitiveClass(self, Primitive::kPrimShort)); - SetClassRoot(kPrimitiveInt, CreatePrimitiveClass(self, Primitive::kPrimInt)); - SetClassRoot(kPrimitiveLong, CreatePrimitiveClass(self, Primitive::kPrimLong)); - SetClassRoot(kPrimitiveFloat, CreatePrimitiveClass(self, Primitive::kPrimFloat)); - SetClassRoot(kPrimitiveDouble, CreatePrimitiveClass(self, Primitive::kPrimDouble)); - SetClassRoot(kPrimitiveVoid, CreatePrimitiveClass(self, Primitive::kPrimVoid)); + SetClassRoot(ClassRoot::kPrimitiveBoolean, CreatePrimitiveClass(self, Primitive::kPrimBoolean)); + SetClassRoot(ClassRoot::kPrimitiveByte, CreatePrimitiveClass(self, Primitive::kPrimByte)); + SetClassRoot(ClassRoot::kPrimitiveShort, CreatePrimitiveClass(self, Primitive::kPrimShort)); + SetClassRoot(ClassRoot::kPrimitiveInt, CreatePrimitiveClass(self, Primitive::kPrimInt)); + SetClassRoot(ClassRoot::kPrimitiveLong, CreatePrimitiveClass(self, Primitive::kPrimLong)); + SetClassRoot(ClassRoot::kPrimitiveFloat, CreatePrimitiveClass(self, Primitive::kPrimFloat)); + SetClassRoot(ClassRoot::kPrimitiveDouble, CreatePrimitiveClass(self, Primitive::kPrimDouble)); + SetClassRoot(ClassRoot::kPrimitiveVoid, CreatePrimitiveClass(self, Primitive::kPrimVoid)); // Create array interface entries to populate once we can load system classes. array_iftable_ = GcRoot(AllocIfTable(self, 2)); @@ -546,23 +548,23 @@ bool ClassLinker::InitWithoutImage(std::vector> b // Create int array type for AllocDexCache (done in AppendToBootClassPath). Handle int_array_class(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_)))); - int_array_class->SetComponentType(GetClassRoot(kPrimitiveInt)); + int_array_class->SetComponentType(GetClassRoot(ClassRoot::kPrimitiveInt, this)); mirror::IntArray::SetArrayClass(int_array_class.Get()); - SetClassRoot(kIntArrayClass, int_array_class.Get()); + SetClassRoot(ClassRoot::kIntArrayClass, int_array_class.Get()); // Create long array type for AllocDexCache (done in AppendToBootClassPath). Handle long_array_class(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_)))); - long_array_class->SetComponentType(GetClassRoot(kPrimitiveLong)); + long_array_class->SetComponentType(GetClassRoot(ClassRoot::kPrimitiveLong, this)); mirror::LongArray::SetArrayClass(long_array_class.Get()); - SetClassRoot(kLongArrayClass, long_array_class.Get()); + SetClassRoot(ClassRoot::kLongArrayClass, long_array_class.Get()); // now that these are registered, we can use AllocClass() and AllocObjectArray // Set up DexCache. This cannot be done later since AppendToBootClassPath calls AllocDexCache. Handle java_lang_DexCache(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::DexCache::ClassSize(image_pointer_size_)))); - SetClassRoot(kJavaLangDexCache, java_lang_DexCache.Get()); + SetClassRoot(ClassRoot::kJavaLangDexCache, java_lang_DexCache.Get()); java_lang_DexCache->SetDexCacheClass(); java_lang_DexCache->SetObjectSize(mirror::DexCache::InstanceSize()); mirror::Class::SetStatus(java_lang_DexCache, ClassStatus::kResolved, self); @@ -571,7 +573,7 @@ bool ClassLinker::InitWithoutImage(std::vector> b // Setup dalvik.system.ClassExt Handle dalvik_system_ClassExt(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::ClassExt::ClassSize(image_pointer_size_)))); - SetClassRoot(kDalvikSystemClassExt, dalvik_system_ClassExt.Get()); + SetClassRoot(ClassRoot::kDalvikSystemClassExt, dalvik_system_ClassExt.Get()); mirror::ClassExt::SetClass(dalvik_system_ClassExt.Get()); mirror::Class::SetStatus(dalvik_system_ClassExt, ClassStatus::kResolved, self); @@ -580,7 +582,7 @@ bool ClassLinker::InitWithoutImage(std::vector> b AllocClass(self, java_lang_Class.Get(), mirror::ObjectArray::ClassSize(image_pointer_size_)))); object_array_string->SetComponentType(java_lang_String.Get()); - SetClassRoot(kJavaLangStringArrayClass, object_array_string.Get()); + SetClassRoot(ClassRoot::kJavaLangStringArrayClass, object_array_string.Get()); LinearAlloc* linear_alloc = runtime->GetLinearAlloc(); // Create runtime resolution and imt conflict methods. @@ -608,7 +610,7 @@ bool ClassLinker::InitWithoutImage(std::vector> b // run char class through InitializePrimitiveClass to finish init InitializePrimitiveClass(char_class.Get(), Primitive::kPrimChar); - SetClassRoot(kPrimitiveChar, char_class.Get()); // needs descriptor + SetClassRoot(ClassRoot::kPrimitiveChar, char_class.Get()); // needs descriptor // Set up GenericJNI entrypoint. That is mainly a hack for common_compiler_test.h so that // we do not need friend classes or a publicly exposed setter. @@ -634,25 +636,25 @@ bool ClassLinker::InitWithoutImage(std::vector> b CHECK_EQ(dalvik_system_ClassExt->GetObjectSize(), mirror::ClassExt::InstanceSize()); // Setup the primitive array type classes - can't be done until Object has a vtable. - SetClassRoot(kBooleanArrayClass, FindSystemClass(self, "[Z")); - mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass)); + SetClassRoot(ClassRoot::kBooleanArrayClass, FindSystemClass(self, "[Z")); + mirror::BooleanArray::SetArrayClass(GetClassRoot(ClassRoot::kBooleanArrayClass, this)); - SetClassRoot(kByteArrayClass, FindSystemClass(self, "[B")); - mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass)); + SetClassRoot(ClassRoot::kByteArrayClass, FindSystemClass(self, "[B")); + mirror::ByteArray::SetArrayClass(GetClassRoot(ClassRoot::kByteArrayClass, this)); CheckSystemClass(self, char_array_class, "[C"); - SetClassRoot(kShortArrayClass, FindSystemClass(self, "[S")); - mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass)); + SetClassRoot(ClassRoot::kShortArrayClass, FindSystemClass(self, "[S")); + mirror::ShortArray::SetArrayClass(GetClassRoot(ClassRoot::kShortArrayClass, this)); CheckSystemClass(self, int_array_class, "[I"); CheckSystemClass(self, long_array_class, "[J"); - SetClassRoot(kFloatArrayClass, FindSystemClass(self, "[F")); - mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass)); + SetClassRoot(ClassRoot::kFloatArrayClass, FindSystemClass(self, "[F")); + mirror::FloatArray::SetArrayClass(GetClassRoot(ClassRoot::kFloatArrayClass, this)); - SetClassRoot(kDoubleArrayClass, FindSystemClass(self, "[D")); - mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass)); + SetClassRoot(ClassRoot::kDoubleArrayClass, FindSystemClass(self, "[D")); + mirror::DoubleArray::SetArrayClass(GetClassRoot(ClassRoot::kDoubleArrayClass, this)); // Run Class through FindSystemClass. This initializes the dex_cache_ fields and register it // in class_table_. @@ -683,102 +685,103 @@ bool ClassLinker::InitWithoutImage(std::vector> b mirror::Class::GetDirectInterface(self, object_array_class.Get(), 1)); CHECK_EQ(object_array_string.Get(), - FindSystemClass(self, GetClassRootDescriptor(kJavaLangStringArrayClass))); + FindSystemClass(self, GetClassRootDescriptor(ClassRoot::kJavaLangStringArrayClass))); // End of special init trickery, all subsequent classes may be loaded via FindSystemClass. // Create java.lang.reflect.Proxy root. - SetClassRoot(kJavaLangReflectProxy, FindSystemClass(self, "Ljava/lang/reflect/Proxy;")); + SetClassRoot(ClassRoot::kJavaLangReflectProxy, + FindSystemClass(self, "Ljava/lang/reflect/Proxy;")); // Create java.lang.reflect.Field.class root. auto* class_root = FindSystemClass(self, "Ljava/lang/reflect/Field;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangReflectField, class_root); + SetClassRoot(ClassRoot::kJavaLangReflectField, class_root); mirror::Field::SetClass(class_root); // Create java.lang.reflect.Field array root. class_root = FindSystemClass(self, "[Ljava/lang/reflect/Field;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangReflectFieldArrayClass, class_root); + SetClassRoot(ClassRoot::kJavaLangReflectFieldArrayClass, class_root); mirror::Field::SetArrayClass(class_root); // Create java.lang.reflect.Constructor.class root and array root. class_root = FindSystemClass(self, "Ljava/lang/reflect/Constructor;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangReflectConstructor, class_root); + SetClassRoot(ClassRoot::kJavaLangReflectConstructor, class_root); mirror::Constructor::SetClass(class_root); class_root = FindSystemClass(self, "[Ljava/lang/reflect/Constructor;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangReflectConstructorArrayClass, class_root); + SetClassRoot(ClassRoot::kJavaLangReflectConstructorArrayClass, class_root); mirror::Constructor::SetArrayClass(class_root); // Create java.lang.reflect.Method.class root and array root. class_root = FindSystemClass(self, "Ljava/lang/reflect/Method;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangReflectMethod, class_root); + SetClassRoot(ClassRoot::kJavaLangReflectMethod, class_root); mirror::Method::SetClass(class_root); class_root = FindSystemClass(self, "[Ljava/lang/reflect/Method;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangReflectMethodArrayClass, class_root); + SetClassRoot(ClassRoot::kJavaLangReflectMethodArrayClass, class_root); mirror::Method::SetArrayClass(class_root); // Create java.lang.invoke.CallSite.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/CallSite;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangInvokeCallSite, class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeCallSite, class_root); mirror::CallSite::SetClass(class_root); // Create java.lang.invoke.MethodType.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodType;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangInvokeMethodType, class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeMethodType, class_root); mirror::MethodType::SetClass(class_root); // Create java.lang.invoke.MethodHandleImpl.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodHandleImpl;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangInvokeMethodHandleImpl, class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeMethodHandleImpl, class_root); mirror::MethodHandleImpl::SetClass(class_root); // Create java.lang.invoke.MethodHandles.Lookup.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodHandles$Lookup;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangInvokeMethodHandlesLookup, class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeMethodHandlesLookup, class_root); mirror::MethodHandlesLookup::SetClass(class_root); // Create java.lang.invoke.VarHandle.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/VarHandle;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangInvokeVarHandle, class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeVarHandle, class_root); mirror::VarHandle::SetClass(class_root); // Create java.lang.invoke.FieldVarHandle.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/FieldVarHandle;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangInvokeFieldVarHandle, class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeFieldVarHandle, class_root); mirror::FieldVarHandle::SetClass(class_root); // Create java.lang.invoke.ArrayElementVarHandle.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/ArrayElementVarHandle;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangInvokeArrayElementVarHandle, class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeArrayElementVarHandle, class_root); mirror::ArrayElementVarHandle::SetClass(class_root); // Create java.lang.invoke.ByteArrayViewVarHandle.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/ByteArrayViewVarHandle;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangInvokeByteArrayViewVarHandle, class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeByteArrayViewVarHandle, class_root); mirror::ByteArrayViewVarHandle::SetClass(class_root); // Create java.lang.invoke.ByteBufferViewVarHandle.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/ByteBufferViewVarHandle;"); CHECK(class_root != nullptr); - SetClassRoot(kJavaLangInvokeByteBufferViewVarHandle, class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeByteBufferViewVarHandle, class_root); mirror::ByteBufferViewVarHandle::SetClass(class_root); class_root = FindSystemClass(self, "Ldalvik/system/EmulatedStackFrame;"); CHECK(class_root != nullptr); - SetClassRoot(kDalvikSystemEmulatedStackFrame, class_root); + SetClassRoot(ClassRoot::kDalvikSystemEmulatedStackFrame, class_root); mirror::EmulatedStackFrame::SetClass(class_root); // java.lang.ref classes need to be specially flagged, but otherwise are normal classes @@ -805,18 +808,19 @@ bool ClassLinker::InitWithoutImage(std::vector> b class_root = FindSystemClass(self, "Ljava/lang/ClassLoader;"); class_root->SetClassLoaderClass(); CHECK_EQ(class_root->GetObjectSize(), mirror::ClassLoader::InstanceSize()); - SetClassRoot(kJavaLangClassLoader, class_root); + SetClassRoot(ClassRoot::kJavaLangClassLoader, class_root); // Set up java.lang.Throwable, java.lang.ClassNotFoundException, and // java.lang.StackTraceElement as a convenience. - SetClassRoot(kJavaLangThrowable, FindSystemClass(self, "Ljava/lang/Throwable;")); - mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable)); - SetClassRoot(kJavaLangClassNotFoundException, + SetClassRoot(ClassRoot::kJavaLangThrowable, FindSystemClass(self, "Ljava/lang/Throwable;")); + mirror::Throwable::SetClass(GetClassRoot(ClassRoot::kJavaLangThrowable, this)); + SetClassRoot(ClassRoot::kJavaLangClassNotFoundException, FindSystemClass(self, "Ljava/lang/ClassNotFoundException;")); - SetClassRoot(kJavaLangStackTraceElement, FindSystemClass(self, "Ljava/lang/StackTraceElement;")); - SetClassRoot(kJavaLangStackTraceElementArrayClass, + SetClassRoot(ClassRoot::kJavaLangStackTraceElement, + FindSystemClass(self, "Ljava/lang/StackTraceElement;")); + SetClassRoot(ClassRoot::kJavaLangStackTraceElementArrayClass, FindSystemClass(self, "[Ljava/lang/StackTraceElement;")); - mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement)); + mirror::StackTraceElement::SetClass(GetClassRoot(ClassRoot::kJavaLangStackTraceElement, this)); // Create conflict tables that depend on the class linker. runtime->FixupConflictTables(); @@ -834,8 +838,7 @@ static void CreateStringInitBindings(Thread* self, ClassLinker* class_linker) ObjPtr string_factory_class = class_linker->FindSystemClass(self, "Ljava/lang/StringFactory;"); CHECK(string_factory_class != nullptr); - ObjPtr string_class = - class_linker->GetClassRoot(ClassLinker::ClassRoot::kJavaLangString); + ObjPtr string_class = GetClassRoot(class_linker); WellKnownClasses::InitStringInit(string_class, string_factory_class); // Update the primordial thread. self->InitStringEntryPoints(); @@ -851,7 +854,8 @@ void ClassLinker::FinishInit(Thread* self) { // as the types of the field can't be resolved prior to the runtime being // fully initialized StackHandleScope<2> hs(self); - Handle java_lang_ref_Reference = hs.NewHandle(GetClassRoot(kJavaLangRefReference)); + Handle java_lang_ref_Reference = + hs.NewHandle(GetClassRoot(this)); Handle java_lang_ref_FinalizerReference = hs.NewHandle(FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;")); @@ -876,7 +880,7 @@ void ClassLinker::FinishInit(Thread* self) { CHECK_STREQ(zombie->GetTypeDescriptor(), "Ljava/lang/Object;"); // ensure all class_roots_ are initialized - for (size_t i = 0; i < kClassRootsMax; i++) { + for (size_t i = 0; i < static_cast(ClassRoot::kMax); i++) { ClassRoot class_root = static_cast(i); ObjPtr klass = GetClassRoot(class_root); CHECK(klass != nullptr); @@ -896,11 +900,11 @@ void ClassLinker::FinishInit(Thread* self) { void ClassLinker::RunRootClinits() { Thread* self = Thread::Current(); - for (size_t i = 0; i < ClassLinker::kClassRootsMax; ++i) { - ObjPtr c = GetClassRoot(ClassRoot(i)); + for (size_t i = 0; i < static_cast(ClassRoot::kMax); ++i) { + ObjPtr c = GetClassRoot(ClassRoot(i), this); if (!c->IsArrayClass() && !c->IsPrimitive()) { StackHandleScope<1> hs(self); - Handle h_class(hs.NewHandle(GetClassRoot(ClassRoot(i)))); + Handle h_class(hs.NewHandle(c)); EnsureInitialized(self, h_class, true, true); self->AssertNoPendingException(); } @@ -1034,13 +1038,13 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { class_roots_ = GcRoot>( down_cast*>( spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots))); - mirror::Class::SetClassClass(class_roots_.Read()->Get(kJavaLangClass)); + mirror::Class::SetClassClass(GetClassRoot(ClassRoot::kJavaLangClass, this)); // Special case of setting up the String class early so that we can test arbitrary objects // as being Strings or not - mirror::String::SetClass(GetClassRoot(kJavaLangString)); + mirror::String::SetClass(GetClassRoot(this)); - ObjPtr java_lang_Object = GetClassRoot(kJavaLangObject); + ObjPtr java_lang_Object = GetClassRoot(this); java_lang_Object->SetObjectSize(sizeof(mirror::Object)); // Allocate in non-movable so that it's possible to check if a JNI weak global ref has been // cleared without triggering the read barrier and unintentionally mark the sentinel alive. @@ -1048,37 +1052,48 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { self, java_lang_Object, java_lang_Object->GetObjectSize(), VoidFunctor())); // reinit array_iftable_ from any array class instance, they should be == - array_iftable_ = GcRoot(GetClassRoot(kObjectArrayClass)->GetIfTable()); - DCHECK_EQ(array_iftable_.Read(), GetClassRoot(kBooleanArrayClass)->GetIfTable()); + array_iftable_ = + GcRoot(GetClassRoot(ClassRoot::kObjectArrayClass, this)->GetIfTable()); + DCHECK_EQ(array_iftable_.Read(), GetClassRoot(ClassRoot::kBooleanArrayClass, this)->GetIfTable()); // String class root was set above - mirror::Field::SetClass(GetClassRoot(kJavaLangReflectField)); - mirror::Field::SetArrayClass(GetClassRoot(kJavaLangReflectFieldArrayClass)); - mirror::Constructor::SetClass(GetClassRoot(kJavaLangReflectConstructor)); - mirror::Constructor::SetArrayClass(GetClassRoot(kJavaLangReflectConstructorArrayClass)); - mirror::Method::SetClass(GetClassRoot(kJavaLangReflectMethod)); - mirror::Method::SetArrayClass(GetClassRoot(kJavaLangReflectMethodArrayClass)); - mirror::CallSite::SetClass(GetClassRoot(kJavaLangInvokeCallSite)); - mirror::MethodHandleImpl::SetClass(GetClassRoot(kJavaLangInvokeMethodHandleImpl)); - mirror::MethodHandlesLookup::SetClass(GetClassRoot(kJavaLangInvokeMethodHandlesLookup)); - mirror::MethodType::SetClass(GetClassRoot(kJavaLangInvokeMethodType)); - mirror::VarHandle::SetClass(GetClassRoot(kJavaLangInvokeVarHandle)); - mirror::FieldVarHandle::SetClass(GetClassRoot(kJavaLangInvokeFieldVarHandle)); - mirror::ArrayElementVarHandle::SetClass(GetClassRoot(kJavaLangInvokeArrayElementVarHandle)); - mirror::ByteArrayViewVarHandle::SetClass(GetClassRoot(kJavaLangInvokeByteArrayViewVarHandle)); - mirror::ByteBufferViewVarHandle::SetClass(GetClassRoot(kJavaLangInvokeByteBufferViewVarHandle)); - mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference)); - mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass)); - mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass)); - mirror::CharArray::SetArrayClass(GetClassRoot(kCharArrayClass)); - mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass)); - mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass)); - mirror::IntArray::SetArrayClass(GetClassRoot(kIntArrayClass)); - mirror::LongArray::SetArrayClass(GetClassRoot(kLongArrayClass)); - mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass)); - mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable)); - mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement)); - mirror::EmulatedStackFrame::SetClass(GetClassRoot(kDalvikSystemEmulatedStackFrame)); - mirror::ClassExt::SetClass(GetClassRoot(kDalvikSystemClassExt)); + mirror::Field::SetClass(GetClassRoot(ClassRoot::kJavaLangReflectField, this)); + mirror::Field::SetArrayClass(GetClassRoot(ClassRoot::kJavaLangReflectFieldArrayClass, this)); + mirror::Constructor::SetClass(GetClassRoot(ClassRoot::kJavaLangReflectConstructor, this).Ptr()); + mirror::Constructor::SetArrayClass( + GetClassRoot(ClassRoot::kJavaLangReflectConstructorArrayClass, this).Ptr()); + mirror::Method::SetClass(GetClassRoot(ClassRoot::kJavaLangReflectMethod, this).Ptr()); + mirror::Method::SetArrayClass( + GetClassRoot(ClassRoot::kJavaLangReflectMethodArrayClass, this).Ptr()); + mirror::CallSite::SetClass(GetClassRoot(ClassRoot::kJavaLangInvokeCallSite, this).Ptr()); + mirror::MethodHandleImpl::SetClass( + GetClassRoot(ClassRoot::kJavaLangInvokeMethodHandleImpl, this).Ptr()); + mirror::MethodHandlesLookup::SetClass( + GetClassRoot(ClassRoot::kJavaLangInvokeMethodHandlesLookup, this).Ptr()); + mirror::MethodType::SetClass( + GetClassRoot(ClassRoot::kJavaLangInvokeMethodType, this).Ptr()); + mirror::VarHandle::SetClass(GetClassRoot(ClassRoot::kJavaLangInvokeVarHandle, this).Ptr()); + mirror::FieldVarHandle::SetClass( + GetClassRoot(ClassRoot::kJavaLangInvokeFieldVarHandle, this).Ptr()); + mirror::ArrayElementVarHandle::SetClass( + GetClassRoot(ClassRoot::kJavaLangInvokeArrayElementVarHandle, this).Ptr()); + mirror::ByteArrayViewVarHandle::SetClass( + GetClassRoot(ClassRoot::kJavaLangInvokeByteArrayViewVarHandle, this).Ptr()); + mirror::ByteBufferViewVarHandle::SetClass( + GetClassRoot(ClassRoot::kJavaLangInvokeByteBufferViewVarHandle, this).Ptr()); + mirror::Reference::SetClass(GetClassRoot(ClassRoot::kJavaLangRefReference, this)); + mirror::BooleanArray::SetArrayClass(GetClassRoot(ClassRoot::kBooleanArrayClass, this)); + mirror::ByteArray::SetArrayClass(GetClassRoot(ClassRoot::kByteArrayClass, this)); + mirror::CharArray::SetArrayClass(GetClassRoot(ClassRoot::kCharArrayClass, this)); + mirror::DoubleArray::SetArrayClass(GetClassRoot(ClassRoot::kDoubleArrayClass, this)); + mirror::FloatArray::SetArrayClass(GetClassRoot(ClassRoot::kFloatArrayClass, this)); + mirror::IntArray::SetArrayClass(GetClassRoot(ClassRoot::kIntArrayClass, this)); + mirror::LongArray::SetArrayClass(GetClassRoot(ClassRoot::kLongArrayClass, this)); + mirror::ShortArray::SetArrayClass(GetClassRoot(ClassRoot::kShortArrayClass, this)); + mirror::Throwable::SetClass(GetClassRoot(ClassRoot::kJavaLangThrowable, this)); + mirror::StackTraceElement::SetClass(GetClassRoot(ClassRoot::kJavaLangStackTraceElement, this)); + mirror::EmulatedStackFrame::SetClass( + GetClassRoot(ClassRoot::kDalvikSystemEmulatedStackFrame, this).Ptr()); + mirror::ClassExt::SetClass(GetClassRoot(ClassRoot::kDalvikSystemClassExt, this)); for (gc::space::ImageSpace* image_space : spaces) { // Boot class loader, use a null handle. @@ -1695,15 +1710,16 @@ bool ClassLinker::AddImageSpace( MutableHandle image_class_loader(hs.NewHandle( app_image ? header.GetImageRoot(ImageHeader::kClassLoader)->AsClassLoader() : nullptr)); DCHECK(class_roots != nullptr); - if (class_roots->GetLength() != static_cast(kClassRootsMax)) { + if (class_roots->GetLength() != static_cast(ClassRoot::kMax)) { *error_msg = StringPrintf("Expected %d class roots but got %d", class_roots->GetLength(), - static_cast(kClassRootsMax)); + static_cast(ClassRoot::kMax)); return false; } // Check against existing class roots to make sure they match the ones in the boot image. - for (size_t i = 0; i < kClassRootsMax; i++) { - if (class_roots->Get(i) != GetClassRoot(static_cast(i))) { + ObjPtr> existing_class_roots = GetClassRoots(); + for (size_t i = 0; i < static_cast(ClassRoot::kMax); i++) { + if (class_roots->Get(i) != GetClassRoot(static_cast(i), existing_class_roots)) { *error_msg = "App image class roots must have pointer equality with runtime ones."; return false; } @@ -2231,7 +2247,7 @@ mirror::DexCache* ClassLinker::AllocDexCache(ObjPtr* out_locatio StackHandleScope<1> hs(self); DCHECK(out_location != nullptr); auto dex_cache(hs.NewHandle(ObjPtr::DownCast( - GetClassRoot(kJavaLangDexCache)->AllocObject(self)))); + GetClassRoot(this)->AllocObject(self)))); if (dex_cache == nullptr) { self->AssertPendingOOMException(); return nullptr; @@ -2280,14 +2296,14 @@ mirror::Class* ClassLinker::AllocClass(Thread* self, } mirror::Class* ClassLinker::AllocClass(Thread* self, uint32_t class_size) { - return AllocClass(self, GetClassRoot(kJavaLangClass), class_size); + return AllocClass(self, GetClassRoot(this), class_size); } mirror::ObjectArray* ClassLinker::AllocStackTraceElementArray( Thread* self, size_t length) { return mirror::ObjectArray::Alloc( - self, GetClassRoot(kJavaLangStackTraceElementArrayClass), length); + self, GetClassRoot>(this), length); } mirror::Class* ClassLinker::EnsureResolved(Thread* self, @@ -2684,17 +2700,17 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, if (UNLIKELY(!init_done_)) { // finish up init of hand crafted class_roots_ if (strcmp(descriptor, "Ljava/lang/Object;") == 0) { - klass.Assign(GetClassRoot(kJavaLangObject)); + klass.Assign(GetClassRoot(this)); } else if (strcmp(descriptor, "Ljava/lang/Class;") == 0) { - klass.Assign(GetClassRoot(kJavaLangClass)); + klass.Assign(GetClassRoot(this)); } else if (strcmp(descriptor, "Ljava/lang/String;") == 0) { - klass.Assign(GetClassRoot(kJavaLangString)); + klass.Assign(GetClassRoot(this)); } else if (strcmp(descriptor, "Ljava/lang/ref/Reference;") == 0) { - klass.Assign(GetClassRoot(kJavaLangRefReference)); + klass.Assign(GetClassRoot(this)); } else if (strcmp(descriptor, "Ljava/lang/DexCache;") == 0) { - klass.Assign(GetClassRoot(kJavaLangDexCache)); + klass.Assign(GetClassRoot(this)); } else if (strcmp(descriptor, "Ldalvik/system/ClassExt;") == 0) { - klass.Assign(GetClassRoot(kDalvikSystemClassExt)); + klass.Assign(GetClassRoot(this)); } } @@ -2744,7 +2760,7 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, ObjectLock lock(self, klass); klass->SetClinitThreadId(self->GetTid()); // Make sure we have a valid empty iftable even if there are errors. - klass->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable()); + klass->SetIfTable(GetClassRoot(this)->GetIfTable()); // Add the newly loaded class to the loaded classes table. ObjPtr existing = InsertClass(descriptor, klass.Get(), hash); @@ -3084,7 +3100,7 @@ void ClassLinker::SetupClass(const DexFile& dex_file, const char* descriptor = dex_file.GetClassDescriptor(dex_class_def); CHECK(descriptor != nullptr); - klass->SetClass(GetClassRoot(kJavaLangClass)); + klass->SetClass(GetClassRoot(this)); uint32_t access_flags = dex_class_def.GetJavaAccessFlags(); CHECK_EQ(access_flags & ~kAccJavaFlagsMask, 0U); klass->SetAccessFlags(access_flags); @@ -3654,7 +3670,7 @@ mirror::Class* ClassLinker::InitializePrimitiveClass(ObjPtr primi ObjectLock lock(self, h_class); h_class->SetAccessFlags(kAccPublic | kAccFinal | kAccAbstract); h_class->SetPrimitiveType(type); - h_class->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable()); + h_class->SetIfTable(GetClassRoot(this)->GetIfTable()); mirror::Class::SetStatus(h_class, ClassStatus::kInitialized, self); const char* descriptor = Primitive::Descriptor(type); ObjPtr existing = InsertClass(descriptor, @@ -3737,17 +3753,17 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto if (UNLIKELY(!init_done_)) { // Classes that were hand created, ie not by FindSystemClass if (strcmp(descriptor, "[Ljava/lang/Class;") == 0) { - new_class.Assign(GetClassRoot(kClassArrayClass)); + new_class.Assign(GetClassRoot>(this)); } else if (strcmp(descriptor, "[Ljava/lang/Object;") == 0) { - new_class.Assign(GetClassRoot(kObjectArrayClass)); - } else if (strcmp(descriptor, GetClassRootDescriptor(kJavaLangStringArrayClass)) == 0) { - new_class.Assign(GetClassRoot(kJavaLangStringArrayClass)); + new_class.Assign(GetClassRoot>(this)); + } else if (strcmp(descriptor, "[Ljava/lang/String;") == 0) { + new_class.Assign(GetClassRoot>(this)); } else if (strcmp(descriptor, "[C") == 0) { - new_class.Assign(GetClassRoot(kCharArrayClass)); + new_class.Assign(GetClassRoot(this)); } else if (strcmp(descriptor, "[I") == 0) { - new_class.Assign(GetClassRoot(kIntArrayClass)); + new_class.Assign(GetClassRoot(this)); } else if (strcmp(descriptor, "[J") == 0) { - new_class.Assign(GetClassRoot(kLongArrayClass)); + new_class.Assign(GetClassRoot(this)); } } if (new_class == nullptr) { @@ -3760,7 +3776,7 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto } ObjectLock lock(self, new_class); // Must hold lock on object when initializing. DCHECK(new_class->GetComponentType() != nullptr); - ObjPtr java_lang_Object = GetClassRoot(kJavaLangObject); + ObjPtr java_lang_Object = GetClassRoot(this); new_class->SetSuperClass(java_lang_Object); new_class->SetVTable(java_lang_Object->GetVTable()); new_class->SetPrimitiveType(Primitive::kPrimNot); @@ -3828,25 +3844,26 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto } mirror::Class* ClassLinker::FindPrimitiveClass(char type) { + ObjPtr> class_roots = GetClassRoots(); switch (type) { case 'B': - return GetClassRoot(kPrimitiveByte); + return GetClassRoot(ClassRoot::kPrimitiveByte, class_roots).Ptr(); case 'C': - return GetClassRoot(kPrimitiveChar); + return GetClassRoot(ClassRoot::kPrimitiveChar, class_roots).Ptr(); case 'D': - return GetClassRoot(kPrimitiveDouble); + return GetClassRoot(ClassRoot::kPrimitiveDouble, class_roots).Ptr(); case 'F': - return GetClassRoot(kPrimitiveFloat); + return GetClassRoot(ClassRoot::kPrimitiveFloat, class_roots).Ptr(); case 'I': - return GetClassRoot(kPrimitiveInt); + return GetClassRoot(ClassRoot::kPrimitiveInt, class_roots).Ptr(); case 'J': - return GetClassRoot(kPrimitiveLong); + return GetClassRoot(ClassRoot::kPrimitiveLong, class_roots).Ptr(); case 'S': - return GetClassRoot(kPrimitiveShort); + return GetClassRoot(ClassRoot::kPrimitiveShort, class_roots).Ptr(); case 'Z': - return GetClassRoot(kPrimitiveBoolean); + return GetClassRoot(ClassRoot::kPrimitiveBoolean, class_roots).Ptr(); case 'V': - return GetClassRoot(kPrimitiveVoid); + return GetClassRoot(ClassRoot::kPrimitiveVoid, class_roots).Ptr(); default: break; } @@ -4381,7 +4398,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& Thread* self = soa.Self(); StackHandleScope<10> hs(self); MutableHandle temp_klass(hs.NewHandle( - AllocClass(self, GetClassRoot(kJavaLangClass), sizeof(mirror::Class)))); + AllocClass(self, GetClassRoot(this), sizeof(mirror::Class)))); if (temp_klass == nullptr) { CHECK(self->IsExceptionPending()); // OOME. return nullptr; @@ -4394,9 +4411,9 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& temp_klass->SetClassLoader(soa.Decode(loader)); DCHECK_EQ(temp_klass->GetPrimitiveType(), Primitive::kPrimNot); temp_klass->SetName(soa.Decode(name)); - temp_klass->SetDexCache(GetClassRoot(kJavaLangReflectProxy)->GetDexCache()); + temp_klass->SetDexCache(GetClassRoot(this)->GetDexCache()); // Object has an empty iftable, copy it for that reason. - temp_klass->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable()); + temp_klass->SetIfTable(GetClassRoot(this)->GetIfTable()); mirror::Class::SetStatus(temp_klass, ClassStatus::kIdx, self); std::string descriptor(GetDescriptorForProxy(temp_klass.Get())); const size_t hash = ComputeModifiedUtf8Hash(descriptor.c_str()); @@ -4463,7 +4480,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& } // The super class is java.lang.reflect.Proxy - temp_klass->SetSuperClass(GetClassRoot(kJavaLangReflectProxy)); + temp_klass->SetSuperClass(GetClassRoot(this)); // Now effectively in the loaded state. mirror::Class::SetStatus(temp_klass, ClassStatus::kLoaded, self); self->AssertNoPendingException(); @@ -4551,11 +4568,12 @@ std::string ClassLinker::GetDescriptorForProxy(ObjPtr proxy_class void ClassLinker::CreateProxyConstructor(Handle klass, ArtMethod* out) { // Create constructor for Proxy that must initialize the method. - CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 21u); + ObjPtr proxy_class = GetClassRoot(this); + CHECK_EQ(proxy_class->NumDirectMethods(), 21u); // Find the (InvocationHandler)V method. The exact method offset varies depending // on which front-end compiler was used to build the libcore DEX files. - ArtMethod* proxy_constructor = GetClassRoot(kJavaLangReflectProxy)->FindConstructor( + ArtMethod* proxy_constructor = proxy_class->FindConstructor( "(Ljava/lang/reflect/InvocationHandler;)V", image_pointer_size_); DCHECK(proxy_constructor != nullptr) << "Could not find method in java.lang.reflect.Proxy"; @@ -5553,7 +5571,8 @@ bool ClassLinker::LoadSuperAndInterfaces(Handle klass, const DexF bool ClassLinker::LinkSuperClass(Handle klass) { CHECK(!klass->IsPrimitive()); ObjPtr super = klass->GetSuperClass(); - if (klass.Get() == GetClassRoot(kJavaLangObject)) { + ObjPtr object_class = GetClassRoot(this); + if (klass.Get() == object_class) { if (super != nullptr) { ThrowClassFormatError(klass.Get(), "java.lang.Object must not have a superclass"); return false; @@ -5566,7 +5585,7 @@ bool ClassLinker::LinkSuperClass(Handle klass) { return false; } // Verify - if (klass->IsInterface() && super != GetClassRoot(kJavaLangObject)) { + if (klass->IsInterface() && super != object_class) { ThrowClassFormatError(klass.Get(), "Interfaces must have java.lang.Object as superclass"); return false; } @@ -5609,7 +5628,7 @@ bool ClassLinker::LinkSuperClass(Handle klass) { klass->SetClassFlags(klass->GetClassFlags() | reference_flags); } // Disallow custom direct subclasses of java.lang.ref.Reference. - if (init_done_ && super == GetClassRoot(kJavaLangRefReference)) { + if (init_done_ && super == GetClassRoot(this)) { ThrowLinkageError(klass.Get(), "Class %s attempts to subclass java.lang.ref.Reference, which is not allowed", klass->PrettyDescriptor().c_str()); @@ -5974,7 +5993,7 @@ bool ClassLinker::LinkVirtualMethods( } klass->SetVTable(vtable.Get()); } else { - CHECK_EQ(klass.Get(), GetClassRoot(kJavaLangObject)); + CHECK_EQ(klass.Get(), GetClassRoot(this)); if (!IsUint<16>(num_virtual_methods)) { ThrowClassFormatError(klass.Get(), "Too many methods: %d", static_cast(num_virtual_methods)); @@ -7851,7 +7870,7 @@ ObjPtr ClassLinker::DoResolveType(dex::TypeIndex type_idx, // Convert a ClassNotFoundException to a NoClassDefFoundError. StackHandleScope<1> hs(self); Handle cause(hs.NewHandle(self->GetException())); - if (cause->InstanceOf(GetClassRoot(kJavaLangClassNotFoundException))) { + if (cause->InstanceOf(GetClassRoot(ClassRoot::kJavaLangClassNotFoundException, this))) { DCHECK(resolved == nullptr); // No Handle needed to preserve resolved. self->ClearException(); ThrowNoClassDefFoundError("Failed resolution of: %s", descriptor); @@ -8678,67 +8697,10 @@ void ClassLinker::SetClassRoot(ClassRoot class_root, ObjPtr klass mirror::ObjectArray* class_roots = class_roots_.Read(); DCHECK(class_roots != nullptr); - DCHECK(class_roots->Get(class_root) == nullptr); - class_roots->Set(class_root, klass); -} - -const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) { - static const char* class_roots_descriptors[] = { - "Ljava/lang/Class;", - "Ljava/lang/Object;", - "[Ljava/lang/Class;", - "[Ljava/lang/Object;", - "Ljava/lang/String;", - "Ljava/lang/DexCache;", - "Ljava/lang/ref/Reference;", - "Ljava/lang/reflect/Constructor;", - "Ljava/lang/reflect/Field;", - "Ljava/lang/reflect/Method;", - "Ljava/lang/reflect/Proxy;", - "[Ljava/lang/String;", - "[Ljava/lang/reflect/Constructor;", - "[Ljava/lang/reflect/Field;", - "[Ljava/lang/reflect/Method;", - "Ljava/lang/invoke/CallSite;", - "Ljava/lang/invoke/MethodHandleImpl;", - "Ljava/lang/invoke/MethodHandles$Lookup;", - "Ljava/lang/invoke/MethodType;", - "Ljava/lang/invoke/VarHandle;", - "Ljava/lang/invoke/FieldVarHandle;", - "Ljava/lang/invoke/ArrayElementVarHandle;", - "Ljava/lang/invoke/ByteArrayViewVarHandle;", - "Ljava/lang/invoke/ByteBufferViewVarHandle;", - "Ljava/lang/ClassLoader;", - "Ljava/lang/Throwable;", - "Ljava/lang/ClassNotFoundException;", - "Ljava/lang/StackTraceElement;", - "Ldalvik/system/EmulatedStackFrame;", - "Z", - "B", - "C", - "D", - "F", - "I", - "J", - "S", - "V", - "[Z", - "[B", - "[C", - "[D", - "[F", - "[I", - "[J", - "[S", - "[Ljava/lang/StackTraceElement;", - "Ldalvik/system/ClassExt;", - }; - static_assert(arraysize(class_roots_descriptors) == size_t(kClassRootsMax), - "Mismatch between class descriptors and class-root enum"); - - const char* descriptor = class_roots_descriptors[class_root]; - CHECK(descriptor != nullptr); - return descriptor; + DCHECK_LT(static_cast(class_root), static_cast(ClassRoot::kMax)); + int32_t index = static_cast(class_root); + DCHECK(class_roots->Get(index) == nullptr); + class_roots->Set(index, klass); } jobject ClassLinker::CreateWellKnownClassLoader(Thread* self, @@ -9071,7 +9033,7 @@ mirror::Class* ClassLinker::GetHoldingClassOfCopiedMethod(ArtMethod* method) { mirror::IfTable* ClassLinker::AllocIfTable(Thread* self, size_t ifcount) { return down_cast( mirror::IfTable::Alloc(self, - GetClassRoot(kObjectArrayClass), + GetClassRoot>(this), ifcount * mirror::IfTable::kMax)); } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 52ecf82c86..afe5c99990 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -68,6 +68,7 @@ using MethodDexCacheType = std::atomic; } // namespace mirror class ClassHierarchyAnalysis; +enum class ClassRoot : uint32_t; class ClassTable; template class Handle; class ImtConflictTable; @@ -107,59 +108,6 @@ class AllocatorVisitor { class ClassLinker { public: - // Well known mirror::Class roots accessed via GetClassRoot. - enum ClassRoot { - kJavaLangClass, - kJavaLangObject, - kClassArrayClass, - kObjectArrayClass, - kJavaLangString, - kJavaLangDexCache, - kJavaLangRefReference, - kJavaLangReflectConstructor, - kJavaLangReflectField, - kJavaLangReflectMethod, - kJavaLangReflectProxy, - kJavaLangStringArrayClass, - kJavaLangReflectConstructorArrayClass, - kJavaLangReflectFieldArrayClass, - kJavaLangReflectMethodArrayClass, - kJavaLangInvokeCallSite, - kJavaLangInvokeMethodHandleImpl, - kJavaLangInvokeMethodHandlesLookup, - kJavaLangInvokeMethodType, - kJavaLangInvokeVarHandle, - kJavaLangInvokeFieldVarHandle, - kJavaLangInvokeArrayElementVarHandle, - kJavaLangInvokeByteArrayViewVarHandle, - kJavaLangInvokeByteBufferViewVarHandle, - kJavaLangClassLoader, - kJavaLangThrowable, - kJavaLangClassNotFoundException, - kJavaLangStackTraceElement, - kDalvikSystemEmulatedStackFrame, - kPrimitiveBoolean, - kPrimitiveByte, - kPrimitiveChar, - kPrimitiveDouble, - kPrimitiveFloat, - kPrimitiveInt, - kPrimitiveLong, - kPrimitiveShort, - kPrimitiveVoid, - kBooleanArrayClass, - kByteArrayClass, - kCharArrayClass, - kDoubleArrayClass, - kFloatArrayClass, - kIntArrayClass, - kLongArrayClass, - kShortArrayClass, - kJavaLangStackTraceElementArrayClass, - kDalvikSystemClassExt, - kClassRootsMax, - }; - static constexpr bool kAppImageMayContainStrings = false; explicit ClassLinker(InternTable* intern_table); @@ -552,10 +500,6 @@ class ClassLinker { pid_t GetClassesLockOwner(); // For SignalCatcher. pid_t GetDexLockOwner(); // For SignalCatcher. - mirror::Class* GetClassRoot(ClassRoot class_root) REQUIRES_SHARED(Locks::mutator_lock_); - - static const char* GetClassRootDescriptor(ClassRoot class_root); - // Is the given entry point quick code to run the resolution stub? bool IsQuickResolutionStub(const void* entry_point) const; @@ -597,8 +541,9 @@ class ClassLinker { REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + template mirror::ObjectArray* GetClassRoots() REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::ObjectArray* class_roots = class_roots_.Read(); + mirror::ObjectArray* class_roots = class_roots_.Read(); DCHECK(class_roots != nullptr); return class_roots; } diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 6ed029ceb1..48ec6b6c27 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -25,6 +25,7 @@ #include "art_method-inl.h" #include "base/enums.h" #include "class_linker-inl.h" +#include "class_root.h" #include "common_runtime_test.h" #include "dex/dex_file_types.h" #include "dex/standard_dex_file.h" @@ -1387,11 +1388,10 @@ TEST_F(ClassLinkerTest, FinalizableBit) { TEST_F(ClassLinkerTest, ClassRootDescriptors) { ScopedObjectAccess soa(Thread::Current()); std::string temp; - for (int i = 0; i < ClassLinker::kClassRootsMax; i++) { - ObjPtr klass = class_linker_->GetClassRoot(ClassLinker::ClassRoot(i)); + for (size_t i = 0; i < static_cast(ClassRoot::kMax); i++) { + ObjPtr klass = GetClassRoot(ClassRoot(i), class_linker_); EXPECT_GT(strlen(klass->GetDescriptor(&temp)), 0U); - EXPECT_STREQ(klass->GetDescriptor(&temp), - class_linker_->GetClassRootDescriptor(ClassLinker::ClassRoot(i))) << " i = " << i; + EXPECT_STREQ(klass->GetDescriptor(&temp), GetClassRootDescriptor(ClassRoot(i))) << " i = " << i; } } diff --git a/runtime/class_root.cc b/runtime/class_root.cc new file mode 100644 index 0000000000..08820b0c61 --- /dev/null +++ b/runtime/class_root.cc @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#include "class_root.h" + +namespace art { + +const char* GetClassRootDescriptor(ClassRoot class_root) { + static const char* class_roots_descriptors[] = { +#define CLASS_ROOT_DESCRIPTOR(name, descriptor, mirror_type) descriptor, + CLASS_ROOT_LIST(CLASS_ROOT_DESCRIPTOR) +#undef CLASS_ROOT_DESCRIPTOR + }; + static_assert(arraysize(class_roots_descriptors) == static_cast(ClassRoot::kMax), + "Mismatch between class descriptors and class-root enum"); + + DCHECK_LT(static_cast(class_root), static_cast(ClassRoot::kMax)); + const char* descriptor = class_roots_descriptors[static_cast(class_root)]; + CHECK(descriptor != nullptr); + return descriptor; +} + +} // namespace art diff --git a/runtime/class_root.h b/runtime/class_root.h new file mode 100644 index 0000000000..f43e2c6920 --- /dev/null +++ b/runtime/class_root.h @@ -0,0 +1,183 @@ +/* + * 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. + */ + +#ifndef ART_RUNTIME_CLASS_ROOT_H_ +#define ART_RUNTIME_CLASS_ROOT_H_ + +#include "class_linker.h" +#include "mirror/class.h" +#include "mirror/object_array-inl.h" +#include "obj_ptr-inl.h" +#include "runtime.h" + +namespace art { + +namespace mirror { +class ArrayElementVarHandle; +class ByteArrayViewVarHandle; +class ByteBufferViewVarHandle; +class CallSite; +class ClassExt; +class ClassLoader; +class Constructor; +class DexCache; +class EmulatedStackFrame; +class Field; +class FieldVarHandle; +class Method; +class MethodHandleImpl; +class MethodHandlesLookup; +class MethodType; +class Object; +class Proxy; +template class PrimitiveArray; +class Reference; +class StackTraceElement; +class String; +class Throwable; +class VarHandle; +} // namespace mirror + +#define CLASS_ROOT_LIST(M) \ + M(kJavaLangClass, "Ljava/lang/Class;", mirror::Class) \ + M(kJavaLangObject, "Ljava/lang/Object;", mirror::Object) \ + M(kClassArrayClass, "[Ljava/lang/Class;", mirror::ObjectArray) \ + M(kObjectArrayClass, "[Ljava/lang/Object;", mirror::ObjectArray) \ + M(kJavaLangString, "Ljava/lang/String;", mirror::String) \ + M(kJavaLangDexCache, "Ljava/lang/DexCache;", mirror::DexCache) \ + M(kJavaLangRefReference, "Ljava/lang/ref/Reference;", mirror::Reference) \ + M(kJavaLangReflectConstructor, "Ljava/lang/reflect/Constructor;", mirror::Constructor) \ + M(kJavaLangReflectField, "Ljava/lang/reflect/Field;", mirror::Field) \ + M(kJavaLangReflectMethod, "Ljava/lang/reflect/Method;", mirror::Method) \ + M(kJavaLangReflectProxy, "Ljava/lang/reflect/Proxy;", mirror::Proxy) \ + M(kJavaLangStringArrayClass, "[Ljava/lang/String;", mirror::ObjectArray) \ + M(kJavaLangReflectConstructorArrayClass, "[Ljava/lang/reflect/Constructor;", mirror::ObjectArray) \ + M(kJavaLangReflectFieldArrayClass, "[Ljava/lang/reflect/Field;", mirror::ObjectArray) \ + M(kJavaLangReflectMethodArrayClass, "[Ljava/lang/reflect/Method;", mirror::ObjectArray) \ + M(kJavaLangInvokeCallSite, "Ljava/lang/invoke/CallSite;", mirror::CallSite) \ + M(kJavaLangInvokeMethodHandleImpl, "Ljava/lang/invoke/MethodHandleImpl;", mirror::MethodHandleImpl) \ + M(kJavaLangInvokeMethodHandlesLookup, "Ljava/lang/invoke/MethodHandles$Lookup;", mirror::MethodHandlesLookup) \ + M(kJavaLangInvokeMethodType, "Ljava/lang/invoke/MethodType;", mirror::MethodType) \ + M(kJavaLangInvokeVarHandle, "Ljava/lang/invoke/VarHandle;", mirror::VarHandle) \ + M(kJavaLangInvokeFieldVarHandle, "Ljava/lang/invoke/FieldVarHandle;", mirror::FieldVarHandle) \ + M(kJavaLangInvokeArrayElementVarHandle, "Ljava/lang/invoke/ArrayElementVarHandle;", mirror::ArrayElementVarHandle) \ + M(kJavaLangInvokeByteArrayViewVarHandle, "Ljava/lang/invoke/ByteArrayViewVarHandle;", mirror::ByteArrayViewVarHandle) \ + M(kJavaLangInvokeByteBufferViewVarHandle, "Ljava/lang/invoke/ByteBufferViewVarHandle;", mirror::ByteBufferViewVarHandle) \ + M(kJavaLangClassLoader, "Ljava/lang/ClassLoader;", mirror::ClassLoader) \ + M(kJavaLangThrowable, "Ljava/lang/Throwable;", mirror::Throwable) \ + M(kJavaLangClassNotFoundException, "Ljava/lang/ClassNotFoundException;", detail::NoMirrorType) \ + M(kJavaLangStackTraceElement, "Ljava/lang/StackTraceElement;", mirror::StackTraceElement) \ + M(kDalvikSystemEmulatedStackFrame, "Ldalvik/system/EmulatedStackFrame;", mirror::EmulatedStackFrame) \ + M(kPrimitiveBoolean, "Z", detail::NoMirrorType) \ + M(kPrimitiveByte, "B", detail::NoMirrorType) \ + M(kPrimitiveChar, "C", detail::NoMirrorType) \ + M(kPrimitiveDouble, "D", detail::NoMirrorType) \ + M(kPrimitiveFloat, "F", detail::NoMirrorType) \ + M(kPrimitiveInt, "I", detail::NoMirrorType) \ + M(kPrimitiveLong, "J", detail::NoMirrorType) \ + M(kPrimitiveShort, "S", detail::NoMirrorType) \ + M(kPrimitiveVoid, "V", detail::NoMirrorType) \ + M(kBooleanArrayClass, "[Z", mirror::PrimitiveArray) \ + M(kByteArrayClass, "[B", mirror::PrimitiveArray) \ + M(kCharArrayClass, "[C", mirror::PrimitiveArray) \ + M(kDoubleArrayClass, "[D", mirror::PrimitiveArray) \ + M(kFloatArrayClass, "[F", mirror::PrimitiveArray) \ + M(kIntArrayClass, "[I", mirror::PrimitiveArray) \ + M(kLongArrayClass, "[J", mirror::PrimitiveArray) \ + M(kShortArrayClass, "[S", mirror::PrimitiveArray) \ + M(kJavaLangStackTraceElementArrayClass, "[Ljava/lang/StackTraceElement;", mirror::ObjectArray) \ + M(kDalvikSystemClassExt, "Ldalvik/system/ClassExt;", mirror::ClassExt) + +// Well known mirror::Class roots accessed via ClassLinker::GetClassRoots(). +enum class ClassRoot : uint32_t { +#define CLASS_ROOT_ENUMERATOR(name, descriptor, mirror_type) name, + CLASS_ROOT_LIST(CLASS_ROOT_ENUMERATOR) +#undef CLASS_ROOT_ENUMERATOR + kMax, +}; + +const char* GetClassRootDescriptor(ClassRoot class_root); + +template +inline ObjPtr GetClassRoot( + ClassRoot class_root, + ObjPtr> class_roots) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(class_roots != nullptr); + if (kReadBarrierOption == kWithReadBarrier) { + // With read barrier all references must point to the to-space. + // Without read barrier, this check could fail. + DCHECK_EQ(class_roots, Runtime::Current()->GetClassLinker()->GetClassRoots()); + } + DCHECK_LT(static_cast(class_root), static_cast(ClassRoot::kMax)); + int32_t index = static_cast(class_root); + ObjPtr klass = + class_roots->GetWithoutChecks(index); + DCHECK(klass != nullptr); + return klass.Ptr(); +} + +template +inline ObjPtr GetClassRoot(ClassRoot class_root, ClassLinker* linker) + REQUIRES_SHARED(Locks::mutator_lock_) { + return GetClassRoot(class_root, linker->GetClassRoots()); +} + +template +inline ObjPtr GetClassRoot(ClassRoot class_root) + REQUIRES_SHARED(Locks::mutator_lock_) { + return GetClassRoot(class_root, Runtime::Current()->GetClassLinker()); +} + +namespace detail { + +class ClassNotFoundExceptionTag; +template struct NoMirrorType; + +template +struct ClassRootSelector; // No definition for unspecialized ClassRoot selector. + +#define SPECIALIZE_CLASS_ROOT_SELECTOR(name, descriptor, mirror_type) \ + template <> \ + struct ClassRootSelector { \ + static constexpr ClassRoot value = ClassRoot::name; \ + }; + +CLASS_ROOT_LIST(SPECIALIZE_CLASS_ROOT_SELECTOR) + +#undef SPECIALIZE_CLASS_ROOT_SELECTOR + +} // namespace detail + +template +inline ObjPtr GetClassRoot(ObjPtr> class_roots) + REQUIRES_SHARED(Locks::mutator_lock_) { + return GetClassRoot(detail::ClassRootSelector::value, class_roots); +} + +template +inline ObjPtr GetClassRoot(ClassLinker* linker) + REQUIRES_SHARED(Locks::mutator_lock_) { + return GetClassRoot(detail::ClassRootSelector::value, linker); +} + +template +inline ObjPtr GetClassRoot() REQUIRES_SHARED(Locks::mutator_lock_) { + return GetClassRoot(detail::ClassRootSelector::value); +} + +} // namespace art + +#endif // ART_RUNTIME_CLASS_ROOT_H_ diff --git a/runtime/entrypoints/quick/callee_save_frame.h b/runtime/entrypoints/quick/callee_save_frame.h index 6f1bbaa093..1e309071f6 100644 --- a/runtime/entrypoints/quick/callee_save_frame.h +++ b/runtime/entrypoints/quick/callee_save_frame.h @@ -68,7 +68,7 @@ class ScopedQuickEntrypointChecks { bool exit_check_; }; -namespace detail_ { +namespace detail { template struct CSFSelector; // No definition for unspecialized callee save frame selector. @@ -87,9 +87,9 @@ struct CSFSelector { using type = x86::X86CalleeSaveFrame; template <> struct CSFSelector { using type = x86_64::X86_64CalleeSaveFrame; }; -} // namespace detail_ +} // namespace detail -using RuntimeCalleeSaveFrame = detail_::CSFSelector::type; +using RuntimeCalleeSaveFrame = detail::CSFSelector::type; } // namespace art diff --git a/runtime/gc/accounting/mod_union_table_test.cc b/runtime/gc/accounting/mod_union_table_test.cc index e5b8ea5609..d59ff71676 100644 --- a/runtime/gc/accounting/mod_union_table_test.cc +++ b/runtime/gc/accounting/mod_union_table_test.cc @@ -17,6 +17,7 @@ #include "mod_union_table-inl.h" #include "class_linker-inl.h" +#include "class_root.h" #include "common_runtime_test.h" #include "gc/space/space-inl.h" #include "mirror/array-inl.h" @@ -70,8 +71,7 @@ class ModUnionTableTest : public CommonRuntimeTest { mirror::Class* GetObjectArrayClass(Thread* self, space::ContinuousMemMapAllocSpace* space) REQUIRES_SHARED(Locks::mutator_lock_) { if (java_lang_object_array_ == nullptr) { - java_lang_object_array_ = - Runtime::Current()->GetClassLinker()->GetClassRoot(ClassLinker::kObjectArrayClass); + java_lang_object_array_ = GetClassRoot>().Ptr(); // Since the test doesn't have an image, the class of the object array keeps cards live // inside the card cache mod-union table and causes the check // ASSERT_FALSE(table->ContainsCardFor(reinterpret_cast(obj3))); diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index 0747c3c77b..2c2c437365 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -23,6 +23,7 @@ #include "base/quasi_atomic.h" #include "base/stl_util.h" #include "base/systrace.h" +#include "class_root.h" #include "debugger.h" #include "gc/accounting/atomic_stack.h" #include "gc/accounting/heap_bitmap-inl.h" @@ -2244,7 +2245,7 @@ void ConcurrentCopying::FillWithDummyObject(mirror::Object* dummy_obj, size_t by // Avoid going through read barrier for since kDisallowReadBarrierDuringScan may be enabled. // Explicitly mark to make sure to get an object in the to-space. mirror::Class* int_array_class = down_cast( - Mark(mirror::IntArray::GetArrayClass())); + Mark(GetClassRoot().Ptr())); CHECK(int_array_class != nullptr); if (ReadBarrier::kEnableToSpaceInvariantChecks) { AssertToSpaceInvariant(nullptr, MemberOffset(0), int_array_class); @@ -2324,7 +2325,7 @@ mirror::Object* ConcurrentCopying::AllocateInSkippedBlock(size_t alloc_size) { CHECK_GE(byte_size - alloc_size, min_object_size); // FillWithDummyObject may mark an object, avoid holding skipped_blocks_lock_ to prevent lock // violation and possible deadlock. The deadlock case is a recursive case: - // FillWithDummyObject -> IntArray::GetArrayClass -> Mark -> Copy -> AllocateInSkippedBlock. + // FillWithDummyObject -> Mark(IntArray.class) -> Copy -> AllocateInSkippedBlock. FillWithDummyObject(reinterpret_cast(addr + alloc_size), byte_size - alloc_size); CHECK(region_space_->IsInToSpace(reinterpret_cast(addr + alloc_size))); diff --git a/runtime/gc/heap_verification_test.cc b/runtime/gc/heap_verification_test.cc index 40ee86ce79..4f06ee6910 100644 --- a/runtime/gc/heap_verification_test.cc +++ b/runtime/gc/heap_verification_test.cc @@ -18,6 +18,7 @@ #include "base/memory_tool.h" #include "class_linker-inl.h" +#include "class_root.h" #include "handle_scope-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" @@ -36,10 +37,9 @@ class VerificationTest : public CommonRuntimeTest { template mirror::ObjectArray* AllocObjectArray(Thread* self, size_t length) REQUIRES_SHARED(Locks::mutator_lock_) { - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); return mirror::ObjectArray::Alloc( self, - class_linker->GetClassRoot(ClassLinker::ClassRoot::kObjectArrayClass), + GetClassRoot>(), length); } }; diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index 3b6487449b..7bd5a6a68a 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -50,6 +50,7 @@ #include "base/time_utils.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" +#include "class_root.h" #include "common_throws.h" #include "debugger.h" #include "dex/dex_file-inl.h" @@ -1418,8 +1419,7 @@ void Hprof::DumpFakeObjectArray(mirror::Object* obj, const std::setGetClassLinker()->GetClassRoot(ClassLinker::kObjectArrayClass))); + __ AddClassId(LookupClassId(GetClassRoot>().Ptr())); for (mirror::Object* e : elements) { __ AddObjectId(e); } diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc index 860de2c28b..98fe8b271b 100644 --- a/runtime/interpreter/unstarted_runtime_test.cc +++ b/runtime/interpreter/unstarted_runtime_test.cc @@ -23,6 +23,7 @@ #include "base/enums.h" #include "base/memory_tool.h" #include "class_linker.h" +#include "class_root.h" #include "common_runtime_test.h" #include "dex/descriptors_names.h" #include "dex/dex_instruction.h" @@ -1370,7 +1371,7 @@ TEST_F(UnstartedRuntimeTest, ConstructorNewInstance0) { Handle> args = hs.NewHandle( mirror::ObjectArray::Alloc( - self, class_linker_->GetClassRoot(ClassLinker::ClassRoot::kObjectArrayClass), 1)); + self, GetClassRoot>(class_linker_), 1)); ASSERT_TRUE(args != nullptr); args->Set(0, input.Get()); diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 3f4e841f86..ad6b37b86a 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -26,6 +26,7 @@ #include "class_ext.h" #include "class_linker-inl.h" #include "class_loader.h" +#include "class_root.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_annotations.h" @@ -75,26 +76,26 @@ void Class::VisitRoots(RootVisitor* visitor) { ObjPtr Class::GetPrimitiveClass(ObjPtr name) { const char* expected_name = nullptr; - ClassLinker::ClassRoot class_root = ClassLinker::kJavaLangObject; // Invalid. + ClassRoot class_root = ClassRoot::kJavaLangObject; // Invalid. if (name != nullptr && name->GetLength() >= 2) { // Perfect hash for the expected values: from the second letters of the primitive types, // only 'y' has the bit 0x10 set, so use it to change 'b' to 'B'. char hash = name->CharAt(0) ^ ((name->CharAt(1) & 0x10) << 1); switch (hash) { - case 'b': expected_name = "boolean"; class_root = ClassLinker::kPrimitiveBoolean; break; - case 'B': expected_name = "byte"; class_root = ClassLinker::kPrimitiveByte; break; - case 'c': expected_name = "char"; class_root = ClassLinker::kPrimitiveChar; break; - case 'd': expected_name = "double"; class_root = ClassLinker::kPrimitiveDouble; break; - case 'f': expected_name = "float"; class_root = ClassLinker::kPrimitiveFloat; break; - case 'i': expected_name = "int"; class_root = ClassLinker::kPrimitiveInt; break; - case 'l': expected_name = "long"; class_root = ClassLinker::kPrimitiveLong; break; - case 's': expected_name = "short"; class_root = ClassLinker::kPrimitiveShort; break; - case 'v': expected_name = "void"; class_root = ClassLinker::kPrimitiveVoid; break; + case 'b': expected_name = "boolean"; class_root = ClassRoot::kPrimitiveBoolean; break; + case 'B': expected_name = "byte"; class_root = ClassRoot::kPrimitiveByte; break; + case 'c': expected_name = "char"; class_root = ClassRoot::kPrimitiveChar; break; + case 'd': expected_name = "double"; class_root = ClassRoot::kPrimitiveDouble; break; + case 'f': expected_name = "float"; class_root = ClassRoot::kPrimitiveFloat; break; + case 'i': expected_name = "int"; class_root = ClassRoot::kPrimitiveInt; break; + case 'l': expected_name = "long"; class_root = ClassRoot::kPrimitiveLong; break; + case 's': expected_name = "short"; class_root = ClassRoot::kPrimitiveShort; break; + case 'v': expected_name = "void"; class_root = ClassRoot::kPrimitiveVoid; break; default: break; } } if (expected_name != nullptr && name->Equals(expected_name)) { - ObjPtr klass = Runtime::Current()->GetClassLinker()->GetClassRoot(class_root); + ObjPtr klass = GetClassRoot(class_root); DCHECK(klass != nullptr); return klass; } else { diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc index 5f00c6e9c7..527408b3e5 100644 --- a/runtime/mirror/emulated_stack_frame.cc +++ b/runtime/mirror/emulated_stack_frame.cc @@ -17,6 +17,7 @@ #include "emulated_stack_frame.h" #include "class-inl.h" +#include "class_root.h" #include "gc_root-inl.h" #include "jvalue-inl.h" #include "method_handles-inl.h" @@ -166,8 +167,7 @@ mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs( CalculateFrameAndReferencesSize(to_types.Get(), r_type.Get(), &frame_size, &refs_size); // Step 3 : Allocate the arrays. - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ObjPtr array_class(class_linker->GetClassRoot(ClassLinker::kObjectArrayClass)); + ObjPtr array_class(GetClassRoot>()); Handle> references(hs.NewHandle( mirror::ObjectArray::Alloc(self, array_class, refs_size))); diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index 69ba4b98cf..8b7a1b6876 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -28,6 +28,7 @@ #include "class-inl.h" #include "class_linker-inl.h" #include "class_linker.h" +#include "class_root.h" #include "common_runtime_test.h" #include "dex/dex_file.h" #include "entrypoints/entrypoint_utils-inl.h" @@ -78,7 +79,7 @@ class ObjectTest : public CommonRuntimeTest { mirror::ObjectArray* AllocObjectArray(Thread* self, size_t length) REQUIRES_SHARED(Locks::mutator_lock_) { return mirror::ObjectArray::Alloc( - self, class_linker_->GetClassRoot(ClassLinker::ClassRoot::kObjectArrayClass), length); + self, GetClassRoot(ClassRoot::kObjectArrayClass, class_linker_), length); } }; diff --git a/runtime/thread.cc b/runtime/thread.cc index 5a80929350..129bae6d9a 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -47,6 +47,7 @@ #include "base/to_str.h" #include "base/utils.h" #include "class_linker-inl.h" +#include "class_root.h" #include "debugger.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" @@ -2510,7 +2511,8 @@ class BuildInternalStackTraceVisitor : public StackVisitor { // class of the ArtMethod pointers. ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); StackHandleScope<1> hs(self_); - ObjPtr array_class = class_linker->GetClassRoot(ClassLinker::kObjectArrayClass); + ObjPtr array_class = + GetClassRoot>(class_linker); // The first element is the methods and dex pc array, the other elements are declaring classes // for the methods to ensure classes in the stack trace don't get unloaded. Handle> trace( -- GitLab From 0278be74269fcfe4f2517d449f2bd53472f9b2f9 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 24 May 2018 13:30:24 +0100 Subject: [PATCH 462/749] Remove PrimitiveArray::array_class_. And ObjPtr<>-ify jni_internal.cc while we're touching it. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Bug: 31113334 Change-Id: I15a8ade772e3e4337371c6f3c472f9efea9f4636 --- runtime/class_linker.cc | 24 ---------- runtime/dex/dex_file_annotations.cc | 3 +- runtime/jni/jni_internal.cc | 74 ++++++++++++++++------------- runtime/mirror/array-inl.h | 22 --------- runtime/mirror/array.cc | 14 ++++-- runtime/mirror/array.h | 17 ------- runtime/runtime.cc | 9 ---- 7 files changed, 53 insertions(+), 110 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index e2449f941c..1c75fba9fd 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -499,7 +499,6 @@ bool ClassLinker::InitWithoutImage(std::vector> b Handle char_array_class(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_)))); char_array_class->SetComponentType(char_class.Get()); - mirror::CharArray::SetArrayClass(char_array_class.Get()); // Setup String. Handle java_lang_String(hs.NewHandle( @@ -549,14 +548,12 @@ bool ClassLinker::InitWithoutImage(std::vector> b Handle int_array_class(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_)))); int_array_class->SetComponentType(GetClassRoot(ClassRoot::kPrimitiveInt, this)); - mirror::IntArray::SetArrayClass(int_array_class.Get()); SetClassRoot(ClassRoot::kIntArrayClass, int_array_class.Get()); // Create long array type for AllocDexCache (done in AppendToBootClassPath). Handle long_array_class(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_)))); long_array_class->SetComponentType(GetClassRoot(ClassRoot::kPrimitiveLong, this)); - mirror::LongArray::SetArrayClass(long_array_class.Get()); SetClassRoot(ClassRoot::kLongArrayClass, long_array_class.Get()); // now that these are registered, we can use AllocClass() and AllocObjectArray @@ -637,24 +634,19 @@ bool ClassLinker::InitWithoutImage(std::vector> b // Setup the primitive array type classes - can't be done until Object has a vtable. SetClassRoot(ClassRoot::kBooleanArrayClass, FindSystemClass(self, "[Z")); - mirror::BooleanArray::SetArrayClass(GetClassRoot(ClassRoot::kBooleanArrayClass, this)); SetClassRoot(ClassRoot::kByteArrayClass, FindSystemClass(self, "[B")); - mirror::ByteArray::SetArrayClass(GetClassRoot(ClassRoot::kByteArrayClass, this)); CheckSystemClass(self, char_array_class, "[C"); SetClassRoot(ClassRoot::kShortArrayClass, FindSystemClass(self, "[S")); - mirror::ShortArray::SetArrayClass(GetClassRoot(ClassRoot::kShortArrayClass, this)); CheckSystemClass(self, int_array_class, "[I"); CheckSystemClass(self, long_array_class, "[J"); SetClassRoot(ClassRoot::kFloatArrayClass, FindSystemClass(self, "[F")); - mirror::FloatArray::SetArrayClass(GetClassRoot(ClassRoot::kFloatArrayClass, this)); SetClassRoot(ClassRoot::kDoubleArrayClass, FindSystemClass(self, "[D")); - mirror::DoubleArray::SetArrayClass(GetClassRoot(ClassRoot::kDoubleArrayClass, this)); // Run Class through FindSystemClass. This initializes the dex_cache_ fields and register it // in class_table_. @@ -1081,14 +1073,6 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { mirror::ByteBufferViewVarHandle::SetClass( GetClassRoot(ClassRoot::kJavaLangInvokeByteBufferViewVarHandle, this).Ptr()); mirror::Reference::SetClass(GetClassRoot(ClassRoot::kJavaLangRefReference, this)); - mirror::BooleanArray::SetArrayClass(GetClassRoot(ClassRoot::kBooleanArrayClass, this)); - mirror::ByteArray::SetArrayClass(GetClassRoot(ClassRoot::kByteArrayClass, this)); - mirror::CharArray::SetArrayClass(GetClassRoot(ClassRoot::kCharArrayClass, this)); - mirror::DoubleArray::SetArrayClass(GetClassRoot(ClassRoot::kDoubleArrayClass, this)); - mirror::FloatArray::SetArrayClass(GetClassRoot(ClassRoot::kFloatArrayClass, this)); - mirror::IntArray::SetArrayClass(GetClassRoot(ClassRoot::kIntArrayClass, this)); - mirror::LongArray::SetArrayClass(GetClassRoot(ClassRoot::kLongArrayClass, this)); - mirror::ShortArray::SetArrayClass(GetClassRoot(ClassRoot::kShortArrayClass, this)); mirror::Throwable::SetClass(GetClassRoot(ClassRoot::kJavaLangThrowable, this)); mirror::StackTraceElement::SetClass(GetClassRoot(ClassRoot::kJavaLangStackTraceElement, this)); mirror::EmulatedStackFrame::SetClass( @@ -2179,17 +2163,9 @@ ClassLinker::~ClassLinker() { mirror::StackTraceElement::ResetClass(); mirror::String::ResetClass(); mirror::Throwable::ResetClass(); - mirror::BooleanArray::ResetArrayClass(); - mirror::ByteArray::ResetArrayClass(); - mirror::CharArray::ResetArrayClass(); mirror::Constructor::ResetArrayClass(); - mirror::DoubleArray::ResetArrayClass(); mirror::Field::ResetArrayClass(); - mirror::FloatArray::ResetArrayClass(); mirror::Method::ResetArrayClass(); - mirror::IntArray::ResetArrayClass(); - mirror::LongArray::ResetArrayClass(); - mirror::ShortArray::ResetArrayClass(); mirror::CallSite::ResetClass(); mirror::MethodType::ResetClass(); mirror::MethodHandleImpl::ResetClass(); diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc index 95b42d27d2..ffa0a9065a 100644 --- a/runtime/dex/dex_file_annotations.cc +++ b/runtime/dex/dex_file_annotations.cc @@ -23,6 +23,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" #include "class_linker-inl.h" +#include "class_root.h" #include "dex/dex_file-inl.h" #include "jni/jni_internal.h" #include "jvalue-inl.h" @@ -1211,7 +1212,7 @@ bool GetParametersMetadataForMethod(ArtMethod* method, } // Extract the parameters' access flags int[]. - Handle int_array_class(hs.NewHandle(mirror::IntArray::GetArrayClass())); + Handle int_array_class(hs.NewHandle(GetClassRoot())); if (UNLIKELY(int_array_class == nullptr)) { return false; } diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc index cd66a60376..cf6751782b 100644 --- a/runtime/jni/jni_internal.cc +++ b/runtime/jni/jni_internal.cc @@ -33,6 +33,7 @@ #include "base/safe_map.h" #include "base/stl_util.h" #include "class_linker-inl.h" +#include "class_root.h" #include "dex/dex_file-inl.h" #include "dex/utf.h" #include "fault_handler.h" @@ -305,7 +306,7 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con return nullptr; } ArtField* field = nullptr; - mirror::Class* field_type; + ObjPtr field_type; ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); if (sig[1] != '\0') { Handle class_loader(hs.NewHandle(c->GetClassLoader())); @@ -346,8 +347,11 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con return jni::EncodeArtField(field); } -static void ThrowAIOOBE(ScopedObjectAccess& soa, mirror::Array* array, jsize start, - jsize length, const char* identifier) +static void ThrowAIOOBE(ScopedObjectAccess& soa, + ObjPtr array, + jsize start, + jsize length, + const char* identifier) REQUIRES_SHARED(Locks::mutator_lock_) { std::string type(array->PrettyTypeOf()); soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;", @@ -434,7 +438,9 @@ static JavaVMExt* JavaVmExtFromEnv(JNIEnv* env) { } template -static ArtMethod* FindMethod(mirror::Class* c, const StringPiece& name, const StringPiece& sig) +static ArtMethod* FindMethod(ObjPtr c, + const StringPiece& name, + const StringPiece& sig) REQUIRES_SHARED(Locks::mutator_lock_) { auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); for (auto& method : c->GetMethods(pointer_size)) { @@ -462,7 +468,7 @@ class JNI { ClassLinker* class_linker = runtime->GetClassLinker(); std::string descriptor(NormalizeJniClassDescriptor(name)); ScopedObjectAccess soa(env); - mirror::Class* c = nullptr; + ObjPtr c = nullptr; if (runtime->IsStarted()) { StackHandleScope<1> hs(soa.Self()); Handle class_loader(hs.NewHandle(GetClassLoader(soa))); @@ -608,7 +614,7 @@ class JNI { static jthrowable ExceptionOccurred(JNIEnv* env) { ScopedObjectAccess soa(env); - mirror::Object* exception = soa.Self()->GetException(); + ObjPtr exception = soa.Self()->GetException(); return soa.AddLocalReference(exception); } @@ -1979,7 +1985,7 @@ class JNI { ObjPtr> array = soa.Decode>(java_array); ObjPtr value = soa.Decode(java_value); - array->Set(index, value.Ptr()); + array->Set(index, value); } static jbooleanArray NewBooleanArray(JNIEnv* env, jsize length) { @@ -2022,7 +2028,7 @@ class JNI { ScopedObjectAccess soa(env); ObjPtr array_class; { - ObjPtr element_class = soa.Decode(element_jclass).Ptr(); + ObjPtr element_class = soa.Decode(element_jclass); if (UNLIKELY(element_class->IsPrimitive())) { soa.Vm()->JniAbortF("NewObjectArray", "not an object type: %s", @@ -2042,7 +2048,7 @@ class JNI { if (result != nullptr && initial_element != nullptr) { ObjPtr initial_object = soa.Decode(initial_element); if (initial_object != nullptr) { - mirror::Class* element_class = result->GetClass()->GetComponentType(); + ObjPtr element_class = result->GetClass()->GetComponentType(); if (UNLIKELY(!element_class->IsAssignableFrom(initial_object->GetClass()))) { soa.Vm()->JniAbortF("NewObjectArray", "cannot assign object of type '%s' to array with " "element type of '%s'", @@ -2051,7 +2057,7 @@ class JNI { return nullptr; } else { for (jsize i = 0; i < length; ++i) { - result->SetWithoutChecks(i, initial_object.Ptr()); + result->SetWithoutChecks(i, initial_object); } } } @@ -2101,7 +2107,7 @@ class JNI { return; } const size_t component_size = array->GetClass()->GetComponentSize(); - ReleasePrimitiveArray(soa, array.Ptr(), component_size, elements, mode); + ReleasePrimitiveArray(soa, array, component_size, elements, mode); } static jboolean* GetBooleanArrayElements(JNIEnv* env, jbooleanArray array, jboolean* is_copy) { @@ -2338,13 +2344,13 @@ class JNI { current_class != nullptr; current_class = current_class->GetSuperClass()) { // Search first only comparing methods which are native. - m = FindMethod(current_class.Ptr(), name, sig); + m = FindMethod(current_class, name, sig); if (m != nullptr) { break; } // Search again comparing to all methods, to find non-native methods that match. - m = FindMethod(current_class.Ptr(), name, sig); + m = FindMethod(current_class, name, sig); if (m != nullptr) { break; } @@ -2547,30 +2553,32 @@ class JNI { } template - static ArtArrayT* DecodeAndCheckArrayType(ScopedObjectAccess& soa, JArrayT java_array, - const char* fn_name, const char* operation) + static ObjPtr DecodeAndCheckArrayType(ScopedObjectAccess& soa, + JArrayT java_array, + const char* fn_name, + const char* operation) REQUIRES_SHARED(Locks::mutator_lock_) { ObjPtr array = soa.Decode(java_array); - if (UNLIKELY(ArtArrayT::GetArrayClass() != array->GetClass())) { + ObjPtr expected_array_class = GetClassRoot(); + if (UNLIKELY(expected_array_class != array->GetClass())) { soa.Vm()->JniAbortF(fn_name, "attempt to %s %s primitive array elements with an object of type %s", operation, mirror::Class::PrettyDescriptor( - ArtArrayT::GetArrayClass()->GetComponentType()).c_str(), + expected_array_class->GetComponentType()).c_str(), mirror::Class::PrettyDescriptor(array->GetClass()).c_str()); return nullptr; } DCHECK_EQ(sizeof(ElementT), array->GetClass()->GetComponentSize()); - return array.Ptr(); + return array; } template static ElementT* GetPrimitiveArray(JNIEnv* env, ArrayT java_array, jboolean* is_copy) { CHECK_NON_NULL_ARGUMENT(java_array); ScopedObjectAccess soa(env); - ArtArrayT* array = DecodeAndCheckArrayType(soa, java_array, - "GetArrayElements", - "get"); + ObjPtr array = DecodeAndCheckArrayType( + soa, java_array, "GetArrayElements", "get"); if (UNLIKELY(array == nullptr)) { return nullptr; } @@ -2596,17 +2604,19 @@ class JNI { static void ReleasePrimitiveArray(JNIEnv* env, ArrayT java_array, ElementT* elements, jint mode) { CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_array); ScopedObjectAccess soa(env); - ArtArrayT* array = DecodeAndCheckArrayType(soa, java_array, - "ReleaseArrayElements", - "release"); + ObjPtr array = DecodeAndCheckArrayType( + soa, java_array, "ReleaseArrayElements", "release"); if (array == nullptr) { return; } ReleasePrimitiveArray(soa, array, sizeof(ElementT), elements, mode); } - static void ReleasePrimitiveArray(ScopedObjectAccess& soa, mirror::Array* array, - size_t component_size, void* elements, jint mode) + static void ReleasePrimitiveArray(ScopedObjectAccess& soa, + ObjPtr array, + size_t component_size, + void* elements, + jint mode) REQUIRES_SHARED(Locks::mutator_lock_) { void* array_data = array->GetRawData(component_size, 0); gc::Heap* heap = Runtime::Current()->GetHeap(); @@ -2649,10 +2659,8 @@ class JNI { jsize start, jsize length, ElementT* buf) { CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_array); ScopedObjectAccess soa(env); - ArtArrayT* array = - DecodeAndCheckArrayType(soa, java_array, - "GetPrimitiveArrayRegion", - "get region of"); + ObjPtr array = DecodeAndCheckArrayType( + soa, java_array, "GetPrimitiveArrayRegion", "get region of"); if (array != nullptr) { if (start < 0 || length < 0 || length > array->GetLength() - start) { ThrowAIOOBE(soa, array, start, length, "src"); @@ -2669,10 +2677,8 @@ class JNI { jsize start, jsize length, const ElementT* buf) { CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_array); ScopedObjectAccess soa(env); - ArtArrayT* array = - DecodeAndCheckArrayType(soa, java_array, - "SetPrimitiveArrayRegion", - "set region of"); + ObjPtr array = DecodeAndCheckArrayType( + soa, java_array, "SetPrimitiveArrayRegion", "set region of"); if (array != nullptr) { if (start < 0 || length < 0 || length > array->GetLength() - start) { ThrowAIOOBE(soa, array, start, length, "dst"); diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index 636c84c759..d2adcb4766 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -201,11 +201,6 @@ inline Array* Array::Alloc(Thread* self, return result; } -template -inline void PrimitiveArray::VisitRoots(RootVisitor* visitor) { - array_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - template inline PrimitiveArray* PrimitiveArray::AllocateAndFill(Thread* self, const T* data, @@ -219,16 +214,6 @@ inline PrimitiveArray* PrimitiveArray::AllocateAndFill(Thread* self, return arr.Get(); } -template -inline PrimitiveArray* PrimitiveArray::Alloc(Thread* self, size_t length) { - Array* raw_array = Array::Alloc(self, - GetArrayClass(), - length, - ComponentSizeShiftWidth(sizeof(T)), - Runtime::Current()->GetHeap()->GetCurrentAllocator()); - return down_cast*>(raw_array); -} - template inline T PrimitiveArray::Get(int32_t i) { if (!CheckIsValidIndex(i)) { @@ -461,13 +446,6 @@ void PointerArray::Memcpy(int32_t dst_pos, } } -template -inline void PrimitiveArray::SetArrayClass(ObjPtr array_class) { - CHECK(array_class_.IsNull()); - CHECK(array_class != nullptr); - array_class_ = GcRoot(array_class); -} - } // namespace mirror } // namespace art diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc index ea202e766f..06ce0bb5dd 100644 --- a/runtime/mirror/array.cc +++ b/runtime/mirror/array.cc @@ -20,6 +20,7 @@ #include "class-inl.h" #include "class.h" #include "class_linker-inl.h" +#include "class_root.h" #include "common_throws.h" #include "dex/dex_file-inl.h" #include "gc/accounting/card_table-inl.h" @@ -118,6 +119,16 @@ Array* Array::CreateMultiArray(Thread* self, Handle element_class, return new_array.Ptr(); } +template +PrimitiveArray* PrimitiveArray::Alloc(Thread* self, size_t length) { + Array* raw_array = Array::Alloc(self, + GetClassRoot>(), + length, + ComponentSizeShiftWidth(sizeof(T)), + Runtime::Current()->GetHeap()->GetCurrentAllocator()); + return down_cast*>(raw_array); +} + void Array::ThrowArrayIndexOutOfBoundsException(int32_t index) { art::ThrowArrayIndexOutOfBoundsException(index, GetLength()); } @@ -146,9 +157,6 @@ Array* Array::CopyOf(Thread* self, int32_t new_length) { return new_array.Ptr(); } - -template GcRoot PrimitiveArray::array_class_; - // Explicitly instantiate all the primitive array types. template class PrimitiveArray; // BooleanArray template class PrimitiveArray; // ByteArray diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h index 11128bb5a2..c9c99e8621 100644 --- a/runtime/mirror/array.h +++ b/runtime/mirror/array.h @@ -167,24 +167,7 @@ class MANAGED PrimitiveArray : public Array { void Memcpy(int32_t dst_pos, ObjPtr> src, int32_t src_pos, int32_t count) REQUIRES_SHARED(Locks::mutator_lock_); - static void SetArrayClass(ObjPtr array_class); - - template - static Class* GetArrayClass() REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(!array_class_.IsNull()); - return array_class_.Read(); - } - - static void ResetArrayClass() { - CHECK(!array_class_.IsNull()); - array_class_ = GcRoot(nullptr); - } - - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: - static GcRoot array_class_; - DISALLOW_IMPLICIT_CONSTRUCTORS(PrimitiveArray); }; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 14027493d8..621eec5cb9 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1995,15 +1995,6 @@ void Runtime::VisitConstantRoots(RootVisitor* visitor) { mirror::ArrayElementVarHandle::VisitRoots(visitor); mirror::ByteArrayViewVarHandle::VisitRoots(visitor); mirror::ByteBufferViewVarHandle::VisitRoots(visitor); - // Visit all the primitive array types classes. - mirror::PrimitiveArray::VisitRoots(visitor); // BooleanArray - mirror::PrimitiveArray::VisitRoots(visitor); // ByteArray - mirror::PrimitiveArray::VisitRoots(visitor); // CharArray - mirror::PrimitiveArray::VisitRoots(visitor); // DoubleArray - mirror::PrimitiveArray::VisitRoots(visitor); // FloatArray - mirror::PrimitiveArray::VisitRoots(visitor); // IntArray - mirror::PrimitiveArray::VisitRoots(visitor); // LongArray - mirror::PrimitiveArray::VisitRoots(visitor); // ShortArray // Visiting the roots of these ArtMethods is not currently required since all the GcRoots are // null. BufferedRootVisitor<16> buffered_visitor(visitor, RootInfo(kRootVMInternal)); -- GitLab From c7aa87e1666ac48ddf9149cfdfd64b026b3969e5 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 24 May 2018 15:19:52 +0100 Subject: [PATCH 463/749] Remove static_class_ from Method/VarHandle and CallSite. And add MethodHandle to the class roots to avoid extra indirection through MethodHandleImpl. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Bug: 31113334 Change-Id: Iaf172f3732677f2b4509e8297e6e9af5fb81a89f --- runtime/art_method.cc | 20 +-- runtime/class_linker.cc | 35 +----- runtime/class_root.h | 1 + .../quick/quick_trampoline_entrypoints.cc | 5 +- runtime/image.cc | 2 +- runtime/interpreter/interpreter_common.cc | 49 ++++---- runtime/jit/jit.cc | 4 +- runtime/method_handles.cc | 5 +- runtime/mirror/call_site.cc | 20 +-- runtime/mirror/call_site.h | 10 -- runtime/mirror/method_handle_impl.cc | 32 +---- runtime/mirror/method_handle_impl.h | 9 -- runtime/mirror/method_handles_lookup.cc | 25 +--- runtime/mirror/method_handles_lookup.h | 10 -- runtime/mirror/method_type.cc | 20 +-- runtime/mirror/method_type.h | 10 -- runtime/mirror/var_handle.cc | 117 ++---------------- runtime/mirror/var_handle.h | 35 ------ runtime/mirror/var_handle_test.cc | 9 +- runtime/native/java_lang_Class.cc | 3 +- runtime/runtime.cc | 9 -- runtime/verifier/method_verifier.cc | 23 ++-- runtime/verifier/reg_type_cache-inl.h | 5 +- 23 files changed, 93 insertions(+), 365 deletions(-) diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 608e33cf65..151c36f3bc 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -24,6 +24,7 @@ #include "art_method-inl.h" #include "base/stringpiece.h" #include "class_linker-inl.h" +#include "class_root.h" #include "debugger.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" @@ -47,7 +48,6 @@ #include "runtime_callbacks.h" #include "scoped_thread_state_change-inl.h" #include "vdex_file.h" -#include "well_known_classes.h" namespace art { @@ -68,7 +68,7 @@ ArtMethod* ArtMethod::GetCanonicalMethod(PointerSize pointer_size) { if (LIKELY(!IsDefault())) { return this; } else { - mirror::Class* declaring_class = GetDeclaringClass(); + ObjPtr declaring_class = GetDeclaringClass(); DCHECK(declaring_class->IsInterface()); ArtMethod* ret = declaring_class->FindInterfaceMethod(declaring_class->GetDexCache(), GetDexMethodIndex(), @@ -213,8 +213,8 @@ ArtMethod* ArtMethod::FindOverriddenMethod(PointerSize pointer_size) { if (IsStatic()) { return nullptr; } - mirror::Class* declaring_class = GetDeclaringClass(); - mirror::Class* super_class = declaring_class->GetSuperClass(); + ObjPtr declaring_class = GetDeclaringClass(); + ObjPtr super_class = declaring_class->GetSuperClass(); uint16_t method_index = GetMethodIndex(); ArtMethod* result = nullptr; // Did this method override a super class method? If so load the result from the super class' @@ -229,7 +229,7 @@ ArtMethod* ArtMethod::FindOverriddenMethod(PointerSize pointer_size) { } else { mirror::IfTable* iftable = GetDeclaringClass()->GetIfTable(); for (size_t i = 0; i < iftable->Count() && result == nullptr; i++) { - mirror::Class* interface = iftable->GetInterface(i); + ObjPtr interface = iftable->GetInterface(i); for (ArtMethod& interface_method : interface->GetVirtualMethods(pointer_size)) { if (HasSameNameAndSignature(interface_method.GetInterfaceMethodIfProxy(pointer_size))) { result = &interface_method; @@ -424,9 +424,11 @@ bool ArtMethod::IsPolymorphicSignature() { if (!IsNative() || !IsVarargs()) { return false; } - mirror::Class* cls = GetDeclaringClass(); - return (cls == WellKnownClasses::ToClass(WellKnownClasses::java_lang_invoke_MethodHandle) || - cls == WellKnownClasses::ToClass(WellKnownClasses::java_lang_invoke_VarHandle)); + ObjPtr> class_roots = + Runtime::Current()->GetClassLinker()->GetClassRoots(); + ObjPtr cls = GetDeclaringClass(); + return (cls == GetClassRoot(class_roots) || + cls == GetClassRoot(class_roots)); } static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, @@ -510,7 +512,7 @@ static const OatFile::OatMethod FindOatMethodFor(ArtMethod* method, } // Although we overwrite the trampoline of non-static methods, we may get here via the resolution // method for direct methods (or virtual methods made direct). - mirror::Class* declaring_class = method->GetDeclaringClass(); + ObjPtr declaring_class = method->GetDeclaringClass(); size_t oat_method_index; if (method->IsStatic() || method->IsDirect()) { // Simple case where the oat method index was stashed at load time. diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 1c75fba9fd..5d0932128c 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -721,55 +721,47 @@ bool ClassLinker::InitWithoutImage(std::vector> b class_root = FindSystemClass(self, "Ljava/lang/invoke/CallSite;"); CHECK(class_root != nullptr); SetClassRoot(ClassRoot::kJavaLangInvokeCallSite, class_root); - mirror::CallSite::SetClass(class_root); // Create java.lang.invoke.MethodType.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodType;"); CHECK(class_root != nullptr); SetClassRoot(ClassRoot::kJavaLangInvokeMethodType, class_root); - mirror::MethodType::SetClass(class_root); // Create java.lang.invoke.MethodHandleImpl.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodHandleImpl;"); CHECK(class_root != nullptr); SetClassRoot(ClassRoot::kJavaLangInvokeMethodHandleImpl, class_root); - mirror::MethodHandleImpl::SetClass(class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeMethodHandle, class_root->GetSuperClass()); // Create java.lang.invoke.MethodHandles.Lookup.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodHandles$Lookup;"); CHECK(class_root != nullptr); SetClassRoot(ClassRoot::kJavaLangInvokeMethodHandlesLookup, class_root); - mirror::MethodHandlesLookup::SetClass(class_root); // Create java.lang.invoke.VarHandle.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/VarHandle;"); CHECK(class_root != nullptr); SetClassRoot(ClassRoot::kJavaLangInvokeVarHandle, class_root); - mirror::VarHandle::SetClass(class_root); // Create java.lang.invoke.FieldVarHandle.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/FieldVarHandle;"); CHECK(class_root != nullptr); SetClassRoot(ClassRoot::kJavaLangInvokeFieldVarHandle, class_root); - mirror::FieldVarHandle::SetClass(class_root); // Create java.lang.invoke.ArrayElementVarHandle.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/ArrayElementVarHandle;"); CHECK(class_root != nullptr); SetClassRoot(ClassRoot::kJavaLangInvokeArrayElementVarHandle, class_root); - mirror::ArrayElementVarHandle::SetClass(class_root); // Create java.lang.invoke.ByteArrayViewVarHandle.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/ByteArrayViewVarHandle;"); CHECK(class_root != nullptr); SetClassRoot(ClassRoot::kJavaLangInvokeByteArrayViewVarHandle, class_root); - mirror::ByteArrayViewVarHandle::SetClass(class_root); // Create java.lang.invoke.ByteBufferViewVarHandle.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/ByteBufferViewVarHandle;"); CHECK(class_root != nullptr); SetClassRoot(ClassRoot::kJavaLangInvokeByteBufferViewVarHandle, class_root); - mirror::ByteBufferViewVarHandle::SetClass(class_root); class_root = FindSystemClass(self, "Ldalvik/system/EmulatedStackFrame;"); CHECK(class_root != nullptr); @@ -1056,22 +1048,6 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { mirror::Method::SetClass(GetClassRoot(ClassRoot::kJavaLangReflectMethod, this).Ptr()); mirror::Method::SetArrayClass( GetClassRoot(ClassRoot::kJavaLangReflectMethodArrayClass, this).Ptr()); - mirror::CallSite::SetClass(GetClassRoot(ClassRoot::kJavaLangInvokeCallSite, this).Ptr()); - mirror::MethodHandleImpl::SetClass( - GetClassRoot(ClassRoot::kJavaLangInvokeMethodHandleImpl, this).Ptr()); - mirror::MethodHandlesLookup::SetClass( - GetClassRoot(ClassRoot::kJavaLangInvokeMethodHandlesLookup, this).Ptr()); - mirror::MethodType::SetClass( - GetClassRoot(ClassRoot::kJavaLangInvokeMethodType, this).Ptr()); - mirror::VarHandle::SetClass(GetClassRoot(ClassRoot::kJavaLangInvokeVarHandle, this).Ptr()); - mirror::FieldVarHandle::SetClass( - GetClassRoot(ClassRoot::kJavaLangInvokeFieldVarHandle, this).Ptr()); - mirror::ArrayElementVarHandle::SetClass( - GetClassRoot(ClassRoot::kJavaLangInvokeArrayElementVarHandle, this).Ptr()); - mirror::ByteArrayViewVarHandle::SetClass( - GetClassRoot(ClassRoot::kJavaLangInvokeByteArrayViewVarHandle, this).Ptr()); - mirror::ByteBufferViewVarHandle::SetClass( - GetClassRoot(ClassRoot::kJavaLangInvokeByteBufferViewVarHandle, this).Ptr()); mirror::Reference::SetClass(GetClassRoot(ClassRoot::kJavaLangRefReference, this)); mirror::Throwable::SetClass(GetClassRoot(ClassRoot::kJavaLangThrowable, this)); mirror::StackTraceElement::SetClass(GetClassRoot(ClassRoot::kJavaLangStackTraceElement, this)); @@ -2166,15 +2142,6 @@ ClassLinker::~ClassLinker() { mirror::Constructor::ResetArrayClass(); mirror::Field::ResetArrayClass(); mirror::Method::ResetArrayClass(); - mirror::CallSite::ResetClass(); - mirror::MethodType::ResetClass(); - mirror::MethodHandleImpl::ResetClass(); - mirror::MethodHandlesLookup::ResetClass(); - mirror::VarHandle::ResetClass(); - mirror::FieldVarHandle::ResetClass(); - mirror::ArrayElementVarHandle::ResetClass(); - mirror::ByteArrayViewVarHandle::ResetClass(); - mirror::ByteBufferViewVarHandle::ResetClass(); mirror::EmulatedStackFrame::ResetClass(); Thread* const self = Thread::Current(); for (const ClassLoaderData& data : class_loaders_) { diff --git a/runtime/class_root.h b/runtime/class_root.h index f43e2c6920..5c7819841b 100644 --- a/runtime/class_root.h +++ b/runtime/class_root.h @@ -68,6 +68,7 @@ class VarHandle; M(kJavaLangReflectFieldArrayClass, "[Ljava/lang/reflect/Field;", mirror::ObjectArray) \ M(kJavaLangReflectMethodArrayClass, "[Ljava/lang/reflect/Method;", mirror::ObjectArray) \ M(kJavaLangInvokeCallSite, "Ljava/lang/invoke/CallSite;", mirror::CallSite) \ + M(kJavaLangInvokeMethodHandle, "Ljava/lang/invoke/MethodHandle;", mirror::MethodHandle) \ M(kJavaLangInvokeMethodHandleImpl, "Ljava/lang/invoke/MethodHandleImpl;", mirror::MethodHandleImpl) \ M(kJavaLangInvokeMethodHandlesLookup, "Ljava/lang/invoke/MethodHandles$Lookup;", mirror::MethodHandlesLookup) \ M(kJavaLangInvokeMethodType, "Ljava/lang/invoke/MethodType;", mirror::MethodType) \ diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 0985bf216f..a59faeae9b 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -19,6 +19,7 @@ #include "base/enums.h" #include "callee_save_frame.h" #include "common_throws.h" +#include "class_root.h" #include "debug_print.h" #include "debugger.h" #include "dex/dex_file-inl.h" @@ -2810,7 +2811,7 @@ extern "C" uintptr_t artInvokePolymorphic( RangeInstructionOperands operands(first_arg + 1, num_vregs - 1); Intrinsics intrinsic = static_cast(resolved_method->GetIntrinsic()); bool success = false; - if (resolved_method->GetDeclaringClass() == mirror::MethodHandle::StaticClass()) { + if (resolved_method->GetDeclaringClass() == GetClassRoot(linker)) { Handle method_handle(hs.NewHandle( ObjPtr::DownCast(MakeObjPtr(receiver_handle.Get())))); if (intrinsic == Intrinsics::kMethodHandleInvokeExact) { @@ -2831,7 +2832,7 @@ extern "C" uintptr_t artInvokePolymorphic( result); } } else { - DCHECK_EQ(mirror::VarHandle::StaticClass(), resolved_method->GetDeclaringClass()); + DCHECK_EQ(GetClassRoot(linker), resolved_method->GetDeclaringClass()); Handle var_handle(hs.NewHandle( ObjPtr::DownCast(MakeObjPtr(receiver_handle.Get())))); mirror::VarHandle::AccessMode access_mode = diff --git a/runtime/image.cc b/runtime/image.cc index 316f7a5c63..7ad2e7bf95 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -26,7 +26,7 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '9', '\0' }; // ReachabilityFence. +const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '0', '\0' }; // ClassRoot::MethodHandle. ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 708a7884fa..fab350942a 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -19,6 +19,7 @@ #include #include "base/enums.h" +#include "class_root.h" #include "debugger.h" #include "dex/dex_file_types.h" #include "entrypoints/runtime_asm_entrypoints.h" @@ -865,6 +866,7 @@ static JValue ConvertScalarBootstrapArgument(jvalue value) { static ObjPtr GetClassForBootstrapArgument(EncodedArrayValueIterator::ValueType type) REQUIRES_SHARED(Locks::mutator_lock_) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + ObjPtr> class_roots = class_linker->GetClassRoots(); switch (type) { case EncodedArrayValueIterator::ValueType::kBoolean: case EncodedArrayValueIterator::ValueType::kByte: @@ -874,21 +876,21 @@ static ObjPtr GetClassForBootstrapArgument(EncodedArrayValueItera // will result in CCE's being raised if the BSM has one of these // types. case EncodedArrayValueIterator::ValueType::kInt: - return class_linker->FindPrimitiveClass('I'); + return GetClassRoot(ClassRoot::kPrimitiveInt, class_roots); case EncodedArrayValueIterator::ValueType::kLong: - return class_linker->FindPrimitiveClass('J'); + return GetClassRoot(ClassRoot::kPrimitiveLong, class_roots); case EncodedArrayValueIterator::ValueType::kFloat: - return class_linker->FindPrimitiveClass('F'); + return GetClassRoot(ClassRoot::kPrimitiveFloat, class_roots); case EncodedArrayValueIterator::ValueType::kDouble: - return class_linker->FindPrimitiveClass('D'); + return GetClassRoot(ClassRoot::kPrimitiveDouble, class_roots); case EncodedArrayValueIterator::ValueType::kMethodType: - return mirror::MethodType::StaticClass(); + return GetClassRoot(class_roots); case EncodedArrayValueIterator::ValueType::kMethodHandle: - return mirror::MethodHandle::StaticClass(); + return GetClassRoot(class_roots); case EncodedArrayValueIterator::ValueType::kString: - return mirror::String::GetJavaLangString(); + return GetClassRoot(); case EncodedArrayValueIterator::ValueType::kType: - return mirror::Class::GetJavaLangClass(); + return GetClassRoot(); case EncodedArrayValueIterator::ValueType::kField: case EncodedArrayValueIterator::ValueType::kMethod: case EncodedArrayValueIterator::ValueType::kEnum: @@ -1091,21 +1093,23 @@ static bool PackCollectorArrayForBootstrapMethod(Thread* self, setter->SetReference(array.Get()); \ return true; - if (array_type->GetComponentType() == class_linker->FindPrimitiveClass('I')) { + ObjPtr> class_roots = class_linker->GetClassRoots(); + ObjPtr component_type = array_type->GetComponentType(); + if (component_type == GetClassRoot(ClassRoot::kPrimitiveInt, class_roots)) { COLLECT_PRIMITIVE_ARRAY(I, Int); - } else if (array_type->GetComponentType() == class_linker->FindPrimitiveClass('J')) { + } else if (component_type == GetClassRoot(ClassRoot::kPrimitiveLong, class_roots)) { COLLECT_PRIMITIVE_ARRAY(J, Long); - } else if (array_type->GetComponentType() == class_linker->FindPrimitiveClass('F')) { + } else if (component_type == GetClassRoot(ClassRoot::kPrimitiveFloat, class_roots)) { COLLECT_PRIMITIVE_ARRAY(F, Float); - } else if (array_type->GetComponentType() == class_linker->FindPrimitiveClass('D')) { + } else if (component_type == GetClassRoot(ClassRoot::kPrimitiveDouble, class_roots)) { COLLECT_PRIMITIVE_ARRAY(D, Double); - } else if (array_type->GetComponentType() == mirror::MethodType::StaticClass()) { + } else if (component_type == GetClassRoot()) { COLLECT_REFERENCE_ARRAY(mirror::MethodType, MethodType); - } else if (array_type->GetComponentType() == mirror::MethodHandle::StaticClass()) { + } else if (component_type == GetClassRoot()) { COLLECT_REFERENCE_ARRAY(mirror::MethodHandle, MethodHandle); - } else if (array_type->GetComponentType() == mirror::String::GetJavaLangString()) { + } else if (component_type == GetClassRoot(class_roots)) { COLLECT_REFERENCE_ARRAY(mirror::String, String); - } else if (array_type->GetComponentType() == mirror::Class::GetJavaLangClass()) { + } else if (component_type == GetClassRoot()) { COLLECT_REFERENCE_ARRAY(mirror::Class, Type); } else { UNREACHABLE(); @@ -1125,8 +1129,8 @@ static ObjPtr BuildCallSiteForBootstrapMethod(Thread* self, StackHandleScope<2> hs(self); // Create array for parameter types. ObjPtr class_type = mirror::Class::GetJavaLangClass(); - mirror::Class* class_array_type = - Runtime::Current()->GetClassLinker()->FindArrayClass(self, &class_type); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + ObjPtr class_array_type = class_linker->FindArrayClass(self, &class_type); Handle> ptypes = hs.NewHandle( mirror::ObjectArray::Alloc(self, class_array_type, @@ -1138,7 +1142,7 @@ static ObjPtr BuildCallSiteForBootstrapMethod(Thread* self, // Populate the first argument with an instance of j.l.i.MethodHandles.Lookup // that the runtime will construct. - ptypes->Set(0, mirror::MethodHandlesLookup::StaticClass()); + ptypes->Set(0, GetClassRoot(class_linker)); it.Next(); // The remaining parameter types are derived from the types of @@ -1157,7 +1161,7 @@ static ObjPtr BuildCallSiteForBootstrapMethod(Thread* self, DCHECK_EQ(static_cast(index), it.Size()); // By definition, the return type is always a j.l.i.CallSite. - Handle rtype = hs.NewHandle(mirror::CallSite::StaticClass()); + Handle rtype = hs.NewHandle(GetClassRoot()); return mirror::MethodType::Create(self, rtype, ptypes); } @@ -1352,8 +1356,9 @@ static ObjPtr InvokeBootstrapMethod(Thread* self, } // Check the result type is a subclass of j.l.i.CallSite. - if (UNLIKELY(!object->InstanceOf(mirror::CallSite::StaticClass()))) { - ThrowClassCastException(object->GetClass(), mirror::CallSite::StaticClass()); + ObjPtr call_site_class = GetClassRoot(class_linker); + if (UNLIKELY(!object->InstanceOf(call_site_class))) { + ThrowClassCastException(object->GetClass(), call_site_class); return nullptr; } diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 5d4b9e8cc9..9c02dce98a 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -24,6 +24,7 @@ #include "base/memory_tool.h" #include "base/runtime_debug.h" #include "base/utils.h" +#include "class_root.h" #include "debugger.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "interpreter/interpreter.h" @@ -632,7 +633,8 @@ static bool IgnoreSamplesForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mut } if (method->IsNative()) { ObjPtr klass = method->GetDeclaringClass(); - if (klass == mirror::MethodHandle::StaticClass() || klass == mirror::VarHandle::StaticClass()) { + if (klass == GetClassRoot() || + klass == GetClassRoot()) { // MethodHandle and VarHandle invocation methods are required to throw an // UnsupportedOperationException if invoked reflectively. We achieve this by having native // implementations that arise the exception. We need to disable JIT compilation of these JNI diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 27de72533c..28df1749a4 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -18,6 +18,7 @@ #include "android-base/stringprintf.h" +#include "class_root.h" #include "common_dex_operations.h" #include "interpreter/shadow_frame-inl.h" #include "jvalue-inl.h" @@ -1022,7 +1023,7 @@ bool DoVarHandleInvokeTranslation(Thread* self, // Check that the first parameter is a VarHandle if (callsite_ptypes->GetLength() < 1 || !mh_ptypes->Get(0)->IsAssignableFrom(callsite_ptypes->Get(0)) || - mh_ptypes->Get(0) != mirror::VarHandle::StaticClass()) { + mh_ptypes->Get(0) != GetClassRoot()) { ThrowWrongMethodTypeException(method_handle->GetMethodType(), callsite_type.Get()); return false; } @@ -1036,7 +1037,7 @@ bool DoVarHandleInvokeTranslation(Thread* self, // Cast to VarHandle instance Handle vh(hs.NewHandle(down_cast(receiver))); - DCHECK(mirror::VarHandle::StaticClass()->IsAssignableFrom(vh->GetClass())); + DCHECK(GetClassRoot()->IsAssignableFrom(vh->GetClass())); // Determine the accessor kind to dispatch ArtMethod* target_method = method_handle->GetTargetMethod(); diff --git a/runtime/mirror/call_site.cc b/runtime/mirror/call_site.cc index eb613df4c6..808f77cde1 100644 --- a/runtime/mirror/call_site.cc +++ b/runtime/mirror/call_site.cc @@ -17,36 +17,20 @@ #include "call_site.h" #include "class-inl.h" +#include "class_root.h" #include "gc_root-inl.h" namespace art { namespace mirror { -GcRoot CallSite::static_class_; - mirror::CallSite* CallSite::Create(Thread* const self, Handle target) { StackHandleScope<1> hs(self); Handle cs( - hs.NewHandle(ObjPtr::DownCast(StaticClass()->AllocObject(self)))); + hs.NewHandle(ObjPtr::DownCast(GetClassRoot()->AllocObject(self)))); CHECK(!Runtime::Current()->IsActiveTransaction()); cs->SetFieldObject(TargetOffset(), target.Get()); return cs.Get(); } -void CallSite::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void CallSite::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void CallSite::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - } // namespace mirror } // namespace art diff --git a/runtime/mirror/call_site.h b/runtime/mirror/call_site.h index 93f274808c..9b6afca3aa 100644 --- a/runtime/mirror/call_site.h +++ b/runtime/mirror/call_site.h @@ -33,18 +33,10 @@ class MANAGED CallSite : public Object { Handle method_handle) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); - } - MethodHandle* GetTarget() REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldObject(TargetOffset()); } - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: static inline MemberOffset TargetOffset() { return MemberOffset(OFFSETOF_MEMBER(CallSite, target_)); @@ -52,8 +44,6 @@ class MANAGED CallSite : public Object { HeapReference target_; - static GcRoot static_class_; // java.lang.invoke.CallSite.class - friend struct art::CallSiteOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(CallSite); }; diff --git a/runtime/mirror/method_handle_impl.cc b/runtime/mirror/method_handle_impl.cc index 0b4dde1aa8..a6c1609d01 100644 --- a/runtime/mirror/method_handle_impl.cc +++ b/runtime/mirror/method_handle_impl.cc @@ -17,6 +17,7 @@ #include "method_handle_impl-inl.h" #include "class-inl.h" +#include "class_root.h" #include "gc_root-inl.h" namespace art { @@ -30,12 +31,6 @@ const char* MethodHandle::GetReturnTypeDescriptor(const char* invoke_method_name } } -mirror::Class* MethodHandle::StaticClass() { - mirror::Class* klass = MethodHandleImpl::StaticClass()->GetSuperClass(); - DCHECK(klass->DescriptorEquals("Ljava/lang/invoke/MethodHandle;")); - return klass; -} - void MethodHandle::Initialize(uintptr_t art_field_or_method, Kind kind, Handle method_type) @@ -48,35 +43,14 @@ void MethodHandle::Initialize(uintptr_t art_field_or_method, SetField64(ArtFieldOrMethodOffset(), art_field_or_method); } -GcRoot MethodHandleImpl::static_class_; - -mirror::Class* MethodHandleImpl::StaticClass() { - return static_class_.Read(); -} - -void MethodHandleImpl::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void MethodHandleImpl::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void MethodHandleImpl::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - mirror::MethodHandleImpl* MethodHandleImpl::Create(Thread* const self, uintptr_t art_field_or_method, MethodHandle::Kind kind, Handle method_type) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) { StackHandleScope<1> hs(self); - Handle mh( - hs.NewHandle(ObjPtr::DownCast(StaticClass()->AllocObject(self)))); + Handle mh(hs.NewHandle(ObjPtr::DownCast( + GetClassRoot()->AllocObject(self)))); mh->Initialize(art_field_or_method, kind, method_type); return mh.Get(); } diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h index 3b0002c2af..4813b3c3f7 100644 --- a/runtime/mirror/method_handle_impl.h +++ b/runtime/mirror/method_handle_impl.h @@ -87,8 +87,6 @@ class MANAGED MethodHandle : public Object { // supported. static const char* GetReturnTypeDescriptor(const char* invoke_method_name); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); - protected: void Initialize(uintptr_t art_field_or_method, Kind kind, Handle method_type) REQUIRES_SHARED(Locks::mutator_lock_); @@ -130,19 +128,12 @@ class MANAGED MethodHandleImpl : public MethodHandle { Handle method_type) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); - - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: static MemberOffset InfoOffset() { return MemberOffset(OFFSETOF_MEMBER(MethodHandleImpl, info_)); } HeapReference info_; // Unused by the runtime. - static GcRoot static_class_; // java.lang.invoke.MethodHandleImpl.class friend struct art::MethodHandleImplOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(MethodHandleImpl); diff --git a/runtime/mirror/method_handles_lookup.cc b/runtime/mirror/method_handles_lookup.cc index aeecf75c1f..1ac38dad24 100644 --- a/runtime/mirror/method_handles_lookup.cc +++ b/runtime/mirror/method_handles_lookup.cc @@ -17,6 +17,7 @@ #include "method_handles_lookup.h" #include "class-inl.h" +#include "class_root.h" #include "dex/modifiers.h" #include "gc_root-inl.h" #include "handle_scope.h" @@ -28,33 +29,15 @@ namespace art { namespace mirror { -GcRoot MethodHandlesLookup::static_class_; - -void MethodHandlesLookup::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void MethodHandlesLookup::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void MethodHandlesLookup::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - MethodHandlesLookup* MethodHandlesLookup::Create(Thread* const self, Handle lookup_class) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) { static constexpr uint32_t kAllModes = kAccPublic | kAccPrivate | kAccProtected | kAccStatic; - StackHandleScope<1> hs(self); - Handle mhl( - hs.NewHandle(ObjPtr::DownCast(StaticClass()->AllocObject(self)))); + ObjPtr mhl = ObjPtr::DownCast( + GetClassRoot()->AllocObject(self)); mhl->SetFieldObject(LookupClassOffset(), lookup_class.Get()); mhl->SetField32(AllowedModesOffset(), kAllModes); - return mhl.Get(); + return mhl.Ptr(); } MethodHandlesLookup* MethodHandlesLookup::GetDefault(Thread* const self) { diff --git a/runtime/mirror/method_handles_lookup.h b/runtime/mirror/method_handles_lookup.h index fefcb2ed29..aa94f95ae0 100644 --- a/runtime/mirror/method_handles_lookup.h +++ b/runtime/mirror/method_handles_lookup.h @@ -40,14 +40,6 @@ class MANAGED MethodHandlesLookup : public Object { Handle lookup_class) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); - } - - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - // Returns the result of java.lang.invoke.MethodHandles.lookup(). static mirror::MethodHandlesLookup* GetDefault(Thread* const self) REQUIRES_SHARED(Locks::mutator_lock_); @@ -71,8 +63,6 @@ class MANAGED MethodHandlesLookup : public Object { int32_t allowed_modes_; - static GcRoot static_class_; // java.lang.invoke.MethodHandles.Lookup.class - friend struct art::MethodHandlesLookupOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(MethodHandlesLookup); }; diff --git a/runtime/mirror/method_type.cc b/runtime/mirror/method_type.cc index 45f7a87951..a8be8b7019 100644 --- a/runtime/mirror/method_type.cc +++ b/runtime/mirror/method_type.cc @@ -17,6 +17,7 @@ #include "method_type.h" #include "class-inl.h" +#include "class_root.h" #include "gc_root-inl.h" #include "method_handles.h" @@ -35,14 +36,12 @@ ObjPtr> AllocatePTypesArray(Thread* self, int count) } // namespace -GcRoot MethodType::static_class_; - MethodType* MethodType::Create(Thread* const self, Handle return_type, Handle> parameter_types) { StackHandleScope<1> hs(self); Handle mt( - hs.NewHandle(ObjPtr::DownCast(StaticClass()->AllocObject(self)))); + hs.NewHandle(ObjPtr::DownCast(GetClassRoot()->AllocObject(self)))); // TODO: Do we ever create a MethodType during a transaction ? There doesn't // seem like a good reason to do a polymorphic invoke that results in the @@ -172,20 +171,5 @@ std::string MethodType::PrettyDescriptor() REQUIRES_SHARED(Locks::mutator_lock_) return ss.str(); } -void MethodType::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void MethodType::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void MethodType::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - } // namespace mirror } // namespace art diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h index 771162a2de..014b211d66 100644 --- a/runtime/mirror/method_type.h +++ b/runtime/mirror/method_type.h @@ -48,10 +48,6 @@ class MANAGED MethodType : public Object { int32_t start_index) REQUIRES_SHARED(Locks::mutator_lock_); - static Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); - } - ObjectArray* GetPTypes() REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldObject>(OFFSET_OF_OBJECT_MEMBER(MethodType, p_types_)); } @@ -68,10 +64,6 @@ class MANAGED MethodType : public Object { return GetFieldObject(OFFSET_OF_OBJECT_MEMBER(MethodType, r_type_)); } - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - // Returns true iff. |this| is an exact match for method type |target|, i.e // iff. they have the same return types and parameter types. bool IsExactMatch(MethodType* target) REQUIRES_SHARED(Locks::mutator_lock_); @@ -111,8 +103,6 @@ class MANAGED MethodType : public Object { HeapReference r_type_; HeapReference wrap_alt_; // Unused in the runtime - static GcRoot static_class_; // java.lang.invoke.MethodType.class - friend struct art::MethodTypeOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(MethodType); }; diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc index c755299a79..8311d911cc 100644 --- a/runtime/mirror/var_handle.cc +++ b/runtime/mirror/var_handle.cc @@ -20,6 +20,7 @@ #include "art_field-inl.h" #include "class-inl.h" #include "class_linker.h" +#include "class_root.h" #include "gc_root-inl.h" #include "intrinsics_enum.h" #include "jni/jni_internal.h" @@ -1580,17 +1581,18 @@ bool VarHandle::Access(AccessMode access_mode, ShadowFrame* shadow_frame, const InstructionOperands* const operands, JValue* result) { - Class* klass = GetClass(); - if (klass == FieldVarHandle::StaticClass()) { + ObjPtr> class_roots = Runtime::Current()->GetClassLinker()->GetClassRoots(); + ObjPtr klass = GetClass(); + if (klass == GetClassRoot(class_roots)) { auto vh = reinterpret_cast(this); return vh->Access(access_mode, shadow_frame, operands, result); - } else if (klass == ArrayElementVarHandle::StaticClass()) { + } else if (klass == GetClassRoot(class_roots)) { auto vh = reinterpret_cast(this); return vh->Access(access_mode, shadow_frame, operands, result); - } else if (klass == ByteArrayViewVarHandle::StaticClass()) { + } else if (klass == GetClassRoot(class_roots)) { auto vh = reinterpret_cast(this); return vh->Access(access_mode, shadow_frame, operands, result); - } else if (klass == ByteBufferViewVarHandle::StaticClass()) { + } else if (klass == GetClassRoot(class_roots)) { auto vh = reinterpret_cast(this); return vh->Access(access_mode, shadow_frame, operands, result); } else { @@ -1681,27 +1683,6 @@ bool VarHandle::GetAccessModeByMethodName(const char* method_name, AccessMode* a return true; } -Class* VarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); -} - -void VarHandle::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void VarHandle::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void VarHandle::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - -GcRoot VarHandle::static_class_; - ArtField* FieldVarHandle::GetField() { uintptr_t opaque_field = static_cast(GetField64(ArtFieldOffset())); return reinterpret_cast(opaque_field); @@ -1758,27 +1739,6 @@ bool FieldVarHandle::Access(AccessMode access_mode, UNREACHABLE(); } -Class* FieldVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); -} - -void FieldVarHandle::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void FieldVarHandle::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void FieldVarHandle::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - -GcRoot FieldVarHandle::static_class_; - bool ArrayElementVarHandle::Access(AccessMode access_mode, ShadowFrame* shadow_frame, const InstructionOperands* const operands, @@ -1867,27 +1827,6 @@ bool ArrayElementVarHandle::Access(AccessMode access_mode, UNREACHABLE(); } -Class* ArrayElementVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); -} - -void ArrayElementVarHandle::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void ArrayElementVarHandle::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void ArrayElementVarHandle::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - -GcRoot ArrayElementVarHandle::static_class_; - bool ByteArrayViewVarHandle::GetNativeByteOrder() { return GetFieldBoolean(NativeByteOrderOffset()); } @@ -1976,27 +1915,6 @@ bool ByteArrayViewVarHandle::Access(AccessMode access_mode, UNREACHABLE(); } -Class* ByteArrayViewVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); -} - -void ByteArrayViewVarHandle::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void ByteArrayViewVarHandle::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void ByteArrayViewVarHandle::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - -GcRoot ByteArrayViewVarHandle::static_class_; - bool ByteBufferViewVarHandle::GetNativeByteOrder() { return GetFieldBoolean(NativeByteOrderOffset()); } @@ -2117,26 +2035,5 @@ bool ByteBufferViewVarHandle::Access(AccessMode access_mode, UNREACHABLE(); } -Class* ByteBufferViewVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); -} - -void ByteBufferViewVarHandle::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void ByteBufferViewVarHandle::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void ByteBufferViewVarHandle::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - -GcRoot ByteBufferViewVarHandle::static_class_; - } // namespace mirror } // namespace art diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h index 5186d43830..4fd18c14e2 100644 --- a/runtime/mirror/var_handle.h +++ b/runtime/mirror/var_handle.h @@ -149,11 +149,6 @@ class MANAGED VarHandle : public Object { // VarHandle access method, such as "setOpaque". Returns false otherwise. static bool GetAccessModeByMethodName(const char* method_name, AccessMode* access_mode); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: Class* GetCoordinateType0() REQUIRES_SHARED(Locks::mutator_lock_); Class* GetCoordinateType1() REQUIRES_SHARED(Locks::mutator_lock_); @@ -185,9 +180,6 @@ class MANAGED VarHandle : public Object { HeapReference var_type_; int32_t access_modes_bit_mask_; - // Root representing java.lang.invoke.VarHandle.class. - static GcRoot static_class_; - friend class VarHandleTest; // for testing purposes friend struct art::VarHandleOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(VarHandle); @@ -218,9 +210,6 @@ class MANAGED FieldVarHandle : public VarHandle { // ArtField instance corresponding to variable for accessors. int64_t art_field_; - // Root representing java.lang.invoke.FieldVarHandle.class. - static GcRoot static_class_; - friend class VarHandleTest; // for var_handle_test. friend struct art::FieldVarHandleOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(FieldVarHandle); @@ -236,15 +225,7 @@ class MANAGED ArrayElementVarHandle : public VarHandle { JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: - // Root representing java.lang.invoke.ArrayElementVarHandle.class. - static GcRoot static_class_; - friend class VarHandleTest; DISALLOW_IMPLICIT_CONSTRUCTORS(ArrayElementVarHandle); }; @@ -261,11 +242,6 @@ class MANAGED ByteArrayViewVarHandle : public VarHandle { bool GetNativeByteOrder() REQUIRES_SHARED(Locks::mutator_lock_); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: static MemberOffset NativeByteOrderOffset() { return MemberOffset(OFFSETOF_MEMBER(ByteArrayViewVarHandle, native_byte_order_)); @@ -274,9 +250,6 @@ class MANAGED ByteArrayViewVarHandle : public VarHandle { // Flag indicating that accessors should use native byte-ordering. uint8_t native_byte_order_; - // Root representing java.lang.invoke.ByteArrayViewVarHandle.class. - static GcRoot static_class_; - friend class VarHandleTest; // for var_handle_test. friend struct art::ByteArrayViewVarHandleOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(ByteArrayViewVarHandle); @@ -294,11 +267,6 @@ class MANAGED ByteBufferViewVarHandle : public VarHandle { bool GetNativeByteOrder() REQUIRES_SHARED(Locks::mutator_lock_); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: bool AccessHeapBuffer(AccessMode access_mode, ObjPtr byte_buffer, @@ -322,9 +290,6 @@ class MANAGED ByteBufferViewVarHandle : public VarHandle { // Flag indicating that accessors should use native byte-ordering. uint8_t native_byte_order_; - // Root representing java.lang.invoke.ByteBufferViewVarHandle.class. - static GcRoot static_class_; - friend class VarHandleTest; // for var_handle_test. friend struct art::ByteBufferViewVarHandleOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(ByteBufferViewVarHandle); diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc index 005aba3edd..2c1283225d 100644 --- a/runtime/mirror/var_handle_test.cc +++ b/runtime/mirror/var_handle_test.cc @@ -23,6 +23,7 @@ #include "class-inl.h" #include "class_linker-inl.h" #include "class_loader.h" +#include "class_root.h" #include "common_runtime_test.h" #include "handle_scope-inl.h" #include "jvalue-inl.h" @@ -43,7 +44,7 @@ class VarHandleTest : public CommonRuntimeTest { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) { StackHandleScope<4> hs(self); Handle fvh = hs.NewHandle( - ObjPtr::DownCast(FieldVarHandle::StaticClass()->AllocObject(self))); + ObjPtr::DownCast(GetClassRoot()->AllocObject(self))); Handle var_type = hs.NewHandle(art_field->ResolveType()); if (art_field->IsStatic()) { @@ -67,7 +68,7 @@ class VarHandleTest : public CommonRuntimeTest { StackHandleScope<3> hs(self); Handle vh = hs.NewHandle( ObjPtr::DownCast( - ArrayElementVarHandle::StaticClass()->AllocObject(self))); + GetClassRoot()->AllocObject(self))); // Initialize super class fields ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); @@ -85,7 +86,7 @@ class VarHandleTest : public CommonRuntimeTest { StackHandleScope<4> hs(self); Handle bvh = hs.NewHandle( ObjPtr::DownCast( - ByteArrayViewVarHandle::StaticClass()->AllocObject(self))); + GetClassRoot()->AllocObject(self))); // Initialize super class fields ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); @@ -106,7 +107,7 @@ class VarHandleTest : public CommonRuntimeTest { StackHandleScope<5> hs(self); Handle bvh = hs.NewHandle( ObjPtr::DownCast( - ByteArrayViewVarHandle::StaticClass()->AllocObject(self))); + GetClassRoot()->AllocObject(self))); // Initialize super class fields ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle var_type = hs.NewHandle(view_array_class->GetComponentType()); diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 9f595b1c29..b7dad89106 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -22,6 +22,7 @@ #include "art_method-inl.h" #include "base/enums.h" #include "class_linker-inl.h" +#include "class_root.h" #include "common_throws.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" @@ -82,7 +83,7 @@ static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) // is subject to change so conservatively cover the entire package. // NB Static initializers within java.lang.invoke are permitted and do not // need further stack inspection. - ObjPtr lookup_class = mirror::MethodHandlesLookup::StaticClass(); + ObjPtr lookup_class = GetClassRoot(); if ((declaring_class == lookup_class || declaring_class->IsInSamePackage(lookup_class)) && !m->IsClassInitializer()) { return true; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 621eec5cb9..d126fe979a 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1984,17 +1984,8 @@ void Runtime::VisitConstantRoots(RootVisitor* visitor) { mirror::String::VisitRoots(visitor); mirror::Throwable::VisitRoots(visitor); mirror::Field::VisitRoots(visitor); - mirror::MethodType::VisitRoots(visitor); - mirror::MethodHandleImpl::VisitRoots(visitor); - mirror::MethodHandlesLookup::VisitRoots(visitor); mirror::EmulatedStackFrame::VisitRoots(visitor); mirror::ClassExt::VisitRoots(visitor); - mirror::CallSite::VisitRoots(visitor); - mirror::VarHandle::VisitRoots(visitor); - mirror::FieldVarHandle::VisitRoots(visitor); - mirror::ArrayElementVarHandle::VisitRoots(visitor); - mirror::ByteArrayViewVarHandle::VisitRoots(visitor); - mirror::ByteBufferViewVarHandle::VisitRoots(visitor); // Visiting the roots of these ArtMethods is not currently required since all the GcRoots are // null. BufferedRootVisitor<16> buffered_visitor(visitor, RootInfo(kRootVMInternal)); diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 91cec23dd5..3a49e4d21f 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -33,6 +33,7 @@ #include "base/time_utils.h" #include "base/utils.h" #include "class_linker.h" +#include "class_root.h" #include "compiler_callbacks.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" @@ -4206,9 +4207,11 @@ bool MethodVerifier::CheckSignaturePolymorphicMethod(ArtMethod* method) { const char* method_name = method->GetName(); const char* expected_return_descriptor; - if (klass == mirror::MethodHandle::StaticClass()) { + ObjPtr> class_roots = + Runtime::Current()->GetClassLinker()->GetClassRoots(); + if (klass == GetClassRoot(class_roots)) { expected_return_descriptor = mirror::MethodHandle::GetReturnTypeDescriptor(method_name); - } else if (klass == mirror::VarHandle::StaticClass()) { + } else if (klass == GetClassRoot(class_roots)) { expected_return_descriptor = mirror::VarHandle::GetReturnTypeDescriptor(method_name); } else { Fail(VERIFY_ERROR_BAD_CLASS_HARD) @@ -4268,12 +4271,16 @@ bool MethodVerifier::CheckSignaturePolymorphicReceiver(const Instruction* inst) << "invoke-polymorphic receiver has no class: " << this_type; return false; - } else if (!this_type.GetClass()->IsSubClass(mirror::MethodHandle::StaticClass()) && - !this_type.GetClass()->IsSubClass(mirror::VarHandle::StaticClass())) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) - << "invoke-polymorphic receiver is not a subclass of MethodHandle or VarHandle: " - << this_type; - return false; + } else { + ObjPtr> class_roots = + Runtime::Current()->GetClassLinker()->GetClassRoots(); + if (!this_type.GetClass()->IsSubClass(GetClassRoot(class_roots)) && + !this_type.GetClass()->IsSubClass(GetClassRoot(class_roots))) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) + << "invoke-polymorphic receiver is not a subclass of MethodHandle or VarHandle: " + << this_type; + return false; + } } return true; } diff --git a/runtime/verifier/reg_type_cache-inl.h b/runtime/verifier/reg_type_cache-inl.h index 61f34afdac..43c0ab9598 100644 --- a/runtime/verifier/reg_type_cache-inl.h +++ b/runtime/verifier/reg_type_cache-inl.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_VERIFIER_REG_TYPE_CACHE_INL_H_ #include "class_linker.h" +#include "class_root.h" #include "mirror/class-inl.h" #include "mirror/method_handle_impl.h" #include "mirror/method_type.h" @@ -138,14 +139,14 @@ inline const PreciseReferenceType& RegTypeCache::JavaLangString() { inline const PreciseReferenceType& RegTypeCache::JavaLangInvokeMethodHandle() { const RegType* result = &FromClass("Ljava/lang/invoke/MethodHandle;", - mirror::MethodHandle::StaticClass(), true); + GetClassRoot().Ptr(), true); DCHECK(result->IsPreciseReference()); return *down_cast(result); } inline const PreciseReferenceType& RegTypeCache::JavaLangInvokeMethodType() { const RegType* result = &FromClass("Ljava/lang/invoke/MethodType;", - mirror::MethodType::StaticClass(), true); + GetClassRoot().Ptr(), true); DCHECK(result->IsPreciseReference()); return *down_cast(result); } -- GitLab From b4258999448ce4503fac317ba843af25a00ab10a Mon Sep 17 00:00:00 2001 From: Tamas Kenez Date: Thu, 24 May 2018 15:32:36 +0200 Subject: [PATCH 464/749] ART-tests: Remove DX-dependency from 463-checker-boolean-simplifier. This test was failing with D8 because some test cases relied on DX-specific code patterns. This CL moves those test cases to smali and enables D8. Bug: 65168732 Test: art/test.py -r --host -t 463 Change-Id: I7472593eea0bd0889319f19f9b65246856ceb15f --- test/463-checker-boolean-simplifier/build | 20 -- .../smali/Main2.smali | 308 ++++++++++++++++++ .../src/Main.java | 174 ++-------- 3 files changed, 329 insertions(+), 173 deletions(-) delete mode 100755 test/463-checker-boolean-simplifier/build create mode 100644 test/463-checker-boolean-simplifier/smali/Main2.smali diff --git a/test/463-checker-boolean-simplifier/build b/test/463-checker-boolean-simplifier/build deleted file mode 100755 index 10ffcc537d..0000000000 --- a/test/463-checker-boolean-simplifier/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/463-checker-boolean-simplifier/smali/Main2.smali b/test/463-checker-boolean-simplifier/smali/Main2.smali new file mode 100644 index 0000000000..5fc553ea36 --- /dev/null +++ b/test/463-checker-boolean-simplifier/smali/Main2.smali @@ -0,0 +1,308 @@ +# 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. + +.class public LMain2; +.super Ljava/lang/Object; +.source "Main2.java" + + +# direct methods +.method constructor ()V + .registers 1 + + .prologue + .line 17 + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + +# Elementary test negating a boolean. Verifies that blocks are merged and +# empty branches removed. + +## CHECK-START: boolean Main2.BooleanNot(boolean) select_generator (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: If [<>] +## CHECK-DAG: <> Phi [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: boolean Main2.BooleanNot(boolean) select_generator (before) +## CHECK: Goto +## CHECK: Goto +## CHECK: Goto +## CHECK-NOT: Goto + +## CHECK-START: boolean Main2.BooleanNot(boolean) select_generator (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> Select [<>,<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: boolean Main2.BooleanNot(boolean) select_generator (after) +## CHECK-NOT: If +## CHECK-NOT: Phi + +## CHECK-START: boolean Main2.BooleanNot(boolean) select_generator (after) +## CHECK: Goto +## CHECK-NOT: Goto + +# The original java source of this method: +# +# return !x; +# +.method public static BooleanNot(Z)Z + .registers 2 + .param p0, "x" # Z + + .prologue + .line 70 + if-nez p0, :cond_4 + + const/4 v0, 0x1 + + :goto_3 + return v0 + + :cond_4 + const/4 v0, 0x0 + + goto :goto_3 +.end method + +# Program which further uses negated conditions. +# Note that Phis are discovered retrospectively. + +## CHECK-START: boolean Main2.ValuesOrdered(int, int, int) select_generator (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> GreaterThan [<>,<>] +## CHECK-DAG: If [<>] +## CHECK-DAG: <> GreaterThan [<>,<>] +## CHECK-DAG: If [<>] +## CHECK-DAG: <> NotEqual [<>,<>] +## CHECK-DAG: If [<>] +## CHECK-DAG: Return [<>] +## CHECK-DAG: <> Phi [<>,<>] +## CHECK-DAG: <> Phi [<>,<>] +## CHECK-DAG: <> Phi [<>,<>] + +## CHECK-START: boolean Main2.ValuesOrdered(int, int, int) select_generator (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> GreaterThan [<>,<>] +## CHECK-DAG: <> Select [<>,<>,<>] +## CHECK-DAG: <> GreaterThan [<>,<>] +## CHECK-DAG: <> Select [<>,<>,<>] +## CHECK-DAG: <> NotEqual [<>,<>] +## CHECK-DAG: <> Select [<>,<>,<>] +## CHECK-DAG: Return [<>] + +# The original java source of this method: +# +# return (x <= y) == (y <= z); +# +.method public static ValuesOrdered(III)Z + .registers 7 + .param p0, "x" # I + .param p1, "y" # I + .param p2, "z" # I + + .prologue + const/4 v0, 0x1 + + const/4 v1, 0x0 + + .line 166 + if-gt p0, p1, :cond_b + + move v3, v0 + + :goto_5 + if-gt p1, p2, :cond_d + + move v2, v0 + + :goto_8 + if-ne v3, v2, :cond_f + + :goto_a + return v0 + + :cond_b + move v3, v1 + + goto :goto_5 + + :cond_d + move v2, v1 + + goto :goto_8 + + :cond_f + move v0, v1 + + goto :goto_a +.end method + +## CHECK-START: int Main2.NegatedCondition(boolean) select_generator (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 42 +## CHECK-DAG: <> IntConstant 43 +## CHECK-DAG: If [<>] +## CHECK-DAG: <> Phi [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int Main2.NegatedCondition(boolean) select_generator (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 42 +## CHECK-DAG: <> IntConstant 43 +## CHECK-DAG: <> Select [<>,<>,<>] +## CHECK-DAG: Return [<> Select [<>,<>,<>] +## CHECK-DAG: Return [<>] + +# The original java source of this method: +# +# int x = 0; +# int y = 1; +# while (y++ < 10) { +# if (y > 1) { +# x = 13; +# } else { +# x = 42; +# } +# } +# return x; +# +.method public static MultiplePhis()I + .registers 4 + + .prologue + .line 290 + const/4 v0, 0x0 + + .line 291 + .local v0, "x":I + const/4 v1, 0x1 + + .local v1, "y":I + move v2, v1 + + .line 292 + .end local v1 # "y":I + .local v2, "y":I + :goto_3 + add-int/lit8 v1, v2, 0x1 + + .end local v2 # "y":I + .restart local v1 # "y":I + const/16 v3, 0xa + + if-ge v2, v3, :cond_14 + + .line 293 + const/4 v3, 0x1 + + if-le v1, v3, :cond_10 + + .line 294 + const/16 v0, 0xd + + move v2, v1 + + .end local v1 # "y":I + .restart local v2 # "y":I + goto :goto_3 + + .line 296 + .end local v2 # "y":I + .restart local v1 # "y":I + :cond_10 + const/16 v0, 0x2a + + move v2, v1 + + .end local v1 # "y":I + .restart local v2 # "y":I + goto :goto_3 + + .line 299 + .end local v2 # "y":I + .restart local v1 # "y":I + :cond_14 + return v0 +.end method diff --git a/test/463-checker-boolean-simplifier/src/Main.java b/test/463-checker-boolean-simplifier/src/Main.java index d1d02cdfee..2c759ed6f9 100644 --- a/test/463-checker-boolean-simplifier/src/Main.java +++ b/test/463-checker-boolean-simplifier/src/Main.java @@ -14,6 +14,8 @@ * limitations under the License. */ +import java.lang.reflect.Method; + public class Main { // Note #1: `javac` flips the conditions of If statements. @@ -32,44 +34,6 @@ public class Main { } } - /* - * Elementary test negating a boolean. Verifies that blocks are merged and - * empty branches removed. - */ - - /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: If [<>] - /// CHECK-DAG: <> Phi [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (before) - /// CHECK: Goto - /// CHECK: Goto - /// CHECK: Goto - /// CHECK-NOT: Goto - - /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (after) - /// CHECK-NOT: If - /// CHECK-NOT: Phi - - /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (after) - /// CHECK: Goto - /// CHECK-NOT: Goto - - public static boolean BooleanNot(boolean x) { - return !x; - } - /* * Program which only delegates the condition, i.e. returns 1 when True * and 0 when False. @@ -126,72 +90,6 @@ public class Main { return (x < y) ? true : false; } - /* - * Program which further uses negated conditions. - * Note that Phis are discovered retrospectively. - */ - - /// CHECK-START: boolean Main.ValuesOrdered(int, int, int) select_generator (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> GreaterThan [<>,<>] - /// CHECK-DAG: If [<>] - /// CHECK-DAG: <> GreaterThan [<>,<>] - /// CHECK-DAG: If [<>] - /// CHECK-DAG: <> NotEqual [<>,<>] - /// CHECK-DAG: If [<>] - /// CHECK-DAG: Return [<>] - /// CHECK-DAG: <> Phi [<>,<>] - /// CHECK-DAG: <> Phi [<>,<>] - /// CHECK-DAG: <> Phi [<>,<>] - - /// CHECK-START: boolean Main.ValuesOrdered(int, int, int) select_generator (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> GreaterThan [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> GreaterThan [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> NotEqual [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - public static boolean ValuesOrdered(int x, int y, int z) { - return (x <= y) == (y <= z); - } - - /// CHECK-START: int Main.NegatedCondition(boolean) select_generator (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 42 - /// CHECK-DAG: <> IntConstant 43 - /// CHECK-DAG: If [<>] - /// CHECK-DAG: <> Phi [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.NegatedCondition(boolean) select_generator (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 42 - /// CHECK-DAG: <> IntConstant 43 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - public static int MultiplePhis() { - int x = 0; - int y = 1; - while (y++ < 10) { - if (y > 1) { - x = 13; - } else { - x = 42; - } - } - return x; - } - /// CHECK-START: int Main.TrueBlockWithTooManyInstructions(boolean) select_generator (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> ParameterValue @@ -366,23 +227,30 @@ public class Main { } public static void main(String[] args) throws Exception { - assertBoolEquals(false, BooleanNot(true)); - assertBoolEquals(true, BooleanNot(false)); + Class main2 = Class.forName("Main2"); + Method booleanNot = main2.getMethod("BooleanNot", boolean.class); + Method valuesOrdered = main2.getMethod("ValuesOrdered", int.class, int.class, int.class); + Method negatedCondition = main2.getMethod("NegatedCondition", boolean.class); + Method multiplePhis = main2.getMethod("MultiplePhis"); + + assertBoolEquals(false, (boolean)booleanNot.invoke(null, true)); + assertBoolEquals(true, (boolean)booleanNot.invoke(null, false)); assertBoolEquals(true, GreaterThan(10, 5)); assertBoolEquals(false, GreaterThan(10, 10)); assertBoolEquals(false, GreaterThan(5, 10)); assertBoolEquals(true, LessThan(5, 10)); assertBoolEquals(false, LessThan(10, 10)); assertBoolEquals(false, LessThan(10, 5)); - assertBoolEquals(true, ValuesOrdered(1, 3, 5)); - assertBoolEquals(true, ValuesOrdered(5, 3, 1)); - assertBoolEquals(false, ValuesOrdered(1, 3, 2)); - assertBoolEquals(false, ValuesOrdered(2, 3, 1)); - assertBoolEquals(true, ValuesOrdered(3, 3, 3)); - assertBoolEquals(true, ValuesOrdered(3, 3, 5)); - assertBoolEquals(false, ValuesOrdered(5, 5, 3)); - assertIntEquals(42, NegatedCondition(true)); - assertIntEquals(43, NegatedCondition(false)); + + assertBoolEquals(true, (boolean)valuesOrdered.invoke(null, 1, 3, 5)); + assertBoolEquals(true, (boolean)valuesOrdered.invoke(null, 5, 3, 1)); + assertBoolEquals(false, (boolean)valuesOrdered.invoke(null, 1, 3, 2)); + assertBoolEquals(false, (boolean)valuesOrdered.invoke(null, 2, 3, 1)); + assertBoolEquals(true, (boolean)valuesOrdered.invoke(null, 3, 3, 3)); + assertBoolEquals(true, (boolean)valuesOrdered.invoke(null, 3, 3, 5)); + assertBoolEquals(false, (boolean)valuesOrdered.invoke(null, 5, 5, 3)); + assertIntEquals(42, (int)negatedCondition.invoke(null, true)); + assertIntEquals(43, (int)negatedCondition.invoke(null, false)); assertIntEquals(46, SimpleTrueBlock(true, 4)); assertIntEquals(43, SimpleTrueBlock(false, 4)); assertIntEquals(42, SimpleFalseBlock(true, 7)); @@ -393,7 +261,7 @@ public class Main { assertIntEquals(1, ThreeBlocks(true, false)); assertIntEquals(2, ThreeBlocks(false, true)); assertIntEquals(3, ThreeBlocks(false, false)); - assertIntEquals(13, MultiplePhis()); + assertIntEquals(13, (int)multiplePhis.invoke(null)); Main m = new Main(); assertIntEquals(42, m.TrueBlockWithTooManyInstructions(true)); -- GitLab From 6834d3414308e3d536bc685dbb3d60fe70186f23 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 25 May 2018 13:12:09 +0100 Subject: [PATCH 465/749] Remove mirror::Reference::java_lang_ref_Reference. And some #include cleanup after previous CLs removing static GcRoot<>s. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Bug: 31113334 Change-Id: I56b34637f9d793ed7680696a1eeedd07d061ad00 --- openjdkjvmti/ti_class.cc | 2 +- runtime/Android.bp | 1 - runtime/class_linker.cc | 3 -- runtime/class_table-inl.h | 1 + runtime/gc/reference_processor-inl.h | 34 ---------------------- runtime/gc/reference_processor.cc | 29 +++++++++++++++++-- runtime/image-inl.h | 1 + runtime/mirror/array.h | 1 - runtime/mirror/class-inl.h | 30 ------------------- runtime/mirror/class.h | 9 ------ runtime/mirror/dex_cache.h | 1 + runtime/mirror/method_handle_impl.h | 1 - runtime/mirror/reference-inl.h | 6 ---- runtime/mirror/reference.cc | 43 ---------------------------- runtime/mirror/reference.h | 10 ------- runtime/mirror/string.h | 3 +- runtime/mirror/var_handle.h | 1 - runtime/runtime.cc | 1 - 18 files changed, 32 insertions(+), 145 deletions(-) delete mode 100644 runtime/gc/reference_processor-inl.h delete mode 100644 runtime/mirror/reference.cc diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc index c9d71b4857..726e47ed5f 100644 --- a/openjdkjvmti/ti_class.cc +++ b/openjdkjvmti/ti_class.cc @@ -63,7 +63,7 @@ #include "mirror/object-refvisitor-inl.h" #include "mirror/object_array-inl.h" #include "mirror/object_reference.h" -#include "mirror/reference.h" +#include "mirror/reference-inl.h" #include "nativehelper/scoped_local_ref.h" #include "reflection.h" #include "runtime.h" diff --git a/runtime/Android.bp b/runtime/Android.bp index 9043866e73..b276c81d5a 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -130,7 +130,6 @@ libart_cc_defaults { "mirror/method_handles_lookup.cc", "mirror/method_type.cc", "mirror/object.cc", - "mirror/reference.cc", "mirror/stack_trace_element.cc", "mirror/string.cc", "mirror/throwable.cc", diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 5d0932128c..ea6d7ccc05 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -510,7 +510,6 @@ bool ClassLinker::InitWithoutImage(std::vector> b // Setup java.lang.ref.Reference. Handle java_lang_ref_Reference(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::Reference::ClassSize(image_pointer_size_)))); - mirror::Reference::SetClass(java_lang_ref_Reference.Get()); java_lang_ref_Reference->SetObjectSize(mirror::Reference::InstanceSize()); mirror::Class::SetStatus(java_lang_ref_Reference, ClassStatus::kResolved, self); @@ -1048,7 +1047,6 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { mirror::Method::SetClass(GetClassRoot(ClassRoot::kJavaLangReflectMethod, this).Ptr()); mirror::Method::SetArrayClass( GetClassRoot(ClassRoot::kJavaLangReflectMethodArrayClass, this).Ptr()); - mirror::Reference::SetClass(GetClassRoot(ClassRoot::kJavaLangRefReference, this)); mirror::Throwable::SetClass(GetClassRoot(ClassRoot::kJavaLangThrowable, this)); mirror::StackTraceElement::SetClass(GetClassRoot(ClassRoot::kJavaLangStackTraceElement, this)); mirror::EmulatedStackFrame::SetClass( @@ -2135,7 +2133,6 @@ ClassLinker::~ClassLinker() { mirror::Constructor::ResetClass(); mirror::Field::ResetClass(); mirror::Method::ResetClass(); - mirror::Reference::ResetClass(); mirror::StackTraceElement::ResetClass(); mirror::String::ResetClass(); mirror::Throwable::ResetClass(); diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h index 5da5470c1a..6b6fe341e0 100644 --- a/runtime/class_table-inl.h +++ b/runtime/class_table-inl.h @@ -20,6 +20,7 @@ #include "class_table.h" #include "gc_root-inl.h" +#include "mirror/class.h" #include "oat_file.h" namespace art { diff --git a/runtime/gc/reference_processor-inl.h b/runtime/gc/reference_processor-inl.h deleted file mode 100644 index 0f47d3dc9f..0000000000 --- a/runtime/gc/reference_processor-inl.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_GC_REFERENCE_PROCESSOR_INL_H_ -#define ART_RUNTIME_GC_REFERENCE_PROCESSOR_INL_H_ - -#include "reference_processor.h" - -#include "mirror/reference-inl.h" - -namespace art { -namespace gc { - -inline bool ReferenceProcessor::SlowPathEnabled() { - return mirror::Reference::GetJavaLangRefReference()->GetSlowPathEnabled(); -} - -} // namespace gc -} // namespace art - -#endif // ART_RUNTIME_GC_REFERENCE_PROCESSOR_INL_H_ diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc index 5be7b325d0..fe4124d788 100644 --- a/runtime/gc/reference_processor.cc +++ b/runtime/gc/reference_processor.cc @@ -16,8 +16,10 @@ #include "reference_processor.h" +#include "art_field-inl.h" #include "base/time_utils.h" #include "base/utils.h" +#include "class_root.h" #include "collector/garbage_collector.h" #include "jni/java_vm_ext.h" #include "mirror/class-inl.h" @@ -25,7 +27,6 @@ #include "mirror/reference-inl.h" #include "nativehelper/scoped_local_ref.h" #include "object_callbacks.h" -#include "reference_processor-inl.h" #include "reflection.h" #include "scoped_thread_state_change-inl.h" #include "task_processor.h" @@ -47,15 +48,37 @@ ReferenceProcessor::ReferenceProcessor() cleared_references_(Locks::reference_queue_cleared_references_lock_) { } +static inline MemberOffset GetSlowPathFlagOffset(ObjPtr reference_class) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(reference_class == GetClassRoot()); + // Second static field + ArtField* field = reference_class->GetStaticField(1); + DCHECK_STREQ(field->GetName(), "slowPathEnabled"); + return field->GetOffset(); +} + +static inline void SetSlowPathFlag(bool enabled) REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr reference_class = GetClassRoot(); + MemberOffset slow_path_offset = GetSlowPathFlagOffset(reference_class); + reference_class->SetFieldBoolean( + slow_path_offset, enabled ? 1 : 0); +} + void ReferenceProcessor::EnableSlowPath() { - mirror::Reference::GetJavaLangRefReference()->SetSlowPath(true); + SetSlowPathFlag(/* enabled */ true); } void ReferenceProcessor::DisableSlowPath(Thread* self) { - mirror::Reference::GetJavaLangRefReference()->SetSlowPath(false); + SetSlowPathFlag(/* enabled */ false); condition_.Broadcast(self); } +bool ReferenceProcessor::SlowPathEnabled() { + ObjPtr reference_class = GetClassRoot(); + MemberOffset slow_path_offset = GetSlowPathFlagOffset(reference_class); + return reference_class->GetFieldBoolean(slow_path_offset); +} + void ReferenceProcessor::BroadcastForSlowPath(Thread* self) { MutexLock mu(self, *Locks::reference_processor_lock_); condition_.Broadcast(self); diff --git a/runtime/image-inl.h b/runtime/image-inl.h index 935a1b6705..3a66a34cb3 100644 --- a/runtime/image-inl.h +++ b/runtime/image-inl.h @@ -22,6 +22,7 @@ #include "art_method.h" #include "imt_conflict_table.h" #include "imtable.h" +#include "mirror/object_array-inl.h" #include "read_barrier-inl.h" namespace art { diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h index c9c99e8621..aeaaf67310 100644 --- a/runtime/mirror/array.h +++ b/runtime/mirror/array.h @@ -19,7 +19,6 @@ #include "base/enums.h" #include "gc/allocator_type.h" -#include "gc_root.h" #include "obj_ptr.h" #include "object.h" diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 72b31790f0..ab50973e89 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -36,7 +36,6 @@ #include "object-inl.h" #include "object_array.h" #include "read_barrier-inl.h" -#include "reference-inl.h" #include "runtime.h" #include "string.h" @@ -857,11 +856,6 @@ inline uint32_t Class::ComputeClassSize(bool has_embedded_vtable, return size; } -template -inline bool Class::IsReferenceClass() const { - return this == Reference::GetJavaLangRefReference(); -} - template inline bool Class::IsClassClass() { ObjPtr java_lang_Class = GetClass()-> @@ -916,30 +910,6 @@ inline ObjectArray>* Class::GetProxyThrows() { return GetFieldObject>>(field_offset); } -inline MemberOffset Class::GetDisableIntrinsicFlagOffset() { - CHECK(IsReferenceClass()); - // First static field - auto* field = GetStaticField(0); - DCHECK_STREQ(field->GetName(), "disableIntrinsic"); - return field->GetOffset(); -} - -inline MemberOffset Class::GetSlowPathFlagOffset() { - CHECK(IsReferenceClass()); - // Second static field - auto* field = GetStaticField(1); - DCHECK_STREQ(field->GetName(), "slowPathEnabled"); - return field->GetOffset(); -} - -inline bool Class::GetSlowPathEnabled() { - return GetFieldBoolean(GetSlowPathFlagOffset()); -} - -inline void Class::SetSlowPath(bool enabled) { - SetFieldBoolean(GetSlowPathFlagOffset(), enabled); -} - inline void Class::InitializeClassVisitor::operator()(ObjPtr obj, size_t usable_size) const { DCHECK_LE(class_size_, usable_size); diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 98e25eb320..7d5f539576 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -437,9 +437,6 @@ class MANAGED Class FINAL : public Object { bool IsThrowableClass() REQUIRES_SHARED(Locks::mutator_lock_); - template - bool IsReferenceClass() const REQUIRES_SHARED(Locks::mutator_lock_); - static MemberOffset ComponentTypeOffset() { return OFFSET_OF_OBJECT_MEMBER(Class, component_type_); } @@ -1212,12 +1209,6 @@ class MANAGED Class FINAL : public Object { // For proxy class only. ObjectArray>* GetProxyThrows() REQUIRES_SHARED(Locks::mutator_lock_); - // For reference class only. - MemberOffset GetDisableIntrinsicFlagOffset() REQUIRES_SHARED(Locks::mutator_lock_); - MemberOffset GetSlowPathFlagOffset() REQUIRES_SHARED(Locks::mutator_lock_); - bool GetSlowPathEnabled() REQUIRES_SHARED(Locks::mutator_lock_); - void SetSlowPath(bool enabled) REQUIRES_SHARED(Locks::mutator_lock_); - // May cause thread suspension due to EqualParameters. ArtMethod* GetDeclaredConstructor(Thread* self, Handle> args, diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index 9aff9ec49a..bb86004a90 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -21,6 +21,7 @@ #include "base/bit_utils.h" #include "base/mutex.h" #include "dex/dex_file_types.h" +#include "gc_root-inl.h" #include "object.h" #include "object_array.h" diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h index 4813b3c3f7..030a49ed1e 100644 --- a/runtime/mirror/method_handle_impl.h +++ b/runtime/mirror/method_handle_impl.h @@ -20,7 +20,6 @@ #include "art_field.h" #include "art_method.h" #include "class.h" -#include "gc_root.h" #include "method_type.h" #include "object.h" diff --git a/runtime/mirror/reference-inl.h b/runtime/mirror/reference-inl.h index 84e54948dd..c65f740a78 100644 --- a/runtime/mirror/reference-inl.h +++ b/runtime/mirror/reference-inl.h @@ -49,12 +49,6 @@ inline void FinalizerReference::SetZombie(ObjPtr zombie) { return SetFieldObjectVolatile(ZombieOffset(), zombie); } -template -inline Class* Reference::GetJavaLangRefReference() { - DCHECK(!java_lang_ref_Reference_.IsNull()); - return java_lang_ref_Reference_.Read(); -} - } // namespace mirror } // namespace art diff --git a/runtime/mirror/reference.cc b/runtime/mirror/reference.cc deleted file mode 100644 index 1d0b4c5b27..0000000000 --- a/runtime/mirror/reference.cc +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2011 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 "reference-inl.h" - -#include "art_method.h" -#include "gc_root-inl.h" - -namespace art { -namespace mirror { - -GcRoot Reference::java_lang_ref_Reference_; - -void Reference::SetClass(ObjPtr java_lang_ref_Reference) { - CHECK(java_lang_ref_Reference_.IsNull()); - CHECK(java_lang_ref_Reference != nullptr); - java_lang_ref_Reference_ = GcRoot(java_lang_ref_Reference); -} - -void Reference::ResetClass() { - CHECK(!java_lang_ref_Reference_.IsNull()); - java_lang_ref_Reference_ = GcRoot(nullptr); -} - -void Reference::VisitRoots(RootVisitor* visitor) { - java_lang_ref_Reference_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - -} // namespace mirror -} // namespace art diff --git a/runtime/mirror/reference.h b/runtime/mirror/reference.h index b10c29444e..63c5ae5ff8 100644 --- a/runtime/mirror/reference.h +++ b/runtime/mirror/reference.h @@ -20,8 +20,6 @@ #include "base/enums.h" #include "base/macros.h" #include "base/mutex.h" -#include "class.h" -#include "gc_root.h" #include "obj_ptr.h" #include "object.h" #include "read_barrier_option.h" @@ -98,12 +96,6 @@ class MANAGED Reference : public Object { return GetPendingNext() == nullptr; } - template - static ALWAYS_INLINE Class* GetJavaLangRefReference() REQUIRES_SHARED(Locks::mutator_lock_); - static void SetClass(ObjPtr klass); - static void ResetClass(); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: // Note: This avoids a read barrier, it should only be used by the GC. HeapReference* GetReferentReferenceAddr() REQUIRES_SHARED(Locks::mutator_lock_) { @@ -116,8 +108,6 @@ class MANAGED Reference : public Object { HeapReference queue_next_; HeapReference referent_; // Note this is Java volatile: - static GcRoot java_lang_ref_Reference_; - friend struct art::ReferenceOffsets; // for verifying offset information friend class gc::ReferenceProcessor; friend class gc::ReferenceQueue; diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index 545fe93516..c45dc499e5 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -18,7 +18,8 @@ #define ART_RUNTIME_MIRROR_STRING_H_ #include "gc/allocator_type.h" -#include "gc_root.h" +#include "gc_root-inl.h" +#include "class.h" #include "object.h" namespace art { diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h index 4fd18c14e2..9829456854 100644 --- a/runtime/mirror/var_handle.h +++ b/runtime/mirror/var_handle.h @@ -19,7 +19,6 @@ #include "handle.h" #include "interpreter/shadow_frame.h" -#include "gc_root.h" #include "jvalue.h" #include "object.h" diff --git a/runtime/runtime.cc b/runtime/runtime.cc index d126fe979a..e42bbc20ef 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1978,7 +1978,6 @@ void Runtime::VisitConstantRoots(RootVisitor* visitor) { // need to be visited once per GC since they never change. mirror::Class::VisitRoots(visitor); mirror::Constructor::VisitRoots(visitor); - mirror::Reference::VisitRoots(visitor); mirror::Method::VisitRoots(visitor); mirror::StackTraceElement::VisitRoots(visitor); mirror::String::VisitRoots(visitor); -- GitLab From 679730e6c4be680b3e72b55a4200de9590641009 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 25 May 2018 15:06:48 +0100 Subject: [PATCH 466/749] Remove static GcRoot<>s from Field, Method, Constructor. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Bug: 31113334 Change-Id: I648b88339995761fb81180286ef48a42bbd2f83d --- dex2oat/linker/image_writer.cc | 10 ++++- patchoat/patchoat.cc | 5 ++- runtime/check_jni.cc | 8 +++- runtime/class_linker.cc | 22 +---------- runtime/jni/jni_internal.cc | 2 +- runtime/mirror/field-inl.h | 3 +- runtime/mirror/field.cc | 30 -------------- runtime/mirror/field.h | 20 ---------- runtime/mirror/method.cc | 65 ++----------------------------- runtime/mirror/method.h | 43 -------------------- runtime/native/java_lang_Class.cc | 10 ++--- runtime/proxy_test.h | 3 +- runtime/runtime.cc | 9 ++--- 13 files changed, 36 insertions(+), 194 deletions(-) diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 058724570d..028de34e96 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -778,8 +778,11 @@ class ImageWriter::PruneObjectReferenceVisitor { return; } + ObjPtr> class_roots = + Runtime::Current()->GetClassLinker()->GetClassRoots(); ObjPtr klass = ref->IsClass() ? ref->AsClass() : ref->GetClass(); - if (klass == mirror::Method::StaticClass() || klass == mirror::Constructor::StaticClass()) { + if (klass == GetClassRoot(class_roots) || + klass == GetClassRoot(class_roots)) { // Prune all classes using reflection because the content they held will not be fixup. *result_ = true; } @@ -2402,7 +2405,10 @@ void ImageWriter::FixupObject(Object* orig, Object* copy) { if (orig->IsClass()) { FixupClass(orig->AsClass(), down_cast(copy)); } else { - if (klass == mirror::Method::StaticClass() || klass == mirror::Constructor::StaticClass()) { + ObjPtr> class_roots = + Runtime::Current()->GetClassLinker()->GetClassRoots(); + if (klass == GetClassRoot(class_roots) || + klass == GetClassRoot(class_roots)) { // Need to go update the ArtMethod. auto* dest = down_cast(copy); auto* src = down_cast(orig); diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index dc9d990e29..a6d3903f19 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -43,6 +43,7 @@ #include "base/unix_file/fd_file.h" #include "base/unix_file/random_access_file_utils.h" #include "base/utils.h" +#include "class_root.h" #include "elf_file.h" #include "elf_file_impl.h" #include "elf_utils.h" @@ -1053,8 +1054,8 @@ void PatchOat::VisitObject(mirror::Object* object) { native_visitor); } } - } else if (object->GetClass() == mirror::Method::StaticClass() || - object->GetClass() == mirror::Constructor::StaticClass()) { + } else if (object->GetClass() == GetClassRoot() || + object->GetClass() == GetClassRoot()) { // Need to go update the ArtMethod. auto* dest = down_cast(copy); auto* src = down_cast(object); diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index f8b977eea7..0ff55ae25b 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -31,6 +31,7 @@ #include "base/time_utils.h" #include "class_linker-inl.h" #include "class_linker.h" +#include "class_root.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "gc/space/space.h" @@ -638,8 +639,11 @@ class ScopedCheck { AbortF("expected non-null method"); return false; } + ObjPtr> class_roots = + Runtime::Current()->GetClassLinker()->GetClassRoots(); ObjPtr c = method->GetClass(); - if (mirror::Method::StaticClass() != c && mirror::Constructor::StaticClass() != c) { + if (c != GetClassRoot(class_roots) && + c != GetClassRoot(class_roots)) { AbortF("expected java.lang.reflect.Method or " "java.lang.reflect.Constructor but got object of type %s: %p", method->PrettyTypeOf().c_str(), jmethod); @@ -669,7 +673,7 @@ class ScopedCheck { return false; } ObjPtr c = field->GetClass(); - if (mirror::Field::StaticClass() != c) { + if (GetClassRoot() != c) { AbortF("expected java.lang.reflect.Field but got object of type %s: %p", field->PrettyTypeOf().c_str(), jfield); return false; diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index ea6d7ccc05..a2e26866d1 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -688,33 +688,27 @@ bool ClassLinker::InitWithoutImage(std::vector> b auto* class_root = FindSystemClass(self, "Ljava/lang/reflect/Field;"); CHECK(class_root != nullptr); SetClassRoot(ClassRoot::kJavaLangReflectField, class_root); - mirror::Field::SetClass(class_root); // Create java.lang.reflect.Field array root. class_root = FindSystemClass(self, "[Ljava/lang/reflect/Field;"); CHECK(class_root != nullptr); SetClassRoot(ClassRoot::kJavaLangReflectFieldArrayClass, class_root); - mirror::Field::SetArrayClass(class_root); // Create java.lang.reflect.Constructor.class root and array root. class_root = FindSystemClass(self, "Ljava/lang/reflect/Constructor;"); CHECK(class_root != nullptr); SetClassRoot(ClassRoot::kJavaLangReflectConstructor, class_root); - mirror::Constructor::SetClass(class_root); class_root = FindSystemClass(self, "[Ljava/lang/reflect/Constructor;"); CHECK(class_root != nullptr); SetClassRoot(ClassRoot::kJavaLangReflectConstructorArrayClass, class_root); - mirror::Constructor::SetArrayClass(class_root); // Create java.lang.reflect.Method.class root and array root. class_root = FindSystemClass(self, "Ljava/lang/reflect/Method;"); CHECK(class_root != nullptr); SetClassRoot(ClassRoot::kJavaLangReflectMethod, class_root); - mirror::Method::SetClass(class_root); class_root = FindSystemClass(self, "[Ljava/lang/reflect/Method;"); CHECK(class_root != nullptr); SetClassRoot(ClassRoot::kJavaLangReflectMethodArrayClass, class_root); - mirror::Method::SetArrayClass(class_root); // Create java.lang.invoke.CallSite.class root class_root = FindSystemClass(self, "Ljava/lang/invoke/CallSite;"); @@ -1039,14 +1033,6 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { GcRoot(GetClassRoot(ClassRoot::kObjectArrayClass, this)->GetIfTable()); DCHECK_EQ(array_iftable_.Read(), GetClassRoot(ClassRoot::kBooleanArrayClass, this)->GetIfTable()); // String class root was set above - mirror::Field::SetClass(GetClassRoot(ClassRoot::kJavaLangReflectField, this)); - mirror::Field::SetArrayClass(GetClassRoot(ClassRoot::kJavaLangReflectFieldArrayClass, this)); - mirror::Constructor::SetClass(GetClassRoot(ClassRoot::kJavaLangReflectConstructor, this).Ptr()); - mirror::Constructor::SetArrayClass( - GetClassRoot(ClassRoot::kJavaLangReflectConstructorArrayClass, this).Ptr()); - mirror::Method::SetClass(GetClassRoot(ClassRoot::kJavaLangReflectMethod, this).Ptr()); - mirror::Method::SetArrayClass( - GetClassRoot(ClassRoot::kJavaLangReflectMethodArrayClass, this).Ptr()); mirror::Throwable::SetClass(GetClassRoot(ClassRoot::kJavaLangThrowable, this)); mirror::StackTraceElement::SetClass(GetClassRoot(ClassRoot::kJavaLangStackTraceElement, this)); mirror::EmulatedStackFrame::SetClass( @@ -2130,15 +2116,9 @@ void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor) { ClassLinker::~ClassLinker() { mirror::Class::ResetClass(); - mirror::Constructor::ResetClass(); - mirror::Field::ResetClass(); - mirror::Method::ResetClass(); mirror::StackTraceElement::ResetClass(); mirror::String::ResetClass(); mirror::Throwable::ResetClass(); - mirror::Constructor::ResetArrayClass(); - mirror::Field::ResetArrayClass(); - mirror::Method::ResetArrayClass(); mirror::EmulatedStackFrame::ResetClass(); Thread* const self = Thread::Current(); for (const ClassLoaderData& data : class_loaders_) { @@ -4391,7 +4371,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& // They have as many virtual methods as the array auto h_methods = hs.NewHandle(soa.Decode>(methods)); - DCHECK_EQ(h_methods->GetClass(), mirror::Method::ArrayClass()) + DCHECK_EQ(h_methods->GetClass(), GetClassRoot>()) << mirror::Class::PrettyClass(h_methods->GetClass()); const size_t num_virtual_methods = h_methods->GetLength(); diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc index cf6751782b..987c8e974f 100644 --- a/runtime/jni/jni_internal.cc +++ b/runtime/jni/jni_internal.cc @@ -489,7 +489,7 @@ class JNI { CHECK_NON_NULL_ARGUMENT(jlr_field); ScopedObjectAccess soa(env); ObjPtr obj_field = soa.Decode(jlr_field); - if (obj_field->GetClass() != mirror::Field::StaticClass()) { + if (obj_field->GetClass() != GetClassRoot()) { // Not even a java.lang.reflect.Field, return null. TODO, is this check necessary? return nullptr; } diff --git a/runtime/mirror/field-inl.h b/runtime/mirror/field-inl.h index bcd2db4dbd..2e263b9517 100644 --- a/runtime/mirror/field-inl.h +++ b/runtime/mirror/field-inl.h @@ -21,6 +21,7 @@ #include "art_field-inl.h" #include "class-inl.h" +#include "class_root.h" #include "dex_cache-inl.h" namespace art { @@ -48,7 +49,7 @@ inline mirror::Field* Field::CreateFromArtField(Thread* self, ArtField* field, b self->ClearException(); } } - auto ret = hs.NewHandle(ObjPtr::DownCast(StaticClass()->AllocObject(self))); + auto ret = hs.NewHandle(ObjPtr::DownCast(GetClassRoot()->AllocObject(self))); if (UNLIKELY(ret == nullptr)) { self->AssertPendingOOMException(); return nullptr; diff --git a/runtime/mirror/field.cc b/runtime/mirror/field.cc index b4d93b6d4d..a2b51d8d9a 100644 --- a/runtime/mirror/field.cc +++ b/runtime/mirror/field.cc @@ -24,36 +24,6 @@ namespace art { namespace mirror { -GcRoot Field::static_class_; -GcRoot Field::array_class_; - -void Field::SetClass(ObjPtr klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void Field::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void Field::SetArrayClass(ObjPtr klass) { - CHECK(array_class_.IsNull()) << array_class_.Read() << " " << klass; - CHECK(klass != nullptr); - array_class_ = GcRoot(klass); -} - -void Field::ResetArrayClass() { - CHECK(!array_class_.IsNull()); - array_class_ = GcRoot(nullptr); -} - -void Field::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); - array_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - ArtField* Field::GetArtField() { mirror::Class* declaring_class = GetDeclaringClass(); if (UNLIKELY(declaring_class->IsProxyClass())) { diff --git a/runtime/mirror/field.h b/runtime/mirror/field.h index 03fd031304..3501e71f82 100644 --- a/runtime/mirror/field.h +++ b/runtime/mirror/field.h @@ -21,7 +21,6 @@ #include "base/enums.h" #include "dex/modifiers.h" #include "dex/primitive.h" -#include "gc_root.h" #include "obj_ptr.h" #include "object.h" #include "read_barrier_option.h" @@ -39,14 +38,6 @@ class String; // C++ mirror of java.lang.reflect.Field. class MANAGED Field : public AccessibleObject { public: - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); - } - - static mirror::Class* ArrayClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return array_class_.Read(); - } - ALWAYS_INLINE uint32_t GetDexFieldIndex() REQUIRES_SHARED(Locks::mutator_lock_) { return GetField32(OFFSET_OF_OBJECT_MEMBER(Field, dex_field_index_)); } @@ -81,14 +72,6 @@ class MANAGED Field : public AccessibleObject { return GetField32(OFFSET_OF_OBJECT_MEMBER(Field, offset_)); } - static void SetClass(ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - - static void SetArrayClass(ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetArrayClass() REQUIRES_SHARED(Locks::mutator_lock_); - - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - // Slow, try to use only for PrettyField and such. ArtField* GetArtField() REQUIRES_SHARED(Locks::mutator_lock_); @@ -128,9 +111,6 @@ class MANAGED Field : public AccessibleObject { SetField32(OFFSET_OF_OBJECT_MEMBER(Field, offset_), offset); } - static GcRoot static_class_; // java.lang.reflect.Field.class. - static GcRoot array_class_; // array of java.lang.reflect.Field. - friend struct art::FieldOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(Field); }; diff --git a/runtime/mirror/method.cc b/runtime/mirror/method.cc index 25cbdc131b..e5d3403107 100644 --- a/runtime/mirror/method.cc +++ b/runtime/mirror/method.cc @@ -17,6 +17,7 @@ #include "method.h" #include "art_method.h" +#include "class_root.h" #include "gc_root-inl.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" @@ -24,37 +25,10 @@ namespace art { namespace mirror { -GcRoot Method::static_class_; -GcRoot Method::array_class_; -GcRoot Constructor::static_class_; -GcRoot Constructor::array_class_; - -void Method::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void Method::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void Method::SetArrayClass(Class* klass) { - CHECK(array_class_.IsNull()) << array_class_.Read() << " " << klass; - CHECK(klass != nullptr); - array_class_ = GcRoot(klass); -} - -void Method::ResetArrayClass() { - CHECK(!array_class_.IsNull()); - array_class_ = GcRoot(nullptr); -} - template Method* Method::CreateFromArtMethod(Thread* self, ArtMethod* method) { DCHECK(!method->IsConstructor()) << method->PrettyMethod(); - ObjPtr ret = ObjPtr::DownCast(StaticClass()->AllocObject(self)); + ObjPtr ret = ObjPtr::DownCast(GetClassRoot()->AllocObject(self)); if (LIKELY(ret != nullptr)) { ObjPtr(ret)-> CreateFromArtMethod(method); @@ -71,42 +45,11 @@ template Method* Method::CreateFromArtMethod(Thread* se template Method* Method::CreateFromArtMethod(Thread* self, ArtMethod* method); -void Method::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); - array_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - -void Constructor::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void Constructor::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void Constructor::SetArrayClass(Class* klass) { - CHECK(array_class_.IsNull()) << array_class_.Read() << " " << klass; - CHECK(klass != nullptr); - array_class_ = GcRoot(klass); -} - -void Constructor::ResetArrayClass() { - CHECK(!array_class_.IsNull()); - array_class_ = GcRoot(nullptr); -} - -void Constructor::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); - array_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - template Constructor* Constructor::CreateFromArtMethod(Thread* self, ArtMethod* method) { DCHECK(method->IsConstructor()) << method->PrettyMethod(); - ObjPtr ret = ObjPtr::DownCast(StaticClass()->AllocObject(self)); + ObjPtr ret = + ObjPtr::DownCast(GetClassRoot()->AllocObject(self)); if (LIKELY(ret != nullptr)) { ObjPtr(ret)-> CreateFromArtMethod(method); diff --git a/runtime/mirror/method.h b/runtime/mirror/method.h index 61332e3bd9..aea15a7748 100644 --- a/runtime/mirror/method.h +++ b/runtime/mirror/method.h @@ -18,7 +18,6 @@ #define ART_RUNTIME_MIRROR_METHOD_H_ #include "executable.h" -#include "gc_root.h" namespace art { namespace mirror { @@ -32,28 +31,7 @@ class MANAGED Method : public Executable { static Method* CreateFromArtMethod(Thread* self, ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); - } - - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - - static mirror::Class* ArrayClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return array_class_.Read(); - } - - static void SetArrayClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - - static void ResetArrayClass() REQUIRES_SHARED(Locks::mutator_lock_); - - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: - static GcRoot static_class_; // java.lang.reflect.Method.class. - static GcRoot array_class_; // [java.lang.reflect.Method.class. - DISALLOW_COPY_AND_ASSIGN(Method); }; @@ -64,28 +42,7 @@ class MANAGED Constructor: public Executable { static Constructor* CreateFromArtMethod(Thread* self, ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); - } - - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - - static mirror::Class* ArrayClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return array_class_.Read(); - } - - static void SetArrayClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - - static void ResetArrayClass() REQUIRES_SHARED(Locks::mutator_lock_); - - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: - static GcRoot static_class_; // java.lang.reflect.Constructor.class. - static GcRoot array_class_; // [java.lang.reflect.Constructor.class. - DISALLOW_COPY_AND_ASSIGN(Constructor); }; diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index b7dad89106..261178b0ee 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -282,7 +282,7 @@ static mirror::ObjectArray* GetDeclaredFields( } size_t array_idx = 0; auto object_array = hs.NewHandle(mirror::ObjectArray::Alloc( - self, mirror::Field::ArrayClass(), array_size)); + self, GetClassRoot>(), array_size)); if (object_array == nullptr) { return nullptr; } @@ -539,7 +539,7 @@ static jobjectArray Class_getDeclaredConstructorsInternal( constructor_count += MethodMatchesConstructor(&m, public_only, enforce_hidden_api) ? 1u : 0u; } auto h_constructors = hs.NewHandle(mirror::ObjectArray::Alloc( - soa.Self(), mirror::Constructor::ArrayClass(), constructor_count)); + soa.Self(), GetClassRoot>(), constructor_count)); if (UNLIKELY(h_constructors == nullptr)) { soa.Self()->AssertPendingException(); return nullptr; @@ -598,7 +598,7 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT } } auto ret = hs.NewHandle(mirror::ObjectArray::Alloc( - soa.Self(), mirror::Method::ArrayClass(), num_methods)); + soa.Self(), GetClassRoot>(), num_methods)); if (ret == nullptr) { soa.Self()->AssertPendingOOMException(); return nullptr; @@ -702,7 +702,7 @@ static jobject Class_getEnclosingConstructorNative(JNIEnv* env, jobject javaThis } ObjPtr method = annotations::GetEnclosingMethod(klass); if (method != nullptr) { - if (mirror::Constructor::StaticClass() == method->GetClass()) { + if (GetClassRoot() == method->GetClass()) { return soa.AddLocalReference(method); } } @@ -718,7 +718,7 @@ static jobject Class_getEnclosingMethodNative(JNIEnv* env, jobject javaThis) { } ObjPtr method = annotations::GetEnclosingMethod(klass); if (method != nullptr) { - if (mirror::Method::StaticClass() == method->GetClass()) { + if (GetClassRoot() == method->GetClass()) { return soa.AddLocalReference(method); } } diff --git a/runtime/proxy_test.h b/runtime/proxy_test.h index 98362649f5..fa5a449e31 100644 --- a/runtime/proxy_test.h +++ b/runtime/proxy_test.h @@ -22,6 +22,7 @@ #include "art_method-inl.h" #include "class_linker-inl.h" +#include "class_root.h" #include "mirror/class-inl.h" #include "mirror/method.h" @@ -59,7 +60,7 @@ mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, methods_count += interface->NumVirtualMethods(); } jobjectArray proxyClassMethods = soa.Env()->NewObjectArray( - methods_count, soa.AddLocalReference(mirror::Method::StaticClass()), nullptr); + methods_count, soa.AddLocalReference(GetClassRoot()), nullptr); soa.Self()->AssertNoPendingException(); jsize array_index = 0; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index e42bbc20ef..630053ac71 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -67,6 +67,7 @@ #include "base/unix_file/fd_file.h" #include "base/utils.h" #include "class_linker-inl.h" +#include "class_root.h" #include "compiler_callbacks.h" #include "debugger.h" #include "dex/art_dex_file_loader.h" @@ -736,8 +737,9 @@ bool Runtime::Start() { ScopedObjectAccess soa(self); StackHandleScope<2> hs(soa.Self()); - auto class_class(hs.NewHandle(mirror::Class::GetJavaLangClass())); - auto field_class(hs.NewHandle(mirror::Field::StaticClass())); + ObjPtr> class_roots = GetClassLinker()->GetClassRoots(); + auto class_class(hs.NewHandle(GetClassRoot(class_roots))); + auto field_class(hs.NewHandle(GetClassRoot(class_roots))); class_linker_->EnsureInitialized(soa.Self(), class_class, true, true); // Field class is needed for register_java_net_InetAddress in libcore, b/28153851. @@ -1977,12 +1979,9 @@ void Runtime::VisitConstantRoots(RootVisitor* visitor) { // Visit the classes held as static in mirror classes, these can be visited concurrently and only // need to be visited once per GC since they never change. mirror::Class::VisitRoots(visitor); - mirror::Constructor::VisitRoots(visitor); - mirror::Method::VisitRoots(visitor); mirror::StackTraceElement::VisitRoots(visitor); mirror::String::VisitRoots(visitor); mirror::Throwable::VisitRoots(visitor); - mirror::Field::VisitRoots(visitor); mirror::EmulatedStackFrame::VisitRoots(visitor); mirror::ClassExt::VisitRoots(visitor); // Visiting the roots of these ArtMethods is not currently required since all the GcRoots are -- GitLab From 0f6cc7fc009a006f0dcecca67e3a13c94d503f3f Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Fri, 25 May 2018 15:33:44 +0100 Subject: [PATCH 467/749] Dexdump: fix type printing for call sites Test: art/test/dexdump/run-all-tests Change-Id: I0a8eabd413ad6f26946af26946bc163e65d1d001 --- dexdump/dexdump.cc | 5 +- test/dexdump/invoke-custom.dex | Bin 8984 -> 31732 bytes test/dexdump/invoke-custom.lst | 178 +- test/dexdump/invoke-custom.txt | 6710 ++++++++++++++++++++++++++------ test/dexdump/invoke-custom.xml | 844 ++-- 5 files changed, 6158 insertions(+), 1579 deletions(-) diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index f5a13f0920..85778b6411 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -1773,9 +1773,8 @@ static void dumpCallSite(const DexFile* pDexFile, u4 idx) { case EncodedArrayValueIterator::ValueType::kType: { type = "Class"; dex::TypeIndex type_idx = static_cast(it.GetJavaValue().i); - const DexFile::ClassDef* class_def = pDexFile->FindClassDef(type_idx); - value = pDexFile->GetClassDescriptor(*class_def); - value = descriptorClassToDot(value.c_str()).get(); + const DexFile::TypeId& type_id = pDexFile->GetTypeId(type_idx); + value = pDexFile->GetTypeDescriptor(type_id); break; } case EncodedArrayValueIterator::ValueType::kField: diff --git a/test/dexdump/invoke-custom.dex b/test/dexdump/invoke-custom.dex index dab6f0f0d61a4e9131765779619e7d283594ce09..c7de9dedf0d8b10b4030eb5a89f75882ac50f7d7 100644 GIT binary patch literal 31732 zcmYdEt>7{+wqPhxEV{FGmV5tGzqNIK98o{D6#r~xwf({TrJ8}EfPsObBFsdH0Sb7k z85r&{Ld1O;7#Lo#GcZImFfhn)GBAiTFfb(WGBA8*U|`_jV_@K8U|?7v%E0g?iGhJZ zl7V5LCj-MlPX>mgo(v49JsBAOc``8Qc`-1UdoeIrc`-29dND9KdNDA#doeKhc`-1A zc`-0Vc`+~~c`-0#c`+~)c`-0lc`-0Fc`-0_c`-0d@?v0^<;B1-*NcH+ffobADlZ0x zOwt3|ig{ z48h(E3}xO744vK#3~Rg@7&dw{FdXt`V7TGUz;NH2f#JC~1H&tC28MUu3=E&W85sU} zGcYjuFfefVFfefYFfj1>Ffa)DFffSuFfd5@FfhpZFfb_lFfge5FfeHQFfbVUFff?; zFfiEoFfh3IFfjP|FffGpFfgR}Ffio!Fff$(Ffdg6FfcUxFfercFfdH@VPKf+!@zLK zhk@a}4+Fz*9|i^)Uj_y%Uj~LiUj~L6Uj~Myz6=Z}eHj=&`7$u@_%Sf(`Y|xr`Y|v# z`Y|vh_%Sfl_%Sd{@nc}v;m5#m!;gXCw;uz8ls^N5nLh)AgFgd9xIY6!g+Bws6n_SW zZT<`lXZ;x%9{4jbeD-Hx;0$13&$5uuqJ?kVP60P!`%P| zhK~UZ4D5jn4AOxN3}%503|@f@3~_-B4CR3g3=;ww7?uYzFzgIuV7L~@!03=DaSgn_{>gn=O%~B6UM-h5XQie8pgno6~@3&5XQhz62`z#8OFd+8^*xU z6vn{N9>&1X8^*veDU5+(S{MVvyf6la6=4hvtHT%=HiR)SYzbpv*b&CSuqTXx;XoJz z!<8@whF4(>4FAIz7(~Ju7&OBf7~I1d7)rw#7^=e=7@EQv7&^ll7<$4P7-obsFf0vc zU|1E-z_1~lfni5D1H-;>28NU23=9{-85r(`GcbG%XJBB7U|j&U|=YZU|{HoU|^UZ!N9OFf`MUg1Ovmx2nL255ey8sBN!O&M=&rvj$mMT9l^lx zC4zy0DUyLfIFf-uEs}x3Jd%OICX#_6Dw2VrJd%N7QX~Vzj7SECMUe~)DZ804ZE7&M|87!0Br7%ZY07`&nx7($~N7!soy7}BB{ z7`mew7#2k{Ff5H`U|1Q=z_2!&fniTH1H;K^28J8a3=D6g85q7tGcf#$W?GnRp2ZY%@CvRDR&ZLtgt2V)r+F2*u2+=yjhxE;&D za6guT;c+Yj!>d>Zh99vE44iQc48m~?49al~42E$G4AyZB41RG83}JB$47qU(49#&2 z40Gcc7*@qGFdU9!V0aYA!05*QdH5*Zkj6B!t^6B!sR5*Zl06B!uN5*Zj86B!t0B{DEl*qtvB9VdN zN+JWp(?kY_w}}i4-x3)Z{wFdpa3nD>2qrNwNF^~aC?_#6=p->Pm?kkW*d;MAxF<0% z1SByq#3V5=q$V*i6eTe*R3|YobR;n_OiW^6n3u%Busn%@VM`JN!+|6Qh9gM~3}=!U z7#=4vFg#CUV0fFv!0fuSpzfni261H-~(28Pwi3=Er-85s5_GcX)UW?(p;%)oFunStSa zG6TcSWCn(N$qWoHlNlI(Co?c`r!X*xr!X+cr7$q)q%bg8rZ6zrr!X+Mr!X)?rZ6z1 zq%bgKr7$p5rZ6ycr7$qePhntKlfuAoB87qBb_xRnLn;G@(K_HERK{$VL63of!GwWSvKSZ`vKbf{au^sGav2yH@)#Hx z@);m)i9!Yjh9U+AhGGT=h7tw_hEfIwhB5{QhH_B*gMopeih+Tlnt_3#hJk^hmVtqx zj)8%po`Hd(fq{Xck%57siGhKknSp_!g@J*gm4Shwje&uooq>U&mw|y{DFXw;P6h^s z-3$y2dl(oP_A)Rq>|+V9Feo!HFfcJN zFhsaBFlaI`FffDs<_-xv7HC^YpMik^#LjSMU@(BP3)~qPjG*iacLoMyD7(d-fx(`E zfq|WYfnkC>1A`?40|Pe$0|Q8}9jN`pz`!uaoq@rWfq_90YCcE}hz(L>4t3KKcLoLv zXgscgs#ix+4`PGVgX{vaL3UX)FfeE^FfeR^s@H<52dM$EL27Im7#Kk8Jy10|P&FWN z5c`Nb0|ShG28n$IiG2r&4Kl}!fq_Anfq~(PI|BpA{UA2X{UA2T{UASr*dRB9+zDcX z+zGM^#C`)c6U6@F&cF}>HS-VDorX|%g8TvsYmi?Y7@*vM0uKfTcc}OV4+aKLDEk1C z-YZD#2T1HUNbDa-Y!**QdH|&fkT*eo1hEA?A!!N3mhog@0Hq%gTf>uq!IlA{#{h|K z12qSfZb0T(F+kD~D6N9R86@rjH50@RfT{;!iPhSg9uD6ZE)*(II~3~QlmkbBla*)^UF4C|q6PUdCwMY2Y=p8w>0~pMJp)NRz#X9mlzw1pKzR+OcY!AZ!xpGsQ2K$1ukd7G z*a{Wj;K{(S4ax?kJ(!vuo{+Kyl+Hlmvx@@GrU~z8-H!y9-FcD1qFl++T1`K-`85sB(SQu0pAha5k z2IX~-I4GZkXhTK@1_1^Zh8GMB3;Z(z6C>OlSh$!`JkSsAu7 zg6c7MaQyQ?-Oa-QaVHPdJRWFz0Lg>Q{2fqwCzRgJ$iTo0bvG{qAA=K^ zF9fFfpy30eLE*#4;J|PSoKGR)#0T{khz5l-9|IGE4LF~wFr%+L2)qm}X{J z0j44D;)j|KqCw_^^6fEbem(=HA@+deLE**^jX!<{i23{sUSR(TK+OTsAaev5m>9Gf zAo(9;9;h4unI`~^7Xb!H`VoM}j{pNC{Rlw)12P9>p8$gf*dHKupl}p~hLa!zBg1h9 z1_ls)0!p6+(-3n(>OlHI^6MEv>LC6Vg6ap+AbBAM6NVoQ3=BdHx(q+Tv@ydkFm1^2 z8%(P)`~lNi41dA2Hp4$KZOZT;OdByUFoN_uGL%B;GALaRr7NIxC6um$($!G921?gL z={hJ~52YKSbTgE0h0^U{I*4H|n1=XI80tS^1|4v|kzwFuFk)n2kYR9Pm;QH$iQI6V9B5frp+0&ptLrawqVc!)8-7iP+AX6TQKN@ zX)cB=Mo=5nia{7mTQfK_g4#fK3>;wEmO%te+c9*4X?rNm#4rQQ=Vq7*rnwjvGJ@Lf zp4rI3=)i>wi%?Hv}Isoc)$Q^w?X)J3`}7EgJ=^* zi1>1Fy$WhkfWiYpPhenRcvH^6@C!633ogeYoeNOA9i(C+hyV$JI#b|w2SflSKZSvT z;ZHdOgB?_!7a|SryMUNe85kINDi|2Npz^{XL1@1K#9RXEr&KU7#6soS7{J+&ffFRj zz`)P}>Q`1UFyuhxInd-m?oX*;U}%HNb3ygN^iKxaU&+9r0_q5Y*l6+~_v=(LFnB@b zLF3De&~N~?^FZ#OQ^~-vpooFtXfddLgzCQO3=9lsDj67V7cnrLCWz+h9wz~BXyzgWz`aIctw;dL za4KbB2rXq`NGfGu$SGxDXf0)6SWwEqu&P50ebD5<{T^3xg4Zkb*E1H$*)XKJ{ua^#;OBJgDlK zco~Ekgqe7uqI}@-2Q~&q25E5nn*}_sz{bGNz|1Jm#VD`DC~E~7uKCOgaon%t8!8BFZjq3_`3)Y=R8FY%HvW29FqwSa_H~CMq(p3&6}2X8O-6 z%$&mT%iuDDBC`ihv-!Z|Cfp2+;IR@=n~8~0Rh&`P6&jli3}+ocbuI%#Avn|uK}`gP zLhxu#Awv%XNE|fsR0y88Dg^iP3&HKBLU1n@)QV#QtA1b)O&1E67z7yj7+4vY!D)lZ z3Djo;ji<3QOkU@!oyL>FJr;DA+pEkgiS@eK?KP;qvsTcLI{fI@@GnTJ7% z(S)Hu;SvKQgCMwH!wzb0!6(k43EGY7}aCuOD1;T$EA!!`qW>9Gl5@ldm zghN~ohxmV}|Ip3(hC{tD4)HJ?;`eZf^EzR7|1liuYjB9K!y&GPL);99I47DoC=Nha z0wo@LFybKwO&=r{7#NNhbydK_*zb>W9jMdQ%Jx zf1o8IX#812h?gOWQJ6uBDUlIWhA4u0pgN6#frlwiVH1NQ!yj;I2#HyQ`anGDL9T$R zH|AxKVYI@iEejR67$9~@G5KIMX+2z?37l&os?lp*mB)NBlb z407PHUr~lwFf9r0E3+}kGJwX|*ccQU9)iVH8MqlW1sOFp8RZ=r<*OLwH#0KIzhPvQ zU&_QN|AdKAegZS2{2pdT`4^zl0#@Fg!BO5dp_HE&;pHc&ECk^#DDsyuLMqP#ifrAW; z3{p&&8S)fPFo0UkM;Qzmq?mY-_>K&D0;iyD@2w014C)N53=lK_;WhIL+>AqDGx?DC zm}Y`nWT5ajZ~%=PfyQi@SuGi`x%Dc{9D~CQhAdJ{{7C#jHe7C1gPN<$z|4Z(+-op% z6pn!1D}cnubT2FngdFgN>vgypkZ=`5;^Q{c%7K9aG@nwa@P@&efs4TcYdGA3n{^!Q zUST9YrrEIYm4SwXAp@@Py#X`F;21c3g^>8T!&jHeI0p$cK#>ET;3XrtT!oUb_WxTOxV2}dMPcmamgZJTfo@6k@ z$R8l{K{&_(G$#RT`=~+lY9Rw?3>;M6!TMWYoFHo>K>dn5g|!R{(B2Gjy`@bI4h;NE zEZ{yS1H(%vSlb^o%PhnI8ZY5y0F|Z;3`d+8!2Qbv1_p*33=E8*wGc84Tu3DWs1!&9 z`>l~7&)^utL@ro<0`;U=3wfB5n1vanSYCnKan1}%%#ePSGJ_y9WDE;be!}_~Lg3XF zeBk~tsPAzA+D3+rZ-BxGgaw?z%QB(9o&+{qfT0jP*9~&3kYS;MBLgFn2!sy`Lr|Fk z#?GK|RZyIDGe|H>=(>dbR^)UjRM^R&$iTxW#Pr92pMeQ9dZOUSz{DWMu!uo|F*97G zn`=KSLY2@T0|69O^BKe#85rQEDE%=IM3M*D4=M>kev5$iJ0Lw|P~ROS%E0i!85SR) zl`W9I7&Ext%fL|K44S(HjRSz>BpH6xgJwuTzF}YxaDm8~8?i7jDrhh<8t_APF)+BJ znF)$xSp09sBFD}E8bxAYxa7>h02&v|6L`wN#=ygn#0biV_Zh?(co_2xt}?JN@-QLC zC@6h`+Oz3UeV`eRJcTC=EDSshATwA%`W&t?FbSjfuwnXqoFQv*k@X1)f=mzr>%5Mk z6BJgEu?<;g$hfEsid{D`binK~bq3AvfW}*R7=#!RcHF|y0SZ@$9dn#PE(2j9!90Tp z46KYi3^JfG3WwVa!Yn+DN=%T{EyN(m1gl>qogw`^SQ-Y|4@&c~&XDq?5WIp36qiv7 zObn2;50ck|>VwFG(g$RmjLn6C0W?ssah*Q`pb=4!f4CW7y{JM3A820&CIacTfYLFj7Xgav zZf6Dt&^jB4c{`wGmJ$OGLjh>H2ZI5qXZg8&m3cvLS>VIzYOcr7=`-ymG? z3~DF9%Gcdcw+b2j5de)kCNMHH6e@T#Ffs@^{1E`nFC#<}7?>IUfWw`O5u`?dL4@J| z|Nr&rU~x&%Tr^Z16gEuY5{!W%*#RD2TqqCK`I%L$IxJE;~hY0nSmisAQ2Rw zC}S`nIZ#*^2%HDc;(|&F5SDd;n4iSJ#gHd(0W`wNAkU!AkS73IEye{d%R%W7l%5n_ zAmuG6?}N${Px{fvbQ=?vlwc>)k#p1@-UMg}fMA)!KrGzLWmWkt~V zDA;UfP$)1n@GuLp_%buH6dK%PaA4$NP*!vUkG?|0?t;b8O@o*P6NT8A#K_8!C-9De zks*nZ9p-*`>BP+7%go4xEaS$=$OIbO1BD|fd_Z*&D4iuSaDc-e6h0t)$b|vCW<`m? zjzNgAK;b=u07E7N2ZIs=FGB%nu_{9*0~doZg8+jvqc=k)BRhjKqYpzSBO8?FWKd$X zWhhX1%fQHx$;b*$kDxdK*(>RSS~kGa!f6-K+6M*(p$~rqKy3;xhC%_*`a@9K;(@N| z^KxMT_iKb07#W3`1Q<*e#Tk;A*cgh6BcsL(WkoLrMrKoG zF$N_TeFh~~K86B;6b4p?0)^8IAq>7OjLdEr+; z19*K)0s}vTkfD(PE5if^CT4zUduIXI4FU{G46+PTjAs}M6t;kSS)jF^pcKo%aLol$ zmK1_lqzZii<#|w@4k|-lxiBz*+DM@C8o5W8&IoGDDKaF0`$tDXeLH9`4ivru3^EM- z;JnU~%EOSr$j_k72wHK6JWdW7javwIog#x0qZ)%0QyOD|!fpmd26aY|tP&F>ZkZUQ zn0vr+X#^&N7^IjPz$_5|I71RRZb5TXAiEVAgct=G85lvi15)>a<~2e22b6DNZA4Jp zksn%rg5=QaT{Z^LED-|(yDMbfq!5FUpcOL<1EY`z6C+chKq&(ggOHJ#G7AHv00RSv z&%lt*%+D}^fra&tf;2-21E}OLhR#eNhnyDJ<%qs8BNE>coOS~lq?kaX|KJ`oN~nVB zb&zWr81h^}@dt_<0dTnuaV-ObB2;_=10xeC9%14VuCO=;tssWPu{?N%9Rq`=D`fpK zs3hZH6k-6)aVarF!k(EyiUrbQcLuu?#D|1Eh<}_x7##MXav2o%Mhrrqn3#nb3Ki-> z^MgW6EFxgG6N4~oA_EhfD43VZAk50Z!o)5H=9Mxqv5JFPK@1{Hvt}APGI2;S6e`p) zG%-kU&YB4Vj1H{ak_?3owG1myOEKv%aBxa96e=__Ok$8>5@6uqk^%D;F^Kbuo{eT_ zWs+qmbZB6BYb3)fz`(&L08WYb1bBFr_#rK>`wW8okopajCbC>1*F)oa0wWVMs4W392^2?=JXQ^jmt|=2(hL<}jwW6N zkFPG!3LpXOafy(Fxj7x)Pe9D^f?Uw;3K>TL?`K@KFsAj){a9wfrZ0GdN(0ncw;c7vEH#qfhc$Wd9)gF%Q1RBs9i z`LZxF2`TxqGBODn`LZ!G6)G$T&#K;GU0dA1`Q;OjygHRwo zGliUd*@-b3v~CmP-hQ}ye^KmS(0B)^d@P2W`I}-hIiX=-y3px36AcqCG2jy^s0aW@Tm${&J0%TtGg)7XCzj*A}OO_p6 z(D48fxIg~kwnJebLp!oRIEnKIEc}l^)3p@Ce~QB&X6ANyoH8&{nyz8_64YJvDaKN09|Kg*L;7#`P~^)n(8*xIzzLpllwyL6 zx+yZaF|smPFcyMoP=6V;`XoO5h|TgR5~*h3cNs8sf1AJgrd@kp-|vCvPxc1 zivi>=P<#0`vJ3~P9p}abYKep9IYFa>nhX{HA*r1M-1dXjlc4m@!tmaW0la1q)K3vo zGF9Yb_`?9&)Affz0ko|J;OwXA3yyY3} zn3ggv1G8M2CNNC|vwWDQFwbD-%a&#cg0mtR_!wds_$q7|HZp=NV^Cz^V^Cq>V-RQH zV~}Qf#njK-#mvCR!n%=JDa)Igp_|!^L6m`yL4tvAD>FkIlS#Xsv>Y?zbSAYbRz^lK zfx~vva%_y1tc(m=415(86%2+9d^sQ`4AYquuCg*@f#exL3|kq6ECxTu3ygd=Fu)+q zz{jA>`iiNMxr=!*(-J1WifI*5P}mN!+>L?HhQW)0uYw^7#7YCPYCx<8Mi6rrBZxf@ z%-#lO?*g+gf!Wu|&1IU$G@psjhM|E8q^|``_Ax=70FvheTMxB-4Oo5y z6Ue4rI91&MtGWYL^##oS0cQVUVg`lh0cO4m22h~dF!V8lY@Ni65MKxuSqdgsB8l$+ zi|hfD$Km1(SHS`|nL!S^3nFc8?4{(G8D=qQRm@;z0CCtDz$_=GDp|0E!ljC76<1gp zY``LH3?y?evohG&+Bh;*%gV7aK+PhK2hzzP1xjG7pn&LNp2j?ZnJ-h9g<%qtQWm2S zKQm(^v#vKYQ#Z3&1%ngwex@bN{h-)!hcUs4fgyl_k5QN(gar5*!a%7v(hW zD99;Q415*dQQlG0qAD0#LCGnKnc*lCuXlS?MFqnoMwqTlW`;gy%S=#SHe;C2$h?60 z2J=l&wq60%vw)emf|+q4GiwyXcDOEY7KR=srL0V5#y%!XNH{Q@U}QW5(s&lGk!RX8 zhAWH=d|N>IhG7XK6WC}569zs8a|S*JYsMSQd<_1KhnV;(wlIV#j_i_EOL?MrnhIw>wCtn4;63~~&79SzyOz6|yZd{N8{H<(#%7<8FVF+O7CvtclXFk&q# z7;IrqXO!RvxxR246(crpC4vE z%6yFZIP(eSlgy`>Pct(dVdi6zIm4`!#i+!enORX0)xNZ%qFpzWnYV}8jG+i(?6e97 z8PEtX10RDH#6TMceF%fW6H5Cs@NKy?jX{>_E7Ju=z9^8_8TK==+UQm?3h?VPGhSlU zZodSEmyfVA$}#1YGl_b!GE^|wvOo%;i;R4XBK**#3QEWfnV_6p4I)|@p{3AbrbSGA zPnoKhI6)OI1EmCpyBJDUk)q%_RP?_k+RSV;wzeYn^bYR(*Rsk96x%W#pA zkA-bEC?|>wK$RQ@D>=i2&ArdSa__Oqb%4quhPmLqaifI+sSQ#J!;Nl4%?=mZ7+yvCl z0gXg~#+O0;7|;wLh!3j6L3~h;8#F%7!k`0gn}OIo3?Na^muZ%|UL8Cm7c@l^`$juN88cWk*P(;%Y@*%SMEZ{xS2=iFM`>sLZ z0x=JwAI1jx1))|M+(t$4b-;FkR;NMKgM7jW-d~R}&j~yh1Ce({V!I)+-I3Uy;F)|y z29Qr6aRFk3MrT3cuLxcZ0@{nj0-ECit2Jd{0q+F|g(Ya8J}7<^85F_tAU&WS6^I74 z&p7WZUeRA69I z&|qLuu(eZQFjR0(%*jzG&MZk)$jmD)Nz6-5RZvznFiKG!PEAozRWM3X zD9OxCE#_j-QwYf@N=<|c!c-*VFouhPT_G&LB(+$9fl-5jQImm5)0%-r%g$PpfmO@S z+DnsxP0P+Y+D(&zNk@T!O-CWg$O=p+L+KPKod%^dp>z(E&WF;)AezyTfzgP8!zMW= zGcU8mj)BbvLNnP}GcY-rxt(&71E0Ga}`SSK&EFV=44i-rYOKefPvA4 zfyu>(fzgdY-ObU{Cp5@4J|r^0HQvY5&)YR9-q$t6!`~%7$TcK1$S*kFFVx3}K@pF_ z5RV}L@L&c(6eWKCey(V`LL7tKT|;2TLv=B`xw$d$yCr7kloq8bgj5!!DrhS(h`6Vg zIOXS;6qghw76g^%Ip>$=l`yb+q~_%0Gq8avMFvJs1}0BW24+uBPX=~R5O8y0VDa?y z@nK-`^zrm$VDw^O^kHD~ab{rhabaNcabsZe@nm2INiq3&F))D?`uKSIFfjW-m_9xX zY(73dJ}zDm+6~J0bYb8FiG%F$baV0Xfy#SA<-L3uSV8hoE==0Thk*km9OdL3%)sX3 zo0yqr&0y>kl3HBil$ZkdpJP#aX>Mv>NwEq@Cbg*8IUk(TOF;1*oLQ1;%^;0ilQjbu zRI)hLnn51ScB#xu%*{;3w85G|2CTp{uPh&ue7rOBQi@f=GgH!2ORO10vB_F9Xu^fF zQ=LnTOY(EWGfOg@^YfBRi;7b7N+21oSjEtqL7h0I)(lEug+7^i*{MaosU;ctDZZI` znYoEM@Zf+MhN7l4rzEo=Cl#g$lwgXj86=Pl3(YG^%}FdtO>qkLg#}1hVo_#d5=dKS zNhPv*)(m_;s2Z4jq8Ma-67%x%OCb46A0CVlXIe8zp-6*bDK*8Z(lalm(wadGRSp`H zAn&Fm=9Fb->my9ocg;)A$uG{#OLxvmEH1WYkn_P*#3JvTnwtbwkXmfb zAd5vwaAta5Vo7OHD#+!SHiTpp<(C&*GjM>yB{MHwfkD(KE3qt5KQmuHpeQr1B)Fs~ zH8B?ylwjeU#JqHU$D+)VjNH_c%w$(snFBH%NkMUOY7wX?axE&#FS2G(fS3u^j256! z6;NBjDx5PCixQJdQj4q^1d%jC!vRCchCu{fm5r7iC_oVo0_$~7EG|KEun>|im;BPC zoK#f%+;Z|0ORO0bk(7DnmBA%NnIOX@k*tG= zIhAJSq<~z4qP()WBsCY+2uLQkW)Md*0wU&KXC5?}a)K%^ zJ&=bO1pPvNom_+B{oUe2Jc3*uU4j_|AXW#ZmXsFdfqc)v?4Os)pbilLl_`0pIXU14 z9H?QIp9XH#fudC*Ex$;CfiakY1=IpzkOb9$SnEaxIh;}uXYoShi&G(75fB$z55g*o zVg@B7QFti?mxkz+0I5N%AR#hZFd4kHAVjSKNG+&D#Z|j8s1i~EF$hx`wknVT>^2np zuqlHWDUGlhx$c8Vf}3VHb@3MCn- z3dM=Jso;hwq*-glzzR~8%Ag7^CO`=XOGTrQnWvDLlA@u>Uoy)ZvI?V2<+fVPJ}OVPJ}OV_=H*WMGQ+Vi1akWMFWC39h%S8AOo9;Mr0bSrn29 ztr<`RA%#4eASjR%Q>+`CJ!oZSPlwQohnOKxrQt1d{ zvL=FxIR>6YaAU%?urx8Jm_ZPkr;uc5rGO@sj470YDU^mOl!+;ngDF%D5)wjogq1=* zNQ@sM=9garb*U(t092g-TsSBd)Za^C;DU&R6qTkjKurd*70MEGN>i;A7+4d_6EjN~ z7?T*7k_;J`l8hMGk`jwDOLH06lOV-815Z+Zeoks)UKmIV1Ah{%^8~3$7zE)0@ZJ=E zQhr5dUOK$=V_;4y&Sj8ED$Z3X0vF_%sW)MJD1ZsgW2qqPSyEabw6{#uUI+g+CD{yZI)JAvAO98hD zAyP2a;4BIe0=WgG8q%7Ch)F;Vh3L#n0eKHq#xt+PF)sy@RUtBBP(5I^c_|`BGB;9e(ZQe{ahBuy|S8!|8@8!>Qz1_D5JF9REhm6`&jix>ow zGZORCQz2mquEsf&GZKp++PRWJ0RUk$C+8G1C?`W(U+@&t4VlK{jX-jt&J0=V%ELLhEHVo_plYDsDl z$e>~d&Xm&Jg5Z+GN*K7oDj^ZYz?5dlz?5dhz>=1jnZv*h zB1?->!HyJ6%S_El@rMSFUt(@51Akg(UWzj)cp)Ljzy%U;Pc11)ErPIvk=dY*YBGZW zNDZi8l9>!ui6jD5&ka%&mRVF%nwZ1Dl?LkGKtfs|Ex#x^6*>$9b{1P&eo<~>2?KL_ zY6$~>dMYS|dgdh;rGkbk7&y{XOPmu+i&G&iP|{!!NKY++)WgZCZXiD~2&Jc%_$F3B zGb+R;_Vm;euqXM_Q%fMug(&7sPb~>aECN{vl7iGxo_Q&$6%4Eyi3J6f42+o!OqqrZ zOqoUuteId59_RoU#AyD^JW!_-kzhDrLxv1|FjhP`v>A9H{E|%2Kmh|sW?l)z2*FH9 zfrpd_*dP@ngD8Y{LMt^zAi}QDbOlWo64*q+BbwY084s|J#33AL03lf_i$g*~6Ksey zMA|pA7&Lr=0QUXRL+4441~*}p#_sbk0M2gnK-1u9)vmu z-1$P73ynooQLuVwSfR=|rYC0RF~A%H>c_#20!v}&M2k3Nov2b^o$Q&#kkXwcD?c-j zfhot3fhos`fd{#T%OHi!Q-GE;3MHU%RR+$S{JeBXI%mrPcXJuIAv7dL1fd+Hf=3b} zgp#eHr3*GGXfY!W(S?+|p?WYSpgMRVI$%W;OD-rRxpEV;Q=zS9{#>+jge@05Xvn}0 zp&eliCm6#S#&Cf#Twx417{eXL@PIKqVGJ)A!<&H%VgDSHi%Y zUs}SzSirzn0IPB#!|=RtKG?MgX%M%Vfw>?vm4USYG;+wm1E!pcbHUAdb}%m|kAb5o zH90l2EVYP%tthp)G^d1t3)18SnZUqYRGP=YSj@mwY{+o-&}Iq?sGwkAE6pnfr<5`VrZPhY<}yPA2BtD22BtD&2BtC-2BtDo2BtDI z2BtD|2BtC#2BtDg23Bxig@GMR`KK|kl%*C`GVp;q0Z7S=wG1@X#voXpnUacHgp0$3 z&1+ zzyV=oCNsp;)wx(yE9GY9=_VFrDp?ts=_nOvBJc~np&8cn4}t*q*|I9 z877-prY5H(r5G8RnH!`go1~_inI|dfD3zra6=&w>DOo8Q>KW-7n(L;dmMPUTFo`ps zVV2-vl3+T`$imUbEIt<^!h;am4iVvlh%odq%P~nYo?(_@7Oxb~7ng)^*u~kz>%~jN z8^trkImK(3q(LeeB@7uwnV&PtFv^NBPiK+Dv zn9L_&m?-`MDH53^SfLRCcGQ0`cn>lQ6sako-KHy;%r9aZ@(IZhWeE)&hP+^sVSENQ z^Z)<)6-?qUF(MSZIUAT{7z_UYuLn5dte&$4Mh?A3MTVqj7*Y{?843{$_jBCD7$d= zF^eN-7ae#=gABV*U|Ps5!NMrY0x^qeI+FxXAG3Kr zD1EF2sbZ910r?a|MIKxQ16C7qLGrF36PP4;z_vJGn2`fl!zjVCkXeEWLq!2dg$2kI zW(gh%7O-0|U7HP3bpWIa;#zK4AaTPD1O*abAG3KHNEI{&S1_5c0C6Fj5XOSk@?)qK zKQ@b5h6&*Yq|g_DDdvSF4Ui%zW|m-*(2-CCM+X*;WGk3Z9SbRUIpO85p?HaShB%ja z4N4jkhNdAFs3#PAm}EeX0Gltkf=T>1H~^R>m=`ijFq5YB1gch~f?lYPS^WPjW*L~R zo;^%5pu!WJZlz)Vlz}m1S1_5Eq81R?OaQqXY{HWjOy*^XoCS(n4D~CR%*!#2{EJlP zutdtgmWw;4&20U599rkYplX*3!;eU_}XO?h<#uzxUhWyYy z0JUh@fBvOcUNFFTCko%Ey!yU^$-IUUk{*%U0o1&}t67W&-m;MuS$2!LlLjloS{rS}%jF zsl&A97m_t@(7*zFLmifT&0!4d6-?&!m?mJRMR*jYLrV^pj1^4gjl^h$Cu5LSa3%p& z4`7BI%rLDLOy?a`Xf;a$E=mT}8pj;L+H?vL}>*fsrYpp@HQ^Y$D5oSjHm?XJSFi`c^VF zG_ahgXXIE?4_e{J(AdDzV8qBZ!-$dVf-xgkj|mIs1yj(OQ=E+rEKjVMx&Bx&acwbY zidTBS(ihBgY0aMvghwj9e$I7`gUXGjiRrX5{)~&B!&yhLLNH4I|eP z8%C}>HjG?fY#6zEY#F(h*fMhMv1R1CV#~<&#+H$*#g36{jvXV{7CT0+Gj@zzPwW`E zTI?CQ=GZfGZLw$MxMI)9)#J&?wZxN=>xd^K*Aq`ht`;vwt|eZKptDW6>^mA*P6UH| z^B|a+<3SK3*N;F(jvv8{9D70-xsC)eG72#=b1^Y_HZ-s-iDc#a5X!{0B8-viK^P;) zjBrMd3*n3$JrRr?dmrNyi=a)!EjxAA)TrJUz9B-l- zxi-Z3GxmhBGjK4nNHHS8fqFnv%=NwI-LD z>ja3O%fKMb&VhrZjU>s&K z2J;?9QAaj0Mso#_Afvb#MEw;;QAcr6c&i^|#8pO7N4S9bY({o54)Y0MO)T{+jw%cd ztYV-Rw5a1pNJIF2J-AuTD#k4S5=ju=OlB2hHowFu>IiQCf*ZcfV(jMU8ATnL#aP9= zKopC)2bjxdzKl`SkyVV-d;_B>tGHAes4>Yb#$|qhQIvyOjK|!SQPh`Nj2lEVFpKe< zTQG{UF^ln;uVoZ_uMo~uo4Y=}NPDD4iVL4E|ew;0M_kPY#RZw|uGX*m$R z3!(HKC@q%@5%+`Ag;4q+l>Q5)_46R=dh#G<-_1jq{Rt`;k`Ga{8%qC$(iQ~}@m46k zzkq?k0CbQ(sCfa!HfVegG(J=rc&#~z4LUDB0!a;Mojph#6b>n65I2Ho(7JXI4O*KI zqMtzZfbItXsR!*e28n~tZ2-{%pnC$#Kxfr6Fo5`=djmi;=q#u$WuSBA7#MoWK;R{mky4xe7oPnX6fq@|bWEjX02FN)wASUQu29Q3` zy$mbL85ltKGJyD?dl^79=w1d84Z4>BM1$^S0MVd(89+4XUIq{ix_2z2oPhy!7e_%k z0|V&Jjf!#x2GBiZ4do0BpgT4?${85ug6>f)XJDAez`!u09J1GZK{*4%0?<8&&cLu3boWO&0|V#|lPl#6;5#Ajlru1Z?z4MR4oQC?e}KXqM#Jv8 z2899Wq(IPJ*Pw6%?cD|4cMZw|p!5p5^BS}_1$OT>=$vLyItSf-4ax_g`!*BAA?FQ% z(yfF918DaK11PLO_hK_EK<=sp-8TR_H3xJjBIrI7CItrYUE830vsn}vz;`Hu%3siy rZ;<(*dtg`7{+wqVelCv`D8ceFl-THVAw6jz_3q@f#HxC1H&0H28JtQ z3=G%B7#MDeF)-W}V_aR!FD;tUK6#TgiuiZd{*6lY*qFV4WQ zQ=Ea}fH(ugNpS{-YvK$HpFnm$VoFWC`&Uis7o_27)vuSm`O7*SV=Q5_)9Y|q)0O` zq)Rg}#3=He#85p+7GcX*GXJ9xc&%nULz`(%Ez`(%A zz`y`XU-BU;=p==g01_qE`F$M+(5iy9H zL2Lyvh`+=^4rYLeOEWMq!2AM=Kadp2FQ9PIfT{s<$pz;r-9uz+QP;pTB1VGuKcnpNHJ;WFo zVB(-~gRw#324e?^F)#!{)kh$yPY`2Z2!@Jhh%qpPK-mRIYC6Oi7{Z`o1Tr7S2AR*! zz|4>XPAe=7%?u0-f()Dtbzr_CIL)v#C^B$^`KnM_m?0j_7i35T(?Sf%U|NJB6-?_e zq(kXUFs;Z?1g4D`%D}WDLphi>V5kDqiVTfVx)n_8GqizeMTT}L-3g|d8G67p3qwDc zW?+~ErnMNRfN4dBX;6A5l%55pXG7^ZPIYk+BS1~GuHz#z>a$It>UN7)%_89-%+0z)pCmSV^Q)4Jexg#v>-Lp3b3=|m{%oQ0J>=YRo zTooA@e2{RkA_GIdA_GGOR4>R}7zVXhK=y*$F%Tz#+cco^7KTA(EQrkpl?RorAU>$P z2gz|l%VALc0pf$|3y@ln*`P8RR4#Bp>l2W@?-U?@`wpfV!Tvle&%gjOvp@h;`|vR^ zGcbbNQkUdGX_J8=iGhhhim{s^PeGGGfPoLJ2juP>P<=uS=?shvd<;ws5cBTJL+XP9 z0R;v|1|fz>0Z4tv%D@CR_mw=PTn5QAgX9e$b|^6lFcb(FFfcMmF-bAxDVQ-RGVn0w zDHt<2GVm};F>xTo1k4#2!Ez86f$U=eoBdlJQig-vjBH*4La%};Ln7F`NQ4-Qc}ff- z3y;IQFPfYf;)bEO!QpkYx6u1k3s@&sHN7(niEaA6Q|=3!7`6k*6y zuwrmv-~pS&$RNlFs{1*?W{D_3%2$wC$ZkwQxJSVl>PAk4n1KmHAqOZNki-;>7$$=K zf+S{O$gq$bqz;$=u(~<~;pzY+SMxG(f!(a8zyK~I(cPSea34Hek=&wS%20^zJ_S>T zMl50wf8cU6s=c7J0Z9{#DE0?};s7Ns3l%sR${3Uw*cl20Kzh;9<^FFk)b4;9-zrW(SKIFz_&d#00=%Y$#%&IFe$9 z_#UiI0Mrr$saIxTVgT8p%fQHL$-u!N#oz)?txODj3{nh&3>v>Up!qOZ0n!!_VHGe6 zDf=bhz`(}9#J~l1YdC`{16%~d%~@c#K-_GM#m&YHm~J*^Ana!2Ky2=Xl}AYm4B&c3 zioum3Pe7l62^8B3dJF;#T;TRAs2l~AdzlIh4DO)zwSh8&DuWQC6q6@|FB1c!kf0Qk zD}<#e#Uuu16$*e<@-P+&cz{Y2hCiT!je&=$NWqIifq{qN54f}g@qNMkA^}i~jE6yr ziGd+c!4oVd&%nVT!@$V!M*vhF@-P-D_<&1IP`L|AfnW>3ZT>Zq< z+{P{hx2_8rq!<_&3K>8(QXy20ELa58hAafPkU`}vq^(}6fLuO{gJT&~KJzkggVSWS z0;CTC$~$=qS_}%{k~~jAoxuRgQekj_veX#@;H(5FOO2rb!ZOfcXy62!VxY>ffE&zG z&}29OF7qL7nS|n&4!B!DZ9Y((%us-|9Valbu!6!!i3ySem>8s(d%>y6h=GR*RMtB& z@Gwa+v!I9tfyG!+#F#+p1gLoz!LvlI)&-7v9cPLLSH-7qm`Mjqxo z0dr8UVFtO|h{2GFfy;tP1)OV<&4;)hS)UXO#O*M##oXv-f%-t85*w5kb|^sF;wu=~ z*gG*ypNw$5f)PV91IVS!C}PE6F=W>pGGMx1pNWBkkn17(P+f1x016${kPu*CfwnzP zC_ws~ptOcuM@|L15YjF%V=x4j04U`>s9z0AZyX^-48lyw48qI|EFvrpj~E$5SZB?gX)n$udN!I}f@9mS9~(R*c^ss; zJQ)6RgUkZi57H0AAhADSlOgRUSP1}Xw;^h!KLVg}0gzshdQjYf^n?0`!VE8%g&9-- z2s8OI{4ro<5MbiMNaKCrFhNS=44_a2wKqWN3)C`WU;woVK^SB%Xna9}p&SI*85ks( z-oJbI?%lut|0P(`K!A;b;V&CVEjLD*nFzKCmim|(K`kFxy`#whs$oEFP>>%$_JI1> zBB0?1dl5zk2ZsF;ph#h05xB7bj{`e{fDwN4!D$A?Cm{1d;RnJX^Fe7{ib0g&j{vBJ zE5+c>@J9d?u2KvF41WYbEoD%Mf>c5<$X$P+qc5;@c0&QuzDG}I#bBR;(wPhss5Ul* zv_&z}8mw-5h?3Tt;Asse{{lt63oZ|F869S}4`YilARx#e5OGNcF$PI+&I8rn zps^*8Jun&+k03s%-UHE~@e>dY!XUdq^$Lg$8lM62K{kN+pxP2dgYpi@3=qx8pvb_& zz|6qPzyxk5faF2rLW~LwEDDOY3JlB&S|CCjMA#}Ya4Do#6r?7Xq^2k^uqx!GmMbtY zYBDhCC@^r?BrA$UlgI(T#!0&4+>AEip3(WR;v7er+6X`9&p_A?5iD zJRYezIr$3V`9(P?iVTdN3@o0Wo}LUMo?siDON&eLbM+t&)L;-q5zR_0OJrd5Vqo#| z^73L}^kHD~abaNcabsZe@nm4~@nT@|@nK-`@$vCt-~f@HUT!W7tRR*T13Q@V@nPVG zGC&lAlut@xPFZHQeqvr;ehJuz`XL!b`Q^pd4AMT3Ku88VNgvrI)(j%3lEI|~sYTWd zqCOxe>SyNb2NY%Il?0a*r6%TDGYCS2a}x8?^__DPi;Jxpl#qlxb8=GC6LTDslT(X} zT`Q7P3qT=l&7g>+#xFm(G&#dJwIm}y1xbkzk`n)_SH<(0)Hskw09fei)+62xR_BvT+Erth4XlM|d- zl4{MMj72g(uec;JuLPk;1yhkvVs27OqHk(RVp?KyNq$kKHG@2+DoA*EB<7`nyr_;} zL9vQYetvdofi;5y0fp8Kvba=)R2Cotv$P~LN8hoiD6z8Gnn3`f1uW*1S&WDXu!ui6 zP7`zBMuSDuO7oIIajoy1pI2O(3krCs&8X6DP<7S}a!9tobq5rsrer23g2EdbrC{yF z;N+(dONC7S-VDtCd8rJH!3^@jsmb|yDd3EaHJh_R(gOo)NJdd=Dg$E}15=nY15=m_ z15=nA15=nM15=n615=m}19O;<8;I~>U+IM=x- zHL)ZWmWmjJF@zMN8TgZmb0M)F49eZf3_@rE3em9)GH7DqnI##n6(vQ9jz#Ik408Bn z6`~pV;PN4<#U+*u0;qhDBa$;xle672Q*%-vN<`5FtQ7KdQWO{%Q<7T5z@C%I>0xpag z1T#`|3Q~(8nF;Db3?YTgJOu_eNd9KvhS07RiOD4l!a3l)ssf=v`c(`VxDY%Bwj6N2 zW8j9+5KCEd6EpJ|m~#^g7`Ss0*_?qbzsNPGIF*4pzqEvbwE$F%GO&ZGoID2RqS8DD z{-V-64NZl#{2~RAWtI#A#o)L_3NkiG(q|AXNi0fFEkXFq5L3tqQ^=S>8C|2XLRwLN zt^#r;fT_=fK^tA438p>;jr{zAVns~`861kil?kTlrVK*p8cY>14KYRWPK1nCcAd zd`xE;teLjdTQg`df_SGHteLjeTQkb>Gb~^PH4Yir`9PdLMo>$Vov*^Xonbm7s3i$% z`Lgpd+VFd~Gpu3+wcJ4>prLp^MjHV}89|#UJD*G+?{nQIiMk9eOG7Q;B z+-Wil2N*#XGwcVEjAH!%|Nm#u1v}9Q)E0!8$<7xA0}OUxNe_r~bQyK{bs3ce7y=P| zZxBBU!na}6;Ro}R!NyDQGqi#j6%36aicyN6p%cWYU~mDO%EZRFgi(f3LI}cOlHifx zW)tHSpCKwMYAD7bY6RlMh{}kvi}8qZfjCj3(xRGT+@e~{Vyxn=qKY7@O;jI5^@uWq zsD4ov5H(4Z0Yps^)nXT86K@w)WMNDLbw~>;Uw(7?7*SxqfJfz@1B)1oc&Dhk7$27y zyZAIwMKMM(1~FzaCNVBCUNL?K21X`@h6a`i!itR?42+B`gc%uSnHjklm>4=5SWXBt zHa4(40Fw-DZXJon}$;j0q&D5o^Lxz#_fea&OgB%mr2{}ft z7jleTSLB&EzsNIku2E#xBvoJ-UgIsd3J zaz0UKbJtP6h^M0mxVuxL>6JnRlJQ z$-uw{o-2XO0Wk_NfakbD^FmAtkhLkGbtcRTkhya3T#5q39MD_~s{*JW&cFbgGXhO& QgWLd~XA*$)!$9j>0O4_=!2kdN diff --git a/test/dexdump/invoke-custom.lst b/test/dexdump/invoke-custom.lst index 9037c28990..e481d2abcd 100644 --- a/test/dexdump/invoke-custom.lst +++ b/test/dexdump/invoke-custom.lst @@ -1,35 +1,145 @@ #invoke-custom.dex -0x000009a0 8 invokecustom.Super ()V InvokeCustom.java 29 -0x000009b8 16 invokecustom.Super targetMethodTest4 ()V InvokeCustom.java 31 -0x000009d8 8 invokecustom.InvokeCustom ()V InvokeCustom.java 102 -0x000009f0 14 invokecustom.InvokeCustom ()V InvokeCustom.java 39 -0x00000a10 74 invokecustom.InvokeCustom (I)V InvokeCustom.java 40 -0x00000a6c 72 invokecustom.InvokeCustom bsmCreateCallSite (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite; InvokeCustom.java 160 -0x00000ac4 58 invokecustom.InvokeCustom bsmLookupStatic (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; InvokeCustom.java 142 -0x00000b10 164 invokecustom.InvokeCustom bsmLookupStaticWithExtraArgs (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;IJFD)Ljava/lang/invoke/CallSite; InvokeCustom.java 151 -0x00000bc4 270 invokecustom.InvokeCustom bsmLookupTest9 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite; InvokeCustom.java 170 -0x00000ce4 164 invokecustom.InvokeCustom checkFieldTest9 (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V InvokeCustom.java 120 -0x00000d98 160 invokecustom.InvokeCustom checkStaticFieldTest9 (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V InvokeCustom.java 107 -0x00000e48 22 invokecustom.InvokeCustom lambda$lambdaTest$0 (Ljava/lang/String;)Z InvokeCustom.java 192 -0x00000e70 142 invokecustom.InvokeCustom lambdaTest ()V InvokeCustom.java 191 -0x00000f10 56 invokecustom.InvokeCustom main ([Ljava/lang/String;)V InvokeCustom.java -1 -0x00000f58 16 invokecustom.InvokeCustom targetMethodTest1 ()V InvokeCustom.java 45 -0x00000f78 92 invokecustom.InvokeCustom targetMethodTest2 (ZBCSIFJDLjava/lang/String;)V InvokeCustom.java 50 -0x00000fe4 16 invokecustom.InvokeCustom targetMethodTest3 ()V InvokeCustom.java 62 -0x00001004 166 invokecustom.InvokeCustom targetMethodTest5 (III)I InvokeCustom.java 72 -0x000010bc 170 invokecustom.InvokeCustom targetMethodTest6 (JJJ)J InvokeCustom.java 81 -0x00001178 172 invokecustom.InvokeCustom targetMethodTest7 (FFD)D InvokeCustom.java 90 -0x00001234 50 invokecustom.InvokeCustom targetMethodTest8 (Ljava/lang/String;)V InvokeCustom.java 99 -0x00001278 16 invokecustom.InvokeCustom targetMethodTest9 ()V InvokeCustom.java 133 -0x00001298 8 invokecustom.InvokeCustom test1 ()V InvokeCustom.java -1 -0x000012b0 54 invokecustom.InvokeCustom test2 ()V InvokeCustom.java -1 -0x000012f8 8 invokecustom.InvokeCustom test3 ()V InvokeCustom.java -1 -0x00001310 18 invokecustom.InvokeCustom test4 ()V InvokeCustom.java -1 -0x00001334 70 invokecustom.InvokeCustom test5 ()V InvokeCustom.java -1 -0x0000138c 88 invokecustom.InvokeCustom test6 ()V InvokeCustom.java -1 -0x000013f4 80 invokecustom.InvokeCustom test7 ()V InvokeCustom.java -1 -0x00001454 32 invokecustom.InvokeCustom test8 ()V InvokeCustom.java -1 -0x00001484 8 invokecustom.InvokeCustom test9 ()V InvokeCustom.java -1 -0x0000149c 54 invokecustom.InvokeCustom helperMethodTest9 ()V InvokeCustom.java 129 -0x000014e4 16 invokecustom.InvokeCustom run ()V InvokeCustom.java 137 -0x00001504 16 invokecustom.InvokeCustom targetMethodTest4 ()V InvokeCustom.java 68 +0x00001b28 8 TestBadBootstrapArguments$TestersConstantCallSite (Ljava/lang/invoke/MethodHandle;)V TestBadBootstrapArguments.java 449 +0x00002554 8 TestBase ()V TestBase.java 19 +0x0000256c 68 TestBase assertEquals (BB)V TestBase.java 27 +0x000025c0 68 TestBase assertEquals (CC)V TestBase.java 34 +0x00002614 72 TestBase assertEquals (DD)V TestBase.java 69 +0x0000266c 72 TestBase assertEquals (FF)V TestBase.java 62 +0x000026c4 68 TestBase assertEquals (II)V TestBase.java 48 +0x00002774 72 TestBase assertEquals (JJ)V TestBase.java 55 +0x00002718 76 TestBase assertEquals (Ljava/lang/Object;Ljava/lang/Object;)V TestBase.java 76 +0x000027cc 68 TestBase assertEquals (SS)V TestBase.java 41 +0x00002820 76 TestBase assertNotEquals (Ljava/lang/Object;Ljava/lang/Object;)V TestBase.java 82 +0x0000287c 16 TestBase assertNotReached ()V TestBase.java 88 +0x0000289c 52 TestBase assertTrue (Z)V TestBase.java 21 +0x000028e0 22 TestBase fail ()V TestBase.java 92 +0x00002acc 8 TestInvocationKinds$Widget (I)V TestInvocationKinds.java 177 +0x00002ef8 8 TestInvokeCustomWithConcurrentThreads$1 ()V TestInvokeCustomWithConcurrentThreads.java 33 +0x00002eb0 26 TestInvokeCustomWithConcurrentThreads$1 initialValue ()Ljava/lang/Integer; TestInvokeCustomWithConcurrentThreads.java 36 +0x00002edc 10 TestInvokeCustomWithConcurrentThreads$1 initialValue ()Ljava/lang/Object; TestInvokeCustomWithConcurrentThreads.java 33 +0x00003fd8 8 UnrelatedBSM ()V UnrelatedBSM.java 23 +0x00003fb4 20 UnrelatedBSM bsm (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Class;)Ljava/lang/invoke/CallSite; UnrelatedBSM.java 27 +0x00001910 8 Main ()V Main.java 21 +0x00001928 132 Main TestLinkerMethodMinimalArguments ()V Main.java 49 +0x000019e0 44 Main TestLinkerMethodMultipleArgumentTypes ()V Main.java 42 +0x00001a1c 156 Main TestUninitializedCallSite ()V Main.java 24 +0x00001ae0 56 Main main ([Ljava/lang/String;)V Main.java 78 +0x00001d74 8 TestBadBootstrapArguments ()V TestBadBootstrapArguments.java 27 +0x00001d8c 16 TestBadBootstrapArguments boxingArguments ()V TestBadBootstrapArguments.java 348 +0x00001bc4 170 TestBadBootstrapArguments bsm (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ILjava/lang/String;)Ljava/lang/invoke/CallSite; TestBadBootstrapArguments.java 35 +0x00001c80 90 TestBadBootstrapArguments bsmDJ (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;DJ)Ljava/lang/invoke/CallSite; TestBadBootstrapArguments.java 270 +0x00001cec 90 TestBadBootstrapArguments bsmDoubleLong (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Double;Ljava/lang/Long;)Ljava/lang/invoke/CallSite; TestBadBootstrapArguments.java 314 +0x00001b6c 26 TestBadBootstrapArguments bsmReturningInteger (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/Integer; TestBadBootstrapArguments.java 425 +0x00001b98 26 TestBadBootstrapArguments bsmReturningObject (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/Object; TestBadBootstrapArguments.java 402 +0x00001b40 28 TestBadBootstrapArguments bsmReturningTestersConstantCallsite (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)LTestBadBootstrapArguments$TestersConstantCallSite; TestBadBootstrapArguments.java 455 +0x00001dac 16 TestBadBootstrapArguments bsmReturningVoid (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)V TestBadBootstrapArguments.java 380 +0x00001d58 10 TestBadBootstrapArguments bsmZBCS (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCS)Ljava/lang/invoke/CallSite; TestBadBootstrapArguments.java 227 +0x00001dcc 16 TestBadBootstrapArguments extraArguments ()V TestBadBootstrapArguments.java 158 +0x00001dec 16 TestBadBootstrapArguments happy ()V TestBadBootstrapArguments.java 74 +0x00001e0c 8 TestBadBootstrapArguments integerReturnType ()V TestBadBootstrapArguments.java 444 +0x00001e24 8 TestBadBootstrapArguments invokeBoxingArguments ()V TestBadBootstrapArguments.java 344 +0x00001e3c 8 TestBadBootstrapArguments invokeExtraArguments ()V TestBadBootstrapArguments.java 154 +0x00001e54 8 TestBadBootstrapArguments invokeHappy ()V TestBadBootstrapArguments.java 70 +0x00001e6c 8 TestBadBootstrapArguments invokeIntegerReturnType ()V TestBadBootstrapArguments.java 440 +0x00001e84 8 TestBadBootstrapArguments invokeMissingParameterTypes ()V TestBadBootstrapArguments.java 124 +0x00001e9c 8 TestBadBootstrapArguments invokeNarrowArguments ()V TestBadBootstrapArguments.java 256 +0x00001eb4 8 TestBadBootstrapArguments invokeObjectReturnType ()V TestBadBootstrapArguments.java 417 +0x00001ecc 8 TestBadBootstrapArguments invokeViaCustomCallSiteClass ()V TestBadBootstrapArguments.java 469 +0x00001ee4 8 TestBadBootstrapArguments invokeVoidReturnType ()V TestBadBootstrapArguments.java 394 +0x00001efc 8 TestBadBootstrapArguments invokeWideningArguments ()V TestBadBootstrapArguments.java 300 +0x00001f14 8 TestBadBootstrapArguments invokeWideningBoxingArguments ()V TestBadBootstrapArguments.java 372 +0x00001f2c 8 TestBadBootstrapArguments invokeWrongArguments ()V TestBadBootstrapArguments.java 182 +0x00001f44 8 TestBadBootstrapArguments invokeWrongArgumentsAgain ()V TestBadBootstrapArguments.java 210 +0x00001f5c 8 TestBadBootstrapArguments invokeWrongParameterTypes ()V TestBadBootstrapArguments.java 98 +0x00001f74 16 TestBadBootstrapArguments missingParameterTypes ()V TestBadBootstrapArguments.java 128 +0x00001f94 8 TestBadBootstrapArguments narrowArguments ()V TestBadBootstrapArguments.java 260 +0x00001fac 8 TestBadBootstrapArguments objectReturnType ()V TestBadBootstrapArguments.java 421 +0x00001fc4 16 TestBadBootstrapArguments sayHello ()V TestBadBootstrapArguments.java 473 +0x00001fe4 1058 TestBadBootstrapArguments test ()V TestBadBootstrapArguments.java 477 +0x0000249c 8 TestBadBootstrapArguments voidReturnType ()V TestBadBootstrapArguments.java 398 +0x000024b4 16 TestBadBootstrapArguments wideningArguments ()V TestBadBootstrapArguments.java 304 +0x000024d4 16 TestBadBootstrapArguments wideningBoxingArguments ()V TestBadBootstrapArguments.java 376 +0x000024f4 16 TestBadBootstrapArguments wrongArguments ()V TestBadBootstrapArguments.java 186 +0x00002514 16 TestBadBootstrapArguments wrongArgumentsAgain ()V TestBadBootstrapArguments.java 214 +0x00002534 16 TestBadBootstrapArguments wrongParameterTypes ()V TestBadBootstrapArguments.java 102 +0x000029d8 8 TestDynamicBootstrapArguments ()V TestDynamicBootstrapArguments.java 27 +0x000029f0 8 TestDynamicBootstrapArguments ()V TestDynamicBootstrapArguments.java 26 +0x00002970 86 TestDynamicBootstrapArguments bsm (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;J)Ljava/lang/invoke/CallSite; TestDynamicBootstrapArguments.java 36 +0x00002908 60 TestDynamicBootstrapArguments targetA100000000 (ILjava/lang/String;Ljava/lang/Double;)I TestDynamicBootstrapArguments.java 71 +0x00002a08 50 TestDynamicBootstrapArguments test ()V TestDynamicBootstrapArguments.java 86 +0x00002a4c 110 TestDynamicBootstrapArguments testCallSites ()V TestDynamicBootstrapArguments.java 80 +0x00002954 10 TestDynamicBootstrapArguments testDynamic (ILjava/lang/String;Ljava/lang/Double;)I TestDynamicBootstrapArguments.java 66 +0x00002cb4 8 TestInvocationKinds ()V TestInvocationKinds.java 25 +0x00002b00 12 TestInvocationKinds getInstanceField (LTestInvocationKinds;)D TestInvocationKinds.java 117 +0x00002b38 10 TestInvocationKinds getStaticField ()I TestInvocationKinds.java 71 +0x00002b70 40 TestInvocationKinds lookupConstructor (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; TestInvocationKinds.java 183 +0x00002ba8 40 TestInvocationKinds lookupInstanceFieldGetter (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; TestInvocationKinds.java 101 +0x00002be0 42 TestInvocationKinds lookupInstanceFieldSetter (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; TestInvocationKinds.java 78 +0x00002c1c 32 TestInvocationKinds lookupStaticFieldGetter (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; TestInvocationKinds.java 32 +0x00002c4c 34 TestInvocationKinds lookupStaticFieldSetter (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; TestInvocationKinds.java 54 +0x00002c80 36 TestInvocationKinds lookupVirtual (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; TestInvocationKinds.java 146 +0x00002ae4 10 TestInvocationKinds makeWidget (I)LTestInvocationKinds$Widget; TestInvocationKinds.java 200 +0x00002b54 10 TestInvocationKinds maxIntegerValue (LTestInvocationKinds;II)I TestInvocationKinds.java 159 +0x00002ccc 16 TestInvocationKinds setInstanceField (LTestInvocationKinds;D)V TestInvocationKinds.java 94 +0x00002cec 8 TestInvocationKinds setStaticField (I)V TestInvocationKinds.java 48 +0x00002d04 48 TestInvocationKinds test ()V TestInvocationKinds.java 212 +0x00002d44 62 TestInvocationKinds testConstructor ()V TestInvocationKinds.java 205 +0x00002d94 88 TestInvocationKinds testInstanceFieldAccessors ()V TestInvocationKinds.java 133 +0x00002dfc 50 TestInvocationKinds testInvokeVirtual ()V TestInvocationKinds.java 168 +0x00002e40 94 TestInvocationKinds testStaticFieldAccessors ()V TestInvocationKinds.java 122 +0x00002b1c 12 TestInvocationKinds getMaxIntegerValue (II)I TestInvocationKinds.java 164 +0x00003074 74 TestInvokeCustomWithConcurrentThreads ()V TestInvokeCustomWithConcurrentThreads.java 30 +0x000030d0 8 TestInvokeCustomWithConcurrentThreads ()V TestInvokeCustomWithConcurrentThreads.java 52 +0x0000305c 6 TestInvokeCustomWithConcurrentThreads access$000 ()Ljava/util/concurrent/atomic/AtomicInteger; TestInvokeCustomWithConcurrentThreads.java 27 +0x00002f10 26 TestInvokeCustomWithConcurrentThreads getThreadIndex ()I TestInvokeCustomWithConcurrentThreads.java 55 +0x00002f88 194 TestInvokeCustomWithConcurrentThreads linkerMethod (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; TestInvokeCustomWithConcurrentThreads.java 87 +0x00002f3c 2 TestInvokeCustomWithConcurrentThreads notUsed (I)I TestInvokeCustomWithConcurrentThreads.java 59 +0x00002f50 40 TestInvokeCustomWithConcurrentThreads setCalled (I)I TestInvokeCustomWithConcurrentThreads.java 79 +0x0000310c 458 TestInvokeCustomWithConcurrentThreads test ()V TestInvokeCustomWithConcurrentThreads.java 107 +0x000030e8 18 TestInvokeCustomWithConcurrentThreads run ()V TestInvokeCustomWithConcurrentThreads.java 63 +0x00003414 8 TestLinkerMethodMinimalArguments ()V TestLinkerMethodMinimalArguments.java 26 +0x0000342c 8 TestLinkerMethodMinimalArguments ()V TestLinkerMethodMinimalArguments.java 25 +0x000032e8 46 TestLinkerMethodMinimalArguments _add (II)I TestLinkerMethodMinimalArguments.java 51 +0x00003328 10 TestLinkerMethodMinimalArguments add (II)I TestLinkerMethodMinimalArguments.java 45 +0x00003344 192 TestLinkerMethodMinimalArguments linkerMethod (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; TestLinkerMethodMinimalArguments.java 61 +0x00003444 136 TestLinkerMethodMinimalArguments test (III)V TestLinkerMethodMinimalArguments.java 78 +0x00003628 8 TestLinkerMethodMultipleArgumentTypes ()V TestLinkerMethodMultipleArgumentTypes.java 28 +0x00003640 8 TestLinkerMethodMultipleArgumentTypes ()V TestLinkerMethodMultipleArgumentTypes.java 26 +0x000034f4 6 TestLinkerMethodMultipleArgumentTypes _add (II)I TestLinkerMethodMultipleArgumentTypes.java 74 +0x0000350c 10 TestLinkerMethodMultipleArgumentTypes add (II)I TestLinkerMethodMultipleArgumentTypes.java 68 +0x00003528 238 TestLinkerMethodMultipleArgumentTypes linkerMethod (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;IIIIIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite; TestLinkerMethodMultipleArgumentTypes.java 93 +0x00003658 34 TestLinkerMethodMultipleArgumentTypes test (II)V TestLinkerMethodMultipleArgumentTypes.java 114 +0x000034dc 6 TestLinkerMethodMultipleArgumentTypes GetBootstrapRunCount ()I TestLinkerMethodMultipleArgumentTypes.java 110 +0x000036f4 8 TestLinkerUnrelatedBSM ()V TestLinkerUnrelatedBSM.java 23 +0x0000368c 6 TestLinkerUnrelatedBSM _addf (FF)F TestLinkerUnrelatedBSM.java 47 +0x000036a4 6 TestLinkerUnrelatedBSM _subf (FF)F TestLinkerUnrelatedBSM.java 73 +0x000036bc 10 TestLinkerUnrelatedBSM addf (FF)F TestLinkerUnrelatedBSM.java 42 +0x000036d8 10 TestLinkerUnrelatedBSM subf (FF)F TestLinkerUnrelatedBSM.java 68 +0x0000370c 68 TestLinkerUnrelatedBSM test ()V TestLinkerUnrelatedBSM.java 77 +0x00003a8c 8 TestVariableArityLinkerMethod ()V TestVariableArityLinkerMethod.java 27 +0x00003760 68 TestVariableArityLinkerMethod bsmWithBoxedArray (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Integer;)Ljava/lang/invoke/CallSite; TestVariableArityLinkerMethod.java 477 +0x000037b4 74 TestVariableArityLinkerMethod bsmWithClassAndFloatArray (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Class;[F)Ljava/lang/invoke/CallSite; TestVariableArityLinkerMethod.java 294 +0x00003810 68 TestVariableArityLinkerMethod bsmWithClassArray (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Class;)Ljava/lang/invoke/CallSite; TestVariableArityLinkerMethod.java 367 +0x00003864 68 TestVariableArityLinkerMethod bsmWithDoubleArray (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[D)Ljava/lang/invoke/CallSite; TestVariableArityLinkerMethod.java 332 +0x000038b8 82 TestVariableArityLinkerMethod bsmWithFloatAndLongArray (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;F[J)Ljava/lang/invoke/CallSite; TestVariableArityLinkerMethod.java 257 +0x0000391c 82 TestVariableArityLinkerMethod bsmWithIntAndStringArray (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I[Ljava/lang/String;)Ljava/lang/invoke/CallSite; TestVariableArityLinkerMethod.java 133 +0x00003980 82 TestVariableArityLinkerMethod bsmWithLongAndIntArray (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;J[I)Ljava/lang/invoke/CallSite; TestVariableArityLinkerMethod.java 219 +0x000039e4 68 TestVariableArityLinkerMethod bsmWithStringArray (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/String;)Ljava/lang/invoke/CallSite; TestVariableArityLinkerMethod.java 61 +0x00003a38 68 TestVariableArityLinkerMethod bsmWithWiderArray (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[J)Ljava/lang/invoke/CallSite; TestVariableArityLinkerMethod.java 447 +0x00003aa4 16 TestVariableArityLinkerMethod methodA ()V TestVariableArityLinkerMethod.java 86 +0x00003ac4 16 TestVariableArityLinkerMethod methodB ()V TestVariableArityLinkerMethod.java 105 +0x00003ae4 16 TestVariableArityLinkerMethod methodC ()V TestVariableArityLinkerMethod.java 123 +0x00003b04 16 TestVariableArityLinkerMethod methodD ()V TestVariableArityLinkerMethod.java 166 +0x00003b24 16 TestVariableArityLinkerMethod methodE ()V TestVariableArityLinkerMethod.java 189 +0x00003b44 16 TestVariableArityLinkerMethod methodF ()V TestVariableArityLinkerMethod.java 209 +0x00003b64 16 TestVariableArityLinkerMethod methodG ()V TestVariableArityLinkerMethod.java 247 +0x00003b84 16 TestVariableArityLinkerMethod methodH ()V TestVariableArityLinkerMethod.java 284 +0x00003ba4 16 TestVariableArityLinkerMethod methodI ()V TestVariableArityLinkerMethod.java 323 +0x00003bc4 16 TestVariableArityLinkerMethod methodJ ()V TestVariableArityLinkerMethod.java 358 +0x00003be4 16 TestVariableArityLinkerMethod methodK ()V TestVariableArityLinkerMethod.java 392 +0x00003c04 8 TestVariableArityLinkerMethod methodO ()V TestVariableArityLinkerMethod.java 413 +0x00003c1c 8 TestVariableArityLinkerMethod methodP ()V TestVariableArityLinkerMethod.java 441 +0x00003c34 8 TestVariableArityLinkerMethod methodQ ()V TestVariableArityLinkerMethod.java 468 +0x00003c4c 8 TestVariableArityLinkerMethod methodR ()V TestVariableArityLinkerMethod.java 501 +0x00003c64 318 TestVariableArityLinkerMethod printBsmArgs (Ljava/lang/String;[Ljava/lang/Object;)V TestVariableArityLinkerMethod.java 29 +0x00003db4 448 TestVariableArityLinkerMethod test ()V TestVariableArityLinkerMethod.java 506 diff --git a/test/dexdump/invoke-custom.txt b/test/dexdump/invoke-custom.txt index bd3250865b..cfab248168 100644 --- a/test/dexdump/invoke-custom.txt +++ b/test/dexdump/invoke-custom.txt @@ -2,49 +2,98 @@ Processing 'invoke-custom.dex'... Opened 'invoke-custom.dex', DEX version '038' DEX file header: magic : 'dex\n038\0' -checksum : d11a9e29 -signature : 5b54...15c3 -file_size : 8984 +checksum : dc722174 +signature : b59a...f803 +file_size : 31732 header_size : 112 link_size : 0 link_off : 0 (0x000000) -string_ids_size : 165 +string_ids_size : 478 string_ids_off : 112 (0x000070) -type_ids_size : 38 -type_ids_off : 772 (0x000304) -proto_ids_size : 51 -proto_ids_off : 924 (0x00039c) -field_ids_size : 3 -field_ids_off : 1536 (0x000600) -method_ids_size : 78 -method_ids_off : 1560 (0x000618) -class_defs_size : 2 -class_defs_off : 2184 (0x000888) -data_size : 6552 -data_off : 2432 (0x000980) +type_ids_size : 77 +type_ids_off : 2024 (0x0007e8) +proto_ids_size : 91 +proto_ids_off : 2332 (0x00091c) +field_ids_size : 21 +field_ids_off : 3424 (0x000d60) +method_ids_size : 243 +method_ids_off : 3592 (0x000e08) +class_defs_size : 14 +class_defs_off : 5536 (0x0015a0) +data_size : 25332 +data_off : 6400 (0x001900) Class #0 header: -class_idx : 8 -access_flags : 1024 (0x0400) -superclass_idx : 13 +class_idx : 7 +access_flags : 0 (0x0000) +superclass_idx : 52 interfaces_off : 0 (0x000000) -source_file_idx : 27 -annotations_off : 0 (0x000000) -class_data_off : 8589 (0x00218d) +source_file_idx : 144 +annotations_off : 30700 (0x0077ec) +class_data_off : 28922 (0x0070fa) static_fields_size : 0 instance_fields_size: 0 direct_methods_size : 1 -virtual_methods_size: 2 +virtual_methods_size: 0 + +Class #0 annotations: +Annotations on class + VISIBILITY_SYSTEM Ldalvik/annotation/EnclosingClass; value=LTestBadBootstrapArguments; + VISIBILITY_SYSTEM Ldalvik/annotation/InnerClass; accessFlags=8 name="TestersConstantCallSite" Class #0 - - Class descriptor : 'Linvokecustom/Super;' + Class descriptor : 'LTestBadBootstrapArguments$TestersConstantCallSite;' + Access flags : 0x0000 () + Superclass : 'Ljava/lang/invoke/ConstantCallSite;' + Interfaces - + Static fields - + Instance fields - + Direct methods - + #0 : (in LTestBadBootstrapArguments$TestersConstantCallSite;) + name : '' + type : '(Ljava/lang/invoke/MethodHandle;)V' + access : 0x10001 (PUBLIC CONSTRUCTOR) + code - + registers : 2 + ins : 2 + outs : 2 + insns size : 4 16-bit code units +001b18: |[001b18] TestBadBootstrapArguments.TestersConstantCallSite.:(Ljava/lang/invoke/MethodHandle;)V +001b28: 7020 d200 1000 |0000: invoke-direct {v0, v1}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +001b2e: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=449 + 0x0003 line=450 + locals : + 0x0000 - 0x0004 reg=0 this LTestBadBootstrapArguments$TestersConstantCallSite; + 0x0000 - 0x0004 reg=1 mh Ljava/lang/invoke/MethodHandle; + + Virtual methods - + source_file_idx : 144 (TestBadBootstrapArguments.java) + +Class #1 header: +class_idx : 9 +access_flags : 1024 (0x0400) +superclass_idx : 42 +interfaces_off : 0 (0x000000) +source_file_idx : 145 +annotations_off : 0 (0x000000) +class_data_off : 28932 (0x007104) +static_fields_size : 0 +instance_fields_size: 0 +direct_methods_size : 13 +virtual_methods_size: 0 + +Class #1 - + Class descriptor : 'LTestBase;' Access flags : 0x0400 (ABSTRACT) Superclass : 'Ljava/lang/Object;' Interfaces - Static fields - Instance fields - Direct methods - - #0 : (in Linvokecustom/Super;) + #0 : (in LTestBase;) name : '' type : '()V' access : 0x10000 (CONSTRUCTOR) @@ -53,627 +102,4734 @@ Class #0 - ins : 1 outs : 1 insns size : 4 16-bit code units -000990: |[000990] invokecustom.Super.:()V -0009a0: 7010 2b00 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.:()V // method@002b -0009a6: 0e00 |0003: return-void +002544: |[002544] TestBase.:()V +002554: 7010 bf00 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.:()V // method@00bf +00255a: 0e00 |0003: return-void catches : (none) positions : - 0x0000 line=29 + 0x0000 line=19 + locals : + 0x0000 - 0x0004 reg=0 this LTestBase; + + #1 : (in LTestBase;) + name : 'assertEquals' + type : '(BB)V' + access : 0x0008 (STATIC) + code - + registers : 5 + ins : 2 + outs : 2 + insns size : 34 16-bit code units +00255c: |[00255c] TestBase.assertEquals:(BB)V +00256c: 3343 0300 |0000: if-ne v3, v4, 0003 // +0003 +002570: 0e00 |0002: return-void +002572: 2200 1e00 |0003: new-instance v0, Ljava/lang/AssertionError; // type@001e +002576: 2201 2d00 |0005: new-instance v1, Ljava/lang/StringBuilder; // type@002d +00257a: 7010 c100 0100 |0007: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@00c1 +002580: 1a02 d300 |000a: const-string v2, "assertEquals b1: " // string@00d3 +002584: 6e20 c800 2100 |000c: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +00258a: 6e20 c500 3100 |000f: invoke-virtual {v1, v3}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@00c5 +002590: 1a02 0d00 |0012: const-string v2, ", b2: " // string@000d +002594: 6e20 c800 2100 |0014: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +00259a: 6e20 c500 4100 |0017: invoke-virtual {v1, v4}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@00c5 +0025a0: 6e10 ca00 0100 |001a: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@00ca +0025a6: 0c01 |001d: move-result-object v1 +0025a8: 7020 b500 1000 |001e: invoke-direct {v0, v1}, Ljava/lang/AssertionError;.:(Ljava/lang/Object;)V // method@00b5 +0025ae: 2700 |0021: throw v0 + catches : (none) + positions : + 0x0000 line=27 + 0x0002 line=28 + 0x0003 line=30 + locals : + 0x0000 - 0x0022 reg=3 b1 B + 0x0000 - 0x0022 reg=4 b2 B + + #2 : (in LTestBase;) + name : 'assertEquals' + type : '(CC)V' + access : 0x0008 (STATIC) + code - + registers : 5 + ins : 2 + outs : 2 + insns size : 34 16-bit code units +0025b0: |[0025b0] TestBase.assertEquals:(CC)V +0025c0: 3343 0300 |0000: if-ne v3, v4, 0003 // +0003 +0025c4: 0e00 |0002: return-void +0025c6: 2200 1e00 |0003: new-instance v0, Ljava/lang/AssertionError; // type@001e +0025ca: 2201 2d00 |0005: new-instance v1, Ljava/lang/StringBuilder; // type@002d +0025ce: 7010 c100 0100 |0007: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@00c1 +0025d4: 1a02 d400 |000a: const-string v2, "assertEquals c1: " // string@00d4 +0025d8: 6e20 c800 2100 |000c: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +0025de: 6e20 c200 3100 |000f: invoke-virtual {v1, v3}, Ljava/lang/StringBuilder;.append:(C)Ljava/lang/StringBuilder; // method@00c2 +0025e4: 1a02 0e00 |0012: const-string v2, ", c2: " // string@000e +0025e8: 6e20 c800 2100 |0014: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +0025ee: 6e20 c200 4100 |0017: invoke-virtual {v1, v4}, Ljava/lang/StringBuilder;.append:(C)Ljava/lang/StringBuilder; // method@00c2 +0025f4: 6e10 ca00 0100 |001a: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@00ca +0025fa: 0c01 |001d: move-result-object v1 +0025fc: 7020 b500 1000 |001e: invoke-direct {v0, v1}, Ljava/lang/AssertionError;.:(Ljava/lang/Object;)V // method@00b5 +002602: 2700 |0021: throw v0 + catches : (none) + positions : + 0x0000 line=34 + 0x0002 line=35 + 0x0003 line=37 + locals : + 0x0000 - 0x0022 reg=3 c1 C + 0x0000 - 0x0022 reg=4 c2 C + + #3 : (in LTestBase;) + name : 'assertEquals' + type : '(DD)V' + access : 0x0008 (STATIC) + code - + registers : 7 + ins : 4 + outs : 3 + insns size : 36 16-bit code units +002604: |[002604] TestBase.assertEquals:(DD)V +002614: 2f00 0305 |0000: cmpl-double v0, v3, v5 +002618: 3900 0300 |0002: if-nez v0, 0005 // +0003 +00261c: 0e00 |0004: return-void +00261e: 2200 1e00 |0005: new-instance v0, Ljava/lang/AssertionError; // type@001e +002622: 2201 2d00 |0007: new-instance v1, Ljava/lang/StringBuilder; // type@002d +002626: 7010 c100 0100 |0009: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@00c1 +00262c: 1a02 d500 |000c: const-string v2, "assertEquals d1: " // string@00d5 +002630: 6e20 c800 2100 |000e: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +002636: 6e30 c300 3104 |0011: invoke-virtual {v1, v3, v4}, Ljava/lang/StringBuilder;.append:(D)Ljava/lang/StringBuilder; // method@00c3 +00263c: 1a02 0f00 |0014: const-string v2, ", d2: " // string@000f +002640: 6e20 c800 2100 |0016: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +002646: 6e30 c300 5106 |0019: invoke-virtual {v1, v5, v6}, Ljava/lang/StringBuilder;.append:(D)Ljava/lang/StringBuilder; // method@00c3 +00264c: 6e10 ca00 0100 |001c: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@00ca +002652: 0c01 |001f: move-result-object v1 +002654: 7020 b500 1000 |0020: invoke-direct {v0, v1}, Ljava/lang/AssertionError;.:(Ljava/lang/Object;)V // method@00b5 +00265a: 2700 |0023: throw v0 + catches : (none) + positions : + 0x0000 line=69 + 0x0004 line=70 + 0x0005 line=72 + locals : + 0x0000 - 0x0024 reg=3 d1 D + 0x0000 - 0x0024 reg=5 d2 D + + #4 : (in LTestBase;) + name : 'assertEquals' + type : '(FF)V' + access : 0x0008 (STATIC) + code - + registers : 5 + ins : 2 + outs : 2 + insns size : 36 16-bit code units +00265c: |[00265c] TestBase.assertEquals:(FF)V +00266c: 2d00 0304 |0000: cmpl-float v0, v3, v4 +002670: 3900 0300 |0002: if-nez v0, 0005 // +0003 +002674: 0e00 |0004: return-void +002676: 2200 1e00 |0005: new-instance v0, Ljava/lang/AssertionError; // type@001e +00267a: 2201 2d00 |0007: new-instance v1, Ljava/lang/StringBuilder; // type@002d +00267e: 7010 c100 0100 |0009: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@00c1 +002684: 1a02 d600 |000c: const-string v2, "assertEquals f1: " // string@00d6 +002688: 6e20 c800 2100 |000e: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +00268e: 6e20 c400 3100 |0011: invoke-virtual {v1, v3}, Ljava/lang/StringBuilder;.append:(F)Ljava/lang/StringBuilder; // method@00c4 +002694: 1a02 1000 |0014: const-string v2, ", f2: " // string@0010 +002698: 6e20 c800 2100 |0016: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +00269e: 6e20 c400 4100 |0019: invoke-virtual {v1, v4}, Ljava/lang/StringBuilder;.append:(F)Ljava/lang/StringBuilder; // method@00c4 +0026a4: 6e10 ca00 0100 |001c: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@00ca +0026aa: 0c01 |001f: move-result-object v1 +0026ac: 7020 b500 1000 |0020: invoke-direct {v0, v1}, Ljava/lang/AssertionError;.:(Ljava/lang/Object;)V // method@00b5 +0026b2: 2700 |0023: throw v0 + catches : (none) + positions : + 0x0000 line=62 + 0x0004 line=63 + 0x0005 line=65 + locals : + 0x0000 - 0x0024 reg=3 f1 F + 0x0000 - 0x0024 reg=4 f2 F + + #5 : (in LTestBase;) + name : 'assertEquals' + type : '(II)V' + access : 0x0008 (STATIC) + code - + registers : 5 + ins : 2 + outs : 2 + insns size : 34 16-bit code units +0026b4: |[0026b4] TestBase.assertEquals:(II)V +0026c4: 3343 0300 |0000: if-ne v3, v4, 0003 // +0003 +0026c8: 0e00 |0002: return-void +0026ca: 2200 1e00 |0003: new-instance v0, Ljava/lang/AssertionError; // type@001e +0026ce: 2201 2d00 |0005: new-instance v1, Ljava/lang/StringBuilder; // type@002d +0026d2: 7010 c100 0100 |0007: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@00c1 +0026d8: 1a02 d700 |000a: const-string v2, "assertEquals i1: " // string@00d7 +0026dc: 6e20 c800 2100 |000c: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +0026e2: 6e20 c500 3100 |000f: invoke-virtual {v1, v3}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@00c5 +0026e8: 1a02 1100 |0012: const-string v2, ", i2: " // string@0011 +0026ec: 6e20 c800 2100 |0014: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +0026f2: 6e20 c500 4100 |0017: invoke-virtual {v1, v4}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@00c5 +0026f8: 6e10 ca00 0100 |001a: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@00ca +0026fe: 0c01 |001d: move-result-object v1 +002700: 7020 b500 1000 |001e: invoke-direct {v0, v1}, Ljava/lang/AssertionError;.:(Ljava/lang/Object;)V // method@00b5 +002706: 2700 |0021: throw v0 + catches : (none) + positions : + 0x0000 line=48 + 0x0002 line=49 + 0x0003 line=51 + locals : + 0x0000 - 0x0022 reg=3 i1 I + 0x0000 - 0x0022 reg=4 i2 I + + #6 : (in LTestBase;) + name : 'assertEquals' + type : '(JJ)V' + access : 0x0008 (STATIC) + code - + registers : 7 + ins : 4 + outs : 3 + insns size : 36 16-bit code units +002764: |[002764] TestBase.assertEquals:(JJ)V +002774: 3100 0305 |0000: cmp-long v0, v3, v5 +002778: 3900 0300 |0002: if-nez v0, 0005 // +0003 +00277c: 0e00 |0004: return-void +00277e: 2200 1e00 |0005: new-instance v0, Ljava/lang/AssertionError; // type@001e +002782: 2201 2d00 |0007: new-instance v1, Ljava/lang/StringBuilder; // type@002d +002786: 7010 c100 0100 |0009: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@00c1 +00278c: 1a02 d800 |000c: const-string v2, "assertEquals l1: " // string@00d8 +002790: 6e20 c800 2100 |000e: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +002796: 6e30 c600 3104 |0011: invoke-virtual {v1, v3, v4}, Ljava/lang/StringBuilder;.append:(J)Ljava/lang/StringBuilder; // method@00c6 +00279c: 1a02 1200 |0014: const-string v2, ", l2: " // string@0012 +0027a0: 6e20 c800 2100 |0016: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +0027a6: 6e30 c600 5106 |0019: invoke-virtual {v1, v5, v6}, Ljava/lang/StringBuilder;.append:(J)Ljava/lang/StringBuilder; // method@00c6 +0027ac: 6e10 ca00 0100 |001c: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@00ca +0027b2: 0c01 |001f: move-result-object v1 +0027b4: 7020 b500 1000 |0020: invoke-direct {v0, v1}, Ljava/lang/AssertionError;.:(Ljava/lang/Object;)V // method@00b5 +0027ba: 2700 |0023: throw v0 + catches : (none) + positions : + 0x0000 line=55 + 0x0004 line=56 + 0x0005 line=58 + locals : + 0x0000 - 0x0024 reg=3 l1 J + 0x0000 - 0x0024 reg=5 l2 J + + #7 : (in LTestBase;) + name : 'assertEquals' + type : '(Ljava/lang/Object;Ljava/lang/Object;)V' + access : 0x0008 (STATIC) + code - + registers : 5 + ins : 2 + outs : 2 + insns size : 38 16-bit code units +002708: |[002708] TestBase.assertEquals:(Ljava/lang/Object;Ljava/lang/Object;)V +002718: 7120 ec00 4300 |0000: invoke-static {v3, v4}, Ljava/util/Objects;.equals:(Ljava/lang/Object;Ljava/lang/Object;)Z // method@00ec +00271e: 0a00 |0003: move-result v0 +002720: 3800 0300 |0004: if-eqz v0, 0007 // +0003 +002724: 0e00 |0006: return-void +002726: 2200 1e00 |0007: new-instance v0, Ljava/lang/AssertionError; // type@001e +00272a: 2201 2d00 |0009: new-instance v1, Ljava/lang/StringBuilder; // type@002d +00272e: 7010 c100 0100 |000b: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@00c1 +002734: 1a02 da00 |000e: const-string v2, "assertEquals: o1: " // string@00da +002738: 6e20 c800 2100 |0010: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +00273e: 6e20 c700 3100 |0013: invoke-virtual {v1, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@00c7 +002744: 1a02 1300 |0016: const-string v2, ", o2: " // string@0013 +002748: 6e20 c800 2100 |0018: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +00274e: 6e20 c700 4100 |001b: invoke-virtual {v1, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@00c7 +002754: 6e10 ca00 0100 |001e: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@00ca +00275a: 0c01 |0021: move-result-object v1 +00275c: 7020 b500 1000 |0022: invoke-direct {v0, v1}, Ljava/lang/AssertionError;.:(Ljava/lang/Object;)V // method@00b5 +002762: 2700 |0025: throw v0 + catches : (none) + positions : + 0x0000 line=76 + 0x0006 line=79 + 0x0007 line=77 + locals : + 0x0000 - 0x0026 reg=3 o Ljava/lang/Object; + 0x0000 - 0x0026 reg=4 p Ljava/lang/Object; + + #8 : (in LTestBase;) + name : 'assertEquals' + type : '(SS)V' + access : 0x0008 (STATIC) + code - + registers : 5 + ins : 2 + outs : 2 + insns size : 34 16-bit code units +0027bc: |[0027bc] TestBase.assertEquals:(SS)V +0027cc: 3343 0300 |0000: if-ne v3, v4, 0003 // +0003 +0027d0: 0e00 |0002: return-void +0027d2: 2200 1e00 |0003: new-instance v0, Ljava/lang/AssertionError; // type@001e +0027d6: 2201 2d00 |0005: new-instance v1, Ljava/lang/StringBuilder; // type@002d +0027da: 7010 c100 0100 |0007: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@00c1 +0027e0: 1a02 d900 |000a: const-string v2, "assertEquals s1: " // string@00d9 +0027e4: 6e20 c800 2100 |000c: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +0027ea: 6e20 c500 3100 |000f: invoke-virtual {v1, v3}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@00c5 +0027f0: 1a02 1400 |0012: const-string v2, ", s2: " // string@0014 +0027f4: 6e20 c800 2100 |0014: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +0027fa: 6e20 c500 4100 |0017: invoke-virtual {v1, v4}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@00c5 +002800: 6e10 ca00 0100 |001a: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@00ca +002806: 0c01 |001d: move-result-object v1 +002808: 7020 b500 1000 |001e: invoke-direct {v0, v1}, Ljava/lang/AssertionError;.:(Ljava/lang/Object;)V // method@00b5 +00280e: 2700 |0021: throw v0 + catches : (none) + positions : + 0x0000 line=41 + 0x0002 line=42 + 0x0003 line=44 + locals : + 0x0000 - 0x0022 reg=3 s1 S + 0x0000 - 0x0022 reg=4 s2 S + + #9 : (in LTestBase;) + name : 'assertNotEquals' + type : '(Ljava/lang/Object;Ljava/lang/Object;)V' + access : 0x0008 (STATIC) + code - + registers : 5 + ins : 2 + outs : 2 + insns size : 38 16-bit code units +002810: |[002810] TestBase.assertNotEquals:(Ljava/lang/Object;Ljava/lang/Object;)V +002820: 7120 ec00 4300 |0000: invoke-static {v3, v4}, Ljava/util/Objects;.equals:(Ljava/lang/Object;Ljava/lang/Object;)Z // method@00ec +002826: 0a00 |0003: move-result v0 +002828: 3900 0300 |0004: if-nez v0, 0007 // +0003 +00282c: 0e00 |0006: return-void +00282e: 2200 1e00 |0007: new-instance v0, Ljava/lang/AssertionError; // type@001e +002832: 2201 2d00 |0009: new-instance v1, Ljava/lang/StringBuilder; // type@002d +002836: 7010 c100 0100 |000b: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@00c1 +00283c: 1a02 dc00 |000e: const-string v2, "assertNotEquals: o1: " // string@00dc +002840: 6e20 c800 2100 |0010: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +002846: 6e20 c700 3100 |0013: invoke-virtual {v1, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@00c7 +00284c: 1a02 1300 |0016: const-string v2, ", o2: " // string@0013 +002850: 6e20 c800 2100 |0018: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +002856: 6e20 c700 4100 |001b: invoke-virtual {v1, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@00c7 +00285c: 6e10 ca00 0100 |001e: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@00ca +002862: 0c01 |0021: move-result-object v1 +002864: 7020 b500 1000 |0022: invoke-direct {v0, v1}, Ljava/lang/AssertionError;.:(Ljava/lang/Object;)V // method@00b5 +00286a: 2700 |0025: throw v0 + catches : (none) + positions : + 0x0000 line=82 + 0x0006 line=85 + 0x0007 line=83 + locals : + 0x0000 - 0x0026 reg=3 o Ljava/lang/Object; + 0x0000 - 0x0026 reg=4 p Ljava/lang/Object; + + #10 : (in LTestBase;) + name : 'assertNotReached' + type : '()V' + access : 0x0008 (STATIC) + code - + registers : 2 + ins : 0 + outs : 2 + insns size : 8 16-bit code units +00286c: |[00286c] TestBase.assertNotReached:()V +00287c: 2200 1e00 |0000: new-instance v0, Ljava/lang/AssertionError; // type@001e +002880: 1a01 a300 |0002: const-string v1, "Unreachable" // string@00a3 +002884: 7020 b500 1000 |0004: invoke-direct {v0, v1}, Ljava/lang/AssertionError;.:(Ljava/lang/Object;)V // method@00b5 +00288a: 2700 |0007: throw v0 + catches : (none) + positions : + 0x0000 line=88 + locals : + + #11 : (in LTestBase;) + name : 'assertTrue' + type : '(Z)V' + access : 0x0008 (STATIC) + code - + registers : 4 + ins : 1 + outs : 2 + insns size : 26 16-bit code units +00288c: |[00288c] TestBase.assertTrue:(Z)V +00289c: 3803 0300 |0000: if-eqz v3, 0003 // +0003 +0028a0: 0e00 |0002: return-void +0028a2: 2200 1e00 |0003: new-instance v0, Ljava/lang/AssertionError; // type@001e +0028a6: 2201 2d00 |0005: new-instance v1, Ljava/lang/StringBuilder; // type@002d +0028aa: 7010 c100 0100 |0007: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@00c1 +0028b0: 1a02 df00 |000a: const-string v2, "assertTrue value: " // string@00df +0028b4: 6e20 c800 2100 |000c: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +0028ba: 6e20 c900 3100 |000f: invoke-virtual {v1, v3}, Ljava/lang/StringBuilder;.append:(Z)Ljava/lang/StringBuilder; // method@00c9 +0028c0: 6e10 ca00 0100 |0012: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@00ca +0028c6: 0c01 |0015: move-result-object v1 +0028c8: 7020 b500 1000 |0016: invoke-direct {v0, v1}, Ljava/lang/AssertionError;.:(Ljava/lang/Object;)V // method@00b5 +0028ce: 2700 |0019: throw v0 + catches : (none) + positions : + 0x0000 line=21 + 0x0002 line=24 + 0x0003 line=22 + locals : + 0x0000 - 0x001a reg=3 value Z + + #12 : (in LTestBase;) + name : 'fail' + type : '()V' + access : 0x0008 (STATIC) + code - + registers : 2 + ins : 0 + outs : 2 + insns size : 11 16-bit code units +0028d0: |[0028d0] TestBase.fail:()V +0028e0: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0028e4: 1a01 2601 |0002: const-string v1, "fail" // string@0126 +0028e8: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +0028ee: 7100 cc00 0000 |0007: invoke-static {}, Ljava/lang/Thread;.dumpStack:()V // method@00cc +0028f4: 0e00 |000a: return-void + catches : (none) + positions : + 0x0000 line=92 + 0x0007 line=93 + 0x000a line=94 + locals : + + Virtual methods - + source_file_idx : 145 (TestBase.java) + +Class #2 header: +class_idx : 11 +access_flags : 0 (0x0000) +superclass_idx : 42 +interfaces_off : 0 (0x000000) +source_file_idx : 148 +annotations_off : 30716 (0x0077fc) +class_data_off : 28990 (0x00713e) +static_fields_size : 0 +instance_fields_size: 1 +direct_methods_size : 1 +virtual_methods_size: 0 + +Class #2 annotations: +Annotations on class + VISIBILITY_SYSTEM Ldalvik/annotation/EnclosingClass; value=LTestInvocationKinds; + VISIBILITY_SYSTEM Ldalvik/annotation/InnerClass; accessFlags=8 name="Widget" + +Class #2 - + Class descriptor : 'LTestInvocationKinds$Widget;' + Access flags : 0x0000 () + Superclass : 'Ljava/lang/Object;' + Interfaces - + Static fields - + Instance fields - + #0 : (in LTestInvocationKinds$Widget;) + name : 'value' + type : 'I' + access : 0x0000 () + Direct methods - + #0 : (in LTestInvocationKinds$Widget;) + name : '' + type : '(I)V' + access : 0x10001 (PUBLIC CONSTRUCTOR) + code - + registers : 2 + ins : 2 + outs : 1 + insns size : 4 16-bit code units +002abc: |[002abc] TestInvocationKinds.Widget.:(I)V +002acc: 7010 bf00 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.:()V // method@00bf +002ad2: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=177 locals : - 0x0000 - 0x0004 reg=0 this Linvokecustom/Super; + 0x0000 - 0x0004 reg=0 this LTestInvocationKinds$Widget; + 0x0000 - 0x0004 reg=1 value I Virtual methods - - #0 : (in Linvokecustom/Super;) - name : 'helperMethodTest9' + source_file_idx : 148 (TestInvocationKinds.java) + +Class #3 header: +class_idx : 13 +access_flags : 0 (0x0000) +superclass_idx : 48 +interfaces_off : 0 (0x000000) +source_file_idx : 149 +annotations_off : 30732 (0x00780c) +class_data_off : 29002 (0x00714a) +static_fields_size : 0 +instance_fields_size: 0 +direct_methods_size : 1 +virtual_methods_size: 2 + +Class #3 annotations: +Annotations on class + VISIBILITY_SYSTEM Ldalvik/annotation/EnclosingClass; value=LTestInvokeCustomWithConcurrentThreads; + VISIBILITY_SYSTEM Ldalvik/annotation/InnerClass; accessFlags=0 name=null + VISIBILITY_SYSTEM Ldalvik/annotation/Signature; value={ "Ljava/lang/ThreadLocal<" "Ljava/lang/Integer;" ">;" } + +Class #3 - + Class descriptor : 'LTestInvokeCustomWithConcurrentThreads$1;' + Access flags : 0x0000 () + Superclass : 'Ljava/lang/ThreadLocal;' + Interfaces - + Static fields - + Instance fields - + Direct methods - + #0 : (in LTestInvokeCustomWithConcurrentThreads$1;) + name : '' type : '()V' - access : 0x0401 (PUBLIC ABSTRACT) - code : (none) + access : 0x10000 (CONSTRUCTOR) + code - + registers : 1 + ins : 1 + outs : 1 + insns size : 4 16-bit code units +002ee8: |[002ee8] TestInvokeCustomWithConcurrentThreads.1.:()V +002ef8: 7010 cf00 0000 |0000: invoke-direct {v0}, Ljava/lang/ThreadLocal;.:()V // method@00cf +002efe: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=33 + locals : + 0x0000 - 0x0004 reg=0 this LTestInvokeCustomWithConcurrentThreads$1; + + Virtual methods - + #0 : (in LTestInvokeCustomWithConcurrentThreads$1;) + name : 'initialValue' + type : '()Ljava/lang/Integer;' + access : 0x0004 (PROTECTED) + code - + registers : 2 + ins : 1 + outs : 1 + insns size : 13 16-bit code units +002ea0: |[002ea0] TestInvokeCustomWithConcurrentThreads.1.initialValue:()Ljava/lang/Integer; +002eb0: 7100 6500 0000 |0000: invoke-static {}, LTestInvokeCustomWithConcurrentThreads;.access$000:()Ljava/util/concurrent/atomic/AtomicInteger; // method@0065 +002eb6: 0c00 |0003: move-result-object v0 +002eb8: 6e10 f100 0000 |0004: invoke-virtual {v0}, Ljava/util/concurrent/atomic/AtomicInteger;.getAndIncrement:()I // method@00f1 +002ebe: 0a00 |0007: move-result v0 +002ec0: 7110 bd00 0000 |0008: invoke-static {v0}, Ljava/lang/Integer;.valueOf:(I)Ljava/lang/Integer; // method@00bd +002ec6: 0c00 |000b: move-result-object v0 +002ec8: 1100 |000c: return-object v0 + catches : (none) + positions : + 0x0000 line=36 + locals : + 0x0000 - 0x000d reg=1 this LTestInvokeCustomWithConcurrentThreads$1; + + #1 : (in LTestInvokeCustomWithConcurrentThreads$1;) + name : 'initialValue' + type : '()Ljava/lang/Object;' + access : 0x1044 (PROTECTED BRIDGE SYNTHETIC) + code - + registers : 2 + ins : 1 + outs : 1 + insns size : 5 16-bit code units +002ecc: |[002ecc] TestInvokeCustomWithConcurrentThreads.1.initialValue:()Ljava/lang/Object; +002edc: 6e10 6100 0100 |0000: invoke-virtual {v1}, LTestInvokeCustomWithConcurrentThreads$1;.initialValue:()Ljava/lang/Integer; // method@0061 +002ee2: 0c00 |0003: move-result-object v0 +002ee4: 1100 |0004: return-object v0 + catches : (none) + positions : + 0x0000 line=33 + locals : + 0x0000 - 0x0005 reg=1 this LTestInvokeCustomWithConcurrentThreads$1; + + source_file_idx : 149 (TestInvokeCustomWithConcurrentThreads.java) + +Class #4 header: +class_idx : 19 +access_flags : 0 (0x0000) +superclass_idx : 42 +interfaces_off : 0 (0x000000) +source_file_idx : 164 +annotations_off : 30748 (0x00781c) +class_data_off : 29021 (0x00715d) +static_fields_size : 0 +instance_fields_size: 0 +direct_methods_size : 2 +virtual_methods_size: 0 + +Class #4 annotations: +Annotations on method #170 'bsm' + VISIBILITY_SYSTEM Ldalvik/annotation/Signature; value={ "(" "Ljava/lang/invoke/MethodHandles$Lookup;" "Ljava/lang/String;" "Ljava/lang/invoke/MethodType;" "Ljava/lang/Class<" "*>;)" "Ljava/lang/invoke/CallSite;" } + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } - #1 : (in Linvokecustom/Super;) - name : 'targetMethodTest4' +Class #4 - + Class descriptor : 'LUnrelatedBSM;' + Access flags : 0x0000 () + Superclass : 'Ljava/lang/Object;' + Interfaces - + Static fields - + Instance fields - + Direct methods - + #0 : (in LUnrelatedBSM;) + name : '' type : '()V' - access : 0x0001 (PUBLIC) + access : 0x10000 (CONSTRUCTOR) code - - registers : 3 + registers : 1 ins : 1 - outs : 2 - insns size : 8 16-bit code units -0009a8: |[0009a8] invokecustom.Super.targetMethodTest4:()V -0009b8: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -0009bc: 1a01 8b00 |0002: const-string v1, "targetMethodTest4 from Super" // string@008b -0009c0: 6e20 2900 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -0009c6: 0e00 |0007: return-void + outs : 1 + insns size : 4 16-bit code units +003fc8: |[003fc8] UnrelatedBSM.:()V +003fd8: 7010 bf00 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.:()V // method@00bf +003fde: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=23 + locals : + 0x0000 - 0x0004 reg=0 this LUnrelatedBSM; + + #1 : (in LUnrelatedBSM;) + name : 'bsm' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Class;)Ljava/lang/invoke/CallSite;' + access : 0x0008 (STATIC) + code - + registers : 6 + ins : 4 + outs : 4 + insns size : 10 16-bit code units +003fa4: |[003fa4] UnrelatedBSM.bsm:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Class;)Ljava/lang/invoke/CallSite; +003fb4: 6e40 d800 5243 |0000: invoke-virtual {v2, v5, v3, v4}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00d8 +003fba: 0c00 |0003: move-result-object v0 +003fbc: 2201 3400 |0004: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0034 +003fc0: 7020 d200 0100 |0006: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +003fc6: 1101 |0009: return-object v1 catches : (none) positions : - 0x0000 line=31 - 0x0007 line=32 + 0x0000 line=27 + 0x0004 line=28 locals : - 0x0000 - 0x0008 reg=2 this Linvokecustom/Super; + 0x0000 - 0x0000 reg=5 (null) Ljava/lang/Class; + 0x0004 - 0x000a reg=0 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x000a reg=2 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x000a reg=3 name Ljava/lang/String; + 0x0000 - 0x000a reg=4 methodType Ljava/lang/invoke/MethodType; + 0x0000 - 0x000a reg=5 target Ljava/lang/Class; Ljava/lang/Class<*>; - source_file_idx : 27 (InvokeCustom.java) + Virtual methods - + source_file_idx : 164 (UnrelatedBSM.java) -Class #1 header: -class_idx : 7 +Class #5 header: +class_idx : 6 access_flags : 1 (0x0001) -superclass_idx : 8 -interfaces_off : 5460 (0x001554) -source_file_idx : 27 -annotations_off : 5396 (0x001514) -class_data_off : 8607 (0x00219f) -static_fields_size : 1 -instance_fields_size: 1 -direct_methods_size : 29 -virtual_methods_size: 3 +superclass_idx : 9 +interfaces_off : 0 (0x000000) +source_file_idx : 136 +annotations_off : 30772 (0x007834) +class_data_off : 29036 (0x00716c) +static_fields_size : 0 +instance_fields_size: 0 +direct_methods_size : 5 +virtual_methods_size: 0 -Class #1 annotations: -Annotations on method #3 'bsmCreateCallSite' +Class #5 annotations: +Annotations on method #1 'TestLinkerMethodMinimalArguments' VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } -Annotations on method #4 'bsmLookupStatic' - VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/NoSuchMethodException; Ljava/lang/IllegalAccessException; } -Annotations on method #5 'bsmLookupStaticWithExtraArgs' - VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/NoSuchMethodException; Ljava/lang/IllegalAccessException; } -Annotations on method #6 'bsmLookupTest9' +Annotations on method #2 'TestLinkerMethodMultipleArgumentTypes' VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } -Annotations on method #7 'checkFieldTest9' +Annotations on method #3 'TestUninitializedCallSite' VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } -Annotations on method #8 'checkStaticFieldTest9' +Annotations on method #7 'main' VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } -Class #1 - - Class descriptor : 'Linvokecustom/InvokeCustom;' +Class #5 - + Class descriptor : 'LMain;' Access flags : 0x0001 (PUBLIC) - Superclass : 'Linvokecustom/Super;' + Superclass : 'LTestBase;' Interfaces - - #0 : 'Ljava/lang/Runnable;' Static fields - - #0 : (in Linvokecustom/InvokeCustom;) - name : 'staticFieldTest9' - type : 'I' - access : 0x000a (PRIVATE STATIC) Instance fields - - #0 : (in Linvokecustom/InvokeCustom;) - name : 'fieldTest9' - type : 'F' - access : 0x0002 (PRIVATE) Direct methods - - #0 : (in Linvokecustom/InvokeCustom;) - name : '' + #0 : (in LMain;) + name : '' type : '()V' - access : 0x10008 (STATIC CONSTRUCTOR) + access : 0x10001 (PUBLIC CONSTRUCTOR) code - registers : 1 - ins : 0 - outs : 0 + ins : 1 + outs : 1 insns size : 4 16-bit code units -0009c8: |[0009c8] invokecustom.InvokeCustom.:()V -0009d8: 1200 |0000: const/4 v0, #int 0 // #0 -0009da: 6700 0100 |0001: sput v0, Linvokecustom/InvokeCustom;.staticFieldTest9:I // field@0001 -0009de: 0e00 |0003: return-void +001900: |[001900] Main.:()V +001910: 7010 3200 0000 |0000: invoke-direct {v0}, LTestBase;.:()V // method@0032 +001916: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=21 + locals : + 0x0000 - 0x0004 reg=0 this LMain; + + #1 : (in LMain;) + name : 'TestLinkerMethodMinimalArguments' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 4 + ins : 0 + outs : 3 + insns size : 66 16-bit code units +001918: |[001918] Main.TestLinkerMethodMinimalArguments:()V +001928: 1210 |0000: const/4 v0, #int 1 // #1 +00192a: 1301 0a00 |0001: const/16 v1, #int 10 // #a +00192e: 7130 7700 1001 |0003: invoke-static {v0, v1, v1}, LTestLinkerMethodMinimalArguments;.test:(III)V // method@0077 +001934: 7100 0500 0000 |0006: invoke-static {}, LMain;.assertNotReached:()V // method@0005 +00193a: 280f |0009: goto 0018 // +000f +00193c: 0d00 |000a: move-exception v0 +00193e: 6e10 b600 0000 |000b: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +001944: 0c02 |000e: move-result-object v2 +001946: 6e10 c000 0200 |000f: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +00194c: 0c02 |0012: move-result-object v2 +00194e: 1c03 2200 |0013: const-class v3, Ljava/lang/ClassCastException; // type@0022 +001952: 7120 0400 3200 |0015: invoke-static {v2, v3}, LMain;.assertEquals:(Ljava/lang/Object;Ljava/lang/Object;)V // method@0004 +001958: 1220 |0018: const/4 v0, #int 2 // #2 +00195a: 1302 0b00 |0019: const/16 v2, #int 11 // #b +00195e: 7130 7700 1002 |001b: invoke-static {v0, v1, v2}, LTestLinkerMethodMinimalArguments;.test:(III)V // method@0077 +001964: 7100 0500 0000 |001e: invoke-static {}, LMain;.assertNotReached:()V // method@0005 +00196a: 280f |0021: goto 0030 // +000f +00196c: 0d00 |0022: move-exception v0 +00196e: 6e10 b600 0000 |0023: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +001974: 0c02 |0026: move-result-object v2 +001976: 6e10 c000 0200 |0027: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +00197c: 0c02 |002a: move-result-object v2 +00197e: 1c03 2600 |002b: const-class v3, Ljava/lang/InstantiationException; // type@0026 +001982: 7120 0400 3200 |002d: invoke-static {v2, v3}, LMain;.assertEquals:(Ljava/lang/Object;Ljava/lang/Object;)V // method@0004 +001988: 1230 |0030: const/4 v0, #int 3 // #3 +00198a: 1302 0c00 |0031: const/16 v2, #int 12 // #c +00198e: 7130 7700 1002 |0033: invoke-static {v0, v1, v2}, LTestLinkerMethodMinimalArguments;.test:(III)V // method@0077 +001994: 7100 0500 0000 |0036: invoke-static {}, LMain;.assertNotReached:()V // method@0005 +00199a: 2802 |0039: goto 003b // +0002 +00199c: 0d00 |003a: move-exception v0 +00199e: 1200 |003b: const/4 v0, #int 0 // #0 +0019a0: 1302 0d00 |003c: const/16 v2, #int 13 // #d +0019a4: 7130 7700 1002 |003e: invoke-static {v0, v1, v2}, LTestLinkerMethodMinimalArguments;.test:(III)V // method@0077 +0019aa: 0e00 |0041: return-void + catches : 3 + 0x0003 - 0x0009 + Ljava/lang/BootstrapMethodError; -> 0x000a + 0x001b - 0x0021 + Ljava/lang/BootstrapMethodError; -> 0x0022 + 0x0033 - 0x0039 + Ljava/lang/ArithmeticException; -> 0x003a + positions : + 0x0000 line=49 + 0x0006 line=53 + 0x0009 line=56 + 0x000a line=54 + 0x000b line=55 + 0x0018 line=59 + 0x001e line=61 + 0x0021 line=64 + 0x0022 line=62 + 0x0023 line=63 + 0x0030 line=67 + 0x0036 line=69 + 0x0039 line=71 + 0x003a line=70 + 0x003b line=73 + 0x0041 line=75 + locals : + 0x000b - 0x0018 reg=0 e Ljava/lang/BootstrapMethodError; + 0x0023 - 0x0030 reg=0 e Ljava/lang/BootstrapMethodError; + + #2 : (in LMain;) + name : 'TestLinkerMethodMultipleArgumentTypes' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 2 + ins : 0 + outs : 2 + insns size : 22 16-bit code units +0019d0: |[0019d0] Main.TestLinkerMethodMultipleArgumentTypes:()V +0019e0: 1300 2100 |0000: const/16 v0, #int 33 // #21 +0019e4: 1301 4300 |0002: const/16 v1, #int 67 // #43 +0019e8: 7120 8400 1000 |0004: invoke-static {v0, v1}, LTestLinkerMethodMultipleArgumentTypes;.test:(II)V // method@0084 +0019ee: 1300 f0d8 |0007: const/16 v0, #int -10000 // #d8f0 +0019f2: 1301 e803 |0009: const/16 v1, #int 1000 // #3e8 +0019f6: 7120 8400 1000 |000b: invoke-static {v0, v1}, LTestLinkerMethodMultipleArgumentTypes;.test:(II)V // method@0084 +0019fc: 1300 18fc |000e: const/16 v0, #int -1000 // #fc18 +001a00: 1301 1027 |0010: const/16 v1, #int 10000 // #2710 +001a04: 7120 8400 1000 |0012: invoke-static {v0, v1}, LTestLinkerMethodMultipleArgumentTypes;.test:(II)V // method@0084 +001a0a: 0e00 |0015: return-void + catches : (none) + positions : + 0x0000 line=42 + 0x0007 line=43 + 0x000e line=44 + 0x0015 line=45 + locals : + + #3 : (in LMain;) + name : 'TestUninitializedCallSite' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 7 + ins : 0 + outs : 3 + insns size : 78 16-bit code units +001a0c: |[001a0c] Main.TestUninitializedCallSite:()V +001a1c: 2200 3900 |0000: new-instance v0, Ljava/lang/invoke/MutableCallSite; // type@0039 +001a20: 6201 1200 |0002: sget-object v1, Ljava/lang/Integer;.TYPE:Ljava/lang/Class; // field@0012 +001a24: 7110 e100 0100 |0004: invoke-static {v1}, Ljava/lang/invoke/MethodType;.methodType:(Ljava/lang/Class;)Ljava/lang/invoke/MethodType; // method@00e1 +001a2a: 0c01 |0007: move-result-object v1 +001a2c: 7020 e600 1000 |0008: invoke-direct {v0, v1}, Ljava/lang/invoke/MutableCallSite;.:(Ljava/lang/invoke/MethodType;)V // method@00e6 +001a32: 6e10 d100 0000 |000b: invoke-virtual {v0}, Ljava/lang/invoke/CallSite;.getTarget:()Ljava/lang/invoke/MethodHandle; // method@00d1 +001a38: 0c01 |000e: move-result-object v1 +001a3a: fa10 d300 0100 4100 |000f: invoke-polymorphic {v1}, Ljava/lang/invoke/MethodHandle;.invoke:([Ljava/lang/Object;)Ljava/lang/Object;, ()V // method@00d3, proto@0041 +001a42: 7100 0600 0000 |0013: invoke-static {}, LMain;.fail:()V // method@0006 +001a48: 2809 |0016: goto 001f // +0009 +001a4a: 0d01 |0017: move-exception v1 +001a4c: 6202 1300 |0018: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001a50: 1a03 2100 |001a: const-string v3, "Caught exception from uninitialized call site" // string@0021 +001a54: 6e20 b300 3200 |001c: invoke-virtual {v2, v3}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +001a5a: 2201 3900 |001f: new-instance v1, Ljava/lang/invoke/MutableCallSite; // type@0039 +001a5e: 1c02 2c00 |0021: const-class v2, Ljava/lang/String; // type@002c +001a62: 6203 1200 |0023: sget-object v3, Ljava/lang/Integer;.TYPE:Ljava/lang/Class; // field@0012 +001a66: 1214 |0025: const/4 v4, #int 1 // #1 +001a68: 2344 4600 |0026: new-array v4, v4, [Ljava/lang/Class; // type@0046 +001a6c: 1205 |0028: const/4 v5, #int 0 // #0 +001a6e: 6206 1100 |0029: sget-object v6, Ljava/lang/Character;.TYPE:Ljava/lang/Class; // field@0011 +001a72: 4d06 0405 |002b: aput-object v6, v4, v5 +001a76: 7130 e200 3204 |002d: invoke-static {v2, v3, v4}, Ljava/lang/invoke/MethodType;.methodType:(Ljava/lang/Class;Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType; // method@00e2 +001a7c: 0c02 |0030: move-result-object v2 +001a7e: 7020 e600 2100 |0031: invoke-direct {v1, v2}, Ljava/lang/invoke/MutableCallSite;.:(Ljava/lang/invoke/MethodType;)V // method@00e6 +001a84: 0710 |0034: move-object v0, v1 +001a86: 6e10 d100 0000 |0035: invoke-virtual {v0}, Ljava/lang/invoke/CallSite;.getTarget:()Ljava/lang/invoke/MethodHandle; // method@00d1 +001a8c: 0c01 |0038: move-result-object v1 +001a8e: 1302 ff05 |0039: const/16 v2, #int 1535 // #5ff +001a92: 1303 6400 |003b: const/16 v3, #int 100 // #64 +001a96: fa30 d300 2103 4800 |003d: invoke-polymorphic {v1, v2, v3}, Ljava/lang/invoke/MethodHandle;.invoke:([Ljava/lang/Object;)Ljava/lang/Object;, (IC)V // method@00d3, proto@0048 +001a9e: 7100 0600 0000 |0041: invoke-static {}, LMain;.fail:()V // method@0006 +001aa4: 2809 |0044: goto 004d // +0009 +001aa6: 0d01 |0045: move-exception v1 +001aa8: 6202 1300 |0046: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001aac: 1a03 2100 |0048: const-string v3, "Caught exception from uninitialized call site" // string@0021 +001ab0: 6e20 b300 3200 |004a: invoke-virtual {v2, v3}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +001ab6: 0e00 |004d: return-void + catches : 2 + 0x000b - 0x0016 + Ljava/lang/IllegalStateException; -> 0x0017 + 0x0035 - 0x0044 + Ljava/lang/IllegalStateException; -> 0x0045 + positions : + 0x0000 line=24 + 0x000b line=26 + 0x0013 line=27 + 0x0016 line=30 + 0x0017 line=28 + 0x0018 line=29 + 0x001f line=32 + 0x0035 line=34 + 0x0041 line=35 + 0x0044 line=38 + 0x0045 line=36 + 0x0046 line=37 + 0x004d line=39 + locals : + 0x0018 - 0x001f reg=1 e Ljava/lang/IllegalStateException; + 0x0046 - 0x004d reg=1 e Ljava/lang/IllegalStateException; + 0x000b - 0x004e reg=0 callSite Ljava/lang/invoke/CallSite; + + #4 : (in LMain;) + name : 'main' + type : '([Ljava/lang/String;)V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 1 + ins : 1 + outs : 0 + insns size : 28 16-bit code units +001ad0: |[001ad0] Main.main:([Ljava/lang/String;)V +001ae0: 7100 0300 0000 |0000: invoke-static {}, LMain;.TestUninitializedCallSite:()V // method@0003 +001ae6: 7100 0100 0000 |0003: invoke-static {}, LMain;.TestLinkerMethodMinimalArguments:()V // method@0001 +001aec: 7100 0200 0000 |0006: invoke-static {}, LMain;.TestLinkerMethodMultipleArgumentTypes:()V // method@0002 +001af2: 7100 8c00 0000 |0009: invoke-static {}, LTestLinkerUnrelatedBSM;.test:()V // method@008c +001af8: 7100 6e00 0000 |000c: invoke-static {}, LTestInvokeCustomWithConcurrentThreads;.test:()V // method@006e +001afe: 7100 5b00 0000 |000f: invoke-static {}, LTestInvocationKinds;.test:()V // method@005b +001b04: 7100 4500 0000 |0012: invoke-static {}, LTestDynamicBootstrapArguments;.test:()V // method@0045 +001b0a: 7100 2b00 0000 |0015: invoke-static {}, LTestBadBootstrapArguments;.test:()V // method@002b +001b10: 7100 a800 0000 |0018: invoke-static {}, LTestVariableArityLinkerMethod;.test:()V // method@00a8 +001b16: 0e00 |001b: return-void + catches : (none) + positions : + 0x0000 line=78 + 0x0003 line=79 + 0x0006 line=80 + 0x0009 line=81 + 0x000c line=82 + 0x000f line=83 + 0x0012 line=84 + 0x0015 line=85 + 0x0018 line=86 + 0x001b line=87 + locals : + 0x0000 - 0x001c reg=0 args [Ljava/lang/String; + + Virtual methods - + source_file_idx : 136 (Main.java) + +Class #6 header: +class_idx : 8 +access_flags : 1 (0x0001) +superclass_idx : 9 +interfaces_off : 0 (0x000000) +source_file_idx : 144 +annotations_off : 30820 (0x007864) +class_data_off : 29062 (0x007186) +static_fields_size : 0 +instance_fields_size: 0 +direct_methods_size : 38 +virtual_methods_size: 0 + +Class #6 annotations: +Annotations on class + VISIBILITY_SYSTEM Ldalvik/annotation/MemberClasses; value={ LTestBadBootstrapArguments$TestersConstantCallSite; } +Annotations on method #14 'bsm' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #15 'bsmDJ' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #16 'bsmDoubleLong' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #19 'bsmReturningTestersConstantCallsite' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #21 'bsmZBCS' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #25 'invokeBoxingArguments' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestBadBootstrapArguments; name="bsmDoubleLong" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; Ljava/lang/Double; Ljava/lang/Long; } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; doubleValue={ 1.79769e+308 } Lannotations/Constant; longValue={ 9223372036854775807 } } fieldOrMethodName="boxingArguments" +Annotations on method #26 'invokeExtraArguments' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestBadBootstrapArguments; name="bsm" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; I Ljava/lang/String; } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; intValue={ 1 } Lannotations/Constant; stringValue={ "2" } Lannotations/Constant; intValue={ 3 } } fieldOrMethodName="extraArguments" +Annotations on method #27 'invokeHappy' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestBadBootstrapArguments; name="bsm" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; I Ljava/lang/String; } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; intValue={ -1 } Lannotations/Constant; stringValue={ "very" } } fieldOrMethodName="happy" +Annotations on method #28 'invokeIntegerReturnType' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestBadBootstrapArguments; name="bsmReturningInteger" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; } returnType=Ljava/lang/Integer; } fieldOrMethodName="integerReturnType" +Annotations on method #29 'invokeMissingParameterTypes' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestBadBootstrapArguments; name="bsm" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; I D } } constantArgumentsForBootstrapMethod={ } fieldOrMethodName="missingParameterTypes" + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/NoSuchMethodError; } +Annotations on method #30 'invokeNarrowArguments' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestBadBootstrapArguments; name="bsmZBCS" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; Z B C S } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; booleanValue={ true } Lannotations/Constant; byteValue={ 127 } Lannotations/Constant; charValue={ 65 } Lannotations/Constant; shortValue={ -32768 } } fieldOrMethodName="narrowArguments" +Annotations on method #31 'invokeObjectReturnType' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestBadBootstrapArguments; name="bsmReturningObject" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; } returnType=Ljava/lang/Object; } fieldOrMethodName="ObjectReturnType" +Annotations on method #32 'invokeViaCustomCallSiteClass' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestBadBootstrapArguments; name="bsmReturningTestersConstantCallsite" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; } returnType=LTestBadBootstrapArguments$TestersConstantCallSite; } fieldOrMethodName="sayHello" +Annotations on method #33 'invokeVoidReturnType' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestBadBootstrapArguments; name="bsmReturningVoid" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; } returnType=V } fieldOrMethodName="voidReturnType" +Annotations on method #34 'invokeWideningArguments' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestBadBootstrapArguments; name="bsmDJ" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; D J } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; doubleValue={ 1.79769e+308 } Lannotations/Constant; intValue={ 2147483647 } } fieldOrMethodName="wideningArguments" +Annotations on method #35 'invokeWideningBoxingArguments' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestBadBootstrapArguments; name="bsmDoubleLong" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; Ljava/lang/Double; Ljava/lang/Long; } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; floatValue={ 3.40282e+38 } Lannotations/Constant; longValue={ 2147483647 } } fieldOrMethodName="wideningBoxingArguments" +Annotations on method #36 'invokeWrongArguments' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestBadBootstrapArguments; name="bsm" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; I Ljava/lang/String; } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; stringValue={ "1" } Lannotations/Constant; doubleValue={ 3.14159 } } fieldOrMethodName="wrongArguments" +Annotations on method #37 'invokeWrongArgumentsAgain' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestBadBootstrapArguments; name="bsm" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; I Ljava/lang/String; } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; doubleValue={ 3.14159 } Lannotations/Constant; stringValue={ "pie" } } fieldOrMethodName="wrongArgumentsAgain" +Annotations on method #38 'invokeWrongParameterTypes' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestBadBootstrapArguments; name="bsm" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; I D } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; intValue={ -1 } Lannotations/Constant; stringValue={ "very" } } fieldOrMethodName="wrongParameterTypes" + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/NoSuchMethodError; } + +Class #6 - + Class descriptor : 'LTestBadBootstrapArguments;' + Access flags : 0x0001 (PUBLIC) + Superclass : 'LTestBase;' + Interfaces - + Static fields - + Instance fields - + Direct methods - + #0 : (in LTestBadBootstrapArguments;) + name : '' + type : '()V' + access : 0x10001 (PUBLIC CONSTRUCTOR) + code - + registers : 1 + ins : 1 + outs : 1 + insns size : 4 16-bit code units +001d64: |[001d64] TestBadBootstrapArguments.:()V +001d74: 7010 3200 0000 |0000: invoke-direct {v0}, LTestBase;.:()V // method@0032 +001d7a: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=27 + locals : + 0x0000 - 0x0004 reg=0 this LTestBadBootstrapArguments; + + #1 : (in LTestBadBootstrapArguments;) + name : 'boxingArguments' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 2 + ins : 0 + outs : 2 + insns size : 8 16-bit code units +001d7c: |[001d7c] TestBadBootstrapArguments.boxingArguments:()V +001d8c: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001d90: 1a01 e900 |0002: const-string v1, "boxingArguments" // string@00e9 +001d94: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +001d9a: 0e00 |0007: return-void + catches : (none) + positions : + 0x0000 line=348 + 0x0007 line=349 + locals : + + #2 : (in LTestBadBootstrapArguments;) + name : 'bsm' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ILjava/lang/String;)Ljava/lang/invoke/CallSite;' + access : 0x000a (PRIVATE STATIC) + code - + registers : 7 + ins : 5 + outs : 4 + insns size : 85 16-bit code units +001bb4: |[001bb4] TestBadBootstrapArguments.bsm:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ILjava/lang/String;)Ljava/lang/invoke/CallSite; +001bc4: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001bc8: 1a01 ee00 |0002: const-string v1, "bsm(" // string@00ee +001bcc: 6e20 b000 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +001bd2: 6200 1300 |0007: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001bd6: 6e10 dc00 0200 |0009: invoke-virtual {v2}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@00dc +001bdc: 0c01 |000c: move-result-object v1 +001bde: 6e20 af00 1000 |000d: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.print:(Ljava/lang/Object;)V // method@00af +001be4: 6200 1300 |0010: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001be8: 1a01 0c00 |0012: const-string v1, ", " // string@000c +001bec: 6e20 b000 1000 |0014: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +001bf2: 6200 1300 |0017: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001bf6: 6e20 b000 3000 |0019: invoke-virtual {v0, v3}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +001bfc: 6200 1300 |001c: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001c00: 1a01 0c00 |001e: const-string v1, ", " // string@000c +001c04: 6e20 b000 1000 |0020: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +001c0a: 6200 1300 |0023: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001c0e: 6e20 af00 4000 |0025: invoke-virtual {v0, v4}, Ljava/io/PrintStream;.print:(Ljava/lang/Object;)V // method@00af +001c14: 6200 1300 |0028: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001c18: 1a01 0c00 |002a: const-string v1, ", " // string@000c +001c1c: 6e20 b000 1000 |002c: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +001c22: 6200 1300 |002f: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001c26: 6e20 ad00 5000 |0031: invoke-virtual {v0, v5}, Ljava/io/PrintStream;.print:(I)V // method@00ad +001c2c: 6200 1300 |0034: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001c30: 1a01 0c00 |0036: const-string v1, ", " // string@000c +001c34: 6e20 b000 1000 |0038: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +001c3a: 6200 1300 |003b: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001c3e: 6e20 b000 6000 |003d: invoke-virtual {v0, v6}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +001c44: 6200 1300 |0040: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001c48: 1a01 0700 |0042: const-string v1, ")" // string@0007 +001c4c: 6e20 b300 1000 |0044: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +001c52: 6e10 dc00 0200 |0047: invoke-virtual {v2}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@00dc +001c58: 0c00 |004a: move-result-object v0 +001c5a: 6e40 d800 0243 |004b: invoke-virtual {v2, v0, v3, v4}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00d8 +001c60: 0c00 |004e: move-result-object v0 +001c62: 2201 3400 |004f: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0034 +001c66: 7020 d200 0100 |0051: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +001c6c: 1101 |0054: return-object v1 + catches : (none) + positions : + 0x0000 line=35 + 0x0007 line=36 + 0x0010 line=37 + 0x0017 line=38 + 0x001c line=39 + 0x0023 line=40 + 0x0028 line=41 + 0x002f line=42 + 0x0034 line=43 + 0x003b line=44 + 0x0040 line=45 + 0x0047 line=46 + 0x004f line=47 + locals : + 0x004f - 0x0055 reg=0 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x0055 reg=2 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0055 reg=3 methodName Ljava/lang/String; + 0x0000 - 0x0055 reg=4 methodType Ljava/lang/invoke/MethodType; + 0x0000 - 0x0055 reg=5 extraInt I + 0x0000 - 0x0055 reg=6 extraString Ljava/lang/String; + + #3 : (in LTestBadBootstrapArguments;) + name : 'bsmDJ' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;DJ)Ljava/lang/invoke/CallSite;' + access : 0x000a (PRIVATE STATIC) + code - + registers : 9 + ins : 7 + outs : 4 + insns size : 45 16-bit code units +001c70: |[001c70] TestBadBootstrapArguments.bsmDJ:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;DJ)Ljava/lang/invoke/CallSite; +001c80: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001c84: 1a01 f100 |0002: const-string v1, "bsmDJ(..., " // string@00f1 +001c88: 6e20 b000 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +001c8e: 6200 1300 |0007: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001c92: 6e30 ac00 5006 |0009: invoke-virtual {v0, v5, v6}, Ljava/io/PrintStream;.print:(D)V // method@00ac +001c98: 6200 1300 |000c: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001c9c: 1a01 0c00 |000e: const-string v1, ", " // string@000c +001ca0: 6e20 b000 1000 |0010: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +001ca6: 6200 1300 |0013: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001caa: 6e30 ae00 7008 |0015: invoke-virtual {v0, v7, v8}, Ljava/io/PrintStream;.print:(J)V // method@00ae +001cb0: 6200 1300 |0018: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001cb4: 1a01 0700 |001a: const-string v1, ")" // string@0007 +001cb8: 6e20 b300 1000 |001c: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +001cbe: 6e10 dc00 0200 |001f: invoke-virtual {v2}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@00dc +001cc4: 0c00 |0022: move-result-object v0 +001cc6: 6e40 d800 0243 |0023: invoke-virtual {v2, v0, v3, v4}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00d8 +001ccc: 0c00 |0026: move-result-object v0 +001cce: 2201 3400 |0027: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0034 +001cd2: 7020 d200 0100 |0029: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +001cd8: 1101 |002c: return-object v1 + catches : (none) + positions : + 0x0000 line=270 + 0x0007 line=271 + 0x000c line=272 + 0x0013 line=273 + 0x0018 line=274 + 0x001f line=275 + 0x0027 line=276 + locals : + 0x0027 - 0x002d reg=0 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x002d reg=2 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x002d reg=3 methodName Ljava/lang/String; + 0x0000 - 0x002d reg=4 methodType Ljava/lang/invoke/MethodType; + 0x0000 - 0x002d reg=5 extraArg0 D + 0x0000 - 0x002d reg=7 extraArg1 J + + #4 : (in LTestBadBootstrapArguments;) + name : 'bsmDoubleLong' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Double;Ljava/lang/Long;)Ljava/lang/invoke/CallSite;' + access : 0x000a (PRIVATE STATIC) + code - + registers : 7 + ins : 5 + outs : 4 + insns size : 45 16-bit code units +001cdc: |[001cdc] TestBadBootstrapArguments.bsmDoubleLong:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Double;Ljava/lang/Long;)Ljava/lang/invoke/CallSite; +001cec: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001cf0: 1a01 f300 |0002: const-string v1, "bsmDoubleLong(..., " // string@00f3 +001cf4: 6e20 b000 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +001cfa: 6200 1300 |0007: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001cfe: 6e20 af00 5000 |0009: invoke-virtual {v0, v5}, Ljava/io/PrintStream;.print:(Ljava/lang/Object;)V // method@00af +001d04: 6200 1300 |000c: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001d08: 1a01 0c00 |000e: const-string v1, ", " // string@000c +001d0c: 6e20 b000 1000 |0010: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +001d12: 6200 1300 |0013: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001d16: 6e20 af00 6000 |0015: invoke-virtual {v0, v6}, Ljava/io/PrintStream;.print:(Ljava/lang/Object;)V // method@00af +001d1c: 6200 1300 |0018: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001d20: 1a01 0700 |001a: const-string v1, ")" // string@0007 +001d24: 6e20 b300 1000 |001c: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +001d2a: 6e10 dc00 0200 |001f: invoke-virtual {v2}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@00dc +001d30: 0c00 |0022: move-result-object v0 +001d32: 6e40 d800 0243 |0023: invoke-virtual {v2, v0, v3, v4}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00d8 +001d38: 0c00 |0026: move-result-object v0 +001d3a: 2201 3400 |0027: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0034 +001d3e: 7020 d200 0100 |0029: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +001d44: 1101 |002c: return-object v1 + catches : (none) + positions : + 0x0000 line=314 + 0x0007 line=315 + 0x000c line=316 + 0x0013 line=317 + 0x0018 line=318 + 0x001f line=319 + 0x0027 line=320 + locals : + 0x0027 - 0x002d reg=0 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x002d reg=2 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x002d reg=3 methodName Ljava/lang/String; + 0x0000 - 0x002d reg=4 methodType Ljava/lang/invoke/MethodType; + 0x0000 - 0x002d reg=5 extraArg0 Ljava/lang/Double; + 0x0000 - 0x002d reg=6 extraArg1 Ljava/lang/Long; + + #5 : (in LTestBadBootstrapArguments;) + name : 'bsmReturningInteger' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/Integer;' + access : 0x0008 (STATIC) + code - + registers : 5 + ins : 3 + outs : 2 + insns size : 13 16-bit code units +001b5c: |[001b5c] TestBadBootstrapArguments.bsmReturningInteger:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/Integer; +001b6c: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001b70: 1a01 eb00 |0002: const-string v1, "bsm returning Integer value." // string@00eb +001b74: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +001b7a: 1230 |0007: const/4 v0, #int 3 // #3 +001b7c: 7110 bd00 0000 |0008: invoke-static {v0}, Ljava/lang/Integer;.valueOf:(I)Ljava/lang/Integer; // method@00bd +001b82: 0c00 |000b: move-result-object v0 +001b84: 1100 |000c: return-object v0 + catches : (none) + positions : + 0x0000 line=425 + 0x0007 line=426 + locals : + 0x0000 - 0x000d reg=2 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x000d reg=3 name Ljava/lang/String; + 0x0000 - 0x000d reg=4 type Ljava/lang/invoke/MethodType; + + #6 : (in LTestBadBootstrapArguments;) + name : 'bsmReturningObject' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/Object;' + access : 0x0008 (STATIC) + code - + registers : 5 + ins : 3 + outs : 2 + insns size : 13 16-bit code units +001b88: |[001b88] TestBadBootstrapArguments.bsmReturningObject:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/Object; +001b98: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001b9c: 1a01 ec00 |0002: const-string v1, "bsm returning Object value." // string@00ec +001ba0: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +001ba6: 2200 2a00 |0007: new-instance v0, Ljava/lang/Object; // type@002a +001baa: 7010 bf00 0000 |0009: invoke-direct {v0}, Ljava/lang/Object;.:()V // method@00bf +001bb0: 1100 |000c: return-object v0 + catches : (none) + positions : + 0x0000 line=402 + 0x0007 line=403 + locals : + 0x0000 - 0x000d reg=2 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x000d reg=3 name Ljava/lang/String; + 0x0000 - 0x000d reg=4 type Ljava/lang/invoke/MethodType; + + #7 : (in LTestBadBootstrapArguments;) + name : 'bsmReturningTestersConstantCallsite' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)LTestBadBootstrapArguments$TestersConstantCallSite;' + access : 0x0008 (STATIC) + code - + registers : 5 + ins : 3 + outs : 4 + insns size : 14 16-bit code units +001b30: |[001b30] TestBadBootstrapArguments.bsmReturningTestersConstantCallsite:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)LTestBadBootstrapArguments$TestersConstantCallSite; +001b40: 2200 0700 |0000: new-instance v0, LTestBadBootstrapArguments$TestersConstantCallSite; // type@0007 +001b44: 6e10 dc00 0200 |0002: invoke-virtual {v2}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@00dc +001b4a: 0c01 |0005: move-result-object v1 +001b4c: 6e40 d800 1243 |0006: invoke-virtual {v2, v1, v3, v4}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00d8 +001b52: 0c01 |0009: move-result-object v1 +001b54: 7020 0800 1000 |000a: invoke-direct {v0, v1}, LTestBadBootstrapArguments$TestersConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@0008 +001b5a: 1100 |000d: return-object v0 + catches : (none) + positions : + 0x0000 line=455 + locals : + 0x0000 - 0x000e reg=2 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x000e reg=3 name Ljava/lang/String; + 0x0000 - 0x000e reg=4 type Ljava/lang/invoke/MethodType; + + #8 : (in LTestBadBootstrapArguments;) + name : 'bsmReturningVoid' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)V' + access : 0x0008 (STATIC) + code - + registers : 5 + ins : 3 + outs : 2 + insns size : 8 16-bit code units +001d9c: |[001d9c] TestBadBootstrapArguments.bsmReturningVoid:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)V +001dac: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001db0: 1a01 ed00 |0002: const-string v1, "bsm returning void value." // string@00ed +001db4: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +001dba: 0e00 |0007: return-void + catches : (none) + positions : + 0x0000 line=380 + 0x0007 line=381 + locals : + 0x0000 - 0x0008 reg=2 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0008 reg=3 name Ljava/lang/String; + 0x0000 - 0x0008 reg=4 type Ljava/lang/invoke/MethodType; + + #9 : (in LTestBadBootstrapArguments;) + name : 'bsmZBCS' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCS)Ljava/lang/invoke/CallSite;' + access : 0x000a (PRIVATE STATIC) + code - + registers : 8 + ins : 7 + outs : 0 + insns size : 5 16-bit code units +001d48: |[001d48] TestBadBootstrapArguments.bsmZBCS:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCS)Ljava/lang/invoke/CallSite; +001d58: 7100 0b00 0000 |0000: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +001d5e: 1200 |0003: const/4 v0, #int 0 // #0 +001d60: 1100 |0004: return-object v0 + catches : (none) + positions : + 0x0000 line=227 + 0x0003 line=228 + locals : + 0x0000 - 0x0005 reg=1 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0005 reg=2 methodName Ljava/lang/String; + 0x0000 - 0x0005 reg=3 methodType Ljava/lang/invoke/MethodType; + 0x0000 - 0x0005 reg=4 extraArg0 Z + 0x0000 - 0x0005 reg=5 extraArg1 B + 0x0000 - 0x0005 reg=6 extraArg2 C + 0x0000 - 0x0005 reg=7 extraArg3 S + + #10 : (in LTestBadBootstrapArguments;) + name : 'extraArguments' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 2 + ins : 0 + outs : 2 + insns size : 8 16-bit code units +001dbc: |[001dbc] TestBadBootstrapArguments.extraArguments:()V +001dcc: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001dd0: 1a01 2001 |0002: const-string v1, "extraArguments" // string@0120 +001dd4: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +001dda: 0e00 |0007: return-void + catches : (none) + positions : + 0x0000 line=158 + 0x0007 line=159 + locals : + + #11 : (in LTestBadBootstrapArguments;) + name : 'happy' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 2 + ins : 0 + outs : 2 + insns size : 8 16-bit code units +001ddc: |[001ddc] TestBadBootstrapArguments.happy:()V +001dec: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001df0: 1a01 3d01 |0002: const-string v1, "happy" // string@013d +001df4: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +001dfa: 0e00 |0007: return-void + catches : (none) + positions : + 0x0000 line=74 + 0x0007 line=75 + locals : + + #12 : (in LTestBadBootstrapArguments;) + name : 'integerReturnType' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 0 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +001dfc: |[001dfc] TestBadBootstrapArguments.integerReturnType:()V +001e0c: 7100 0b00 0000 |0000: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +001e12: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=444 + 0x0003 line=445 + locals : + + #13 : (in LTestBadBootstrapArguments;) + name : 'invokeBoxingArguments' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 0 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +001e14: |[001e14] TestBadBootstrapArguments.invokeBoxingArguments:()V +001e24: 7100 0b00 0000 |0000: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +001e2a: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=344 + 0x0003 line=345 + locals : + + #14 : (in LTestBadBootstrapArguments;) + name : 'invokeExtraArguments' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 0 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +001e2c: |[001e2c] TestBadBootstrapArguments.invokeExtraArguments:()V +001e3c: 7100 0b00 0000 |0000: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +001e42: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=154 + 0x0003 line=155 + locals : + + #15 : (in LTestBadBootstrapArguments;) + name : 'invokeHappy' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 0 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +001e44: |[001e44] TestBadBootstrapArguments.invokeHappy:()V +001e54: 7100 0b00 0000 |0000: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +001e5a: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=70 + 0x0003 line=71 + locals : + + #16 : (in LTestBadBootstrapArguments;) + name : 'invokeIntegerReturnType' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 0 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +001e5c: |[001e5c] TestBadBootstrapArguments.invokeIntegerReturnType:()V +001e6c: 7100 0b00 0000 |0000: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +001e72: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=440 + 0x0003 line=441 + locals : + + #17 : (in LTestBadBootstrapArguments;) + name : 'invokeMissingParameterTypes' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 0 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +001e74: |[001e74] TestBadBootstrapArguments.invokeMissingParameterTypes:()V +001e84: 7100 0b00 0000 |0000: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +001e8a: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=124 + 0x0003 line=125 + locals : + + #18 : (in LTestBadBootstrapArguments;) + name : 'invokeNarrowArguments' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 0 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +001e8c: |[001e8c] TestBadBootstrapArguments.invokeNarrowArguments:()V +001e9c: 7100 0b00 0000 |0000: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +001ea2: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=256 + 0x0003 line=257 + locals : + + #19 : (in LTestBadBootstrapArguments;) + name : 'invokeObjectReturnType' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 0 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +001ea4: |[001ea4] TestBadBootstrapArguments.invokeObjectReturnType:()V +001eb4: 7100 0b00 0000 |0000: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +001eba: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=417 + 0x0003 line=418 + locals : + + #20 : (in LTestBadBootstrapArguments;) + name : 'invokeViaCustomCallSiteClass' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 0 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +001ebc: |[001ebc] TestBadBootstrapArguments.invokeViaCustomCallSiteClass:()V +001ecc: 7100 0b00 0000 |0000: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +001ed2: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=469 + 0x0003 line=470 + locals : + + #21 : (in LTestBadBootstrapArguments;) + name : 'invokeVoidReturnType' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 0 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +001ed4: |[001ed4] TestBadBootstrapArguments.invokeVoidReturnType:()V +001ee4: 7100 0b00 0000 |0000: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +001eea: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=394 + 0x0003 line=395 + locals : + + #22 : (in LTestBadBootstrapArguments;) + name : 'invokeWideningArguments' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 0 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +001eec: |[001eec] TestBadBootstrapArguments.invokeWideningArguments:()V +001efc: 7100 0b00 0000 |0000: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +001f02: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=300 + 0x0003 line=301 + locals : + + #23 : (in LTestBadBootstrapArguments;) + name : 'invokeWideningBoxingArguments' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 0 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +001f04: |[001f04] TestBadBootstrapArguments.invokeWideningBoxingArguments:()V +001f14: 7100 0b00 0000 |0000: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +001f1a: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=372 + 0x0003 line=373 + locals : + + #24 : (in LTestBadBootstrapArguments;) + name : 'invokeWrongArguments' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 0 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +001f1c: |[001f1c] TestBadBootstrapArguments.invokeWrongArguments:()V +001f2c: 7100 0b00 0000 |0000: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +001f32: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=182 + 0x0003 line=183 + locals : + + #25 : (in LTestBadBootstrapArguments;) + name : 'invokeWrongArgumentsAgain' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 0 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +001f34: |[001f34] TestBadBootstrapArguments.invokeWrongArgumentsAgain:()V +001f44: 7100 0b00 0000 |0000: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +001f4a: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=210 + 0x0003 line=211 + locals : + + #26 : (in LTestBadBootstrapArguments;) + name : 'invokeWrongParameterTypes' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 0 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +001f4c: |[001f4c] TestBadBootstrapArguments.invokeWrongParameterTypes:()V +001f5c: 7100 0b00 0000 |0000: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +001f62: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=98 + 0x0003 line=99 + locals : + + #27 : (in LTestBadBootstrapArguments;) + name : 'missingParameterTypes' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 2 + ins : 0 + outs : 2 + insns size : 8 16-bit code units +001f64: |[001f64] TestBadBootstrapArguments.missingParameterTypes:()V +001f74: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001f78: 1a01 8c01 |0002: const-string v1, "missingParameterTypes" // string@018c +001f7c: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +001f82: 0e00 |0007: return-void + catches : (none) + positions : + 0x0000 line=128 + 0x0007 line=129 + locals : + + #28 : (in LTestBadBootstrapArguments;) + name : 'narrowArguments' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 0 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +001f84: |[001f84] TestBadBootstrapArguments.narrowArguments:()V +001f94: 7100 0b00 0000 |0000: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +001f9a: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=260 + 0x0003 line=261 + locals : + + #29 : (in LTestBadBootstrapArguments;) + name : 'objectReturnType' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 0 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +001f9c: |[001f9c] TestBadBootstrapArguments.objectReturnType:()V +001fac: 7100 0b00 0000 |0000: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +001fb2: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=421 + 0x0003 line=422 + locals : + + #30 : (in LTestBadBootstrapArguments;) + name : 'sayHello' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 2 + ins : 0 + outs : 2 + insns size : 8 16-bit code units +001fb4: |[001fb4] TestBadBootstrapArguments.sayHello:()V +001fc4: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001fc8: 1a01 2d00 |0002: const-string v1, "Hello!" // string@002d +001fcc: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +001fd2: 0e00 |0007: return-void + catches : (none) + positions : + 0x0000 line=473 + 0x0007 line=474 + locals : + + #31 : (in LTestBadBootstrapArguments;) + name : 'test' + type : '()V' + access : 0x0008 (STATIC) + code - + registers : 3 + ins : 0 + outs : 2 + insns size : 529 16-bit code units +001fd4: |[001fd4] TestBadBootstrapArguments.test:()V +001fe4: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +001fe8: 1a01 8f00 |0002: const-string v1, "TestBadBootstrapArguments" // string@008f +001fec: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +001ff2: fc00 0000 0000 |0007: invoke-custom {}, call_site@0000 +001ff8: fc00 0100 0000 |000a: invoke-custom {}, call_site@0001 +001ffe: 7100 0b00 0000 |000d: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +002004: 2812 |0010: goto 0022 // +0012 +002006: 0d00 |0011: move-exception v0 +002008: 6201 1300 |0012: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +00200c: 1a02 6101 |0014: const-string v2, "invokeWrongParameterTypes => " // string@0161 +002010: 6e20 b000 2100 |0016: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +002016: 6201 1300 |0019: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +00201a: 6e10 c000 0000 |001b: invoke-virtual {v0}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +002020: 0c02 |001e: move-result-object v2 +002022: 6e20 b200 2100 |001f: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@00b2 +002028: fc00 0200 0000 |0022: invoke-custom {}, call_site@0002 +00202e: 7100 0b00 0000 |0025: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +002034: 2812 |0028: goto 003a // +0012 +002036: 0d00 |0029: move-exception v0 +002038: 6201 1300 |002a: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +00203c: 1a02 5101 |002c: const-string v2, "invokeMissingParameterTypes => " // string@0151 +002040: 6e20 b000 2100 |002e: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +002046: 6201 1300 |0031: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +00204a: 6e10 c000 0000 |0033: invoke-virtual {v0}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +002050: 0c02 |0036: move-result-object v2 +002052: 6e20 b200 2100 |0037: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@00b2 +002058: fc00 0300 0000 |003a: invoke-custom {}, call_site@0003 +00205e: 7100 0b00 0000 |003d: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +002064: 2833 |0040: goto 0073 // +0033 +002066: 0d00 |0041: move-exception v0 +002068: 1c01 3a00 |0042: const-class v1, Ljava/lang/invoke/WrongMethodTypeException; // type@003a +00206c: 6e10 b600 0000 |0044: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +002072: 0c02 |0047: move-result-object v2 +002074: 6e10 c000 0200 |0048: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +00207a: 0c02 |004b: move-result-object v2 +00207c: 7120 0a00 2100 |004c: invoke-static {v1, v2}, LTestBadBootstrapArguments;.assertEquals:(Ljava/lang/Object;Ljava/lang/Object;)V // method@000a +002082: 6201 1300 |004f: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002086: 1a02 4c01 |0051: const-string v2, "invokeExtraArguments => " // string@014c +00208a: 6e20 b000 2100 |0053: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +002090: 6201 1300 |0056: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002094: 6e10 c000 0000 |0058: invoke-virtual {v0}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +00209a: 0c02 |005b: move-result-object v2 +00209c: 6e20 af00 2100 |005c: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/Object;)V // method@00af +0020a2: 6201 1300 |005f: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0020a6: 1a02 0200 |0061: const-string v2, " => " // string@0002 +0020aa: 6e20 b000 2100 |0063: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +0020b0: 6201 1300 |0066: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0020b4: 6e10 b600 0000 |0068: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +0020ba: 0c02 |006b: move-result-object v2 +0020bc: 6e10 c000 0200 |006c: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +0020c2: 0c02 |006f: move-result-object v2 +0020c4: 6e20 b200 2100 |0070: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@00b2 +0020ca: fc00 0400 0000 |0073: invoke-custom {}, call_site@0004 +0020d0: 7100 0b00 0000 |0076: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +0020d6: 2833 |0079: goto 00ac // +0033 +0020d8: 0d00 |007a: move-exception v0 +0020da: 1c01 2200 |007b: const-class v1, Ljava/lang/ClassCastException; // type@0022 +0020de: 6e10 b600 0000 |007d: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +0020e4: 0c02 |0080: move-result-object v2 +0020e6: 6e10 c000 0200 |0081: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +0020ec: 0c02 |0084: move-result-object v2 +0020ee: 7120 0a00 2100 |0085: invoke-static {v1, v2}, LTestBadBootstrapArguments;.assertEquals:(Ljava/lang/Object;Ljava/lang/Object;)V // method@000a +0020f4: 6201 1300 |0088: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0020f8: 1a02 5d01 |008a: const-string v2, "invokeWrongArguments => " // string@015d +0020fc: 6e20 b000 2100 |008c: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +002102: 6201 1300 |008f: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002106: 6e10 c000 0000 |0091: invoke-virtual {v0}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +00210c: 0c02 |0094: move-result-object v2 +00210e: 6e20 af00 2100 |0095: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/Object;)V // method@00af +002114: 6201 1300 |0098: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002118: 1a02 0200 |009a: const-string v2, " => " // string@0002 +00211c: 6e20 b000 2100 |009c: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +002122: 6201 1300 |009f: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002126: 6e10 b600 0000 |00a1: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +00212c: 0c02 |00a4: move-result-object v2 +00212e: 6e10 c000 0200 |00a5: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +002134: 0c02 |00a8: move-result-object v2 +002136: 6e20 b200 2100 |00a9: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@00b2 +00213c: fc00 0500 0000 |00ac: invoke-custom {}, call_site@0005 +002142: 7100 0b00 0000 |00af: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +002148: 2833 |00b2: goto 00e5 // +0033 +00214a: 0d00 |00b3: move-exception v0 +00214c: 1c01 2200 |00b4: const-class v1, Ljava/lang/ClassCastException; // type@0022 +002150: 6e10 b600 0000 |00b6: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +002156: 0c02 |00b9: move-result-object v2 +002158: 6e10 c000 0200 |00ba: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +00215e: 0c02 |00bd: move-result-object v2 +002160: 7120 0a00 2100 |00be: invoke-static {v1, v2}, LTestBadBootstrapArguments;.assertEquals:(Ljava/lang/Object;Ljava/lang/Object;)V // method@000a +002166: 6201 1300 |00c1: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +00216a: 1a02 5d01 |00c3: const-string v2, "invokeWrongArguments => " // string@015d +00216e: 6e20 b000 2100 |00c5: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +002174: 6201 1300 |00c8: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002178: 6e10 c000 0000 |00ca: invoke-virtual {v0}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +00217e: 0c02 |00cd: move-result-object v2 +002180: 6e20 af00 2100 |00ce: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/Object;)V // method@00af +002186: 6201 1300 |00d1: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +00218a: 1a02 0200 |00d3: const-string v2, " => " // string@0002 +00218e: 6e20 b000 2100 |00d5: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +002194: 6201 1300 |00d8: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002198: 6e10 b600 0000 |00da: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +00219e: 0c02 |00dd: move-result-object v2 +0021a0: 6e10 c000 0200 |00de: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +0021a6: 0c02 |00e1: move-result-object v2 +0021a8: 6e20 b200 2100 |00e2: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@00b2 +0021ae: fc00 0600 0000 |00e5: invoke-custom {}, call_site@0006 +0021b4: 7100 0b00 0000 |00e8: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +0021ba: 2833 |00eb: goto 011e // +0033 +0021bc: 0d00 |00ec: move-exception v0 +0021be: 1c01 2200 |00ed: const-class v1, Ljava/lang/ClassCastException; // type@0022 +0021c2: 6e10 b600 0000 |00ef: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +0021c8: 0c02 |00f2: move-result-object v2 +0021ca: 6e10 c000 0200 |00f3: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +0021d0: 0c02 |00f6: move-result-object v2 +0021d2: 7120 0a00 2100 |00f7: invoke-static {v1, v2}, LTestBadBootstrapArguments;.assertEquals:(Ljava/lang/Object;Ljava/lang/Object;)V // method@000a +0021d8: 6201 1300 |00fa: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0021dc: 1a02 5f01 |00fc: const-string v2, "invokeWrongArgumentsAgain => " // string@015f +0021e0: 6e20 b000 2100 |00fe: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +0021e6: 6201 1300 |0101: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0021ea: 6e10 c000 0000 |0103: invoke-virtual {v0}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +0021f0: 0c02 |0106: move-result-object v2 +0021f2: 6e20 af00 2100 |0107: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/Object;)V // method@00af +0021f8: 6201 1300 |010a: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0021fc: 1a02 0200 |010c: const-string v2, " => " // string@0002 +002200: 6e20 b000 2100 |010e: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +002206: 6201 1300 |0111: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +00220a: 6e10 b600 0000 |0113: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +002210: 0c02 |0116: move-result-object v2 +002212: 6e10 c000 0200 |0117: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +002218: 0c02 |011a: move-result-object v2 +00221a: 6e20 b200 2100 |011b: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@00b2 +002220: fc00 0700 0000 |011e: invoke-custom {}, call_site@0007 +002226: 7100 0b00 0000 |0121: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +00222c: 2833 |0124: goto 0157 // +0033 +00222e: 0d00 |0125: move-exception v0 +002230: 1c01 2200 |0126: const-class v1, Ljava/lang/ClassCastException; // type@0022 +002234: 6e10 b600 0000 |0128: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +00223a: 0c02 |012b: move-result-object v2 +00223c: 6e10 c000 0200 |012c: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +002242: 0c02 |012f: move-result-object v2 +002244: 7120 0a00 2100 |0130: invoke-static {v1, v2}, LTestBadBootstrapArguments;.assertEquals:(Ljava/lang/Object;Ljava/lang/Object;)V // method@000a +00224a: 6201 1300 |0133: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +00224e: 1a02 5301 |0135: const-string v2, "invokeNarrowArguments => " // string@0153 +002252: 6e20 b000 2100 |0137: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +002258: 6201 1300 |013a: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +00225c: 6e10 c000 0000 |013c: invoke-virtual {v0}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +002262: 0c02 |013f: move-result-object v2 +002264: 6e20 af00 2100 |0140: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/Object;)V // method@00af +00226a: 6201 1300 |0143: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +00226e: 1a02 0200 |0145: const-string v2, " => " // string@0002 +002272: 6e20 b000 2100 |0147: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +002278: 6201 1300 |014a: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +00227c: 6e10 b600 0000 |014c: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +002282: 0c02 |014f: move-result-object v2 +002284: 6e10 c000 0200 |0150: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +00228a: 0c02 |0153: move-result-object v2 +00228c: 6e20 b200 2100 |0154: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@00b2 +002292: fc00 0800 0000 |0157: invoke-custom {}, call_site@0008 +002298: fc00 0900 0000 |015a: invoke-custom {}, call_site@0009 +00229e: fc00 0a00 0000 |015d: invoke-custom {}, call_site@000a +0022a4: 7100 0b00 0000 |0160: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +0022aa: 2826 |0163: goto 0189 // +0026 +0022ac: 0d00 |0164: move-exception v0 +0022ae: 6201 1300 |0165: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0022b2: 1a02 5b01 |0167: const-string v2, "invokeWideningBoxingArguments => " // string@015b +0022b6: 6e20 b000 2100 |0169: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +0022bc: 6201 1300 |016c: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0022c0: 6e10 c000 0000 |016e: invoke-virtual {v0}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +0022c6: 0c02 |0171: move-result-object v2 +0022c8: 6e20 af00 2100 |0172: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/Object;)V // method@00af +0022ce: 6201 1300 |0175: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0022d2: 1a02 0200 |0177: const-string v2, " => " // string@0002 +0022d6: 6e20 b000 2100 |0179: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +0022dc: 6201 1300 |017c: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0022e0: 6e10 b600 0000 |017e: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +0022e6: 0c02 |0181: move-result-object v2 +0022e8: 6e10 c000 0200 |0182: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +0022ee: 0c02 |0185: move-result-object v2 +0022f0: 6e20 b200 2100 |0186: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@00b2 +0022f6: fc00 0b00 0000 |0189: invoke-custom {}, call_site@000b +0022fc: 7100 0b00 0000 |018c: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +002302: 2826 |018f: goto 01b5 // +0026 +002304: 0d00 |0190: move-exception v0 +002306: 6201 1300 |0191: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +00230a: 1a02 5801 |0193: const-string v2, "invokeVoidReturnType() => " // string@0158 +00230e: 6e20 b000 2100 |0195: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +002314: 6201 1300 |0198: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002318: 6e10 c000 0000 |019a: invoke-virtual {v0}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +00231e: 0c02 |019d: move-result-object v2 +002320: 6e20 af00 2100 |019e: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/Object;)V // method@00af +002326: 6201 1300 |01a1: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +00232a: 1a02 0200 |01a3: const-string v2, " => " // string@0002 +00232e: 6e20 b000 2100 |01a5: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +002334: 6201 1300 |01a8: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002338: 6e10 b600 0000 |01aa: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +00233e: 0c02 |01ad: move-result-object v2 +002340: 6e10 c000 0200 |01ae: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +002346: 0c02 |01b1: move-result-object v2 +002348: 6e20 b200 2100 |01b2: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@00b2 +00234e: fc00 0c00 0000 |01b5: invoke-custom {}, call_site@000c +002354: 7100 0b00 0000 |01b8: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +00235a: 2826 |01bb: goto 01e1 // +0026 +00235c: 0d00 |01bc: move-exception v0 +00235e: 6201 1300 |01bd: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002362: 1a02 5501 |01bf: const-string v2, "invokeObjectReturnType() => " // string@0155 +002366: 6e20 b000 2100 |01c1: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +00236c: 6201 1300 |01c4: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002370: 6e10 c000 0000 |01c6: invoke-virtual {v0}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +002376: 0c02 |01c9: move-result-object v2 +002378: 6e20 af00 2100 |01ca: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/Object;)V // method@00af +00237e: 6201 1300 |01cd: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002382: 1a02 0200 |01cf: const-string v2, " => " // string@0002 +002386: 6e20 b000 2100 |01d1: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +00238c: 6201 1300 |01d4: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002390: 6e10 b600 0000 |01d6: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +002396: 0c02 |01d9: move-result-object v2 +002398: 6e10 c000 0200 |01da: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +00239e: 0c02 |01dd: move-result-object v2 +0023a0: 6e20 b200 2100 |01de: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@00b2 +0023a6: fc00 0d00 0000 |01e1: invoke-custom {}, call_site@000d +0023ac: 7100 0b00 0000 |01e4: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +0023b2: 2826 |01e7: goto 020d // +0026 +0023b4: 0d00 |01e8: move-exception v0 +0023b6: 6201 1300 |01e9: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0023ba: 1a02 4f01 |01eb: const-string v2, "invokeIntegerReturnType() => " // string@014f +0023be: 6e20 b000 2100 |01ed: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +0023c4: 6201 1300 |01f0: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0023c8: 6e10 c000 0000 |01f2: invoke-virtual {v0}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +0023ce: 0c02 |01f5: move-result-object v2 +0023d0: 6e20 af00 2100 |01f6: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/Object;)V // method@00af +0023d6: 6201 1300 |01f9: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0023da: 1a02 0200 |01fb: const-string v2, " => " // string@0002 +0023de: 6e20 b000 2100 |01fd: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +0023e4: 6201 1300 |0200: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0023e8: 6e10 b600 0000 |0202: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +0023ee: 0c02 |0205: move-result-object v2 +0023f0: 6e10 c000 0200 |0206: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +0023f6: 0c02 |0209: move-result-object v2 +0023f8: 6e20 b200 2100 |020a: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@00b2 +0023fe: fc00 0e00 0000 |020d: invoke-custom {}, call_site@000e +002404: 0e00 |0210: return-void + catches : 11 + 0x000a - 0x0010 + Ljava/lang/NoSuchMethodError; -> 0x0011 + 0x0022 - 0x0028 + Ljava/lang/NoSuchMethodError; -> 0x0029 + 0x003a - 0x0040 + Ljava/lang/BootstrapMethodError; -> 0x0041 + 0x0073 - 0x0079 + Ljava/lang/BootstrapMethodError; -> 0x007a + 0x00ac - 0x00b2 + Ljava/lang/BootstrapMethodError; -> 0x00b3 + 0x00e5 - 0x00eb + Ljava/lang/BootstrapMethodError; -> 0x00ec + 0x011e - 0x0124 + Ljava/lang/BootstrapMethodError; -> 0x0125 + 0x015d - 0x0163 + Ljava/lang/BootstrapMethodError; -> 0x0164 + 0x0189 - 0x018f + Ljava/lang/BootstrapMethodError; -> 0x0190 + 0x01b5 - 0x01bb + Ljava/lang/BootstrapMethodError; -> 0x01bc + 0x01e1 - 0x01e7 + Ljava/lang/BootstrapMethodError; -> 0x01e8 + positions : + 0x0000 line=477 + 0x0007 line=478 + 0x000a line=480 + 0x000d line=481 + 0x0010 line=485 + 0x0011 line=482 + 0x0012 line=483 + 0x0019 line=484 + 0x0022 line=487 + 0x0025 line=488 + 0x0028 line=492 + 0x0029 line=489 + 0x002a line=490 + 0x0031 line=491 + 0x003a line=494 + 0x003d line=495 + 0x0040 line=502 + 0x0041 line=496 + 0x0042 line=497 + 0x004f line=498 + 0x0056 line=499 + 0x005f line=500 + 0x0066 line=501 + 0x0073 line=504 + 0x0076 line=505 + 0x0079 line=512 + 0x007a line=506 + 0x007b line=507 + 0x0088 line=508 + 0x008f line=509 + 0x0098 line=510 + 0x009f line=511 + 0x00ac line=514 + 0x00af line=515 + 0x00b2 line=522 + 0x00b3 line=516 + 0x00b4 line=517 + 0x00c1 line=518 + 0x00c8 line=519 + 0x00d1 line=520 + 0x00d8 line=521 + 0x00e5 line=524 + 0x00e8 line=525 + 0x00eb line=532 + 0x00ec line=526 + 0x00ed line=527 + 0x00fa line=528 + 0x0101 line=529 + 0x010a line=530 + 0x0111 line=531 + 0x011e line=534 + 0x0121 line=535 + 0x0124 line=542 + 0x0125 line=536 + 0x0126 line=537 + 0x0133 line=538 + 0x013a line=539 + 0x0143 line=540 + 0x014a line=541 + 0x0157 line=543 + 0x015a line=544 + 0x015d line=546 + 0x0160 line=547 + 0x0163 line=553 + 0x0164 line=548 + 0x0165 line=549 + 0x016c line=550 + 0x0175 line=551 + 0x017c line=552 + 0x0189 line=555 + 0x018c line=556 + 0x018f line=562 + 0x0190 line=557 + 0x0191 line=558 + 0x0198 line=559 + 0x01a1 line=560 + 0x01a8 line=561 + 0x01b5 line=564 + 0x01b8 line=565 + 0x01bb line=571 + 0x01bc line=566 + 0x01bd line=567 + 0x01c4 line=568 + 0x01cd line=569 + 0x01d4 line=570 + 0x01e1 line=573 + 0x01e4 line=574 + 0x01e7 line=580 + 0x01e8 line=575 + 0x01e9 line=576 + 0x01f0 line=577 + 0x01f9 line=578 + 0x0200 line=579 + 0x020d line=581 + 0x0210 line=582 + locals : + 0x0012 - 0x0022 reg=0 expected Ljava/lang/NoSuchMethodError; + 0x002a - 0x003a reg=0 expected Ljava/lang/NoSuchMethodError; + 0x0042 - 0x0073 reg=0 expected Ljava/lang/BootstrapMethodError; + 0x007b - 0x00ac reg=0 expected Ljava/lang/BootstrapMethodError; + 0x00b4 - 0x00e5 reg=0 expected Ljava/lang/BootstrapMethodError; + 0x00ed - 0x011e reg=0 expected Ljava/lang/BootstrapMethodError; + 0x0126 - 0x0157 reg=0 expected Ljava/lang/BootstrapMethodError; + 0x0165 - 0x0189 reg=0 expected Ljava/lang/BootstrapMethodError; + 0x0191 - 0x01b5 reg=0 expected Ljava/lang/BootstrapMethodError; + 0x01bd - 0x01e1 reg=0 expected Ljava/lang/BootstrapMethodError; + 0x01e9 - 0x020d reg=0 expected Ljava/lang/BootstrapMethodError; + + #32 : (in LTestBadBootstrapArguments;) + name : 'voidReturnType' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 0 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +00248c: |[00248c] TestBadBootstrapArguments.voidReturnType:()V +00249c: 7100 0b00 0000 |0000: invoke-static {}, LTestBadBootstrapArguments;.assertNotReached:()V // method@000b +0024a2: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=398 + 0x0003 line=399 + locals : + + #33 : (in LTestBadBootstrapArguments;) + name : 'wideningArguments' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 2 + ins : 0 + outs : 2 + insns size : 8 16-bit code units +0024a4: |[0024a4] TestBadBootstrapArguments.wideningArguments:()V +0024b4: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0024b8: 1a01 d101 |0002: const-string v1, "wideningArguments" // string@01d1 +0024bc: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +0024c2: 0e00 |0007: return-void + catches : (none) + positions : + 0x0000 line=304 + 0x0007 line=305 + locals : + + #34 : (in LTestBadBootstrapArguments;) + name : 'wideningBoxingArguments' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 2 + ins : 0 + outs : 2 + insns size : 8 16-bit code units +0024c4: |[0024c4] TestBadBootstrapArguments.wideningBoxingArguments:()V +0024d4: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0024d8: 1a01 d201 |0002: const-string v1, "wideningBoxingArguments" // string@01d2 +0024dc: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +0024e2: 0e00 |0007: return-void + catches : (none) + positions : + 0x0000 line=376 + 0x0007 line=377 + locals : + + #35 : (in LTestBadBootstrapArguments;) + name : 'wrongArguments' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 2 + ins : 0 + outs : 2 + insns size : 8 16-bit code units +0024e4: |[0024e4] TestBadBootstrapArguments.wrongArguments:()V +0024f4: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0024f8: 1a01 d401 |0002: const-string v1, "wrongArguments" // string@01d4 +0024fc: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +002502: 0e00 |0007: return-void + catches : (none) + positions : + 0x0000 line=186 + 0x0007 line=187 + locals : + + #36 : (in LTestBadBootstrapArguments;) + name : 'wrongArgumentsAgain' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 2 + ins : 0 + outs : 2 + insns size : 8 16-bit code units +002504: |[002504] TestBadBootstrapArguments.wrongArgumentsAgain:()V +002514: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002518: 1a01 d501 |0002: const-string v1, "wrongArgumentsAgain" // string@01d5 +00251c: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +002522: 0e00 |0007: return-void + catches : (none) + positions : + 0x0000 line=214 + 0x0007 line=215 + locals : + + #37 : (in LTestBadBootstrapArguments;) + name : 'wrongParameterTypes' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 2 + ins : 0 + outs : 2 + insns size : 8 16-bit code units +002524: |[002524] TestBadBootstrapArguments.wrongParameterTypes:()V +002534: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002538: 1a01 d601 |0002: const-string v1, "wrongParameterTypes" // string@01d6 +00253c: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +002542: 0e00 |0007: return-void + catches : (none) + positions : + 0x0000 line=102 + 0x0007 line=103 + locals : + + Virtual methods - + source_file_idx : 144 (TestBadBootstrapArguments.java) + +Class #7 header: +class_idx : 10 +access_flags : 0 (0x0000) +superclass_idx : 9 +interfaces_off : 0 (0x000000) +source_file_idx : 147 +annotations_off : 30988 (0x00790c) +class_data_off : 29220 (0x007224) +static_fields_size : 1 +instance_fields_size: 0 +direct_methods_size : 7 +virtual_methods_size: 0 + +Class #7 annotations: +Annotations on method #67 'bsm' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #71 'testDynamic' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestDynamicBootstrapArguments; name="bsm" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; Ljava/lang/String; J } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; stringValue={ "A" } Lannotations/Constant; longValue={ 100000000 } } fieldOrMethodName="target" parameterTypes={ I Ljava/lang/String; D } returnType=I + +Class #7 - + Class descriptor : 'LTestDynamicBootstrapArguments;' + Access flags : 0x0000 () + Superclass : 'LTestBase;' + Interfaces - + Static fields - + #0 : (in LTestDynamicBootstrapArguments;) + name : 'bsmCalls' + type : 'I' + access : 0x000a (PRIVATE STATIC) + Instance fields - + Direct methods - + #0 : (in LTestDynamicBootstrapArguments;) + name : '' + type : '()V' + access : 0x10008 (STATIC CONSTRUCTOR) + code - + registers : 1 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +0029c8: |[0029c8] TestDynamicBootstrapArguments.:()V +0029d8: 1200 |0000: const/4 v0, #int 0 // #0 +0029da: 6700 0000 |0001: sput v0, LTestDynamicBootstrapArguments;.bsmCalls:I // field@0000 +0029de: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=27 + locals : + + #1 : (in LTestDynamicBootstrapArguments;) + name : '' + type : '()V' + access : 0x10000 (CONSTRUCTOR) + code - + registers : 1 + ins : 1 + outs : 1 + insns size : 4 16-bit code units +0029e0: |[0029e0] TestDynamicBootstrapArguments.:()V +0029f0: 7010 3200 0000 |0000: invoke-direct {v0}, LTestBase;.:()V // method@0032 +0029f6: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=26 + locals : + 0x0000 - 0x0004 reg=0 this LTestDynamicBootstrapArguments; + + #2 : (in LTestDynamicBootstrapArguments;) + name : 'bsm' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;J)Ljava/lang/invoke/CallSite;' + access : 0x0008 (STATIC) + code - + registers : 11 + ins : 6 + outs : 4 + insns size : 43 16-bit code units +002960: |[002960] TestDynamicBootstrapArguments.bsm:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;J)Ljava/lang/invoke/CallSite; +002970: 6000 0000 |0000: sget v0, LTestDynamicBootstrapArguments;.bsmCalls:I // field@0000 +002974: d800 0001 |0002: add-int/lit8 v0, v0, #int 1 // #01 +002978: 6700 0000 |0004: sput v0, LTestDynamicBootstrapArguments;.bsmCalls:I // field@0000 +00297c: 1c00 0a00 |0006: const-class v0, LTestDynamicBootstrapArguments; // type@000a +002980: 2201 2d00 |0008: new-instance v1, Ljava/lang/StringBuilder; // type@002d +002984: 7010 c100 0100 |000a: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@00c1 +00298a: 6e20 c800 6100 |000d: invoke-virtual {v1, v6}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +002990: 6e20 c800 8100 |0010: invoke-virtual {v1, v8}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +002996: 6e30 c600 910a |0013: invoke-virtual {v1, v9, v10}, Ljava/lang/StringBuilder;.append:(J)Ljava/lang/StringBuilder; // method@00c6 +00299c: 6e10 ca00 0100 |0016: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@00ca +0029a2: 0c01 |0019: move-result-object v1 +0029a4: 6e40 d800 0571 |001a: invoke-virtual {v5, v0, v1, v7}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00d8 +0029aa: 0c02 |001d: move-result-object v2 +0029ac: 6203 1300 |001e: sget-object v3, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0029b0: 1a04 ea00 |0020: const-string v4, "bsm" // string@00ea +0029b4: 6e20 b300 4300 |0022: invoke-virtual {v3, v4}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +0029ba: 2203 3400 |0025: new-instance v3, Ljava/lang/invoke/ConstantCallSite; // type@0034 +0029be: 7020 d200 2300 |0027: invoke-direct {v3, v2}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +0029c4: 1103 |002a: return-object v3 + catches : (none) + positions : + 0x0000 line=36 + 0x0006 line=37 + 0x0008 line=38 + 0x001a line=39 + 0x001e line=40 + 0x0025 line=41 + locals : + 0x0008 - 0x002b reg=0 definingClass Ljava/lang/Class; Ljava/lang/Class<*>; + 0x001a - 0x002b reg=1 methodName Ljava/lang/String; + 0x001e - 0x002b reg=2 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x002b reg=5 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x002b reg=6 name Ljava/lang/String; + 0x0000 - 0x002b reg=7 methodType Ljava/lang/invoke/MethodType; + 0x0000 - 0x002b reg=8 otherNameComponent Ljava/lang/String; + 0x0000 - 0x002b reg=9 nameSuffix J + + #3 : (in LTestDynamicBootstrapArguments;) + name : 'targetA100000000' + type : '(ILjava/lang/String;Ljava/lang/Double;)I' + access : 0x000a (PRIVATE STATIC) + code - + registers : 5 + ins : 3 + outs : 2 + insns size : 30 16-bit code units +0028f8: |[0028f8] TestDynamicBootstrapArguments.targetA100000000:(ILjava/lang/String;Ljava/lang/Double;)I +002908: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +00290c: 6e20 ad00 2000 |0002: invoke-virtual {v0, v2}, Ljava/io/PrintStream;.print:(I)V // method@00ad +002912: 6200 1300 |0005: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002916: 1a01 0c00 |0007: const-string v1, ", " // string@000c +00291a: 6e20 b000 1000 |0009: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +002920: 6200 1300 |000c: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002924: 6e20 b000 3000 |000e: invoke-virtual {v0, v3}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +00292a: 6200 1300 |0011: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +00292e: 1a01 0c00 |0013: const-string v1, ", " // string@000c +002932: 6e20 b000 1000 |0015: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +002938: 6200 1300 |0018: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +00293c: 6e20 b200 4000 |001a: invoke-virtual {v0, v4}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@00b2 +002942: 0f02 |001d: return v2 + catches : (none) + positions : + 0x0000 line=71 + 0x0005 line=72 + 0x000c line=73 + 0x0011 line=74 + 0x0018 line=75 + 0x001d line=76 + locals : + 0x0000 - 0x001e reg=2 i I + 0x0000 - 0x001e reg=3 s Ljava/lang/String; + 0x0000 - 0x001e reg=4 d Ljava/lang/Double; + + #4 : (in LTestDynamicBootstrapArguments;) + name : 'test' + type : '()V' + access : 0x0008 (STATIC) + code - + registers : 2 + ins : 0 + outs : 2 + insns size : 25 16-bit code units +0029f8: |[0029f8] TestDynamicBootstrapArguments.test:()V +002a08: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002a0c: 1a01 9200 |0002: const-string v1, "TestDynamicArguments" // string@0092 +002a10: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +002a16: 7100 4600 0000 |0007: invoke-static {}, LTestDynamicBootstrapArguments;.testCallSites:()V // method@0046 +002a1c: 6000 0000 |000a: sget v0, LTestDynamicBootstrapArguments;.bsmCalls:I // field@0000 +002a20: 1231 |000c: const/4 v1, #int 3 // #3 +002a22: 7120 4100 0100 |000d: invoke-static {v1, v0}, LTestDynamicBootstrapArguments;.assertEquals:(II)V // method@0041 +002a28: 7100 4600 0000 |0010: invoke-static {}, LTestDynamicBootstrapArguments;.testCallSites:()V // method@0046 +002a2e: 6000 0000 |0013: sget v0, LTestDynamicBootstrapArguments;.bsmCalls:I // field@0000 +002a32: 7120 4100 0100 |0015: invoke-static {v1, v0}, LTestDynamicBootstrapArguments;.assertEquals:(II)V // method@0041 +002a38: 0e00 |0018: return-void + catches : (none) + positions : + 0x0000 line=86 + 0x0007 line=87 + 0x000a line=88 + 0x0010 line=89 + 0x0013 line=90 + 0x0018 line=91 + locals : + + #5 : (in LTestDynamicBootstrapArguments;) + name : 'testCallSites' + type : '()V' + access : 0x0008 (STATIC) + code - + registers : 3 + ins : 0 + outs : 3 + insns size : 55 16-bit code units +002a3c: |[002a3c] TestDynamicBootstrapArguments.testCallSites:()V +002a4c: 1a00 8b00 |0000: const-string v0, "One" // string@008b +002a50: 1801 182d 4454 fb21 0940 |0002: const-wide v1, #double 3.14159 // #400921fb54442d18 +002a5a: 7120 b900 2100 |0007: invoke-static {v1, v2}, Ljava/lang/Double;.valueOf:(D)Ljava/lang/Double; // method@00b9 +002a60: 0c01 |000a: move-result-object v1 +002a62: 1202 |000b: const/4 v2, #int 0 // #0 +002a64: fc30 0f00 0201 |000c: invoke-custom {v2, v0, v1}, call_site@000f +002a6a: 0a00 |000f: move-result v0 +002a6c: 7120 4100 0200 |0010: invoke-static {v2, v0}, LTestDynamicBootstrapArguments;.assertEquals:(II)V // method@0041 +002a72: 1a00 a200 |0013: const-string v0, "Two" // string@00a2 +002a76: 1801 6957 148b 0abf 0540 |0015: const-wide v1, #double 2.71828 // #4005bf0a8b145769 +002a80: 7120 b900 2100 |001a: invoke-static {v1, v2}, Ljava/lang/Double;.valueOf:(D)Ljava/lang/Double; // method@00b9 +002a86: 0c01 |001d: move-result-object v1 +002a88: 1212 |001e: const/4 v2, #int 1 // #1 +002a8a: fc30 1000 0201 |001f: invoke-custom {v2, v0, v1}, call_site@0010 +002a90: 0a00 |0022: move-result v0 +002a92: 7120 4100 0200 |0023: invoke-static {v2, v0}, LTestDynamicBootstrapArguments;.assertEquals:(II)V // method@0041 +002a98: 1a00 9f00 |0026: const-string v0, "Three" // string@009f +002a9c: 1601 0000 |0028: const-wide/16 v1, #int 0 // #0 +002aa0: 7120 b900 2100 |002a: invoke-static {v1, v2}, Ljava/lang/Double;.valueOf:(D)Ljava/lang/Double; // method@00b9 +002aa6: 0c01 |002d: move-result-object v1 +002aa8: 1222 |002e: const/4 v2, #int 2 // #2 +002aaa: fc30 1100 0201 |002f: invoke-custom {v2, v0, v1}, call_site@0011 +002ab0: 0a00 |0032: move-result v0 +002ab2: 7120 4100 0200 |0033: invoke-static {v2, v0}, LTestDynamicBootstrapArguments;.assertEquals:(II)V // method@0041 +002ab8: 0e00 |0036: return-void + catches : (none) + positions : + 0x0000 line=80 + 0x0013 line=81 + 0x0026 line=82 + 0x0036 line=83 + locals : + + #6 : (in LTestDynamicBootstrapArguments;) + name : 'testDynamic' + type : '(ILjava/lang/String;Ljava/lang/Double;)I' + access : 0x000a (PRIVATE STATIC) + code - + registers : 4 + ins : 3 + outs : 0 + insns size : 5 16-bit code units +002944: |[002944] TestDynamicBootstrapArguments.testDynamic:(ILjava/lang/String;Ljava/lang/Double;)I +002954: 7100 4200 0000 |0000: invoke-static {}, LTestDynamicBootstrapArguments;.assertNotReached:()V // method@0042 +00295a: 1200 |0003: const/4 v0, #int 0 // #0 +00295c: 0f00 |0004: return v0 + catches : (none) + positions : + 0x0000 line=66 + 0x0003 line=67 + locals : + 0x0000 - 0x0005 reg=1 i I + 0x0000 - 0x0005 reg=2 s Ljava/lang/String; + 0x0000 - 0x0005 reg=3 d Ljava/lang/Double; + + Virtual methods - + source_file_idx : 147 (TestDynamicBootstrapArguments.java) + +Class #8 header: +class_idx : 12 +access_flags : 0 (0x0000) +superclass_idx : 9 +interfaces_off : 0 (0x000000) +source_file_idx : 148 +annotations_off : 31020 (0x00792c) +class_data_off : 29258 (0x00724a) +static_fields_size : 1 +instance_fields_size: 1 +direct_methods_size : 18 +virtual_methods_size: 1 + +Class #8 annotations: +Annotations on class + VISIBILITY_SYSTEM Ldalvik/annotation/MemberClasses; value={ LTestInvocationKinds$Widget; } +Annotations on method #78 'getInstanceField' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestInvocationKinds; name="lookupInstanceFieldGetter" } fieldOrMethodName="instance_field" parameterTypes={ LTestInvocationKinds; } returnType=D +Annotations on method #80 'getStaticField' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestInvocationKinds; name="lookupStaticFieldGetter" } fieldOrMethodName="static_field" parameterTypes={ } returnType=I +Annotations on method #81 'lookupConstructor' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #82 'lookupInstanceFieldGetter' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #83 'lookupInstanceFieldSetter' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #84 'lookupStaticFieldGetter' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #85 'lookupStaticFieldSetter' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #86 'lookupVirtual' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #87 'makeWidget' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestInvocationKinds; name="lookupConstructor" } fieldOrMethodName="unused" parameterTypes={ I } returnType=LTestInvocationKinds$Widget; +Annotations on method #88 'maxIntegerValue' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestInvocationKinds; name="lookupVirtual" } fieldOrMethodName="getMaxIntegerValue" parameterTypes={ LTestInvocationKinds; I I } returnType=I +Annotations on method #89 'setInstanceField' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestInvocationKinds; name="lookupInstanceFieldSetter" } fieldOrMethodName="instance_field" parameterTypes={ LTestInvocationKinds; D } returnType=V +Annotations on method #90 'setStaticField' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestInvocationKinds; name="lookupStaticFieldSetter" } fieldOrMethodName="static_field" parameterTypes={ I } returnType=V + +Class #8 - + Class descriptor : 'LTestInvocationKinds;' + Access flags : 0x0000 () + Superclass : 'LTestBase;' + Interfaces - + Static fields - + #0 : (in LTestInvocationKinds;) + name : 'static_field' + type : 'I' + access : 0x000a (PRIVATE STATIC) + Instance fields - + #0 : (in LTestInvocationKinds;) + name : 'instance_field' + type : 'D' + access : 0x0002 (PRIVATE) + Direct methods - + #0 : (in LTestInvocationKinds;) + name : '' + type : '()V' + access : 0x10000 (CONSTRUCTOR) + code - + registers : 1 + ins : 1 + outs : 1 + insns size : 4 16-bit code units +002ca4: |[002ca4] TestInvocationKinds.:()V +002cb4: 7010 3200 0000 |0000: invoke-direct {v0}, LTestBase;.:()V // method@0032 +002cba: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=25 + locals : + 0x0000 - 0x0004 reg=0 this LTestInvocationKinds; + + #1 : (in LTestInvocationKinds;) + name : 'getInstanceField' + type : '(LTestInvocationKinds;)D' + access : 0x000a (PRIVATE STATIC) + code - + registers : 3 + ins : 1 + outs : 0 + insns size : 6 16-bit code units +002af0: |[002af0] TestInvocationKinds.getInstanceField:(LTestInvocationKinds;)D +002b00: 7100 4d00 0000 |0000: invoke-static {}, LTestInvocationKinds;.assertNotReached:()V // method@004d +002b06: 1900 f87f |0003: const-wide/high16 v0, #long 9221120237041090560 // #7ff8 +002b0a: 1000 |0005: return-wide v0 + catches : (none) + positions : + 0x0000 line=117 + 0x0003 line=118 + locals : + 0x0000 - 0x0006 reg=2 instance LTestInvocationKinds; + + #2 : (in LTestInvocationKinds;) + name : 'getStaticField' + type : '()I' + access : 0x000a (PRIVATE STATIC) + code - + registers : 1 + ins : 0 + outs : 0 + insns size : 5 16-bit code units +002b28: |[002b28] TestInvocationKinds.getStaticField:()I +002b38: 7100 4d00 0000 |0000: invoke-static {}, LTestInvocationKinds;.assertNotReached:()V // method@004d +002b3e: 1200 |0003: const/4 v0, #int 0 // #0 +002b40: 0f00 |0004: return v0 + catches : (none) + positions : + 0x0000 line=71 + 0x0003 line=72 + locals : + + #3 : (in LTestInvocationKinds;) + name : 'lookupConstructor' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;' + access : 0x000a (PRIVATE STATIC) + code - + registers : 7 + ins : 3 + outs : 3 + insns size : 20 16-bit code units +002b60: |[002b60] TestInvocationKinds.lookupConstructor:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; +002b70: 6e10 e500 0600 |0000: invoke-virtual {v6}, Ljava/lang/invoke/MethodType;.returnType:()Ljava/lang/Class; // method@00e5 +002b76: 0c00 |0003: move-result-object v0 +002b78: 6201 1400 |0004: sget-object v1, Ljava/lang/Void;.TYPE:Ljava/lang/Class; // field@0014 +002b7c: 6e20 df00 1600 |0006: invoke-virtual {v6, v1}, Ljava/lang/invoke/MethodType;.changeReturnType:(Ljava/lang/Class;)Ljava/lang/invoke/MethodType; // method@00df +002b82: 0c01 |0009: move-result-object v1 +002b84: 6e30 d500 0401 |000a: invoke-virtual {v4, v0, v1}, Ljava/lang/invoke/MethodHandles$Lookup;.findConstructor:(Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00d5 +002b8a: 0c02 |000d: move-result-object v2 +002b8c: 2203 3400 |000e: new-instance v3, Ljava/lang/invoke/ConstantCallSite; // type@0034 +002b90: 7020 d200 2300 |0010: invoke-direct {v3, v2}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +002b96: 1103 |0013: return-object v3 + catches : (none) + positions : + 0x0000 line=183 + 0x0004 line=184 + 0x000a line=185 + 0x000e line=186 + locals : + 0x0004 - 0x0014 reg=0 cls Ljava/lang/Class; Ljava/lang/Class<*>; + 0x000a - 0x0014 reg=1 constructorMethodType Ljava/lang/invoke/MethodType; + 0x000e - 0x0014 reg=2 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x0014 reg=4 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0014 reg=5 name Ljava/lang/String; + 0x0000 - 0x0014 reg=6 methodType Ljava/lang/invoke/MethodType; + + #4 : (in LTestInvocationKinds;) + name : 'lookupInstanceFieldGetter' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;' + access : 0x0008 (STATIC) + code - + registers : 5 + ins : 3 + outs : 4 + insns size : 20 16-bit code units +002b98: |[002b98] TestInvocationKinds.lookupInstanceFieldGetter:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; +002ba8: 0000 |0000: nop // spacer +002baa: 1200 |0001: const/4 v0, #int 0 // #0 +002bac: 6e20 e400 0400 |0002: invoke-virtual {v4, v0}, Ljava/lang/invoke/MethodType;.parameterType:(I)Ljava/lang/Class; // method@00e4 +002bb2: 0c00 |0005: move-result-object v0 +002bb4: 6e10 e500 0400 |0006: invoke-virtual {v4}, Ljava/lang/invoke/MethodType;.returnType:()Ljava/lang/Class; // method@00e5 +002bba: 0c01 |0009: move-result-object v1 +002bbc: 6e40 d600 0213 |000a: invoke-virtual {v2, v0, v3, v1}, Ljava/lang/invoke/MethodHandles$Lookup;.findGetter:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle; // method@00d6 +002bc2: 0c00 |000d: move-result-object v0 +002bc4: 2201 3400 |000e: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0034 +002bc8: 7020 d200 0100 |0010: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +002bce: 1101 |0013: return-object v1 + catches : (none) + positions : + 0x0000 line=101 + 0x0001 line=102 + 0x000e line=103 + locals : + 0x000e - 0x0014 reg=0 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x0014 reg=2 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0014 reg=3 name Ljava/lang/String; + 0x0000 - 0x0014 reg=4 methodType Ljava/lang/invoke/MethodType; + + #5 : (in LTestInvocationKinds;) + name : 'lookupInstanceFieldSetter' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;' + access : 0x0008 (STATIC) + code - + registers : 5 + ins : 3 + outs : 4 + insns size : 21 16-bit code units +002bd0: |[002bd0] TestInvocationKinds.lookupInstanceFieldSetter:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; +002be0: 0000 |0000: nop // spacer +002be2: 1200 |0001: const/4 v0, #int 0 // #0 +002be4: 6e20 e400 0400 |0002: invoke-virtual {v4, v0}, Ljava/lang/invoke/MethodType;.parameterType:(I)Ljava/lang/Class; // method@00e4 +002bea: 0c00 |0005: move-result-object v0 +002bec: 1211 |0006: const/4 v1, #int 1 // #1 +002bee: 6e20 e400 1400 |0007: invoke-virtual {v4, v1}, Ljava/lang/invoke/MethodType;.parameterType:(I)Ljava/lang/Class; // method@00e4 +002bf4: 0c01 |000a: move-result-object v1 +002bf6: 6e40 d700 0213 |000b: invoke-virtual {v2, v0, v3, v1}, Ljava/lang/invoke/MethodHandles$Lookup;.findSetter:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle; // method@00d7 +002bfc: 0c00 |000e: move-result-object v0 +002bfe: 2201 3400 |000f: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0034 +002c02: 7020 d200 0100 |0011: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +002c08: 1101 |0014: return-object v1 + catches : (none) + positions : + 0x0000 line=78 + 0x0001 line=79 + 0x000f line=80 + locals : + 0x000f - 0x0015 reg=0 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x0015 reg=2 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0015 reg=3 name Ljava/lang/String; + 0x0000 - 0x0015 reg=4 methodType Ljava/lang/invoke/MethodType; + + #6 : (in LTestInvocationKinds;) + name : 'lookupStaticFieldGetter' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;' + access : 0x0008 (STATIC) + code - + registers : 5 + ins : 3 + outs : 4 + insns size : 16 16-bit code units +002c0c: |[002c0c] TestInvocationKinds.lookupStaticFieldGetter:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; +002c1c: 1c00 0c00 |0000: const-class v0, LTestInvocationKinds; // type@000c +002c20: 6e10 e500 0400 |0002: invoke-virtual {v4}, Ljava/lang/invoke/MethodType;.returnType:()Ljava/lang/Class; // method@00e5 +002c26: 0c01 |0005: move-result-object v1 +002c28: 6e40 d900 0213 |0006: invoke-virtual {v2, v0, v3, v1}, Ljava/lang/invoke/MethodHandles$Lookup;.findStaticGetter:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle; // method@00d9 +002c2e: 0c00 |0009: move-result-object v0 +002c30: 2201 3400 |000a: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0034 +002c34: 7020 d200 0100 |000c: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +002c3a: 1101 |000f: return-object v1 + catches : (none) + positions : + 0x0000 line=32 + 0x0002 line=33 + 0x000a line=34 + locals : + 0x000a - 0x0010 reg=0 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x0010 reg=2 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0010 reg=3 name Ljava/lang/String; + 0x0000 - 0x0010 reg=4 methodType Ljava/lang/invoke/MethodType; + + #7 : (in LTestInvocationKinds;) + name : 'lookupStaticFieldSetter' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;' + access : 0x0008 (STATIC) + code - + registers : 5 + ins : 3 + outs : 4 + insns size : 17 16-bit code units +002c3c: |[002c3c] TestInvocationKinds.lookupStaticFieldSetter:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; +002c4c: 1c00 0c00 |0000: const-class v0, LTestInvocationKinds; // type@000c +002c50: 1201 |0002: const/4 v1, #int 0 // #0 +002c52: 6e20 e400 1400 |0003: invoke-virtual {v4, v1}, Ljava/lang/invoke/MethodType;.parameterType:(I)Ljava/lang/Class; // method@00e4 +002c58: 0c01 |0006: move-result-object v1 +002c5a: 6e40 da00 0213 |0007: invoke-virtual {v2, v0, v3, v1}, Ljava/lang/invoke/MethodHandles$Lookup;.findStaticSetter:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle; // method@00da +002c60: 0c00 |000a: move-result-object v0 +002c62: 2201 3400 |000b: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0034 +002c66: 7020 d200 0100 |000d: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +002c6c: 1101 |0010: return-object v1 + catches : (none) + positions : + 0x0000 line=54 + 0x0002 line=56 + 0x0007 line=55 + 0x000b line=57 + locals : + 0x000b - 0x0011 reg=0 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x0011 reg=2 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0011 reg=3 name Ljava/lang/String; + 0x0000 - 0x0011 reg=4 methodType Ljava/lang/invoke/MethodType; + + #8 : (in LTestInvocationKinds;) + name : 'lookupVirtual' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;' + access : 0x000a (PRIVATE STATIC) + code - + registers : 6 + ins : 3 + outs : 4 + insns size : 18 16-bit code units +002c70: |[002c70] TestInvocationKinds.lookupVirtual:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; +002c80: 1200 |0000: const/4 v0, #int 0 // #0 +002c82: 1211 |0001: const/4 v1, #int 1 // #1 +002c84: 6e30 e000 0501 |0002: invoke-virtual {v5, v0, v1}, Ljava/lang/invoke/MethodType;.dropParameterTypes:(II)Ljava/lang/invoke/MethodType; // method@00e0 +002c8a: 0c00 |0005: move-result-object v0 +002c8c: 1c01 0c00 |0006: const-class v1, LTestInvocationKinds; // type@000c +002c90: 6e40 db00 1304 |0008: invoke-virtual {v3, v1, v4, v0}, Ljava/lang/invoke/MethodHandles$Lookup;.findVirtual:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00db +002c96: 0c01 |000b: move-result-object v1 +002c98: 2202 3400 |000c: new-instance v2, Ljava/lang/invoke/ConstantCallSite; // type@0034 +002c9c: 7020 d200 1200 |000e: invoke-direct {v2, v1}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +002ca2: 1102 |0011: return-object v2 + catches : (none) + positions : + 0x0000 line=146 + 0x0006 line=147 + 0x000c line=148 + locals : + 0x0006 - 0x0012 reg=0 mt Ljava/lang/invoke/MethodType; + 0x000c - 0x0012 reg=1 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x0012 reg=3 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0012 reg=4 name Ljava/lang/String; + 0x0000 - 0x0012 reg=5 methodType Ljava/lang/invoke/MethodType; + + #9 : (in LTestInvocationKinds;) + name : 'makeWidget' + type : '(I)LTestInvocationKinds$Widget;' + access : 0x000a (PRIVATE STATIC) + code - + registers : 2 + ins : 1 + outs : 0 + insns size : 5 16-bit code units +002ad4: |[002ad4] TestInvocationKinds.makeWidget:(I)LTestInvocationKinds$Widget; +002ae4: 7100 4d00 0000 |0000: invoke-static {}, LTestInvocationKinds;.assertNotReached:()V // method@004d +002aea: 1200 |0003: const/4 v0, #int 0 // #0 +002aec: 1100 |0004: return-object v0 + catches : (none) + positions : + 0x0000 line=200 + 0x0003 line=201 + locals : + 0x0000 - 0x0005 reg=1 v I + + #10 : (in LTestInvocationKinds;) + name : 'maxIntegerValue' + type : '(LTestInvocationKinds;II)I' + access : 0x000a (PRIVATE STATIC) + code - + registers : 4 + ins : 3 + outs : 0 + insns size : 5 16-bit code units +002b44: |[002b44] TestInvocationKinds.maxIntegerValue:(LTestInvocationKinds;II)I +002b54: 7100 4d00 0000 |0000: invoke-static {}, LTestInvocationKinds;.assertNotReached:()V // method@004d +002b5a: 1200 |0003: const/4 v0, #int 0 // #0 +002b5c: 0f00 |0004: return v0 + catches : (none) + positions : + 0x0000 line=159 + 0x0003 line=160 + locals : + 0x0000 - 0x0005 reg=1 receiver LTestInvocationKinds; + 0x0000 - 0x0005 reg=2 x I + 0x0000 - 0x0005 reg=3 y I + + #11 : (in LTestInvocationKinds;) + name : 'setInstanceField' + type : '(LTestInvocationKinds;D)V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 5 + ins : 3 + outs : 0 + insns size : 8 16-bit code units +002cbc: |[002cbc] TestInvocationKinds.setInstanceField:(LTestInvocationKinds;D)V +002ccc: 7100 4d00 0000 |0000: invoke-static {}, LTestInvocationKinds;.assertNotReached:()V // method@004d +002cd2: 1900 f87f |0003: const-wide/high16 v0, #long 9221120237041090560 // #7ff8 +002cd6: 5a20 0200 |0005: iput-wide v0, v2, LTestInvocationKinds;.instance_field:D // field@0002 +002cda: 0e00 |0007: return-void + catches : (none) + positions : + 0x0000 line=94 + 0x0003 line=95 + 0x0007 line=96 + locals : + 0x0000 - 0x0008 reg=2 instance LTestInvocationKinds; + 0x0000 - 0x0008 reg=3 value D + + #12 : (in LTestInvocationKinds;) + name : 'setStaticField' + type : '(I)V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 1 + ins : 1 + outs : 0 + insns size : 4 16-bit code units +002cdc: |[002cdc] TestInvocationKinds.setStaticField:(I)V +002cec: 7100 4d00 0000 |0000: invoke-static {}, LTestInvocationKinds;.assertNotReached:()V // method@004d +002cf2: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=48 + 0x0003 line=49 + locals : + 0x0000 - 0x0004 reg=0 value I + + #13 : (in LTestInvocationKinds;) + name : 'test' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 2 + ins : 0 + outs : 2 + insns size : 24 16-bit code units +002cf4: |[002cf4] TestInvocationKinds.test:()V +002d04: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002d08: 1c01 0c00 |0002: const-class v1, LTestInvocationKinds; // type@000c +002d0c: 6e10 b700 0100 |0004: invoke-virtual {v1}, Ljava/lang/Class;.getName:()Ljava/lang/String; // method@00b7 +002d12: 0c01 |0007: move-result-object v1 +002d14: 6e20 b300 1000 |0008: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +002d1a: 7100 5f00 0000 |000b: invoke-static {}, LTestInvocationKinds;.testStaticFieldAccessors:()V // method@005f +002d20: 7100 5d00 0000 |000e: invoke-static {}, LTestInvocationKinds;.testInstanceFieldAccessors:()V // method@005d +002d26: 7100 5e00 0000 |0011: invoke-static {}, LTestInvocationKinds;.testInvokeVirtual:()V // method@005e +002d2c: 7100 5c00 0000 |0014: invoke-static {}, LTestInvocationKinds;.testConstructor:()V // method@005c +002d32: 0e00 |0017: return-void + catches : (none) + positions : + 0x0000 line=212 + 0x000b line=213 + 0x000e line=214 + 0x0011 line=215 + 0x0014 line=216 + 0x0017 line=217 + locals : + + #14 : (in LTestInvocationKinds;) + name : 'testConstructor' + type : '()V' + access : 0x0008 (STATIC) + code - + registers : 3 + ins : 0 + outs : 2 + insns size : 31 16-bit code units +002d34: |[002d34] TestInvocationKinds.testConstructor:()V +002d44: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002d48: 1a01 b601 |0002: const-string v1, "testConstructor => " // string@01b6 +002d4c: 6e20 b000 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +002d52: 1230 |0007: const/4 v0, #int 3 // #3 +002d54: fc10 1200 0000 |0008: invoke-custom {v0}, call_site@0012 +002d5a: 0c00 |000b: move-result-object v0 +002d5c: 1c01 0b00 |000c: const-class v1, LTestInvocationKinds$Widget; // type@000b +002d60: 6e10 c000 0000 |000e: invoke-virtual {v0}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +002d66: 0c02 |0011: move-result-object v2 +002d68: 7120 4c00 2100 |0012: invoke-static {v1, v2}, LTestInvocationKinds;.assertEquals:(Ljava/lang/Object;Ljava/lang/Object;)V // method@004c +002d6e: 6201 1300 |0015: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002d72: 6e10 c000 0000 |0017: invoke-virtual {v0}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +002d78: 0c02 |001a: move-result-object v2 +002d7a: 6e20 b200 2100 |001b: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@00b2 +002d80: 0e00 |001e: return-void + catches : (none) + positions : + 0x0000 line=205 + 0x0007 line=206 + 0x000c line=207 + 0x0015 line=208 + 0x001e line=209 + locals : + 0x000c - 0x001f reg=0 receiver LTestInvocationKinds$Widget; + + #15 : (in LTestInvocationKinds;) + name : 'testInstanceFieldAccessors' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 5 + ins : 0 + outs : 4 + insns size : 44 16-bit code units +002d84: |[002d84] TestInvocationKinds.testInstanceFieldAccessors:()V +002d94: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002d98: 1a01 b801 |0002: const-string v1, "testInstanceFieldAccessors" // string@01b8 +002d9c: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +002da2: 2200 0c00 |0007: new-instance v0, LTestInvocationKinds; // type@000c +002da6: 7010 4900 0000 |0009: invoke-direct {v0}, LTestInvocationKinds;.:()V // method@0049 +002dac: 1601 0100 |000c: const-wide/16 v1, #int 1 // #1 +002db0: 5a01 0200 |000e: iput-wide v1, v0, LTestInvocationKinds;.instance_field:D // field@0002 +002db4: 1801 182d 4454 fb21 0940 |0010: const-wide v1, #double 3.14159 // #400921fb54442d18 +002dbe: fc30 1300 1002 |0015: invoke-custom {v0, v1, v2}, call_site@0013 +002dc4: 5303 0200 |0018: iget-wide v3, v0, LTestInvocationKinds;.instance_field:D // field@0002 +002dc8: 7140 4a00 2143 |001a: invoke-static {v1, v2, v3, v4}, LTestInvocationKinds;.assertEquals:(DD)V // method@004a +002dce: 1801 6957 148b 0abf 0540 |001d: const-wide v1, #double 2.71828 // #4005bf0a8b145769 +002dd8: 5a01 0200 |0022: iput-wide v1, v0, LTestInvocationKinds;.instance_field:D // field@0002 +002ddc: fc10 1400 0000 |0024: invoke-custom {v0}, call_site@0014 +002de2: 0b03 |0027: move-result-wide v3 +002de4: 7140 4a00 2143 |0028: invoke-static {v1, v2, v3, v4}, LTestInvocationKinds;.assertEquals:(DD)V // method@004a +002dea: 0e00 |002b: return-void + catches : (none) + positions : + 0x0000 line=133 + 0x0007 line=134 + 0x000c line=135 + 0x0010 line=136 + 0x0018 line=137 + 0x001d line=138 + 0x0024 line=139 + 0x002b line=140 + locals : + 0x000c - 0x002c reg=0 instance LTestInvocationKinds; + + #16 : (in LTestInvocationKinds;) + name : 'testInvokeVirtual' + type : '()V' + access : 0x0008 (STATIC) + code - + registers : 3 + ins : 0 + outs : 3 + insns size : 25 16-bit code units +002dec: |[002dec] TestInvocationKinds.testInvokeVirtual:()V +002dfc: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002e00: 1a01 ba01 |0002: const-string v1, "testInvokeVirtual => max(77, -3) = " // string@01ba +002e04: 6e20 b000 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +002e0a: 2200 0c00 |0007: new-instance v0, LTestInvocationKinds; // type@000c +002e0e: 7010 4900 0000 |0009: invoke-direct {v0}, LTestInvocationKinds;.:()V // method@0049 +002e14: 1301 4d00 |000c: const/16 v1, #int 77 // #4d +002e18: 12d2 |000e: const/4 v2, #int -3 // #fd +002e1a: fc30 1500 1002 |000f: invoke-custom {v0, v1, v2}, call_site@0015 +002e20: 0a01 |0012: move-result v1 +002e22: 6202 1300 |0013: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002e26: 6e20 b100 1200 |0015: invoke-virtual {v2, v1}, Ljava/io/PrintStream;.println:(I)V // method@00b1 +002e2c: 0e00 |0018: return-void + catches : (none) + positions : + 0x0000 line=168 + 0x0007 line=169 + 0x000c line=170 + 0x0013 line=171 + 0x0018 line=172 + locals : + 0x000c - 0x0019 reg=0 receiver LTestInvocationKinds; + 0x0013 - 0x0019 reg=1 result I + + #17 : (in LTestInvocationKinds;) + name : 'testStaticFieldAccessors' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 2 + ins : 0 + outs : 2 + insns size : 47 16-bit code units +002e30: |[002e30] TestInvocationKinds.testStaticFieldAccessors:()V +002e40: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +002e44: 1a01 bb01 |0002: const-string v1, "testStaticFieldAccessors" // string@01bb +002e48: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +002e4e: 1230 |0007: const/4 v0, #int 3 // #3 +002e50: fc10 1600 0000 |0008: invoke-custom {v0}, call_site@0016 +002e56: 6001 0300 |000b: sget v1, LTestInvocationKinds;.static_field:I // field@0003 +002e5a: 7120 4b00 0100 |000d: invoke-static {v1, v0}, LTestInvocationKinds;.assertEquals:(II)V // method@004b +002e60: 1240 |0010: const/4 v0, #int 4 // #4 +002e62: fc10 1700 0000 |0011: invoke-custom {v0}, call_site@0017 +002e68: 6001 0300 |0014: sget v1, LTestInvocationKinds;.static_field:I // field@0003 +002e6c: 7120 4b00 0100 |0016: invoke-static {v1, v0}, LTestInvocationKinds;.assertEquals:(II)V // method@004b +002e72: 6000 0300 |0019: sget v0, LTestInvocationKinds;.static_field:I // field@0003 +002e76: fc00 1800 0000 |001b: invoke-custom {}, call_site@0018 +002e7c: 0a01 |001e: move-result v1 +002e7e: 7120 4b00 1000 |001f: invoke-static {v0, v1}, LTestInvocationKinds;.assertEquals:(II)V // method@004b +002e84: 1400 ffff ff7f |0022: const v0, #float nan // #7fffffff +002e8a: 6700 0300 |0025: sput v0, LTestInvocationKinds;.static_field:I // field@0003 +002e8e: fc00 1900 0000 |0027: invoke-custom {}, call_site@0019 +002e94: 0a01 |002a: move-result v1 +002e96: 7120 4b00 1000 |002b: invoke-static {v0, v1}, LTestInvocationKinds;.assertEquals:(II)V // method@004b +002e9c: 0e00 |002e: return-void + catches : (none) + positions : + 0x0000 line=122 + 0x0007 line=123 + 0x000b line=124 + 0x0010 line=125 + 0x0014 line=126 + 0x0019 line=127 + 0x0022 line=128 + 0x0027 line=129 + 0x002e line=130 + locals : + + Virtual methods - + #0 : (in LTestInvocationKinds;) + name : 'getMaxIntegerValue' + type : '(II)I' + access : 0x0001 (PUBLIC) + code - + registers : 4 + ins : 3 + outs : 0 + insns size : 6 16-bit code units +002b0c: |[002b0c] TestInvocationKinds.getMaxIntegerValue:(II)I +002b1c: 3732 0400 |0000: if-le v2, v3, 0004 // +0004 +002b20: 0120 |0002: move v0, v2 +002b22: 2802 |0003: goto 0005 // +0002 +002b24: 0130 |0004: move v0, v3 +002b26: 0f00 |0005: return v0 + catches : (none) + positions : + 0x0000 line=164 + locals : + 0x0000 - 0x0006 reg=1 this LTestInvocationKinds; + 0x0000 - 0x0006 reg=2 x I + 0x0000 - 0x0006 reg=3 y I + + source_file_idx : 148 (TestInvocationKinds.java) + +Class #9 header: +class_idx : 14 +access_flags : 1 (0x0001) +superclass_idx : 9 +interfaces_off : 18256 (0x004750) +source_file_idx : 149 +annotations_off : 31132 (0x00799c) +class_data_off : 29344 (0x0072a0) +static_fields_size : 7 +instance_fields_size: 0 +direct_methods_size : 8 +virtual_methods_size: 1 + +Class #9 annotations: +Annotations on field #10 'threadIndex' + VISIBILITY_SYSTEM Ldalvik/annotation/Signature; value={ "Ljava/lang/ThreadLocal<" "Ljava/lang/Integer;" ">;" } +Annotations on method #106 'linkerMethod' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #109 'setCalled' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestInvokeCustomWithConcurrentThreads; name="linkerMethod" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; } } fieldOrMethodName="setCalled" parameterTypes={ I } returnType=I +Annotations on method #110 'test' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } + +Class #9 - + Class descriptor : 'LTestInvokeCustomWithConcurrentThreads;' + Access flags : 0x0001 (PUBLIC) + Superclass : 'LTestBase;' + Interfaces - + #0 : 'Ljava/lang/Runnable;' + Static fields - + #0 : (in LTestInvokeCustomWithConcurrentThreads;) + name : 'NUMBER_OF_THREADS' + type : 'I' + access : 0x001a (PRIVATE STATIC FINAL) + value : 16 + #1 : (in LTestInvokeCustomWithConcurrentThreads;) + name : 'barrier' + type : 'Ljava/util/concurrent/CyclicBarrier;' + access : 0x001a (PRIVATE STATIC FINAL) + #2 : (in LTestInvokeCustomWithConcurrentThreads;) + name : 'called' + type : '[Ljava/util/concurrent/atomic/AtomicInteger;' + access : 0x001a (PRIVATE STATIC FINAL) + #3 : (in LTestInvokeCustomWithConcurrentThreads;) + name : 'instantiated' + type : '[Ljava/lang/invoke/CallSite;' + access : 0x001a (PRIVATE STATIC FINAL) + #4 : (in LTestInvokeCustomWithConcurrentThreads;) + name : 'nextIndex' + type : 'Ljava/util/concurrent/atomic/AtomicInteger;' + access : 0x001a (PRIVATE STATIC FINAL) + #5 : (in LTestInvokeCustomWithConcurrentThreads;) + name : 'targetted' + type : '[Ljava/util/concurrent/atomic/AtomicInteger;' + access : 0x001a (PRIVATE STATIC FINAL) + #6 : (in LTestInvokeCustomWithConcurrentThreads;) + name : 'threadIndex' + type : 'Ljava/lang/ThreadLocal;' + access : 0x001a (PRIVATE STATIC FINAL) + Instance fields - + Direct methods - + #0 : (in LTestInvokeCustomWithConcurrentThreads;) + name : '' + type : '()V' + access : 0x10008 (STATIC CONSTRUCTOR) + code - + registers : 2 + ins : 0 + outs : 2 + insns size : 37 16-bit code units +003064: |[003064] TestInvokeCustomWithConcurrentThreads.:()V +003074: 2200 3e00 |0000: new-instance v0, Ljava/util/concurrent/atomic/AtomicInteger; // type@003e +003078: 1201 |0002: const/4 v1, #int 0 // #0 +00307a: 7020 ef00 1000 |0003: invoke-direct {v0, v1}, Ljava/util/concurrent/atomic/AtomicInteger;.:(I)V // method@00ef +003080: 6900 0800 |0006: sput-object v0, LTestInvokeCustomWithConcurrentThreads;.nextIndex:Ljava/util/concurrent/atomic/AtomicInteger; // field@0008 +003084: 2200 0d00 |0008: new-instance v0, LTestInvokeCustomWithConcurrentThreads$1; // type@000d +003088: 7010 6000 0000 |000a: invoke-direct {v0}, LTestInvokeCustomWithConcurrentThreads$1;.:()V // method@0060 +00308e: 6900 0a00 |000d: sput-object v0, LTestInvokeCustomWithConcurrentThreads;.threadIndex:Ljava/lang/ThreadLocal; // field@000a +003092: 1300 1000 |000f: const/16 v0, #int 16 // #10 +003096: 2301 4b00 |0011: new-array v1, v0, [Ljava/lang/invoke/CallSite; // type@004b +00309a: 6901 0700 |0013: sput-object v1, LTestInvokeCustomWithConcurrentThreads;.instantiated:[Ljava/lang/invoke/CallSite; // field@0007 +00309e: 2301 4c00 |0015: new-array v1, v0, [Ljava/util/concurrent/atomic/AtomicInteger; // type@004c +0030a2: 6901 0600 |0017: sput-object v1, LTestInvokeCustomWithConcurrentThreads;.called:[Ljava/util/concurrent/atomic/AtomicInteger; // field@0006 +0030a6: 2301 4c00 |0019: new-array v1, v0, [Ljava/util/concurrent/atomic/AtomicInteger; // type@004c +0030aa: 6901 0900 |001b: sput-object v1, LTestInvokeCustomWithConcurrentThreads;.targetted:[Ljava/util/concurrent/atomic/AtomicInteger; // field@0009 +0030ae: 2201 3d00 |001d: new-instance v1, Ljava/util/concurrent/CyclicBarrier; // type@003d +0030b2: 7020 ed00 0100 |001f: invoke-direct {v1, v0}, Ljava/util/concurrent/CyclicBarrier;.:(I)V // method@00ed +0030b8: 6901 0500 |0022: sput-object v1, LTestInvokeCustomWithConcurrentThreads;.barrier:Ljava/util/concurrent/CyclicBarrier; // field@0005 +0030bc: 0e00 |0024: return-void + catches : (none) + positions : + 0x0000 line=30 + 0x0008 line=32 + 0x000f line=41 + 0x0015 line=44 + 0x0019 line=47 + 0x001d line=50 + locals : + + #1 : (in LTestInvokeCustomWithConcurrentThreads;) + name : '' + type : '()V' + access : 0x10002 (PRIVATE CONSTRUCTOR) + code - + registers : 1 + ins : 1 + outs : 1 + insns size : 4 16-bit code units +0030c0: |[0030c0] TestInvokeCustomWithConcurrentThreads.:()V +0030d0: 7010 3200 0000 |0000: invoke-direct {v0}, LTestBase;.:()V // method@0032 +0030d6: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=52 + locals : + 0x0000 - 0x0004 reg=0 this LTestInvokeCustomWithConcurrentThreads; + + #2 : (in LTestInvokeCustomWithConcurrentThreads;) + name : 'access$000' + type : '()Ljava/util/concurrent/atomic/AtomicInteger;' + access : 0x1008 (STATIC SYNTHETIC) + code - + registers : 1 + ins : 0 + outs : 0 + insns size : 3 16-bit code units +00304c: |[00304c] TestInvokeCustomWithConcurrentThreads.access$000:()Ljava/util/concurrent/atomic/AtomicInteger; +00305c: 6200 0800 |0000: sget-object v0, LTestInvokeCustomWithConcurrentThreads;.nextIndex:Ljava/util/concurrent/atomic/AtomicInteger; // field@0008 +003060: 1100 |0002: return-object v0 + catches : (none) + positions : + 0x0000 line=27 + locals : + + #3 : (in LTestInvokeCustomWithConcurrentThreads;) + name : 'getThreadIndex' + type : '()I' + access : 0x000a (PRIVATE STATIC) + code - + registers : 1 + ins : 0 + outs : 1 + insns size : 13 16-bit code units +002f00: |[002f00] TestInvokeCustomWithConcurrentThreads.getThreadIndex:()I +002f10: 6200 0a00 |0000: sget-object v0, LTestInvokeCustomWithConcurrentThreads;.threadIndex:Ljava/lang/ThreadLocal; // field@000a +002f14: 6e10 d000 0000 |0002: invoke-virtual {v0}, Ljava/lang/ThreadLocal;.get:()Ljava/lang/Object; // method@00d0 +002f1a: 0c00 |0005: move-result-object v0 +002f1c: 1f00 2700 |0006: check-cast v0, Ljava/lang/Integer; // type@0027 +002f20: 6e10 bc00 0000 |0008: invoke-virtual {v0}, Ljava/lang/Integer;.intValue:()I // method@00bc +002f26: 0a00 |000b: move-result v0 +002f28: 0f00 |000c: return v0 + catches : (none) + positions : + 0x0000 line=55 + locals : + + #4 : (in LTestInvokeCustomWithConcurrentThreads;) + name : 'linkerMethod' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;' + access : 0x000a (PRIVATE STATIC) + code - + registers : 8 + ins : 3 + outs : 4 + insns size : 97 16-bit code units +002f78: |[002f78] TestInvokeCustomWithConcurrentThreads.linkerMethod:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; +002f88: 1c00 0e00 |0000: const-class v0, LTestInvokeCustomWithConcurrentThreads; // type@000e +002f8c: 6e40 d800 0576 |0002: invoke-virtual {v5, v0, v6, v7}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00d8 +002f92: 0c00 |0005: move-result-object v0 +002f94: 6e10 d400 0000 |0006: invoke-virtual {v0}, Ljava/lang/invoke/MethodHandle;.type:()Ljava/lang/invoke/MethodType; // method@00d4 +002f9a: 0c01 |0009: move-result-object v1 +002f9c: 7120 6700 1700 |000a: invoke-static {v7, v1}, LTestInvokeCustomWithConcurrentThreads;.assertEquals:(Ljava/lang/Object;Ljava/lang/Object;)V // method@0067 +002fa2: 6e10 d400 0000 |000d: invoke-virtual {v0}, Ljava/lang/invoke/MethodHandle;.type:()Ljava/lang/invoke/MethodType; // method@00d4 +002fa8: 0c01 |0010: move-result-object v1 +002faa: 6e10 e300 0100 |0011: invoke-virtual {v1}, Ljava/lang/invoke/MethodType;.parameterCount:()I // method@00e3 +002fb0: 0a01 |0014: move-result v1 +002fb2: 1212 |0015: const/4 v2, #int 1 // #1 +002fb4: 7120 6600 2100 |0016: invoke-static {v1, v2}, LTestInvokeCustomWithConcurrentThreads;.assertEquals:(II)V // method@0066 +002fba: 2321 4800 |0019: new-array v1, v2, [Ljava/lang/Object; // type@0048 +002fbe: 7100 6900 0000 |001b: invoke-static {}, LTestInvokeCustomWithConcurrentThreads;.getThreadIndex:()I // method@0069 +002fc4: 0a03 |001e: move-result v3 +002fc6: 7110 bd00 0300 |001f: invoke-static {v3}, Ljava/lang/Integer;.valueOf:(I)Ljava/lang/Integer; // method@00bd +002fcc: 0c03 |0022: move-result-object v3 +002fce: 1204 |0023: const/4 v4, #int 0 // #0 +002fd0: 4d03 0104 |0024: aput-object v3, v1, v4 +002fd4: 7130 de00 4001 |0026: invoke-static {v0, v4, v1}, Ljava/lang/invoke/MethodHandles;.insertArguments:(Ljava/lang/invoke/MethodHandle;I[Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle; // method@00de +002fda: 0c00 |0029: move-result-object v0 +002fdc: 2321 4600 |002a: new-array v1, v2, [Ljava/lang/Class; // type@0046 +002fe0: 6203 1200 |002c: sget-object v3, Ljava/lang/Integer;.TYPE:Ljava/lang/Class; // field@0012 +002fe4: 4d03 0104 |002e: aput-object v3, v1, v4 +002fe8: 7130 dd00 4001 |0030: invoke-static {v0, v4, v1}, Ljava/lang/invoke/MethodHandles;.dropArguments:(Ljava/lang/invoke/MethodHandle;I[Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle; // method@00dd +002fee: 0c00 |0033: move-result-object v0 +002ff0: 6e10 d400 0000 |0034: invoke-virtual {v0}, Ljava/lang/invoke/MethodHandle;.type:()Ljava/lang/invoke/MethodType; // method@00d4 +002ff6: 0c01 |0037: move-result-object v1 +002ff8: 6e10 e300 0100 |0038: invoke-virtual {v1}, Ljava/lang/invoke/MethodType;.parameterCount:()I // method@00e3 +002ffe: 0a01 |003b: move-result v1 +003000: 7120 6600 2100 |003c: invoke-static {v1, v2}, LTestInvokeCustomWithConcurrentThreads;.assertEquals:(II)V // method@0066 +003006: 6e10 d400 0000 |003f: invoke-virtual {v0}, Ljava/lang/invoke/MethodHandle;.type:()Ljava/lang/invoke/MethodType; // method@00d4 +00300c: 0c01 |0042: move-result-object v1 +00300e: 7120 6700 1700 |0043: invoke-static {v7, v1}, LTestInvokeCustomWithConcurrentThreads;.assertEquals:(Ljava/lang/Object;Ljava/lang/Object;)V // method@0067 +003014: 6201 0500 |0046: sget-object v1, LTestInvokeCustomWithConcurrentThreads;.barrier:Ljava/util/concurrent/CyclicBarrier; // field@0005 +003018: 6e10 ee00 0100 |0048: invoke-virtual {v1}, Ljava/util/concurrent/CyclicBarrier;.await:()I // method@00ee +00301e: 6201 0700 |004b: sget-object v1, LTestInvokeCustomWithConcurrentThreads;.instantiated:[Ljava/lang/invoke/CallSite; // field@0007 +003022: 7100 6900 0000 |004d: invoke-static {}, LTestInvokeCustomWithConcurrentThreads;.getThreadIndex:()I // method@0069 +003028: 0a02 |0050: move-result v2 +00302a: 2203 3400 |0051: new-instance v3, Ljava/lang/invoke/ConstantCallSite; // type@0034 +00302e: 7020 d200 0300 |0053: invoke-direct {v3, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +003034: 4d03 0102 |0056: aput-object v3, v1, v2 +003038: 6201 0700 |0058: sget-object v1, LTestInvokeCustomWithConcurrentThreads;.instantiated:[Ljava/lang/invoke/CallSite; // field@0007 +00303c: 7100 6900 0000 |005a: invoke-static {}, LTestInvokeCustomWithConcurrentThreads;.getThreadIndex:()I // method@0069 +003042: 0a02 |005d: move-result v2 +003044: 4601 0102 |005e: aget-object v1, v1, v2 +003048: 1101 |0060: return-object v1 + catches : (none) + positions : + 0x0000 line=87 + 0x0002 line=88 + 0x0006 line=89 + 0x000d line=90 + 0x0019 line=91 + 0x002a line=92 + 0x0034 line=93 + 0x003f line=94 + 0x0046 line=99 + 0x004b line=101 + 0x0058 line=102 + locals : + 0x0006 - 0x0061 reg=0 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x0061 reg=5 caller Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0061 reg=6 name Ljava/lang/String; + 0x0000 - 0x0061 reg=7 methodType Ljava/lang/invoke/MethodType; + + #5 : (in LTestInvokeCustomWithConcurrentThreads;) + name : 'notUsed' + type : '(I)I' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 1 + ins : 1 + outs : 0 + insns size : 1 16-bit code units +002f2c: |[002f2c] TestInvokeCustomWithConcurrentThreads.notUsed:(I)I +002f3c: 0f00 |0000: return v0 + catches : (none) + positions : + 0x0000 line=59 + locals : + 0x0000 - 0x0001 reg=0 x I + + #6 : (in LTestInvokeCustomWithConcurrentThreads;) + name : 'setCalled' + type : '(I)I' + access : 0x000a (PRIVATE STATIC) + code - + registers : 3 + ins : 1 + outs : 2 + insns size : 20 16-bit code units +002f40: |[002f40] TestInvokeCustomWithConcurrentThreads.setCalled:(I)I +002f50: 6200 0600 |0000: sget-object v0, LTestInvokeCustomWithConcurrentThreads;.called:[Ljava/util/concurrent/atomic/AtomicInteger; // field@0006 +002f54: 4600 0002 |0002: aget-object v0, v0, v2 +002f58: 6e10 f100 0000 |0004: invoke-virtual {v0}, Ljava/util/concurrent/atomic/AtomicInteger;.getAndIncrement:()I // method@00f1 +002f5e: 6200 0900 |0007: sget-object v0, LTestInvokeCustomWithConcurrentThreads;.targetted:[Ljava/util/concurrent/atomic/AtomicInteger; // field@0009 +002f62: 7100 6900 0000 |0009: invoke-static {}, LTestInvokeCustomWithConcurrentThreads;.getThreadIndex:()I // method@0069 +002f68: 0a01 |000c: move-result v1 +002f6a: 4600 0001 |000d: aget-object v0, v0, v1 +002f6e: 6e20 f200 2000 |000f: invoke-virtual {v0, v2}, Ljava/util/concurrent/atomic/AtomicInteger;.set:(I)V // method@00f2 +002f74: 1200 |0012: const/4 v0, #int 0 // #0 +002f76: 0f00 |0013: return v0 + catches : (none) + positions : + 0x0000 line=79 + 0x0007 line=80 + 0x0012 line=81 + locals : + 0x0000 - 0x0014 reg=2 index I + + #7 : (in LTestInvokeCustomWithConcurrentThreads;) + name : 'test' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 12 + ins : 0 + outs : 3 + insns size : 229 16-bit code units +0030fc: |[0030fc] TestInvokeCustomWithConcurrentThreads.test:()V +00310c: 1200 |0000: const/4 v0, #int 0 // #0 +00310e: 0101 |0001: move v1, v0 +003110: 1302 1000 |0002: const/16 v2, #int 16 // #10 +003114: 3521 1700 |0004: if-ge v1, v2, 001b // +0017 +003118: 6202 0600 |0006: sget-object v2, LTestInvokeCustomWithConcurrentThreads;.called:[Ljava/util/concurrent/atomic/AtomicInteger; // field@0006 +00311c: 2203 3e00 |0008: new-instance v3, Ljava/util/concurrent/atomic/AtomicInteger; // type@003e +003120: 7020 ef00 0300 |000a: invoke-direct {v3, v0}, Ljava/util/concurrent/atomic/AtomicInteger;.:(I)V // method@00ef +003126: 4d03 0201 |000d: aput-object v3, v2, v1 +00312a: 6202 0900 |000f: sget-object v2, LTestInvokeCustomWithConcurrentThreads;.targetted:[Ljava/util/concurrent/atomic/AtomicInteger; // field@0009 +00312e: 2203 3e00 |0011: new-instance v3, Ljava/util/concurrent/atomic/AtomicInteger; // type@003e +003132: 7020 ef00 0300 |0013: invoke-direct {v3, v0}, Ljava/util/concurrent/atomic/AtomicInteger;.:(I)V // method@00ef +003138: 4d03 0201 |0016: aput-object v3, v2, v1 +00313c: d801 0101 |0018: add-int/lit8 v1, v1, #int 1 // #01 +003140: 28e8 |001a: goto 0002 // -0018 +003142: 2321 4a00 |001b: new-array v1, v2, [Ljava/lang/Thread; // type@004a +003146: 0103 |001d: move v3, v0 +003148: 3523 1600 |001e: if-ge v3, v2, 0034 // +0016 +00314c: 2204 2f00 |0020: new-instance v4, Ljava/lang/Thread; // type@002f +003150: 2205 0e00 |0022: new-instance v5, LTestInvokeCustomWithConcurrentThreads; // type@000e +003154: 7010 6400 0500 |0024: invoke-direct {v5}, LTestInvokeCustomWithConcurrentThreads;.:()V // method@0064 +00315a: 7020 cb00 5400 |0027: invoke-direct {v4, v5}, Ljava/lang/Thread;.:(Ljava/lang/Runnable;)V // method@00cb +003160: 4d04 0103 |002a: aput-object v4, v1, v3 +003164: 4604 0103 |002c: aget-object v4, v1, v3 +003168: 6e10 ce00 0400 |002e: invoke-virtual {v4}, Ljava/lang/Thread;.start:()V // method@00ce +00316e: d803 0301 |0031: add-int/lit8 v3, v3, #int 1 // #01 +003172: 28eb |0033: goto 001e // -0015 +003174: 0103 |0034: move v3, v0 +003176: 3523 0a00 |0035: if-ge v3, v2, 003f // +000a +00317a: 4604 0103 |0037: aget-object v4, v1, v3 +00317e: 6e10 cd00 0400 |0039: invoke-virtual {v4}, Ljava/lang/Thread;.join:()V // method@00cd +003184: d803 0301 |003c: add-int/lit8 v3, v3, #int 1 // #01 +003188: 28f7 |003e: goto 0035 // -0009 +00318a: 1203 |003f: const/4 v3, #int 0 // #0 +00318c: 1204 |0040: const/4 v4, #int 0 // #0 +00318e: 0145 |0041: move v5, v4 +003190: 0134 |0042: move v4, v3 +003192: 0103 |0043: move v3, v0 +003194: 3523 2200 |0044: if-ge v3, v2, 0066 // +0022 +003198: 6206 0700 |0046: sget-object v6, LTestInvokeCustomWithConcurrentThreads;.instantiated:[Ljava/lang/invoke/CallSite; // field@0007 +00319c: 4606 0603 |0048: aget-object v6, v6, v3 +0031a0: 1207 |004a: const/4 v7, #int 0 // #0 +0031a2: 7120 6800 7600 |004b: invoke-static {v6, v7}, LTestInvokeCustomWithConcurrentThreads;.assertNotEquals:(Ljava/lang/Object;Ljava/lang/Object;)V // method@0068 +0031a8: 6206 0600 |004e: sget-object v6, LTestInvokeCustomWithConcurrentThreads;.called:[Ljava/util/concurrent/atomic/AtomicInteger; // field@0006 +0031ac: 4606 0603 |0050: aget-object v6, v6, v3 +0031b0: 6e10 f000 0600 |0052: invoke-virtual {v6}, Ljava/util/concurrent/atomic/AtomicInteger;.get:()I // method@00f0 +0031b6: 0a06 |0055: move-result v6 +0031b8: 3806 0d00 |0056: if-eqz v6, 0063 // +000d +0031bc: d804 0401 |0058: add-int/lit8 v4, v4, #int 1 // #01 +0031c0: 6206 0600 |005a: sget-object v6, LTestInvokeCustomWithConcurrentThreads;.called:[Ljava/util/concurrent/atomic/AtomicInteger; // field@0006 +0031c4: 4606 0603 |005c: aget-object v6, v6, v3 +0031c8: 6e10 f000 0600 |005e: invoke-virtual {v6}, Ljava/util/concurrent/atomic/AtomicInteger;.get:()I // method@00f0 +0031ce: 0a06 |0061: move-result v6 +0031d0: b065 |0062: add-int/2addr v5, v6 +0031d2: d803 0301 |0063: add-int/lit8 v3, v3, #int 1 // #01 +0031d6: 28df |0065: goto 0044 // -0021 +0031d8: 6203 1300 |0066: sget-object v3, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0031dc: 2206 2d00 |0068: new-instance v6, Ljava/lang/StringBuilder; // type@002d +0031e0: 7010 c100 0600 |006a: invoke-direct {v6}, Ljava/lang/StringBuilder;.:()V // method@00c1 +0031e6: 1a07 b800 |006d: const-string v7, "Winners " // string@00b8 +0031ea: 6e20 c800 7600 |006f: invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +0031f0: 6e20 c500 4600 |0072: invoke-virtual {v6, v4}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@00c5 +0031f6: 1a07 0500 |0075: const-string v7, " Votes " // string@0005 +0031fa: 6e20 c800 7600 |0077: invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +003200: 6e20 c500 5600 |007a: invoke-virtual {v6, v5}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@00c5 +003206: 6e10 ca00 0600 |007d: invoke-virtual {v6}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@00ca +00320c: 0c06 |0080: move-result-object v6 +00320e: 6e20 b300 6300 |0081: invoke-virtual {v3, v6}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +003214: 1223 |0084: const/4 v3, #int 2 // #2 +003216: 1216 |0085: const/4 v6, #int 1 // #1 +003218: 3264 2c00 |0086: if-eq v4, v6, 00b2 // +002c +00321c: 6207 1300 |0088: sget-object v7, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003220: 1a08 9e00 |008a: const-string v8, "Threads did not the same call-sites:" // string@009e +003224: 6e20 b300 8700 |008c: invoke-virtual {v7, v8}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +00322a: 0107 |008f: move v7, v0 +00322c: 3527 2200 |0090: if-ge v7, v2, 00b2 // +0022 +003230: 6208 1300 |0092: sget-object v8, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003234: 1a09 0400 |0094: const-string v9, " Thread % 2d invoked call site instance #%02d +" // string@0004 +003238: 233a 4800 |0096: new-array v10, v3, [Ljava/lang/Object; // type@0048 +00323c: 7110 bd00 0700 |0098: invoke-static {v7}, Ljava/lang/Integer;.valueOf:(I)Ljava/lang/Integer; // method@00bd +003242: 0c0b |009b: move-result-object v11 +003244: 4d0b 0a00 |009c: aput-object v11, v10, v0 +003248: 620b 0900 |009e: sget-object v11, LTestInvokeCustomWithConcurrentThreads;.targetted:[Ljava/util/concurrent/atomic/AtomicInteger; // field@0009 +00324c: 460b 0b07 |00a0: aget-object v11, v11, v7 +003250: 6e10 f000 0b00 |00a2: invoke-virtual {v11}, Ljava/util/concurrent/atomic/AtomicInteger;.get:()I // method@00f0 +003256: 0a0b |00a5: move-result v11 +003258: 7110 bd00 0b00 |00a6: invoke-static {v11}, Ljava/lang/Integer;.valueOf:(I)Ljava/lang/Integer; // method@00bd +00325e: 0c0b |00a9: move-result-object v11 +003260: 4d0b 0a06 |00aa: aput-object v11, v10, v6 +003264: 6e30 ab00 980a |00ac: invoke-virtual {v8, v9, v10}, Ljava/io/PrintStream;.format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; // method@00ab +00326a: d807 0701 |00af: add-int/lit8 v7, v7, #int 1 // #01 +00326e: 28df |00b1: goto 0090 // -0021 +003270: 3225 2c00 |00b2: if-eq v5, v2, 00de // +002c +003274: 6207 1300 |00b4: sget-object v7, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003278: 1a08 2000 |00b6: const-string v8, "Call-sites invocations :" // string@0020 +00327c: 6e20 b300 8700 |00b8: invoke-virtual {v7, v8}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +003282: 0107 |00bb: move v7, v0 +003284: 3527 2200 |00bc: if-ge v7, v2, 00de // +0022 +003288: 6208 1300 |00be: sget-object v8, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +00328c: 1a09 0300 |00c0: const-string v9, " Call site instance #%02d was invoked % 2d times +" // string@0003 +003290: 233a 4800 |00c2: new-array v10, v3, [Ljava/lang/Object; // type@0048 +003294: 7110 bd00 0700 |00c4: invoke-static {v7}, Ljava/lang/Integer;.valueOf:(I)Ljava/lang/Integer; // method@00bd +00329a: 0c0b |00c7: move-result-object v11 +00329c: 4d0b 0a00 |00c8: aput-object v11, v10, v0 +0032a0: 620b 0600 |00ca: sget-object v11, LTestInvokeCustomWithConcurrentThreads;.called:[Ljava/util/concurrent/atomic/AtomicInteger; // field@0006 +0032a4: 460b 0b07 |00cc: aget-object v11, v11, v7 +0032a8: 6e10 f000 0b00 |00ce: invoke-virtual {v11}, Ljava/util/concurrent/atomic/AtomicInteger;.get:()I // method@00f0 +0032ae: 0a0b |00d1: move-result v11 +0032b0: 7110 bd00 0b00 |00d2: invoke-static {v11}, Ljava/lang/Integer;.valueOf:(I)Ljava/lang/Integer; // method@00bd +0032b6: 0c0b |00d5: move-result-object v11 +0032b8: 4d0b 0a06 |00d6: aput-object v11, v10, v6 +0032bc: 6e30 ab00 980a |00d8: invoke-virtual {v8, v9, v10}, Ljava/io/PrintStream;.format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; // method@00ab +0032c2: d807 0701 |00db: add-int/lit8 v7, v7, #int 1 // #01 +0032c6: 28df |00dd: goto 00bc // -0021 +0032c8: 7120 6600 6400 |00de: invoke-static {v4, v6}, LTestInvokeCustomWithConcurrentThreads;.assertEquals:(II)V // method@0066 +0032ce: 7120 6600 2500 |00e1: invoke-static {v5, v2}, LTestInvokeCustomWithConcurrentThreads;.assertEquals:(II)V // method@0066 +0032d4: 0e00 |00e4: return-void + catches : (none) + positions : + 0x0000 line=107 + 0x0006 line=108 + 0x000f line=109 + 0x0018 line=107 + 0x001b line=113 + 0x001d line=114 + 0x0020 line=115 + 0x002c line=116 + 0x0031 line=114 + 0x0034 line=120 + 0x0037 line=121 + 0x003c line=120 + 0x003f line=125 + 0x0040 line=126 + 0x0041 line=127 + 0x0046 line=128 + 0x004e line=129 + 0x0058 line=130 + 0x005a line=131 + 0x0063 line=127 + 0x0066 line=135 + 0x0084 line=139 + 0x0088 line=140 + 0x008f line=141 + 0x0092 line=142 + 0x0098 line=143 + 0x00ac line=142 + 0x00af line=141 + 0x00b2 line=149 + 0x00b4 line=150 + 0x00bb line=151 + 0x00be line=152 + 0x00c4 line=153 + 0x00d8 line=152 + 0x00db line=151 + 0x00de line=157 + 0x00e1 line=158 + 0x00e4 line=159 + locals : + 0x0002 - 0x001b reg=1 i I + 0x001e - 0x0034 reg=3 i I + 0x0035 - 0x003f reg=3 i I + 0x0040 - 0x0044 reg=3 winners I + 0x0041 - 0x0044 reg=4 votes I + 0x0044 - 0x0066 reg=3 i I + 0x0090 - 0x00b2 reg=7 i I + 0x00bc - 0x00de reg=7 i I + 0x001d - 0x00e5 reg=1 threads [Ljava/lang/Thread; + 0x0044 - 0x00e5 reg=4 winners I + 0x0044 - 0x00e5 reg=5 votes I + + Virtual methods - + #0 : (in LTestInvokeCustomWithConcurrentThreads;) + name : 'run' + type : '()V' + access : 0x0001 (PUBLIC) + code - + registers : 2 + ins : 1 + outs : 1 + insns size : 9 16-bit code units +0030d8: |[0030d8] TestInvokeCustomWithConcurrentThreads.run:()V +0030e8: 12f0 |0000: const/4 v0, #int -1 // #ff +0030ea: fc10 1a00 0000 |0001: invoke-custom {v0}, call_site@001a +0030f0: 0a00 |0004: move-result v0 +0030f2: 7110 6b00 0000 |0005: invoke-static {v0}, LTestInvokeCustomWithConcurrentThreads;.notUsed:(I)I // method@006b +0030f8: 0e00 |0008: return-void + catches : (none) + positions : + 0x0000 line=63 + 0x0005 line=64 + 0x0008 line=65 + locals : + 0x0005 - 0x0009 reg=0 x I + 0x0000 - 0x0009 reg=1 this LTestInvokeCustomWithConcurrentThreads; + + source_file_idx : 149 (TestInvokeCustomWithConcurrentThreads.java) + +Class #10 header: +class_idx : 15 +access_flags : 1 (0x0001) +superclass_idx : 9 +interfaces_off : 0 (0x000000) +source_file_idx : 151 +annotations_off : 31180 (0x0079cc) +class_data_off : 29403 (0x0072db) +static_fields_size : 5 +instance_fields_size: 0 +direct_methods_size : 6 +virtual_methods_size: 0 + +Class #10 annotations: +Annotations on method #114 'add' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestLinkerMethodMinimalArguments; name="linkerMethod" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; } } fieldOrMethodName="_add" parameterTypes={ I I } returnType=I +Annotations on method #118 'linkerMethod' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #119 'test' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } + +Class #10 - + Class descriptor : 'LTestLinkerMethodMinimalArguments;' + Access flags : 0x0001 (PUBLIC) + Superclass : 'LTestBase;' + Interfaces - + Static fields - + #0 : (in LTestLinkerMethodMinimalArguments;) + name : 'FAILURE_TYPE_LINKER_METHOD_RETURNS_NULL' + type : 'I' + access : 0x0018 (STATIC FINAL) + value : 1 + #1 : (in LTestLinkerMethodMinimalArguments;) + name : 'FAILURE_TYPE_LINKER_METHOD_THROWS' + type : 'I' + access : 0x0018 (STATIC FINAL) + value : 2 + #2 : (in LTestLinkerMethodMinimalArguments;) + name : 'FAILURE_TYPE_NONE' + type : 'I' + access : 0x0018 (STATIC FINAL) + value : 0 + #3 : (in LTestLinkerMethodMinimalArguments;) + name : 'FAILURE_TYPE_TARGET_METHOD_THROWS' + type : 'I' + access : 0x0018 (STATIC FINAL) + value : 3 + #4 : (in LTestLinkerMethodMinimalArguments;) + name : 'forceFailureType' + type : 'I' + access : 0x000a (PRIVATE STATIC) + Instance fields - + Direct methods - + #0 : (in LTestLinkerMethodMinimalArguments;) + name : '' + type : '()V' + access : 0x10008 (STATIC CONSTRUCTOR) + code - + registers : 1 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +003404: |[003404] TestLinkerMethodMinimalArguments.:()V +003414: 1200 |0000: const/4 v0, #int 0 // #0 +003416: 6700 0f00 |0001: sput v0, LTestLinkerMethodMinimalArguments;.forceFailureType:I // field@000f +00341a: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=26 + locals : + + #1 : (in LTestLinkerMethodMinimalArguments;) + name : '' + type : '()V' + access : 0x10001 (PUBLIC CONSTRUCTOR) + code - + registers : 1 + ins : 1 + outs : 1 + insns size : 4 16-bit code units +00341c: |[00341c] TestLinkerMethodMinimalArguments.:()V +00342c: 7010 3200 0000 |0000: invoke-direct {v0}, LTestBase;.:()V // method@0032 +003432: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=25 + locals : + 0x0000 - 0x0004 reg=0 this LTestLinkerMethodMinimalArguments; + + #2 : (in LTestLinkerMethodMinimalArguments;) + name : '_add' + type : '(II)I' + access : 0x0008 (STATIC) + code - + registers : 4 + ins : 2 + outs : 2 + insns size : 23 16-bit code units +0032d8: |[0032d8] TestLinkerMethodMinimalArguments._add:(II)I +0032e8: 6000 0f00 |0000: sget v0, LTestLinkerMethodMinimalArguments;.forceFailureType:I // field@000f +0032ec: 1231 |0002: const/4 v1, #int 3 // #3 +0032ee: 3210 0500 |0003: if-eq v0, v1, 0008 // +0005 +0032f2: 9000 0203 |0005: add-int v0, v2, v3 +0032f6: 0f00 |0007: return v0 +0032f8: 6200 1300 |0008: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0032fc: 1a01 a000 |000a: const-string v1, "Throwing ArithmeticException in add()" // string@00a0 +003300: 6e20 b300 1000 |000c: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +003306: 2200 1d00 |000f: new-instance v0, Ljava/lang/ArithmeticException; // type@001d +00330a: 1a01 cc00 |0011: const-string v1, "add" // string@00cc +00330e: 7020 b400 1000 |0013: invoke-direct {v0, v1}, Ljava/lang/ArithmeticException;.:(Ljava/lang/String;)V // method@00b4 +003314: 2700 |0016: throw v0 + catches : (none) + positions : + 0x0000 line=51 + 0x0005 line=55 + 0x0008 line=52 + 0x000f line=53 + locals : + 0x0000 - 0x0017 reg=2 a I + 0x0000 - 0x0017 reg=3 b I + + #3 : (in LTestLinkerMethodMinimalArguments;) + name : 'add' + type : '(II)I' + access : 0x000a (PRIVATE STATIC) + code - + registers : 3 + ins : 2 + outs : 0 + insns size : 5 16-bit code units +003318: |[003318] TestLinkerMethodMinimalArguments.add:(II)I +003328: 7100 7400 0000 |0000: invoke-static {}, LTestLinkerMethodMinimalArguments;.assertNotReached:()V // method@0074 +00332e: 12f0 |0003: const/4 v0, #int -1 // #ff +003330: 0f00 |0004: return v0 + catches : (none) + positions : + 0x0000 line=45 + 0x0003 line=46 + locals : + 0x0000 - 0x0005 reg=1 a I + 0x0000 - 0x0005 reg=2 b I + + #4 : (in LTestLinkerMethodMinimalArguments;) + name : 'linkerMethod' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;' + access : 0x000a (PRIVATE STATIC) + code - + registers : 7 + ins : 3 + outs : 4 + insns size : 96 16-bit code units +003334: |[003334] TestLinkerMethodMinimalArguments.linkerMethod:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; +003344: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003348: 2201 2d00 |0002: new-instance v1, Ljava/lang/StringBuilder; // type@002d +00334c: 7010 c100 0100 |0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@00c1 +003352: 1a02 6701 |0007: const-string v2, "linkerMethod failure type " // string@0167 +003356: 6e20 c800 2100 |0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +00335c: 6002 0f00 |000c: sget v2, LTestLinkerMethodMinimalArguments;.forceFailureType:I // field@000f +003360: 6e20 c500 2100 |000e: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@00c5 +003366: 6e10 ca00 0100 |0011: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@00ca +00336c: 0c01 |0014: move-result-object v1 +00336e: 6e20 b300 1000 |0015: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +003374: 1c00 0f00 |0018: const-class v0, LTestLinkerMethodMinimalArguments; // type@000f +003378: 6e40 d800 0465 |001a: invoke-virtual {v4, v0, v5, v6}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00d8 +00337e: 0c00 |001d: move-result-object v0 +003380: 6001 0f00 |001e: sget v1, LTestLinkerMethodMinimalArguments;.forceFailureType:I // field@000f +003384: 2b01 3800 0000 |0020: packed-switch v1, 00000058 // +00000038 +00338a: 2201 3400 |0023: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0034 +00338e: 7020 d200 0100 |0025: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +003394: 1101 |0028: return-object v1 +003396: 6201 1300 |0029: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +00339a: 1a02 a100 |002b: const-string v2, "Throwing InstantiationException in linkerMethod()" // string@00a1 +00339e: 6e20 b300 2100 |002d: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +0033a4: 2201 2600 |0030: new-instance v1, Ljava/lang/InstantiationException; // type@0026 +0033a8: 1a02 6601 |0032: const-string v2, "linkerMethod" // string@0166 +0033ac: 7020 bb00 2100 |0034: invoke-direct {v1, v2}, Ljava/lang/InstantiationException;.:(Ljava/lang/String;)V // method@00bb +0033b2: 2701 |0037: throw v1 +0033b4: 6201 1300 |0038: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +0033b8: 2202 2d00 |003a: new-instance v2, Ljava/lang/StringBuilder; // type@002d +0033bc: 7010 c100 0200 |003c: invoke-direct {v2}, Ljava/lang/StringBuilder;.:()V // method@00c1 +0033c2: 1a03 8c00 |003f: const-string v3, "Returning null instead of CallSite for " // string@008c +0033c6: 6e20 c800 3200 |0041: invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +0033cc: 6e20 c800 5200 |0044: invoke-virtual {v2, v5}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +0033d2: 1a03 0000 |0047: const-string v3, " " // string@0000 +0033d6: 6e20 c800 3200 |0049: invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +0033dc: 6e20 c700 6200 |004c: invoke-virtual {v2, v6}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@00c7 +0033e2: 6e10 ca00 0200 |004f: invoke-virtual {v2}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@00ca +0033e8: 0c02 |0052: move-result-object v2 +0033ea: 6e20 b300 2100 |0053: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +0033f0: 1201 |0056: const/4 v1, #int 0 // #0 +0033f2: 1101 |0057: return-object v1 +0033f4: 0001 0200 0100 0000 1800 0000 0900 ... |0058: packed-switch-data (8 units) + catches : (none) + positions : + 0x0000 line=61 + 0x0018 line=62 + 0x001a line=63 + 0x001e line=64 + 0x0023 line=73 + 0x0029 line=70 + 0x0030 line=71 + 0x0038 line=66 + 0x0056 line=68 + locals : + 0x001e - 0x0060 reg=0 mh_add Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x0060 reg=4 caller Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0060 reg=5 name Ljava/lang/String; + 0x0000 - 0x0060 reg=6 methodType Ljava/lang/invoke/MethodType; + + #5 : (in LTestLinkerMethodMinimalArguments;) + name : 'test' + type : '(III)V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 6 + ins : 3 + outs : 2 + insns size : 68 16-bit code units +003434: |[003434] TestLinkerMethodMinimalArguments.test:(III)V +003444: 1200 |0000: const/4 v0, #int 0 // #0 +003446: 1211 |0001: const/4 v1, #int 1 // #1 +003448: 3a03 0400 |0002: if-ltz v3, 0006 // +0004 +00344c: 0112 |0004: move v2, v1 +00344e: 2802 |0005: goto 0007 // +0002 +003450: 0102 |0006: move v2, v0 +003452: 7110 7500 0200 |0007: invoke-static {v2}, LTestLinkerMethodMinimalArguments;.assertTrue:(Z)V // method@0075 +003458: 1232 |000a: const/4 v2, #int 3 // #3 +00345a: 3623 0400 |000b: if-gt v3, v2, 000f // +0004 +00345e: 0110 |000d: move v0, v1 +003460: 0000 |000e: nop // spacer +003462: 7110 7500 0000 |000f: invoke-static {v0}, LTestLinkerMethodMinimalArguments;.assertTrue:(Z)V // method@0075 +003468: 6703 0f00 |0012: sput v3, LTestLinkerMethodMinimalArguments;.forceFailureType:I // field@000f +00346c: 9000 0405 |0014: add-int v0, v4, v5 +003470: fc20 1b00 5400 |0016: invoke-custom {v4, v5}, call_site@001b +003476: 0a01 |0019: move-result v1 +003478: 7120 7300 1000 |001a: invoke-static {v0, v1}, LTestLinkerMethodMinimalArguments;.assertEquals:(II)V // method@0073 +00347e: 6200 1300 |001d: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003482: 2201 2d00 |001f: new-instance v1, Ljava/lang/StringBuilder; // type@002d +003486: 7010 c100 0100 |0021: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@00c1 +00348c: 1a02 2a00 |0024: const-string v2, "Failure Type + " // string@002a +003490: 6e20 c800 2100 |0026: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +003496: 6e20 c500 3100 |0029: invoke-virtual {v1, v3}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@00c5 +00349c: 1a02 0100 |002c: const-string v2, " (" // string@0001 +0034a0: 6e20 c800 2100 |002e: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +0034a6: 6e20 c500 4100 |0031: invoke-virtual {v1, v4}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@00c5 +0034ac: 6e20 c500 5100 |0034: invoke-virtual {v1, v5}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@00c5 +0034b2: 1a02 0700 |0037: const-string v2, ")" // string@0007 +0034b6: 6e20 c800 2100 |0039: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +0034bc: 6e10 ca00 0100 |003c: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@00ca +0034c2: 0c01 |003f: move-result-object v1 +0034c4: 6e20 b300 1000 |0040: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +0034ca: 0e00 |0043: return-void + catches : (none) + positions : + 0x0000 line=78 + 0x000a line=79 + 0x0012 line=80 + 0x0014 line=81 + 0x001d line=82 + 0x0043 line=83 + locals : + 0x0000 - 0x0044 reg=3 failureType I + 0x0000 - 0x0044 reg=4 x I + 0x0000 - 0x0044 reg=5 y I + + Virtual methods - + source_file_idx : 151 (TestLinkerMethodMinimalArguments.java) + +Class #11 header: +class_idx : 16 +access_flags : 1 (0x0001) +superclass_idx : 9 +interfaces_off : 0 (0x000000) +source_file_idx : 153 +annotations_off : 31220 (0x0079f4) +class_data_off : 29445 (0x007305) +static_fields_size : 1 +instance_fields_size: 0 +direct_methods_size : 6 +virtual_methods_size: 1 + +Class #11 annotations: +Annotations on method #124 'add' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestLinkerMethodMultipleArgumentTypes; name="linkerMethod" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; I I I I I F D Ljava/lang/String; Ljava/lang/Class; J } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; intValue={ -1 } Lannotations/Constant; intValue={ 1 } Lannotations/Constant; intValue={ 97 } Lannotations/Constant; intValue={ 1024 } Lannotations/Constant; intValue={ 1 } Lannotations/Constant; floatValue={ 11.1 } Lannotations/Constant; doubleValue={ 2.2 } Lannotations/Constant; stringValue={ "Hello" } Lannotations/Constant; classValue={ LTestLinkerMethodMultipleArgumentTypes; } Lannotations/Constant; longValue={ 123456789 } } fieldOrMethodName="_add" parameterTypes={ I I } returnType=I +Annotations on method #131 'linkerMethod' + VISIBILITY_SYSTEM Ldalvik/annotation/Signature; value={ "(" "Ljava/lang/invoke/MethodHandles$Lookup;" "Ljava/lang/String;" "Ljava/lang/invoke/MethodType;" "IIIIIFD" "Ljava/lang/String;" "Ljava/lang/Class<" "*>;J)" "Ljava/lang/invoke/CallSite;" } + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #132 'test' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } + +Class #11 - + Class descriptor : 'LTestLinkerMethodMultipleArgumentTypes;' + Access flags : 0x0001 (PUBLIC) + Superclass : 'LTestBase;' + Interfaces - + Static fields - + #0 : (in LTestLinkerMethodMultipleArgumentTypes;) + name : 'bootstrapRunCount' + type : 'I' + access : 0x000a (PRIVATE STATIC) + Instance fields - + Direct methods - + #0 : (in LTestLinkerMethodMultipleArgumentTypes;) + name : '' + type : '()V' + access : 0x10008 (STATIC CONSTRUCTOR) + code - + registers : 1 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +003618: |[003618] TestLinkerMethodMultipleArgumentTypes.:()V +003628: 1200 |0000: const/4 v0, #int 0 // #0 +00362a: 6700 1000 |0001: sput v0, LTestLinkerMethodMultipleArgumentTypes;.bootstrapRunCount:I // field@0010 +00362e: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=28 + locals : + + #1 : (in LTestLinkerMethodMultipleArgumentTypes;) + name : '' + type : '()V' + access : 0x10001 (PUBLIC CONSTRUCTOR) + code - + registers : 1 + ins : 1 + outs : 1 + insns size : 4 16-bit code units +003630: |[003630] TestLinkerMethodMultipleArgumentTypes.:()V +003640: 7010 3200 0000 |0000: invoke-direct {v0}, LTestBase;.:()V // method@0032 +003646: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=26 + locals : + 0x0000 - 0x0004 reg=0 this LTestLinkerMethodMultipleArgumentTypes; + + #2 : (in LTestLinkerMethodMultipleArgumentTypes;) + name : '_add' + type : '(II)I' + access : 0x000a (PRIVATE STATIC) + code - + registers : 3 + ins : 2 + outs : 0 + insns size : 3 16-bit code units +0034e4: |[0034e4] TestLinkerMethodMultipleArgumentTypes._add:(II)I +0034f4: 9000 0102 |0000: add-int v0, v1, v2 +0034f8: 0f00 |0002: return v0 + catches : (none) + positions : + 0x0000 line=74 + locals : + 0x0000 - 0x0003 reg=1 a I + 0x0000 - 0x0003 reg=2 b I + + #3 : (in LTestLinkerMethodMultipleArgumentTypes;) + name : 'add' + type : '(II)I' + access : 0x000a (PRIVATE STATIC) + code - + registers : 3 + ins : 2 + outs : 0 + insns size : 5 16-bit code units +0034fc: |[0034fc] TestLinkerMethodMultipleArgumentTypes.add:(II)I +00350c: 7100 8200 0000 |0000: invoke-static {}, LTestLinkerMethodMultipleArgumentTypes;.assertNotReached:()V // method@0082 +003512: 12f0 |0003: const/4 v0, #int -1 // #ff +003514: 0f00 |0004: return v0 + catches : (none) + positions : + 0x0000 line=68 + 0x0003 line=69 + locals : + 0x0000 - 0x0005 reg=1 a I + 0x0000 - 0x0005 reg=2 b I + + #4 : (in LTestLinkerMethodMultipleArgumentTypes;) + name : 'linkerMethod' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;IIIIIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite;' + access : 0x000a (PRIVATE STATIC) + code - + registers : 31 + ins : 15 + outs : 4 + insns size : 119 16-bit code units +003518: |[003518] TestLinkerMethodMultipleArgumentTypes.linkerMethod:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;IIIIIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite; +003528: 0800 1100 |0000: move-object/from16 v0, v17 +00352c: 0801 1200 |0002: move-object/from16 v1, v18 +003530: 6202 1300 |0004: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003534: 2203 2d00 |0006: new-instance v3, Ljava/lang/StringBuilder; // type@002d +003538: 7010 c100 0300 |0008: invoke-direct {v3}, Ljava/lang/StringBuilder;.:()V // method@00c1 +00353e: 1a04 6100 |000b: const-string v4, "Linking " // string@0061 +003542: 6e20 c800 4300 |000d: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +003548: 6e20 c800 0300 |0010: invoke-virtual {v3, v0}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +00354e: 1a04 0000 |0013: const-string v4, " " // string@0000 +003552: 6e20 c800 4300 |0015: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@00c8 +003558: 6e20 c700 1300 |0018: invoke-virtual {v3, v1}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@00c7 +00355e: 6e10 ca00 0300 |001b: invoke-virtual {v3}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@00ca +003564: 0c03 |001e: move-result-object v3 +003566: 6e20 b300 3200 |001f: invoke-virtual {v2, v3}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +00356c: 12f2 |0022: const/4 v2, #int -1 // #ff +00356e: 0203 1300 |0023: move/from16 v3, v19 +003572: 7120 7f00 3200 |0025: invoke-static {v2, v3}, LTestLinkerMethodMultipleArgumentTypes;.assertEquals:(II)V // method@007f +003578: 1212 |0028: const/4 v2, #int 1 // #1 +00357a: 0204 1400 |0029: move/from16 v4, v20 +00357e: 7120 7f00 4200 |002b: invoke-static {v2, v4}, LTestLinkerMethodMultipleArgumentTypes;.assertEquals:(II)V // method@007f +003584: 1305 6100 |002e: const/16 v5, #int 97 // #61 +003588: 0206 1500 |0030: move/from16 v6, v21 +00358c: 7120 7f00 6500 |0032: invoke-static {v5, v6}, LTestLinkerMethodMultipleArgumentTypes;.assertEquals:(II)V // method@007f +003592: 1305 0004 |0035: const/16 v5, #int 1024 // #400 +003596: 0207 1600 |0037: move/from16 v7, v22 +00359a: 7120 7f00 7500 |0039: invoke-static {v5, v7}, LTestLinkerMethodMultipleArgumentTypes;.assertEquals:(II)V // method@007f +0035a0: 0205 1700 |003c: move/from16 v5, v23 +0035a4: 7120 7f00 5200 |003e: invoke-static {v2, v5}, LTestLinkerMethodMultipleArgumentTypes;.assertEquals:(II)V // method@007f +0035aa: 1402 9a99 3141 |0041: const v2, #float 11.1 // #4131999a +0035b0: 0208 1800 |0044: move/from16 v8, v24 +0035b4: 7120 7e00 8200 |0046: invoke-static {v2, v8}, LTestLinkerMethodMultipleArgumentTypes;.assertEquals:(FF)V // method@007e +0035ba: 1809 9a99 9999 9999 0140 |0049: const-wide v9, #double 2.2 // #400199999999999a +0035c4: 050b 1900 |004e: move-wide/from16 v11, v25 +0035c8: 7140 7d00 a9cb |0050: invoke-static {v9, v10, v11, v12}, LTestLinkerMethodMultipleArgumentTypes;.assertEquals:(DD)V // method@007d +0035ce: 1a02 2c00 |0053: const-string v2, "Hello" // string@002c +0035d2: 0809 1b00 |0055: move-object/from16 v9, v27 +0035d6: 7120 8100 9200 |0057: invoke-static {v2, v9}, LTestLinkerMethodMultipleArgumentTypes;.assertEquals:(Ljava/lang/Object;Ljava/lang/Object;)V // method@0081 +0035dc: 1c02 1000 |005a: const-class v2, LTestLinkerMethodMultipleArgumentTypes; // type@0010 +0035e0: 080a 1c00 |005c: move-object/from16 v10, v28 +0035e4: 7120 8100 a200 |005e: invoke-static {v2, v10}, LTestLinkerMethodMultipleArgumentTypes;.assertEquals:(Ljava/lang/Object;Ljava/lang/Object;)V // method@0081 +0035ea: 170d 15cd 5b07 |0061: const-wide/32 v13, #float 1.6536e-34 // #075bcd15 +0035f0: 0502 1d00 |0064: move-wide/from16 v2, v29 +0035f4: 7140 8000 ed32 |0066: invoke-static {v13, v14, v2, v3}, LTestLinkerMethodMultipleArgumentTypes;.assertEquals:(JJ)V // method@0080 +0035fa: 1c0d 1000 |0069: const-class v13, LTestLinkerMethodMultipleArgumentTypes; // type@0010 +0035fe: 080e 1000 |006b: move-object/from16 v14, v16 +003602: 6e40 d800 de10 |006d: invoke-virtual {v14, v13, v0, v1}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00d8 +003608: 0c0d |0070: move-result-object v13 +00360a: 220f 3400 |0071: new-instance v15, Ljava/lang/invoke/ConstantCallSite; // type@0034 +00360e: 7020 d200 df00 |0073: invoke-direct {v15, v13}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +003614: 110f |0076: return-object v15 + catches : (none) + positions : + 0x0000 line=93 + 0x0022 line=94 + 0x0028 line=95 + 0x002e line=96 + 0x0035 line=97 + 0x003c line=98 + 0x0041 line=99 + 0x0049 line=100 + 0x0053 line=101 + 0x005a line=102 + 0x0061 line=103 + 0x0069 line=104 + 0x006b line=105 + 0x0071 line=106 + locals : + 0x0000 - 0x0000 reg=28 (null) Ljava/lang/Class; + 0x0071 - 0x0077 reg=13 mh_add Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x0077 reg=16 caller Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0077 reg=17 name Ljava/lang/String; + 0x0000 - 0x0077 reg=18 methodType Ljava/lang/invoke/MethodType; + 0x0000 - 0x0077 reg=19 v1 I + 0x0000 - 0x0077 reg=20 v2 I + 0x0000 - 0x0077 reg=21 v3 I + 0x0000 - 0x0077 reg=22 v4 I + 0x0000 - 0x0077 reg=23 v5 I + 0x0000 - 0x0077 reg=24 v6 F + 0x0000 - 0x0077 reg=25 v7 D + 0x0000 - 0x0077 reg=27 v8 Ljava/lang/String; + 0x0000 - 0x0077 reg=28 v9 Ljava/lang/Class; Ljava/lang/Class<*>; + 0x0000 - 0x0077 reg=29 v10 J + + #5 : (in LTestLinkerMethodMultipleArgumentTypes;) + name : 'test' + type : '(II)V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 4 + ins : 2 + outs : 2 + insns size : 17 16-bit code units +003648: |[003648] TestLinkerMethodMultipleArgumentTypes.test:(II)V +003658: 9000 0203 |0000: add-int v0, v2, v3 +00365c: fc20 1c00 3200 |0002: invoke-custom {v2, v3}, call_site@001c +003662: 0a01 |0005: move-result v1 +003664: 7120 7f00 1000 |0006: invoke-static {v0, v1}, LTestLinkerMethodMultipleArgumentTypes;.assertEquals:(II)V // method@007f +00366a: 6200 1300 |0009: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +00366e: 9001 0203 |000b: add-int v1, v2, v3 +003672: 6e20 b100 1000 |000d: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(I)V // method@00b1 +003678: 0e00 |0010: return-void + catches : (none) + positions : + 0x0000 line=114 + 0x0009 line=115 + 0x0010 line=116 + locals : + 0x0000 - 0x0011 reg=2 x I + 0x0000 - 0x0011 reg=3 y I + + Virtual methods - + #0 : (in LTestLinkerMethodMultipleArgumentTypes;) + name : 'GetBootstrapRunCount' + type : '()I' + access : 0x0001 (PUBLIC) + code - + registers : 2 + ins : 1 + outs : 0 + insns size : 3 16-bit code units +0034cc: |[0034cc] TestLinkerMethodMultipleArgumentTypes.GetBootstrapRunCount:()I +0034dc: 6000 1000 |0000: sget v0, LTestLinkerMethodMultipleArgumentTypes;.bootstrapRunCount:I // field@0010 +0034e0: 0f00 |0002: return v0 + catches : (none) + positions : + 0x0000 line=110 + locals : + 0x0000 - 0x0003 reg=1 this LTestLinkerMethodMultipleArgumentTypes; + + source_file_idx : 153 (TestLinkerMethodMultipleArgumentTypes.java) + +Class #12 header: +class_idx : 17 +access_flags : 0 (0x0000) +superclass_idx : 9 +interfaces_off : 0 (0x000000) +source_file_idx : 154 +annotations_off : 31260 (0x007a1c) +class_data_off : 29483 (0x00732b) +static_fields_size : 0 +instance_fields_size: 0 +direct_methods_size : 6 +virtual_methods_size: 0 + +Class #12 annotations: +Annotations on method #136 'addf' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LUnrelatedBSM; name="bsm" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; Ljava/lang/Class; } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; classValue={ LTestLinkerUnrelatedBSM; } } fieldOrMethodName="_addf" parameterTypes={ F F } returnType=F +Annotations on method #139 'subf' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LUnrelatedBSM; name="bsm" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; Ljava/lang/Class; } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; classValue={ LTestLinkerUnrelatedBSM; } } fieldOrMethodName="_subf" parameterTypes={ F F } returnType=F + +Class #12 - + Class descriptor : 'LTestLinkerUnrelatedBSM;' + Access flags : 0x0000 () + Superclass : 'LTestBase;' + Interfaces - + Static fields - + Instance fields - + Direct methods - + #0 : (in LTestLinkerUnrelatedBSM;) + name : '' + type : '()V' + access : 0x10000 (CONSTRUCTOR) + code - + registers : 1 + ins : 1 + outs : 1 + insns size : 4 16-bit code units +0036e4: |[0036e4] TestLinkerUnrelatedBSM.:()V +0036f4: 7010 3200 0000 |0000: invoke-direct {v0}, LTestBase;.:()V // method@0032 +0036fa: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=23 + locals : + 0x0000 - 0x0004 reg=0 this LTestLinkerUnrelatedBSM; + + #1 : (in LTestLinkerUnrelatedBSM;) + name : '_addf' + type : '(FF)F' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 3 + ins : 2 + outs : 0 + insns size : 3 16-bit code units +00367c: |[00367c] TestLinkerUnrelatedBSM._addf:(FF)F +00368c: a600 0102 |0000: add-float v0, v1, v2 +003690: 0f00 |0002: return v0 + catches : (none) + positions : + 0x0000 line=47 + locals : + 0x0000 - 0x0003 reg=1 a F + 0x0000 - 0x0003 reg=2 b F + + #2 : (in LTestLinkerUnrelatedBSM;) + name : '_subf' + type : '(FF)F' + access : 0x000a (PRIVATE STATIC) + code - + registers : 3 + ins : 2 + outs : 0 + insns size : 3 16-bit code units +003694: |[003694] TestLinkerUnrelatedBSM._subf:(FF)F +0036a4: a700 0102 |0000: sub-float v0, v1, v2 +0036a8: 0f00 |0002: return v0 + catches : (none) + positions : + 0x0000 line=73 + locals : + 0x0000 - 0x0003 reg=1 a F + 0x0000 - 0x0003 reg=2 b F + + #3 : (in LTestLinkerUnrelatedBSM;) + name : 'addf' + type : '(FF)F' + access : 0x000a (PRIVATE STATIC) + code - + registers : 3 + ins : 2 + outs : 0 + insns size : 5 16-bit code units +0036ac: |[0036ac] TestLinkerUnrelatedBSM.addf:(FF)F +0036bc: 7100 8a00 0000 |0000: invoke-static {}, LTestLinkerUnrelatedBSM;.assertNotReached:()V // method@008a +0036c2: 1210 |0003: const/4 v0, #int 1 // #1 +0036c4: 0f00 |0004: return v0 + catches : (none) + positions : + 0x0000 line=42 + 0x0003 line=43 + locals : + 0x0000 - 0x0005 reg=1 a F + 0x0000 - 0x0005 reg=2 b F + + #4 : (in LTestLinkerUnrelatedBSM;) + name : 'subf' + type : '(FF)F' + access : 0x000a (PRIVATE STATIC) + code - + registers : 3 + ins : 2 + outs : 0 + insns size : 5 16-bit code units +0036c8: |[0036c8] TestLinkerUnrelatedBSM.subf:(FF)F +0036d8: 7100 8a00 0000 |0000: invoke-static {}, LTestLinkerUnrelatedBSM;.assertNotReached:()V // method@008a +0036de: 1210 |0003: const/4 v0, #int 1 // #1 +0036e0: 0f00 |0004: return v0 + catches : (none) + positions : + 0x0000 line=68 + 0x0003 line=69 + locals : + 0x0000 - 0x0005 reg=1 a F + 0x0000 - 0x0005 reg=2 b F + + #5 : (in LTestLinkerUnrelatedBSM;) + name : 'test' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 4 + ins : 0 + outs : 2 + insns size : 34 16-bit code units +0036fc: |[0036fc] TestLinkerUnrelatedBSM.test:()V +00370c: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003710: 1c01 1100 |0002: const-class v1, LTestLinkerUnrelatedBSM; // type@0011 +003714: 6e10 b700 0100 |0004: invoke-virtual {v1}, Ljava/lang/Class;.getName:()Ljava/lang/String; // method@00b7 +00371a: 0c01 |0007: move-result-object v1 +00371c: 6e20 b300 1000 |0008: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +003722: 1500 003f |000b: const/high16 v0, #int 1056964608 // #3f00 +003726: 1501 0040 |000d: const/high16 v1, #int 1073741824 // #4000 +00372a: fc20 1d00 0100 |000f: invoke-custom {v1, v0}, call_site@001d +003730: 0a02 |0012: move-result v2 +003732: 1503 2040 |0013: const/high16 v3, #int 1075838976 // #4020 +003736: 7120 8900 2300 |0015: invoke-static {v3, v2}, LTestLinkerUnrelatedBSM;.assertEquals:(FF)V // method@0089 +00373c: fc20 1e00 0100 |0018: invoke-custom {v1, v0}, call_site@001e +003742: 0a00 |001b: move-result v0 +003744: 1501 c03f |001c: const/high16 v1, #int 1069547520 // #3fc0 +003748: 7120 8900 0100 |001e: invoke-static {v1, v0}, LTestLinkerUnrelatedBSM;.assertEquals:(FF)V // method@0089 +00374e: 0e00 |0021: return-void + catches : (none) + positions : + 0x0000 line=77 + 0x000b line=78 + 0x0018 line=79 + 0x0021 line=80 + locals : + + Virtual methods - + source_file_idx : 154 (TestLinkerUnrelatedBSM.java) + +Class #13 header: +class_idx : 18 +access_flags : 1 (0x0001) +superclass_idx : 9 +interfaces_off : 0 (0x000000) +source_file_idx : 156 +annotations_off : 31292 (0x007a3c) +class_data_off : 29514 (0x00734a) +static_fields_size : 0 +instance_fields_size: 0 +direct_methods_size : 27 +virtual_methods_size: 0 + +Class #13 annotations: +Annotations on method #143 'bsmWithBoxedArray' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #144 'bsmWithClassAndFloatArray' + VISIBILITY_SYSTEM Ldalvik/annotation/Signature; value={ "(" "Ljava/lang/invoke/MethodHandles$Lookup;" "Ljava/lang/String;" "Ljava/lang/invoke/MethodType;" "Ljava/lang/Class<" "*>;[F)" "Ljava/lang/invoke/CallSite;" } + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #145 'bsmWithClassArray' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #146 'bsmWithDoubleArray' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #147 'bsmWithFloatAndLongArray' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #148 'bsmWithIntAndStringArray' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #149 'bsmWithLongAndIntArray' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #150 'bsmWithStringArray' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #151 'bsmWithWiderArray' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #152 'methodA' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestVariableArityLinkerMethod; name="bsmWithStringArray" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; [Ljava/lang/String; } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; stringValue={ "Aachen" } Lannotations/Constant; stringValue={ "Aalborg" } Lannotations/Constant; stringValue={ "Aalto" } } fieldOrMethodName="methodA" +Annotations on method #153 'methodB' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestVariableArityLinkerMethod; name="bsmWithStringArray" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; [Ljava/lang/String; } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; stringValue={ "barium" } } fieldOrMethodName="methodB" +Annotations on method #154 'methodC' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestVariableArityLinkerMethod; name="bsmWithStringArray" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; [Ljava/lang/String; } } fieldOrMethodName="methodC" +Annotations on method #155 'methodD' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestVariableArityLinkerMethod; name="bsmWithIntAndStringArray" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; I [Ljava/lang/String; } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; intValue={ 101 } Lannotations/Constant; stringValue={ "zoo" } Lannotations/Constant; stringValue={ "zoogene" } Lannotations/Constant; stringValue={ "zoogenic" } } fieldOrMethodName="methodD" +Annotations on method #156 'methodE' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestVariableArityLinkerMethod; name="bsmWithIntAndStringArray" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; I [Ljava/lang/String; } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; intValue={ 102 } Lannotations/Constant; stringValue={ "zonic" } } fieldOrMethodName="methodE" +Annotations on method #157 'methodF' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestVariableArityLinkerMethod; name="bsmWithIntAndStringArray" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; I [Ljava/lang/String; } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; intValue={ 103 } } fieldOrMethodName="methodF" +Annotations on method #158 'methodG' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestVariableArityLinkerMethod; name="bsmWithLongAndIntArray" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; J [I } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; longValue={ 81985529216486895 } Lannotations/Constant; intValue={ 1 } Lannotations/Constant; intValue={ -1 } Lannotations/Constant; intValue={ 2 } Lannotations/Constant; intValue={ -2 } } fieldOrMethodName="methodG" +Annotations on method #159 'methodH' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestVariableArityLinkerMethod; name="bsmWithFloatAndLongArray" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; F [J } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; floatValue={ -2.71828 } Lannotations/Constant; longValue={ 999999999999 } Lannotations/Constant; longValue={ -8888888888888 } } fieldOrMethodName="methodH" +Annotations on method #160 'methodI' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestVariableArityLinkerMethod; name="bsmWithClassAndFloatArray" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; Ljava/lang/Class; [F } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; classValue={ Ljava/lang/Throwable; } Lannotations/Constant; floatValue={ 3.40282e+38 } Lannotations/Constant; floatValue={ 1.4013e-45 } Lannotations/Constant; floatValue={ 3.14159 } Lannotations/Constant; floatValue={ -3.14159 } } fieldOrMethodName="methodI" +Annotations on method #161 'methodJ' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestVariableArityLinkerMethod; name="bsmWithDoubleArray" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; [D } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; doubleValue={ 1.79769e+308 } Lannotations/Constant; doubleValue={ 4.94066e-324 } Lannotations/Constant; doubleValue={ 2.71828 } Lannotations/Constant; doubleValue={ -3.14159 } } fieldOrMethodName="methodJ" +Annotations on method #162 'methodK' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestVariableArityLinkerMethod; name="bsmWithClassArray" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; [Ljava/lang/Class; } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; classValue={ Ljava/lang/Integer; } Lannotations/Constant; classValue={ Ljava/lang/invoke/MethodHandles; } Lannotations/Constant; classValue={ Ljava/util/Arrays; } } fieldOrMethodName="methodK" +Annotations on method #163 'methodO' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestVariableArityLinkerMethod; name="bsmWithIntAndStringArray" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; I [Ljava/lang/String; } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; intValue={ 103 } Lannotations/Constant; intValue={ 104 } } fieldOrMethodName="methodO" +Annotations on method #164 'methodP' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestVariableArityLinkerMethod; name="bsmWithIntAndStringArray" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; I [Ljava/lang/String; } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; intValue={ 103 } Lannotations/Constant; stringValue={ "A" } Lannotations/Constant; stringValue={ "B" } Lannotations/Constant; intValue={ 42 } } fieldOrMethodName="methodP" +Annotations on method #165 'methodQ' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestVariableArityLinkerMethod; name="bsmWithWiderArray" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; [J } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; intValue={ 103 } Lannotations/Constant; intValue={ 42 } } fieldOrMethodName="methodQ" +Annotations on method #166 'methodR' + VISIBILITY_RUNTIME Lannotations/CalledByIndy; bootstrapMethod={ Lannotations/BootstrapMethod; enclosingType=LTestVariableArityLinkerMethod; name="bsmWithBoxedArray" parameterTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; [Ljava/lang/Integer; } } constantArgumentsForBootstrapMethod={ Lannotations/Constant; intValue={ 1030 } Lannotations/Constant; intValue={ 420 } } fieldOrMethodName="methodR" + +Class #13 - + Class descriptor : 'LTestVariableArityLinkerMethod;' + Access flags : 0x0001 (PUBLIC) + Superclass : 'LTestBase;' + Interfaces - + Static fields - + Instance fields - + Direct methods - + #0 : (in LTestVariableArityLinkerMethod;) + name : '' + type : '()V' + access : 0x10001 (PUBLIC CONSTRUCTOR) + code - + registers : 1 + ins : 1 + outs : 1 + insns size : 4 16-bit code units +003a7c: |[003a7c] TestVariableArityLinkerMethod.:()V +003a8c: 7010 3200 0000 |0000: invoke-direct {v0}, LTestBase;.:()V // method@0032 +003a92: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=27 + locals : + 0x0000 - 0x0004 reg=0 this LTestVariableArityLinkerMethod; + + #1 : (in LTestVariableArityLinkerMethod;) + name : 'bsmWithBoxedArray' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Integer;)Ljava/lang/invoke/CallSite;' + access : 0x000a (PRIVATE STATIC) + code - + registers : 7 + ins : 4 + outs : 4 + insns size : 34 16-bit code units +003750: |[003750] TestVariableArityLinkerMethod.bsmWithBoxedArray:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Integer;)Ljava/lang/invoke/CallSite; +003760: 1a00 f800 |0000: const-string v0, "bsmWithBoxedArray" // string@00f8 +003764: 1241 |0002: const/4 v1, #int 4 // #4 +003766: 2311 4800 |0003: new-array v1, v1, [Ljava/lang/Object; // type@0048 +00376a: 1202 |0005: const/4 v2, #int 0 // #0 +00376c: 4d03 0102 |0006: aput-object v3, v1, v2 +003770: 1212 |0008: const/4 v2, #int 1 // #1 +003772: 4d04 0102 |0009: aput-object v4, v1, v2 +003776: 1222 |000b: const/4 v2, #int 2 // #2 +003778: 4d05 0102 |000c: aput-object v5, v1, v2 +00377c: 1232 |000e: const/4 v2, #int 3 // #3 +00377e: 4d06 0102 |000f: aput-object v6, v1, v2 +003782: 7120 a700 1000 |0011: invoke-static {v0, v1}, LTestVariableArityLinkerMethod;.printBsmArgs:(Ljava/lang/String;[Ljava/lang/Object;)V // method@00a7 +003788: 6e10 dc00 0300 |0014: invoke-virtual {v3}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@00dc +00378e: 0c00 |0017: move-result-object v0 +003790: 6e40 d800 0354 |0018: invoke-virtual {v3, v0, v4, v5}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00d8 +003796: 0c00 |001b: move-result-object v0 +003798: 2201 3400 |001c: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0034 +00379c: 7020 d200 0100 |001e: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +0037a2: 1101 |0021: return-object v1 + catches : (none) + positions : + 0x0000 line=477 + 0x0014 line=478 + 0x001c line=479 + locals : + 0x001c - 0x0022 reg=0 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x0022 reg=3 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0022 reg=4 methodName Ljava/lang/String; + 0x0000 - 0x0022 reg=5 methodType Ljava/lang/invoke/MethodType; + 0x0000 - 0x0022 reg=6 extraArgs [Ljava/lang/Integer; + + #2 : (in LTestVariableArityLinkerMethod;) + name : 'bsmWithClassAndFloatArray' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Class;[F)Ljava/lang/invoke/CallSite;' + access : 0x008a (PRIVATE STATIC VARARGS) + code - + registers : 8 + ins : 5 + outs : 4 + insns size : 37 16-bit code units +0037a4: |[0037a4] TestVariableArityLinkerMethod.bsmWithClassAndFloatArray:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Class;[F)Ljava/lang/invoke/CallSite; +0037b4: 1a00 f900 |0000: const-string v0, "bsmWithClassAndFloatArray" // string@00f9 +0037b8: 1251 |0002: const/4 v1, #int 5 // #5 +0037ba: 2311 4800 |0003: new-array v1, v1, [Ljava/lang/Object; // type@0048 +0037be: 1202 |0005: const/4 v2, #int 0 // #0 +0037c0: 4d03 0102 |0006: aput-object v3, v1, v2 +0037c4: 1212 |0008: const/4 v2, #int 1 // #1 +0037c6: 4d04 0102 |0009: aput-object v4, v1, v2 +0037ca: 1222 |000b: const/4 v2, #int 2 // #2 +0037cc: 4d05 0102 |000c: aput-object v5, v1, v2 +0037d0: 1232 |000e: const/4 v2, #int 3 // #3 +0037d2: 4d06 0102 |000f: aput-object v6, v1, v2 +0037d6: 1242 |0011: const/4 v2, #int 4 // #4 +0037d8: 4d07 0102 |0012: aput-object v7, v1, v2 +0037dc: 7120 a700 1000 |0014: invoke-static {v0, v1}, LTestVariableArityLinkerMethod;.printBsmArgs:(Ljava/lang/String;[Ljava/lang/Object;)V // method@00a7 +0037e2: 6e10 dc00 0300 |0017: invoke-virtual {v3}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@00dc +0037e8: 0c00 |001a: move-result-object v0 +0037ea: 6e40 d800 0354 |001b: invoke-virtual {v3, v0, v4, v5}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00d8 +0037f0: 0c00 |001e: move-result-object v0 +0037f2: 2201 3400 |001f: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0034 +0037f6: 7020 d200 0100 |0021: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +0037fc: 1101 |0024: return-object v1 + catches : (none) + positions : + 0x0000 line=294 + 0x0017 line=296 + 0x001f line=297 + locals : + 0x0000 - 0x0000 reg=6 (null) Ljava/lang/Class; + 0x001f - 0x0025 reg=0 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x0025 reg=3 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0025 reg=4 methodName Ljava/lang/String; + 0x0000 - 0x0025 reg=5 methodType Ljava/lang/invoke/MethodType; + 0x0000 - 0x0025 reg=6 extraArg Ljava/lang/Class; Ljava/lang/Class<*>; + 0x0000 - 0x0025 reg=7 arityArgs [F + + #3 : (in LTestVariableArityLinkerMethod;) + name : 'bsmWithClassArray' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Class;)Ljava/lang/invoke/CallSite;' + access : 0x008a (PRIVATE STATIC VARARGS) + code - + registers : 7 + ins : 4 + outs : 4 + insns size : 34 16-bit code units +003800: |[003800] TestVariableArityLinkerMethod.bsmWithClassArray:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Class;)Ljava/lang/invoke/CallSite; +003810: 1a00 fa00 |0000: const-string v0, "bsmWithClassArray" // string@00fa +003814: 1241 |0002: const/4 v1, #int 4 // #4 +003816: 2311 4800 |0003: new-array v1, v1, [Ljava/lang/Object; // type@0048 +00381a: 1202 |0005: const/4 v2, #int 0 // #0 +00381c: 4d03 0102 |0006: aput-object v3, v1, v2 +003820: 1212 |0008: const/4 v2, #int 1 // #1 +003822: 4d04 0102 |0009: aput-object v4, v1, v2 +003826: 1222 |000b: const/4 v2, #int 2 // #2 +003828: 4d05 0102 |000c: aput-object v5, v1, v2 +00382c: 1232 |000e: const/4 v2, #int 3 // #3 +00382e: 4d06 0102 |000f: aput-object v6, v1, v2 +003832: 7120 a700 1000 |0011: invoke-static {v0, v1}, LTestVariableArityLinkerMethod;.printBsmArgs:(Ljava/lang/String;[Ljava/lang/Object;)V // method@00a7 +003838: 6e10 dc00 0300 |0014: invoke-virtual {v3}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@00dc +00383e: 0c00 |0017: move-result-object v0 +003840: 6e40 d800 0354 |0018: invoke-virtual {v3, v0, v4, v5}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00d8 +003846: 0c00 |001b: move-result-object v0 +003848: 2201 3400 |001c: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0034 +00384c: 7020 d200 0100 |001e: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +003852: 1101 |0021: return-object v1 + catches : (none) + positions : + 0x0000 line=367 + 0x0014 line=368 + 0x001c line=369 + locals : + 0x001c - 0x0022 reg=0 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x0022 reg=3 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0022 reg=4 methodName Ljava/lang/String; + 0x0000 - 0x0022 reg=5 methodType Ljava/lang/invoke/MethodType; + 0x0000 - 0x0022 reg=6 arityArgs [Ljava/lang/Class; + + #4 : (in LTestVariableArityLinkerMethod;) + name : 'bsmWithDoubleArray' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[D)Ljava/lang/invoke/CallSite;' + access : 0x008a (PRIVATE STATIC VARARGS) + code - + registers : 7 + ins : 4 + outs : 4 + insns size : 34 16-bit code units +003854: |[003854] TestVariableArityLinkerMethod.bsmWithDoubleArray:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[D)Ljava/lang/invoke/CallSite; +003864: 1a00 fb00 |0000: const-string v0, "bsmWithDoubleArray" // string@00fb +003868: 1241 |0002: const/4 v1, #int 4 // #4 +00386a: 2311 4800 |0003: new-array v1, v1, [Ljava/lang/Object; // type@0048 +00386e: 1202 |0005: const/4 v2, #int 0 // #0 +003870: 4d03 0102 |0006: aput-object v3, v1, v2 +003874: 1212 |0008: const/4 v2, #int 1 // #1 +003876: 4d04 0102 |0009: aput-object v4, v1, v2 +00387a: 1222 |000b: const/4 v2, #int 2 // #2 +00387c: 4d05 0102 |000c: aput-object v5, v1, v2 +003880: 1232 |000e: const/4 v2, #int 3 // #3 +003882: 4d06 0102 |000f: aput-object v6, v1, v2 +003886: 7120 a700 1000 |0011: invoke-static {v0, v1}, LTestVariableArityLinkerMethod;.printBsmArgs:(Ljava/lang/String;[Ljava/lang/Object;)V // method@00a7 +00388c: 6e10 dc00 0300 |0014: invoke-virtual {v3}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@00dc +003892: 0c00 |0017: move-result-object v0 +003894: 6e40 d800 0354 |0018: invoke-virtual {v3, v0, v4, v5}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00d8 +00389a: 0c00 |001b: move-result-object v0 +00389c: 2201 3400 |001c: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0034 +0038a0: 7020 d200 0100 |001e: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +0038a6: 1101 |0021: return-object v1 + catches : (none) + positions : + 0x0000 line=332 + 0x0014 line=333 + 0x001c line=334 + locals : + 0x001c - 0x0022 reg=0 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x0022 reg=3 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0022 reg=4 methodName Ljava/lang/String; + 0x0000 - 0x0022 reg=5 methodType Ljava/lang/invoke/MethodType; + 0x0000 - 0x0022 reg=6 arityArgs [D + + #5 : (in LTestVariableArityLinkerMethod;) + name : 'bsmWithFloatAndLongArray' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;F[J)Ljava/lang/invoke/CallSite;' + access : 0x008a (PRIVATE STATIC VARARGS) + code - + registers : 9 + ins : 5 + outs : 4 + insns size : 41 16-bit code units +0038a8: |[0038a8] TestVariableArityLinkerMethod.bsmWithFloatAndLongArray:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;F[J)Ljava/lang/invoke/CallSite; +0038b8: 1a00 fc00 |0000: const-string v0, "bsmWithFloatAndLongArray" // string@00fc +0038bc: 1251 |0002: const/4 v1, #int 5 // #5 +0038be: 2311 4800 |0003: new-array v1, v1, [Ljava/lang/Object; // type@0048 +0038c2: 1202 |0005: const/4 v2, #int 0 // #0 +0038c4: 4d04 0102 |0006: aput-object v4, v1, v2 +0038c8: 1212 |0008: const/4 v2, #int 1 // #1 +0038ca: 4d05 0102 |0009: aput-object v5, v1, v2 +0038ce: 1222 |000b: const/4 v2, #int 2 // #2 +0038d0: 4d06 0102 |000c: aput-object v6, v1, v2 +0038d4: 7110 ba00 0700 |000e: invoke-static {v7}, Ljava/lang/Float;.valueOf:(F)Ljava/lang/Float; // method@00ba +0038da: 0c02 |0011: move-result-object v2 +0038dc: 1233 |0012: const/4 v3, #int 3 // #3 +0038de: 4d02 0103 |0013: aput-object v2, v1, v3 +0038e2: 1242 |0015: const/4 v2, #int 4 // #4 +0038e4: 4d08 0102 |0016: aput-object v8, v1, v2 +0038e8: 7120 a700 1000 |0018: invoke-static {v0, v1}, LTestVariableArityLinkerMethod;.printBsmArgs:(Ljava/lang/String;[Ljava/lang/Object;)V // method@00a7 +0038ee: 6e10 dc00 0400 |001b: invoke-virtual {v4}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@00dc +0038f4: 0c00 |001e: move-result-object v0 +0038f6: 6e40 d800 0465 |001f: invoke-virtual {v4, v0, v5, v6}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00d8 +0038fc: 0c00 |0022: move-result-object v0 +0038fe: 2201 3400 |0023: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0034 +003902: 7020 d200 0100 |0025: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +003908: 1101 |0028: return-object v1 + catches : (none) + positions : + 0x0000 line=257 + 0x000e line=258 + 0x0018 line=257 + 0x001b line=259 + 0x0023 line=260 + locals : + 0x0023 - 0x0029 reg=0 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x0029 reg=4 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0029 reg=5 methodName Ljava/lang/String; + 0x0000 - 0x0029 reg=6 methodType Ljava/lang/invoke/MethodType; + 0x0000 - 0x0029 reg=7 extraArg F + 0x0000 - 0x0029 reg=8 arityArgs [J + + #6 : (in LTestVariableArityLinkerMethod;) + name : 'bsmWithIntAndStringArray' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I[Ljava/lang/String;)Ljava/lang/invoke/CallSite;' + access : 0x008a (PRIVATE STATIC VARARGS) + code - + registers : 9 + ins : 5 + outs : 4 + insns size : 41 16-bit code units +00390c: |[00390c] TestVariableArityLinkerMethod.bsmWithIntAndStringArray:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I[Ljava/lang/String;)Ljava/lang/invoke/CallSite; +00391c: 1a00 fd00 |0000: const-string v0, "bsmWithIntAndStringArray" // string@00fd +003920: 1251 |0002: const/4 v1, #int 5 // #5 +003922: 2311 4800 |0003: new-array v1, v1, [Ljava/lang/Object; // type@0048 +003926: 1202 |0005: const/4 v2, #int 0 // #0 +003928: 4d04 0102 |0006: aput-object v4, v1, v2 +00392c: 1212 |0008: const/4 v2, #int 1 // #1 +00392e: 4d05 0102 |0009: aput-object v5, v1, v2 +003932: 1222 |000b: const/4 v2, #int 2 // #2 +003934: 4d06 0102 |000c: aput-object v6, v1, v2 +003938: 7110 bd00 0700 |000e: invoke-static {v7}, Ljava/lang/Integer;.valueOf:(I)Ljava/lang/Integer; // method@00bd +00393e: 0c02 |0011: move-result-object v2 +003940: 1233 |0012: const/4 v3, #int 3 // #3 +003942: 4d02 0103 |0013: aput-object v2, v1, v3 +003946: 1242 |0015: const/4 v2, #int 4 // #4 +003948: 4d08 0102 |0016: aput-object v8, v1, v2 +00394c: 7120 a700 1000 |0018: invoke-static {v0, v1}, LTestVariableArityLinkerMethod;.printBsmArgs:(Ljava/lang/String;[Ljava/lang/Object;)V // method@00a7 +003952: 6e10 dc00 0400 |001b: invoke-virtual {v4}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@00dc +003958: 0c00 |001e: move-result-object v0 +00395a: 6e40 d800 0465 |001f: invoke-virtual {v4, v0, v5, v6}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00d8 +003960: 0c00 |0022: move-result-object v0 +003962: 2201 3400 |0023: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0034 +003966: 7020 d200 0100 |0025: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +00396c: 1101 |0028: return-object v1 + catches : (none) + positions : + 0x0000 line=133 + 0x000e line=138 + 0x0018 line=133 + 0x001b line=140 + 0x0023 line=141 + locals : + 0x0023 - 0x0029 reg=0 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x0029 reg=4 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0029 reg=5 methodName Ljava/lang/String; + 0x0000 - 0x0029 reg=6 methodType Ljava/lang/invoke/MethodType; + 0x0000 - 0x0029 reg=7 extraInt I + 0x0000 - 0x0029 reg=8 extraArityArgs [Ljava/lang/String; + + #7 : (in LTestVariableArityLinkerMethod;) + name : 'bsmWithLongAndIntArray' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;J[I)Ljava/lang/invoke/CallSite;' + access : 0x008a (PRIVATE STATIC VARARGS) + code - + registers : 10 + ins : 6 + outs : 4 + insns size : 41 16-bit code units +003970: |[003970] TestVariableArityLinkerMethod.bsmWithLongAndIntArray:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;J[I)Ljava/lang/invoke/CallSite; +003980: 1a00 fe00 |0000: const-string v0, "bsmWithLongAndIntArray" // string@00fe +003984: 1251 |0002: const/4 v1, #int 5 // #5 +003986: 2311 4800 |0003: new-array v1, v1, [Ljava/lang/Object; // type@0048 +00398a: 1202 |0005: const/4 v2, #int 0 // #0 +00398c: 4d04 0102 |0006: aput-object v4, v1, v2 +003990: 1212 |0008: const/4 v2, #int 1 // #1 +003992: 4d05 0102 |0009: aput-object v5, v1, v2 +003996: 1222 |000b: const/4 v2, #int 2 // #2 +003998: 4d06 0102 |000c: aput-object v6, v1, v2 +00399c: 7120 be00 8700 |000e: invoke-static {v7, v8}, Ljava/lang/Long;.valueOf:(J)Ljava/lang/Long; // method@00be +0039a2: 0c02 |0011: move-result-object v2 +0039a4: 1233 |0012: const/4 v3, #int 3 // #3 +0039a6: 4d02 0103 |0013: aput-object v2, v1, v3 +0039aa: 1242 |0015: const/4 v2, #int 4 // #4 +0039ac: 4d09 0102 |0016: aput-object v9, v1, v2 +0039b0: 7120 a700 1000 |0018: invoke-static {v0, v1}, LTestVariableArityLinkerMethod;.printBsmArgs:(Ljava/lang/String;[Ljava/lang/Object;)V // method@00a7 +0039b6: 6e10 dc00 0400 |001b: invoke-virtual {v4}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@00dc +0039bc: 0c00 |001e: move-result-object v0 +0039be: 6e40 d800 0465 |001f: invoke-virtual {v4, v0, v5, v6}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00d8 +0039c4: 0c00 |0022: move-result-object v0 +0039c6: 2201 3400 |0023: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0034 +0039ca: 7020 d200 0100 |0025: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +0039d0: 1101 |0028: return-object v1 + catches : (none) + positions : + 0x0000 line=219 + 0x001b line=220 + 0x0023 line=221 + locals : + 0x0023 - 0x0029 reg=0 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x0029 reg=4 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0029 reg=5 methodName Ljava/lang/String; + 0x0000 - 0x0029 reg=6 methodType Ljava/lang/invoke/MethodType; + 0x0000 - 0x0029 reg=7 extraArg J + 0x0000 - 0x0029 reg=9 arityArgs [I + + #8 : (in LTestVariableArityLinkerMethod;) + name : 'bsmWithStringArray' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/String;)Ljava/lang/invoke/CallSite;' + access : 0x008a (PRIVATE STATIC VARARGS) + code - + registers : 7 + ins : 4 + outs : 4 + insns size : 34 16-bit code units +0039d4: |[0039d4] TestVariableArityLinkerMethod.bsmWithStringArray:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/String;)Ljava/lang/invoke/CallSite; +0039e4: 1a00 ff00 |0000: const-string v0, "bsmWithStringArray" // string@00ff +0039e8: 1241 |0002: const/4 v1, #int 4 // #4 +0039ea: 2311 4800 |0003: new-array v1, v1, [Ljava/lang/Object; // type@0048 +0039ee: 1202 |0005: const/4 v2, #int 0 // #0 +0039f0: 4d03 0102 |0006: aput-object v3, v1, v2 +0039f4: 1212 |0008: const/4 v2, #int 1 // #1 +0039f6: 4d04 0102 |0009: aput-object v4, v1, v2 +0039fa: 1222 |000b: const/4 v2, #int 2 // #2 +0039fc: 4d05 0102 |000c: aput-object v5, v1, v2 +003a00: 1232 |000e: const/4 v2, #int 3 // #3 +003a02: 4d06 0102 |000f: aput-object v6, v1, v2 +003a06: 7120 a700 1000 |0011: invoke-static {v0, v1}, LTestVariableArityLinkerMethod;.printBsmArgs:(Ljava/lang/String;[Ljava/lang/Object;)V // method@00a7 +003a0c: 6e10 dc00 0300 |0014: invoke-virtual {v3}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@00dc +003a12: 0c00 |0017: move-result-object v0 +003a14: 6e40 d800 0354 |0018: invoke-virtual {v3, v0, v4, v5}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00d8 +003a1a: 0c00 |001b: move-result-object v0 +003a1c: 2201 3400 |001c: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0034 +003a20: 7020 d200 0100 |001e: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +003a26: 1101 |0021: return-object v1 + catches : (none) + positions : + 0x0000 line=61 + 0x0014 line=62 + 0x001c line=63 + locals : + 0x001c - 0x0022 reg=0 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x0022 reg=3 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0022 reg=4 methodName Ljava/lang/String; + 0x0000 - 0x0022 reg=5 methodType Ljava/lang/invoke/MethodType; + 0x0000 - 0x0022 reg=6 arityArgs [Ljava/lang/String; + + #9 : (in LTestVariableArityLinkerMethod;) + name : 'bsmWithWiderArray' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[J)Ljava/lang/invoke/CallSite;' + access : 0x000a (PRIVATE STATIC) + code - + registers : 7 + ins : 4 + outs : 4 + insns size : 34 16-bit code units +003a28: |[003a28] TestVariableArityLinkerMethod.bsmWithWiderArray:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[J)Ljava/lang/invoke/CallSite; +003a38: 1a00 0001 |0000: const-string v0, "bsmWithWiderArray" // string@0100 +003a3c: 1241 |0002: const/4 v1, #int 4 // #4 +003a3e: 2311 4800 |0003: new-array v1, v1, [Ljava/lang/Object; // type@0048 +003a42: 1202 |0005: const/4 v2, #int 0 // #0 +003a44: 4d03 0102 |0006: aput-object v3, v1, v2 +003a48: 1212 |0008: const/4 v2, #int 1 // #1 +003a4a: 4d04 0102 |0009: aput-object v4, v1, v2 +003a4e: 1222 |000b: const/4 v2, #int 2 // #2 +003a50: 4d05 0102 |000c: aput-object v5, v1, v2 +003a54: 1232 |000e: const/4 v2, #int 3 // #3 +003a56: 4d06 0102 |000f: aput-object v6, v1, v2 +003a5a: 7120 a700 1000 |0011: invoke-static {v0, v1}, LTestVariableArityLinkerMethod;.printBsmArgs:(Ljava/lang/String;[Ljava/lang/Object;)V // method@00a7 +003a60: 6e10 dc00 0300 |0014: invoke-virtual {v3}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@00dc +003a66: 0c00 |0017: move-result-object v0 +003a68: 6e40 d800 0354 |0018: invoke-virtual {v3, v0, v4, v5}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@00d8 +003a6e: 0c00 |001b: move-result-object v0 +003a70: 2201 3400 |001c: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0034 +003a74: 7020 d200 0100 |001e: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@00d2 +003a7a: 1101 |0021: return-object v1 + catches : (none) + positions : + 0x0000 line=447 + 0x0014 line=448 + 0x001c line=449 + locals : + 0x001c - 0x0022 reg=0 mh Ljava/lang/invoke/MethodHandle; + 0x0000 - 0x0022 reg=3 lookup Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0022 reg=4 methodName Ljava/lang/String; + 0x0000 - 0x0022 reg=5 methodType Ljava/lang/invoke/MethodType; + 0x0000 - 0x0022 reg=6 extraArgs [J + + #10 : (in LTestVariableArityLinkerMethod;) + name : 'methodA' + type : '()V' + access : 0x000a (PRIVATE STATIC) + code - + registers : 2 + ins : 0 + outs : 2 + insns size : 8 16-bit code units +003a94: |[003a94] TestVariableArityLinkerMethod.methodA:()V +003aa4: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003aa8: 1a01 7501 |0002: const-string v1, "methodA" // string@0175 +003aac: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +003ab2: 0e00 |0007: return-void catches : (none) positions : - 0x0000 line=102 + 0x0000 line=86 + 0x0007 line=87 locals : - #1 : (in Linvokecustom/InvokeCustom;) - name : '' + #11 : (in LTestVariableArityLinkerMethod;) + name : 'methodB' type : '()V' - access : 0x10001 (PUBLIC CONSTRUCTOR) + access : 0x000a (PRIVATE STATIC) code - registers : 2 - ins : 1 - outs : 1 - insns size : 7 16-bit code units -0009e0: |[0009e0] invokecustom.InvokeCustom.:()V -0009f0: 7010 2000 0100 |0000: invoke-direct {v1}, Linvokecustom/Super;.:()V // method@0020 -0009f6: 1200 |0003: const/4 v0, #int 0 // #0 -0009f8: 5910 0000 |0004: iput v0, v1, Linvokecustom/InvokeCustom;.fieldTest9:F // field@0000 -0009fc: 0e00 |0006: return-void + ins : 0 + outs : 2 + insns size : 8 16-bit code units +003ab4: |[003ab4] TestVariableArityLinkerMethod.methodB:()V +003ac4: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003ac8: 1a01 7601 |0002: const-string v1, "methodB" // string@0176 +003acc: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +003ad2: 0e00 |0007: return-void catches : (none) positions : - 0x0000 line=39 - 0x0003 line=115 - 0x0006 line=39 + 0x0000 line=105 + 0x0007 line=106 locals : - 0x0000 - 0x0007 reg=1 this Linvokecustom/InvokeCustom; - #2 : (in Linvokecustom/InvokeCustom;) - name : '' - type : '(I)V' - access : 0x10001 (PUBLIC CONSTRUCTOR) - code - - registers : 5 - ins : 2 - outs : 2 - insns size : 37 16-bit code units -000a00: |[000a00] invokecustom.InvokeCustom.:(I)V -000a10: 7010 2000 0300 |0000: invoke-direct {v3}, Linvokecustom/Super;.:()V // method@0020 -000a16: 1200 |0003: const/4 v0, #int 0 // #0 -000a18: 5930 0000 |0004: iput v0, v3, Linvokecustom/InvokeCustom;.fieldTest9:F // field@0000 -000a1c: 6200 0200 |0006: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000a20: 2201 1000 |0008: new-instance v1, Ljava/lang/StringBuilder; // type@0010 -000a24: 7010 3000 0100 |000a: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@0030 -000a2a: 1a02 1a00 |000d: const-string v2, "InvokeCustom.(" // string@001a -000a2e: 6e20 3600 2100 |000f: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000a34: 0c01 |0012: move-result-object v1 -000a36: 6e20 3300 4100 |0013: invoke-virtual {v1, v4}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033 -000a3c: 0c01 |0016: move-result-object v1 -000a3e: 1a02 0800 |0017: const-string v2, ")" // string@0008 -000a42: 6e20 3600 2100 |0019: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000a48: 0c01 |001c: move-result-object v1 -000a4a: 6e10 3700 0100 |001d: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037 -000a50: 0c01 |0020: move-result-object v1 -000a52: 6e20 2900 1000 |0021: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -000a58: 0e00 |0024: return-void - catches : (none) - positions : - 0x0000 line=40 - 0x0003 line=115 - 0x0006 line=41 - 0x0024 line=42 - locals : - 0x0000 - 0x0025 reg=3 this Linvokecustom/InvokeCustom; - 0x0000 - 0x0025 reg=4 (null) I - - #3 : (in Linvokecustom/InvokeCustom;) - name : 'bsmCreateCallSite' - type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite;' - access : 0x0009 (PUBLIC STATIC) - code - - registers : 7 - ins : 4 - outs : 2 - insns size : 36 16-bit code units -000a5c: |[000a5c] invokecustom.InvokeCustom.bsmCreateCallSite:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite; -000a6c: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000a70: 2201 1000 |0002: new-instance v1, Ljava/lang/StringBuilder; // type@0010 -000a74: 7010 3000 0100 |0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@0030 -000a7a: 1a02 6000 |0007: const-string v2, "bsmCreateCallSite [" // string@0060 -000a7e: 6e20 3600 2100 |0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000a84: 0c01 |000c: move-result-object v1 -000a86: 6e20 3500 6100 |000d: invoke-virtual {v1, v6}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@0035 -000a8c: 0c01 |0010: move-result-object v1 -000a8e: 1a02 5900 |0011: const-string v2, "]" // string@0059 -000a92: 6e20 3600 2100 |0013: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000a98: 0c01 |0016: move-result-object v1 -000a9a: 6e10 3700 0100 |0017: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037 -000aa0: 0c01 |001a: move-result-object v1 -000aa2: 6e20 2900 1000 |001b: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -000aa8: 2200 1400 |001e: new-instance v0, Ljava/lang/invoke/ConstantCallSite; // type@0014 -000aac: 7020 3800 6000 |0020: invoke-direct {v0, v6}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@0038 -000ab2: 1100 |0023: return-object v0 - catches : (none) - positions : - 0x0000 line=160 - 0x001e line=161 - locals : - 0x0000 - 0x0024 reg=3 (null) Ljava/lang/invoke/MethodHandles$Lookup; - 0x0000 - 0x0024 reg=4 (null) Ljava/lang/String; - 0x0000 - 0x0024 reg=5 (null) Ljava/lang/invoke/MethodType; - 0x0000 - 0x0024 reg=6 (null) Ljava/lang/invoke/MethodHandle; - - #4 : (in Linvokecustom/InvokeCustom;) - name : 'bsmLookupStatic' - type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;' - access : 0x0009 (PUBLIC STATIC) - code - - registers : 5 - ins : 3 - outs : 4 - insns size : 29 16-bit code units -000ab4: |[000ab4] invokecustom.InvokeCustom.bsmLookupStatic:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; -000ac4: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000ac8: 1a01 6200 |0002: const-string v1, "bsmLookupStatic []" // string@0062 -000acc: 6e20 2900 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -000ad2: 7100 4600 0000 |0007: invoke-static {}, Ljava/lang/invoke/MethodHandles;.lookup:()Ljava/lang/invoke/MethodHandles$Lookup; // method@0046 -000ad8: 0c00 |000a: move-result-object v0 -000ada: 6e10 4500 0000 |000b: invoke-virtual {v0}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@0045 -000ae0: 0c01 |000e: move-result-object v1 -000ae2: 6e40 4400 1043 |000f: invoke-virtual {v0, v1, v3, v4}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@0044 -000ae8: 0c00 |0012: move-result-object v0 -000aea: 2201 1400 |0013: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0014 -000aee: 6e20 3a00 4000 |0015: invoke-virtual {v0, v4}, Ljava/lang/invoke/MethodHandle;.asType:(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@003a -000af4: 0c00 |0018: move-result-object v0 -000af6: 7020 3800 0100 |0019: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@0038 -000afc: 1101 |001c: return-object v1 - catches : (none) - positions : - 0x0000 line=142 - 0x0007 line=143 - 0x000b line=144 - 0x0013 line=145 - locals : - 0x0000 - 0x001d reg=2 (null) Ljava/lang/invoke/MethodHandles$Lookup; - 0x0000 - 0x001d reg=3 (null) Ljava/lang/String; - 0x0000 - 0x001d reg=4 (null) Ljava/lang/invoke/MethodType; - - #5 : (in Linvokecustom/InvokeCustom;) - name : 'bsmLookupStaticWithExtraArgs' - type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;IJFD)Ljava/lang/invoke/CallSite;' - access : 0x0009 (PUBLIC STATIC) - code - - registers : 12 - ins : 9 - outs : 4 - insns size : 82 16-bit code units -000b00: |[000b00] invokecustom.InvokeCustom.bsmLookupStaticWithExtraArgs:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;IJFD)Ljava/lang/invoke/CallSite; -000b10: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000b14: 2201 1000 |0002: new-instance v1, Ljava/lang/StringBuilder; // type@0010 -000b18: 7010 3000 0100 |0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@0030 -000b1e: 1a02 6400 |0007: const-string v2, "bsmLookupStaticWithExtraArgs [" // string@0064 -000b22: 6e20 3600 2100 |0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000b28: 0c01 |000c: move-result-object v1 -000b2a: 6e20 3300 6100 |000d: invoke-virtual {v1, v6}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033 -000b30: 0c01 |0010: move-result-object v1 -000b32: 1a02 0900 |0011: const-string v2, ", " // string@0009 -000b36: 6e20 3600 2100 |0013: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000b3c: 0c01 |0016: move-result-object v1 -000b3e: 6e30 3400 7108 |0017: invoke-virtual {v1, v7, v8}, Ljava/lang/StringBuilder;.append:(J)Ljava/lang/StringBuilder; // method@0034 -000b44: 0c01 |001a: move-result-object v1 -000b46: 1a02 0900 |001b: const-string v2, ", " // string@0009 -000b4a: 6e20 3600 2100 |001d: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000b50: 0c01 |0020: move-result-object v1 -000b52: 6e20 3200 9100 |0021: invoke-virtual {v1, v9}, Ljava/lang/StringBuilder;.append:(F)Ljava/lang/StringBuilder; // method@0032 -000b58: 0c01 |0024: move-result-object v1 -000b5a: 1a02 0900 |0025: const-string v2, ", " // string@0009 -000b5e: 6e20 3600 2100 |0027: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000b64: 0c01 |002a: move-result-object v1 -000b66: 6e30 3100 a10b |002b: invoke-virtual {v1, v10, v11}, Ljava/lang/StringBuilder;.append:(D)Ljava/lang/StringBuilder; // method@0031 -000b6c: 0c01 |002e: move-result-object v1 -000b6e: 1a02 5900 |002f: const-string v2, "]" // string@0059 -000b72: 6e20 3600 2100 |0031: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000b78: 0c01 |0034: move-result-object v1 -000b7a: 6e10 3700 0100 |0035: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037 -000b80: 0c01 |0038: move-result-object v1 -000b82: 6e20 2900 1000 |0039: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -000b88: 7100 4600 0000 |003c: invoke-static {}, Ljava/lang/invoke/MethodHandles;.lookup:()Ljava/lang/invoke/MethodHandles$Lookup; // method@0046 -000b8e: 0c00 |003f: move-result-object v0 -000b90: 6e10 4500 0000 |0040: invoke-virtual {v0}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@0045 -000b96: 0c01 |0043: move-result-object v1 -000b98: 6e40 4400 1054 |0044: invoke-virtual {v0, v1, v4, v5}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@0044 -000b9e: 0c00 |0047: move-result-object v0 -000ba0: 2201 1400 |0048: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0014 -000ba4: 6e20 3a00 5000 |004a: invoke-virtual {v0, v5}, Ljava/lang/invoke/MethodHandle;.asType:(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@003a -000baa: 0c00 |004d: move-result-object v0 -000bac: 7020 3800 0100 |004e: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@0038 -000bb2: 1101 |0051: return-object v1 - catches : (none) - positions : - 0x0000 line=151 - 0x003c line=152 - 0x0040 line=153 - 0x0048 line=154 - locals : - 0x0000 - 0x0052 reg=3 (null) Ljava/lang/invoke/MethodHandles$Lookup; - 0x0000 - 0x0052 reg=4 (null) Ljava/lang/String; - 0x0000 - 0x0052 reg=5 (null) Ljava/lang/invoke/MethodType; - 0x0000 - 0x0052 reg=6 (null) I - 0x0000 - 0x0052 reg=7 (null) J - 0x0000 - 0x0052 reg=9 (null) F - 0x0000 - 0x0052 reg=10 (null) D - - #6 : (in Linvokecustom/InvokeCustom;) - name : 'bsmLookupTest9' - type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite;' - access : 0x0009 (PUBLIC STATIC) - code - - registers : 13 - ins : 10 - outs : 4 - insns size : 135 16-bit code units -000bb4: |[000bb4] invokecustom.InvokeCustom.bsmLookupTest9:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite; -000bc4: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000bc8: 2201 1000 |0002: new-instance v1, Ljava/lang/StringBuilder; // type@0010 -000bcc: 7010 3000 0100 |0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@0030 -000bd2: 1a02 6600 |0007: const-string v2, "bsmLookupTest9 [" // string@0066 -000bd6: 6e20 3600 2100 |0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000bdc: 0c01 |000c: move-result-object v1 -000bde: 6e20 3500 6100 |000d: invoke-virtual {v1, v6}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@0035 -000be4: 0c01 |0010: move-result-object v1 -000be6: 1a02 0900 |0011: const-string v2, ", " // string@0009 -000bea: 6e20 3600 2100 |0013: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000bf0: 0c01 |0016: move-result-object v1 -000bf2: 6e20 3500 7100 |0017: invoke-virtual {v1, v7}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@0035 -000bf8: 0c01 |001a: move-result-object v1 -000bfa: 1a02 0900 |001b: const-string v2, ", " // string@0009 -000bfe: 6e20 3600 2100 |001d: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000c04: 0c01 |0020: move-result-object v1 -000c06: 6e20 3500 8100 |0021: invoke-virtual {v1, v8}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@0035 -000c0c: 0c01 |0024: move-result-object v1 -000c0e: 1a02 0900 |0025: const-string v2, ", " // string@0009 -000c12: 6e20 3600 2100 |0027: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000c18: 0c01 |002a: move-result-object v1 -000c1a: 6e20 3500 9100 |002b: invoke-virtual {v1, v9}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@0035 -000c20: 0c01 |002e: move-result-object v1 -000c22: 1a02 5900 |002f: const-string v2, "]" // string@0059 -000c26: 6e20 3600 2100 |0031: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000c2c: 0c01 |0034: move-result-object v1 -000c2e: 6e10 3700 0100 |0035: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037 -000c34: 0c01 |0038: move-result-object v1 -000c36: 6e20 2900 1000 |0039: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -000c3c: 6200 0200 |003c: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000c40: 2201 1000 |003e: new-instance v1, Ljava/lang/StringBuilder; // type@0010 -000c44: 7010 3000 0100 |0040: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@0030 -000c4a: 6e20 3600 4100 |0043: invoke-virtual {v1, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000c50: 0c01 |0046: move-result-object v1 -000c52: 1a02 0100 |0047: const-string v2, " " // string@0001 -000c56: 6e20 3600 2100 |0049: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000c5c: 0c01 |004c: move-result-object v1 -000c5e: 6e20 3500 5100 |004d: invoke-virtual {v1, v5}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@0035 -000c64: 0c01 |0050: move-result-object v1 -000c66: 6e10 3700 0100 |0051: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037 -000c6c: 0c01 |0054: move-result-object v1 -000c6e: 6e20 2900 1000 |0055: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -000c74: 7120 0800 7600 |0058: invoke-static {v6, v7}, Linvokecustom/InvokeCustom;.checkStaticFieldTest9:(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V // method@0008 -000c7a: 2200 0700 |005b: new-instance v0, Linvokecustom/InvokeCustom; // type@0007 -000c7e: 7010 0100 0000 |005d: invoke-direct {v0}, Linvokecustom/InvokeCustom;.:()V // method@0001 -000c84: 7030 0700 8009 |0060: invoke-direct {v0, v8, v9}, Linvokecustom/InvokeCustom;.checkFieldTest9:(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V // method@0007 -000c8a: fa20 4000 0a00 2700 |0063: invoke-polymorphic {v10, v0}, Ljava/lang/invoke/MethodHandle;.invokeExact:([Ljava/lang/Object;)Ljava/lang/Object;, (Linvokecustom/InvokeCustom;)V // method@0040, proto@0027 -000c92: 1230 |0067: const/4 v0, #int 3 // #3 -000c94: fa20 4000 0b00 0500 |0068: invoke-polymorphic {v11, v0}, Ljava/lang/invoke/MethodHandle;.invokeExact:([Ljava/lang/Object;)Ljava/lang/Object;, (I)Linvokecustom/InvokeCustom; // method@0040, proto@0005 -000c9c: 0c00 |006c: move-result-object v0 -000c9e: fa20 3b00 0c00 2700 |006d: invoke-polymorphic {v12, v0}, Ljava/lang/invoke/MethodHandle;.invoke:([Ljava/lang/Object;)Ljava/lang/Object;, (Linvokecustom/InvokeCustom;)V // method@003b, proto@0027 -000ca6: 7100 4600 0000 |0071: invoke-static {}, Ljava/lang/invoke/MethodHandles;.lookup:()Ljava/lang/invoke/MethodHandles$Lookup; // method@0046 -000cac: 0c00 |0074: move-result-object v0 -000cae: 6e10 4500 0000 |0075: invoke-virtual {v0}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@0045 -000cb4: 0c01 |0078: move-result-object v1 -000cb6: 6e40 4400 1054 |0079: invoke-virtual {v0, v1, v4, v5}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@0044 -000cbc: 0c00 |007c: move-result-object v0 -000cbe: 2201 1400 |007d: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0014 -000cc2: 6e20 3a00 5000 |007f: invoke-virtual {v0, v5}, Ljava/lang/invoke/MethodHandle;.asType:(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@003a -000cc8: 0c00 |0082: move-result-object v0 -000cca: 7020 3800 0100 |0083: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.:(Ljava/lang/invoke/MethodHandle;)V // method@0038 -000cd0: 1101 |0086: return-object v1 - catches : (none) - positions : - 0x0000 line=170 - 0x003c line=172 - 0x0058 line=175 - 0x005b line=176 - 0x0060 line=177 - 0x0063 line=180 - 0x0067 line=182 - 0x006d line=183 - 0x0071 line=185 - 0x0075 line=186 - 0x007d line=187 - locals : - 0x0000 - 0x0087 reg=3 (null) Ljava/lang/invoke/MethodHandles$Lookup; - 0x0000 - 0x0087 reg=4 (null) Ljava/lang/String; - 0x0000 - 0x0087 reg=5 (null) Ljava/lang/invoke/MethodType; - 0x0000 - 0x0087 reg=6 (null) Ljava/lang/invoke/MethodHandle; - 0x0000 - 0x0087 reg=7 (null) Ljava/lang/invoke/MethodHandle; - 0x0000 - 0x0087 reg=8 (null) Ljava/lang/invoke/MethodHandle; - 0x0000 - 0x0087 reg=9 (null) Ljava/lang/invoke/MethodHandle; - 0x0000 - 0x0087 reg=10 (null) Ljava/lang/invoke/MethodHandle; - 0x0000 - 0x0087 reg=11 (null) Ljava/lang/invoke/MethodHandle; - 0x0000 - 0x0087 reg=12 (null) Ljava/lang/invoke/MethodHandle; - - #7 : (in Linvokecustom/InvokeCustom;) - name : 'checkFieldTest9' - type : '(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V' - access : 0x0002 (PRIVATE) - code - - registers : 9 - ins : 3 - outs : 3 - insns size : 82 16-bit code units -000cd4: |[000cd4] invokecustom.InvokeCustom.checkFieldTest9:(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V -000ce4: 1405 0ff0 6a20 |0000: const v5, #float 1.99e-19 // #206af00f -000cea: fa20 4000 6700 0100 |0003: invoke-polymorphic {v7, v6}, Ljava/lang/invoke/MethodHandle;.invokeExact:([Ljava/lang/Object;)Ljava/lang/Object;, (Linvokecustom/InvokeCustom;)F // method@0040, proto@0001 -000cf2: 0a00 |0007: move-result v0 -000cf4: fa30 4000 6805 2800 |0008: invoke-polymorphic {v8, v6, v5}, Ljava/lang/invoke/MethodHandle;.invokeExact:([Ljava/lang/Object;)Ljava/lang/Object;, (Linvokecustom/InvokeCustom;F)V // method@0040, proto@0028 -000cfc: fa20 4000 6700 0100 |000c: invoke-polymorphic {v7, v6}, Ljava/lang/invoke/MethodHandle;.invokeExact:([Ljava/lang/Object;)Ljava/lang/Object;, (Linvokecustom/InvokeCustom;)F // method@0040, proto@0001 -000d04: 0a01 |0010: move-result v1 -000d06: 6202 0200 |0011: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000d0a: 2203 1000 |0013: new-instance v3, Ljava/lang/StringBuilder; // type@0010 -000d0e: 7010 3000 0300 |0015: invoke-direct {v3}, Ljava/lang/StringBuilder;.:()V // method@0030 -000d14: 1a04 6800 |0018: const-string v4, "checkFieldTest9: old " // string@0068 -000d18: 6e20 3600 4300 |001a: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000d1e: 0c03 |001d: move-result-object v3 -000d20: 6e20 3200 0300 |001e: invoke-virtual {v3, v0}, Ljava/lang/StringBuilder;.append:(F)Ljava/lang/StringBuilder; // method@0032 -000d26: 0c00 |0021: move-result-object v0 -000d28: 1a03 0700 |0022: const-string v3, " new " // string@0007 -000d2c: 6e20 3600 3000 |0024: invoke-virtual {v0, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000d32: 0c00 |0027: move-result-object v0 -000d34: 6e20 3200 1000 |0028: invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;.append:(F)Ljava/lang/StringBuilder; // method@0032 -000d3a: 0c00 |002b: move-result-object v0 -000d3c: 1a03 0600 |002c: const-string v3, " expected " // string@0006 -000d40: 6e20 3600 3000 |002e: invoke-virtual {v0, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000d46: 0c00 |0031: move-result-object v0 -000d48: 6e20 3200 5000 |0032: invoke-virtual {v0, v5}, Ljava/lang/StringBuilder;.append:(F)Ljava/lang/StringBuilder; // method@0032 -000d4e: 0c00 |0035: move-result-object v0 -000d50: 1a03 0100 |0036: const-string v3, " " // string@0001 -000d54: 6e20 3600 3000 |0038: invoke-virtual {v0, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000d5a: 0c00 |003b: move-result-object v0 -000d5c: 6e10 3700 0000 |003c: invoke-virtual {v0}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037 -000d62: 0c00 |003f: move-result-object v0 -000d64: 6e20 2300 0200 |0040: invoke-virtual {v2, v0}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@0023 -000d6a: 6202 0200 |0043: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000d6e: 2d00 0105 |0045: cmpl-float v0, v1, v5 -000d72: 3900 0800 |0047: if-nez v0, 004f // +0008 -000d76: 1a00 4400 |0049: const-string v0, "OK" // string@0044 -000d7a: 6e20 2900 0200 |004b: invoke-virtual {v2, v0}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -000d80: 0e00 |004e: return-void -000d82: 1a00 1100 |004f: const-string v0, "ERROR" // string@0011 -000d86: 28fa |0051: goto 004b // -0006 - catches : (none) - positions : - 0x0003 line=120 - 0x0008 line=121 - 0x000c line=122 - 0x0011 line=123 - 0x0043 line=125 - 0x004e line=126 - 0x004f line=125 - locals : - 0x0000 - 0x0052 reg=6 this Linvokecustom/InvokeCustom; - 0x0000 - 0x0052 reg=7 (null) Ljava/lang/invoke/MethodHandle; - 0x0000 - 0x0052 reg=8 (null) Ljava/lang/invoke/MethodHandle; - - #8 : (in Linvokecustom/InvokeCustom;) - name : 'checkStaticFieldTest9' - type : '(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V' + #12 : (in LTestVariableArityLinkerMethod;) + name : 'methodC' + type : '()V' access : 0x000a (PRIVATE STATIC) code - - registers : 8 - ins : 2 - outs : 2 - insns size : 80 16-bit code units -000d88: |[000d88] invokecustom.InvokeCustom.checkStaticFieldTest9:(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V -000d98: 1405 1032 5476 |0000: const v5, #float 1.07596e+33 // #76543210 -000d9e: fa10 4000 0600 0200 |0003: invoke-polymorphic {v6}, Ljava/lang/invoke/MethodHandle;.invokeExact:([Ljava/lang/Object;)Ljava/lang/Object;, ()I // method@0040, proto@0002 -000da6: 0a00 |0007: move-result v0 -000da8: fa20 4000 5700 2500 |0008: invoke-polymorphic {v7, v5}, Ljava/lang/invoke/MethodHandle;.invokeExact:([Ljava/lang/Object;)Ljava/lang/Object;, (I)V // method@0040, proto@0025 -000db0: fa10 4000 0600 0200 |000c: invoke-polymorphic {v6}, Ljava/lang/invoke/MethodHandle;.invokeExact:([Ljava/lang/Object;)Ljava/lang/Object;, ()I // method@0040, proto@0002 -000db8: 0a01 |0010: move-result v1 -000dba: 6202 0200 |0011: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000dbe: 2203 1000 |0013: new-instance v3, Ljava/lang/StringBuilder; // type@0010 -000dc2: 7010 3000 0300 |0015: invoke-direct {v3}, Ljava/lang/StringBuilder;.:()V // method@0030 -000dc8: 1a04 6a00 |0018: const-string v4, "checkStaticFieldTest9: old " // string@006a -000dcc: 6e20 3600 4300 |001a: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000dd2: 0c03 |001d: move-result-object v3 -000dd4: 6e20 3300 0300 |001e: invoke-virtual {v3, v0}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033 -000dda: 0c00 |0021: move-result-object v0 -000ddc: 1a03 0700 |0022: const-string v3, " new " // string@0007 -000de0: 6e20 3600 3000 |0024: invoke-virtual {v0, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000de6: 0c00 |0027: move-result-object v0 -000de8: 6e20 3300 1000 |0028: invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033 -000dee: 0c00 |002b: move-result-object v0 -000df0: 1a03 0600 |002c: const-string v3, " expected " // string@0006 -000df4: 6e20 3600 3000 |002e: invoke-virtual {v0, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000dfa: 0c00 |0031: move-result-object v0 -000dfc: 6e20 3300 5000 |0032: invoke-virtual {v0, v5}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033 -000e02: 0c00 |0035: move-result-object v0 -000e04: 1a03 0100 |0036: const-string v3, " " // string@0001 -000e08: 6e20 3600 3000 |0038: invoke-virtual {v0, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -000e0e: 0c00 |003b: move-result-object v0 -000e10: 6e10 3700 0000 |003c: invoke-virtual {v0}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037 -000e16: 0c00 |003f: move-result-object v0 -000e18: 6e20 2300 0200 |0040: invoke-virtual {v2, v0}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@0023 -000e1e: 6202 0200 |0043: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000e22: 3351 0800 |0045: if-ne v1, v5, 004d // +0008 -000e26: 1a00 4400 |0047: const-string v0, "OK" // string@0044 -000e2a: 6e20 2900 0200 |0049: invoke-virtual {v2, v0}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -000e30: 0e00 |004c: return-void -000e32: 1a00 1100 |004d: const-string v0, "ERROR" // string@0011 -000e36: 28fa |004f: goto 0049 // -0006 - catches : (none) - positions : - 0x0003 line=107 - 0x0008 line=108 - 0x000c line=109 - 0x0011 line=110 - 0x0043 line=112 - 0x004c line=113 - 0x004d line=112 - locals : - 0x0000 - 0x0050 reg=6 (null) Ljava/lang/invoke/MethodHandle; - 0x0000 - 0x0050 reg=7 (null) Ljava/lang/invoke/MethodHandle; - - #9 : (in Linvokecustom/InvokeCustom;) - name : 'lambda$lambdaTest$0' - type : '(Ljava/lang/String;)Z' - access : 0x100a (PRIVATE STATIC SYNTHETIC) - code - - registers : 3 - ins : 1 + registers : 2 + ins : 0 outs : 2 - insns size : 11 16-bit code units -000e38: |[000e38] invokecustom.InvokeCustom.lambda$lambdaTest$0:(Ljava/lang/String;)Z -000e48: 1a00 4500 |0000: const-string v0, "One" // string@0045 -000e4c: 6e10 2f00 0200 |0002: invoke-virtual {v2}, Ljava/lang/String;.trim:()Ljava/lang/String; // method@002f -000e52: 0c01 |0005: move-result-object v1 -000e54: 6e20 2e00 1000 |0006: invoke-virtual {v0, v1}, Ljava/lang/String;.equals:(Ljava/lang/Object;)Z // method@002e -000e5a: 0a00 |0009: move-result v0 -000e5c: 0f00 |000a: return v0 + insns size : 8 16-bit code units +003ad4: |[003ad4] TestVariableArityLinkerMethod.methodC:()V +003ae4: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003ae8: 1a01 7701 |0002: const-string v1, "methodC" // string@0177 +003aec: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +003af2: 0e00 |0007: return-void catches : (none) positions : - 0x0000 line=192 + 0x0000 line=123 + 0x0007 line=124 locals : - 0x0000 - 0x000b reg=2 (null) Ljava/lang/String; - #10 : (in Linvokecustom/InvokeCustom;) - name : 'lambdaTest' + #13 : (in LTestVariableArityLinkerMethod;) + name : 'methodD' type : '()V' - access : 0x0009 (PUBLIC STATIC) + access : 0x000a (PRIVATE STATIC) code - - registers : 3 + registers : 2 ins : 0 outs : 2 - insns size : 71 16-bit code units -000e60: |[000e60] invokecustom.InvokeCustom.lambdaTest:()V -000e70: 1230 |0000: const/4 v0, #int 3 // #3 -000e72: 2300 2500 |0001: new-array v0, v0, [Ljava/lang/String; // type@0025 -000e76: 1201 |0003: const/4 v1, #int 0 // #0 -000e78: 1a02 4900 |0004: const-string v2, "Three" // string@0049 -000e7c: 4d02 0001 |0006: aput-object v2, v0, v1 -000e80: 1211 |0008: const/4 v1, #int 1 // #1 -000e82: 1a02 4500 |0009: const-string v2, "One" // string@0045 -000e86: 4d02 0001 |000b: aput-object v2, v0, v1 -000e8a: 1221 |000d: const/4 v1, #int 2 // #2 -000e8c: 1a02 1600 |000e: const-string v2, "FortyTwo" // string@0016 -000e90: 4d02 0001 |0010: aput-object v2, v0, v1 -000e94: 7110 4700 0000 |0012: invoke-static {v0}, Ljava/util/Arrays;.asList:([Ljava/lang/Object;)Ljava/util/List; // method@0047 -000e9a: 0c01 |0015: move-result-object v1 -000e9c: 7210 4800 0100 |0016: invoke-interface {v1}, Ljava/util/List;.stream:()Ljava/util/stream/Stream; // method@0048 -000ea2: 0c00 |0019: move-result-object v0 -000ea4: fc00 0000 0000 |001a: invoke-custom {}, call_site@0000 -000eaa: 0c02 |001d: move-result-object v2 -000eac: 7220 4a00 2000 |001e: invoke-interface {v0, v2}, Ljava/util/stream/Stream;.filter:(Ljava/util/function/Predicate;)Ljava/util/stream/Stream; // method@004a -000eb2: 0c00 |0021: move-result-object v0 -000eb4: fc00 0100 0000 |0022: invoke-custom {}, call_site@0001 -000eba: 0c02 |0025: move-result-object v2 -000ebc: 7220 4d00 2000 |0026: invoke-interface {v0, v2}, Ljava/util/stream/Stream;.map:(Ljava/util/function/Function;)Ljava/util/stream/Stream; // method@004d -000ec2: 0c00 |0029: move-result-object v0 -000ec4: 7210 4b00 0000 |002a: invoke-interface {v0}, Ljava/util/stream/Stream;.findAny:()Ljava/util/Optional; // method@004b -000eca: 0c00 |002d: move-result-object v0 -000ecc: 1a02 0000 |002e: const-string v2, "" // string@0000 -000ed0: 6e20 4900 2000 |0030: invoke-virtual {v0, v2}, Ljava/util/Optional;.orElse:(Ljava/lang/Object;)Ljava/lang/Object; // method@0049 -000ed6: 0c00 |0033: move-result-object v0 -000ed8: 1f00 0f00 |0034: check-cast v0, Ljava/lang/String; // type@000f -000edc: 7210 4800 0100 |0036: invoke-interface {v1}, Ljava/util/List;.stream:()Ljava/util/stream/Stream; // method@0048 -000ee2: 0c00 |0039: move-result-object v0 -000ee4: 6201 0200 |003a: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000ee8: 6e10 2c00 0100 |003c: invoke-virtual {v1}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@002c -000eee: fc10 0200 0100 |003f: invoke-custom {v1}, call_site@0002 -000ef4: 0c01 |0042: move-result-object v1 -000ef6: 7220 4c00 1000 |0043: invoke-interface {v0, v1}, Ljava/util/stream/Stream;.forEach:(Ljava/util/function/Consumer;)V // method@004c -000efc: 0e00 |0046: return-void - catches : (none) - positions : - 0x0000 line=191 - 0x0016 line=192 - 0x0026 line=193 - 0x0036 line=194 - 0x0046 line=195 - locals : - - #11 : (in Linvokecustom/InvokeCustom;) - name : 'main' - type : '([Ljava/lang/String;)V' - access : 0x0009 (PUBLIC STATIC) - code - - registers : 1 - ins : 1 - outs : 0 - insns size : 28 16-bit code units -000f00: |[000f00] invokecustom.InvokeCustom.main:([Ljava/lang/String;)V -000f10: 7100 1700 0000 |0000: invoke-static {}, Linvokecustom/InvokeCustom;.test1:()V // method@0017 -000f16: 7100 1800 0000 |0003: invoke-static {}, Linvokecustom/InvokeCustom;.test2:()V // method@0018 -000f1c: 7100 1900 0000 |0006: invoke-static {}, Linvokecustom/InvokeCustom;.test3:()V // method@0019 -000f22: 7100 1a00 0000 |0009: invoke-static {}, Linvokecustom/InvokeCustom;.test4:()V // method@001a -000f28: 7100 1b00 0000 |000c: invoke-static {}, Linvokecustom/InvokeCustom;.test5:()V // method@001b -000f2e: 7100 1c00 0000 |000f: invoke-static {}, Linvokecustom/InvokeCustom;.test6:()V // method@001c -000f34: 7100 1d00 0000 |0012: invoke-static {}, Linvokecustom/InvokeCustom;.test7:()V // method@001d -000f3a: 7100 1e00 0000 |0015: invoke-static {}, Linvokecustom/InvokeCustom;.test8:()V // method@001e -000f40: 7100 1f00 0000 |0018: invoke-static {}, Linvokecustom/InvokeCustom;.test9:()V // method@001f -000f46: 0e00 |001b: return-void + insns size : 8 16-bit code units +003af4: |[003af4] TestVariableArityLinkerMethod.methodD:()V +003b04: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003b08: 1a01 7801 |0002: const-string v1, "methodD" // string@0178 +003b0c: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +003b12: 0e00 |0007: return-void catches : (none) positions : + 0x0000 line=166 + 0x0007 line=167 locals : - #12 : (in Linvokecustom/InvokeCustom;) - name : 'targetMethodTest1' + #14 : (in LTestVariableArityLinkerMethod;) + name : 'methodE' type : '()V' access : 0x000a (PRIVATE STATIC) code - @@ -681,71 +4837,39 @@ Class #1 - ins : 0 outs : 2 insns size : 8 16-bit code units -000f48: |[000f48] invokecustom.InvokeCustom.targetMethodTest1:()V -000f58: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000f5c: 1a01 1700 |0002: const-string v1, "Hello World!" // string@0017 -000f60: 6e20 2900 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -000f66: 0e00 |0007: return-void +003b14: |[003b14] TestVariableArityLinkerMethod.methodE:()V +003b24: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003b28: 1a01 7901 |0002: const-string v1, "methodE" // string@0179 +003b2c: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +003b32: 0e00 |0007: return-void catches : (none) positions : - 0x0000 line=45 - 0x0007 line=46 + 0x0000 line=189 + 0x0007 line=190 locals : - #13 : (in Linvokecustom/InvokeCustom;) - name : 'targetMethodTest2' - type : '(ZBCSIFJDLjava/lang/String;)V' + #15 : (in LTestVariableArityLinkerMethod;) + name : 'methodF' + type : '()V' access : 0x000a (PRIVATE STATIC) code - - registers : 13 - ins : 11 - outs : 3 - insns size : 46 16-bit code units -000f68: |[000f68] invokecustom.InvokeCustom.targetMethodTest2:(ZBCSIFJDLjava/lang/String;)V -000f78: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000f7c: 6e20 2a00 2000 |0002: invoke-virtual {v0, v2}, Ljava/io/PrintStream;.println:(Z)V // method@002a -000f82: 6200 0200 |0005: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000f86: 6e20 2700 3000 |0007: invoke-virtual {v0, v3}, Ljava/io/PrintStream;.println:(I)V // method@0027 -000f8c: 6200 0200 |000a: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000f90: 6e20 2400 4000 |000c: invoke-virtual {v0, v4}, Ljava/io/PrintStream;.println:(C)V // method@0024 -000f96: 6200 0200 |000f: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000f9a: 6e20 2700 5000 |0011: invoke-virtual {v0, v5}, Ljava/io/PrintStream;.println:(I)V // method@0027 -000fa0: 6200 0200 |0014: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000fa4: 6e20 2700 6000 |0016: invoke-virtual {v0, v6}, Ljava/io/PrintStream;.println:(I)V // method@0027 -000faa: 6200 0200 |0019: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000fae: 6e20 2600 7000 |001b: invoke-virtual {v0, v7}, Ljava/io/PrintStream;.println:(F)V // method@0026 -000fb4: 6200 0200 |001e: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000fb8: 6e30 2800 8009 |0020: invoke-virtual {v0, v8, v9}, Ljava/io/PrintStream;.println:(J)V // method@0028 -000fbe: 6200 0200 |0023: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000fc2: 6e30 2500 a00b |0025: invoke-virtual {v0, v10, v11}, Ljava/io/PrintStream;.println:(D)V // method@0025 -000fc8: 6200 0200 |0028: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000fcc: 6e20 2900 c000 |002a: invoke-virtual {v0, v12}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -000fd2: 0e00 |002d: return-void - catches : (none) - positions : - 0x0000 line=50 - 0x0005 line=51 - 0x000a line=52 - 0x000f line=53 - 0x0014 line=54 - 0x0019 line=55 - 0x001e line=56 - 0x0023 line=57 - 0x0028 line=58 - 0x002d line=59 + registers : 2 + ins : 0 + outs : 2 + insns size : 8 16-bit code units +003b34: |[003b34] TestVariableArityLinkerMethod.methodF:()V +003b44: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003b48: 1a01 7a01 |0002: const-string v1, "methodF" // string@017a +003b4c: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +003b52: 0e00 |0007: return-void + catches : (none) + positions : + 0x0000 line=209 + 0x0007 line=210 locals : - 0x0000 - 0x002e reg=2 (null) Z - 0x0000 - 0x002e reg=3 (null) B - 0x0000 - 0x002e reg=4 (null) C - 0x0000 - 0x002e reg=5 (null) S - 0x0000 - 0x002e reg=6 (null) I - 0x0000 - 0x002e reg=7 (null) F - 0x0000 - 0x002e reg=8 (null) J - 0x0000 - 0x002e reg=10 (null) D - 0x0000 - 0x002e reg=12 (null) Ljava/lang/String; - #14 : (in Linvokecustom/InvokeCustom;) - name : 'targetMethodTest3' + #16 : (in LTestVariableArityLinkerMethod;) + name : 'methodG' type : '()V' access : 0x000a (PRIVATE STATIC) code - @@ -753,237 +4877,39 @@ Class #1 - ins : 0 outs : 2 insns size : 8 16-bit code units -000fd4: |[000fd4] invokecustom.InvokeCustom.targetMethodTest3:()V -000fe4: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -000fe8: 1a01 8800 |0002: const-string v1, "targetMethodTest3 from InvokeCustom" // string@0088 -000fec: 6e20 2900 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -000ff2: 0e00 |0007: return-void +003b54: |[003b54] TestVariableArityLinkerMethod.methodG:()V +003b64: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003b68: 1a01 7b01 |0002: const-string v1, "methodG" // string@017b +003b6c: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +003b72: 0e00 |0007: return-void catches : (none) positions : - 0x0000 line=62 - 0x0007 line=63 + 0x0000 line=247 + 0x0007 line=248 locals : - #15 : (in Linvokecustom/InvokeCustom;) - name : 'targetMethodTest5' - type : '(III)I' - access : 0x0009 (PUBLIC STATIC) - code - - registers : 7 - ins : 3 - outs : 2 - insns size : 83 16-bit code units -000ff4: |[000ff4] invokecustom.InvokeCustom.targetMethodTest5:(III)I -001004: 9000 0405 |0000: add-int v0, v4, v5 -001008: 6201 0200 |0002: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -00100c: 2202 1000 |0004: new-instance v2, Ljava/lang/StringBuilder; // type@0010 -001010: 7010 3000 0200 |0006: invoke-direct {v2}, Ljava/lang/StringBuilder;.:()V // method@0030 -001016: 1a03 8d00 |0009: const-string v3, "targetMethodTest5 " // string@008d -00101a: 6e20 3600 3200 |000b: invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -001020: 0c02 |000e: move-result-object v2 -001022: 6e20 3300 4200 |000f: invoke-virtual {v2, v4}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033 -001028: 0c02 |0012: move-result-object v2 -00102a: 1a03 0400 |0013: const-string v3, " + " // string@0004 -00102e: 6e20 3600 3200 |0015: invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -001034: 0c02 |0018: move-result-object v2 -001036: 6e20 3300 5200 |0019: invoke-virtual {v2, v5}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033 -00103c: 0c02 |001c: move-result-object v2 -00103e: 1a03 0500 |001d: const-string v3, " = " // string@0005 -001042: 6e20 3600 3200 |001f: invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -001048: 0c02 |0022: move-result-object v2 -00104a: 6e20 3300 0200 |0023: invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033 -001050: 0c02 |0026: move-result-object v2 -001052: 6e10 3700 0200 |0027: invoke-virtual {v2}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037 -001058: 0c02 |002a: move-result-object v2 -00105a: 6e20 2900 2100 |002b: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -001060: 3260 2400 |002e: if-eq v0, v6, 0052 // +0024 -001064: 6201 0200 |0030: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -001068: 2202 1000 |0032: new-instance v2, Ljava/lang/StringBuilder; // type@0010 -00106c: 7010 3000 0200 |0034: invoke-direct {v2}, Ljava/lang/StringBuilder;.:()V // method@0030 -001072: 1a03 1400 |0037: const-string v3, "Failed " // string@0014 -001076: 6e20 3600 3200 |0039: invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -00107c: 0c02 |003c: move-result-object v2 -00107e: 6e20 3300 0200 |003d: invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033 -001084: 0c02 |0040: move-result-object v2 -001086: 1a03 0200 |0041: const-string v3, " != " // string@0002 -00108a: 6e20 3600 3200 |0043: invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -001090: 0c02 |0046: move-result-object v2 -001092: 6e20 3300 6200 |0047: invoke-virtual {v2, v6}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033 -001098: 0c02 |004a: move-result-object v2 -00109a: 6e10 3700 0200 |004b: invoke-virtual {v2}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037 -0010a0: 0c02 |004e: move-result-object v2 -0010a2: 6e20 2900 2100 |004f: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -0010a8: 0f00 |0052: return v0 - catches : (none) - positions : - 0x0000 line=72 - 0x0002 line=73 - 0x002e line=74 - 0x0030 line=75 - 0x0052 line=77 - locals : - 0x0000 - 0x0053 reg=4 (null) I - 0x0000 - 0x0053 reg=5 (null) I - 0x0000 - 0x0053 reg=6 (null) I - - #16 : (in Linvokecustom/InvokeCustom;) - name : 'targetMethodTest6' - type : '(JJJ)J' - access : 0x0009 (PUBLIC STATIC) - code - - registers : 12 - ins : 6 - outs : 3 - insns size : 85 16-bit code units -0010ac: |[0010ac] invokecustom.InvokeCustom.targetMethodTest6:(JJJ)J -0010bc: 9b00 0608 |0000: add-long v0, v6, v8 -0010c0: 6202 0200 |0002: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -0010c4: 2203 1000 |0004: new-instance v3, Ljava/lang/StringBuilder; // type@0010 -0010c8: 7010 3000 0300 |0006: invoke-direct {v3}, Ljava/lang/StringBuilder;.:()V // method@0030 -0010ce: 1a04 9000 |0009: const-string v4, "targetMethodTest6 " // string@0090 -0010d2: 6e20 3600 4300 |000b: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -0010d8: 0c03 |000e: move-result-object v3 -0010da: 6e30 3400 6307 |000f: invoke-virtual {v3, v6, v7}, Ljava/lang/StringBuilder;.append:(J)Ljava/lang/StringBuilder; // method@0034 -0010e0: 0c03 |0012: move-result-object v3 -0010e2: 1a04 0400 |0013: const-string v4, " + " // string@0004 -0010e6: 6e20 3600 4300 |0015: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -0010ec: 0c03 |0018: move-result-object v3 -0010ee: 6e30 3400 8309 |0019: invoke-virtual {v3, v8, v9}, Ljava/lang/StringBuilder;.append:(J)Ljava/lang/StringBuilder; // method@0034 -0010f4: 0c03 |001c: move-result-object v3 -0010f6: 1a04 0500 |001d: const-string v4, " = " // string@0005 -0010fa: 6e20 3600 4300 |001f: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -001100: 0c03 |0022: move-result-object v3 -001102: 6e30 3400 0301 |0023: invoke-virtual {v3, v0, v1}, Ljava/lang/StringBuilder;.append:(J)Ljava/lang/StringBuilder; // method@0034 -001108: 0c03 |0026: move-result-object v3 -00110a: 6e10 3700 0300 |0027: invoke-virtual {v3}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037 -001110: 0c03 |002a: move-result-object v3 -001112: 6e20 2900 3200 |002b: invoke-virtual {v2, v3}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -001118: 3102 000a |002e: cmp-long v2, v0, v10 -00111c: 3802 2400 |0030: if-eqz v2, 0054 // +0024 -001120: 6202 0200 |0032: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -001124: 2203 1000 |0034: new-instance v3, Ljava/lang/StringBuilder; // type@0010 -001128: 7010 3000 0300 |0036: invoke-direct {v3}, Ljava/lang/StringBuilder;.:()V // method@0030 -00112e: 1a04 1400 |0039: const-string v4, "Failed " // string@0014 -001132: 6e20 3600 4300 |003b: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -001138: 0c03 |003e: move-result-object v3 -00113a: 6e30 3400 0301 |003f: invoke-virtual {v3, v0, v1}, Ljava/lang/StringBuilder;.append:(J)Ljava/lang/StringBuilder; // method@0034 -001140: 0c03 |0042: move-result-object v3 -001142: 1a04 0200 |0043: const-string v4, " != " // string@0002 -001146: 6e20 3600 4300 |0045: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -00114c: 0c03 |0048: move-result-object v3 -00114e: 6e30 3400 a30b |0049: invoke-virtual {v3, v10, v11}, Ljava/lang/StringBuilder;.append:(J)Ljava/lang/StringBuilder; // method@0034 -001154: 0c03 |004c: move-result-object v3 -001156: 6e10 3700 0300 |004d: invoke-virtual {v3}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037 -00115c: 0c03 |0050: move-result-object v3 -00115e: 6e20 2900 3200 |0051: invoke-virtual {v2, v3}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -001164: 1000 |0054: return-wide v0 - catches : (none) - positions : - 0x0000 line=81 - 0x0002 line=82 - 0x002e line=83 - 0x0032 line=84 - 0x0054 line=86 - locals : - 0x0000 - 0x0055 reg=6 (null) J - 0x0000 - 0x0055 reg=8 (null) J - 0x0000 - 0x0055 reg=10 (null) J - - #17 : (in Linvokecustom/InvokeCustom;) - name : 'targetMethodTest7' - type : '(FFD)D' - access : 0x0009 (PUBLIC STATIC) - code - - registers : 10 - ins : 4 - outs : 3 - insns size : 86 16-bit code units -001168: |[001168] invokecustom.InvokeCustom.targetMethodTest7:(FFD)D -001178: a800 0607 |0000: mul-float v0, v6, v7 -00117c: 8900 |0002: float-to-double v0, v0 -00117e: 6202 0200 |0003: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -001182: 2203 1000 |0005: new-instance v3, Ljava/lang/StringBuilder; // type@0010 -001186: 7010 3000 0300 |0007: invoke-direct {v3}, Ljava/lang/StringBuilder;.:()V // method@0030 -00118c: 1a04 9300 |000a: const-string v4, "targetMethodTest7 " // string@0093 -001190: 6e20 3600 4300 |000c: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -001196: 0c03 |000f: move-result-object v3 -001198: 6e20 3200 6300 |0010: invoke-virtual {v3, v6}, Ljava/lang/StringBuilder;.append:(F)Ljava/lang/StringBuilder; // method@0032 -00119e: 0c03 |0013: move-result-object v3 -0011a0: 1a04 0300 |0014: const-string v4, " * " // string@0003 -0011a4: 6e20 3600 4300 |0016: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -0011aa: 0c03 |0019: move-result-object v3 -0011ac: 6e20 3200 7300 |001a: invoke-virtual {v3, v7}, Ljava/lang/StringBuilder;.append:(F)Ljava/lang/StringBuilder; // method@0032 -0011b2: 0c03 |001d: move-result-object v3 -0011b4: 1a04 0500 |001e: const-string v4, " = " // string@0005 -0011b8: 6e20 3600 4300 |0020: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -0011be: 0c03 |0023: move-result-object v3 -0011c0: 6e30 3100 0301 |0024: invoke-virtual {v3, v0, v1}, Ljava/lang/StringBuilder;.append:(D)Ljava/lang/StringBuilder; // method@0031 -0011c6: 0c03 |0027: move-result-object v3 -0011c8: 6e10 3700 0300 |0028: invoke-virtual {v3}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037 -0011ce: 0c03 |002b: move-result-object v3 -0011d0: 6e20 2900 3200 |002c: invoke-virtual {v2, v3}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -0011d6: 2f02 0008 |002f: cmpl-double v2, v0, v8 -0011da: 3802 2400 |0031: if-eqz v2, 0055 // +0024 -0011de: 6202 0200 |0033: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -0011e2: 2203 1000 |0035: new-instance v3, Ljava/lang/StringBuilder; // type@0010 -0011e6: 7010 3000 0300 |0037: invoke-direct {v3}, Ljava/lang/StringBuilder;.:()V // method@0030 -0011ec: 1a04 1400 |003a: const-string v4, "Failed " // string@0014 -0011f0: 6e20 3600 4300 |003c: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -0011f6: 0c03 |003f: move-result-object v3 -0011f8: 6e30 3100 0301 |0040: invoke-virtual {v3, v0, v1}, Ljava/lang/StringBuilder;.append:(D)Ljava/lang/StringBuilder; // method@0031 -0011fe: 0c03 |0043: move-result-object v3 -001200: 1a04 0200 |0044: const-string v4, " != " // string@0002 -001204: 6e20 3600 4300 |0046: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -00120a: 0c03 |0049: move-result-object v3 -00120c: 6e30 3100 8309 |004a: invoke-virtual {v3, v8, v9}, Ljava/lang/StringBuilder;.append:(D)Ljava/lang/StringBuilder; // method@0031 -001212: 0c03 |004d: move-result-object v3 -001214: 6e10 3700 0300 |004e: invoke-virtual {v3}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037 -00121a: 0c03 |0051: move-result-object v3 -00121c: 6e20 2900 3200 |0052: invoke-virtual {v2, v3}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -001222: 1000 |0055: return-wide v0 - catches : (none) - positions : - 0x0000 line=90 - 0x0003 line=91 - 0x002f line=92 - 0x0033 line=93 - 0x0055 line=95 - locals : - 0x0000 - 0x0056 reg=6 (null) F - 0x0000 - 0x0056 reg=7 (null) F - 0x0000 - 0x0056 reg=8 (null) D - - #18 : (in Linvokecustom/InvokeCustom;) - name : 'targetMethodTest8' - type : '(Ljava/lang/String;)V' - access : 0x0009 (PUBLIC STATIC) + #17 : (in LTestVariableArityLinkerMethod;) + name : 'methodH' + type : '()V' + access : 0x000a (PRIVATE STATIC) code - - registers : 4 - ins : 1 + registers : 2 + ins : 0 outs : 2 - insns size : 25 16-bit code units -001224: |[001224] invokecustom.InvokeCustom.targetMethodTest8:(Ljava/lang/String;)V -001234: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -001238: 2201 1000 |0002: new-instance v1, Ljava/lang/StringBuilder; // type@0010 -00123c: 7010 3000 0100 |0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@0030 -001242: 1a02 9500 |0007: const-string v2, "targetMethodTest8 " // string@0095 -001246: 6e20 3600 2100 |0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -00124c: 0c01 |000c: move-result-object v1 -00124e: 6e20 3600 3100 |000d: invoke-virtual {v1, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -001254: 0c01 |0010: move-result-object v1 -001256: 6e10 3700 0100 |0011: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037 -00125c: 0c01 |0014: move-result-object v1 -00125e: 6e20 2900 1000 |0015: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -001264: 0e00 |0018: return-void + insns size : 8 16-bit code units +003b74: |[003b74] TestVariableArityLinkerMethod.methodH:()V +003b84: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003b88: 1a01 7c01 |0002: const-string v1, "methodH" // string@017c +003b8c: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +003b92: 0e00 |0007: return-void catches : (none) positions : - 0x0000 line=99 - 0x0018 line=100 + 0x0000 line=284 + 0x0007 line=285 locals : - 0x0000 - 0x0019 reg=3 (null) Ljava/lang/String; - #19 : (in Linvokecustom/InvokeCustom;) - name : 'targetMethodTest9' + #18 : (in LTestVariableArityLinkerMethod;) + name : 'methodI' type : '()V' access : 0x000a (PRIVATE STATIC) code - @@ -991,435 +4917,809 @@ Class #1 - ins : 0 outs : 2 insns size : 8 16-bit code units -001268: |[001268] invokecustom.InvokeCustom.targetMethodTest9:()V -001278: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -00127c: 1a01 9700 |0002: const-string v1, "targetMethodTest9()" // string@0097 -001280: 6e20 2900 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -001286: 0e00 |0007: return-void +003b94: |[003b94] TestVariableArityLinkerMethod.methodI:()V +003ba4: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003ba8: 1a01 7d01 |0002: const-string v1, "methodI" // string@017d +003bac: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +003bb2: 0e00 |0007: return-void catches : (none) positions : - 0x0000 line=133 - 0x0007 line=134 + 0x0000 line=323 + 0x0007 line=324 locals : - #20 : (in Linvokecustom/InvokeCustom;) - name : 'test1' + #19 : (in LTestVariableArityLinkerMethod;) + name : 'methodJ' type : '()V' - access : 0x0009 (PUBLIC STATIC) + access : 0x000a (PRIVATE STATIC) code - - registers : 0 + registers : 2 ins : 0 - outs : 0 - insns size : 4 16-bit code units -001288: |[001288] invokecustom.InvokeCustom.test1:()V -001298: fc00 0300 0000 |0000: invoke-custom {}, call_site@0003 -00129e: 0e00 |0003: return-void + outs : 2 + insns size : 8 16-bit code units +003bb4: |[003bb4] TestVariableArityLinkerMethod.methodJ:()V +003bc4: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003bc8: 1a01 7e01 |0002: const-string v1, "methodJ" // string@017e +003bcc: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +003bd2: 0e00 |0007: return-void catches : (none) positions : + 0x0000 line=358 + 0x0007 line=359 locals : - #21 : (in Linvokecustom/InvokeCustom;) - name : 'test2' + #20 : (in LTestVariableArityLinkerMethod;) + name : 'methodK' type : '()V' - access : 0x0009 (PUBLIC STATIC) + access : 0x000a (PRIVATE STATIC) code - - registers : 11 + registers : 2 ins : 0 - outs : 11 - insns size : 27 16-bit code units -0012a0: |[0012a0] invokecustom.InvokeCustom.test2:()V -0012b0: 1210 |0000: const/4 v0, #int 1 // #1 -0012b2: 1301 7f00 |0001: const/16 v1, #int 127 // #7f -0012b6: 1302 6300 |0003: const/16 v2, #int 99 // #63 -0012ba: 1303 0004 |0005: const/16 v3, #int 1024 // #400 -0012be: 1404 40e2 0100 |0007: const v4, #float 1.72999e-40 // #0001e240 -0012c4: 1405 9a99 993f |000a: const v5, #float 1.2 // #3f99999a -0012ca: 1706 15cd 5b07 |000d: const-wide/32 v6, #float 1.6536e-34 // #075bcd15 -0012d0: 1808 b6fa f8b0 4819 0c40 |0010: const-wide v8, #double 3.51235 // #400c1948b0f8fab6 -0012da: 1a0a 4800 |0015: const-string v10, "String" // string@0048 -0012de: fd0b 0400 0000 |0017: invoke-custom/range {v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10}, call_site@0004 -0012e4: 0e00 |001a: return-void + outs : 2 + insns size : 8 16-bit code units +003bd4: |[003bd4] TestVariableArityLinkerMethod.methodK:()V +003be4: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003be8: 1a01 7f01 |0002: const-string v1, "methodK" // string@017f +003bec: 6e20 b300 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +003bf2: 0e00 |0007: return-void catches : (none) positions : + 0x0000 line=392 + 0x0007 line=393 locals : - #22 : (in Linvokecustom/InvokeCustom;) - name : 'test3' + #21 : (in LTestVariableArityLinkerMethod;) + name : 'methodO' type : '()V' - access : 0x0009 (PUBLIC STATIC) + access : 0x000a (PRIVATE STATIC) code - registers : 0 ins : 0 outs : 0 insns size : 4 16-bit code units -0012e8: |[0012e8] invokecustom.InvokeCustom.test3:()V -0012f8: fc00 0b00 0000 |0000: invoke-custom {}, call_site@000b -0012fe: 0e00 |0003: return-void +003bf4: |[003bf4] TestVariableArityLinkerMethod.methodO:()V +003c04: 7100 8e00 0000 |0000: invoke-static {}, LTestVariableArityLinkerMethod;.assertNotReached:()V // method@008e +003c0a: 0e00 |0003: return-void catches : (none) positions : + 0x0000 line=413 + 0x0003 line=414 locals : - #23 : (in Linvokecustom/InvokeCustom;) - name : 'test4' + #22 : (in LTestVariableArityLinkerMethod;) + name : 'methodP' type : '()V' - access : 0x0009 (PUBLIC STATIC) + access : 0x000a (PRIVATE STATIC) code - - registers : 1 + registers : 0 ins : 0 - outs : 1 - insns size : 9 16-bit code units -001300: |[001300] invokecustom.InvokeCustom.test4:()V -001310: 2200 0700 |0000: new-instance v0, Linvokecustom/InvokeCustom; // type@0007 -001314: 7010 0100 0000 |0002: invoke-direct {v0}, Linvokecustom/InvokeCustom;.:()V // method@0001 -00131a: fc10 0c00 0000 |0005: invoke-custom {v0}, call_site@000c -001320: 0e00 |0008: return-void + outs : 0 + insns size : 4 16-bit code units +003c0c: |[003c0c] TestVariableArityLinkerMethod.methodP:()V +003c1c: 7100 8e00 0000 |0000: invoke-static {}, LTestVariableArityLinkerMethod;.assertNotReached:()V // method@008e +003c22: 0e00 |0003: return-void catches : (none) positions : + 0x0000 line=441 + 0x0003 line=442 locals : - #24 : (in Linvokecustom/InvokeCustom;) - name : 'test5' + #23 : (in LTestVariableArityLinkerMethod;) + name : 'methodQ' type : '()V' - access : 0x0009 (PUBLIC STATIC) - code - - registers : 4 - ins : 0 - outs : 3 - insns size : 35 16-bit code units -001324: |[001324] invokecustom.InvokeCustom.test5:()V -001334: 1300 e803 |0000: const/16 v0, #int 1000 // #3e8 -001338: 1301 65fc |0002: const/16 v1, #int -923 // #fc65 -00133c: 1302 4d00 |0004: const/16 v2, #int 77 // #4d -001340: fc30 0500 1002 |0006: invoke-custom {v0, v1, v2}, call_site@0005 -001346: 0a00 |0009: move-result v0 -001348: 6201 0200 |000a: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -00134c: 2202 1000 |000c: new-instance v2, Ljava/lang/StringBuilder; // type@0010 -001350: 7010 3000 0200 |000e: invoke-direct {v2}, Ljava/lang/StringBuilder;.:()V // method@0030 -001356: 1a03 8e00 |0011: const-string v3, "targetMethodTest5 returned: " // string@008e -00135a: 6e20 3600 3200 |0013: invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -001360: 0c02 |0016: move-result-object v2 -001362: 6e20 3300 0200 |0017: invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033 -001368: 0c00 |001a: move-result-object v0 -00136a: 6e10 3700 0000 |001b: invoke-virtual {v0}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037 -001370: 0c00 |001e: move-result-object v0 -001372: 6e20 2900 0100 |001f: invoke-virtual {v1, v0}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -001378: 0e00 |0022: return-void - catches : (none) - positions : - locals : - - #25 : (in Linvokecustom/InvokeCustom;) - name : 'test6' - type : '()V' - access : 0x0009 (PUBLIC STATIC) - code - - registers : 6 - ins : 0 - outs : 6 - insns size : 44 16-bit code units -00137c: |[00137c] invokecustom.InvokeCustom.test6:()V -00138c: 1800 7777 7777 7707 0000 |0000: const-wide v0, #double 4.05612e-311 // #0000077777777777 -001396: 1802 efee eeee eefe ffff |0005: const-wide v2, #double -nan // #fffffeeeeeeeeeef -0013a0: 1804 6666 6666 6606 0000 |000a: const-wide v4, #double 3.47668e-311 // #0000066666666666 -0013aa: fd06 0600 0000 |000f: invoke-custom/range {v0, v1, v2, v3, v4, v5}, call_site@0006 -0013b0: 0b00 |0012: move-result-wide v0 -0013b2: 6202 0200 |0013: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -0013b6: 2203 1000 |0015: new-instance v3, Ljava/lang/StringBuilder; // type@0010 -0013ba: 7010 3000 0300 |0017: invoke-direct {v3}, Ljava/lang/StringBuilder;.:()V // method@0030 -0013c0: 1a04 9100 |001a: const-string v4, "targetMethodTest6 returned: " // string@0091 -0013c4: 6e20 3600 4300 |001c: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -0013ca: 0c03 |001f: move-result-object v3 -0013cc: 6e30 3400 0301 |0020: invoke-virtual {v3, v0, v1}, Ljava/lang/StringBuilder;.append:(J)Ljava/lang/StringBuilder; // method@0034 -0013d2: 0c00 |0023: move-result-object v0 -0013d4: 6e10 3700 0000 |0024: invoke-virtual {v0}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037 -0013da: 0c00 |0027: move-result-object v0 -0013dc: 6e20 2900 0200 |0028: invoke-virtual {v2, v0}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -0013e2: 0e00 |002b: return-void - catches : (none) - positions : - locals : - - #26 : (in Linvokecustom/InvokeCustom;) - name : 'test7' - type : '()V' - access : 0x0009 (PUBLIC STATIC) - code - - registers : 5 - ins : 0 - outs : 4 - insns size : 40 16-bit code units -0013e4: |[0013e4] invokecustom.InvokeCustom.test7:()V -0013f4: 1400 0040 003f |0000: const v0, #float 0.500977 // #3f004000 -0013fa: 1401 0040 00bf |0003: const v1, #float -0.500977 // #bf004000 -001400: 1802 0000 0000 0410 d0bf |0006: const-wide v2, #double -0.250978 // #bfd0100400000000 -00140a: fc40 0700 1032 |000b: invoke-custom {v0, v1, v2, v3}, call_site@0007 -001410: 0b00 |000e: move-result-wide v0 -001412: 6202 0200 |000f: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -001416: 2203 1000 |0011: new-instance v3, Ljava/lang/StringBuilder; // type@0010 -00141a: 7010 3000 0300 |0013: invoke-direct {v3}, Ljava/lang/StringBuilder;.:()V // method@0030 -001420: 1a04 9100 |0016: const-string v4, "targetMethodTest6 returned: " // string@0091 -001424: 6e20 3600 4300 |0018: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -00142a: 0c03 |001b: move-result-object v3 -00142c: 6e30 3100 0301 |001c: invoke-virtual {v3, v0, v1}, Ljava/lang/StringBuilder;.append:(D)Ljava/lang/StringBuilder; // method@0031 -001432: 0c00 |001f: move-result-object v0 -001434: 6e10 3700 0000 |0020: invoke-virtual {v0}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037 -00143a: 0c00 |0023: move-result-object v0 -00143c: 6e20 2900 0200 |0024: invoke-virtual {v2, v0}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -001442: 0e00 |0027: return-void - catches : (none) - positions : - locals : - - #27 : (in Linvokecustom/InvokeCustom;) - name : 'test8' - type : '()V' - access : 0x0009 (PUBLIC STATIC) + access : 0x000a (PRIVATE STATIC) code - - registers : 1 + registers : 0 ins : 0 - outs : 1 - insns size : 16 16-bit code units -001444: |[001444] invokecustom.InvokeCustom.test8:()V -001454: 1a00 1500 |0000: const-string v0, "First invokedynamic invocation" // string@0015 -001458: fc10 0800 0000 |0002: invoke-custom {v0}, call_site@0008 -00145e: 1a00 4700 |0005: const-string v0, "Second invokedynamic invocation" // string@0047 -001462: fc10 0900 0000 |0007: invoke-custom {v0}, call_site@0009 -001468: 1a00 1000 |000a: const-string v0, "Dupe first invokedynamic invocation" // string@0010 -00146c: fc10 0a00 0000 |000c: invoke-custom {v0}, call_site@000a -001472: 0e00 |000f: return-void + outs : 0 + insns size : 4 16-bit code units +003c24: |[003c24] TestVariableArityLinkerMethod.methodQ:()V +003c34: 7100 8e00 0000 |0000: invoke-static {}, LTestVariableArityLinkerMethod;.assertNotReached:()V // method@008e +003c3a: 0e00 |0003: return-void catches : (none) positions : + 0x0000 line=468 + 0x0003 line=469 locals : - #28 : (in Linvokecustom/InvokeCustom;) - name : 'test9' + #24 : (in LTestVariableArityLinkerMethod;) + name : 'methodR' type : '()V' - access : 0x0009 (PUBLIC STATIC) + access : 0x000a (PRIVATE STATIC) code - registers : 0 ins : 0 outs : 0 insns size : 4 16-bit code units -001474: |[001474] invokecustom.InvokeCustom.test9:()V -001484: fc00 0d00 0000 |0000: invoke-custom {}, call_site@000d -00148a: 0e00 |0003: return-void +003c3c: |[003c3c] TestVariableArityLinkerMethod.methodR:()V +003c4c: 7100 8e00 0000 |0000: invoke-static {}, LTestVariableArityLinkerMethod;.assertNotReached:()V // method@008e +003c52: 0e00 |0003: return-void catches : (none) positions : + 0x0000 line=501 + 0x0003 line=502 locals : - Virtual methods - - #0 : (in Linvokecustom/InvokeCustom;) - name : 'helperMethodTest9' - type : '()V' - access : 0x0001 (PUBLIC) - code - - registers : 4 - ins : 1 - outs : 2 - insns size : 27 16-bit code units -00148c: |[00148c] invokecustom.InvokeCustom.helperMethodTest9:()V -00149c: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -0014a0: 2201 1000 |0002: new-instance v1, Ljava/lang/StringBuilder; // type@0010 -0014a4: 7010 3000 0100 |0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.:()V // method@0030 -0014aa: 1a02 7300 |0007: const-string v2, "helperMethodTest9 in " // string@0073 -0014ae: 6e20 3600 2100 |0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036 -0014b4: 0c01 |000c: move-result-object v1 -0014b6: 1c02 0700 |000d: const-class v2, Linvokecustom/InvokeCustom; // type@0007 -0014ba: 6e20 3500 2100 |000f: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@0035 -0014c0: 0c01 |0012: move-result-object v1 -0014c2: 6e10 3700 0100 |0013: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037 -0014c8: 0c01 |0016: move-result-object v1 -0014ca: 6e20 2900 1000 |0017: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -0014d0: 0e00 |001a: return-void - catches : (none) - positions : - 0x0000 line=129 - 0x001a line=130 - locals : - 0x0000 - 0x001b reg=3 this Linvokecustom/InvokeCustom; - - #1 : (in Linvokecustom/InvokeCustom;) - name : 'run' - type : '()V' - access : 0x0001 (PUBLIC) + #25 : (in LTestVariableArityLinkerMethod;) + name : 'printBsmArgs' + type : '(Ljava/lang/String;[Ljava/lang/Object;)V' + access : 0x008a (PRIVATE STATIC VARARGS) code - - registers : 3 - ins : 1 + registers : 6 + ins : 2 outs : 2 - insns size : 8 16-bit code units -0014d4: |[0014d4] invokecustom.InvokeCustom.run:()V -0014e4: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -0014e8: 1a01 8200 |0002: const-string v1, "run() for Test9" // string@0082 -0014ec: 6e20 2900 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -0014f2: 0e00 |0007: return-void + insns size : 159 16-bit code units +003c54: |[003c54] TestVariableArityLinkerMethod.printBsmArgs:(Ljava/lang/String;[Ljava/lang/Object;)V +003c64: 6200 1300 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003c68: 6e20 b000 4000 |0002: invoke-virtual {v0, v4}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +003c6e: 6200 1300 |0005: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003c72: 1a01 0600 |0007: const-string v1, "(" // string@0006 +003c76: 6e20 b000 1000 |0009: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +003c7c: 1200 |000c: const/4 v0, #int 0 // #0 +003c7e: 2151 |000d: array-length v1, v5 +003c80: 3510 8900 |000e: if-ge v0, v1, 0097 // +0089 +003c84: 3800 0900 |0010: if-eqz v0, 0019 // +0009 +003c88: 6201 1300 |0012: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003c8c: 1a02 0c00 |0014: const-string v2, ", " // string@000c +003c90: 6e20 b000 2100 |0016: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +003c96: 4601 0500 |0019: aget-object v1, v5, v0 +003c9a: 3801 7100 |001b: if-eqz v1, 008c // +0071 +003c9e: 4601 0500 |001d: aget-object v1, v5, v0 +003ca2: 6e10 c000 0100 |001f: invoke-virtual {v1}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +003ca8: 0c01 |0022: move-result-object v1 +003caa: 6e10 b800 0100 |0023: invoke-virtual {v1}, Ljava/lang/Class;.isArray:()Z // method@00b8 +003cb0: 0a01 |0026: move-result v1 +003cb2: 3801 6500 |0027: if-eqz v1, 008c // +0065 +003cb6: 4601 0500 |0029: aget-object v1, v5, v0 +003cba: 6e10 c000 0100 |002b: invoke-virtual {v1}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +003cc0: 0c02 |002e: move-result-object v2 +003cc2: 1c03 4400 |002f: const-class v3, [I // type@0044 +003cc6: 3332 0f00 |0031: if-ne v2, v3, 0040 // +000f +003cca: 6202 1300 |0033: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003cce: 0713 |0035: move-object v3, v1 +003cd0: 1f03 4400 |0036: check-cast v3, [I // type@0044 +003cd4: 7110 e900 0300 |0038: invoke-static {v3}, Ljava/util/Arrays;.toString:([I)Ljava/lang/String; // method@00e9 +003cda: 0c03 |003b: move-result-object v3 +003cdc: 6e20 b000 3200 |003c: invoke-virtual {v2, v3}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +003ce2: 284c |003f: goto 008b // +004c +003ce4: 6e10 c000 0100 |0040: invoke-virtual {v1}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +003cea: 0c02 |0043: move-result-object v2 +003cec: 1c03 4500 |0044: const-class v3, [J // type@0045 +003cf0: 3332 0f00 |0046: if-ne v2, v3, 0055 // +000f +003cf4: 6202 1300 |0048: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003cf8: 0713 |004a: move-object v3, v1 +003cfa: 1f03 4500 |004b: check-cast v3, [J // type@0045 +003cfe: 7110 ea00 0300 |004d: invoke-static {v3}, Ljava/util/Arrays;.toString:([J)Ljava/lang/String; // method@00ea +003d04: 0c03 |0050: move-result-object v3 +003d06: 6e20 b000 3200 |0051: invoke-virtual {v2, v3}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +003d0c: 2837 |0054: goto 008b // +0037 +003d0e: 6e10 c000 0100 |0055: invoke-virtual {v1}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +003d14: 0c02 |0058: move-result-object v2 +003d16: 1c03 4300 |0059: const-class v3, [F // type@0043 +003d1a: 3332 0f00 |005b: if-ne v2, v3, 006a // +000f +003d1e: 6202 1300 |005d: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003d22: 0713 |005f: move-object v3, v1 +003d24: 1f03 4300 |0060: check-cast v3, [F // type@0043 +003d28: 7110 e800 0300 |0062: invoke-static {v3}, Ljava/util/Arrays;.toString:([F)Ljava/lang/String; // method@00e8 +003d2e: 0c03 |0065: move-result-object v3 +003d30: 6e20 b000 3200 |0066: invoke-virtual {v2, v3}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +003d36: 2822 |0069: goto 008b // +0022 +003d38: 6e10 c000 0100 |006a: invoke-virtual {v1}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +003d3e: 0c02 |006d: move-result-object v2 +003d40: 1c03 4200 |006e: const-class v3, [D // type@0042 +003d44: 3332 0f00 |0070: if-ne v2, v3, 007f // +000f +003d48: 6202 1300 |0072: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003d4c: 0713 |0074: move-object v3, v1 +003d4e: 1f03 4200 |0075: check-cast v3, [D // type@0042 +003d52: 7110 e700 0300 |0077: invoke-static {v3}, Ljava/util/Arrays;.toString:([D)Ljava/lang/String; // method@00e7 +003d58: 0c03 |007a: move-result-object v3 +003d5a: 6e20 b000 3200 |007b: invoke-virtual {v2, v3}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +003d60: 280d |007e: goto 008b // +000d +003d62: 6202 1300 |007f: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003d66: 0713 |0081: move-object v3, v1 +003d68: 1f03 4800 |0082: check-cast v3, [Ljava/lang/Object; // type@0048 +003d6c: 7110 eb00 0300 |0084: invoke-static {v3}, Ljava/util/Arrays;.toString:([Ljava/lang/Object;)Ljava/lang/String; // method@00eb +003d72: 0c03 |0087: move-result-object v3 +003d74: 6e20 b000 3200 |0088: invoke-virtual {v2, v3}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +003d7a: 2808 |008b: goto 0093 // +0008 +003d7c: 6201 1300 |008c: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003d80: 4602 0500 |008e: aget-object v2, v5, v0 +003d84: 6e20 af00 2100 |0090: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/Object;)V // method@00af +003d8a: d800 0001 |0093: add-int/lit8 v0, v0, #int 1 // #01 +003d8e: 2900 78ff |0095: goto/16 000d // -0088 +003d92: 6200 1300 |0097: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003d96: 1a01 0800 |0099: const-string v1, ");" // string@0008 +003d9a: 6e20 b300 1000 |009b: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@00b3 +003da0: 0e00 |009e: return-void catches : (none) positions : - 0x0000 line=137 - 0x0007 line=138 + 0x0000 line=29 + 0x0005 line=30 + 0x000c line=31 + 0x0010 line=32 + 0x0012 line=33 + 0x0019 line=35 + 0x0029 line=36 + 0x002b line=37 + 0x0033 line=38 + 0x0040 line=39 + 0x0048 line=40 + 0x0055 line=41 + 0x005d line=42 + 0x006a line=43 + 0x0072 line=44 + 0x007f line=46 + 0x008b line=48 + 0x008c line=49 + 0x0093 line=31 + 0x0097 line=52 + 0x009e line=53 locals : - 0x0000 - 0x0008 reg=2 this Linvokecustom/InvokeCustom; + 0x002b - 0x008b reg=1 array Ljava/lang/Object; + 0x000d - 0x0097 reg=0 i I + 0x0000 - 0x009f reg=4 method Ljava/lang/String; + 0x0000 - 0x009f reg=5 args [Ljava/lang/Object; - #2 : (in Linvokecustom/InvokeCustom;) - name : 'targetMethodTest4' + #26 : (in LTestVariableArityLinkerMethod;) + name : 'test' type : '()V' - access : 0x0001 (PUBLIC) + access : 0x0008 (STATIC) code - registers : 3 - ins : 1 + ins : 0 outs : 2 - insns size : 8 16-bit code units -0014f4: |[0014f4] invokecustom.InvokeCustom.targetMethodTest4:()V -001504: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 -001508: 1a01 8a00 |0002: const-string v1, "targetMethodTest4 from InvokeCustom (oops!)" // string@008a -00150c: 6e20 2900 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029 -001512: 0e00 |0007: return-void - catches : (none) + insns size : 224 16-bit code units +003da4: |[003da4] TestVariableArityLinkerMethod.test:()V +003db4: 1200 |0000: const/4 v0, #int 0 // #0 +003db6: 0101 |0001: move v1, v0 +003db8: 1222 |0002: const/4 v2, #int 2 // #2 +003dba: 3521 0e00 |0003: if-ge v1, v2, 0011 // +000e +003dbe: fc00 1f00 0000 |0005: invoke-custom {}, call_site@001f +003dc4: fc00 2000 0000 |0008: invoke-custom {}, call_site@0020 +003dca: fc00 2100 0000 |000b: invoke-custom {}, call_site@0021 +003dd0: d801 0101 |000e: add-int/lit8 v1, v1, #int 1 // #01 +003dd4: 28f2 |0010: goto 0002 // -000e +003dd6: 0000 |0011: nop // spacer +003dd8: 3520 0e00 |0012: if-ge v0, v2, 0020 // +000e +003ddc: fc00 2200 0000 |0014: invoke-custom {}, call_site@0022 +003de2: fc00 2300 0000 |0017: invoke-custom {}, call_site@0023 +003de8: fc00 2400 0000 |001a: invoke-custom {}, call_site@0024 +003dee: d800 0001 |001d: add-int/lit8 v0, v0, #int 1 // #01 +003df2: 28f3 |001f: goto 0012 // -000d +003df4: fc00 2500 0000 |0020: invoke-custom {}, call_site@0025 +003dfa: fc00 2600 0000 |0023: invoke-custom {}, call_site@0026 +003e00: fc00 2700 0000 |0026: invoke-custom {}, call_site@0027 +003e06: fc00 2800 0000 |0029: invoke-custom {}, call_site@0028 +003e0c: fc00 2900 0000 |002c: invoke-custom {}, call_site@0029 +003e12: fc00 2a00 0000 |002f: invoke-custom {}, call_site@002a +003e18: 7100 8e00 0000 |0032: invoke-static {}, LTestVariableArityLinkerMethod;.assertNotReached:()V // method@008e +003e1e: 2826 |0035: goto 005b // +0026 +003e20: 0d00 |0036: move-exception v0 +003e22: 6201 1300 |0037: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003e26: 1a02 8201 |0039: const-string v2, "methodO => " // string@0182 +003e2a: 6e20 b000 2100 |003b: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +003e30: 6201 1300 |003e: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003e34: 6e10 c000 0000 |0040: invoke-virtual {v0}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +003e3a: 0c02 |0043: move-result-object v2 +003e3c: 6e20 af00 2100 |0044: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/Object;)V // method@00af +003e42: 6201 1300 |0047: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003e46: 1a02 0200 |0049: const-string v2, " => " // string@0002 +003e4a: 6e20 b000 2100 |004b: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +003e50: 6201 1300 |004e: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003e54: 6e10 b600 0000 |0050: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +003e5a: 0c02 |0053: move-result-object v2 +003e5c: 6e10 c000 0200 |0054: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +003e62: 0c02 |0057: move-result-object v2 +003e64: 6e20 b200 2100 |0058: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@00b2 +003e6a: fc00 2b00 0000 |005b: invoke-custom {}, call_site@002b +003e70: 7100 8e00 0000 |005e: invoke-static {}, LTestVariableArityLinkerMethod;.assertNotReached:()V // method@008e +003e76: 2826 |0061: goto 0087 // +0026 +003e78: 0d00 |0062: move-exception v0 +003e7a: 6201 1300 |0063: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003e7e: 1a02 8401 |0065: const-string v2, "methodP => " // string@0184 +003e82: 6e20 b000 2100 |0067: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +003e88: 6201 1300 |006a: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003e8c: 6e10 c000 0000 |006c: invoke-virtual {v0}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +003e92: 0c02 |006f: move-result-object v2 +003e94: 6e20 af00 2100 |0070: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/Object;)V // method@00af +003e9a: 6201 1300 |0073: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003e9e: 1a02 0200 |0075: const-string v2, " => " // string@0002 +003ea2: 6e20 b000 2100 |0077: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +003ea8: 6201 1300 |007a: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003eac: 6e10 b600 0000 |007c: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +003eb2: 0c02 |007f: move-result-object v2 +003eb4: 6e10 c000 0200 |0080: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +003eba: 0c02 |0083: move-result-object v2 +003ebc: 6e20 b200 2100 |0084: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@00b2 +003ec2: fc00 2c00 0000 |0087: invoke-custom {}, call_site@002c +003ec8: 7100 8e00 0000 |008a: invoke-static {}, LTestVariableArityLinkerMethod;.assertNotReached:()V // method@008e +003ece: 2826 |008d: goto 00b3 // +0026 +003ed0: 0d00 |008e: move-exception v0 +003ed2: 6201 1300 |008f: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003ed6: 1a02 8601 |0091: const-string v2, "methodQ => " // string@0186 +003eda: 6e20 b000 2100 |0093: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +003ee0: 6201 1300 |0096: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003ee4: 6e10 c000 0000 |0098: invoke-virtual {v0}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +003eea: 0c02 |009b: move-result-object v2 +003eec: 6e20 af00 2100 |009c: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/Object;)V // method@00af +003ef2: 6201 1300 |009f: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003ef6: 1a02 0200 |00a1: const-string v2, " => " // string@0002 +003efa: 6e20 b000 2100 |00a3: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +003f00: 6201 1300 |00a6: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003f04: 6e10 b600 0000 |00a8: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +003f0a: 0c02 |00ab: move-result-object v2 +003f0c: 6e10 c000 0200 |00ac: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +003f12: 0c02 |00af: move-result-object v2 +003f14: 6e20 b200 2100 |00b0: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@00b2 +003f1a: fc00 2d00 0000 |00b3: invoke-custom {}, call_site@002d +003f20: 7100 8e00 0000 |00b6: invoke-static {}, LTestVariableArityLinkerMethod;.assertNotReached:()V // method@008e +003f26: 2826 |00b9: goto 00df // +0026 +003f28: 0d00 |00ba: move-exception v0 +003f2a: 6201 1300 |00bb: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003f2e: 1a02 8801 |00bd: const-string v2, "methodR => " // string@0188 +003f32: 6e20 b000 2100 |00bf: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +003f38: 6201 1300 |00c2: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003f3c: 6e10 c000 0000 |00c4: invoke-virtual {v0}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +003f42: 0c02 |00c7: move-result-object v2 +003f44: 6e20 af00 2100 |00c8: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/Object;)V // method@00af +003f4a: 6201 1300 |00cb: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003f4e: 1a02 0200 |00cd: const-string v2, " => " // string@0002 +003f52: 6e20 b000 2100 |00cf: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@00b0 +003f58: 6201 1300 |00d2: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0013 +003f5c: 6e10 b600 0000 |00d4: invoke-virtual {v0}, Ljava/lang/BootstrapMethodError;.getCause:()Ljava/lang/Throwable; // method@00b6 +003f62: 0c02 |00d7: move-result-object v2 +003f64: 6e10 c000 0200 |00d8: invoke-virtual {v2}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@00c0 +003f6a: 0c02 |00db: move-result-object v2 +003f6c: 6e20 b200 2100 |00dc: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@00b2 +003f72: 0e00 |00df: return-void + catches : 4 + 0x002f - 0x0035 + Ljava/lang/BootstrapMethodError; -> 0x0036 + 0x005b - 0x0061 + Ljava/lang/BootstrapMethodError; -> 0x0062 + 0x0087 - 0x008d + Ljava/lang/BootstrapMethodError; -> 0x008e + 0x00b3 - 0x00b9 + Ljava/lang/BootstrapMethodError; -> 0x00ba positions : - 0x0000 line=68 - 0x0007 line=69 + 0x0000 line=506 + 0x0005 line=507 + 0x0008 line=508 + 0x000b line=509 + 0x000e line=506 + 0x0011 line=511 + 0x0014 line=512 + 0x0017 line=513 + 0x001a line=514 + 0x001d line=511 + 0x0020 line=516 + 0x0023 line=517 + 0x0026 line=518 + 0x0029 line=519 + 0x002c line=520 + 0x002f line=527 + 0x0032 line=528 + 0x0035 line=534 + 0x0036 line=529 + 0x0037 line=530 + 0x003e line=531 + 0x0047 line=532 + 0x004e line=533 + 0x005b line=538 + 0x005e line=539 + 0x0061 line=545 + 0x0062 line=540 + 0x0063 line=541 + 0x006a line=542 + 0x0073 line=543 + 0x007a line=544 + 0x0087 line=549 + 0x008a line=550 + 0x008d line=556 + 0x008e line=551 + 0x008f line=552 + 0x0096 line=553 + 0x009f line=554 + 0x00a6 line=555 + 0x00b3 line=560 + 0x00b6 line=561 + 0x00b9 line=567 + 0x00ba line=562 + 0x00bb line=563 + 0x00c2 line=564 + 0x00cb line=565 + 0x00d2 line=566 + 0x00df line=568 locals : - 0x0000 - 0x0008 reg=2 this Linvokecustom/InvokeCustom; + 0x0002 - 0x0011 reg=1 i I + 0x0012 - 0x0020 reg=0 i I + 0x0037 - 0x005b reg=0 expected Ljava/lang/BootstrapMethodError; + 0x0063 - 0x0087 reg=0 expected Ljava/lang/BootstrapMethodError; + 0x008f - 0x00b3 reg=0 expected Ljava/lang/BootstrapMethodError; + 0x00bb - 0x00df reg=0 expected Ljava/lang/BootstrapMethodError; - source_file_idx : 27 (InvokeCustom.java) + Virtual methods - + source_file_idx : 156 (TestVariableArityLinkerMethod.java) Method handle #0: - type : put-static - target : Linvokecustom/InvokeCustom; staticFieldTest9 - target_type : I + type : invoke-static + target : LTestBadBootstrapArguments; bsm + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ID)Ljava/lang/invoke/CallSite; Method handle #1: - type : get-static - target : Linvokecustom/InvokeCustom; staticFieldTest9 - target_type : I + type : invoke-static + target : LTestBadBootstrapArguments; bsm + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ILjava/lang/String;)Ljava/lang/invoke/CallSite; Method handle #2: - type : put-instance - target : Linvokecustom/InvokeCustom; fieldTest9 - target_type : (Linvokecustom/InvokeCustom; + type : invoke-static + target : LTestBadBootstrapArguments; bsmDJ + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;DJ)Ljava/lang/invoke/CallSite; Method handle #3: - type : get-instance - target : Linvokecustom/InvokeCustom; fieldTest9 - target_type : (Linvokecustom/InvokeCustom; + type : invoke-static + target : LTestBadBootstrapArguments; bsmDoubleLong + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Double;Ljava/lang/Long;)Ljava/lang/invoke/CallSite; Method handle #4: type : invoke-static - target : Linvokecustom/InvokeCustom; bsmCreateCallSite - target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite; + target : LTestBadBootstrapArguments; bsmReturningInteger + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/Integer; Method handle #5: type : invoke-static - target : Linvokecustom/InvokeCustom; bsmLookupStatic - target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + target : LTestBadBootstrapArguments; bsmReturningObject + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/Object; Method handle #6: type : invoke-static - target : Linvokecustom/InvokeCustom; bsmLookupStaticWithExtraArgs - target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;IJFD)Ljava/lang/invoke/CallSite; + target : LTestBadBootstrapArguments; bsmReturningTestersConstantCallsite + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)LTestBadBootstrapArguments$TestersConstantCallSite; Method handle #7: type : invoke-static - target : Linvokecustom/InvokeCustom; bsmLookupTest9 - target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite; + target : LTestBadBootstrapArguments; bsmReturningVoid + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)V Method handle #8: type : invoke-static - target : Linvokecustom/InvokeCustom; lambda$lambdaTest$0 - target_type : (Ljava/lang/String;)Z + target : LTestBadBootstrapArguments; bsmZBCS + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCS)Ljava/lang/invoke/CallSite; Method handle #9: type : invoke-static - target : Ljava/lang/invoke/LambdaMetafactory; metafactory - target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + target : LTestDynamicBootstrapArguments; bsm + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;J)Ljava/lang/invoke/CallSite; Method handle #10: - type : invoke-instance - target : Linvokecustom/InvokeCustom; helperMethodTest9 - target_type : (Linvokecustom/InvokeCustom;)V + type : invoke-static + target : LTestInvocationKinds; lookupConstructor + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; Method handle #11: - type : invoke-instance - target : Ljava/io/PrintStream; println - target_type : (Ljava/io/PrintStream;Ljava/lang/String;)V + type : invoke-static + target : LTestInvocationKinds; lookupInstanceFieldGetter + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; Method handle #12: - type : invoke-instance - target : Ljava/lang/String; trim - target_type : (Ljava/lang/String;)Ljava/lang/String; + type : invoke-static + target : LTestInvocationKinds; lookupInstanceFieldSetter + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; Method handle #13: - type : invoke-constructor - target : Linvokecustom/InvokeCustom; - target_type : (Linvokecustom/InvokeCustom;I)V + type : invoke-static + target : LTestInvocationKinds; lookupStaticFieldGetter + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; Method handle #14: - type : invoke-direct - target : Linvokecustom/Super; targetMethodTest4 - target_type : (Linvokecustom/Super;)V + type : invoke-static + target : LTestInvocationKinds; lookupStaticFieldSetter + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; Method handle #15: - type : invoke-interface - target : Ljava/lang/Runnable; run - target_type : (Ljava/lang/Runnable;)V -Call site #0: // offset 8450 + type : invoke-static + target : LTestInvocationKinds; lookupVirtual + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; +Method handle #16: + type : invoke-static + target : LTestInvokeCustomWithConcurrentThreads; linkerMethod + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; +Method handle #17: + type : invoke-static + target : LTestLinkerMethodMinimalArguments; linkerMethod + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; +Method handle #18: + type : invoke-static + target : LTestLinkerMethodMultipleArgumentTypes; linkerMethod + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;IIIIIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite; +Method handle #19: + type : invoke-static + target : LTestVariableArityLinkerMethod; bsmWithBoxedArray + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Integer;)Ljava/lang/invoke/CallSite; +Method handle #20: + type : invoke-static + target : LTestVariableArityLinkerMethod; bsmWithClassAndFloatArray + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Class;[F)Ljava/lang/invoke/CallSite; +Method handle #21: + type : invoke-static + target : LTestVariableArityLinkerMethod; bsmWithClassArray + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Class;)Ljava/lang/invoke/CallSite; +Method handle #22: + type : invoke-static + target : LTestVariableArityLinkerMethod; bsmWithDoubleArray + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[D)Ljava/lang/invoke/CallSite; +Method handle #23: + type : invoke-static + target : LTestVariableArityLinkerMethod; bsmWithFloatAndLongArray + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;F[J)Ljava/lang/invoke/CallSite; +Method handle #24: + type : invoke-static + target : LTestVariableArityLinkerMethod; bsmWithIntAndStringArray + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I[Ljava/lang/String;)Ljava/lang/invoke/CallSite; +Method handle #25: + type : invoke-static + target : LTestVariableArityLinkerMethod; bsmWithLongAndIntArray + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;J[I)Ljava/lang/invoke/CallSite; +Method handle #26: + type : invoke-static + target : LTestVariableArityLinkerMethod; bsmWithStringArray + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/String;)Ljava/lang/invoke/CallSite; +Method handle #27: + type : invoke-static + target : LTestVariableArityLinkerMethod; bsmWithWiderArray + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[J)Ljava/lang/invoke/CallSite; +Method handle #28: + type : invoke-static + target : LUnrelatedBSM; bsm + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Class;)Ljava/lang/invoke/CallSite; +Call site #0: // offset 29649 + link_argument[0] : 1 (MethodHandle) + link_argument[1] : happy (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : -1 (int) + link_argument[4] : very (String) +Call site #1: // offset 29662 + link_argument[0] : 0 (MethodHandle) + link_argument[1] : wrongParameterTypes (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : -1 (int) + link_argument[4] : very (String) +Call site #2: // offset 29675 + link_argument[0] : 0 (MethodHandle) + link_argument[1] : missingParameterTypes (String) + link_argument[2] : ()V (MethodType) +Call site #3: // offset 29683 + link_argument[0] : 1 (MethodHandle) + link_argument[1] : extraArguments (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : 1 (int) + link_argument[4] : 2 (String) + link_argument[5] : 3 (int) +Call site #4: // offset 29697 + link_argument[0] : 1 (MethodHandle) + link_argument[1] : wrongArguments (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : 1 (String) + link_argument[4] : 3.14159 (double) +Call site #5: // offset 29697 + link_argument[0] : 1 (MethodHandle) + link_argument[1] : wrongArguments (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : 1 (String) + link_argument[4] : 3.14159 (double) +Call site #6: // offset 29716 + link_argument[0] : 1 (MethodHandle) + link_argument[1] : wrongArgumentsAgain (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : 3.14159 (double) + link_argument[4] : pie (String) +Call site #7: // offset 29736 + link_argument[0] : 8 (MethodHandle) + link_argument[1] : narrowArguments (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : 1 (int) + link_argument[4] : 127 (int) + link_argument[5] : 65 (int) + link_argument[6] : -32768 (int) +Call site #8: // offset 29753 + link_argument[0] : 2 (MethodHandle) + link_argument[1] : wideningArguments (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : 1.79769e+308 (double) + link_argument[4] : 2147483647 (int) +Call site #9: // offset 29775 + link_argument[0] : 3 (MethodHandle) + link_argument[1] : boxingArguments (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : 1.79769e+308 (double) + link_argument[4] : 9223372036854775807 (long) +Call site #10: // offset 29800 + link_argument[0] : 3 (MethodHandle) + link_argument[1] : wideningBoxingArguments (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : 3.40282e+38 (float) + link_argument[4] : 2147483647 (long) +Call site #11: // offset 29818 + link_argument[0] : 7 (MethodHandle) + link_argument[1] : voidReturnType (String) + link_argument[2] : ()V (MethodType) +Call site #12: // offset 29826 + link_argument[0] : 5 (MethodHandle) + link_argument[1] : ObjectReturnType (String) + link_argument[2] : ()V (MethodType) +Call site #13: // offset 29833 + link_argument[0] : 4 (MethodHandle) + link_argument[1] : integerReturnType (String) + link_argument[2] : ()V (MethodType) +Call site #14: // offset 29841 + link_argument[0] : 6 (MethodHandle) + link_argument[1] : sayHello (String) + link_argument[2] : ()V (MethodType) +Call site #15: // offset 29849 link_argument[0] : 9 (MethodHandle) - link_argument[1] : test (String) - link_argument[2] : ()Ljava/util/function/Predicate; (MethodType) - link_argument[3] : (Ljava/lang/Object;)Z (MethodType) - link_argument[4] : 8 (MethodHandle) - link_argument[5] : (Ljava/lang/String;)Z (MethodType) -Call site #1: // offset 8463 + link_argument[1] : target (String) + link_argument[2] : (ILjava/lang/String;Ljava/lang/Double;)I (MethodType) + link_argument[3] : A (String) + link_argument[4] : 100000000 (long) +Call site #16: // offset 29849 link_argument[0] : 9 (MethodHandle) - link_argument[1] : apply (String) - link_argument[2] : ()Ljava/util/function/Function; (MethodType) - link_argument[3] : (Ljava/lang/Object;)Ljava/lang/Object; (MethodType) - link_argument[4] : 12 (MethodHandle) - link_argument[5] : (Ljava/lang/String;)Ljava/lang/String; (MethodType) -Call site #2: // offset 8476 + link_argument[1] : target (String) + link_argument[2] : (ILjava/lang/String;Ljava/lang/Double;)I (MethodType) + link_argument[3] : A (String) + link_argument[4] : 100000000 (long) +Call site #17: // offset 29849 link_argument[0] : 9 (MethodHandle) - link_argument[1] : accept (String) - link_argument[2] : (Ljava/io/PrintStream;)Ljava/util/function/Consumer; (MethodType) - link_argument[3] : (Ljava/lang/Object;)V (MethodType) - link_argument[4] : 11 (MethodHandle) - link_argument[5] : (Ljava/lang/String;)V (MethodType) -Call site #3: // offset 8489 - link_argument[0] : 5 (MethodHandle) - link_argument[1] : targetMethodTest1 (String) + link_argument[1] : target (String) + link_argument[2] : (ILjava/lang/String;Ljava/lang/Double;)I (MethodType) + link_argument[3] : A (String) + link_argument[4] : 100000000 (long) +Call site #18: // offset 29864 + link_argument[0] : 10 (MethodHandle) + link_argument[1] : unused (String) + link_argument[2] : (I)LTestInvocationKinds$Widget; (MethodType) +Call site #19: // offset 29872 + link_argument[0] : 12 (MethodHandle) + link_argument[1] : instance_field (String) + link_argument[2] : (LTestInvocationKinds;D)V (MethodType) +Call site #20: // offset 29880 + link_argument[0] : 11 (MethodHandle) + link_argument[1] : instance_field (String) + link_argument[2] : (LTestInvocationKinds;)D (MethodType) +Call site #21: // offset 29888 + link_argument[0] : 15 (MethodHandle) + link_argument[1] : getMaxIntegerValue (String) + link_argument[2] : (LTestInvocationKinds;II)I (MethodType) +Call site #22: // offset 29896 + link_argument[0] : 14 (MethodHandle) + link_argument[1] : static_field (String) + link_argument[2] : (I)V (MethodType) +Call site #23: // offset 29896 + link_argument[0] : 14 (MethodHandle) + link_argument[1] : static_field (String) + link_argument[2] : (I)V (MethodType) +Call site #24: // offset 29904 + link_argument[0] : 13 (MethodHandle) + link_argument[1] : static_field (String) + link_argument[2] : ()I (MethodType) +Call site #25: // offset 29904 + link_argument[0] : 13 (MethodHandle) + link_argument[1] : static_field (String) + link_argument[2] : ()I (MethodType) +Call site #26: // offset 29912 + link_argument[0] : 16 (MethodHandle) + link_argument[1] : setCalled (String) + link_argument[2] : (I)I (MethodType) +Call site #27: // offset 29920 + link_argument[0] : 17 (MethodHandle) + link_argument[1] : _add (String) + link_argument[2] : (II)I (MethodType) +Call site #28: // offset 29927 + link_argument[0] : 18 (MethodHandle) + link_argument[1] : _add (String) + link_argument[2] : (II)I (MethodType) + link_argument[3] : -1 (int) + link_argument[4] : 1 (int) + link_argument[5] : 97 (int) + link_argument[6] : 1024 (int) + link_argument[7] : 1 (int) + link_argument[8] : 11.1 (float) + link_argument[9] : 2.2 (double) + link_argument[10] : Hello (String) + link_argument[11] : LTestLinkerMethodMultipleArgumentTypes; (Class) + link_argument[12] : 123456789 (long) +Call site #29: // offset 29968 + link_argument[0] : 28 (MethodHandle) + link_argument[1] : _addf (String) + link_argument[2] : (FF)F (MethodType) + link_argument[3] : LTestLinkerUnrelatedBSM; (Class) +Call site #30: // offset 29977 + link_argument[0] : 28 (MethodHandle) + link_argument[1] : _subf (String) + link_argument[2] : (FF)F (MethodType) + link_argument[3] : LTestLinkerUnrelatedBSM; (Class) +Call site #31: // offset 29986 + link_argument[0] : 26 (MethodHandle) + link_argument[1] : methodA (String) link_argument[2] : ()V (MethodType) -Call site #4: // offset 8496 - link_argument[0] : 5 (MethodHandle) - link_argument[1] : targetMethodTest2 (String) - link_argument[2] : (ZBCSIFJDLjava/lang/String;)V (MethodType) -Call site #5: // offset 8503 - link_argument[0] : 5 (MethodHandle) - link_argument[1] : targetMethodTest5 (String) - link_argument[2] : (III)I (MethodType) -Call site #6: // offset 8510 - link_argument[0] : 5 (MethodHandle) - link_argument[1] : targetMethodTest6 (String) - link_argument[2] : (JJJ)J (MethodType) -Call site #7: // offset 8517 - link_argument[0] : 5 (MethodHandle) - link_argument[1] : targetMethodTest7 (String) - link_argument[2] : (FFD)D (MethodType) -Call site #8: // offset 8524 - link_argument[0] : 5 (MethodHandle) - link_argument[1] : targetMethodTest8 (String) - link_argument[2] : (Ljava/lang/String;)V (MethodType) -Call site #9: // offset 8524 - link_argument[0] : 5 (MethodHandle) - link_argument[1] : targetMethodTest8 (String) - link_argument[2] : (Ljava/lang/String;)V (MethodType) -Call site #10: // offset 8524 - link_argument[0] : 5 (MethodHandle) - link_argument[1] : targetMethodTest8 (String) - link_argument[2] : (Ljava/lang/String;)V (MethodType) -Call site #11: // offset 8531 - link_argument[0] : 6 (MethodHandle) - link_argument[1] : targetMethodTest3 (String) + link_argument[3] : Aachen (String) + link_argument[4] : Aalborg (String) + link_argument[5] : Aalto (String) +Call site #32: // offset 30000 + link_argument[0] : 26 (MethodHandle) + link_argument[1] : methodB (String) link_argument[2] : ()V (MethodType) - link_argument[3] : 1 (int) - link_argument[4] : 123456789 (long) - link_argument[5] : 123.456 (float) - link_argument[6] : 123457 (double) -Call site #12: // offset 8559 - link_argument[0] : 4 (MethodHandle) - link_argument[1] : targetMethodTest4 (String) - link_argument[2] : (Linvokecustom/InvokeCustom;)V (MethodType) - link_argument[3] : 14 (MethodHandle) -Call site #13: // offset 8568 - link_argument[0] : 7 (MethodHandle) - link_argument[1] : targetMethodTest9 (String) + link_argument[3] : barium (String) +Call site #33: // offset 30010 + link_argument[0] : 26 (MethodHandle) + link_argument[1] : methodC (String) + link_argument[2] : ()V (MethodType) +Call site #34: // offset 30018 + link_argument[0] : 24 (MethodHandle) + link_argument[1] : methodD (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : 101 (int) + link_argument[4] : zoo (String) + link_argument[5] : zoogene (String) + link_argument[6] : zoogenic (String) +Call site #35: // offset 30037 + link_argument[0] : 24 (MethodHandle) + link_argument[1] : methodE (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : 102 (int) + link_argument[4] : zonic (String) +Call site #36: // offset 30050 + link_argument[0] : 24 (MethodHandle) + link_argument[1] : methodF (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : 103 (int) +Call site #37: // offset 30060 + link_argument[0] : 25 (MethodHandle) + link_argument[1] : methodG (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : 81985529216486895 (long) + link_argument[4] : 1 (int) + link_argument[5] : -1 (int) + link_argument[6] : 2 (int) + link_argument[7] : -2 (int) +Call site #38: // offset 30085 + link_argument[0] : 23 (MethodHandle) + link_argument[1] : methodH (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : -2.71828 (float) + link_argument[4] : 999999999999 (long) + link_argument[5] : -8888888888888 (long) +Call site #39: // offset 30112 + link_argument[0] : 20 (MethodHandle) + link_argument[1] : methodI (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : Ljava/lang/Throwable; (Class) + link_argument[4] : 3.40282e+38 (float) + link_argument[5] : 1.4013e-45 (float) + link_argument[6] : 3.14159 (float) + link_argument[7] : -3.14159 (float) +Call site #40: // offset 30142 + link_argument[0] : 22 (MethodHandle) + link_argument[1] : methodJ (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : 1.79769e+308 (double) + link_argument[4] : 4.94066e-324 (double) + link_argument[5] : 2.71828 (double) + link_argument[6] : -3.14159 (double) +Call site #41: // offset 30186 + link_argument[0] : 21 (MethodHandle) + link_argument[1] : methodK (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : Ljava/lang/Integer; (Class) + link_argument[4] : Ljava/lang/invoke/MethodHandles; (Class) + link_argument[5] : Ljava/util/Arrays; (Class) +Call site #42: // offset 30200 + link_argument[0] : 24 (MethodHandle) + link_argument[1] : methodO (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : 103 (int) + link_argument[4] : 104 (int) +Call site #43: // offset 30212 + link_argument[0] : 24 (MethodHandle) + link_argument[1] : methodP (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : 103 (int) + link_argument[4] : A (String) + link_argument[5] : B (String) + link_argument[6] : 42 (int) +Call site #44: // offset 30228 + link_argument[0] : 27 (MethodHandle) + link_argument[1] : methodQ (String) + link_argument[2] : ()V (MethodType) + link_argument[3] : 103 (int) + link_argument[4] : 42 (int) +Call site #45: // offset 30240 + link_argument[0] : 19 (MethodHandle) + link_argument[1] : methodR (String) link_argument[2] : ()V (MethodType) - link_argument[3] : 1 (MethodHandle) - link_argument[4] : 0 (MethodHandle) - link_argument[5] : 3 (MethodHandle) - link_argument[6] : 2 (MethodHandle) - link_argument[7] : 10 (MethodHandle) - link_argument[8] : 13 (MethodHandle) - link_argument[9] : 15 (MethodHandle) + link_argument[3] : 1030 (int) + link_argument[4] : 420 (int) diff --git a/test/dexdump/invoke-custom.xml b/test/dexdump/invoke-custom.xml index 8b22a9d9ee..6d49ce1292 100644 --- a/test/dexdump/invoke-custom.xml +++ b/test/dexdump/invoke-custom.xml @@ -1,130 +1,21 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - + + - - - - - - - - + + - + - - - - - + - - - - + + + + + + + - + - - - - + + + + + - - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + - + - - - - - + + + + - + - - - - - + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + - - - - + + + + - - - - + + + + + + + + - - - - + + + + + + - - - - + + + + + - - - - + + + + + + + + + - - - - + + + + + + + - - - + + + - - - - + + + + + - - - - - + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + -- GitLab From a4f1f6e7ea1066a0ee3a2d99eb0b2bd90474bf04 Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Fri, 25 May 2018 16:10:02 +0100 Subject: [PATCH 468/749] Rename nativeHoldsLock to holdsLock and make it work on current thread The Java native method nativeHoldsLock has been renamed to match the upstream OpenJDK 8u121-b13 and changed from an instance method on Thread to a static method. This makes the corresponding change to the native implementation of that method. Test: make checkbuild, flash, CtsLibcoreTestCases Bug: 74379469 Change-Id: Ib9dedccd3014c01c148ff824764be319c2a7a123 --- runtime/native/java_lang_Thread.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc index 9edb0c21dd..a37a76f809 100644 --- a/runtime/native/java_lang_Thread.cc +++ b/runtime/native/java_lang_Thread.cc @@ -111,15 +111,14 @@ static jint Thread_nativeGetStatus(JNIEnv* env, jobject java_thread, jboolean ha return -1; // Unreachable. } -static jboolean Thread_nativeHoldsLock(JNIEnv* env, jobject java_thread, jobject java_object) { +static jboolean Thread_holdsLock(JNIEnv* env, jclass, jobject java_object) { ScopedObjectAccess soa(env); ObjPtr object = soa.Decode(java_object); if (object == nullptr) { ThrowNullPointerException("object == null"); return JNI_FALSE; } - MutexLock mu(soa.Self(), *Locks::thread_list_lock_); - Thread* thread = Thread::FromManagedThread(soa, java_thread); + Thread* thread = soa.Self(); return thread->HoldsLock(object.Ptr()); } @@ -200,7 +199,7 @@ static JNINativeMethod gMethods[] = { FAST_NATIVE_METHOD(Thread, isInterrupted, "()Z"), NATIVE_METHOD(Thread, nativeCreate, "(Ljava/lang/Thread;JZ)V"), NATIVE_METHOD(Thread, nativeGetStatus, "(Z)I"), - NATIVE_METHOD(Thread, nativeHoldsLock, "(Ljava/lang/Object;)Z"), + NATIVE_METHOD(Thread, holdsLock, "(Ljava/lang/Object;)Z"), FAST_NATIVE_METHOD(Thread, nativeInterrupt, "()V"), NATIVE_METHOD(Thread, nativeSetName, "(Ljava/lang/String;)V"), NATIVE_METHOD(Thread, nativeSetPriority, "(I)V"), -- GitLab From dd966bc5b30aac068ee25d8f9bdb18a53904e312 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Thu, 24 May 2018 13:55:52 +0100 Subject: [PATCH 469/749] Change the BitTableBuilder API to be POD based. The compiler has two copies of all stack map intermediate data in memory at the same time at the moment. Change the BitTableBuilder so that it will be able to store the intermediate data directly (e.g. StackMapEntry), and thus we can save the space, and can avoid the copying code complexity. It will also make it possible to deduplicate data as we go, thus saving further memory and code complexity. Test: test-art-host-gtest-stack_map_test Change-Id: I660fddf0629422ae0d2588333854d8fdf1e1bd0f --- compiler/optimizing/stack_map_stream.cc | 39 ++++----- libartbase/base/arena_allocator.cc | 1 + libartbase/base/arena_allocator.h | 1 + libartbase/base/bit_table.h | 111 +++++++++++++++--------- libartbase/base/bit_table_test.cc | 50 ++++++++--- 5 files changed, 129 insertions(+), 73 deletions(-) diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index aa28c8b500..c6e375a1b2 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -199,9 +199,6 @@ static MemoryRegion EncodeMemoryRegion(Vector* out, size_t* bit_offset, uint32_t return region; } -template -using ScopedBitTableBuilder = BitTableBuilder>; - size_t StackMapStream::PrepareForFillIn() { size_t bit_offset = 0; out_.clear(); @@ -258,20 +255,21 @@ size_t StackMapStream::PrepareForFillIn() { DCHECK_EQ(location_catalog_offset, dex_register_location_catalog_region.size()); // Write stack maps. - ScopedArenaAllocatorAdapter adapter = allocator_->Adapter(kArenaAllocStackMapStream); - ScopedBitTableBuilder stack_map_builder((adapter)); - ScopedBitTableBuilder invoke_info_builder((adapter)); - ScopedBitTableBuilder inline_info_builder((adapter)); + BitTableBuilder> stack_map_builder(allocator_); + BitTableBuilder> invoke_info_builder(allocator_); + BitTableBuilder> inline_info_builder(allocator_); for (const StackMapEntry& entry : stack_maps_) { if (entry.dex_method_index != dex::kDexNoIndex) { - invoke_info_builder.AddRow( + std::array invoke_info_entry { entry.native_pc_code_offset.CompressedValue(), entry.invoke_type, - entry.dex_method_index_idx); + entry.dex_method_index_idx + }; + invoke_info_builder.Add(invoke_info_entry); } // Set the inlining info. - uint32_t inline_info_index = StackMap::kNoValue; + uint32_t inline_info_index = inline_info_builder.size(); DCHECK_LE(entry.inline_infos_start_index + entry.inlining_depth, inline_infos_.size()); for (size_t depth = 0; depth < entry.inlining_depth; ++depth) { InlineInfoEntry inline_entry = inline_infos_[depth + entry.inline_infos_start_index]; @@ -281,32 +279,33 @@ size_t StackMapStream::PrepareForFillIn() { method_index_idx = High32Bits(reinterpret_cast(inline_entry.method)); extra_data = Low32Bits(reinterpret_cast(inline_entry.method)); } - uint32_t index = inline_info_builder.AddRow( + std::array inline_info_entry { (depth == entry.inlining_depth - 1) ? InlineInfo::kLast : InlineInfo::kMore, method_index_idx, inline_entry.dex_pc, extra_data, - dex_register_entries_[inline_entry.dex_register_map_index].offset); - if (depth == 0) { - inline_info_index = index; - } + dex_register_entries_[inline_entry.dex_register_map_index].offset, + }; + inline_info_builder.Add(inline_info_entry); } - stack_map_builder.AddRow( + std::array stack_map_entry { entry.native_pc_code_offset.CompressedValue(), entry.dex_pc, dex_register_entries_[entry.dex_register_map_index].offset, - inline_info_index, + entry.inlining_depth != 0 ? inline_info_index : InlineInfo::kNoValue, entry.register_mask_index, - entry.stack_mask_index); + entry.stack_mask_index, + }; + stack_map_builder.Add(stack_map_entry); } stack_map_builder.Encode(&out_, &bit_offset); invoke_info_builder.Encode(&out_, &bit_offset); inline_info_builder.Encode(&out_, &bit_offset); // Write register masks table. - ScopedBitTableBuilder<1> register_mask_builder((adapter)); + BitTableBuilder register_mask_builder(allocator_); for (size_t i = 0; i < num_register_masks; ++i) { - register_mask_builder.AddRow(register_masks_[i]); + register_mask_builder.Add(register_masks_[i]); } register_mask_builder.Encode(&out_, &bit_offset); diff --git a/libartbase/base/arena_allocator.cc b/libartbase/base/arena_allocator.cc index 183e5c9f74..df3deba178 100644 --- a/libartbase/base/arena_allocator.cc +++ b/libartbase/base/arena_allocator.cc @@ -82,6 +82,7 @@ const char* const ArenaAllocatorStatsImpl::kAllocNames[] = { "RegAllocator ", "RegAllocVldt ", "StackMapStm ", + "BitTableBld ", "VectorNode ", "CodeGen ", "Assembler ", diff --git a/libartbase/base/arena_allocator.h b/libartbase/base/arena_allocator.h index 211ff4f6ad..4dccd033d6 100644 --- a/libartbase/base/arena_allocator.h +++ b/libartbase/base/arena_allocator.h @@ -92,6 +92,7 @@ enum ArenaAllocKind { kArenaAllocRegisterAllocator, kArenaAllocRegisterAllocatorValidate, kArenaAllocStackMapStream, + kArenaAllocBitTableBuilder, kArenaAllocVectorNode, kArenaAllocCodeGenerator, kArenaAllocAssembler, diff --git a/libartbase/base/bit_table.h b/libartbase/base/bit_table.h index 24bdd13324..54a2415c0e 100644 --- a/libartbase/base/bit_table.h +++ b/libartbase/base/bit_table.h @@ -17,11 +17,17 @@ #ifndef ART_LIBARTBASE_BASE_BIT_TABLE_H_ #define ART_LIBARTBASE_BASE_BIT_TABLE_H_ -#include +#include +#include +#include +#include +#include #include "base/bit_memory_region.h" -#include "base/bit_utils.h" +#include "base/casts.h" #include "base/memory_region.h" +#include "base/scoped_arena_containers.h" +#include "base/stl_util.h" namespace art { @@ -104,8 +110,7 @@ class BitTable { column_offset_[0] = 0; for (uint32_t i = 0; i < kNumColumns; i++) { size_t column_end = column_offset_[i] + DecodeVarintBits(region, bit_offset); - column_offset_[i + 1] = column_end; - DCHECK_EQ(column_offset_[i + 1], column_end) << "Overflow"; + column_offset_[i + 1] = dchecked_integral_cast(column_end); } } @@ -146,75 +151,97 @@ constexpr uint32_t BitTable::Accessor::kNoValue; template constexpr uint32_t BitTable::kValueBias; -template> +// Helper class for encoding BitTable. It can optionally de-duplicate the inputs. +// Type 'T' must be POD type consisting of uint32_t fields (one for each column). +template class BitTableBuilder { public: - explicit BitTableBuilder(Alloc alloc = Alloc()) : buffer_(alloc) {} - - template - uint32_t AddRow(T ... values) { - constexpr size_t count = sizeof...(values); - static_assert(count == kNumColumns, "Incorrect argument count"); - uint32_t data[count] = { values... }; - buffer_.insert(buffer_.end(), data, data + count); - return num_rows_++; + static_assert(std::is_pod::value, "Type 'T' must be POD"); + static constexpr size_t kNumColumns = sizeof(T) / sizeof(uint32_t); + + explicit BitTableBuilder(ScopedArenaAllocator* allocator) + : rows_(allocator->Adapter(kArenaAllocBitTableBuilder)) { + } + + T& operator[](size_t row) { return rows_[row]; } + const T& operator[](size_t row) const { return rows_[row]; } + size_t size() const { return rows_.size(); } + + void Add(T value) { + rows_.push_back(value); } ALWAYS_INLINE uint32_t Get(uint32_t row, uint32_t column) const { - return buffer_[row * kNumColumns + column]; + DCHECK_LT(row, size()); + DCHECK_LT(column, kNumColumns); + const uint32_t* data = reinterpret_cast(&rows_[row]); + return data[column]; } - template - void Encode(Vector* out, size_t* bit_offset) { - constexpr uint32_t bias = BitTable::kValueBias; - size_t initial_bit_offset = *bit_offset; - // Measure data size. - uint32_t max_column_value[kNumColumns] = {}; - for (uint32_t r = 0; r < num_rows_; r++) { + // Calculate the column bit widths based on the current data. + void Measure(/*out*/ std::array* column_bits) const { + uint32_t max_column_value[kNumColumns]; + std::fill_n(max_column_value, kNumColumns, 0); + for (uint32_t r = 0; r < size(); r++) { for (uint32_t c = 0; c < kNumColumns; c++) { - max_column_value[c] |= Get(r, c) - bias; + max_column_value[c] |= Get(r, c) - BitTable::kValueBias; } } - // Write table header. - uint32_t table_data_bits = 0; - uint32_t column_bits[kNumColumns] = {}; - EncodeVarintBits(out, bit_offset, num_rows_); - if (num_rows_ != 0) { + for (uint32_t c = 0; c < kNumColumns; c++) { + (*column_bits)[c] = MinimumBitsToStore(max_column_value[c]); + } + } + + // Encode the stored data into a BitTable. + template + void Encode(Vector* out, size_t* bit_offset) const { + constexpr uint32_t bias = BitTable::kValueBias; + size_t initial_bit_offset = *bit_offset; + + std::array column_bits; + Measure(&column_bits); + EncodeVarintBits(out, bit_offset, size()); + if (size() != 0) { + // Write table header. for (uint32_t c = 0; c < kNumColumns; c++) { - column_bits[c] = MinimumBitsToStore(max_column_value[c]); EncodeVarintBits(out, bit_offset, column_bits[c]); - table_data_bits += num_rows_ * column_bits[c]; } - } - // Write table data. - out->resize(BitsToBytesRoundUp(*bit_offset + table_data_bits)); - BitMemoryRegion region(MemoryRegion(out->data(), out->size())); - for (uint32_t r = 0; r < num_rows_; r++) { - for (uint32_t c = 0; c < kNumColumns; c++) { - region.StoreBitsAndAdvance(bit_offset, Get(r, c) - bias, column_bits[c]); + + // Write table data. + uint32_t row_bits = std::accumulate(column_bits.begin(), column_bits.end(), 0u); + out->resize(BitsToBytesRoundUp(*bit_offset + row_bits * size())); + BitMemoryRegion region(MemoryRegion(out->data(), out->size())); + for (uint32_t r = 0; r < size(); r++) { + for (uint32_t c = 0; c < kNumColumns; c++) { + region.StoreBitsAndAdvance(bit_offset, Get(r, c) - bias, column_bits[c]); + } } } + // Verify the written data. if (kIsDebugBuild) { BitTable table; + BitMemoryRegion region(MemoryRegion(out->data(), out->size())); table.Decode(region, &initial_bit_offset); - DCHECK_EQ(this->num_rows_, table.NumRows()); + DCHECK_EQ(size(), table.NumRows()); for (uint32_t c = 0; c < kNumColumns; c++) { DCHECK_EQ(column_bits[c], table.NumColumnBits(c)); } - for (uint32_t r = 0; r < num_rows_; r++) { + for (uint32_t r = 0; r < size(); r++) { for (uint32_t c = 0; c < kNumColumns; c++) { - DCHECK_EQ(this->Get(r, c), table.Get(r, c)) << " (" << r << ", " << c << ")"; + DCHECK_EQ(Get(r, c), table.Get(r, c)) << " (" << r << ", " << c << ")"; } } } } protected: - std::vector buffer_; - uint32_t num_rows_ = 0; + ScopedArenaDeque rows_; }; +template +constexpr size_t BitTableBuilder::kNumColumns; + } // namespace art #endif // ART_LIBARTBASE_BASE_BIT_TABLE_H_ diff --git a/libartbase/base/bit_table_test.cc b/libartbase/base/bit_table_test.cc index 25bfcf095e..e6f0d53541 100644 --- a/libartbase/base/bit_table_test.cc +++ b/libartbase/base/bit_table_test.cc @@ -16,8 +16,14 @@ #include "bit_table.h" +#include + #include "gtest/gtest.h" +#include "base/arena_allocator.h" +#include "base/bit_utils.h" +#include "base/malloc_arena_pool.h" + namespace art { TEST(BitTableTest, TestVarint) { @@ -38,9 +44,13 @@ TEST(BitTableTest, TestVarint) { } TEST(BitTableTest, TestEmptyTable) { + MallocArenaPool pool; + ArenaStack arena_stack(&pool); + ScopedArenaAllocator allocator(&arena_stack); + std::vector buffer; size_t encode_bit_offset = 0; - BitTableBuilder<1> builder; + BitTableBuilder builder(&allocator); builder.Encode(&buffer, &encode_bit_offset); size_t decode_bit_offset = 0; @@ -50,14 +60,18 @@ TEST(BitTableTest, TestEmptyTable) { } TEST(BitTableTest, TestSingleColumnTable) { + MallocArenaPool pool; + ArenaStack arena_stack(&pool); + ScopedArenaAllocator allocator(&arena_stack); + constexpr uint32_t kNoValue = -1; std::vector buffer; size_t encode_bit_offset = 0; - BitTableBuilder<1> builder; - builder.AddRow(42u); - builder.AddRow(kNoValue); - builder.AddRow(1000u); - builder.AddRow(kNoValue); + BitTableBuilder builder(&allocator); + builder.Add(42u); + builder.Add(kNoValue); + builder.Add(1000u); + builder.Add(kNoValue); builder.Encode(&buffer, &encode_bit_offset); size_t decode_bit_offset = 0; @@ -72,11 +86,15 @@ TEST(BitTableTest, TestSingleColumnTable) { } TEST(BitTableTest, TestUnalignedTable) { + MallocArenaPool pool; + ArenaStack arena_stack(&pool); + ScopedArenaAllocator allocator(&arena_stack); + for (size_t start_bit_offset = 0; start_bit_offset <= 32; start_bit_offset++) { std::vector buffer; size_t encode_bit_offset = start_bit_offset; - BitTableBuilder<1> builder; - builder.AddRow(42u); + BitTableBuilder builder(&allocator); + builder.Add(42u); builder.Encode(&buffer, &encode_bit_offset); size_t decode_bit_offset = start_bit_offset; @@ -88,12 +106,22 @@ TEST(BitTableTest, TestUnalignedTable) { } TEST(BitTableTest, TestBigTable) { + MallocArenaPool pool; + ArenaStack arena_stack(&pool); + ScopedArenaAllocator allocator(&arena_stack); + constexpr uint32_t kNoValue = -1; std::vector buffer; size_t encode_bit_offset = 0; - BitTableBuilder<4> builder; - builder.AddRow(42u, kNoValue, 0u, static_cast(-2)); - builder.AddRow(62u, kNoValue, 63u, static_cast(-3)); + struct RowData { + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + }; + BitTableBuilder builder(&allocator); + builder.Add(RowData{42u, kNoValue, 0u, static_cast(-2)}); + builder.Add(RowData{62u, kNoValue, 63u, static_cast(-3)}); builder.Encode(&buffer, &encode_bit_offset); size_t decode_bit_offset = 0; -- GitLab From 7d90630d7c84670f163c396f3dde27c535dd2b13 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Fri, 25 May 2018 15:53:28 +0100 Subject: [PATCH 470/749] ART: fix 463-checker-boolean-simplier for jvm Test: art/test/run-test --host --64 463 Test: art/test/run-test --host --64 --jvm 463 Change-Id: I9ebc0172b8f55869b48ddea07d22064d53f1cf8b --- .../src-art/Main.java | 280 ++++++++++++++++++ .../src/Main.java | 266 +---------------- 2 files changed, 284 insertions(+), 262 deletions(-) create mode 100644 test/463-checker-boolean-simplifier/src-art/Main.java diff --git a/test/463-checker-boolean-simplifier/src-art/Main.java b/test/463-checker-boolean-simplifier/src-art/Main.java new file mode 100644 index 0000000000..2c759ed6f9 --- /dev/null +++ b/test/463-checker-boolean-simplifier/src-art/Main.java @@ -0,0 +1,280 @@ +/* + * 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. + */ + +import java.lang.reflect.Method; + +public class Main { + + // Note #1: `javac` flips the conditions of If statements. + // Note #2: In the optimizing compiler, the first input of Phi is always + // the fall-through path, i.e. the false branch. + + public static void assertBoolEquals(boolean expected, boolean result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void assertIntEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + /* + * Program which only delegates the condition, i.e. returns 1 when True + * and 0 when False. + */ + + /// CHECK-START: boolean Main.GreaterThan(int, int) select_generator (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> GreaterThan [<>,<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: Return [<>] + + /// CHECK-START: boolean Main.GreaterThan(int, int) select_generator (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> GreaterThan [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + + public static boolean LessThan(int x, int y) { + return (x < y) ? true : false; + } + + /// CHECK-START: int Main.SimpleTrueBlock(boolean, int) select_generator (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 42 + /// CHECK-DAG: <> IntConstant 43 + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.SimpleFalseBlock(boolean, int) select_generator (after) + /// CHECK-NOT: If + + public static int SimpleFalseBlock(boolean x, int y) { + return x ? 42 : y + 43; + } + + /// CHECK-START: int Main.SimpleBothBlocks(boolean, int, int) select_generator (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 42 + /// CHECK-DAG: <> IntConstant 43 + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] - - public static boolean GreaterThan(int x, int y) { - return (x <= y) ? false : true; - } - - /* - * Program which negates a condition, i.e. returns 0 when True - * and 1 when False. - */ - - /// CHECK-START: boolean Main.LessThan(int, int) select_generator (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] - /// CHECK-DAG: If [<>] - /// CHECK-DAG: <> Phi [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: boolean Main.LessThan(int, int) select_generator (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.SimpleTrueBlock(boolean, int) select_generator (after) - /// CHECK-NOT: If - - public static int SimpleTrueBlock(boolean x, int y) { - return x ? y + 42 : 43; - } - - /// CHECK-START: int Main.SimpleFalseBlock(boolean, int) select_generator (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 42 - /// CHECK-DAG: <> IntConstant 43 - /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.SimpleBothBlocks(boolean, int, int) select_generator (after) - /// CHECK-NOT: If - - public static int SimpleBothBlocks(boolean x, int y, int z) { - return x ? y + 42 : z + 43; - } - - /// CHECK-START: int Main.ThreeBlocks(boolean, boolean) select_generator (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> IntConstant 2 - /// CHECK-DAG: <> IntConstant 3 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - public static int ThreeBlocks(boolean x, boolean y) { - if (x) { - return 1; - } else if (y) { - return 2; - } else { - return 3; + public static void main(String[] args) { + // This file is just for running on the RI as the test is ART specific. As successful + // execution for this test produces no output, there's nothing to do here. } - } - - /// CHECK-START: int Main.TrueBlockWithTooManyInstructions(boolean) select_generator (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 2 - /// CHECK-DAG: <> IntConstant 43 - /// CHECK-DAG: If [<>] - /// CHECK-DAG: <> InstanceFieldGet [<>] - /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: Phi [<>,<>] - - /// CHECK-START: int Main.TrueBlockWithTooManyInstructions(boolean) select_generator (after) - /// CHECK-NOT: Select - - public int TrueBlockWithTooManyInstructions(boolean x) { - return x ? (read_field + 2) : 43; - } - - /// CHECK-START: int Main.FalseBlockWithTooManyInstructions(boolean) select_generator (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 3 - /// CHECK-DAG: <> IntConstant 42 - /// CHECK-DAG: If [<>] - /// CHECK-DAG: <> InstanceFieldGet [<>] - /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: Phi [<>,<>] - - /// CHECK-START: int Main.FalseBlockWithTooManyInstructions(boolean) select_generator (after) - /// CHECK-NOT: Select - - public int FalseBlockWithTooManyInstructions(boolean x) { - return x ? 42 : (read_field + 3); - } - - /// CHECK-START: int Main.TrueBlockWithSideEffects(boolean) select_generator (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 42 - /// CHECK-DAG: <> IntConstant 43 - /// CHECK-DAG: If [<>] - /// CHECK-DAG: InstanceFieldSet [<>,<>] - /// CHECK-DAG: Phi [<>,<>] - - /// CHECK-START: int Main.TrueBlockWithSideEffects(boolean) select_generator (after) - /// CHECK-NOT: Select - - public int TrueBlockWithSideEffects(boolean x) { - return x ? (write_field = 42) : 43; - } - - /// CHECK-START: int Main.FalseBlockWithSideEffects(boolean) select_generator (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 42 - /// CHECK-DAG: <> IntConstant 43 - /// CHECK-DAG: If [<>] - /// CHECK-DAG: InstanceFieldSet [<>,<>] - /// CHECK-DAG: Phi [<>,<>] - - /// CHECK-START: int Main.FalseBlockWithSideEffects(boolean) select_generator (after) - /// CHECK-NOT: Select - - public int FalseBlockWithSideEffects(boolean x) { - return x ? 42 : (write_field = 43); - } - - public static void main(String[] args) throws Exception { - Class main2 = Class.forName("Main2"); - Method booleanNot = main2.getMethod("BooleanNot", boolean.class); - Method valuesOrdered = main2.getMethod("ValuesOrdered", int.class, int.class, int.class); - Method negatedCondition = main2.getMethod("NegatedCondition", boolean.class); - Method multiplePhis = main2.getMethod("MultiplePhis"); - - assertBoolEquals(false, (boolean)booleanNot.invoke(null, true)); - assertBoolEquals(true, (boolean)booleanNot.invoke(null, false)); - assertBoolEquals(true, GreaterThan(10, 5)); - assertBoolEquals(false, GreaterThan(10, 10)); - assertBoolEquals(false, GreaterThan(5, 10)); - assertBoolEquals(true, LessThan(5, 10)); - assertBoolEquals(false, LessThan(10, 10)); - assertBoolEquals(false, LessThan(10, 5)); - - assertBoolEquals(true, (boolean)valuesOrdered.invoke(null, 1, 3, 5)); - assertBoolEquals(true, (boolean)valuesOrdered.invoke(null, 5, 3, 1)); - assertBoolEquals(false, (boolean)valuesOrdered.invoke(null, 1, 3, 2)); - assertBoolEquals(false, (boolean)valuesOrdered.invoke(null, 2, 3, 1)); - assertBoolEquals(true, (boolean)valuesOrdered.invoke(null, 3, 3, 3)); - assertBoolEquals(true, (boolean)valuesOrdered.invoke(null, 3, 3, 5)); - assertBoolEquals(false, (boolean)valuesOrdered.invoke(null, 5, 5, 3)); - assertIntEquals(42, (int)negatedCondition.invoke(null, true)); - assertIntEquals(43, (int)negatedCondition.invoke(null, false)); - assertIntEquals(46, SimpleTrueBlock(true, 4)); - assertIntEquals(43, SimpleTrueBlock(false, 4)); - assertIntEquals(42, SimpleFalseBlock(true, 7)); - assertIntEquals(50, SimpleFalseBlock(false, 7)); - assertIntEquals(48, SimpleBothBlocks(true, 6, 2)); - assertIntEquals(45, SimpleBothBlocks(false, 6, 2)); - assertIntEquals(1, ThreeBlocks(true, true)); - assertIntEquals(1, ThreeBlocks(true, false)); - assertIntEquals(2, ThreeBlocks(false, true)); - assertIntEquals(3, ThreeBlocks(false, false)); - assertIntEquals(13, (int)multiplePhis.invoke(null)); - - Main m = new Main(); - assertIntEquals(42, m.TrueBlockWithTooManyInstructions(true)); - assertIntEquals(43, m.TrueBlockWithTooManyInstructions(false)); - assertIntEquals(42, m.FalseBlockWithTooManyInstructions(true)); - assertIntEquals(43, m.FalseBlockWithTooManyInstructions(false)); - assertIntEquals(42, m.TrueBlockWithSideEffects(true)); - assertIntEquals(43, m.TrueBlockWithSideEffects(false)); - assertIntEquals(42, m.FalseBlockWithSideEffects(true)); - assertIntEquals(43, m.FalseBlockWithSideEffects(false)); - } - - // These need to be instance fields so as to not generate a LoadClass for iget/iput. - public int read_field = 40; - public int write_field = 42; } -- GitLab From 159c9ddb930b84fac127f90032ed33c13ee15ff4 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Thu, 24 May 2018 14:56:51 +0100 Subject: [PATCH 471/749] Add deduplication logic to BitTableBuilder. Test: test-art-host-gtest-stack_map_test Test: test-art-host-gtest-bit_table_test Change-Id: Ide5d38f6e9f111e0583ff7934f81b266b9d0d6ca --- libartbase/base/bit_table.h | 46 ++++++++++++++++++++++- libartbase/base/bit_table_test.cc | 28 ++++++++++++++ libartbase/base/scoped_arena_containers.h | 8 ++++ 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/libartbase/base/bit_table.h b/libartbase/base/bit_table.h index 54a2415c0e..fbded2785c 100644 --- a/libartbase/base/bit_table.h +++ b/libartbase/base/bit_table.h @@ -160,17 +160,60 @@ class BitTableBuilder { static constexpr size_t kNumColumns = sizeof(T) / sizeof(uint32_t); explicit BitTableBuilder(ScopedArenaAllocator* allocator) - : rows_(allocator->Adapter(kArenaAllocBitTableBuilder)) { + : rows_(allocator->Adapter(kArenaAllocBitTableBuilder)), + dedup_(8, allocator->Adapter(kArenaAllocBitTableBuilder)) { } T& operator[](size_t row) { return rows_[row]; } const T& operator[](size_t row) const { return rows_[row]; } size_t size() const { return rows_.size(); } + // Append given value to the vector without de-duplication. + // This will not add the element to the dedup map to avoid its associated costs. void Add(T value) { rows_.push_back(value); } + // Append given list of values and return the index of the first value. + // If the exact same set of values was already added, return the old index. + uint32_t Dedup(T* values, size_t count = 1) { + FNVHash hasher; + uint32_t hash = hasher(MemoryRegion(values, sizeof(T) * count)); + + // Check if we have already added identical set of values. + auto range = dedup_.equal_range(hash); + for (auto it = range.first; it != range.second; ++it) { + uint32_t index = it->second; + if (count <= size() - index && + std::equal(values, + values + count, + &rows_[index], + [](const T& lhs, const T& rhs) { + return memcmp(&lhs, &rhs, sizeof(T)) == 0; + })) { + return index; + } + } + + // Add the set of values and add the index to the dedup map. + uint32_t index = size(); + rows_.insert(rows_.end(), values, values + count); + dedup_.emplace(hash, index); + return index; + } + + // Check if the table already contains given values starting at the given index. + bool RangeEquals(uint32_t index, T* values, size_t count = 1) { + DCHECK_LE(index, size()); + DCHECK_LE(count, size() - index); + for (uint32_t i = 0; i < count; i++) { + if (memcmp(&values[i], &rows_[index + i], sizeof(T)) != 0) { + return false; + } + } + return true; + } + ALWAYS_INLINE uint32_t Get(uint32_t row, uint32_t column) const { DCHECK_LT(row, size()); DCHECK_LT(column, kNumColumns); @@ -237,6 +280,7 @@ class BitTableBuilder { protected: ScopedArenaDeque rows_; + ScopedArenaUnorderedMultimap dedup_; // Hash -> row index. }; template diff --git a/libartbase/base/bit_table_test.cc b/libartbase/base/bit_table_test.cc index e6f0d53541..f579440199 100644 --- a/libartbase/base/bit_table_test.cc +++ b/libartbase/base/bit_table_test.cc @@ -142,4 +142,32 @@ TEST(BitTableTest, TestBigTable) { EXPECT_EQ(32u, table.NumColumnBits(3)); } +TEST(BitTableTest, TestDedup) { + MallocArenaPool pool; + ArenaStack arena_stack(&pool); + ScopedArenaAllocator allocator(&arena_stack); + + struct RowData { + uint32_t a; + uint32_t b; + }; + BitTableBuilder builder(&allocator); + RowData value0{1, 2}; + RowData value1{3, 4}; + RowData value2{56948505, 0}; + RowData value3{67108869, 0}; + FNVHash hasher; + EXPECT_EQ(hasher(MemoryRegion(&value2, sizeof(RowData))), + hasher(MemoryRegion(&value3, sizeof(RowData)))); // Test hash collision. + EXPECT_EQ(0u, builder.Dedup(&value0)); + EXPECT_EQ(1u, builder.Dedup(&value1)); + EXPECT_EQ(2u, builder.Dedup(&value2)); + EXPECT_EQ(3u, builder.Dedup(&value3)); + EXPECT_EQ(0u, builder.Dedup(&value0)); + EXPECT_EQ(1u, builder.Dedup(&value1)); + EXPECT_EQ(2u, builder.Dedup(&value2)); + EXPECT_EQ(3u, builder.Dedup(&value3)); + EXPECT_EQ(4u, builder.size()); +} + } // namespace art diff --git a/libartbase/base/scoped_arena_containers.h b/libartbase/base/scoped_arena_containers.h index 41939816f5..44d7ebbc96 100644 --- a/libartbase/base/scoped_arena_containers.h +++ b/libartbase/base/scoped_arena_containers.h @@ -86,6 +86,14 @@ template , class KeyEqual = st using ScopedArenaUnorderedMap = std::unordered_map>>; +template , class KeyEqual = std::equal_to> +using ScopedArenaUnorderedMultimap = + std::unordered_multimap>>; + // Implementation details below. template <> -- GitLab From 08f7c1dac69bc8c412b0503fc916ede8d1f33474 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Fri, 25 May 2018 15:34:41 +0100 Subject: [PATCH 472/749] Have tools/run-{libcore,jdwp}-tests.sh honor ART_TEST_CHROOT. Also remove explicit handling of `--chroot` option from these scripts. Test: ART_TEST_CHROOT=/data/local/art-test-chroot tools/run-libcore-tests.sh --mode=device --variant=X64 Test: ART_TEST_CHROOT=/data/local/art-test-chroot tools/run-jdwp-tests.sh --mode=device --variant=X64 Bug: 34729697 Change-Id: Id583ea16b36c3eebc1f80868c06ed26174cb4533 --- tools/run-jdwp-tests.sh | 35 ++++++++++++++++------------------- tools/run-libcore-tests.sh | 30 ++++++++++-------------------- 2 files changed, 26 insertions(+), 39 deletions(-) diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index eebc09278a..0796432f68 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -36,10 +36,9 @@ if [ -z "$ANDROID_HOST_OUT" ] ; then ANDROID_HOST_OUT=${OUT_DIR-$ANDROID_BUILD_TOP/out}/host/linux-x86 fi -android_root="/system" -if [ -n "$ART_TEST_ANDROID_ROOT" ]; then - android_root="$ART_TEST_ANDROID_ROOT" -fi +# "Root" (actually "system") directory on device (in the case of +# target testing). +android_root=${ART_TEST_ANDROID_ROOT:-/system} java_lib_location="${ANDROID_HOST_OUT}/../common/obj/JAVA_LIBRARIES" make_target_name="apache-harmony-jdwp-tests-hostdex" @@ -48,6 +47,7 @@ vm_args="" art="$android_root/bin/art" art_debugee="sh $android_root/bin/art" args=$@ +chroot_option= debuggee_args="-Xcompiler-option --debuggable" device_dir="--device-dir=/data/local/tmp" # We use the art script on target to ensure the runner and the debuggee share the same @@ -68,8 +68,6 @@ test="org.apache.harmony.jpda.tests.share.AllTests" mode="target" # Use JIT compiling by default. use_jit=true -# Don't use chroot by default. -use_chroot=false variant_cmdline_parameter="--variant=X32" dump_command="/bin/true" # Timeout of JDWP test in ms. @@ -112,15 +110,6 @@ while true; do # We don't care about jit with the RI use_jit=false shift - elif [[ "$1" == "--chroot" ]]; then - use_chroot=true - # Adjust settings for chroot environment. - art="/system/bin/art" - art_debugee="sh /system/bin/art" - vm_command="--vm-command=$art" - device_dir="--device-dir=/tmp" - # Shift the "--chroot" flag and its argument. - shift 2 elif [[ $1 == --test-timeout-ms ]]; then # Remove the --test-timeout-ms from the arguments. args=${args/$1} @@ -202,10 +191,17 @@ while true; do fi done -if $use_chroot && [[ $mode == "host" ]]; then - # Chroot-based testing is not supported on host. - echo "Cannot use --chroot with --mode=host" - exit 1 +if [[ $mode == "target" ]]; then + # Honor environment variable ART_TEST_CHROOT. + if [[ -n "$ART_TEST_CHROOT" ]]; then + # Set Vogar's `--chroot` option. + chroot_option="--chroot $ART_TEST_CHROOT" + # Adjust settings for chroot environment. + art="/system/bin/art" + art_debugee="sh /system/bin/art" + vm_command="--vm-command=$art" + device_dir="--device-dir=/tmp" + fi fi if [[ $has_gdb = "yes" ]]; then @@ -341,6 +337,7 @@ vogar $vm_command \ $vm_args \ --verbose \ $args \ + $chroot_option \ $device_dir \ $image_compiler_option \ --timeout 800 \ diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh index 3537c1b861..aff009abb6 100755 --- a/tools/run-libcore-tests.sh +++ b/tools/run-libcore-tests.sh @@ -28,10 +28,9 @@ else JAVA_LIBRARIES=${ANDROID_PRODUCT_OUT}/../../common/obj/JAVA_LIBRARIES fi -android_root="/system" -if [ -n "$ART_TEST_ANDROID_ROOT" ]; then - android_root="$ART_TEST_ANDROID_ROOT" -fi +# "Root" (actually "system") directory on device (in the case of +# target testing). +android_root=${ART_TEST_ANDROID_ROOT:-/system} function classes_jar_path { local var="$1" @@ -106,8 +105,6 @@ debug=false # Don't use device mode by default. device_mode=false -# Don't use chroot by default. -use_chroot=false while true; do if [[ "$1" == "--mode=device" ]]; then @@ -135,10 +132,6 @@ while true; do elif [[ "$1" == "-Xgc:gcstress" ]]; then gcstress=true shift - elif [[ "$1" == "--chroot" ]]; then - use_chroot=true - # Shift the "--chroot" flag and its argument. - shift 2 elif [[ "$1" == "" ]]; then break else @@ -147,20 +140,17 @@ while true; do done if $device_mode; then - if $use_chroot; then + # Honor environment variable ART_TEST_CHROOT. + if [[ -n "$ART_TEST_CHROOT" ]]; then + # Set Vogar's `--chroot` option. + vogar_args="$vogar_args --chroot $ART_TEST_CHROOT" vogar_args="$vogar_args --device-dir=/tmp" - vogar_args="$vogar_args --vm-command=/system/bin/art" else + # When not using a chroot on device, set Vogar's work directory to + # /data/local/tmp. vogar_args="$vogar_args --device-dir=/data/local/tmp" - vogar_args="$vogar_args --vm-command=$android_root/bin/art" - fi -else - # Host mode. - if $use_chroot; then - # Chroot-based testing is not supported on host. - echo "Cannot use --chroot with --mode=host" - exit 1 fi + vogar_args="$vogar_args --vm-command=$android_root/bin/art" fi # Increase the timeout, as vogar cannot set individual test -- GitLab From 7d43242e7061bc5d4ee6c34c45635eb50f3ec8e9 Mon Sep 17 00:00:00 2001 From: David Sehr Date: Fri, 25 May 2018 10:49:02 -0700 Subject: [PATCH 473/749] Move more test code to CommonArtTest Complete migration of libdexfile and libartbase to CommonArtTest. (libprofile and clients remain on CommonRuntimeTest because of required refactorings.) Bug: 78651010 Bug: 72216369 Test: make -j 40 test-art-host-gtest make -j 2 test-art-target-gtest Change-Id: Id10d8fc9002e0ad9451730627dfd848f5761c90c --- dexdump/Android.bp | 4 --- libartbase/base/common_art_test.cc | 18 ++++++++----- libartbase/base/common_art_test.h | 3 +++ libartbase/base/file_utils_test.cc | 8 +++--- libdexfile/dex/art_dex_file_loader_test.cc | 31 +++++++++++++--------- 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/dexdump/Android.bp b/dexdump/Android.bp index 2f0962c19a..ac9a9a2932 100644 --- a/dexdump/Android.bp +++ b/dexdump/Android.bp @@ -23,10 +23,6 @@ cc_defaults { "dexdump.cc", ], cflags: ["-Wall", "-Werror"], - // TODO: fix b/72216369 and remove the need for this. - include_dirs: [ - "art/runtime" // dex utils. - ], } art_cc_binary { diff --git a/libartbase/base/common_art_test.cc b/libartbase/base/common_art_test.cc index 0d798f37f7..67413eb85c 100644 --- a/libartbase/base/common_art_test.cc +++ b/libartbase/base/common_art_test.cc @@ -355,17 +355,18 @@ std::string CommonArtTestImpl::GetTestDexFileName(const char* name) const { return filename; } -std::vector> CommonArtTestImpl::OpenTestDexFiles(const char* name) { - std::string filename = GetTestDexFileName(name); +std::vector> CommonArtTestImpl::OpenDexFiles(const char* filename) { + static constexpr bool kVerify = true; static constexpr bool kVerifyChecksum = true; std::string error_msg; const ArtDexFileLoader dex_file_loader; std::vector> dex_files; - bool success = dex_file_loader.Open(filename.c_str(), - filename.c_str(), - /* verify */ true, + bool success = dex_file_loader.Open(filename, + filename, + kVerify, kVerifyChecksum, - &error_msg, &dex_files); + &error_msg, + &dex_files); CHECK(success) << "Failed to open '" << filename << "': " << error_msg; for (auto& dex_file : dex_files) { CHECK_EQ(PROT_READ, dex_file->GetPermissions()); @@ -374,6 +375,11 @@ std::vector> CommonArtTestImpl::OpenTestDexFiles( return dex_files; } +std::vector> CommonArtTestImpl::OpenTestDexFiles( + const char* name) { + return OpenDexFiles(GetTestDexFileName(name).c_str()); +} + std::unique_ptr CommonArtTestImpl::OpenTestDexFile(const char* name) { std::vector> vector = OpenTestDexFiles(name); EXPECT_EQ(1U, vector.size()); diff --git a/libartbase/base/common_art_test.h b/libartbase/base/common_art_test.h index a4764c275d..3998be516d 100644 --- a/libartbase/base/common_art_test.h +++ b/libartbase/base/common_art_test.h @@ -148,6 +148,9 @@ class CommonArtTestImpl { std::string GetTestAndroidRoot(); + // Open a file (allows reading of framework jars). + std::vector> OpenDexFiles(const char* filename); + // Open a test file (art-gtest-*.jar). std::vector> OpenTestDexFiles(const char* name); std::unique_ptr OpenTestDexFile(const char* name); diff --git a/libartbase/base/file_utils_test.cc b/libartbase/base/file_utils_test.cc index e74dfe5e64..56d1c44fc0 100644 --- a/libartbase/base/file_utils_test.cc +++ b/libartbase/base/file_utils_test.cc @@ -20,11 +20,11 @@ #include #include "base/stl_util.h" -#include "common_runtime_test.h" +#include "common_art_test.h" namespace art { -class FileUtilsTest : public CommonRuntimeTest {}; +class FileUtilsTest : public CommonArtTest {}; TEST_F(FileUtilsTest, GetDalvikCacheFilename) { std::string name; @@ -63,7 +63,7 @@ TEST_F(FileUtilsTest, GetAndroidRootSafe) { // We don't expect null returns for most cases, so don't check and let std::string crash. - // CommonRuntimeTest sets ANDROID_ROOT, so expect this to be the same. + // CommonArtTest sets ANDROID_ROOT, so expect this to be the same. std::string android_root = GetAndroidRootSafe(&error_msg); std::string android_root_env = getenv("ANDROID_ROOT"); EXPECT_EQ(android_root, android_root_env); @@ -78,7 +78,7 @@ TEST_F(FileUtilsTest, GetAndroidRootSafe) { // Set a bogus value for ANDROID_ROOT. This should be an error. ASSERT_EQ(0, setenv("ANDROID_ROOT", "/this/is/obviously/bogus", 1 /* overwrite */)); - EXPECT_TRUE(GetAndroidRootSafe(&error_msg) == nullptr); + EXPECT_EQ(GetAndroidRootSafe(&error_msg), ""); // Unset ANDROID_ROOT and see that it still returns something (as libart code is running). ASSERT_EQ(0, unsetenv("ANDROID_ROOT")); diff --git a/libdexfile/dex/art_dex_file_loader_test.cc b/libdexfile/dex/art_dex_file_loader_test.cc index d353c26b35..5f3fc0266f 100644 --- a/libdexfile/dex/art_dex_file_loader_test.cc +++ b/libdexfile/dex/art_dex_file_loader_test.cc @@ -14,26 +14,25 @@ * limitations under the License. */ +#include "art_dex_file_loader.h" + #include #include #include -#include "art_dex_file_loader.h" +#include "base/common_art_test.h" #include "base/file_utils.h" #include "base/mem_map.h" #include "base/os.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" -#include "common_runtime_test.h" #include "dex/base64_test_util.h" #include "dex/code_item_accessors-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" -#include "scoped_thread_state_change-inl.h" -#include "thread-current-inl.h" namespace art { @@ -43,26 +42,35 @@ static void Copy(const std::string& src, const std::string& dst) { dst_stream << src_stream.rdbuf(); } -class ArtDexFileLoaderTest : public CommonRuntimeTest {}; +class ArtDexFileLoaderTest : public CommonArtTest { + void SetUp() OVERRIDE { + CommonArtTest::SetUp(); + // Open a jar file from the boot classpath for use in basic tests of dex accessors. + std::vector lib_core_dex_file_names = GetLibCoreDexFileNames(); + CHECK_NE(lib_core_dex_file_names.size(), 0U); + dex_files_ = OpenDexFiles(lib_core_dex_file_names[0].c_str()); + CHECK_NE(dex_files_.size(), 0U); + // Save a dex file for use by tests. + java_lang_dex_file_ = dex_files_[0].get(); + } -// TODO: Port OpenTestDexFile(s) need to be ported to use non-ART utilities, and -// the tests that depend upon them should be moved to dex_file_loader_test.cc + protected: + std::vector> dex_files_; + const DexFile* java_lang_dex_file_; +}; TEST_F(ArtDexFileLoaderTest, Open) { - ScopedObjectAccess soa(Thread::Current()); std::unique_ptr dex(OpenTestDexFile("Nested")); ASSERT_TRUE(dex.get() != nullptr); } TEST_F(ArtDexFileLoaderTest, GetLocationChecksum) { - ScopedObjectAccess soa(Thread::Current()); std::unique_ptr raw(OpenTestDexFile("Main")); EXPECT_NE(raw->GetHeader().checksum_, raw->GetLocationChecksum()); } TEST_F(ArtDexFileLoaderTest, GetChecksum) { std::vector checksums; - ScopedObjectAccess soa(Thread::Current()); std::string error_msg; const ArtDexFileLoader dex_file_loader; EXPECT_TRUE(dex_file_loader.GetMultiDexChecksums(GetLibCoreDexFileNames()[0].c_str(), @@ -94,7 +102,6 @@ TEST_F(ArtDexFileLoaderTest, GetMultiDexChecksums) { } TEST_F(ArtDexFileLoaderTest, ClassDefs) { - ScopedObjectAccess soa(Thread::Current()); std::unique_ptr raw(OpenTestDexFile("Nested")); ASSERT_TRUE(raw.get() != nullptr); EXPECT_EQ(3U, raw->NumClassDefs()); @@ -110,7 +117,6 @@ TEST_F(ArtDexFileLoaderTest, ClassDefs) { } TEST_F(ArtDexFileLoaderTest, GetMethodSignature) { - ScopedObjectAccess soa(Thread::Current()); std::unique_ptr raw(OpenTestDexFile("GetMethodSignature")); ASSERT_TRUE(raw.get() != nullptr); EXPECT_EQ(1U, raw->NumClassDefs()); @@ -215,7 +221,6 @@ TEST_F(ArtDexFileLoaderTest, GetMethodSignature) { } TEST_F(ArtDexFileLoaderTest, FindStringId) { - ScopedObjectAccess soa(Thread::Current()); std::unique_ptr raw(OpenTestDexFile("GetMethodSignature")); ASSERT_TRUE(raw.get() != nullptr); EXPECT_EQ(1U, raw->NumClassDefs()); -- GitLab From 2eabc61c9592f18eb02211dd3e76e6d7e6793747 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 25 May 2018 14:31:16 -0700 Subject: [PATCH 474/749] Fix class_def_idx increment for FastVerify Fixes vdex golem benchmarks. Also fixed logic that was overwriting the expected ClassStatus for debug builds. This is why the tests were passing but the CHECK was failing for non debug dex2oat. Added regression test in dex2oat_test. Bug: 80309967 Test: test-art-host Change-Id: I91c48ffbe894ab471572418142fcb4ae85bf0864 --- compiler/driver/compiler_driver.cc | 7 ++--- dex2oat/dex2oat_test.cc | 50 +++++++++++++++++------------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 1b809d232a..decb330f3b 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1935,11 +1935,10 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, // Just update the compiled_classes_ map. The compiler doesn't need to resolve // the type. ClassReference ref(dex_file, class_def_idx); - ClassStatus existing = ClassStatus::kNotReady; - DCHECK(compiled_classes_.Get(ref, &existing)) << ref.dex_file->GetLocation(); + const ClassStatus existing = ClassStatus::kNotReady; ClassStateTable::InsertResult result = compiled_classes_.Insert(ref, existing, ClassStatus::kVerified); - CHECK_EQ(result, ClassStateTable::kInsertResultSuccess); + CHECK_EQ(result, ClassStateTable::kInsertResultSuccess) << ref.dex_file->GetLocation(); } else { // Update the class status, so later compilation stages know they don't need to verify // the class. @@ -1962,8 +1961,8 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, class_loader, soa.Self()); } + ++class_def_idx; } - ++class_def_idx; } return true; } diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 2fe16f7cb7..96d7dba225 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -1912,29 +1912,35 @@ TEST_F(Dex2oatTest, DontExtract) { ASSERT_EQ(dm_file.GetFile()->Flush(), 0); } + auto generate_and_check = [&](CompilerFilter::Filter filter) { + GenerateOdexForTest(dex_location, + odex_location, + filter, + { "--dump-timings", + "--dm-file=" + dm_file.GetFilename(), + // Pass -Xuse-stderr-logger have dex2oat output in output_ on target. + "--runtime-arg", + "-Xuse-stderr-logger" }, + true, // expect_success + false, // use_fd + [](const OatFile& o) { + CHECK(o.ContainsDexCode()); + }); + // Check the output for "Fast verify", this is printed from --dump-timings. + std::istringstream iss(output_); + std::string line; + bool found_fast_verify = false; + const std::string kFastVerifyString = "Fast Verify"; + while (std::getline(iss, line) && !found_fast_verify) { + found_fast_verify = found_fast_verify || line.find(kFastVerifyString) != std::string::npos; + } + EXPECT_TRUE(found_fast_verify) << "Expected to find " << kFastVerifyString << "\n" << output_; + }; + // Generate a quickened dex by using the input dm file to verify. - GenerateOdexForTest(dex_location, - odex_location, - CompilerFilter::Filter::kQuicken, - { "--dump-timings", - "--dm-file=" + dm_file.GetFilename(), - // Pass -Xuse-stderr-logger have dex2oat output in output_ on target. - "--runtime-arg", - "-Xuse-stderr-logger" }, - true, // expect_success - false, // use_fd - [](const OatFile& o) { - CHECK(o.ContainsDexCode()); - }); - // Check the output for "Fast verify", this is printed from --dump-timings. - std::istringstream iss(output_); - std::string line; - bool found_fast_verify = false; - const std::string kFastVerifyString = "Fast Verify"; - while (std::getline(iss, line) && !found_fast_verify) { - found_fast_verify = found_fast_verify || line.find(kFastVerifyString) != std::string::npos; - } - EXPECT_TRUE(found_fast_verify) << "Expected to find " << kFastVerifyString << "\n" << output_; + generate_and_check(CompilerFilter::Filter::kQuicken); + // Use verify compiler filter to sanity check that FastVerify works for that filter too. + generate_and_check(CompilerFilter::Filter::kVerify); } // Test that dex files with quickened opcodes aren't dequickened. -- GitLab From 6baa1c928ba2bd9127b370729d96ff4bcaafcea4 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Fri, 25 May 2018 16:17:49 -0700 Subject: [PATCH 475/749] ART: Ensure correct thread state for stack trace collection As the closure needs the mutator lock, the calling thread must hold the mutator lock. Ensure that that is the case. Test: m test-art-host Test: art/test/testrunner/testrunner.py -b --host --trace --interpreter 911 Change-Id: I47d36abba83a0f38b398b9e3ef6f485b5e735622 --- openjdkjvmti/ti_stack.cc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc index eee8108b01..318d98d877 100644 --- a/openjdkjvmti/ti_stack.cc +++ b/openjdkjvmti/ti_stack.cc @@ -322,7 +322,9 @@ struct GetAllStackTracesVectorClosure : public art::Closure { }; template -static void RunCheckpointAndWait(Data* data, size_t max_frame_count) { +static void RunCheckpointAndWait(Data* data, size_t max_frame_count) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + // Note: requires the mutator lock as the checkpoint requires the mutator lock. GetAllStackTracesVectorClosure closure(max_frame_count, data); size_t barrier_count = art::Runtime::Current()->GetThreadList()->RunCheckpoint(&closure, nullptr); if (barrier_count == 0) { @@ -380,9 +382,11 @@ jvmtiError StackUtil::GetAllStackTraces(jvmtiEnv* env, }; AllStackTracesData data; - RunCheckpointAndWait(&data, static_cast(max_frame_count)); - art::Thread* current = art::Thread::Current(); + { + art::ScopedObjectAccess soa(current); + RunCheckpointAndWait(&data, static_cast(max_frame_count)); + } // Convert the data into our output format. -- GitLab From 5513c2b68a08109a5bfd811c7b2c8bbc66244e8e Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Thu, 24 May 2018 15:03:20 +0100 Subject: [PATCH 476/749] Add BitmapTableBuilder. This will allow the use of BitTable for masks. Test: test-art-host-gtest-bit_table_test Change-Id: I81bb79a5bd82d63de4449d7f1e28ef6722491cbc --- libartbase/base/bit_memory_region.h | 15 ++++- libartbase/base/bit_table.h | 94 +++++++++++++++++++++++++++++ libartbase/base/bit_table_test.cc | 71 +++++++++++++++++++--- 3 files changed, 170 insertions(+), 10 deletions(-) diff --git a/libartbase/base/bit_memory_region.h b/libartbase/base/bit_memory_region.h index 3f4d0ba55b..a3d3ee41d6 100644 --- a/libartbase/base/bit_memory_region.h +++ b/libartbase/base/bit_memory_region.h @@ -67,7 +67,7 @@ class BitMemoryRegion FINAL : public ValueObject { return ((data_[index] >> shift) & 1) != 0; } - ALWAYS_INLINE void StoreBit(uintptr_t bit_offset, bool value) const { + ALWAYS_INLINE void StoreBit(uintptr_t bit_offset, bool value) { DCHECK_LT(bit_offset, bit_size_); uint8_t* data = reinterpret_cast(data_); size_t index = (bit_start_ + bit_offset) / kBitsPerByte; @@ -138,6 +138,19 @@ class BitMemoryRegion FINAL : public ValueObject { *bit_offset += bit_length; } + // Store bits from other bit region. + ALWAYS_INLINE void StoreBits(size_t bit_offset, const BitMemoryRegion& src, size_t bit_length) { + DCHECK_LE(bit_offset, bit_size_); + DCHECK_LE(bit_length, bit_size_ - bit_offset); + size_t bit = 0; + constexpr size_t kNumBits = BitSizeOf(); + for (; bit + kNumBits <= bit_length; bit += kNumBits) { + StoreBits(bit_offset + bit, src.LoadBits(bit, kNumBits), kNumBits); + } + size_t num_bits = bit_length - bit; + StoreBits(bit_offset + bit, src.LoadBits(bit, num_bits), num_bits); + } + ALWAYS_INLINE bool Equals(const BitMemoryRegion& other) const { return data_ == other.data_ && bit_start_ == other.bit_start_ && diff --git a/libartbase/base/bit_table.h b/libartbase/base/bit_table.h index fbded2785c..8cfd044703 100644 --- a/libartbase/base/bit_table.h +++ b/libartbase/base/bit_table.h @@ -126,6 +126,13 @@ class BitTable { return table_data_.LoadBits(offset, NumColumnBits(column)) + kValueBias; } + ALWAYS_INLINE BitMemoryRegion GetBitMemoryRegion(uint32_t row, uint32_t column = 0) const { + DCHECK_LT(row, num_rows_); + DCHECK_LT(column, kNumColumns); + size_t offset = row * NumRowBits() + column_offset_[column]; + return table_data_.Subregion(offset, NumColumnBits(column)); + } + size_t NumRows() const { return num_rows_; } uint32_t NumRowBits() const { return column_offset_[kNumColumns]; } @@ -286,6 +293,93 @@ class BitTableBuilder { template constexpr size_t BitTableBuilder::kNumColumns; +// Helper class for encoding single-column BitTable of bitmaps (allows more than 32 bits). +class BitmapTableBuilder { + public: + explicit BitmapTableBuilder(ScopedArenaAllocator* const allocator) + : allocator_(allocator), + rows_(allocator->Adapter(kArenaAllocBitTableBuilder)), + dedup_(8, allocator_->Adapter(kArenaAllocBitTableBuilder)) { + } + + MemoryRegion operator[](size_t row) { return rows_[row]; } + const MemoryRegion operator[](size_t row) const { return rows_[row]; } + size_t size() const { return rows_.size(); } + + // Add the given bitmap to the table and return its index. + // If the bitmap was already added it will be deduplicated. + // The last bit must be set and any padding bits in the last byte must be zero. + uint32_t Dedup(const void* bitmap, size_t num_bits) { + MemoryRegion region(const_cast(bitmap), BitsToBytesRoundUp(num_bits)); + DCHECK(num_bits == 0 || BitMemoryRegion(region).LoadBit(num_bits - 1) == 1); + DCHECK_EQ(BitMemoryRegion(region).LoadBits(num_bits, region.size_in_bits() - num_bits), 0u); + FNVHash hasher; + uint32_t hash = hasher(region); + + // Check if we have already added identical bitmap. + auto range = dedup_.equal_range(hash); + for (auto it = range.first; it != range.second; ++it) { + if (MemoryRegion::ContentEquals()(region, rows_[it->second])) { + return it->second; + } + } + + // Add the bitmap and add the index to the dedup map. + uint32_t index = size(); + void* copy = allocator_->Alloc(region.size(), kArenaAllocBitTableBuilder); + memcpy(copy, region.pointer(), region.size()); + rows_.push_back(MemoryRegion(copy, region.size())); + dedup_.emplace(hash, index); + max_num_bits_ = std::max(max_num_bits_, num_bits); + return index; + } + + // Encode the stored data into a BitTable. + template + void Encode(Vector* out, size_t* bit_offset) const { + size_t initial_bit_offset = *bit_offset; + + EncodeVarintBits(out, bit_offset, size()); + if (size() != 0) { + EncodeVarintBits(out, bit_offset, max_num_bits_); + + // Write table data. + out->resize(BitsToBytesRoundUp(*bit_offset + max_num_bits_ * size())); + BitMemoryRegion region(MemoryRegion(out->data(), out->size())); + for (MemoryRegion row : rows_) { + BitMemoryRegion src(row); + region.StoreBits(*bit_offset, src, std::min(max_num_bits_, src.size_in_bits())); + *bit_offset += max_num_bits_; + } + } + + // Verify the written data. + if (kIsDebugBuild) { + BitTable<1> table; + BitMemoryRegion region(MemoryRegion(out->data(), out->size())); + table.Decode(region, &initial_bit_offset); + DCHECK_EQ(size(), table.NumRows()); + DCHECK_EQ(max_num_bits_, table.NumColumnBits(0)); + for (uint32_t r = 0; r < size(); r++) { + BitMemoryRegion expected(rows_[r]); + BitMemoryRegion seen = table.GetBitMemoryRegion(r); + size_t num_bits = std::max(expected.size_in_bits(), seen.size_in_bits()); + for (size_t b = 0; b < num_bits; b++) { + bool e = b < expected.size_in_bits() && expected.LoadBit(b); + bool s = b < seen.size_in_bits() && seen.LoadBit(b); + DCHECK_EQ(e, s) << " (" << r << ")[" << b << "]"; + } + } + } + } + + private: + ScopedArenaAllocator* const allocator_; + ScopedArenaDeque rows_; + ScopedArenaUnorderedMultimap dedup_; // Hash -> row index. + size_t max_num_bits_ = 0u; +}; + } // namespace art #endif // ART_LIBARTBASE_BASE_BIT_TABLE_H_ diff --git a/libartbase/base/bit_table_test.cc b/libartbase/base/bit_table_test.cc index f579440199..8abf0da9d9 100644 --- a/libartbase/base/bit_table_test.cc +++ b/libartbase/base/bit_table_test.cc @@ -154,20 +154,73 @@ TEST(BitTableTest, TestDedup) { BitTableBuilder builder(&allocator); RowData value0{1, 2}; RowData value1{3, 4}; - RowData value2{56948505, 0}; - RowData value3{67108869, 0}; + EXPECT_EQ(0u, builder.Dedup(&value0)); + EXPECT_EQ(1u, builder.Dedup(&value1)); + EXPECT_EQ(0u, builder.Dedup(&value0)); + EXPECT_EQ(1u, builder.Dedup(&value1)); + EXPECT_EQ(2u, builder.size()); +} + +TEST(BitTableTest, TestBitmapTable) { + MallocArenaPool pool; + ArenaStack arena_stack(&pool); + ScopedArenaAllocator allocator(&arena_stack); + + std::vector buffer; + size_t encode_bit_offset = 0; + const uint64_t value = 0xDEADBEEF0BADF00Dull; + BitmapTableBuilder builder(&allocator); + std::multimap indicies; // bitmap -> row. + for (size_t bit_length = 0; bit_length <= BitSizeOf(); ++bit_length) { + uint64_t bitmap = value & MaxInt(bit_length); + indicies.emplace(bitmap, builder.Dedup(&bitmap, MinimumBitsToStore(bitmap))); + } + builder.Encode(&buffer, &encode_bit_offset); + EXPECT_EQ(1 + static_cast(POPCOUNT(value)), builder.size()); + + size_t decode_bit_offset = 0; + BitTable<1> table(buffer.data(), buffer.size(), &decode_bit_offset); + EXPECT_EQ(encode_bit_offset, decode_bit_offset); + for (auto it : indicies) { + uint64_t expected = it.first; + BitMemoryRegion actual = table.GetBitMemoryRegion(it.second); + EXPECT_GE(actual.size_in_bits(), MinimumBitsToStore(expected)); + for (size_t b = 0; b < actual.size_in_bits(); b++, expected >>= 1) { + EXPECT_EQ(expected & 1, actual.LoadBit(b)) << "b=" << b; + } + } +} + +TEST(BitTableTest, TestCollisions) { + MallocArenaPool pool; + ArenaStack arena_stack(&pool); + ScopedArenaAllocator allocator(&arena_stack); FNVHash hasher; - EXPECT_EQ(hasher(MemoryRegion(&value2, sizeof(RowData))), - hasher(MemoryRegion(&value3, sizeof(RowData)))); // Test hash collision. + + struct RowData { + uint32_t a; + uint32_t b; + }; + RowData value0{56948505, 0}; + RowData value1{67108869, 0}; + + BitTableBuilder builder(&allocator); + EXPECT_EQ(hasher(MemoryRegion(&value0, sizeof(RowData))), + hasher(MemoryRegion(&value1, sizeof(RowData)))); EXPECT_EQ(0u, builder.Dedup(&value0)); EXPECT_EQ(1u, builder.Dedup(&value1)); - EXPECT_EQ(2u, builder.Dedup(&value2)); - EXPECT_EQ(3u, builder.Dedup(&value3)); EXPECT_EQ(0u, builder.Dedup(&value0)); EXPECT_EQ(1u, builder.Dedup(&value1)); - EXPECT_EQ(2u, builder.Dedup(&value2)); - EXPECT_EQ(3u, builder.Dedup(&value3)); - EXPECT_EQ(4u, builder.size()); + EXPECT_EQ(2u, builder.size()); + + BitmapTableBuilder builder2(&allocator); + EXPECT_EQ(hasher(MemoryRegion(&value0, BitsToBytesRoundUp(MinimumBitsToStore(value0.a)))), + hasher(MemoryRegion(&value1, BitsToBytesRoundUp(MinimumBitsToStore(value1.a))))); + EXPECT_EQ(0u, builder2.Dedup(&value0.a, MinimumBitsToStore(value0.a))); + EXPECT_EQ(1u, builder2.Dedup(&value1.a, MinimumBitsToStore(value1.a))); + EXPECT_EQ(0u, builder2.Dedup(&value0.a, MinimumBitsToStore(value0.a))); + EXPECT_EQ(1u, builder2.Dedup(&value1.a, MinimumBitsToStore(value1.a))); + EXPECT_EQ(2u, builder2.size()); } } // namespace art -- GitLab From ffaf87a429766ed80e6afee5bebea93db539620b Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Wed, 23 May 2018 14:44:39 +0100 Subject: [PATCH 477/749] Optimize register mask and stack mask in stack maps. Use BitTable to store the masks as well and move the deduplication responsibility to the BitTable builders. Don't generate entries for masks which are all zeros. This saves 0.2% of .oat file size on both Arm64 and Arm. Encode registers as (value+shift) due to tailing zeros. This saves 1.0% of .oat file size on Arm64 and 0.2% on Arm. Test: test-art-host-gtest Change-Id: I636b7edd49e10e8afc9f2aa385b5980f7ee0e1f1 --- compiler/optimizing/stack_map_stream.cc | 101 +++++++----------------- compiler/optimizing/stack_map_stream.h | 12 --- compiler/optimizing/stack_map_test.cc | 4 +- oatdump/oatdump.cc | 2 +- runtime/oat.h | 4 +- runtime/quick_exception_handler.cc | 2 +- runtime/stack_map.cc | 2 +- runtime/stack_map.h | 39 ++++++--- runtime/thread.cc | 3 +- 9 files changed, 63 insertions(+), 106 deletions(-) diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index c6e375a1b2..b40ea3768a 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -48,10 +48,6 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream); current_entry_.dex_register_entry.live_dex_registers_mask->ClearAllBits(); } - if (sp_mask != nullptr) { - stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet()); - } - current_dex_register_ = 0; } @@ -217,11 +213,32 @@ size_t StackMapStream::PrepareForFillIn() { PrepareMethodIndices(); // Dedup stack masks. Needs to be done first as it modifies the stack map entry. - size_t stack_mask_bits = stack_mask_max_ + 1; // Need room for max element too. - size_t num_stack_masks = PrepareStackMasks(stack_mask_bits); + BitmapTableBuilder stack_mask_builder(allocator_); + for (StackMapEntry& stack_map : stack_maps_) { + BitVector* mask = stack_map.sp_mask; + size_t num_bits = (mask != nullptr) ? mask->GetNumberOfBits() : 0; + if (num_bits != 0) { + stack_map.stack_mask_index = stack_mask_builder.Dedup(mask->GetRawStorage(), num_bits); + } else { + stack_map.stack_mask_index = StackMap::kNoValue; + } + } // Dedup register masks. Needs to be done first as it modifies the stack map entry. - size_t num_register_masks = PrepareRegisterMasks(); + BitTableBuilder> register_mask_builder(allocator_); + for (StackMapEntry& stack_map : stack_maps_) { + uint32_t register_mask = stack_map.register_mask; + if (register_mask != 0) { + uint32_t shift = LeastSignificantBit(register_mask); + std::array entry = { + register_mask >> shift, + shift, + }; + stack_map.register_mask_index = register_mask_builder.Dedup(&entry); + } else { + stack_map.register_mask_index = StackMap::kNoValue; + } + } // Write dex register maps. MemoryRegion dex_register_map_region = @@ -301,31 +318,8 @@ size_t StackMapStream::PrepareForFillIn() { stack_map_builder.Encode(&out_, &bit_offset); invoke_info_builder.Encode(&out_, &bit_offset); inline_info_builder.Encode(&out_, &bit_offset); - - // Write register masks table. - BitTableBuilder register_mask_builder(allocator_); - for (size_t i = 0; i < num_register_masks; ++i) { - register_mask_builder.Add(register_masks_[i]); - } register_mask_builder.Encode(&out_, &bit_offset); - - // Write stack masks table. - EncodeVarintBits(&out_, &bit_offset, stack_mask_bits); - out_.resize(BitsToBytesRoundUp(bit_offset + stack_mask_bits * num_stack_masks)); - BitMemoryRegion stack_mask_region(MemoryRegion(out_.data(), out_.size()), - bit_offset, - stack_mask_bits * num_stack_masks); - if (stack_mask_bits > 0) { - for (size_t i = 0; i < num_stack_masks; ++i) { - size_t stack_mask_bytes = BitsToBytesRoundUp(stack_mask_bits); - BitMemoryRegion src(MemoryRegion(&stack_masks_[i * stack_mask_bytes], stack_mask_bytes)); - BitMemoryRegion dst = stack_mask_region.Subregion(i * stack_mask_bits, stack_mask_bits); - for (size_t bit_index = 0; bit_index < stack_mask_bits; bit_index += BitSizeOf()) { - size_t num_bits = std::min(stack_mask_bits - bit_index, BitSizeOf()); - dst.StoreBits(bit_index, src.LoadBits(bit_index, num_bits), num_bits); - } - } - } + stack_mask_builder.Encode(&out_, &bit_offset); return UnsignedLeb128Size(out_.size()) + out_.size(); } @@ -448,17 +442,6 @@ void StackMapStream::CheckDexRegisterMap(const CodeInfo& code_info, } } -size_t StackMapStream::PrepareRegisterMasks() { - register_masks_.resize(stack_maps_.size(), 0u); - ScopedArenaUnorderedMap dedupe(allocator_->Adapter(kArenaAllocStackMapStream)); - for (StackMapEntry& stack_map : stack_maps_) { - const size_t index = dedupe.size(); - stack_map.register_mask_index = dedupe.emplace(stack_map.register_mask, index).first->second; - register_masks_[index] = stack_map.register_mask; - } - return dedupe.size(); -} - void StackMapStream::PrepareMethodIndices() { CHECK(method_indices_.empty()); method_indices_.resize(stack_maps_.size() + inline_infos_.size()); @@ -481,35 +464,10 @@ void StackMapStream::PrepareMethodIndices() { method_indices_.resize(dedupe.size()); } - -size_t StackMapStream::PrepareStackMasks(size_t entry_size_in_bits) { - // Preallocate memory since we do not want it to move (the dedup map will point into it). - const size_t byte_entry_size = RoundUp(entry_size_in_bits, kBitsPerByte) / kBitsPerByte; - stack_masks_.resize(byte_entry_size * stack_maps_.size(), 0u); - // For deduplicating we store the stack masks as byte packed for simplicity. We can bit pack later - // when copying out from stack_masks_. - ScopedArenaUnorderedMap, - MemoryRegion::ContentEquals> dedup( - stack_maps_.size(), allocator_->Adapter(kArenaAllocStackMapStream)); - for (StackMapEntry& stack_map : stack_maps_) { - size_t index = dedup.size(); - MemoryRegion stack_mask(stack_masks_.data() + index * byte_entry_size, byte_entry_size); - BitMemoryRegion stack_mask_bits(stack_mask); - for (size_t i = 0; i < entry_size_in_bits; i++) { - stack_mask_bits.StoreBit(i, stack_map.sp_mask != nullptr && stack_map.sp_mask->IsBitSet(i)); - } - stack_map.stack_mask_index = dedup.emplace(stack_mask, index).first->second; - } - return dedup.size(); -} - // Check that all StackMapStream inputs are correctly encoded by trying to read them back. void StackMapStream::CheckCodeInfo(MemoryRegion region) const { CodeInfo code_info(region); DCHECK_EQ(code_info.GetNumberOfStackMaps(), stack_maps_.size()); - DCHECK_EQ(code_info.GetNumberOfStackMaskBits(), static_cast(stack_mask_max_ + 1)); DCHECK_EQ(code_info.GetNumberOfLocationCatalogEntries(), location_catalog_entries_.size()); size_t invoke_info_index = 0; for (size_t s = 0; s < stack_maps_.size(); ++s) { @@ -522,18 +480,15 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { DCHECK_EQ(stack_map.GetDexPc(), entry.dex_pc); DCHECK_EQ(stack_map.GetRegisterMaskIndex(), entry.register_mask_index); DCHECK_EQ(code_info.GetRegisterMaskOf(stack_map), entry.register_mask); - const size_t num_stack_mask_bits = code_info.GetNumberOfStackMaskBits(); DCHECK_EQ(stack_map.GetStackMaskIndex(), entry.stack_mask_index); BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map); if (entry.sp_mask != nullptr) { DCHECK_GE(stack_mask.size_in_bits(), entry.sp_mask->GetNumberOfBits()); - for (size_t b = 0; b < num_stack_mask_bits; b++) { - DCHECK_EQ(stack_mask.LoadBit(b), entry.sp_mask->IsBitSet(b)); + for (size_t b = 0; b < stack_mask.size_in_bits(); b++) { + DCHECK_EQ(stack_mask.LoadBit(b), entry.sp_mask->IsBitSet(b)) << b; } } else { - for (size_t b = 0; b < num_stack_mask_bits; b++) { - DCHECK_EQ(stack_mask.LoadBit(b), 0u); - } + DCHECK_EQ(stack_mask.size_in_bits(), 0u); } if (entry.dex_method_index != dex::kDexNoIndex) { InvokeInfo invoke_info = code_info.GetInvokeInfo(invoke_info_index); diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index ea97cf6530..19863d882a 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -68,11 +68,8 @@ class StackMapStream : public ValueObject { location_catalog_entries_indices_(allocator->Adapter(kArenaAllocStackMapStream)), dex_register_locations_(allocator->Adapter(kArenaAllocStackMapStream)), inline_infos_(allocator->Adapter(kArenaAllocStackMapStream)), - stack_masks_(allocator->Adapter(kArenaAllocStackMapStream)), - register_masks_(allocator->Adapter(kArenaAllocStackMapStream)), method_indices_(allocator->Adapter(kArenaAllocStackMapStream)), dex_register_entries_(allocator->Adapter(kArenaAllocStackMapStream)), - stack_mask_max_(-1), out_(allocator->Adapter(kArenaAllocStackMapStream)), dex_map_hash_to_stack_map_indices_(std::less(), allocator->Adapter(kArenaAllocStackMapStream)), @@ -171,12 +168,6 @@ class StackMapStream : public ValueObject { private: size_t ComputeDexRegisterLocationCatalogSize() const; - // Returns the number of unique stack masks. - size_t PrepareStackMasks(size_t entry_size_in_bits); - - // Returns the number of unique register masks. - size_t PrepareRegisterMasks(); - // Prepare and deduplicate method indices. void PrepareMethodIndices(); @@ -217,11 +208,8 @@ class StackMapStream : public ValueObject { // A set of concatenated maps of Dex register locations indices to `location_catalog_entries_`. ScopedArenaVector dex_register_locations_; ScopedArenaVector inline_infos_; - ScopedArenaVector stack_masks_; - ScopedArenaVector register_masks_; ScopedArenaVector method_indices_; ScopedArenaVector dex_register_entries_; - int stack_mask_max_; ScopedArenaVector out_; diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index 9db7588b3a..c372bb9b22 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -32,10 +32,10 @@ static bool CheckStackMask( const StackMap& stack_map, const BitVector& bit_vector) { BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map); - if (bit_vector.GetNumberOfBits() > code_info.GetNumberOfStackMaskBits()) { + if (bit_vector.GetNumberOfBits() > stack_mask.size_in_bits()) { return false; } - for (size_t i = 0; i < code_info.GetNumberOfStackMaskBits(); ++i) { + for (size_t i = 0; i < stack_mask.size_in_bits(); ++i) { if (stack_mask.LoadBit(i) != bit_vector.IsBitSet(i)) { return false; } diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index fcd6bfd46c..b080f92689 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1731,7 +1731,7 @@ class OatDumper { // Stack masks stats_.AddBits( Stats::kByteKindCodeInfoStackMasks, - code_info.stack_masks_.size_in_bits()); + code_info.stack_masks_.DataBitSize()); // Register masks stats_.AddBits( diff --git a/runtime/oat.h b/runtime/oat.h index 7b8f71a3f3..8069a15661 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: Refactor stackmap encoding. - static constexpr uint8_t kOatVersion[] = { '1', '4', '4', '\0' }; + // Last oat version changed reason: Optimize masks in stack maps. + static constexpr uint8_t kOatVersion[] = { '1', '4', '5', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index de613d3b20..26489209b8 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -439,7 +439,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { const uint8_t* addr = reinterpret_cast(GetCurrentQuickFrame()) + offset; value = *reinterpret_cast(addr); uint32_t bit = (offset >> 2); - if (bit < code_info.GetNumberOfStackMaskBits() && stack_mask.LoadBit(bit)) { + if (bit < stack_mask.size_in_bits() && stack_mask.LoadBit(bit)) { is_reference = true; } break; diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index 2b7e8dd748..fd0e28d9ac 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -200,7 +200,7 @@ void StackMap::Dump(VariableIndentationOutputStream* vios, << std::dec << ", stack_mask=0b"; BitMemoryRegion stack_mask = code_info.GetStackMaskOf(*this); - for (size_t i = 0, e = code_info.GetNumberOfStackMaskBits(); i < e; ++i) { + for (size_t i = 0, e = stack_mask.size_in_bits(); i < e; ++i) { vios->Stream() << stack_mask.LoadBit(e - i - 1); } vios->Stream() << ")\n"; diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 91cecf0690..02d87130ec 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -799,6 +799,24 @@ class InvokeInfo : public BitTable<3>::Accessor { } }; +// Register masks tend to have many tailing zero bits, +// therefore it is worth encoding them as value+shift. +class RegisterMask : public BitTable<2>::Accessor { + public: + enum Field { + kValue, + kShift, + kCount, + }; + + RegisterMask(const BitTable* table, uint32_t row) + : BitTable::Accessor(table, row) {} + + ALWAYS_INLINE uint32_t GetMask() const { + return Get() << Get(); + } +}; + /** * Wrapper around all compiler information collected for a method. * The information is of the form: @@ -833,24 +851,22 @@ class CodeInfo { return DexRegisterLocationCatalog(location_catalog_); } - ALWAYS_INLINE size_t GetNumberOfStackMaskBits() const { - return stack_mask_bits_; - } - ALWAYS_INLINE StackMap GetStackMapAt(size_t index) const { return StackMap(&stack_maps_, index); } BitMemoryRegion GetStackMask(size_t index) const { - return stack_masks_.Subregion(index * stack_mask_bits_, stack_mask_bits_); + return stack_masks_.GetBitMemoryRegion(index); } BitMemoryRegion GetStackMaskOf(const StackMap& stack_map) const { - return GetStackMask(stack_map.GetStackMaskIndex()); + uint32_t index = stack_map.GetStackMaskIndex(); + return (index == StackMap::kNoValue) ? BitMemoryRegion() : GetStackMask(index); } uint32_t GetRegisterMaskOf(const StackMap& stack_map) const { - return register_masks_.Get(stack_map.GetRegisterMaskIndex()); + uint32_t index = stack_map.GetRegisterMaskIndex(); + return (index == StackMap::kNoValue) ? 0 : RegisterMask(®ister_masks_, index).GetMask(); } uint32_t GetNumberOfLocationCatalogEntries() const { @@ -1045,8 +1061,8 @@ class CodeInfo { invoke_infos_.Decode(bit_region, &bit_offset); inline_infos_.Decode(bit_region, &bit_offset); register_masks_.Decode(bit_region, &bit_offset); - stack_mask_bits_ = DecodeVarintBits(bit_region, &bit_offset); - stack_masks_ = bit_region.Subregion(bit_offset, non_header_size * kBitsPerByte - bit_offset); + stack_masks_.Decode(bit_region, &bit_offset); + CHECK_EQ(BitsToBytesRoundUp(bit_offset), non_header_size); } size_t size_; @@ -1056,9 +1072,8 @@ class CodeInfo { BitTable stack_maps_; BitTable invoke_infos_; BitTable inline_infos_; - BitTable<1> register_masks_; - uint32_t stack_mask_bits_ = 0; - BitMemoryRegion stack_masks_; + BitTable register_masks_; + BitTable<1> stack_masks_; friend class OatDumper; }; diff --git a/runtime/thread.cc b/runtime/thread.cc index 129bae6d9a..2e737f5258 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -3568,9 +3568,8 @@ class ReferenceMapVisitor : public StackVisitor { T vreg_info(m, code_info, map, visitor_); // Visit stack entries that hold pointers. - const size_t number_of_bits = code_info.GetNumberOfStackMaskBits(); BitMemoryRegion stack_mask = code_info.GetStackMaskOf(map); - for (size_t i = 0; i < number_of_bits; ++i) { + for (size_t i = 0; i < stack_mask.size_in_bits(); ++i) { if (stack_mask.LoadBit(i)) { StackReference* ref_addr = vreg_base + i; mirror::Object* ref = ref_addr->AsMirrorPtr(); -- GitLab From 57d9fe7ef99e9bf2145a3c3086ae5668bb25d28a Mon Sep 17 00:00:00 2001 From: Tamas Kenez Date: Tue, 29 May 2018 13:52:26 +0200 Subject: [PATCH 478/749] ART-tests: Enable D8 for 450-checker-types. D8 has been enabled in I6f319c7b9d8f2c72f9c739890acd6499f7ffcacd then disabled again in I31b0c0d57d344f54a8c0545fd32c81a893b4ec75. Test: art/test.py -r --host -t 450-checker-types Change-Id: I5317663565e28c1e2a9a609327f7d587a50f9182 --- test/450-checker-types/build | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100755 test/450-checker-types/build diff --git a/test/450-checker-types/build b/test/450-checker-types/build deleted file mode 100755 index 10ffcc537d..0000000000 --- a/test/450-checker-types/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" -- GitLab From a85eef747c0b16c65cb20457bd9b0027cabcfc22 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Tue, 29 May 2018 16:11:28 +0100 Subject: [PATCH 479/749] Fix test checking /system in chroot in clean-up script. Test that "$ART_TEST_CHROOT/system" is a directory, not a regular file. Test: adb shell rm -rf "$ART_TEST_CHROOT/system" && art/tools/cleanup-buildbot-device.sh Bug: 34729697 Change-Id: I35279e93963b046f57099b8f0d52aa6a0020ab5e --- tools/cleanup-buildbot-device.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cleanup-buildbot-device.sh b/tools/cleanup-buildbot-device.sh index 2144b02c2f..ca5219aa25 100755 --- a/tools/cleanup-buildbot-device.sh +++ b/tools/cleanup-buildbot-device.sh @@ -40,7 +40,7 @@ if [[ -n "$ART_TEST_CHROOT" ]]; then # # TODO: Reorder ART Buildbot steps so that "device cleanup" happens # before "setup device" and remove this special case. - adb shell test -f "$ART_TEST_CHROOT/system" \ + adb shell test -d "$ART_TEST_CHROOT/system" \ "&&" find "$ART_TEST_CHROOT/system" \ ! -path "$ART_TEST_CHROOT/system/etc/selinux/plat_property_contexts" \ ! -type d \ -- GitLab From 8b20b5c1f5b454b2f8b8bff492c88724b5002600 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 29 May 2018 15:32:55 +0000 Subject: [PATCH 480/749] Revert "Optimize register mask and stack mask in stack maps." This reverts commit ffaf87a429766ed80e6afee5bebea93db539620b. Reason for revert: Breaks exception_test32 on target for CMS and heap poisoning configs. Change-Id: I127c17f693e28211a799f73a50e73105edee7e4c --- compiler/optimizing/stack_map_stream.cc | 101 +++++++++++++++++------- compiler/optimizing/stack_map_stream.h | 12 +++ compiler/optimizing/stack_map_test.cc | 4 +- oatdump/oatdump.cc | 2 +- runtime/oat.h | 4 +- runtime/quick_exception_handler.cc | 2 +- runtime/stack_map.cc | 2 +- runtime/stack_map.h | 39 +++------ runtime/thread.cc | 3 +- 9 files changed, 106 insertions(+), 63 deletions(-) diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index b40ea3768a..c6e375a1b2 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -48,6 +48,10 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream); current_entry_.dex_register_entry.live_dex_registers_mask->ClearAllBits(); } + if (sp_mask != nullptr) { + stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet()); + } + current_dex_register_ = 0; } @@ -213,32 +217,11 @@ size_t StackMapStream::PrepareForFillIn() { PrepareMethodIndices(); // Dedup stack masks. Needs to be done first as it modifies the stack map entry. - BitmapTableBuilder stack_mask_builder(allocator_); - for (StackMapEntry& stack_map : stack_maps_) { - BitVector* mask = stack_map.sp_mask; - size_t num_bits = (mask != nullptr) ? mask->GetNumberOfBits() : 0; - if (num_bits != 0) { - stack_map.stack_mask_index = stack_mask_builder.Dedup(mask->GetRawStorage(), num_bits); - } else { - stack_map.stack_mask_index = StackMap::kNoValue; - } - } + size_t stack_mask_bits = stack_mask_max_ + 1; // Need room for max element too. + size_t num_stack_masks = PrepareStackMasks(stack_mask_bits); // Dedup register masks. Needs to be done first as it modifies the stack map entry. - BitTableBuilder> register_mask_builder(allocator_); - for (StackMapEntry& stack_map : stack_maps_) { - uint32_t register_mask = stack_map.register_mask; - if (register_mask != 0) { - uint32_t shift = LeastSignificantBit(register_mask); - std::array entry = { - register_mask >> shift, - shift, - }; - stack_map.register_mask_index = register_mask_builder.Dedup(&entry); - } else { - stack_map.register_mask_index = StackMap::kNoValue; - } - } + size_t num_register_masks = PrepareRegisterMasks(); // Write dex register maps. MemoryRegion dex_register_map_region = @@ -318,8 +301,31 @@ size_t StackMapStream::PrepareForFillIn() { stack_map_builder.Encode(&out_, &bit_offset); invoke_info_builder.Encode(&out_, &bit_offset); inline_info_builder.Encode(&out_, &bit_offset); + + // Write register masks table. + BitTableBuilder register_mask_builder(allocator_); + for (size_t i = 0; i < num_register_masks; ++i) { + register_mask_builder.Add(register_masks_[i]); + } register_mask_builder.Encode(&out_, &bit_offset); - stack_mask_builder.Encode(&out_, &bit_offset); + + // Write stack masks table. + EncodeVarintBits(&out_, &bit_offset, stack_mask_bits); + out_.resize(BitsToBytesRoundUp(bit_offset + stack_mask_bits * num_stack_masks)); + BitMemoryRegion stack_mask_region(MemoryRegion(out_.data(), out_.size()), + bit_offset, + stack_mask_bits * num_stack_masks); + if (stack_mask_bits > 0) { + for (size_t i = 0; i < num_stack_masks; ++i) { + size_t stack_mask_bytes = BitsToBytesRoundUp(stack_mask_bits); + BitMemoryRegion src(MemoryRegion(&stack_masks_[i * stack_mask_bytes], stack_mask_bytes)); + BitMemoryRegion dst = stack_mask_region.Subregion(i * stack_mask_bits, stack_mask_bits); + for (size_t bit_index = 0; bit_index < stack_mask_bits; bit_index += BitSizeOf()) { + size_t num_bits = std::min(stack_mask_bits - bit_index, BitSizeOf()); + dst.StoreBits(bit_index, src.LoadBits(bit_index, num_bits), num_bits); + } + } + } return UnsignedLeb128Size(out_.size()) + out_.size(); } @@ -442,6 +448,17 @@ void StackMapStream::CheckDexRegisterMap(const CodeInfo& code_info, } } +size_t StackMapStream::PrepareRegisterMasks() { + register_masks_.resize(stack_maps_.size(), 0u); + ScopedArenaUnorderedMap dedupe(allocator_->Adapter(kArenaAllocStackMapStream)); + for (StackMapEntry& stack_map : stack_maps_) { + const size_t index = dedupe.size(); + stack_map.register_mask_index = dedupe.emplace(stack_map.register_mask, index).first->second; + register_masks_[index] = stack_map.register_mask; + } + return dedupe.size(); +} + void StackMapStream::PrepareMethodIndices() { CHECK(method_indices_.empty()); method_indices_.resize(stack_maps_.size() + inline_infos_.size()); @@ -464,10 +481,35 @@ void StackMapStream::PrepareMethodIndices() { method_indices_.resize(dedupe.size()); } + +size_t StackMapStream::PrepareStackMasks(size_t entry_size_in_bits) { + // Preallocate memory since we do not want it to move (the dedup map will point into it). + const size_t byte_entry_size = RoundUp(entry_size_in_bits, kBitsPerByte) / kBitsPerByte; + stack_masks_.resize(byte_entry_size * stack_maps_.size(), 0u); + // For deduplicating we store the stack masks as byte packed for simplicity. We can bit pack later + // when copying out from stack_masks_. + ScopedArenaUnorderedMap, + MemoryRegion::ContentEquals> dedup( + stack_maps_.size(), allocator_->Adapter(kArenaAllocStackMapStream)); + for (StackMapEntry& stack_map : stack_maps_) { + size_t index = dedup.size(); + MemoryRegion stack_mask(stack_masks_.data() + index * byte_entry_size, byte_entry_size); + BitMemoryRegion stack_mask_bits(stack_mask); + for (size_t i = 0; i < entry_size_in_bits; i++) { + stack_mask_bits.StoreBit(i, stack_map.sp_mask != nullptr && stack_map.sp_mask->IsBitSet(i)); + } + stack_map.stack_mask_index = dedup.emplace(stack_mask, index).first->second; + } + return dedup.size(); +} + // Check that all StackMapStream inputs are correctly encoded by trying to read them back. void StackMapStream::CheckCodeInfo(MemoryRegion region) const { CodeInfo code_info(region); DCHECK_EQ(code_info.GetNumberOfStackMaps(), stack_maps_.size()); + DCHECK_EQ(code_info.GetNumberOfStackMaskBits(), static_cast(stack_mask_max_ + 1)); DCHECK_EQ(code_info.GetNumberOfLocationCatalogEntries(), location_catalog_entries_.size()); size_t invoke_info_index = 0; for (size_t s = 0; s < stack_maps_.size(); ++s) { @@ -480,15 +522,18 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { DCHECK_EQ(stack_map.GetDexPc(), entry.dex_pc); DCHECK_EQ(stack_map.GetRegisterMaskIndex(), entry.register_mask_index); DCHECK_EQ(code_info.GetRegisterMaskOf(stack_map), entry.register_mask); + const size_t num_stack_mask_bits = code_info.GetNumberOfStackMaskBits(); DCHECK_EQ(stack_map.GetStackMaskIndex(), entry.stack_mask_index); BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map); if (entry.sp_mask != nullptr) { DCHECK_GE(stack_mask.size_in_bits(), entry.sp_mask->GetNumberOfBits()); - for (size_t b = 0; b < stack_mask.size_in_bits(); b++) { - DCHECK_EQ(stack_mask.LoadBit(b), entry.sp_mask->IsBitSet(b)) << b; + for (size_t b = 0; b < num_stack_mask_bits; b++) { + DCHECK_EQ(stack_mask.LoadBit(b), entry.sp_mask->IsBitSet(b)); } } else { - DCHECK_EQ(stack_mask.size_in_bits(), 0u); + for (size_t b = 0; b < num_stack_mask_bits; b++) { + DCHECK_EQ(stack_mask.LoadBit(b), 0u); + } } if (entry.dex_method_index != dex::kDexNoIndex) { InvokeInfo invoke_info = code_info.GetInvokeInfo(invoke_info_index); diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index 19863d882a..ea97cf6530 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -68,8 +68,11 @@ class StackMapStream : public ValueObject { location_catalog_entries_indices_(allocator->Adapter(kArenaAllocStackMapStream)), dex_register_locations_(allocator->Adapter(kArenaAllocStackMapStream)), inline_infos_(allocator->Adapter(kArenaAllocStackMapStream)), + stack_masks_(allocator->Adapter(kArenaAllocStackMapStream)), + register_masks_(allocator->Adapter(kArenaAllocStackMapStream)), method_indices_(allocator->Adapter(kArenaAllocStackMapStream)), dex_register_entries_(allocator->Adapter(kArenaAllocStackMapStream)), + stack_mask_max_(-1), out_(allocator->Adapter(kArenaAllocStackMapStream)), dex_map_hash_to_stack_map_indices_(std::less(), allocator->Adapter(kArenaAllocStackMapStream)), @@ -168,6 +171,12 @@ class StackMapStream : public ValueObject { private: size_t ComputeDexRegisterLocationCatalogSize() const; + // Returns the number of unique stack masks. + size_t PrepareStackMasks(size_t entry_size_in_bits); + + // Returns the number of unique register masks. + size_t PrepareRegisterMasks(); + // Prepare and deduplicate method indices. void PrepareMethodIndices(); @@ -208,8 +217,11 @@ class StackMapStream : public ValueObject { // A set of concatenated maps of Dex register locations indices to `location_catalog_entries_`. ScopedArenaVector dex_register_locations_; ScopedArenaVector inline_infos_; + ScopedArenaVector stack_masks_; + ScopedArenaVector register_masks_; ScopedArenaVector method_indices_; ScopedArenaVector dex_register_entries_; + int stack_mask_max_; ScopedArenaVector out_; diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index c372bb9b22..9db7588b3a 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -32,10 +32,10 @@ static bool CheckStackMask( const StackMap& stack_map, const BitVector& bit_vector) { BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map); - if (bit_vector.GetNumberOfBits() > stack_mask.size_in_bits()) { + if (bit_vector.GetNumberOfBits() > code_info.GetNumberOfStackMaskBits()) { return false; } - for (size_t i = 0; i < stack_mask.size_in_bits(); ++i) { + for (size_t i = 0; i < code_info.GetNumberOfStackMaskBits(); ++i) { if (stack_mask.LoadBit(i) != bit_vector.IsBitSet(i)) { return false; } diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index b080f92689..fcd6bfd46c 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1731,7 +1731,7 @@ class OatDumper { // Stack masks stats_.AddBits( Stats::kByteKindCodeInfoStackMasks, - code_info.stack_masks_.DataBitSize()); + code_info.stack_masks_.size_in_bits()); // Register masks stats_.AddBits( diff --git a/runtime/oat.h b/runtime/oat.h index 8069a15661..7b8f71a3f3 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: Optimize masks in stack maps. - static constexpr uint8_t kOatVersion[] = { '1', '4', '5', '\0' }; + // Last oat version changed reason: Refactor stackmap encoding. + static constexpr uint8_t kOatVersion[] = { '1', '4', '4', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 26489209b8..de613d3b20 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -439,7 +439,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { const uint8_t* addr = reinterpret_cast(GetCurrentQuickFrame()) + offset; value = *reinterpret_cast(addr); uint32_t bit = (offset >> 2); - if (bit < stack_mask.size_in_bits() && stack_mask.LoadBit(bit)) { + if (bit < code_info.GetNumberOfStackMaskBits() && stack_mask.LoadBit(bit)) { is_reference = true; } break; diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index fd0e28d9ac..2b7e8dd748 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -200,7 +200,7 @@ void StackMap::Dump(VariableIndentationOutputStream* vios, << std::dec << ", stack_mask=0b"; BitMemoryRegion stack_mask = code_info.GetStackMaskOf(*this); - for (size_t i = 0, e = stack_mask.size_in_bits(); i < e; ++i) { + for (size_t i = 0, e = code_info.GetNumberOfStackMaskBits(); i < e; ++i) { vios->Stream() << stack_mask.LoadBit(e - i - 1); } vios->Stream() << ")\n"; diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 02d87130ec..91cecf0690 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -799,24 +799,6 @@ class InvokeInfo : public BitTable<3>::Accessor { } }; -// Register masks tend to have many tailing zero bits, -// therefore it is worth encoding them as value+shift. -class RegisterMask : public BitTable<2>::Accessor { - public: - enum Field { - kValue, - kShift, - kCount, - }; - - RegisterMask(const BitTable* table, uint32_t row) - : BitTable::Accessor(table, row) {} - - ALWAYS_INLINE uint32_t GetMask() const { - return Get() << Get(); - } -}; - /** * Wrapper around all compiler information collected for a method. * The information is of the form: @@ -851,22 +833,24 @@ class CodeInfo { return DexRegisterLocationCatalog(location_catalog_); } + ALWAYS_INLINE size_t GetNumberOfStackMaskBits() const { + return stack_mask_bits_; + } + ALWAYS_INLINE StackMap GetStackMapAt(size_t index) const { return StackMap(&stack_maps_, index); } BitMemoryRegion GetStackMask(size_t index) const { - return stack_masks_.GetBitMemoryRegion(index); + return stack_masks_.Subregion(index * stack_mask_bits_, stack_mask_bits_); } BitMemoryRegion GetStackMaskOf(const StackMap& stack_map) const { - uint32_t index = stack_map.GetStackMaskIndex(); - return (index == StackMap::kNoValue) ? BitMemoryRegion() : GetStackMask(index); + return GetStackMask(stack_map.GetStackMaskIndex()); } uint32_t GetRegisterMaskOf(const StackMap& stack_map) const { - uint32_t index = stack_map.GetRegisterMaskIndex(); - return (index == StackMap::kNoValue) ? 0 : RegisterMask(®ister_masks_, index).GetMask(); + return register_masks_.Get(stack_map.GetRegisterMaskIndex()); } uint32_t GetNumberOfLocationCatalogEntries() const { @@ -1061,8 +1045,8 @@ class CodeInfo { invoke_infos_.Decode(bit_region, &bit_offset); inline_infos_.Decode(bit_region, &bit_offset); register_masks_.Decode(bit_region, &bit_offset); - stack_masks_.Decode(bit_region, &bit_offset); - CHECK_EQ(BitsToBytesRoundUp(bit_offset), non_header_size); + stack_mask_bits_ = DecodeVarintBits(bit_region, &bit_offset); + stack_masks_ = bit_region.Subregion(bit_offset, non_header_size * kBitsPerByte - bit_offset); } size_t size_; @@ -1072,8 +1056,9 @@ class CodeInfo { BitTable stack_maps_; BitTable invoke_infos_; BitTable inline_infos_; - BitTable register_masks_; - BitTable<1> stack_masks_; + BitTable<1> register_masks_; + uint32_t stack_mask_bits_ = 0; + BitMemoryRegion stack_masks_; friend class OatDumper; }; diff --git a/runtime/thread.cc b/runtime/thread.cc index 2e737f5258..129bae6d9a 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -3568,8 +3568,9 @@ class ReferenceMapVisitor : public StackVisitor { T vreg_info(m, code_info, map, visitor_); // Visit stack entries that hold pointers. + const size_t number_of_bits = code_info.GetNumberOfStackMaskBits(); BitMemoryRegion stack_mask = code_info.GetStackMaskOf(map); - for (size_t i = 0; i < stack_mask.size_in_bits(); ++i) { + for (size_t i = 0; i < number_of_bits; ++i) { if (stack_mask.LoadBit(i)) { StackReference* ref_addr = vreg_base + i; mirror::Object* ref = ref_addr->AsMirrorPtr(); -- GitLab From adbceb73b19cfad3ff9011390415a680382a21ec Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 29 May 2018 14:34:14 +0100 Subject: [PATCH 481/749] Remove GcRoot<> static from Throwable and related classes. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Bug: 31113334 Change-Id: I8115e6413a07419ec261af3ec5068833b72b5218 --- runtime/class_linker.cc | 10 --------- runtime/interpreter/unstarted_runtime_test.cc | 6 +++--- runtime/mirror/emulated_stack_frame.cc | 19 +---------------- runtime/mirror/emulated_stack_frame.h | 10 --------- runtime/mirror/stack_trace_element.cc | 21 ++----------------- runtime/mirror/stack_trace_element.h | 11 ---------- runtime/mirror/throwable.cc | 21 ++----------------- runtime/mirror/throwable.h | 12 ----------- runtime/runtime.cc | 3 --- runtime/thread.cc | 2 +- runtime/verifier/method_verifier.cc | 2 +- runtime/verifier/reg_type_cache-inl.h | 21 ++++++++++++------- 12 files changed, 24 insertions(+), 114 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index a2e26866d1..b882f65a9e 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -759,7 +759,6 @@ bool ClassLinker::InitWithoutImage(std::vector> b class_root = FindSystemClass(self, "Ldalvik/system/EmulatedStackFrame;"); CHECK(class_root != nullptr); SetClassRoot(ClassRoot::kDalvikSystemEmulatedStackFrame, class_root); - mirror::EmulatedStackFrame::SetClass(class_root); // java.lang.ref classes need to be specially flagged, but otherwise are normal classes // finish initializing Reference class @@ -790,14 +789,12 @@ bool ClassLinker::InitWithoutImage(std::vector> b // Set up java.lang.Throwable, java.lang.ClassNotFoundException, and // java.lang.StackTraceElement as a convenience. SetClassRoot(ClassRoot::kJavaLangThrowable, FindSystemClass(self, "Ljava/lang/Throwable;")); - mirror::Throwable::SetClass(GetClassRoot(ClassRoot::kJavaLangThrowable, this)); SetClassRoot(ClassRoot::kJavaLangClassNotFoundException, FindSystemClass(self, "Ljava/lang/ClassNotFoundException;")); SetClassRoot(ClassRoot::kJavaLangStackTraceElement, FindSystemClass(self, "Ljava/lang/StackTraceElement;")); SetClassRoot(ClassRoot::kJavaLangStackTraceElementArrayClass, FindSystemClass(self, "[Ljava/lang/StackTraceElement;")); - mirror::StackTraceElement::SetClass(GetClassRoot(ClassRoot::kJavaLangStackTraceElement, this)); // Create conflict tables that depend on the class linker. runtime->FixupConflictTables(); @@ -1033,10 +1030,6 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { GcRoot(GetClassRoot(ClassRoot::kObjectArrayClass, this)->GetIfTable()); DCHECK_EQ(array_iftable_.Read(), GetClassRoot(ClassRoot::kBooleanArrayClass, this)->GetIfTable()); // String class root was set above - mirror::Throwable::SetClass(GetClassRoot(ClassRoot::kJavaLangThrowable, this)); - mirror::StackTraceElement::SetClass(GetClassRoot(ClassRoot::kJavaLangStackTraceElement, this)); - mirror::EmulatedStackFrame::SetClass( - GetClassRoot(ClassRoot::kDalvikSystemEmulatedStackFrame, this).Ptr()); mirror::ClassExt::SetClass(GetClassRoot(ClassRoot::kDalvikSystemClassExt, this)); for (gc::space::ImageSpace* image_space : spaces) { @@ -2116,10 +2109,7 @@ void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor) { ClassLinker::~ClassLinker() { mirror::Class::ResetClass(); - mirror::StackTraceElement::ResetClass(); mirror::String::ResetClass(); - mirror::Throwable::ResetClass(); - mirror::EmulatedStackFrame::ResetClass(); Thread* const self = Thread::Current(); for (const ClassLoaderData& data : class_loaders_) { // CHA unloading analysis is not needed. No negative consequences are expected because diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc index 98fe8b271b..9bb760c6b7 100644 --- a/runtime/interpreter/unstarted_runtime_test.cc +++ b/runtime/interpreter/unstarted_runtime_test.cc @@ -1348,7 +1348,7 @@ TEST_F(UnstartedRuntimeTest, ConstructorNewInstance0) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); // Get Throwable. - Handle throw_class = hs.NewHandle(mirror::Throwable::GetJavaLangThrowable()); + Handle throw_class = hs.NewHandle(GetClassRoot()); ASSERT_TRUE(class_linker->EnsureInitialized(self, throw_class, true, true)); // Get an input object. @@ -1387,8 +1387,8 @@ TEST_F(UnstartedRuntimeTest, ConstructorNewInstance0) { // Should be a new object. ASSERT_NE(result.GetL(), input.Get()); - // Should be a String. - ASSERT_EQ(mirror::Throwable::GetJavaLangThrowable(), result.GetL()->GetClass()); + // Should be of type Throwable. + ASSERT_OBJ_PTR_EQ(GetClassRoot(), result.GetL()->GetClass()); // Should have the right string. ObjPtr result_msg = reinterpret_cast(result.GetL())->GetDetailMessage(); diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc index 527408b3e5..5595102866 100644 --- a/runtime/mirror/emulated_stack_frame.cc +++ b/runtime/mirror/emulated_stack_frame.cc @@ -27,8 +27,6 @@ namespace art { namespace mirror { -GcRoot EmulatedStackFrame::static_class_; - // Calculates the size of a stack frame based on the size of its argument // types and return types. static void CalculateFrameAndReferencesSize(ObjPtr> p_types, @@ -192,7 +190,7 @@ mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs( // Step 5: Construct the EmulatedStackFrame object. Handle sf(hs.NewHandle( - ObjPtr::DownCast(StaticClass()->AllocObject(self)))); + ObjPtr::DownCast(GetClassRoot()->AllocObject(self)))); sf->SetFieldObject(CallsiteTypeOffset(), caller_type.Get()); sf->SetFieldObject(TypeOffset(), callee_type.Get()); sf->SetFieldObject(ReferencesOffset(), references.Get()); @@ -272,20 +270,5 @@ void EmulatedStackFrame::SetReturnValue(Thread* self, const JValue& value) { } } -void EmulatedStackFrame::SetClass(Class* klass) { - CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; - CHECK(klass != nullptr); - static_class_ = GcRoot(klass); -} - -void EmulatedStackFrame::ResetClass() { - CHECK(!static_class_.IsNull()); - static_class_ = GcRoot(nullptr); -} - -void EmulatedStackFrame::VisitRoots(RootVisitor* visitor) { - static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - } // namespace mirror } // namespace art diff --git a/runtime/mirror/emulated_stack_frame.h b/runtime/mirror/emulated_stack_frame.h index 23626f46e0..ec45f57a4d 100644 --- a/runtime/mirror/emulated_stack_frame.h +++ b/runtime/mirror/emulated_stack_frame.h @@ -64,15 +64,7 @@ class MANAGED EmulatedStackFrame : public Object { return GetReferences()->Get(0); } - static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - private: - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return static_class_.Read(); - } - mirror::ObjectArray* GetReferences() REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldObject>( OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, references_)); @@ -104,8 +96,6 @@ class MANAGED EmulatedStackFrame : public Object { HeapReference stack_frame_; HeapReference type_; - static GcRoot static_class_; // dalvik.system.EmulatedStackFrame.class - friend struct art::EmulatedStackFrameOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(EmulatedStackFrame); }; diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc index bb3242e035..ff353d8939 100644 --- a/runtime/mirror/stack_trace_element.cc +++ b/runtime/mirror/stack_trace_element.cc @@ -18,6 +18,7 @@ #include "class-inl.h" #include "class.h" +#include "class_root.h" #include "gc/accounting/card_table-inl.h" #include "gc_root-inl.h" #include "handle_scope-inl.h" @@ -27,26 +28,13 @@ namespace art { namespace mirror { -GcRoot StackTraceElement::java_lang_StackTraceElement_; - -void StackTraceElement::SetClass(ObjPtr java_lang_StackTraceElement) { - CHECK(java_lang_StackTraceElement_.IsNull()); - CHECK(java_lang_StackTraceElement != nullptr); - java_lang_StackTraceElement_ = GcRoot(java_lang_StackTraceElement); -} - -void StackTraceElement::ResetClass() { - CHECK(!java_lang_StackTraceElement_.IsNull()); - java_lang_StackTraceElement_ = GcRoot(nullptr); -} - StackTraceElement* StackTraceElement::Alloc(Thread* self, Handle declaring_class, Handle method_name, Handle file_name, int32_t line_number) { ObjPtr trace = - ObjPtr::DownCast(GetStackTraceElement()->AllocObject(self)); + ObjPtr::DownCast(GetClassRoot()->AllocObject(self)); if (LIKELY(trace != nullptr)) { if (Runtime::Current()->IsActiveTransaction()) { trace->Init(declaring_class.Get(), method_name.Get(), file_name.Get(), line_number); @@ -72,10 +60,5 @@ void StackTraceElement::Init(ObjPtr declaring_class, line_number); } -void StackTraceElement::VisitRoots(RootVisitor* visitor) { - java_lang_StackTraceElement_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - - } // namespace mirror } // namespace art diff --git a/runtime/mirror/stack_trace_element.h b/runtime/mirror/stack_trace_element.h index 87e8a1f659..f25211c397 100644 --- a/runtime/mirror/stack_trace_element.h +++ b/runtime/mirror/stack_trace_element.h @@ -53,15 +53,6 @@ class MANAGED StackTraceElement FINAL : public Object { int32_t line_number) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - static void SetClass(ObjPtr java_lang_StackTraceElement); - static void ResetClass(); - static void VisitRoots(RootVisitor* visitor) - REQUIRES_SHARED(Locks::mutator_lock_); - static Class* GetStackTraceElement() REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(!java_lang_StackTraceElement_.IsNull()); - return java_lang_StackTraceElement_.Read(); - } - private: // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". HeapReference declaring_class_; @@ -76,8 +67,6 @@ class MANAGED StackTraceElement FINAL : public Object { int32_t line_number) REQUIRES_SHARED(Locks::mutator_lock_); - static GcRoot java_lang_StackTraceElement_; - friend struct art::StackTraceElementOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(StackTraceElement); }; diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc index 70069733d1..82e295a616 100644 --- a/runtime/mirror/throwable.cc +++ b/runtime/mirror/throwable.cc @@ -22,6 +22,7 @@ #include "base/enums.h" #include "base/utils.h" #include "class-inl.h" +#include "class_root.h" #include "dex/dex_file-inl.h" #include "gc/accounting/card_table-inl.h" #include "object-inl.h" @@ -36,8 +37,6 @@ namespace mirror { using android::base::StringPrintf; -GcRoot Throwable::java_lang_Throwable_; - void Throwable::SetDetailMessage(ObjPtr new_detail_message) { if (Runtime::Current()->IsActiveTransaction()) { SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_), new_detail_message); @@ -128,8 +127,7 @@ std::string Throwable::Dump() { } else { ObjPtr stack_trace = GetStackTrace(); if (stack_trace != nullptr && stack_trace->IsObjectArray()) { - CHECK_EQ(stack_trace->GetClass()->GetComponentType(), - StackTraceElement::GetStackTraceElement()); + CHECK_EQ(stack_trace->GetClass()->GetComponentType(), GetClassRoot()); ObjPtr> ste_array = ObjPtr>::DownCast(stack_trace); if (ste_array->GetLength() == 0) { @@ -159,21 +157,6 @@ std::string Throwable::Dump() { return result; } -void Throwable::SetClass(ObjPtr java_lang_Throwable) { - CHECK(java_lang_Throwable_.IsNull()); - CHECK(java_lang_Throwable != nullptr); - java_lang_Throwable_ = GcRoot(java_lang_Throwable); -} - -void Throwable::ResetClass() { - CHECK(!java_lang_Throwable_.IsNull()); - java_lang_Throwable_ = GcRoot(nullptr); -} - -void Throwable::VisitRoots(RootVisitor* visitor) { - java_lang_Throwable_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - Object* Throwable::GetStackState() { return GetFieldObjectVolatile(OFFSET_OF_OBJECT_MEMBER(Throwable, backtrace_)); } diff --git a/runtime/mirror/throwable.h b/runtime/mirror/throwable.h index b901ca2d00..42c612f8b6 100644 --- a/runtime/mirror/throwable.h +++ b/runtime/mirror/throwable.h @@ -46,18 +46,8 @@ class MANAGED Throwable : public Object { bool IsCheckedException() REQUIRES_SHARED(Locks::mutator_lock_); bool IsError() REQUIRES_SHARED(Locks::mutator_lock_); - static Class* GetJavaLangThrowable() REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(!java_lang_Throwable_.IsNull()); - return java_lang_Throwable_.Read(); - } - int32_t GetStackDepth() REQUIRES_SHARED(Locks::mutator_lock_); - static void SetClass(ObjPtr java_lang_Throwable); - static void ResetClass(); - static void VisitRoots(RootVisitor* visitor) - REQUIRES_SHARED(Locks::mutator_lock_); - private: Object* GetStackState() REQUIRES_SHARED(Locks::mutator_lock_); Object* GetStackTrace() REQUIRES_SHARED(Locks::mutator_lock_); @@ -69,8 +59,6 @@ class MANAGED Throwable : public Object { HeapReference stack_trace_; HeapReference suppressed_exceptions_; - static GcRoot java_lang_Throwable_; - friend struct art::ThrowableOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(Throwable); }; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 630053ac71..32d9d68d0d 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1979,10 +1979,7 @@ void Runtime::VisitConstantRoots(RootVisitor* visitor) { // Visit the classes held as static in mirror classes, these can be visited concurrently and only // need to be visited once per GC since they never change. mirror::Class::VisitRoots(visitor); - mirror::StackTraceElement::VisitRoots(visitor); mirror::String::VisitRoots(visitor); - mirror::Throwable::VisitRoots(visitor); - mirror::EmulatedStackFrame::VisitRoots(visitor); mirror::ClassExt::VisitRoots(visitor); // Visiting the roots of these ArtMethods is not currently required since all the GcRoots are // null. diff --git a/runtime/thread.cc b/runtime/thread.cc index 129bae6d9a..f5d2ffbb17 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -2461,7 +2461,7 @@ class FetchStackTraceVisitor : public StackVisitor { // save frame) ArtMethod* m = GetMethod(); if (skipping_ && !m->IsRuntimeMethod() && - !mirror::Throwable::GetJavaLangThrowable()->IsAssignableFrom(m->GetDeclaringClass())) { + !GetClassRoot()->IsAssignableFrom(m->GetDeclaringClass())) { skipping_ = false; } if (!skipping_) { diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 3a49e4d21f..287e3d619a 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3529,7 +3529,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { ObjPtr klass = linker->ResolveType(handler_type_idx, dex_cache_, class_loader_); if (klass != nullptr) { - if (klass == mirror::Throwable::GetJavaLangThrowable()) { + if (klass == GetClassRoot()) { has_catch_all_handler = true; } } else { diff --git a/runtime/verifier/reg_type_cache-inl.h b/runtime/verifier/reg_type_cache-inl.h index 43c0ab9598..0469a3b394 100644 --- a/runtime/verifier/reg_type_cache-inl.h +++ b/runtime/verifier/reg_type_cache-inl.h @@ -124,36 +124,42 @@ inline const ImpreciseConstType& RegTypeCache::PosShortConstant() { } inline const PreciseReferenceType& RegTypeCache::JavaLangClass() { - const RegType* result = &FromClass("Ljava/lang/Class;", mirror::Class::GetJavaLangClass(), true); + const RegType* result = &FromClass("Ljava/lang/Class;", + GetClassRoot().Ptr(), + /* precise */ true); DCHECK(result->IsPreciseReference()); return *down_cast(result); } inline const PreciseReferenceType& RegTypeCache::JavaLangString() { // String is final and therefore always precise. - const RegType* result = &FromClass("Ljava/lang/String;", mirror::String::GetJavaLangString(), - true); + const RegType* result = &FromClass("Ljava/lang/String;", + GetClassRoot().Ptr(), + /* precise */ true); DCHECK(result->IsPreciseReference()); return *down_cast(result); } inline const PreciseReferenceType& RegTypeCache::JavaLangInvokeMethodHandle() { const RegType* result = &FromClass("Ljava/lang/invoke/MethodHandle;", - GetClassRoot().Ptr(), true); + GetClassRoot().Ptr(), + /* precise */ true); DCHECK(result->IsPreciseReference()); return *down_cast(result); } inline const PreciseReferenceType& RegTypeCache::JavaLangInvokeMethodType() { const RegType* result = &FromClass("Ljava/lang/invoke/MethodType;", - GetClassRoot().Ptr(), true); + GetClassRoot().Ptr(), + /* precise */ true); DCHECK(result->IsPreciseReference()); return *down_cast(result); } inline const RegType& RegTypeCache::JavaLangThrowable(bool precise) { const RegType* result = &FromClass("Ljava/lang/Throwable;", - mirror::Throwable::GetJavaLangThrowable(), precise); + GetClassRoot().Ptr(), + precise); if (precise) { DCHECK(result->IsPreciseReference()); return *down_cast(result); @@ -165,7 +171,8 @@ inline const RegType& RegTypeCache::JavaLangThrowable(bool precise) { inline const RegType& RegTypeCache::JavaLangObject(bool precise) { const RegType* result = &FromClass("Ljava/lang/Object;", - mirror::Class::GetJavaLangClass()->GetSuperClass(), precise); + GetClassRoot().Ptr(), + precise); if (precise) { DCHECK(result->IsPreciseReference()); return *down_cast(result); -- GitLab From 0d896bd7da4f7b11559aa88aad82e9a9e170a4e4 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 25 May 2018 00:20:27 -0700 Subject: [PATCH 482/749] Add Method/Field iterator to ClassAccessor Enables ranged based for loops on fields and methods. For visiting both fields and methods, VisitFieldsAndMethods will be faster because of not needing to decode the fields twice for seeking purposes. Added test. Bug: 79758018 Bug: 77709234 Test: test-art-host-gtest Change-Id: I593e23ccd138b87a27d8bab6927ff2b685c057f3 --- compiler/dex/dex_to_dex_compiler.cc | 4 +- compiler/dex/dex_to_dex_decompiler_test.cc | 7 +- compiler/driver/compiler_driver.cc | 20 ++- compiler/verifier_deps_test.cc | 5 +- libdexfile/Android.bp | 1 + libdexfile/dex/class_accessor-inl.h | 76 ++++++------ libdexfile/dex/class_accessor.h | 138 +++++++++++++++++++-- libdexfile/dex/class_accessor_test.cc | 79 ++++++++++++ profman/profman.cc | 4 +- tools/dexanalyze/dexanalyze_experiments.cc | 4 +- 10 files changed, 270 insertions(+), 68 deletions(-) create mode 100644 libdexfile/dex/class_accessor_test.cc diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index 68155d844a..fb6a72b1c5 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -635,13 +635,13 @@ void DexToDexCompiler::SetDexFiles(const std::vector& dex_files) std::unordered_set seen_code_items; for (const DexFile* dex_file : dex_files) { for (ClassAccessor accessor : dex_file->GetClasses()) { - accessor.VisitMethods([&](const ClassAccessor::Method& method) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { const DexFile::CodeItem* code_item = method.GetCodeItem(); // Detect the shared code items. if (!seen_code_items.insert(code_item).second) { shared_code_items_.insert(code_item); } - }); + } } } VLOG(compiler) << "Shared code items " << shared_code_items_.size(); diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc index 082e6091d2..75de238211 100644 --- a/compiler/dex/dex_to_dex_decompiler_test.cc +++ b/compiler/dex/dex_to_dex_decompiler_test.cc @@ -85,10 +85,9 @@ class DexToDexDecompilerTest : public CommonCompilerTest { for (uint32_t i = 0; i < updated_dex_file->NumClassDefs(); ++i) { // Unquicken each method. ClassAccessor accessor(*updated_dex_file, updated_dex_file->GetClassDef(i)); - accessor.VisitMethods([&](const ClassAccessor::Method& method) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { CompiledMethod* compiled_method = compiler_driver_->GetCompiledMethod( - MethodReference(updated_dex_file, - method.GetIndex())); + method.GetReference()); ArrayRef table; if (compiled_method != nullptr) { table = compiled_method->GetVmapTable(); @@ -97,7 +96,7 @@ class DexToDexDecompilerTest : public CommonCompilerTest { *accessor.GetCodeItem(method), table, /* decompile_return_instruction */ true); - }); + } } // Make sure after unquickening we go back to the same contents as the original dex file. diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index decb330f3b..16f2d0f2cc 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -790,8 +790,7 @@ static void ResolveConstStrings(CompilerDriver* driver, // FIXME: Make sure that inlining honors this. b/26687569 continue; } - accessor.VisitMethods([&](const ClassAccessor::Method& method) - REQUIRES_SHARED(Locks::mutator_lock_) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { // Resolve const-strings in the code. Done to have deterministic allocation behavior. Right // now this is single-threaded for simplicity. // TODO: Collect the relevant string indices in parallel, then allocate them sequentially @@ -812,7 +811,7 @@ static void ResolveConstStrings(CompilerDriver* driver, break; } } - }); + } } } } @@ -880,10 +879,9 @@ static void InitializeTypeCheckBitstrings(CompilerDriver* driver, } // Direct and virtual methods. - accessor.VisitMethods([&](const ClassAccessor::Method& method) - REQUIRES_SHARED(Locks::mutator_lock_) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { InitializeTypeCheckBitstrings(driver, class_linker, dex_cache, *dex_file, method); - }); + } } } } @@ -1949,9 +1947,9 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, // - We're only going to compile methods that did verify. // - Quickening will not do checkcast ellision. // TODO(ngeoffray): Reconsider this once we refactor compiler filters. - accessor.VisitMethods([&](const ClassAccessor::Method& method) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { verification_results_->CreateVerifiedMethodFor(method.GetReference()); - }); + } } } else if (!compiler_only_verifies) { // Make sure later compilation stages know they should not try to verify @@ -2747,12 +2745,12 @@ static void CompileDexFile(CompilerDriver* driver, // Compile direct and virtual methods. int64_t previous_method_idx = -1; - accessor.VisitMethods([&](const ClassAccessor::Method& method) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { const uint32_t method_idx = method.GetIndex(); if (method_idx == previous_method_idx) { // smali can create dex files with two encoded_methods sharing the same method_idx // http://code.google.com/p/smali/issues/detail?id=119 - return; + continue; } previous_method_idx = method_idx; compile_fn(soa.Self(), @@ -2767,7 +2765,7 @@ static void CompileDexFile(CompilerDriver* driver, dex_to_dex_compilation_level, compilation_enabled, dex_cache); - }); + } }; context.ForAllLambda(0, dex_file.NumClassDefs(), compile, thread_count); } diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 103862beff..c0892ff466 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -155,8 +155,7 @@ class VerifierDepsTest : public CommonCompilerTest { bool has_failures = true; bool found_method = false; - accessor.VisitMethods([&](const ClassAccessor::Method& method) - REQUIRES_SHARED(Locks::mutator_lock_) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { ArtMethod* resolved_method = class_linker_->ResolveMethod( method.GetIndex(), @@ -186,7 +185,7 @@ class VerifierDepsTest : public CommonCompilerTest { has_failures = verifier.HasFailures(); found_method = true; } - }); + } CHECK(found_method) << "Expected to find method " << method_name; return !has_failures; } diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp index 3818624d7a..06fd19e2fe 100644 --- a/libdexfile/Android.bp +++ b/libdexfile/Android.bp @@ -112,6 +112,7 @@ art_cc_test { ], srcs: [ "dex/art_dex_file_loader_test.cc", + "dex/class_accessor_test.cc", "dex/code_item_accessors_test.cc", "dex/compact_dex_file_test.cc", "dex/compact_offset_table_test.cc", diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h index a082142366..49ca98d47f 100644 --- a/libdexfile/dex/class_accessor-inl.h +++ b/libdexfile/dex/class_accessor-inl.h @@ -50,6 +50,19 @@ inline const uint8_t* ClassAccessor::Field::Read(const uint8_t* ptr) { return ptr; } +template +inline const uint8_t* ClassAccessor::VisitMembers(size_t count, + const Visitor& visitor, + const uint8_t* ptr, + DataType* data) const { + DCHECK(data != nullptr); + for ( ; count != 0; --count) { + ptr = data->Read(ptr); + visitor(*data); + } + return ptr; +} + template -inline void ClassAccessor::VisitMethods(const MethodVisitor& method_visitor) const { - VisitMethods(method_visitor, method_visitor); -} - inline const DexFile::CodeItem* ClassAccessor::GetCodeItem(const Method& method) const { return dex_file_.GetCodeItem(method.GetCodeItemOffset()); } @@ -132,6 +119,25 @@ inline const DexFile::CodeItem* ClassAccessor::Method::GetCodeItem() const { return dex_file_.GetCodeItem(code_off_); } +inline IterationRange> ClassAccessor::GetFields() + const { + const uint32_t limit = num_static_fields_ + num_instance_fields_; + return { DataIterator(dex_file_, 0u, num_static_fields_, limit, ptr_pos_), + DataIterator(dex_file_, limit, num_static_fields_, limit, ptr_pos_) }; +} + +inline IterationRange> + ClassAccessor::GetMethods() const { + // Skip over the fields. + Field field(dex_file_); + const size_t skip_count = num_static_fields_ + num_instance_fields_; + const uint8_t* ptr_pos = VisitMembers(skip_count, VoidFunctor(), ptr_pos_, &field); + // Return the iterator pair for all the methods. + const uint32_t limit = num_direct_methods_ + num_virtual_methods_; + return { DataIterator(dex_file_, 0u, num_direct_methods_, limit, ptr_pos), + DataIterator(dex_file_, limit, num_direct_methods_, limit, ptr_pos) }; +} + } // namespace art #endif // ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_ diff --git a/libdexfile/dex/class_accessor.h b/libdexfile/dex/class_accessor.h index 72bc50b98c..7455704a7f 100644 --- a/libdexfile/dex/class_accessor.h +++ b/libdexfile/dex/class_accessor.h @@ -45,7 +45,7 @@ class ClassAccessor { return (GetAccessFlags() & kAccFinal) != 0; } - public: + protected: uint32_t index_ = 0u; uint32_t access_flags_ = 0u; }; @@ -72,8 +72,13 @@ class ClassAccessor { const DexFile::CodeItem* GetCodeItem() const; + bool IsStaticOrDirect() const { + return is_static_or_direct_; + } + private: - explicit Method(const DexFile& dex_file, bool is_static_or_direct) + explicit Method(const DexFile& dex_file, + bool is_static_or_direct = true) : dex_file_(dex_file), is_static_or_direct_(is_static_or_direct) {} @@ -94,8 +99,14 @@ class ClassAccessor { } } + void NextSection() { + DCHECK(is_static_or_direct_) << "Already in the virtual methods section"; + is_static_or_direct_ = false; + index_ = 0u; + } + const DexFile& dex_file_; - const bool is_static_or_direct_; + bool is_static_or_direct_ = true; uint32_t code_off_ = 0u; friend class ClassAccessor; @@ -103,12 +114,113 @@ class ClassAccessor { // A decoded version of the field of a class_data_item. class Field : public BaseItem { + public: + explicit Field(const DexFile& dex_file) : dex_file_(dex_file) {} + + const DexFile& GetDexFile() const { + return dex_file_; + } + private: const uint8_t* Read(const uint8_t* ptr); + void NextSection() { + index_ = 0u; + } + + const DexFile& dex_file_; friend class ClassAccessor; }; + template + class DataIterator : public std::iterator { + public: + using value_type = typename std::iterator::value_type; + using difference_type = + typename std::iterator::difference_type; + + DataIterator(const DexFile& dex_file, + uint32_t position, + uint32_t partition_pos, + uint32_t iterator_end, + const uint8_t* ptr_pos) + : data_(dex_file), + position_(position), + partition_pos_(partition_pos), + iterator_end_(iterator_end), + ptr_pos_(ptr_pos) { + ReadData(); + } + + bool IsValid() const { + return position_ < iterator_end_; + } + + // Value after modification. + DataIterator& operator++() { + ++position_; + ReadData(); + return *this; + } + + const value_type& operator*() const { + return data_; + } + + const value_type* operator->() const { + return &data_; + } + + bool operator==(const DataIterator& rhs) const { + DCHECK_EQ(&data_.dex_file_, &rhs.data_.dex_file_) << "Comparing different dex files."; + return position_ == rhs.position_; + } + + bool operator!=(const DataIterator& rhs) const { + return !(*this == rhs); + } + + bool operator<(const DataIterator& rhs) const { + DCHECK_EQ(&data_.dex_file_, &rhs.data_.dex_file_) << "Comparing different dex files."; + return position_ < rhs.position_; + } + + bool operator>(const DataIterator& rhs) const { + return rhs < *this; + } + + bool operator<=(const DataIterator& rhs) const { + return !(rhs < *this); + } + + bool operator>=(const DataIterator& rhs) const { + return !(*this < rhs); + } + + private: + // Read data at current position. + void ReadData() { + if (IsValid()) { + // At the end of the first section, go to the next section. + if (position_ == partition_pos_) { + data_.NextSection(); + } + DCHECK(ptr_pos_ != nullptr); + ptr_pos_ = data_.Read(ptr_pos_); + } + } + + DataType data_; + // Iterator position. + uint32_t position_; + // At partition_pos_, we go to the next section. + const uint32_t partition_pos_; + // At iterator_end_, the iterator is no longer valid. + const uint32_t iterator_end_; + // Internal data pointer. + const uint8_t* ptr_pos_; + }; + // Not explicit specifically for range-based loops. ALWAYS_INLINE ClassAccessor(const ClassIteratorData& data); @@ -118,7 +230,6 @@ class ClassAccessor { const DexFile::CodeItem* GetCodeItem(const Method& method) const; // Iterator data is not very iterator friendly, use visitors to get around this. - // No thread safety analysis since the visitor may require capabilities. template @@ -139,9 +249,11 @@ class ClassAccessor { void VisitFields(const StaticFieldVisitor& static_field_visitor, const InstanceFieldVisitor& instance_field_visitor) const; - // Visit direct and virtual methods. - template - void VisitMethods(const MethodVisitor& method_visitor) const; + // Return the iteration range for all the fields. + IterationRange> GetFields() const; + + // Return the iteration range for all the methods. + IterationRange> GetMethods() const; uint32_t NumStaticFields() const { return num_static_fields_; @@ -170,6 +282,14 @@ class ClassAccessor { } protected: + // Template visitor to reduce copy paste for visiting elements. + // No thread safety analysis since the visitor may require capabilities. + template + const uint8_t* VisitMembers(size_t count, + const Visitor& visitor, + const uint8_t* ptr, + DataType* data) const NO_THREAD_SAFETY_ANALYSIS; + const DexFile& dex_file_; const dex::TypeIndex descriptor_index_ = {}; const uint8_t* ptr_pos_ = nullptr; // Pointer into stream of class_data_item. diff --git a/libdexfile/dex/class_accessor_test.cc b/libdexfile/dex/class_accessor_test.cc new file mode 100644 index 0000000000..95380d8140 --- /dev/null +++ b/libdexfile/dex/class_accessor_test.cc @@ -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. + */ + +#include "dex/class_accessor-inl.h" + +#include "base/common_art_test.h" + +namespace art { + +class ClassAccessorTest : public CommonArtTest {}; + +TEST_F(ClassAccessorTest, TestVisiting) { + std::vector> dex_files( + OpenDexFiles(GetLibCoreDexFileNames()[0].c_str())); + ASSERT_GT(dex_files.size(), 0u); + for (const std::unique_ptr& dex_file : dex_files) { + uint32_t class_def_idx = 0u; + ASSERT_GT(dex_file->NumClassDefs(), 0u); + for (ClassAccessor accessor : dex_file->GetClasses()) { + const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx); + EXPECT_EQ(accessor.GetDescriptor(), dex_file->StringByTypeIdx(class_def.class_idx_)); + ++class_def_idx; + // Check iterators against visitors. + auto methods = accessor.GetMethods(); + auto fields = accessor.GetFields(); + auto method_it = methods.begin(); + auto field_it = fields.begin(); + accessor.VisitFieldsAndMethods( + // Static fields. + [&](const ClassAccessor::Field& field) { + EXPECT_EQ(field.GetIndex(), field_it->GetIndex()); + EXPECT_EQ(field.GetAccessFlags(), field_it->GetAccessFlags()); + ++field_it; + }, + // Instance fields. + [&](const ClassAccessor::Field& field) { + EXPECT_EQ(field.GetIndex(), field_it->GetIndex()); + EXPECT_EQ(field.GetAccessFlags(), field_it->GetAccessFlags()); + ++field_it; + }, + // Direct methods. + [&](const ClassAccessor::Method& method) { + EXPECT_TRUE(method.IsStaticOrDirect()); + EXPECT_EQ(method.IsStaticOrDirect(), method_it->IsStaticOrDirect()); + EXPECT_EQ(method.GetIndex(), method_it->GetIndex()); + EXPECT_EQ(method.GetAccessFlags(), method_it->GetAccessFlags()); + EXPECT_EQ(method.GetCodeItem(), method_it->GetCodeItem()); + ++method_it; + }, + // Virtual methods. + [&](const ClassAccessor::Method& method) { + EXPECT_FALSE(method.IsStaticOrDirect()); + EXPECT_EQ(method.IsStaticOrDirect(), method_it->IsStaticOrDirect()); + EXPECT_EQ(method.GetIndex(), method_it->GetIndex()); + EXPECT_EQ(method.GetAccessFlags(), method_it->GetAccessFlags()); + EXPECT_EQ(method.GetCodeItem(), method_it->GetCodeItem()); + ++method_it; + }); + ASSERT_TRUE(field_it == fields.end()); + ASSERT_TRUE(method_it == methods.end()); + } + EXPECT_EQ(class_def_idx, dex_file->NumClassDefs()); + } +} + +} // namespace art diff --git a/profman/profman.cc b/profman/profman.cc index 661132d94f..096e5dc3bd 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -931,12 +931,12 @@ class ProfMan FINAL { std::vector methods; if (method_str == kClassAllMethods) { ClassAccessor accessor(*dex_file, *dex_file->FindClassDef(class_ref.TypeIndex())); - accessor.VisitMethods([&](const ClassAccessor::Method& method) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { if (method.GetCodeItemOffset() != 0) { // Add all of the methods that have code to the profile. methods.push_back(ProfileMethodInfo(method.GetReference())); } - }); + } } // TODO: Check return values? profile->AddMethods(methods, static_cast(flags)); diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc index 82cb0d28ab..0f20a99f05 100644 --- a/tools/dexanalyze/dexanalyze_experiments.cc +++ b/tools/dexanalyze/dexanalyze_experiments.cc @@ -130,7 +130,7 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { for (ClassAccessor accessor : dex_file.GetClasses()) { std::set unique_method_ids; std::set unique_string_ids; - accessor.VisitMethods([&](const ClassAccessor::Method& method) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { dex_code_bytes_ += method.GetInstructions().InsnsSizeInBytes(); unique_code_items.insert(method.GetCodeItemOffset()); for (const DexInstructionPcPair& inst : method.GetInstructions()) { @@ -188,7 +188,7 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { break; } } - }); + } total_unique_method_idx_ += unique_method_ids.size(); total_unique_string_ids_ += unique_string_ids.size(); } -- GitLab From 1fed343e5145846e316d49616f3b94e225f43bf1 Mon Sep 17 00:00:00 2001 From: David Sehr Date: Tue, 29 May 2018 13:19:47 -0700 Subject: [PATCH 483/749] Move profile testing to CommonArtTest Finish the migration of libprofile content by moving test to use CommonArtTest. This required splitting the libart-related portions into a separate test that was moved closer to the code it tests. Bug: 78459333 Test: make -j 40 test-art-host Change-Id: I134c983b1a5ac81a71ac2b72d8c653eff5df4164 --- .../profile/profile_compilation_info_test.cc | 247 +------------ runtime/Android.bp | 1 + runtime/jit/profiling_info_test.cc | 329 ++++++++++++++++++ 3 files changed, 338 insertions(+), 239 deletions(-) create mode 100644 runtime/jit/profiling_info_test.cc diff --git a/libprofile/profile/profile_compilation_info_test.cc b/libprofile/profile/profile_compilation_info_test.cc index ead7cba60b..42c3320ea5 100644 --- a/libprofile/profile/profile_compilation_info_test.cc +++ b/libprofile/profile/profile_compilation_info_test.cc @@ -17,20 +17,14 @@ #include #include -#include "art_method-inl.h" +#include "base/arena_allocator.h" +#include "base/common_art_test.h" #include "base/unix_file/fd_file.h" -#include "class_linker-inl.h" -#include "common_runtime_test.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "dex/method_reference.h" #include "dex/type_reference.h" -#include "handle_scope-inl.h" -#include "linear_alloc.h" -#include "mirror/class-inl.h" -#include "mirror/class_loader.h" #include "profile/profile_compilation_info.h" -#include "scoped_thread_state_change-inl.h" #include "ziparchive/zip_writer.h" namespace art { @@ -39,31 +33,14 @@ using Hotness = ProfileCompilationInfo::MethodHotness; static constexpr size_t kMaxMethodIds = 65535; -class ProfileCompilationInfoTest : public CommonRuntimeTest { +class ProfileCompilationInfoTest : public CommonArtTest { public: - void PostRuntimeCreate() OVERRIDE { - allocator_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool())); + void SetUp() OVERRIDE { + CommonArtTest::SetUp(); + allocator_.reset(new ArenaAllocator(&pool_)); } protected: - std::vector GetVirtualMethods(jobject class_loader, - const std::string& clazz) { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); - StackHandleScope<1> hs(self); - Handle h_loader( - hs.NewHandle(self->DecodeJObject(class_loader)->AsClassLoader())); - ObjPtr klass = class_linker->FindClass(self, clazz.c_str(), h_loader); - - const auto pointer_size = class_linker->GetImagePointerSize(); - std::vector methods; - for (auto& m : klass->GetVirtualMethods(pointer_size)) { - methods.push_back(&m); - } - return methods; - } - bool AddMethod(const std::string& dex_location, uint32_t checksum, uint16_t method_index, @@ -97,89 +74,6 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { return static_cast(file.GetFd()); } - bool SaveProfilingInfo( - const std::string& filename, - const std::vector& methods, - const std::set& resolved_classes, - Hotness::Flag flags) { - ProfileCompilationInfo info; - std::vector profile_methods; - ScopedObjectAccess soa(Thread::Current()); - for (ArtMethod* method : methods) { - profile_methods.emplace_back( - MethodReference(method->GetDexFile(), method->GetDexMethodIndex())); - } - if (!info.AddMethods(profile_methods, flags) || !info.AddClasses(resolved_classes)) { - return false; - } - if (info.GetNumberOfMethods() != profile_methods.size()) { - return false; - } - ProfileCompilationInfo file_profile; - if (!file_profile.Load(filename, false)) { - return false; - } - if (!info.MergeWith(file_profile)) { - return false; - } - - return info.Save(filename, nullptr); - } - - // Saves the given art methods to a profile backed by 'filename' and adds - // some fake inline caches to it. The added inline caches are returned in - // the out map `profile_methods_map`. - bool SaveProfilingInfoWithFakeInlineCaches( - const std::string& filename, - const std::vector& methods, - Hotness::Flag flags, - /*out*/ SafeMap* profile_methods_map) { - ProfileCompilationInfo info; - std::vector profile_methods; - ScopedObjectAccess soa(Thread::Current()); - for (ArtMethod* method : methods) { - std::vector caches; - // Monomorphic - for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) { - std::vector classes; - classes.emplace_back(method->GetDexFile(), dex::TypeIndex(0)); - caches.emplace_back(dex_pc, /*is_missing_types*/false, classes); - } - // Polymorphic - for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) { - std::vector classes; - for (uint16_t k = 0; k < InlineCache::kIndividualCacheSize / 2; k++) { - classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k)); - } - caches.emplace_back(dex_pc, /*is_missing_types*/false, classes); - } - // Megamorphic - for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) { - std::vector classes; - for (uint16_t k = 0; k < 2 * InlineCache::kIndividualCacheSize; k++) { - classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k)); - } - caches.emplace_back(dex_pc, /*is_missing_types*/false, classes); - } - // Missing types - for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) { - std::vector classes; - caches.emplace_back(dex_pc, /*is_missing_types*/true, classes); - } - ProfileMethodInfo pmi(MethodReference(method->GetDexFile(), - method->GetDexMethodIndex()), - caches); - profile_methods.push_back(pmi); - profile_methods_map->Put(method, pmi); - } - - if (!info.AddMethods(profile_methods, flags) - || info.GetNumberOfMethods() != profile_methods.size()) { - return false; - } - return info.Save(filename, nullptr); - } - // Creates an inline cache which will be destructed at the end of the test. ProfileCompilationInfo::InlineCacheMap* CreateInlineCacheMap() { used_inline_caches.emplace_back(new ProfileCompilationInfo::InlineCacheMap( @@ -187,35 +81,6 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { return used_inline_caches.back().get(); } - ProfileCompilationInfo::OfflineProfileMethodInfo ConvertProfileMethodInfo( - const ProfileMethodInfo& pmi) { - ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap(); - ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi(ic_map); - SafeMap dex_map; // dex files to profile index - for (const auto& inline_cache : pmi.inline_caches) { - ProfileCompilationInfo::DexPcData& dex_pc_data = - ic_map->FindOrAdd( - inline_cache.dex_pc, ProfileCompilationInfo::DexPcData(allocator_.get()))->second; - if (inline_cache.is_missing_types) { - dex_pc_data.SetIsMissingTypes(); - } - for (const auto& class_ref : inline_cache.classes) { - uint8_t dex_profile_index = dex_map.FindOrAdd(const_cast(class_ref.dex_file), - static_cast(dex_map.size()))->second; - dex_pc_data.AddClass(dex_profile_index, class_ref.TypeIndex()); - if (dex_profile_index >= offline_pmi.dex_references.size()) { - // This is a new dex. - const std::string& dex_key = ProfileCompilationInfo::GetProfileDexFileKey( - class_ref.dex_file->GetLocation()); - offline_pmi.dex_references.emplace_back(dex_key, - class_ref.dex_file->GetLocationChecksum(), - class_ref.dex_file->NumMethodIds()); - } - } - } - return offline_pmi; - } - // Creates an offline profile used for testing inline caches. ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo() { ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap(); @@ -261,7 +126,7 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { ProfileCompilationInfo::InlineCacheMap* ic_map = const_cast(pmi->inline_caches); for (auto it : *ic_map) { - for (uint16_t k = 0; k <= 2 * InlineCache::kIndividualCacheSize; k++) { + for (uint16_t k = 0; k <= 2 * ProfileCompilationInfo::kIndividualInlineCacheSize; k++) { it.second.AddClass(0, dex::TypeIndex(k)); } } @@ -327,6 +192,7 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { static constexpr int kProfileMagicSize = 4; static constexpr int kProfileVersionSize = 4; + MallocArenaPool pool_; std::unique_ptr allocator_; // Cache of inline caches generated during tests. @@ -335,61 +201,6 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { std::vector> used_inline_caches; }; -TEST_F(ProfileCompilationInfoTest, SaveArtMethods) { - ScratchFile profile; - - Thread* self = Thread::Current(); - jobject class_loader; - { - ScopedObjectAccess soa(self); - class_loader = LoadDex("ProfileTestMultiDex"); - } - ASSERT_NE(class_loader, nullptr); - - // Save virtual methods from Main. - std::set resolved_classes; - std::vector main_methods = GetVirtualMethods(class_loader, "LMain;"); - ASSERT_TRUE(SaveProfilingInfo( - profile.GetFilename(), main_methods, resolved_classes, Hotness::kFlagPostStartup)); - - // Check that what we saved is in the profile. - ProfileCompilationInfo info1; - ASSERT_TRUE(info1.Load(GetFd(profile))); - ASSERT_EQ(info1.GetNumberOfMethods(), main_methods.size()); - { - ScopedObjectAccess soa(self); - for (ArtMethod* m : main_methods) { - Hotness h = info1.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); - ASSERT_TRUE(h.IsHot()); - ASSERT_TRUE(h.IsPostStartup()); - } - } - - // Save virtual methods from Second. - std::vector second_methods = GetVirtualMethods(class_loader, "LSecond;"); - ASSERT_TRUE(SaveProfilingInfo( - profile.GetFilename(), second_methods, resolved_classes, Hotness::kFlagStartup)); - - // Check that what we saved is in the profile (methods form Main and Second). - ProfileCompilationInfo info2; - ASSERT_TRUE(profile.GetFile()->ResetOffset()); - ASSERT_TRUE(info2.Load(GetFd(profile))); - ASSERT_EQ(info2.GetNumberOfMethods(), main_methods.size() + second_methods.size()); - { - ScopedObjectAccess soa(self); - for (ArtMethod* m : main_methods) { - Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); - ASSERT_TRUE(h.IsHot()); - ASSERT_TRUE(h.IsPostStartup()); - } - for (ArtMethod* m : second_methods) { - Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); - ASSERT_TRUE(h.IsHot()); - ASSERT_TRUE(h.IsStartup()); - } - } -} - TEST_F(ProfileCompilationInfoTest, SaveFd) { ScratchFile profile; @@ -722,48 +533,6 @@ TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCaches) { ASSERT_TRUE(*loaded_pmi1 == pmi_extra); } -TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) { - ScratchFile profile; - - Thread* self = Thread::Current(); - jobject class_loader; - { - ScopedObjectAccess soa(self); - class_loader = LoadDex("ProfileTestMultiDex"); - } - ASSERT_NE(class_loader, nullptr); - - // Save virtual methods from Main. - std::set resolved_classes; - std::vector main_methods = GetVirtualMethods(class_loader, "LMain;"); - - SafeMap profile_methods_map; - ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches( - profile.GetFilename(), main_methods, Hotness::kFlagStartup, &profile_methods_map)); - - // Check that what we saved is in the profile. - ProfileCompilationInfo info; - ASSERT_TRUE(info.Load(GetFd(profile))); - ASSERT_EQ(info.GetNumberOfMethods(), main_methods.size()); - { - ScopedObjectAccess soa(self); - for (ArtMethod* m : main_methods) { - Hotness h = info.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); - ASSERT_TRUE(h.IsHot()); - ASSERT_TRUE(h.IsStartup()); - const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second; - std::unique_ptr offline_pmi = - info.GetMethod(m->GetDexFile()->GetLocation(), - m->GetDexFile()->GetLocationChecksum(), - m->GetDexMethodIndex()); - ASSERT_TRUE(offline_pmi != nullptr); - ProfileCompilationInfo::OfflineProfileMethodInfo converted_pmi = - ConvertProfileMethodInfo(pmi); - ASSERT_EQ(converted_pmi, *offline_pmi); - } - } -} - TEST_F(ProfileCompilationInfoTest, InvalidChecksumInInlineCache) { ScratchFile profile; diff --git a/runtime/Android.bp b/runtime/Android.bp index b276c81d5a..777a1fc5ee 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -574,6 +574,7 @@ art_cc_test { "interpreter/safe_math_test.cc", "interpreter/unstarted_runtime_test.cc", "jdwp/jdwp_options_test.cc", + "jit/profiling_info_test.cc", "jni/java_vm_ext_test.cc", "method_handles_test.cc", "mirror/dex_cache_test.cc", diff --git a/runtime/jit/profiling_info_test.cc b/runtime/jit/profiling_info_test.cc new file mode 100644 index 0000000000..106a80a568 --- /dev/null +++ b/runtime/jit/profiling_info_test.cc @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "art_method-inl.h" +#include "base/unix_file/fd_file.h" +#include "class_linker-inl.h" +#include "common_runtime_test.h" +#include "dex/dex_file.h" +#include "dex/dex_file_loader.h" +#include "dex/method_reference.h" +#include "dex/type_reference.h" +#include "handle_scope-inl.h" +#include "linear_alloc.h" +#include "mirror/class-inl.h" +#include "mirror/class_loader.h" +#include "profile/profile_compilation_info.h" +#include "scoped_thread_state_change-inl.h" +#include "ziparchive/zip_writer.h" + +namespace art { + +using Hotness = ProfileCompilationInfo::MethodHotness; + +static constexpr size_t kMaxMethodIds = 65535; + +class ProfileCompilationInfoTest : public CommonRuntimeTest { + public: + void PostRuntimeCreate() OVERRIDE { + allocator_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool())); + } + + protected: + std::vector GetVirtualMethods(jobject class_loader, + const std::string& clazz) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + StackHandleScope<1> hs(self); + Handle h_loader( + hs.NewHandle(self->DecodeJObject(class_loader)->AsClassLoader())); + ObjPtr klass = class_linker->FindClass(self, clazz.c_str(), h_loader); + + const auto pointer_size = class_linker->GetImagePointerSize(); + std::vector methods; + for (auto& m : klass->GetVirtualMethods(pointer_size)) { + methods.push_back(&m); + } + return methods; + } + + bool AddMethod(const std::string& dex_location, + uint32_t checksum, + uint16_t method_index, + ProfileCompilationInfo* info) { + return info->AddMethodIndex(Hotness::kFlagHot, + dex_location, + checksum, + method_index, + kMaxMethodIds); + } + + bool AddMethod(const std::string& dex_location, + uint32_t checksum, + uint16_t method_index, + const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi, + ProfileCompilationInfo* info) { + return info->AddMethod( + dex_location, checksum, method_index, kMaxMethodIds, pmi, Hotness::kFlagPostStartup); + } + + bool AddClass(const std::string& dex_location, + uint32_t checksum, + dex::TypeIndex type_index, + ProfileCompilationInfo* info) { + DexCacheResolvedClasses classes(dex_location, dex_location, checksum, kMaxMethodIds); + classes.AddClass(type_index); + return info->AddClasses({classes}); + } + + uint32_t GetFd(const ScratchFile& file) { + return static_cast(file.GetFd()); + } + + bool SaveProfilingInfo( + const std::string& filename, + const std::vector& methods, + const std::set& resolved_classes, + Hotness::Flag flags) { + ProfileCompilationInfo info; + std::vector profile_methods; + ScopedObjectAccess soa(Thread::Current()); + for (ArtMethod* method : methods) { + profile_methods.emplace_back( + MethodReference(method->GetDexFile(), method->GetDexMethodIndex())); + } + if (!info.AddMethods(profile_methods, flags) || !info.AddClasses(resolved_classes)) { + return false; + } + if (info.GetNumberOfMethods() != profile_methods.size()) { + return false; + } + ProfileCompilationInfo file_profile; + if (!file_profile.Load(filename, false)) { + return false; + } + if (!info.MergeWith(file_profile)) { + return false; + } + + return info.Save(filename, nullptr); + } + + // Saves the given art methods to a profile backed by 'filename' and adds + // some fake inline caches to it. The added inline caches are returned in + // the out map `profile_methods_map`. + bool SaveProfilingInfoWithFakeInlineCaches( + const std::string& filename, + const std::vector& methods, + Hotness::Flag flags, + /*out*/ SafeMap* profile_methods_map) { + ProfileCompilationInfo info; + std::vector profile_methods; + ScopedObjectAccess soa(Thread::Current()); + for (ArtMethod* method : methods) { + std::vector caches; + // Monomorphic + for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) { + std::vector classes; + classes.emplace_back(method->GetDexFile(), dex::TypeIndex(0)); + caches.emplace_back(dex_pc, /*is_missing_types*/false, classes); + } + // Polymorphic + for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) { + std::vector classes; + for (uint16_t k = 0; k < InlineCache::kIndividualCacheSize / 2; k++) { + classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k)); + } + caches.emplace_back(dex_pc, /*is_missing_types*/false, classes); + } + // Megamorphic + for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) { + std::vector classes; + for (uint16_t k = 0; k < 2 * InlineCache::kIndividualCacheSize; k++) { + classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k)); + } + caches.emplace_back(dex_pc, /*is_missing_types*/false, classes); + } + // Missing types + for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) { + std::vector classes; + caches.emplace_back(dex_pc, /*is_missing_types*/true, classes); + } + ProfileMethodInfo pmi(MethodReference(method->GetDexFile(), + method->GetDexMethodIndex()), + caches); + profile_methods.push_back(pmi); + profile_methods_map->Put(method, pmi); + } + + if (!info.AddMethods(profile_methods, flags) + || info.GetNumberOfMethods() != profile_methods.size()) { + return false; + } + return info.Save(filename, nullptr); + } + + // Creates an inline cache which will be destructed at the end of the test. + ProfileCompilationInfo::InlineCacheMap* CreateInlineCacheMap() { + used_inline_caches.emplace_back(new ProfileCompilationInfo::InlineCacheMap( + std::less(), allocator_->Adapter(kArenaAllocProfile))); + return used_inline_caches.back().get(); + } + + ProfileCompilationInfo::OfflineProfileMethodInfo ConvertProfileMethodInfo( + const ProfileMethodInfo& pmi) { + ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap(); + ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi(ic_map); + SafeMap dex_map; // dex files to profile index + for (const auto& inline_cache : pmi.inline_caches) { + ProfileCompilationInfo::DexPcData& dex_pc_data = + ic_map->FindOrAdd( + inline_cache.dex_pc, ProfileCompilationInfo::DexPcData(allocator_.get()))->second; + if (inline_cache.is_missing_types) { + dex_pc_data.SetIsMissingTypes(); + } + for (const auto& class_ref : inline_cache.classes) { + uint8_t dex_profile_index = dex_map.FindOrAdd(const_cast(class_ref.dex_file), + static_cast(dex_map.size()))->second; + dex_pc_data.AddClass(dex_profile_index, class_ref.TypeIndex()); + if (dex_profile_index >= offline_pmi.dex_references.size()) { + // This is a new dex. + const std::string& dex_key = ProfileCompilationInfo::GetProfileDexFileKey( + class_ref.dex_file->GetLocation()); + offline_pmi.dex_references.emplace_back(dex_key, + class_ref.dex_file->GetLocationChecksum(), + class_ref.dex_file->NumMethodIds()); + } + } + } + return offline_pmi; + } + + // Cannot sizeof the actual arrays so hard code the values here. + // They should not change anyway. + static constexpr int kProfileMagicSize = 4; + static constexpr int kProfileVersionSize = 4; + + std::unique_ptr allocator_; + + // Cache of inline caches generated during tests. + // This makes it easier to pass data between different utilities and ensure that + // caches are destructed at the end of the test. + std::vector> used_inline_caches; +}; + +TEST_F(ProfileCompilationInfoTest, SaveArtMethods) { + ScratchFile profile; + + Thread* self = Thread::Current(); + jobject class_loader; + { + ScopedObjectAccess soa(self); + class_loader = LoadDex("ProfileTestMultiDex"); + } + ASSERT_NE(class_loader, nullptr); + + // Save virtual methods from Main. + std::set resolved_classes; + std::vector main_methods = GetVirtualMethods(class_loader, "LMain;"); + ASSERT_TRUE(SaveProfilingInfo( + profile.GetFilename(), main_methods, resolved_classes, Hotness::kFlagPostStartup)); + + // Check that what we saved is in the profile. + ProfileCompilationInfo info1; + ASSERT_TRUE(info1.Load(GetFd(profile))); + ASSERT_EQ(info1.GetNumberOfMethods(), main_methods.size()); + { + ScopedObjectAccess soa(self); + for (ArtMethod* m : main_methods) { + Hotness h = info1.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); + ASSERT_TRUE(h.IsHot()); + ASSERT_TRUE(h.IsPostStartup()); + } + } + + // Save virtual methods from Second. + std::vector second_methods = GetVirtualMethods(class_loader, "LSecond;"); + ASSERT_TRUE(SaveProfilingInfo( + profile.GetFilename(), second_methods, resolved_classes, Hotness::kFlagStartup)); + + // Check that what we saved is in the profile (methods form Main and Second). + ProfileCompilationInfo info2; + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + ASSERT_TRUE(info2.Load(GetFd(profile))); + ASSERT_EQ(info2.GetNumberOfMethods(), main_methods.size() + second_methods.size()); + { + ScopedObjectAccess soa(self); + for (ArtMethod* m : main_methods) { + Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); + ASSERT_TRUE(h.IsHot()); + ASSERT_TRUE(h.IsPostStartup()); + } + for (ArtMethod* m : second_methods) { + Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); + ASSERT_TRUE(h.IsHot()); + ASSERT_TRUE(h.IsStartup()); + } + } +} + +TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) { + ScratchFile profile; + + Thread* self = Thread::Current(); + jobject class_loader; + { + ScopedObjectAccess soa(self); + class_loader = LoadDex("ProfileTestMultiDex"); + } + ASSERT_NE(class_loader, nullptr); + + // Save virtual methods from Main. + std::set resolved_classes; + std::vector main_methods = GetVirtualMethods(class_loader, "LMain;"); + + SafeMap profile_methods_map; + ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches( + profile.GetFilename(), main_methods, Hotness::kFlagStartup, &profile_methods_map)); + + // Check that what we saved is in the profile. + ProfileCompilationInfo info; + ASSERT_TRUE(info.Load(GetFd(profile))); + ASSERT_EQ(info.GetNumberOfMethods(), main_methods.size()); + { + ScopedObjectAccess soa(self); + for (ArtMethod* m : main_methods) { + Hotness h = info.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())); + ASSERT_TRUE(h.IsHot()); + ASSERT_TRUE(h.IsStartup()); + const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second; + std::unique_ptr offline_pmi = + info.GetMethod(m->GetDexFile()->GetLocation(), + m->GetDexFile()->GetLocationChecksum(), + m->GetDexMethodIndex()); + ASSERT_TRUE(offline_pmi != nullptr); + ProfileCompilationInfo::OfflineProfileMethodInfo converted_pmi = + ConvertProfileMethodInfo(pmi); + ASSERT_EQ(converted_pmi, *offline_pmi); + } + } +} + +} // namespace art -- GitLab From 3d092995bbba4f7b559114d8c803890a5c90e646 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 24 May 2018 13:36:23 -0700 Subject: [PATCH 484/749] Move oatdump/ to use ClassAccessor Test: test-art-host Bug: 79758018 Change-Id: I53d70063cfb0a4c94d9d95ac7e98758d97082c20 --- libdexfile/dex/class_accessor.h | 4 ++++ oatdump/oatdump.cc | 38 +++++++++++---------------------- 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/libdexfile/dex/class_accessor.h b/libdexfile/dex/class_accessor.h index 7455704a7f..dda6e1c1a6 100644 --- a/libdexfile/dex/class_accessor.h +++ b/libdexfile/dex/class_accessor.h @@ -271,6 +271,10 @@ class ClassAccessor { return num_virtual_methods_; } + uint32_t NumMethods() const { + return NumDirectMethods() + NumVirtualMethods(); + } + const char* GetDescriptor() const; dex::TypeIndex GetClassIdx() const { diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index fcd6bfd46c..9eeb530644 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -45,6 +45,7 @@ #include "debug/debug_info.h" #include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" +#include "dex/class_accessor-inl.h" #include "dex/code_item_accessors-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" @@ -268,25 +269,18 @@ class OatSymbolizer FINAL { void WalkOatClass(const OatFile::OatClass& oat_class, const DexFile& dex_file, uint32_t class_def_index) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { // empty class such as a marker interface? - return; - } + ClassAccessor accessor(dex_file, dex_file.GetClassDef(class_def_index)); // Note: even if this is an interface or a native class, we still have to walk it, as there // might be a static initializer. - ClassDataItemIterator it(dex_file, class_data); uint32_t class_method_idx = 0; - it.SkipAllFields(); - for (; it.HasNextMethod(); it.Next()) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { WalkOatMethod(oat_class.GetOatMethod(class_method_idx++), dex_file, class_def_index, - it.GetMemberIndex(), - it.GetMethodCodeItem(), - it.GetMethodAccessFlags()); + method.GetIndex(), + method.GetCodeItem(), + method.GetAccessFlags()); } - DCHECK(!it.HasNext()); } void WalkOatMethod(const OatFile::OatMethod& oat_method, @@ -904,21 +898,15 @@ class OatDumper { continue; } offsets_.insert(reinterpret_cast(&dex_file->GetHeader())); - for (size_t class_def_index = 0; - class_def_index < dex_file->NumClassDefs(); - class_def_index++) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); + uint32_t class_def_index = 0u; + for (ClassAccessor accessor : dex_file->GetClasses()) { const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index); - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data != nullptr) { - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - uint32_t class_method_index = 0; - while (it.HasNextMethod()) { - AddOffsets(oat_class.GetOatMethod(class_method_index++)); - it.Next(); - } + for (uint32_t class_method_index = 0; + class_method_index < accessor.NumMethods(); + ++class_method_index) { + AddOffsets(oat_class.GetOatMethod(class_method_index)); } + ++class_def_index; } } -- GitLab From 93f30a99a159f0e46d9fd6e1def04b67aa2a0120 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Tue, 29 May 2018 11:25:37 -0700 Subject: [PATCH 485/749] Change sleep to nanosleep to avoid failures. The sleep function on arm 32 has points at which an unwind will fail since the exidx information does not cover every possible pc. The nanosleep function doesn't have this problem, it should unwind at all points. Bug: 80314302 Test: Ran 137-cfi on target many times. Change-Id: I6cdb8632a4e45f4cdd50f4c51a4f904eb5d2d340 --- test/137-cfi/cfi.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc index 49db0c82b5..7ada47d304 100644 --- a/test/137-cfi/cfi.cc +++ b/test/137-cfi/cfi.cc @@ -56,9 +56,12 @@ static void CauseSegfault() { extern "C" JNIEXPORT jboolean JNICALL Java_Main_sleep(JNIEnv*, jobject, jint, jboolean, jdouble) { // Keep pausing. + struct timespec ts = { .tv_sec = 100, .tv_nsec = 0 }; printf("Going to sleep\n"); for (;;) { - sleep(1); + // Use nanosleep since it gets to the system call quickly and doesn't + // have any points at which an unwind will fail. + nanosleep(&ts, nullptr); } } -- GitLab From baeac17dee4ba878740606d84aa8302da312e98a Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Fri, 25 May 2018 15:21:50 +0100 Subject: [PATCH 486/749] Add update option to dexdump test runner Test: art/test/dexdump/run-all-tests --update Test: art/test/dexdump/run-all-tests --help Test: art/test/dexdump/run-all-tests Change-Id: Ia6d1eee2da274573702b560e6c9c21aaf926f892 --- test/dexdump/run-all-tests | 92 +++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 35 deletions(-) diff --git a/test/dexdump/run-all-tests b/test/dexdump/run-all-tests index c9976cd090..e555a44e5d 100755 --- a/test/dexdump/run-all-tests +++ b/test/dexdump/run-all-tests @@ -37,51 +37,73 @@ prog="${progdir}"/`basename "${prog}"` tmpdir=/tmp/test-$$ mkdir ${tmpdir} -# Set up dexdump binary and flags to test. -DEXD="${ANDROID_HOST_OUT}/bin/dexdump2" -DEXDFLAGS1="-adfh" -DEXDFLAGS2="-e -l xml" +# Set up tools and commands to run +DEXDUMP="${ANDROID_HOST_OUT}/bin/dexdump2" +DEXLIST="${ANDROID_HOST_OUT}/bin/dexlist" -# Set up dexlist binary and flags to test. -DEXL="${ANDROID_HOST_OUT}/bin/dexlist" -DEXLFLAGS="" +declare -A SUFFIX_COMMAND_MAP +SUFFIX_COMMAND_MAP[txt]="${DEXDUMP} -adfh" +SUFFIX_COMMAND_MAP[xml]="${DEXDUMP} -e -l xml" +SUFFIX_COMMAND_MAP[lst]="${DEXLIST}" + +# Parse command-line options +UPDATE="no" +USAGE="no" +while [ $# -ne 0 ]; do + case "$1" in + --update) + UPDATE="yes" + ;; + *) + echo "Unknown option $1" 1>&2 + USAGE="yes" + ;; + esac + shift +done + +if [ "${USAGE}" = "yes" ]; then + cat 1>&2 < ${new_output} + if [ $? -ne 0 ]; then + echo "Failed running ${SUFFIX_COMMAND_MAP[${suffix}]} ${dex} > ${new_output}" 2>&1 + exit 1 + fi + done + done + exit 0 +fi # Run the tests. passed=0 failed=0 -for i in *.dex; do - echo $i - basenm=`basename "${i}" .dex` - txtfile=${basenm}.txt - xmlfile=${basenm}.xml - lstfile=${basenm}.lst - gentxtfile=${tmpdir}/${txtfile} - genxmlfile=${tmpdir}/${xmlfile} - genlstfile=${tmpdir}/${lstfile} - ${DEXD} ${DEXDFLAGS1} ${i} > ${gentxtfile} - cmp ${txtfile} ${gentxtfile} - if [ "$?" = "0" ]; then - ((passed += 1)) - else - ((failed += 1)) - echo failed: ${i} - fi - ${DEXD} ${DEXDFLAGS2} ${i} > ${genxmlfile} - cmp ${xmlfile} ${genxmlfile} - if [ "$?" = "0" ]; then - ((passed += 1)) - else - ((failed += 1)) - echo failed: ${i} - fi - ${DEXL} ${DEXLFLAGS} ${i} > ${genlstfile} - cmp ${lstfile} ${genlstfile} +for dex in *.dex; do + echo ${dex} + for suffix in ${!SUFFIX_COMMAND_MAP[@]}; do + expected_output=${dex%%.*}.${suffix} + actual_output=${tmpdir}/${expected_output} + cmd="${SUFFIX_COMMAND_MAP[${suffix}]} ${dex}" + ${cmd} > ${actual_output} + cmp ${expected_output} ${actual_output} if [ "$?" = "0" ]; then ((passed += 1)) else ((failed += 1)) - echo failed: ${i} + echo failed: ${cmd} fi + done done # Report results. -- GitLab From da4ff8bfcf03d6186b7d21c7bd8b90248f7b2f96 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Tue, 29 May 2018 20:20:12 +0100 Subject: [PATCH 487/749] Cleanup stackmap generation in exception_test The alignment code was unnecessarily complicated, and the final alignment check was incorrect. Test: test-art-target-gtest-exception_test32 Change-Id: I34e17af11d59b93bc445d7a2a5bd76552675fe3a --- compiler/exception_test.cc | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc index c139fcf1d8..da1db4593b 100644 --- a/compiler/exception_test.cc +++ b/compiler/exception_test.cc @@ -68,47 +68,38 @@ class ExceptionTest : public CommonRuntimeTest { fake_code_.push_back(0x70 | i); } + const uint32_t native_pc_offset = 4u; + CHECK_ALIGNED_PARAM(native_pc_offset, GetInstructionSetInstructionAlignment(kRuntimeISA)); + MallocArenaPool pool; ArenaStack arena_stack(&pool); ScopedArenaAllocator allocator(&arena_stack); StackMapStream stack_maps(&allocator, kRuntimeISA); stack_maps.BeginStackMapEntry(kDexPc, - /* native_pc_offset */ 3u, + native_pc_offset, /* register_mask */ 0u, /* sp_mask */ nullptr, /* num_dex_registers */ 0u, /* inlining_depth */ 0u); stack_maps.EndStackMapEntry(); - size_t stack_maps_size = stack_maps.PrepareForFillIn(); - size_t stack_maps_offset = stack_maps_size + sizeof(OatQuickMethodHeader); + const size_t stack_maps_size = stack_maps.PrepareForFillIn(); + const size_t header_size = sizeof(OatQuickMethodHeader); + const size_t code_alignment = GetInstructionSetAlignment(kRuntimeISA); + const size_t code_offset = RoundUp(stack_maps_size + header_size, code_alignment); - fake_header_code_and_maps_.resize(stack_maps_offset + fake_code_.size()); + fake_header_code_and_maps_.resize(code_offset + fake_code_.size()); MemoryRegion stack_maps_region(&fake_header_code_and_maps_[0], stack_maps_size); stack_maps.FillInCodeInfo(stack_maps_region); - OatQuickMethodHeader method_header(stack_maps_offset, 0u, 4 * sizeof(void*), 0u, 0u, code_size); - memcpy(&fake_header_code_and_maps_[stack_maps_size], &method_header, sizeof(method_header)); + OatQuickMethodHeader method_header(code_offset, 0u, 4 * sizeof(void*), 0u, 0u, code_size); + memcpy(&fake_header_code_and_maps_[code_offset - header_size], &method_header, header_size); std::copy(fake_code_.begin(), fake_code_.end(), - fake_header_code_and_maps_.begin() + stack_maps_offset); - - // Align the code. - const size_t alignment = GetInstructionSetAlignment(kRuntimeISA); - fake_header_code_and_maps_.reserve(fake_header_code_and_maps_.size() + alignment); - const void* unaligned_code_ptr = - fake_header_code_and_maps_.data() + (fake_header_code_and_maps_.size() - code_size); - size_t offset = dchecked_integral_cast(reinterpret_cast(unaligned_code_ptr)); - size_t padding = RoundUp(offset, alignment) - offset; - // Make sure no resizing takes place. - CHECK_GE(fake_header_code_and_maps_.capacity(), fake_header_code_and_maps_.size() + padding); - fake_header_code_and_maps_.insert(fake_header_code_and_maps_.begin(), padding, 0); - const void* code_ptr = reinterpret_cast(unaligned_code_ptr) + padding; - CHECK_EQ(code_ptr, - static_cast(fake_header_code_and_maps_.data() + - (fake_header_code_and_maps_.size() - code_size))); + fake_header_code_and_maps_.begin() + code_offset); + const void* code_ptr = fake_header_code_and_maps_.data() + code_offset; if (kRuntimeISA == InstructionSet::kArm) { // Check that the Thumb2 adjustment will be a NOP, see EntryPointToCodePointer(). - CHECK_ALIGNED(stack_maps_offset, 2); + CHECK_ALIGNED(code_ptr, 2); } method_f_ = my_klass_->FindClassMethod("f", "()I", kRuntimePointerSize); -- GitLab From 8ccf92a9353ff00cf3bec69a1b7864eecab4ffc0 Mon Sep 17 00:00:00 2001 From: Tamas Kenez Date: Fri, 25 May 2018 12:12:03 +0200 Subject: [PATCH 488/749] ART-tests: Remove DX-dependency from 476-checker-ctor-fence-redun-elim The test was already passing with D8, this CL only enables D8. Bug: 65168732 Test: art/test.py -r --host --gcstress -t 476-checker-ctor-fence-redun-elim art/test.py -r --target --gcstress -t 476-checker-ctor-fence-redun-elim Change-Id: Ie44e853477b46be578d34480ac2beea7d9a195d8 --- test/476-checker-ctor-fence-redun-elim/build | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 test/476-checker-ctor-fence-redun-elim/build diff --git a/test/476-checker-ctor-fence-redun-elim/build b/test/476-checker-ctor-fence-redun-elim/build deleted file mode 100644 index 10ffcc537d..0000000000 --- a/test/476-checker-ctor-fence-redun-elim/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" -- GitLab From 2cffc5d2807096c0b7a07b89f5dda6920c6429f5 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 29 May 2018 15:40:56 +0100 Subject: [PATCH 489/749] ObjPtr<>-ify RegTypeCache, fix 1 stale reference use. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Bug: 31113334 Change-Id: If8674fc712d9bc4d7d2fef1d154192b31b153627 --- runtime/verifier/method_verifier.cc | 10 +-- runtime/verifier/method_verifier.h | 2 +- runtime/verifier/reg_type.cc | 103 +++++++++++++++----------- runtime/verifier/reg_type.h | 100 +++++++++++++++---------- runtime/verifier/reg_type_cache-inl.h | 16 ++-- runtime/verifier/reg_type_cache.cc | 34 +++++---- runtime/verifier/reg_type_cache.h | 18 +++-- runtime/verifier/verifier_deps.cc | 43 +++++------ runtime/verifier/verifier_deps.h | 20 ++--- 9 files changed, 194 insertions(+), 152 deletions(-) diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 287e3d619a..cc71dc5f84 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3366,7 +3366,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } break; } - auto* klass = declaring_class.GetClass(); + ObjPtr klass = declaring_class.GetClass(); for (uint32_t i = 0, num_fields = klass->NumInstanceFields(); i < num_fields; ++i) { if (klass->GetInstanceField(i)->IsFinal()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-void-no-barrier not expected for " @@ -3667,10 +3667,10 @@ const RegType& MethodVerifier::ResolveClass(dex::TypeIndex class_idx) { UninstantiableError(descriptor); precise = false; } - result = reg_types_.FindClass(klass.Ptr(), precise); + result = reg_types_.FindClass(klass, precise); if (result == nullptr) { const char* descriptor = dex_file_->StringByTypeIdx(class_idx); - result = reg_types_.InsertClass(descriptor, klass.Ptr(), precise); + result = reg_types_.InsertClass(descriptor, klass, precise); } } else { const char* descriptor = dex_file_->StringByTypeIdx(class_idx); @@ -4943,7 +4943,7 @@ const RegType& MethodVerifier::GetDeclaringClass() { const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_)); if (method_being_verified_ != nullptr) { - mirror::Class* klass = method_being_verified_->GetDeclaringClass(); + ObjPtr klass = method_being_verified_->GetDeclaringClass(); declaring_class_ = &FromClass(descriptor, klass, klass->CannotBeAssignedFromOtherTypes()); } else { declaring_class_ = ®_types_.FromDescriptor(GetClassLoader(), descriptor, false); @@ -5045,7 +5045,7 @@ void MethodVerifier::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) } const RegType& MethodVerifier::FromClass(const char* descriptor, - mirror::Class* klass, + ObjPtr klass, bool precise) { DCHECK(klass != nullptr); if (precise && !klass->IsInstantiable() && !klass->IsPrimitive()) { diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 531d3dabfa..b2adc62a97 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -691,7 +691,7 @@ class MethodVerifier { // non-precise reference will be returned. // Note: we reuse NO_CLASS as this will throw an exception at runtime, when the failing class is // actually touched. - const RegType& FromClass(const char* descriptor, mirror::Class* klass, bool precise) + const RegType& FromClass(const char* descriptor, ObjPtr klass, bool precise) REQUIRES_SHARED(Locks::mutator_lock_); ALWAYS_INLINE bool FailOrAbort(bool condition, const char* error_msg, uint32_t work_insn_idx); diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index e7864a28a0..cbfbb4000a 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -54,17 +54,19 @@ const DoubleHiType* DoubleHiType::instance_ = nullptr; const IntegerType* IntegerType::instance_ = nullptr; const NullType* NullType::instance_ = nullptr; -PrimitiveType::PrimitiveType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) +PrimitiveType::PrimitiveType(ObjPtr klass, + const StringPiece& descriptor, + uint16_t cache_id) : RegType(klass, descriptor, cache_id) { CHECK(klass != nullptr); CHECK(!descriptor.empty()); } -Cat1Type::Cat1Type(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) +Cat1Type::Cat1Type(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) : PrimitiveType(klass, descriptor, cache_id) { } -Cat2Type::Cat2Type(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) +Cat2Type::Cat2Type(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) : PrimitiveType(klass, descriptor, cache_id) { } @@ -129,7 +131,7 @@ std::string IntegerType::Dump() const { return "Integer"; } -const DoubleHiType* DoubleHiType::CreateInstance(mirror::Class* klass, +const DoubleHiType* DoubleHiType::CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); @@ -144,7 +146,7 @@ void DoubleHiType::Destroy() { } } -const DoubleLoType* DoubleLoType::CreateInstance(mirror::Class* klass, +const DoubleLoType* DoubleLoType::CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); @@ -159,14 +161,16 @@ void DoubleLoType::Destroy() { } } -const LongLoType* LongLoType::CreateInstance(mirror::Class* klass, const StringPiece& descriptor, +const LongLoType* LongLoType::CreateInstance(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); instance_ = new LongLoType(klass, descriptor, cache_id); return instance_; } -const LongHiType* LongHiType::CreateInstance(mirror::Class* klass, const StringPiece& descriptor, +const LongHiType* LongHiType::CreateInstance(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); instance_ = new LongHiType(klass, descriptor, cache_id); @@ -187,7 +191,8 @@ void LongLoType::Destroy() { } } -const FloatType* FloatType::CreateInstance(mirror::Class* klass, const StringPiece& descriptor, +const FloatType* FloatType::CreateInstance(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); instance_ = new FloatType(klass, descriptor, cache_id); @@ -201,7 +206,8 @@ void FloatType::Destroy() { } } -const CharType* CharType::CreateInstance(mirror::Class* klass, const StringPiece& descriptor, +const CharType* CharType::CreateInstance(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); instance_ = new CharType(klass, descriptor, cache_id); @@ -215,7 +221,8 @@ void CharType::Destroy() { } } -const ShortType* ShortType::CreateInstance(mirror::Class* klass, const StringPiece& descriptor, +const ShortType* ShortType::CreateInstance(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); instance_ = new ShortType(klass, descriptor, cache_id); @@ -229,7 +236,8 @@ void ShortType::Destroy() { } } -const ByteType* ByteType::CreateInstance(mirror::Class* klass, const StringPiece& descriptor, +const ByteType* ByteType::CreateInstance(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); instance_ = new ByteType(klass, descriptor, cache_id); @@ -243,7 +251,8 @@ void ByteType::Destroy() { } } -const IntegerType* IntegerType::CreateInstance(mirror::Class* klass, const StringPiece& descriptor, +const IntegerType* IntegerType::CreateInstance(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); instance_ = new IntegerType(klass, descriptor, cache_id); @@ -257,7 +266,7 @@ void IntegerType::Destroy() { } } -const ConflictType* ConflictType::CreateInstance(mirror::Class* klass, +const ConflictType* ConflictType::CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); @@ -272,8 +281,9 @@ void ConflictType::Destroy() { } } -const BooleanType* BooleanType::CreateInstance(mirror::Class* klass, const StringPiece& descriptor, - uint16_t cache_id) { +const BooleanType* BooleanType::CreateInstance(ObjPtr klass, + const StringPiece& descriptor, + uint16_t cache_id) { CHECK(BooleanType::instance_ == nullptr); instance_ = new BooleanType(klass, descriptor, cache_id); return BooleanType::instance_; @@ -290,7 +300,7 @@ std::string UndefinedType::Dump() const REQUIRES_SHARED(Locks::mutator_lock_) { return "Undefined"; } -const UndefinedType* UndefinedType::CreateInstance(mirror::Class* klass, +const UndefinedType* UndefinedType::CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); @@ -305,7 +315,8 @@ void UndefinedType::Destroy() { } } -PreciseReferenceType::PreciseReferenceType(mirror::Class* klass, const StringPiece& descriptor, +PreciseReferenceType::PreciseReferenceType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) : RegType(klass, descriptor, cache_id) { // Note: no check for IsInstantiable() here. We may produce this in case an InstantiationError @@ -505,7 +516,7 @@ bool UnresolvedType::IsNonZeroReferenceTypes() const { const RegType& RegType::GetSuperClass(RegTypeCache* cache) const { if (!IsUnresolvedTypes()) { - mirror::Class* super_klass = GetClass()->GetSuperClass(); + ObjPtr super_klass = GetClass()->GetSuperClass(); if (super_klass != nullptr) { // A super class of a precise type isn't precise as a precise type indicates the register // holds exactly that type. @@ -543,7 +554,7 @@ bool RegType::IsObjectArrayTypes() const REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(descriptor_[1] == 'L' || descriptor_[1] == '['); return descriptor_[0] == '['; } else if (HasClass()) { - mirror::Class* type = GetClass(); + ObjPtr type = GetClass(); return type->IsArrayClass() && !type->GetComponentType()->IsPrimitive(); } else { return false; @@ -569,7 +580,7 @@ bool RegType::IsArrayTypes() const REQUIRES_SHARED(Locks::mutator_lock_) { bool RegType::IsJavaLangObjectArray() const { if (HasClass()) { - mirror::Class* type = GetClass(); + ObjPtr type = GetClass(); return type->IsArrayClass() && type->GetComponentType()->IsObjectClass(); } return false; @@ -712,11 +723,9 @@ const RegType& RegType::Merge(const RegType& incoming_type, // mechanics to continue. return reg_types->FromUnresolvedMerge(*this, incoming_type, verifier); } else { // Two reference types, compute Join - mirror::Class* c1 = GetClass(); - mirror::Class* c2 = incoming_type.GetClass(); - DCHECK(c1 != nullptr && !c1->IsPrimitive()); - DCHECK(c2 != nullptr && !c2->IsPrimitive()); - mirror::Class* join_class = ClassJoin(c1, c2); + DCHECK(GetClass() != nullptr && !GetClass()->IsPrimitive()); + DCHECK(incoming_type.GetClass() != nullptr && !incoming_type.GetClass()->IsPrimitive()); + ObjPtr join_class = ClassJoin(GetClass(), incoming_type.GetClass()); if (UNLIKELY(join_class == nullptr)) { // Internal error joining the classes (e.g., OOME). Report an unresolved reference type. // We cannot report an unresolved merge type, as that will attempt to merge the resolved @@ -731,30 +740,37 @@ const RegType& RegType::Merge(const RegType& incoming_type, // (In that case, it is likely a misconfiguration of dex2oat.) if (!kIsTargetBuild && Runtime::Current()->IsAotCompiler()) { LOG(FATAL) << "Could not create class join of " - << c1->PrettyClass() + << GetClass()->PrettyClass() << " & " - << c2->PrettyClass(); + << incoming_type.GetClass()->PrettyClass(); UNREACHABLE(); } return reg_types->MakeUnresolvedReference(); } - // Record the dependency that both `c1` and `c2` are assignable to `join_class`. - // The `verifier` is null during unit tests. + // Record the dependency that both `GetClass()` and `incoming_type.GetClass()` + // are assignable to `join_class`. The `verifier` is null during unit tests. if (verifier != nullptr) { - VerifierDeps::MaybeRecordAssignability( - verifier->GetDexFile(), join_class, c1, true /* strict */, true /* is_assignable */); - VerifierDeps::MaybeRecordAssignability( - verifier->GetDexFile(), join_class, c2, true /* strict */, true /* is_assignable */); + VerifierDeps::MaybeRecordAssignability(verifier->GetDexFile(), + join_class, + GetClass(), + /* strict */ true, + /* is_assignable */ true); + VerifierDeps::MaybeRecordAssignability(verifier->GetDexFile(), + join_class, + incoming_type.GetClass(), + /* strict */ true, + /* is_assignable */ true); } - if (c1 == join_class && !IsPreciseReference()) { + if (GetClass() == join_class && !IsPreciseReference()) { return *this; - } else if (c2 == join_class && !incoming_type.IsPreciseReference()) { + } else if (incoming_type.GetClass() == join_class && !incoming_type.IsPreciseReference()) { return incoming_type; } else { std::string temp; - return reg_types->FromClass(join_class->GetDescriptor(&temp), join_class, false); + const char* descriptor = join_class->GetDescriptor(&temp); + return reg_types->FromClass(descriptor, join_class, /* precise */ false); } } } else { @@ -763,7 +779,7 @@ const RegType& RegType::Merge(const RegType& incoming_type, } // See comment in reg_type.h -mirror::Class* RegType::ClassJoin(mirror::Class* s, mirror::Class* t) { +ObjPtr RegType::ClassJoin(ObjPtr s, ObjPtr t) { DCHECK(!s->IsPrimitive()) << s->PrettyClass(); DCHECK(!t->IsPrimitive()) << t->PrettyClass(); if (s == t) { @@ -773,12 +789,12 @@ mirror::Class* RegType::ClassJoin(mirror::Class* s, mirror::Class* t) { } else if (t->IsAssignableFrom(s)) { return t; } else if (s->IsArrayClass() && t->IsArrayClass()) { - mirror::Class* s_ct = s->GetComponentType(); - mirror::Class* t_ct = t->GetComponentType(); + ObjPtr s_ct = s->GetComponentType(); + ObjPtr t_ct = t->GetComponentType(); if (s_ct->IsPrimitive() || t_ct->IsPrimitive()) { // Given the types aren't the same, if either array is of primitive types then the only // common parent is java.lang.Object - mirror::Class* result = s->GetSuperClass(); // short-cut to java.lang.Object + ObjPtr result = s->GetSuperClass(); // short-cut to java.lang.Object DCHECK(result->IsObjectClass()); return result; } @@ -788,8 +804,9 @@ mirror::Class* RegType::ClassJoin(mirror::Class* s, mirror::Class* t) { self->AssertPendingException(); return nullptr; } - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - mirror::Class* array_class = class_linker->FindArrayClass(self, &common_elem); + // Note: The following lookup invalidates existing ObjPtr<>s. + ObjPtr array_class = + Runtime::Current()->GetClassLinker()->FindArrayClass(self, &common_elem); if (UNLIKELY(array_class == nullptr)) { self->AssertPendingException(); return nullptr; @@ -971,7 +988,7 @@ bool RegType::CanAssignArray(const RegType& src, return cmp1.CanAssignArray(cmp2, reg_types, class_loader, verifier, soft_error); } -const NullType* NullType::CreateInstance(mirror::Class* klass, +const NullType* NullType::CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) { CHECK(instance_ == nullptr); diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h index 3e994074a1..29da376091 100644 --- a/runtime/verifier/reg_type.h +++ b/runtime/verifier/reg_type.h @@ -191,7 +191,7 @@ class RegType { !IsUnresolvedSuperClass())); return descriptor_; } - mirror::Class* GetClass() const REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr GetClass() const REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(!IsUnresolvedReference()); DCHECK(!klass_.IsNull()) << Dump(); DCHECK(HasClass()); @@ -318,7 +318,7 @@ class RegType { } protected: - RegType(mirror::Class* klass, + RegType(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : descriptor_(descriptor), @@ -365,7 +365,7 @@ class RegType { * * [1] Java bytecode verification: algorithms and formalizations, Xavier Leroy */ - static mirror::Class* ClassJoin(mirror::Class* s, mirror::Class* t) + static ObjPtr ClassJoin(ObjPtr s, ObjPtr t) REQUIRES_SHARED(Locks::mutator_lock_); static bool AssignableFrom(const RegType& lhs, @@ -388,7 +388,7 @@ class ConflictType FINAL : public RegType { static const ConflictType* GetInstance() PURE; // Create the singleton instance. - static const ConflictType* CreateInstance(mirror::Class* klass, + static const ConflictType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -401,7 +401,8 @@ class ConflictType FINAL : public RegType { } private: - ConflictType(mirror::Class* klass, const StringPiece& descriptor, + ConflictType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : RegType(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -423,7 +424,7 @@ class UndefinedType FINAL : public RegType { static const UndefinedType* GetInstance() PURE; // Create the singleton instance. - static const UndefinedType* CreateInstance(mirror::Class* klass, + static const UndefinedType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -436,7 +437,8 @@ class UndefinedType FINAL : public RegType { } private: - UndefinedType(mirror::Class* klass, const StringPiece& descriptor, + UndefinedType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : RegType(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -447,7 +449,8 @@ class UndefinedType FINAL : public RegType { class PrimitiveType : public RegType { public: - PrimitiveType(mirror::Class* klass, const StringPiece& descriptor, + PrimitiveType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); bool HasClassVirtual() const OVERRIDE { return true; } @@ -455,7 +458,7 @@ class PrimitiveType : public RegType { class Cat1Type : public PrimitiveType { public: - Cat1Type(mirror::Class* klass, const StringPiece& descriptor, + Cat1Type(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); }; @@ -463,7 +466,7 @@ class IntegerType FINAL : public Cat1Type { public: bool IsInteger() const OVERRIDE { return true; } std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); - static const IntegerType* CreateInstance(mirror::Class* klass, + static const IntegerType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -475,7 +478,8 @@ class IntegerType FINAL : public Cat1Type { } private: - IntegerType(mirror::Class* klass, const StringPiece& descriptor, + IntegerType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat1Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -487,7 +491,7 @@ class BooleanType FINAL : public Cat1Type { public: bool IsBoolean() const OVERRIDE { return true; } std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); - static const BooleanType* CreateInstance(mirror::Class* klass, + static const BooleanType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -499,7 +503,8 @@ class BooleanType FINAL : public Cat1Type { } private: - BooleanType(mirror::Class* klass, const StringPiece& descriptor, + BooleanType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat1Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -512,7 +517,7 @@ class ByteType FINAL : public Cat1Type { public: bool IsByte() const OVERRIDE { return true; } std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); - static const ByteType* CreateInstance(mirror::Class* klass, + static const ByteType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -524,7 +529,8 @@ class ByteType FINAL : public Cat1Type { } private: - ByteType(mirror::Class* klass, const StringPiece& descriptor, + ByteType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat1Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -536,7 +542,7 @@ class ShortType FINAL : public Cat1Type { public: bool IsShort() const OVERRIDE { return true; } std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); - static const ShortType* CreateInstance(mirror::Class* klass, + static const ShortType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -548,7 +554,7 @@ class ShortType FINAL : public Cat1Type { } private: - ShortType(mirror::Class* klass, const StringPiece& descriptor, + ShortType(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat1Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -560,7 +566,7 @@ class CharType FINAL : public Cat1Type { public: bool IsChar() const OVERRIDE { return true; } std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); - static const CharType* CreateInstance(mirror::Class* klass, + static const CharType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -572,7 +578,8 @@ class CharType FINAL : public Cat1Type { } private: - CharType(mirror::Class* klass, const StringPiece& descriptor, + CharType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat1Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -584,7 +591,7 @@ class FloatType FINAL : public Cat1Type { public: bool IsFloat() const OVERRIDE { return true; } std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); - static const FloatType* CreateInstance(mirror::Class* klass, + static const FloatType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -596,7 +603,8 @@ class FloatType FINAL : public Cat1Type { } private: - FloatType(mirror::Class* klass, const StringPiece& descriptor, + FloatType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat1Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -606,7 +614,8 @@ class FloatType FINAL : public Cat1Type { class Cat2Type : public PrimitiveType { public: - Cat2Type(mirror::Class* klass, const StringPiece& descriptor, + Cat2Type(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); }; @@ -615,7 +624,7 @@ class LongLoType FINAL : public Cat2Type { std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); bool IsLongLo() const OVERRIDE { return true; } bool IsLong() const OVERRIDE { return true; } - static const LongLoType* CreateInstance(mirror::Class* klass, + static const LongLoType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -627,7 +636,8 @@ class LongLoType FINAL : public Cat2Type { } private: - LongLoType(mirror::Class* klass, const StringPiece& descriptor, + LongLoType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat2Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -639,7 +649,7 @@ class LongHiType FINAL : public Cat2Type { public: std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); bool IsLongHi() const OVERRIDE { return true; } - static const LongHiType* CreateInstance(mirror::Class* klass, + static const LongHiType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -651,7 +661,8 @@ class LongHiType FINAL : public Cat2Type { } private: - LongHiType(mirror::Class* klass, const StringPiece& descriptor, + LongHiType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat2Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -664,7 +675,7 @@ class DoubleLoType FINAL : public Cat2Type { std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); bool IsDoubleLo() const OVERRIDE { return true; } bool IsDouble() const OVERRIDE { return true; } - static const DoubleLoType* CreateInstance(mirror::Class* klass, + static const DoubleLoType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -676,7 +687,8 @@ class DoubleLoType FINAL : public Cat2Type { } private: - DoubleLoType(mirror::Class* klass, const StringPiece& descriptor, + DoubleLoType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat2Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -688,9 +700,9 @@ class DoubleHiType FINAL : public Cat2Type { public: std::string Dump() const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); virtual bool IsDoubleHi() const OVERRIDE { return true; } - static const DoubleHiType* CreateInstance(mirror::Class* klass, - const StringPiece& descriptor, - uint16_t cache_id) + static const DoubleHiType* CreateInstance(ObjPtr klass, + const StringPiece& descriptor, + uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); static const DoubleHiType* GetInstance() PURE; static void Destroy(); @@ -700,7 +712,8 @@ class DoubleHiType FINAL : public Cat2Type { } private: - DoubleHiType(mirror::Class* klass, const StringPiece& descriptor, + DoubleHiType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : Cat2Type(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -872,7 +885,7 @@ class NullType FINAL : public RegType { static const NullType* GetInstance() PURE; // Create the singleton instance. - static const NullType* CreateInstance(mirror::Class* klass, + static const NullType* CreateInstance(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); @@ -892,7 +905,7 @@ class NullType FINAL : public RegType { } private: - NullType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) + NullType(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : RegType(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -906,8 +919,10 @@ class NullType FINAL : public RegType { // instructions and must be passed to a constructor. class UninitializedType : public RegType { public: - UninitializedType(mirror::Class* klass, const StringPiece& descriptor, - uint32_t allocation_pc, uint16_t cache_id) + UninitializedType(ObjPtr klass, + const StringPiece& descriptor, + uint32_t allocation_pc, + uint16_t cache_id) : RegType(klass, descriptor, cache_id), allocation_pc_(allocation_pc) {} bool IsUninitializedTypes() const OVERRIDE; @@ -929,9 +944,10 @@ class UninitializedType : public RegType { // Similar to ReferenceType but not yet having been passed to a constructor. class UninitializedReferenceType FINAL : public UninitializedType { public: - UninitializedReferenceType(mirror::Class* klass, + UninitializedReferenceType(ObjPtr klass, const StringPiece& descriptor, - uint32_t allocation_pc, uint16_t cache_id) + uint32_t allocation_pc, + uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : UninitializedType(klass, descriptor, allocation_pc, cache_id) { CheckConstructorInvariants(this); @@ -969,7 +985,7 @@ class UnresolvedUninitializedRefType FINAL : public UninitializedType { // of a constructor. class UninitializedThisReferenceType FINAL : public UninitializedType { public: - UninitializedThisReferenceType(mirror::Class* klass, + UninitializedThisReferenceType(ObjPtr klass, const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) @@ -1010,7 +1026,8 @@ class UnresolvedUninitializedThisRefType FINAL : public UninitializedType { // sub-class. class ReferenceType FINAL : public RegType { public: - ReferenceType(mirror::Class* klass, const StringPiece& descriptor, + ReferenceType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_) : RegType(klass, descriptor, cache_id) { CheckConstructorInvariants(this); @@ -1034,7 +1051,8 @@ class ReferenceType FINAL : public RegType { // type. class PreciseReferenceType FINAL : public RegType { public: - PreciseReferenceType(mirror::Class* klass, const StringPiece& descriptor, + PreciseReferenceType(ObjPtr klass, + const StringPiece& descriptor, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/verifier/reg_type_cache-inl.h b/runtime/verifier/reg_type_cache-inl.h index 0469a3b394..9f87adfa31 100644 --- a/runtime/verifier/reg_type_cache-inl.h +++ b/runtime/verifier/reg_type_cache-inl.h @@ -125,7 +125,7 @@ inline const ImpreciseConstType& RegTypeCache::PosShortConstant() { inline const PreciseReferenceType& RegTypeCache::JavaLangClass() { const RegType* result = &FromClass("Ljava/lang/Class;", - GetClassRoot().Ptr(), + GetClassRoot(), /* precise */ true); DCHECK(result->IsPreciseReference()); return *down_cast(result); @@ -134,7 +134,7 @@ inline const PreciseReferenceType& RegTypeCache::JavaLangClass() { inline const PreciseReferenceType& RegTypeCache::JavaLangString() { // String is final and therefore always precise. const RegType* result = &FromClass("Ljava/lang/String;", - GetClassRoot().Ptr(), + GetClassRoot(), /* precise */ true); DCHECK(result->IsPreciseReference()); return *down_cast(result); @@ -142,7 +142,7 @@ inline const PreciseReferenceType& RegTypeCache::JavaLangString() { inline const PreciseReferenceType& RegTypeCache::JavaLangInvokeMethodHandle() { const RegType* result = &FromClass("Ljava/lang/invoke/MethodHandle;", - GetClassRoot().Ptr(), + GetClassRoot(), /* precise */ true); DCHECK(result->IsPreciseReference()); return *down_cast(result); @@ -150,7 +150,7 @@ inline const PreciseReferenceType& RegTypeCache::JavaLangInvokeMethodHandle() { inline const PreciseReferenceType& RegTypeCache::JavaLangInvokeMethodType() { const RegType* result = &FromClass("Ljava/lang/invoke/MethodType;", - GetClassRoot().Ptr(), + GetClassRoot(), /* precise */ true); DCHECK(result->IsPreciseReference()); return *down_cast(result); @@ -158,7 +158,7 @@ inline const PreciseReferenceType& RegTypeCache::JavaLangInvokeMethodType() { inline const RegType& RegTypeCache::JavaLangThrowable(bool precise) { const RegType* result = &FromClass("Ljava/lang/Throwable;", - GetClassRoot().Ptr(), + GetClassRoot(), precise); if (precise) { DCHECK(result->IsPreciseReference()); @@ -170,9 +170,7 @@ inline const RegType& RegTypeCache::JavaLangThrowable(bool precise) { } inline const RegType& RegTypeCache::JavaLangObject(bool precise) { - const RegType* result = &FromClass("Ljava/lang/Object;", - GetClassRoot().Ptr(), - precise); + const RegType* result = &FromClass("Ljava/lang/Object;", GetClassRoot(), precise); if (precise) { DCHECK(result->IsPreciseReference()); return *down_cast(result); @@ -187,7 +185,7 @@ inline RegTypeType& RegTypeCache::AddEntry(RegTypeType* new_entry) { DCHECK(new_entry != nullptr); entries_.push_back(new_entry); if (new_entry->HasClass()) { - mirror::Class* klass = new_entry->GetClass(); + ObjPtr klass = new_entry->GetClass(); DCHECK(!klass->IsPrimitive()); klass_entries_.push_back(std::make_pair(GcRoot(klass), new_entry)); } diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index 87fc60bd23..f1f3488a3c 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -77,7 +77,7 @@ void RegTypeCache::FillPrimitiveAndSmallConstantTypes() { DCHECK_EQ(entries_.size(), primitive_count_); } -const RegType& RegTypeCache::FromDescriptor(mirror::ClassLoader* loader, +const RegType& RegTypeCache::FromDescriptor(ObjPtr loader, const char* descriptor, bool precise) { DCHECK(RegTypeCache::primitive_initialized_); @@ -149,14 +149,15 @@ bool RegTypeCache::MatchDescriptor(size_t idx, const StringPiece& descriptor, bo return true; } -mirror::Class* RegTypeCache::ResolveClass(const char* descriptor, mirror::ClassLoader* loader) { +ObjPtr RegTypeCache::ResolveClass(const char* descriptor, + ObjPtr loader) { // Class was not found, must create new type. // Try resolving class ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Thread* self = Thread::Current(); StackHandleScope<1> hs(self); Handle class_loader(hs.NewHandle(loader)); - mirror::Class* klass = nullptr; + ObjPtr klass = nullptr; if (can_load_classes_) { klass = class_linker->FindClass(self, descriptor, class_loader); } else { @@ -175,7 +176,7 @@ StringPiece RegTypeCache::AddString(const StringPiece& string_piece) { return StringPiece(ptr, string_piece.length()); } -const RegType& RegTypeCache::From(mirror::ClassLoader* loader, +const RegType& RegTypeCache::From(ObjPtr loader, const char* descriptor, bool precise) { StringPiece sp_descriptor(descriptor); @@ -188,7 +189,7 @@ const RegType& RegTypeCache::From(mirror::ClassLoader* loader, } // Class not found in the cache, will create a new type for that. // Try resolving class. - mirror::Class* klass = ResolveClass(descriptor, loader); + ObjPtr klass = ResolveClass(descriptor, loader); if (klass != nullptr) { // Class resolved, first look for the class in the list of entries // Class was not found, must create new type. @@ -234,7 +235,7 @@ const RegType& RegTypeCache::MakeUnresolvedReference() { return AddEntry(new (&allocator_) UnresolvedReferenceType(AddString("a"), entries_.size())); } -const RegType* RegTypeCache::FindClass(mirror::Class* klass, bool precise) const { +const RegType* RegTypeCache::FindClass(ObjPtr klass, bool precise) const { DCHECK(klass != nullptr); if (klass->IsPrimitive()) { // Note: precise isn't used for primitive classes. A char is assignable to an int. All @@ -242,7 +243,7 @@ const RegType* RegTypeCache::FindClass(mirror::Class* klass, bool precise) const return &RegTypeFromPrimitiveType(klass->GetPrimitiveType()); } for (auto& pair : klass_entries_) { - mirror::Class* const reg_klass = pair.first.Read(); + ObjPtr const reg_klass = pair.first.Read(); if (reg_klass == klass) { const RegType* reg_type = pair.second; if (MatchingPrecisionForClass(reg_type, precise)) { @@ -254,7 +255,7 @@ const RegType* RegTypeCache::FindClass(mirror::Class* klass, bool precise) const } const RegType* RegTypeCache::InsertClass(const StringPiece& descriptor, - mirror::Class* klass, + ObjPtr klass, bool precise) { // No reference to the class was found, create new reference. DCHECK(FindClass(klass, precise) == nullptr); @@ -265,7 +266,9 @@ const RegType* RegTypeCache::InsertClass(const StringPiece& descriptor, return &AddEntry(reg_type); } -const RegType& RegTypeCache::FromClass(const char* descriptor, mirror::Class* klass, bool precise) { +const RegType& RegTypeCache::FromClass(const char* descriptor, + ObjPtr klass, + bool precise) { DCHECK(klass != nullptr); const RegType* reg_type = FindClass(klass, precise); if (reg_type == nullptr) { @@ -342,7 +345,7 @@ void RegTypeCache::CreatePrimitiveAndSmallConstantTypes() { // code cannot leak to other users. auto create_primitive_type_instance = [&](auto type) REQUIRES_SHARED(Locks::mutator_lock_) { using Type = typename decltype(type)::type; - mirror::Class* klass = nullptr; + ObjPtr klass = nullptr; // Try loading the class from linker. DCHECK(type.descriptor != nullptr); if (strlen(type.descriptor) > 0) { @@ -500,7 +503,7 @@ const UninitializedType& RegTypeCache::Uninitialized(const RegType& type, uint32 allocation_pc, entries_.size()); } else { - mirror::Class* klass = type.GetClass(); + ObjPtr klass = type.GetClass(); for (size_t i = primitive_count_; i < entries_.size(); i++) { const RegType* cur_entry = entries_[i]; if (cur_entry->IsUninitializedReference() && @@ -532,7 +535,7 @@ const RegType& RegTypeCache::FromUninitialized(const RegType& uninit_type) { } entry = new (&allocator_) UnresolvedReferenceType(descriptor, entries_.size()); } else { - mirror::Class* klass = uninit_type.GetClass(); + ObjPtr klass = uninit_type.GetClass(); if (uninit_type.IsUninitializedThisReference() && !klass->IsFinal()) { // For uninitialized "this reference" look for reference types that are not precise. for (size_t i = primitive_count_; i < entries_.size(); i++) { @@ -583,7 +586,7 @@ const UninitializedType& RegTypeCache::UninitializedThisArgument(const RegType& } entry = new (&allocator_) UnresolvedUninitializedThisRefType(descriptor, entries_.size()); } else { - mirror::Class* klass = type.GetClass(); + ObjPtr klass = type.GetClass(); for (size_t i = primitive_count_; i < entries_.size(); i++) { const RegType* cur_entry = entries_[i]; if (cur_entry->IsUninitializedThisReference() && cur_entry->GetClass() == klass) { @@ -647,7 +650,8 @@ const ConstantType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) { return AddEntry(entry); } -const RegType& RegTypeCache::GetComponentType(const RegType& array, mirror::ClassLoader* loader) { +const RegType& RegTypeCache::GetComponentType(const RegType& array, + ObjPtr loader) { if (!array.IsArrayTypes()) { return Conflict(); } else if (array.IsUnresolvedTypes()) { @@ -655,7 +659,7 @@ const RegType& RegTypeCache::GetComponentType(const RegType& array, mirror::Clas const std::string descriptor(array.GetDescriptor().as_string()); return FromDescriptor(loader, descriptor.c_str() + 1, false); } else { - mirror::Class* klass = array.GetClass()->GetComponentType(); + ObjPtr klass = array.GetClass()->GetComponentType(); std::string temp; const char* descriptor = klass->GetDescriptor(&temp); if (klass->IsErroneous()) { diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h index b32dc115a7..d668222901 100644 --- a/runtime/verifier/reg_type_cache.h +++ b/runtime/verifier/reg_type_cache.h @@ -74,16 +74,18 @@ class RegTypeCache { } static void ShutDown(); const art::verifier::RegType& GetFromId(uint16_t id) const; - const RegType& From(mirror::ClassLoader* loader, const char* descriptor, bool precise) + const RegType& From(ObjPtr loader, const char* descriptor, bool precise) REQUIRES_SHARED(Locks::mutator_lock_); // Find a RegType, returns null if not found. - const RegType* FindClass(mirror::Class* klass, bool precise) const + const RegType* FindClass(ObjPtr klass, bool precise) const REQUIRES_SHARED(Locks::mutator_lock_); // Insert a new class with a specified descriptor, must not already be in the cache. - const RegType* InsertClass(const StringPiece& descriptor, mirror::Class* klass, bool precise) + const RegType* InsertClass(const StringPiece& descriptor, + ObjPtr klass, + bool precise) REQUIRES_SHARED(Locks::mutator_lock_); // Get or insert a reg type for a description, klass, and precision. - const RegType& FromClass(const char* descriptor, mirror::Class* klass, bool precise) + const RegType& FromClass(const char* descriptor, ObjPtr klass, bool precise) REQUIRES_SHARED(Locks::mutator_lock_); const ConstantType& FromCat1Const(int32_t value, bool precise) REQUIRES_SHARED(Locks::mutator_lock_); @@ -91,7 +93,9 @@ class RegTypeCache { REQUIRES_SHARED(Locks::mutator_lock_); const ConstantType& FromCat2ConstHi(int32_t value, bool precise) REQUIRES_SHARED(Locks::mutator_lock_); - const RegType& FromDescriptor(mirror::ClassLoader* loader, const char* descriptor, bool precise) + const RegType& FromDescriptor(ObjPtr loader, + const char* descriptor, + bool precise) REQUIRES_SHARED(Locks::mutator_lock_); const RegType& FromUnresolvedMerge(const RegType& left, const RegType& right, @@ -146,7 +150,7 @@ class RegTypeCache { const ImpreciseConstType& IntConstant() REQUIRES_SHARED(Locks::mutator_lock_); const ImpreciseConstType& PosByteConstant() REQUIRES_SHARED(Locks::mutator_lock_); const ImpreciseConstType& PosShortConstant() REQUIRES_SHARED(Locks::mutator_lock_); - const RegType& GetComponentType(const RegType& array, mirror::ClassLoader* loader) + const RegType& GetComponentType(const RegType& array, ObjPtr loader) REQUIRES_SHARED(Locks::mutator_lock_); void Dump(std::ostream& os) REQUIRES_SHARED(Locks::mutator_lock_); const RegType& RegTypeFromPrimitiveType(Primitive::Type) const; @@ -158,7 +162,7 @@ class RegTypeCache { private: void FillPrimitiveAndSmallConstantTypes() REQUIRES_SHARED(Locks::mutator_lock_); - mirror::Class* ResolveClass(const char* descriptor, mirror::ClassLoader* loader) + ObjPtr ResolveClass(const char* descriptor, ObjPtr loader) REQUIRES_SHARED(Locks::mutator_lock_); bool MatchDescriptor(size_t idx, const StringPiece& descriptor, bool precise) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc index fe839f7312..500cc37af4 100644 --- a/runtime/verifier/verifier_deps.cc +++ b/runtime/verifier/verifier_deps.cc @@ -77,8 +77,8 @@ const VerifierDeps::DexFileDeps* VerifierDeps::GetDexFileDeps(const DexFile& dex static constexpr uint32_t kAccVdexAccessFlags = kAccPublic | kAccPrivate | kAccProtected | kAccStatic | kAccInterface; -template -uint16_t VerifierDeps::GetAccessFlags(T* element) { +template +uint16_t VerifierDeps::GetAccessFlags(Ptr element) { static_assert(kAccJavaFlagsMask == 0xFFFF, "Unexpected value of a constant"); if (element == nullptr) { return VerifierDeps::kUnresolvedMarker; @@ -277,7 +277,7 @@ bool VerifierDeps::IsInClassPath(ObjPtr klass) const { void VerifierDeps::AddClassResolution(const DexFile& dex_file, dex::TypeIndex type_idx, - mirror::Class* klass) { + ObjPtr klass) { DexFileDeps* dex_deps = GetDexFileDeps(dex_file); if (dex_deps == nullptr) { // This invocation is from verification of a dex file which is not being compiled. @@ -336,12 +336,13 @@ void VerifierDeps::AddMethodResolution(const DexFile& dex_file, dex_deps->methods_.insert(method_tuple); } -mirror::Class* VerifierDeps::FindOneClassPathBoundaryForInterface(mirror::Class* destination, - mirror::Class* source) const { +ObjPtr VerifierDeps::FindOneClassPathBoundaryForInterface( + ObjPtr destination, + ObjPtr source) const { DCHECK(destination->IsInterface()); DCHECK(IsInClassPath(destination)); Thread* thread = Thread::Current(); - mirror::Class* current = source; + ObjPtr current = source; // Record the classes that are at the boundary between the compiled DEX files and // the classpath. We will check those classes later to find one class that inherits // `destination`. @@ -367,7 +368,7 @@ mirror::Class* VerifierDeps::FindOneClassPathBoundaryForInterface(mirror::Class* int32_t iftable_count = source->GetIfTableCount(); ObjPtr iftable = source->GetIfTable(); for (int32_t i = 0; i < iftable_count; ++i) { - mirror::Class* itf = iftable->GetInterface(i); + ObjPtr itf = iftable->GetInterface(i); if (!IsInClassPath(itf)) { for (size_t j = 0; j < itf->NumDirectInterfaces(); ++j) { ObjPtr direct = mirror::Class::GetDirectInterface(thread, itf, j); @@ -391,8 +392,8 @@ mirror::Class* VerifierDeps::FindOneClassPathBoundaryForInterface(mirror::Class* } void VerifierDeps::AddAssignability(const DexFile& dex_file, - mirror::Class* destination, - mirror::Class* source, + ObjPtr destination, + ObjPtr source, bool is_strict, bool is_assignable) { // Test that the method is only called on reference types. @@ -429,8 +430,8 @@ void VerifierDeps::AddAssignability(const DexFile& dex_file, // Both types are arrays. Break down to component types and add recursively. // This helps filter out destinations from compiled DEX files (see below) // and deduplicate entries with the same canonical component type. - mirror::Class* destination_component = destination->GetComponentType(); - mirror::Class* source_component = source->GetComponentType(); + ObjPtr destination_component = destination->GetComponentType(); + ObjPtr source_component = source->GetComponentType(); // Only perform the optimization if both types are resolved which guarantees // that they linked successfully, as required at the top of this method. @@ -511,7 +512,7 @@ void VerifierDeps::MaybeRecordVerificationStatus(const DexFile& dex_file, void VerifierDeps::MaybeRecordClassResolution(const DexFile& dex_file, dex::TypeIndex type_idx, - mirror::Class* klass) { + ObjPtr klass) { VerifierDeps* thread_deps = GetThreadLocalVerifierDeps(); if (thread_deps != nullptr) { thread_deps->AddClassResolution(dex_file, type_idx, klass); @@ -537,8 +538,8 @@ void VerifierDeps::MaybeRecordMethodResolution(const DexFile& dex_file, } void VerifierDeps::MaybeRecordAssignability(const DexFile& dex_file, - mirror::Class* destination, - mirror::Class* source, + ObjPtr destination, + ObjPtr source, bool is_strict, bool is_assignable) { VerifierDeps* thread_deps = GetThreadLocalVerifierDeps(); @@ -858,12 +859,12 @@ bool VerifierDeps::ValidateDependencies(Handle class_loader // TODO: share that helper with other parts of the compiler that have // the same lookup pattern. -static mirror::Class* FindClassAndClearException(ClassLinker* class_linker, - Thread* self, - const char* name, - Handle class_loader) +static ObjPtr FindClassAndClearException(ClassLinker* class_linker, + Thread* self, + const char* name, + Handle class_loader) REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::Class* result = class_linker->FindClass(self, name, class_loader); + ObjPtr result = class_linker->FindClass(self, name, class_loader); if (result == nullptr) { DCHECK(self->IsExceptionPending()); self->ClearException(); @@ -971,7 +972,7 @@ bool VerifierDeps::VerifyFields(Handle class_loader, std::string expected_decl_klass = entry.IsResolved() ? GetStringFromId(dex_file, entry.GetDeclaringClassIndex()) : dex_file.StringByTypeIdx(field_id.class_idx_); - mirror::Class* cls = FindClassAndClearException( + ObjPtr cls = FindClassAndClearException( class_linker, self, expected_decl_klass.c_str(), class_loader); if (cls == nullptr) { LOG(INFO) << "VerifierDeps: Could not resolve class " << expected_decl_klass; @@ -1034,7 +1035,7 @@ bool VerifierDeps::VerifyMethods(Handle class_loader, ? GetStringFromId(dex_file, entry.GetDeclaringClassIndex()) : dex_file.StringByTypeIdx(method_id.class_idx_); - mirror::Class* cls = FindClassAndClearException( + ObjPtr cls = FindClassAndClearException( class_linker, self, expected_decl_klass.c_str(), class_loader); if (cls == nullptr) { LOG(INFO) << "VerifierDeps: Could not resolve class " << expected_decl_klass; diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h index 94441da7e2..0146b17020 100644 --- a/runtime/verifier/verifier_deps.h +++ b/runtime/verifier/verifier_deps.h @@ -75,7 +75,7 @@ class VerifierDeps { // If `klass` is null, the class is assumed unresolved. static void MaybeRecordClassResolution(const DexFile& dex_file, dex::TypeIndex type_idx, - mirror::Class* klass) + ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::verifier_deps_lock_); @@ -99,8 +99,8 @@ class VerifierDeps { // to `destination` as defined by RegType::AssignableFrom. `dex_file` is the // owner of the method for which MethodVerifier performed the assignability test. static void MaybeRecordAssignability(const DexFile& dex_file, - mirror::Class* destination, - mirror::Class* source, + ObjPtr destination, + ObjPtr source, bool is_strict, bool is_assignable) REQUIRES_SHARED(Locks::mutator_lock_) @@ -218,8 +218,8 @@ class VerifierDeps { // Finds the class in the classpath that makes `source` inherit` from `destination`. // Returns null if a class defined in the compiled DEX files, and assignable to // `source`, direclty inherits from `destination`. - mirror::Class* FindOneClassPathBoundaryForInterface(mirror::Class* destination, - mirror::Class* source) const + ObjPtr FindOneClassPathBoundaryForInterface(ObjPtr destination, + ObjPtr source) const REQUIRES_SHARED(Locks::mutator_lock_); // Returns the index of `str`. If it is defined in `dex_file_`, this is the dex @@ -234,8 +234,8 @@ class VerifierDeps { // Returns the bytecode access flags of `element` (bottom 16 bits), or // `kUnresolvedMarker` if `element` is null. - template - static uint16_t GetAccessFlags(T* element) + template + static uint16_t GetAccessFlags(Ptr element) REQUIRES_SHARED(Locks::mutator_lock_); // Returns a string ID of the descriptor of the declaring class of `element`, @@ -256,7 +256,7 @@ class VerifierDeps { void AddClassResolution(const DexFile& dex_file, dex::TypeIndex type_idx, - mirror::Class* klass) + ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::verifier_deps_lock_); @@ -273,8 +273,8 @@ class VerifierDeps { REQUIRES(!Locks::verifier_deps_lock_); void AddAssignability(const DexFile& dex_file, - mirror::Class* destination, - mirror::Class* source, + ObjPtr destination, + ObjPtr source, bool is_strict, bool is_assignable) REQUIRES_SHARED(Locks::mutator_lock_); -- GitLab From 5924a4a73f1a2dcf83877062d67c297a9496b326 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 29 May 2018 17:40:41 +0100 Subject: [PATCH 490/749] Move String::SizeOf<> to string.h . string.h was already indirectly (through class.h) including everything needed for String::SizeOf<>, so move it to the string.h and make those includes direct. Then we can change the object-inl.h to include only string.h, reducing the number of indirectly included files. Test: m test-art-host-gtest Test: m testrunner.py --host --optimizing Change-Id: I51c462c034f205498c539abe6d888be9738d4a6e --- compiler/optimizing/intrinsics_arm_vixl.cc | 2 +- openjdkjvmti/ti_class.cc | 2 ++ openjdkjvmti/ti_class_loader.h | 33 ++++++------------- openjdkjvmti/ti_redefine.cc | 3 ++ openjdkjvmti/ti_redefine.h | 18 +--------- .../quick/quick_alloc_entrypoints.cc | 1 + runtime/mirror/class.cc | 1 + runtime/mirror/object-inl.h | 2 +- runtime/mirror/string-inl.h | 15 --------- runtime/mirror/string.h | 16 ++++++++- runtime/native/java_lang_StringFactory.cc | 2 +- runtime/native/java_lang_VMClassLoader.cc | 1 + 12 files changed, 37 insertions(+), 59 deletions(-) diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index 5287b4b2fa..fecf1ccbfa 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -25,7 +25,7 @@ #include "mirror/array-inl.h" #include "mirror/object_array-inl.h" #include "mirror/reference.h" -#include "mirror/string.h" +#include "mirror/string-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc index 726e47ed5f..16970921ab 100644 --- a/openjdkjvmti/ti_class.cc +++ b/openjdkjvmti/ti_class.cc @@ -71,9 +71,11 @@ #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" #include "thread_list.h" +#include "ti_class_definition.h" #include "ti_class_loader-inl.h" #include "ti_phase.h" #include "ti_redefine.h" +#include "transform.h" #include "well_known_classes.h" namespace openjdkjvmti { diff --git a/openjdkjvmti/ti_class_loader.h b/openjdkjvmti/ti_class_loader.h index a3857e595a..577c28585e 100644 --- a/openjdkjvmti/ti_class_loader.h +++ b/openjdkjvmti/ti_class_loader.h @@ -36,32 +36,19 @@ #include -#include "art_jvmti.h" -#include "art_method.h" -#include "base/array_slice.h" #include "base/globals.h" -#include "base/mem_map.h" -#include "class_linker.h" -#include "dex/dex_file.h" -#include "dex/utf.h" -#include "gc_root-inl.h" -#include "jni/jni_env_ext-inl.h" +#include "base/mutex.h" #include "jvmti.h" -#include "linear_alloc.h" -#include "mirror/array-inl.h" #include "mirror/array.h" -#include "mirror/class-inl.h" -#include "mirror/class.h" -#include "mirror/class_loader-inl.h" -#include "mirror/string-inl.h" -#include "oat_file.h" -#include "obj_ptr.h" -#include "scoped_thread_state_change-inl.h" -#include "stack.h" -#include "thread_list.h" -#include "ti_class_definition.h" -#include "transform.h" -#include "utils/dex_cache_arrays_layout-inl.h" + +namespace art { + +class DexFile; +template class Handle; +template class ObjPtr; +class Thread; + +} // namespace art namespace openjdkjvmti { diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index 48e2958773..73e37199ed 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -61,6 +61,7 @@ #include "jit/jit_code_cache.h" #include "jni/jni_env_ext-inl.h" #include "jvmti_allocator.h" +#include "linear_alloc.h" #include "mirror/class-inl.h" #include "mirror/class_ext.h" #include "mirror/object.h" @@ -68,6 +69,8 @@ #include "non_debuggable_classes.h" #include "object_lock.h" #include "runtime.h" +#include "stack.h" +#include "thread_list.h" #include "ti_breakpoint.h" #include "ti_class_loader.h" #include "transform.h" diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h index 227eacd180..e337491ae3 100644 --- a/openjdkjvmti/ti_redefine.h +++ b/openjdkjvmti/ti_redefine.h @@ -37,34 +37,18 @@ #include #include "art_jvmti.h" -#include "art_method.h" #include "base/array_ref.h" #include "base/globals.h" -#include "base/mem_map.h" -#include "class_linker.h" #include "dex/dex_file.h" -#include "dex/utf.h" -#include "gc_root-inl.h" #include "jni/jni_env_ext-inl.h" #include "jvmti.h" -#include "linear_alloc.h" -#include "mirror/array-inl.h" #include "mirror/array.h" -#include "mirror/class-inl.h" #include "mirror/class.h" -#include "mirror/class_loader-inl.h" -#include "mirror/string-inl.h" -#include "oat_file.h" #include "obj_ptr.h" -#include "scoped_thread_state_change-inl.h" -#include "stack.h" -#include "thread_list.h" -#include "ti_class_definition.h" -#include "transform.h" -#include "utils/dex_cache_arrays_layout-inl.h" namespace openjdkjvmti { +class ArtClassDefinition; class RedefinitionDataHolder; class RedefinitionDataIter; diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc index ed5885f224..5f7594c68d 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc @@ -25,6 +25,7 @@ #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" +#include "mirror/string-inl.h" namespace art { diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index ad6b37b86a..e6bfe5551a 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -39,6 +39,7 @@ #include "object-refvisitor-inl.h" #include "object_array-inl.h" #include "object_lock.h" +#include "string-inl.h" #include "runtime.h" #include "thread.h" #include "throwable.h" diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index c7561f4278..bfebd5d365 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -37,7 +37,7 @@ #include "read_barrier-inl.h" #include "reference.h" #include "runtime.h" -#include "string-inl.h" +#include "string.h" #include "throwable.h" namespace art { diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index a60861cc28..e6079c0cba 100644 --- a/runtime/mirror/string-inl.h +++ b/runtime/mirror/string-inl.h @@ -194,21 +194,6 @@ int32_t String::FastIndexOf(MemoryType* chars, int32_t ch, int32_t start) { return -1; } -template -inline size_t String::SizeOf() { - size_t size = sizeof(String); - if (IsCompressed()) { - size += (sizeof(uint8_t) * GetLength()); - } else { - size += (sizeof(uint16_t) * GetLength()); - } - // String.equals() intrinsics assume zero-padding up to kObjectAlignment, - // so make sure the zero-padding is actually copied around if GC compaction - // chooses to copy only SizeOf() bytes. - // http://b/23528461 - return RoundUp(size, kObjectAlignment); -} - template inline String* String::Alloc(Thread* self, int32_t utf16_length_with_flag, gc::AllocatorType allocator_type, diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index c45dc499e5..270ace1036 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -17,6 +17,8 @@ #ifndef ART_RUNTIME_MIRROR_STRING_H_ #define ART_RUNTIME_MIRROR_STRING_H_ +#include "base/bit_utils.h" +#include "base/globals.h" #include "gc/allocator_type.h" #include "gc_root-inl.h" #include "class.h" @@ -66,7 +68,19 @@ class MANAGED String FINAL : public Object { } template - size_t SizeOf() REQUIRES_SHARED(Locks::mutator_lock_); + size_t SizeOf() REQUIRES_SHARED(Locks::mutator_lock_) { + size_t size = sizeof(String); + if (IsCompressed()) { + size += (sizeof(uint8_t) * GetLength()); + } else { + size += (sizeof(uint16_t) * GetLength()); + } + // String.equals() intrinsics assume zero-padding up to kObjectAlignment, + // so make sure the zero-padding is actually copied around if GC compaction + // chooses to copy only SizeOf() bytes. + // http://b/23528461 + return RoundUp(size, kObjectAlignment); + } // Taking out the first/uppermost bit because it is not part of actual length value template diff --git a/runtime/native/java_lang_StringFactory.cc b/runtime/native/java_lang_StringFactory.cc index 07e875efcb..3978ca8a36 100644 --- a/runtime/native/java_lang_StringFactory.cc +++ b/runtime/native/java_lang_StringFactory.cc @@ -19,7 +19,7 @@ #include "common_throws.h" #include "jni/jni_internal.h" #include "mirror/object-inl.h" -#include "mirror/string.h" +#include "mirror/string-inl.h" #include "native_util.h" #include "nativehelper/jni_macros.h" #include "nativehelper/scoped_local_ref.h" diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc index 0630737d29..b1511c0d88 100644 --- a/runtime/native/java_lang_VMClassLoader.cc +++ b/runtime/native/java_lang_VMClassLoader.cc @@ -20,6 +20,7 @@ #include "class_linker.h" #include "dex/descriptors_names.h" #include "dex/dex_file_loader.h" +#include "dex/utf.h" #include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" -- GitLab From 8fb6784a56a9df03d115ef47c62ca55b433d00cc Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 23 May 2018 11:22:30 +0100 Subject: [PATCH 491/749] Add sdk destinations for appcompat.sh related files. bug: 79936439 Test: m Change-Id: I9a41a8f2c5b0bd253e38f955204dc3f91956cc28 --- tools/veridex/Android.bp | 7 ++++++- tools/veridex/Android.mk | 31 +++++++++++++++++++++++++++---- tools/veridex/appcompat.sh | 27 ++++++++++++++++++++++----- 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/tools/veridex/Android.bp b/tools/veridex/Android.bp index 5186c43ca2..96d4a094b5 100644 --- a/tools/veridex/Android.bp +++ b/tools/veridex/Android.bp @@ -24,11 +24,16 @@ cc_binary { "veridex.cc", ], cflags: ["-Wall", "-Werror"], - shared_libs: [ + static_libs: [ "libdexfile", "libartbase", "libbase", + "liblog", + "libutils", + "libz", + "libziparchive", ], + stl: "libc++_static", header_libs: [ "art_libartbase_headers", ], diff --git a/tools/veridex/Android.mk b/tools/veridex/Android.mk index 51d924a3c1..83fa0d6397 100644 --- a/tools/veridex/Android.mk +++ b/tools/veridex/Android.mk @@ -16,6 +16,9 @@ LOCAL_PATH := $(call my-dir) +# The veridex tool takes stub dex files as input, so we generate both the system and oahl +# dex stubs. + system_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/core_dex_intermediates/classes.dex $(system_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000 $(system_stub_dex): $(call resolve-prebuilt-sdk-jar-path,system_current) | $(ZIP2ZIP) $(DX) @@ -27,9 +30,29 @@ $(oahl_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000 $(oahl_stub_dex): $(call get-prebuilt-sdk-dir,current)/org.apache.http.legacy.jar | $(ZIP2ZIP) $(DX) $(transform-classes-d8.jar-to-dex) +app_compat_lists := \ + $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \ + $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \ + $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) + +# Phony rule to create all dependencies of the appcompat.sh script. .PHONY: appcompat +appcompat: $(system_stub_dex) $(oahl_stub_dex) $(HOST_OUT_EXECUTABLES)/veridex $(app_compat_lists) + +VERIDEX_FILES_PATH := \ + $(call intermediates-dir-for,PACKAGING,veridex,HOST)/veridex.zip + +VERIDEX_FILES := $(LOCAL_PATH)/appcompat.sh + +$(VERIDEX_FILES_PATH): PRIVATE_VERIDEX_FILES := $(VERIDEX_FILES) +$(VERIDEX_FILES_PATH): PRIVATE_APP_COMPAT_LISTS := $(app_compat_lists) +$(VERIDEX_FILES_PATH) : $(SOONG_ZIP) $(VERIDEX_FILES) $(app_compat_lists) $(HOST_OUT_EXECUTABLES)/veridex + $(hide) $(SOONG_ZIP) -o $@ -C art/tools/veridex -f $(PRIVATE_VERIDEX_FILES) \ + -C $(dir $(lastword $(PRIVATE_APP_COMPAT_LISTS))) $(addprefix -f , $(PRIVATE_APP_COMPAT_LISTS)) \ + -C $(HOST_OUT_EXECUTABLES) -f $(HOST_OUT_EXECUTABLES)/veridex + +# Make the zip file available for prebuilts. +$(call dist-for-goals,sdk,$(VERIDEX_FILES_PATH)) -appcompat: $(system_stub_dex) $(oahl_stub_dex) $(HOST_OUT_EXECUTABLES)/veridex \ - ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-light-greylist.txt \ - ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-dark-greylist.txt \ - ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-blacklist.txt +VERIDEX_FILES := +app_compat_lists := diff --git a/tools/veridex/appcompat.sh b/tools/veridex/appcompat.sh index 31a8654b58..c07ab21a4b 100755 --- a/tools/veridex/appcompat.sh +++ b/tools/veridex/appcompat.sh @@ -14,7 +14,28 @@ # See the License for the specific language governing permissions and # limitations under the License. -# We want to be at the root for simplifying the "out" detection +echo "NOTE: appcompat.sh is still under development. It can report" +echo "API uses that do not execute at runtime, and reflection uses" +echo "that do not exist. It can also miss on reflection uses." + +# First check if the script is invoked from a prebuilts location. +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +if [[ -e ${SCRIPT_DIR}/veridex && \ + -e ${SCRIPT_DIR}/hiddenapi-blacklist.txt && \ + -e ${SCRIPT_DIR}/hiddenapi-light-greylist.txt && \ + -e ${SCRIPT_DIR}/hiddenapi-dark-greylist.txt && \ + -e ${SCRIPT_DIR}/org.apache.http.legacy-stubs.dex && \ + -e ${SCRIPT_DIR}/system-stubs.dex ]]; then + exec ${SCRIPT_DIR}/veridex \ + --core-stubs=${SCRIPT_DIR}/system-stubs.dex:${SCRIPT_DIR}/org.apache.http.legacy-stubs.dex \ + --blacklist=${SCRIPT_DIR}/hiddenapi-blacklist.txt \ + --light-greylist=${SCRIPT_DIR}/hiddenapi-light-greylist.txt \ + --dark-greylist=${SCRIPT_DIR}/hiddenapi-dark-greylist.txt \ + $@ +fi + +# Otherwise, we want to be at the root for simplifying the "out" detection # logic. if [ ! -d art ]; then echo "Script needs to be run at the root of the android tree." @@ -38,10 +59,6 @@ if [ -z "$ANDROID_HOST_OUT" ] ; then ANDROID_HOST_OUT=${OUT}/host/linux-x86 fi -echo "NOTE: appcompat.sh is still under development. It can report" -echo "API uses that do not execute at runtime, and reflection uses" -echo "that do not exist. It can also miss on reflection uses." - ${ANDROID_HOST_OUT}/bin/veridex \ --core-stubs=${PACKAGING}/core_dex_intermediates/classes.dex:${PACKAGING}/oahl_dex_intermediates/classes.dex \ -- GitLab From fc625cd64442cf8e586e65d8e5f0aff62472ac5b Mon Sep 17 00:00:00 2001 From: Tamas Kenez Date: Tue, 15 May 2018 13:49:00 +0200 Subject: [PATCH 492/749] ART-tests: remove DX-dependency from 166-bad-interface-super This tests is creating an illegal class file which compiles with DX but D8 rejects it. This CL, in addition to enabling D8, adds the precompiled, smali versions of the jasmin sources. The build script will use either the smali or the jasmin sources depending on the --host/--target/--jvm flag. Bug: 65168732 Test: art/test.py --host --gcstress -r -t 166-bad-interface-super art/test.py --target --gcstress -r -t 166-bad-interface-super Change-Id: I58e69fa9a4aa08946b7e57001f5e2a2a2bc07b0f --- test/166-bad-interface-super/build | 13 ++++++++++--- .../smali/BadSuper1.smali | 17 +++++++++++++++++ .../smali/BadSuper2.smali | 17 +++++++++++++++++ test/etc/default-build | 3 +++ 4 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 test/166-bad-interface-super/smali/BadSuper1.smali create mode 100644 test/166-bad-interface-super/smali/BadSuper2.smali diff --git a/test/166-bad-interface-super/build b/test/166-bad-interface-super/build index d85147f17b..bba6184e16 100644 --- a/test/166-bad-interface-super/build +++ b/test/166-bad-interface-super/build @@ -14,7 +14,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -# See b/65168732 -export USE_D8=false +# Use the jasmin sources for JVM, otherwise the smali sources. +extra_arg="--no-jasmin" -./default-build "$@" +for arg in "$@"; do + if [[ "$arg" == "--jvm" ]]; then + extra_arg="--no-smali" + break + fi +done + +./default-build "$@" "$extra_arg" diff --git a/test/166-bad-interface-super/smali/BadSuper1.smali b/test/166-bad-interface-super/smali/BadSuper1.smali new file mode 100644 index 0000000000..6233403897 --- /dev/null +++ b/test/166-bad-interface-super/smali/BadSuper1.smali @@ -0,0 +1,17 @@ +# 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. + +.class public interface LBadSuper1; +.super LBaseInterface; +.source "BadSuper1.j" diff --git a/test/166-bad-interface-super/smali/BadSuper2.smali b/test/166-bad-interface-super/smali/BadSuper2.smali new file mode 100644 index 0000000000..8e410cf1b4 --- /dev/null +++ b/test/166-bad-interface-super/smali/BadSuper2.smali @@ -0,0 +1,17 @@ +# 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. + +.class public interface LBadSuper2; +.super LBaseClass; +.source "BadSuper2.j" diff --git a/test/etc/default-build b/test/etc/default-build index d0ebe80e68..9dbc73c6b4 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -169,6 +169,9 @@ while true; do elif [ "x$1" = "x--no-smali" ]; then HAS_SMALI=false shift + elif [ "x$1" = "x--no-jasmin" ]; then + HAS_JASMIN=false + shift elif [ "x$1" = "x--experimental" ]; then shift # We have a specific experimental configuration so don't use the default. -- GitLab From 4b59d107f91601c4e0095d7a9db40970d4ed6956 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Tue, 29 May 2018 21:46:10 +0000 Subject: [PATCH 493/749] Revert^2 "Optimize register mask and stack mask in stack maps." This reverts commit 8b20b5c1f5b454b2f8b8bff492c88724b5002600. Reason for revert: Retry submit unmodified after fixing the test. Use BitTable to store the masks as well and move the deduplication responsibility to the BitTable builders. Don't generate entries for masks which are all zeros. This saves 0.2% of .oat file size on both Arm64 and Arm. Encode registers as (value+shift) due to tailing zeros. This saves 1.0% of .oat file size on Arm64 and 0.2% on Arm. Test: test-art-target-gtest-exception_test Test: test-art-host-gtest-bit_table_test Test: test-art-host-gtest-stack_map_test Change-Id: Ib643776dbec3f051cc29cd13ff39e453fab5fae9 --- compiler/optimizing/stack_map_stream.cc | 101 +++++++----------------- compiler/optimizing/stack_map_stream.h | 12 --- compiler/optimizing/stack_map_test.cc | 4 +- oatdump/oatdump.cc | 2 +- runtime/oat.h | 4 +- runtime/quick_exception_handler.cc | 2 +- runtime/stack_map.cc | 2 +- runtime/stack_map.h | 39 ++++++--- runtime/thread.cc | 3 +- 9 files changed, 63 insertions(+), 106 deletions(-) diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index c6e375a1b2..b40ea3768a 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -48,10 +48,6 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream); current_entry_.dex_register_entry.live_dex_registers_mask->ClearAllBits(); } - if (sp_mask != nullptr) { - stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet()); - } - current_dex_register_ = 0; } @@ -217,11 +213,32 @@ size_t StackMapStream::PrepareForFillIn() { PrepareMethodIndices(); // Dedup stack masks. Needs to be done first as it modifies the stack map entry. - size_t stack_mask_bits = stack_mask_max_ + 1; // Need room for max element too. - size_t num_stack_masks = PrepareStackMasks(stack_mask_bits); + BitmapTableBuilder stack_mask_builder(allocator_); + for (StackMapEntry& stack_map : stack_maps_) { + BitVector* mask = stack_map.sp_mask; + size_t num_bits = (mask != nullptr) ? mask->GetNumberOfBits() : 0; + if (num_bits != 0) { + stack_map.stack_mask_index = stack_mask_builder.Dedup(mask->GetRawStorage(), num_bits); + } else { + stack_map.stack_mask_index = StackMap::kNoValue; + } + } // Dedup register masks. Needs to be done first as it modifies the stack map entry. - size_t num_register_masks = PrepareRegisterMasks(); + BitTableBuilder> register_mask_builder(allocator_); + for (StackMapEntry& stack_map : stack_maps_) { + uint32_t register_mask = stack_map.register_mask; + if (register_mask != 0) { + uint32_t shift = LeastSignificantBit(register_mask); + std::array entry = { + register_mask >> shift, + shift, + }; + stack_map.register_mask_index = register_mask_builder.Dedup(&entry); + } else { + stack_map.register_mask_index = StackMap::kNoValue; + } + } // Write dex register maps. MemoryRegion dex_register_map_region = @@ -301,31 +318,8 @@ size_t StackMapStream::PrepareForFillIn() { stack_map_builder.Encode(&out_, &bit_offset); invoke_info_builder.Encode(&out_, &bit_offset); inline_info_builder.Encode(&out_, &bit_offset); - - // Write register masks table. - BitTableBuilder register_mask_builder(allocator_); - for (size_t i = 0; i < num_register_masks; ++i) { - register_mask_builder.Add(register_masks_[i]); - } register_mask_builder.Encode(&out_, &bit_offset); - - // Write stack masks table. - EncodeVarintBits(&out_, &bit_offset, stack_mask_bits); - out_.resize(BitsToBytesRoundUp(bit_offset + stack_mask_bits * num_stack_masks)); - BitMemoryRegion stack_mask_region(MemoryRegion(out_.data(), out_.size()), - bit_offset, - stack_mask_bits * num_stack_masks); - if (stack_mask_bits > 0) { - for (size_t i = 0; i < num_stack_masks; ++i) { - size_t stack_mask_bytes = BitsToBytesRoundUp(stack_mask_bits); - BitMemoryRegion src(MemoryRegion(&stack_masks_[i * stack_mask_bytes], stack_mask_bytes)); - BitMemoryRegion dst = stack_mask_region.Subregion(i * stack_mask_bits, stack_mask_bits); - for (size_t bit_index = 0; bit_index < stack_mask_bits; bit_index += BitSizeOf()) { - size_t num_bits = std::min(stack_mask_bits - bit_index, BitSizeOf()); - dst.StoreBits(bit_index, src.LoadBits(bit_index, num_bits), num_bits); - } - } - } + stack_mask_builder.Encode(&out_, &bit_offset); return UnsignedLeb128Size(out_.size()) + out_.size(); } @@ -448,17 +442,6 @@ void StackMapStream::CheckDexRegisterMap(const CodeInfo& code_info, } } -size_t StackMapStream::PrepareRegisterMasks() { - register_masks_.resize(stack_maps_.size(), 0u); - ScopedArenaUnorderedMap dedupe(allocator_->Adapter(kArenaAllocStackMapStream)); - for (StackMapEntry& stack_map : stack_maps_) { - const size_t index = dedupe.size(); - stack_map.register_mask_index = dedupe.emplace(stack_map.register_mask, index).first->second; - register_masks_[index] = stack_map.register_mask; - } - return dedupe.size(); -} - void StackMapStream::PrepareMethodIndices() { CHECK(method_indices_.empty()); method_indices_.resize(stack_maps_.size() + inline_infos_.size()); @@ -481,35 +464,10 @@ void StackMapStream::PrepareMethodIndices() { method_indices_.resize(dedupe.size()); } - -size_t StackMapStream::PrepareStackMasks(size_t entry_size_in_bits) { - // Preallocate memory since we do not want it to move (the dedup map will point into it). - const size_t byte_entry_size = RoundUp(entry_size_in_bits, kBitsPerByte) / kBitsPerByte; - stack_masks_.resize(byte_entry_size * stack_maps_.size(), 0u); - // For deduplicating we store the stack masks as byte packed for simplicity. We can bit pack later - // when copying out from stack_masks_. - ScopedArenaUnorderedMap, - MemoryRegion::ContentEquals> dedup( - stack_maps_.size(), allocator_->Adapter(kArenaAllocStackMapStream)); - for (StackMapEntry& stack_map : stack_maps_) { - size_t index = dedup.size(); - MemoryRegion stack_mask(stack_masks_.data() + index * byte_entry_size, byte_entry_size); - BitMemoryRegion stack_mask_bits(stack_mask); - for (size_t i = 0; i < entry_size_in_bits; i++) { - stack_mask_bits.StoreBit(i, stack_map.sp_mask != nullptr && stack_map.sp_mask->IsBitSet(i)); - } - stack_map.stack_mask_index = dedup.emplace(stack_mask, index).first->second; - } - return dedup.size(); -} - // Check that all StackMapStream inputs are correctly encoded by trying to read them back. void StackMapStream::CheckCodeInfo(MemoryRegion region) const { CodeInfo code_info(region); DCHECK_EQ(code_info.GetNumberOfStackMaps(), stack_maps_.size()); - DCHECK_EQ(code_info.GetNumberOfStackMaskBits(), static_cast(stack_mask_max_ + 1)); DCHECK_EQ(code_info.GetNumberOfLocationCatalogEntries(), location_catalog_entries_.size()); size_t invoke_info_index = 0; for (size_t s = 0; s < stack_maps_.size(); ++s) { @@ -522,18 +480,15 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { DCHECK_EQ(stack_map.GetDexPc(), entry.dex_pc); DCHECK_EQ(stack_map.GetRegisterMaskIndex(), entry.register_mask_index); DCHECK_EQ(code_info.GetRegisterMaskOf(stack_map), entry.register_mask); - const size_t num_stack_mask_bits = code_info.GetNumberOfStackMaskBits(); DCHECK_EQ(stack_map.GetStackMaskIndex(), entry.stack_mask_index); BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map); if (entry.sp_mask != nullptr) { DCHECK_GE(stack_mask.size_in_bits(), entry.sp_mask->GetNumberOfBits()); - for (size_t b = 0; b < num_stack_mask_bits; b++) { - DCHECK_EQ(stack_mask.LoadBit(b), entry.sp_mask->IsBitSet(b)); + for (size_t b = 0; b < stack_mask.size_in_bits(); b++) { + DCHECK_EQ(stack_mask.LoadBit(b), entry.sp_mask->IsBitSet(b)) << b; } } else { - for (size_t b = 0; b < num_stack_mask_bits; b++) { - DCHECK_EQ(stack_mask.LoadBit(b), 0u); - } + DCHECK_EQ(stack_mask.size_in_bits(), 0u); } if (entry.dex_method_index != dex::kDexNoIndex) { InvokeInfo invoke_info = code_info.GetInvokeInfo(invoke_info_index); diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index ea97cf6530..19863d882a 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -68,11 +68,8 @@ class StackMapStream : public ValueObject { location_catalog_entries_indices_(allocator->Adapter(kArenaAllocStackMapStream)), dex_register_locations_(allocator->Adapter(kArenaAllocStackMapStream)), inline_infos_(allocator->Adapter(kArenaAllocStackMapStream)), - stack_masks_(allocator->Adapter(kArenaAllocStackMapStream)), - register_masks_(allocator->Adapter(kArenaAllocStackMapStream)), method_indices_(allocator->Adapter(kArenaAllocStackMapStream)), dex_register_entries_(allocator->Adapter(kArenaAllocStackMapStream)), - stack_mask_max_(-1), out_(allocator->Adapter(kArenaAllocStackMapStream)), dex_map_hash_to_stack_map_indices_(std::less(), allocator->Adapter(kArenaAllocStackMapStream)), @@ -171,12 +168,6 @@ class StackMapStream : public ValueObject { private: size_t ComputeDexRegisterLocationCatalogSize() const; - // Returns the number of unique stack masks. - size_t PrepareStackMasks(size_t entry_size_in_bits); - - // Returns the number of unique register masks. - size_t PrepareRegisterMasks(); - // Prepare and deduplicate method indices. void PrepareMethodIndices(); @@ -217,11 +208,8 @@ class StackMapStream : public ValueObject { // A set of concatenated maps of Dex register locations indices to `location_catalog_entries_`. ScopedArenaVector dex_register_locations_; ScopedArenaVector inline_infos_; - ScopedArenaVector stack_masks_; - ScopedArenaVector register_masks_; ScopedArenaVector method_indices_; ScopedArenaVector dex_register_entries_; - int stack_mask_max_; ScopedArenaVector out_; diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index 9db7588b3a..c372bb9b22 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -32,10 +32,10 @@ static bool CheckStackMask( const StackMap& stack_map, const BitVector& bit_vector) { BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map); - if (bit_vector.GetNumberOfBits() > code_info.GetNumberOfStackMaskBits()) { + if (bit_vector.GetNumberOfBits() > stack_mask.size_in_bits()) { return false; } - for (size_t i = 0; i < code_info.GetNumberOfStackMaskBits(); ++i) { + for (size_t i = 0; i < stack_mask.size_in_bits(); ++i) { if (stack_mask.LoadBit(i) != bit_vector.IsBitSet(i)) { return false; } diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index fcd6bfd46c..b080f92689 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1731,7 +1731,7 @@ class OatDumper { // Stack masks stats_.AddBits( Stats::kByteKindCodeInfoStackMasks, - code_info.stack_masks_.size_in_bits()); + code_info.stack_masks_.DataBitSize()); // Register masks stats_.AddBits( diff --git a/runtime/oat.h b/runtime/oat.h index 7b8f71a3f3..8069a15661 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: Refactor stackmap encoding. - static constexpr uint8_t kOatVersion[] = { '1', '4', '4', '\0' }; + // Last oat version changed reason: Optimize masks in stack maps. + static constexpr uint8_t kOatVersion[] = { '1', '4', '5', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index de613d3b20..26489209b8 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -439,7 +439,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { const uint8_t* addr = reinterpret_cast(GetCurrentQuickFrame()) + offset; value = *reinterpret_cast(addr); uint32_t bit = (offset >> 2); - if (bit < code_info.GetNumberOfStackMaskBits() && stack_mask.LoadBit(bit)) { + if (bit < stack_mask.size_in_bits() && stack_mask.LoadBit(bit)) { is_reference = true; } break; diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index 2b7e8dd748..fd0e28d9ac 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -200,7 +200,7 @@ void StackMap::Dump(VariableIndentationOutputStream* vios, << std::dec << ", stack_mask=0b"; BitMemoryRegion stack_mask = code_info.GetStackMaskOf(*this); - for (size_t i = 0, e = code_info.GetNumberOfStackMaskBits(); i < e; ++i) { + for (size_t i = 0, e = stack_mask.size_in_bits(); i < e; ++i) { vios->Stream() << stack_mask.LoadBit(e - i - 1); } vios->Stream() << ")\n"; diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 91cecf0690..1cb9a399f8 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -799,6 +799,24 @@ class InvokeInfo : public BitTable<3>::Accessor { } }; +// Register masks tend to have many trailing zero bits (caller-saves are usually not encoded), +// therefore it is worth encoding the mask as value+shift. +class RegisterMask : public BitTable<2>::Accessor { + public: + enum Field { + kValue, + kShift, + kCount, + }; + + RegisterMask(const BitTable* table, uint32_t row) + : BitTable::Accessor(table, row) {} + + ALWAYS_INLINE uint32_t GetMask() const { + return Get() << Get(); + } +}; + /** * Wrapper around all compiler information collected for a method. * The information is of the form: @@ -833,24 +851,22 @@ class CodeInfo { return DexRegisterLocationCatalog(location_catalog_); } - ALWAYS_INLINE size_t GetNumberOfStackMaskBits() const { - return stack_mask_bits_; - } - ALWAYS_INLINE StackMap GetStackMapAt(size_t index) const { return StackMap(&stack_maps_, index); } BitMemoryRegion GetStackMask(size_t index) const { - return stack_masks_.Subregion(index * stack_mask_bits_, stack_mask_bits_); + return stack_masks_.GetBitMemoryRegion(index); } BitMemoryRegion GetStackMaskOf(const StackMap& stack_map) const { - return GetStackMask(stack_map.GetStackMaskIndex()); + uint32_t index = stack_map.GetStackMaskIndex(); + return (index == StackMap::kNoValue) ? BitMemoryRegion() : GetStackMask(index); } uint32_t GetRegisterMaskOf(const StackMap& stack_map) const { - return register_masks_.Get(stack_map.GetRegisterMaskIndex()); + uint32_t index = stack_map.GetRegisterMaskIndex(); + return (index == StackMap::kNoValue) ? 0 : RegisterMask(®ister_masks_, index).GetMask(); } uint32_t GetNumberOfLocationCatalogEntries() const { @@ -1045,8 +1061,8 @@ class CodeInfo { invoke_infos_.Decode(bit_region, &bit_offset); inline_infos_.Decode(bit_region, &bit_offset); register_masks_.Decode(bit_region, &bit_offset); - stack_mask_bits_ = DecodeVarintBits(bit_region, &bit_offset); - stack_masks_ = bit_region.Subregion(bit_offset, non_header_size * kBitsPerByte - bit_offset); + stack_masks_.Decode(bit_region, &bit_offset); + CHECK_EQ(BitsToBytesRoundUp(bit_offset), non_header_size); } size_t size_; @@ -1056,9 +1072,8 @@ class CodeInfo { BitTable stack_maps_; BitTable invoke_infos_; BitTable inline_infos_; - BitTable<1> register_masks_; - uint32_t stack_mask_bits_ = 0; - BitMemoryRegion stack_masks_; + BitTable register_masks_; + BitTable<1> stack_masks_; friend class OatDumper; }; diff --git a/runtime/thread.cc b/runtime/thread.cc index 129bae6d9a..2e737f5258 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -3568,9 +3568,8 @@ class ReferenceMapVisitor : public StackVisitor { T vreg_info(m, code_info, map, visitor_); // Visit stack entries that hold pointers. - const size_t number_of_bits = code_info.GetNumberOfStackMaskBits(); BitMemoryRegion stack_mask = code_info.GetStackMaskOf(map); - for (size_t i = 0; i < number_of_bits; ++i) { + for (size_t i = 0; i < stack_mask.size_in_bits(); ++i) { if (stack_mask.LoadBit(i)) { StackReference* ref_addr = vreg_base + i; mirror::Object* ref = ref_addr->AsMirrorPtr(); -- GitLab From acb906d7f907b79ef1e6038d7104a5569e81a1ac Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 30 May 2018 10:23:49 +0100 Subject: [PATCH 494/749] Remove mirror::String::java_lang_String_. And simplify ClassLinker::InitWithoutImage(). And finish ObjPtr<>-ification of annotation processing. Test: m test-art-host-gtest Test: testrunner.py --host Bug: 31113334 Change-Id: I882a6c2f2b2a88d6ba34e4759bac4a6caa54cafa --- openjdkjvmti/ti_class.cc | 2 +- openjdkjvmti/ti_field.cc | 2 +- openjdkjvmti/ti_method.cc | 2 +- runtime/arch/stub_test.cc | 3 +- runtime/class_linker.cc | 49 ++----------- runtime/class_linker.h | 4 -- runtime/dex/dex_file_annotations.cc | 68 ++++++++----------- runtime/dex/dex_file_annotations.h | 18 ++--- runtime/interpreter/interpreter_common.h | 3 +- runtime/interpreter/unstarted_runtime_test.cc | 8 +-- runtime/mirror/class.cc | 4 +- runtime/mirror/string-inl.h | 6 +- runtime/mirror/string.cc | 19 ------ runtime/mirror/string.h | 12 ---- runtime/mirror/var_handle_test.cc | 28 ++++---- runtime/runtime.cc | 1 - runtime/verifier/reg_type.cc | 1 + 17 files changed, 75 insertions(+), 155 deletions(-) diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc index 16970921ab..9bea18a763 100644 --- a/openjdkjvmti/ti_class.cc +++ b/openjdkjvmti/ti_class.cc @@ -715,7 +715,7 @@ jvmtiError ClassUtil::GetClassSignature(jvmtiEnv* env, if (!klass->IsProxyClass() && klass->GetDexCache() != nullptr) { art::StackHandleScope<1> hs(soa.Self()); art::Handle h_klass = hs.NewHandle(klass); - art::mirror::ObjectArray* str_array = + art::ObjPtr> str_array = art::annotations::GetSignatureAnnotationForClass(h_klass); if (str_array != nullptr) { std::ostringstream oss; diff --git a/openjdkjvmti/ti_field.cc b/openjdkjvmti/ti_field.cc index 328e2a1e40..2a860d9f43 100644 --- a/openjdkjvmti/ti_field.cc +++ b/openjdkjvmti/ti_field.cc @@ -91,7 +91,7 @@ jvmtiError FieldUtil::GetFieldName(jvmtiEnv* env, if (generic_ptr != nullptr) { *generic_ptr = nullptr; if (!art_field->GetDeclaringClass()->IsProxyClass()) { - art::mirror::ObjectArray* str_array = + art::ObjPtr> str_array = art::annotations::GetSignatureAnnotationForField(art_field); if (str_array != nullptr) { std::ostringstream oss; diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index c0c312c490..d0b7224f93 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -345,7 +345,7 @@ jvmtiError MethodUtil::GetMethodName(jvmtiEnv* env, if (generic_ptr != nullptr) { *generic_ptr = nullptr; if (!art_method->GetDeclaringClass()->IsProxyClass()) { - art::mirror::ObjectArray* str_array = + art::ObjPtr> str_array = art::annotations::GetSignatureAnnotationForMethod(art_method); if (str_array != nullptr) { std::ostringstream oss; diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index 78516e3aeb..b0c0e43e35 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -21,6 +21,7 @@ #include "base/callee_save_type.h" #include "base/enums.h" #include "class_linker-inl.h" +#include "class_root.h" #include "common_runtime_test.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "imt_conflict_table.h" @@ -2096,7 +2097,7 @@ TEST_F(StubTest, ReadBarrierForRoot) { EXPECT_FALSE(self->IsExceptionPending()); - GcRoot& root = mirror::String::java_lang_String_; + GcRoot root(GetClassRoot()); size_t result = Invoke3(reinterpret_cast(&root), 0U, 0U, readBarrierForRootSlow, self); EXPECT_FALSE(self->IsExceptionPending()); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index b882f65a9e..095272394a 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -484,27 +484,10 @@ bool ClassLinker::InitWithoutImage(std::vector> b mirror::ObjectArray::ClassSize(image_pointer_size_)))); object_array_class->SetComponentType(java_lang_Object.Get()); - // Setup the char (primitive) class to be used for char[]. - Handle char_class(hs.NewHandle( - AllocClass(self, java_lang_Class.Get(), - mirror::Class::PrimitiveClassSize(image_pointer_size_)))); - // The primitive char class won't be initialized by - // InitializePrimitiveClass until line 459, but strings (and - // internal char arrays) will be allocated before that and the - // component size, which is computed from the primitive type, needs - // to be set here. - char_class->SetPrimitiveType(Primitive::kPrimChar); - - // Setup the char[] class to be used for String. - Handle char_array_class(hs.NewHandle( - AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_)))); - char_array_class->SetComponentType(char_class.Get()); - // Setup String. Handle java_lang_String(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::String::ClassSize(image_pointer_size_)))); java_lang_String->SetStringClass(); - mirror::String::SetClass(java_lang_String.Get()); mirror::Class::SetStatus(java_lang_String, ClassStatus::kResolved, self); // Setup java.lang.ref.Reference. @@ -523,7 +506,6 @@ bool ClassLinker::InitWithoutImage(std::vector> b SetClassRoot(ClassRoot::kJavaLangObject, java_lang_Object.Get()); SetClassRoot(ClassRoot::kClassArrayClass, class_array_class.Get()); SetClassRoot(ClassRoot::kObjectArrayClass, object_array_class.Get()); - SetClassRoot(ClassRoot::kCharArrayClass, char_array_class.Get()); SetClassRoot(ClassRoot::kJavaLangString, java_lang_String.Get()); SetClassRoot(ClassRoot::kJavaLangRefReference, java_lang_ref_Reference.Get()); @@ -533,6 +515,7 @@ bool ClassLinker::InitWithoutImage(std::vector> b // Setup the primitive type classes. SetClassRoot(ClassRoot::kPrimitiveBoolean, CreatePrimitiveClass(self, Primitive::kPrimBoolean)); SetClassRoot(ClassRoot::kPrimitiveByte, CreatePrimitiveClass(self, Primitive::kPrimByte)); + SetClassRoot(ClassRoot::kPrimitiveChar, CreatePrimitiveClass(self, Primitive::kPrimChar)); SetClassRoot(ClassRoot::kPrimitiveShort, CreatePrimitiveClass(self, Primitive::kPrimShort)); SetClassRoot(ClassRoot::kPrimitiveInt, CreatePrimitiveClass(self, Primitive::kPrimInt)); SetClassRoot(ClassRoot::kPrimitiveLong, CreatePrimitiveClass(self, Primitive::kPrimLong)); @@ -543,13 +526,13 @@ bool ClassLinker::InitWithoutImage(std::vector> b // Create array interface entries to populate once we can load system classes. array_iftable_ = GcRoot(AllocIfTable(self, 2)); - // Create int array type for AllocDexCache (done in AppendToBootClassPath). + // Create int array type for native pointer arrays (for example vtables) on 32-bit archs. Handle int_array_class(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_)))); int_array_class->SetComponentType(GetClassRoot(ClassRoot::kPrimitiveInt, this)); SetClassRoot(ClassRoot::kIntArrayClass, int_array_class.Get()); - // Create long array type for AllocDexCache (done in AppendToBootClassPath). + // Create long array type for native pointer arrays (for example vtables) on 64-bit archs. Handle long_array_class(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_)))); long_array_class->SetComponentType(GetClassRoot(ClassRoot::kPrimitiveLong, this)); @@ -604,10 +587,6 @@ bool ClassLinker::InitWithoutImage(std::vector> b // now we can use FindSystemClass - // run char class through InitializePrimitiveClass to finish init - InitializePrimitiveClass(char_class.Get(), Primitive::kPrimChar); - SetClassRoot(ClassRoot::kPrimitiveChar, char_class.Get()); // needs descriptor - // Set up GenericJNI entrypoint. That is mainly a hack for common_compiler_test.h so that // we do not need friend classes or a publicly exposed setter. quick_generic_jni_trampoline_ = GetQuickGenericJniStub(); @@ -636,7 +615,7 @@ bool ClassLinker::InitWithoutImage(std::vector> b SetClassRoot(ClassRoot::kByteArrayClass, FindSystemClass(self, "[B")); - CheckSystemClass(self, char_array_class, "[C"); + SetClassRoot(ClassRoot::kCharArrayClass, FindSystemClass(self, "[C")); SetClassRoot(ClassRoot::kShortArrayClass, FindSystemClass(self, "[S")); @@ -685,7 +664,7 @@ bool ClassLinker::InitWithoutImage(std::vector> b FindSystemClass(self, "Ljava/lang/reflect/Proxy;")); // Create java.lang.reflect.Field.class root. - auto* class_root = FindSystemClass(self, "Ljava/lang/reflect/Field;"); + ObjPtr class_root = FindSystemClass(self, "Ljava/lang/reflect/Field;"); CHECK(class_root != nullptr); SetClassRoot(ClassRoot::kJavaLangReflectField, class_root); @@ -1014,10 +993,6 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots))); mirror::Class::SetClassClass(GetClassRoot(ClassRoot::kJavaLangClass, this)); - // Special case of setting up the String class early so that we can test arbitrary objects - // as being Strings or not - mirror::String::SetClass(GetClassRoot(this)); - ObjPtr java_lang_Object = GetClassRoot(this); java_lang_Object->SetObjectSize(sizeof(mirror::Object)); // Allocate in non-movable so that it's possible to check if a JNI weak global ref has been @@ -2109,7 +2084,6 @@ void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor) { ClassLinker::~ClassLinker() { mirror::Class::ResetClass(); - mirror::String::ResetClass(); Thread* const self = Thread::Current(); for (const ClassLoaderData& data : class_loaders_) { // CHA unloading analysis is not needed. No negative consequences are expected because @@ -3561,20 +3535,13 @@ ClassLinker::DexCacheData ClassLinker::FindDexCacheDataLocked(const DexFile& dex } mirror::Class* ClassLinker::CreatePrimitiveClass(Thread* self, Primitive::Type type) { - ObjPtr klass = + ObjPtr primitive_class = AllocClass(self, mirror::Class::PrimitiveClassSize(image_pointer_size_)); - if (UNLIKELY(klass == nullptr)) { + if (UNLIKELY(primitive_class == nullptr)) { self->AssertPendingOOMException(); return nullptr; } - return InitializePrimitiveClass(klass, type); -} - -mirror::Class* ClassLinker::InitializePrimitiveClass(ObjPtr primitive_class, - Primitive::Type type) { - CHECK(primitive_class != nullptr); // Must hold lock on object when initializing. - Thread* self = Thread::Current(); StackHandleScope<1> hs(self); Handle h_class(hs.NewHandle(primitive_class)); ObjectLock lock(self, h_class); @@ -3668,8 +3635,6 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto new_class.Assign(GetClassRoot>(this)); } else if (strcmp(descriptor, "[Ljava/lang/String;") == 0) { new_class.Assign(GetClassRoot>(this)); - } else if (strcmp(descriptor, "[C") == 0) { - new_class.Assign(GetClassRoot(this)); } else if (strcmp(descriptor, "[I") == 0) { new_class.Assign(GetClassRoot(this)); } else if (strcmp(descriptor, "[J") == 0) { diff --git a/runtime/class_linker.h b/runtime/class_linker.h index afe5c99990..1f94c43408 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -793,10 +793,6 @@ class ClassLinker { mirror::Class* CreatePrimitiveClass(Thread* self, Primitive::Type type) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - mirror::Class* InitializePrimitiveClass(ObjPtr primitive_class, - Primitive::Type type) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Roles::uninterruptible_); mirror::Class* CreateArrayClass(Thread* self, const char* descriptor, diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc index ffa0a9065a..5cb08dc278 100644 --- a/runtime/dex/dex_file_annotations.cc +++ b/runtime/dex/dex_file_annotations.cc @@ -849,7 +849,8 @@ ObjPtr GetAnnotationValue(const ClassData& klass, return annotation_value.value_.GetL(); } -mirror::ObjectArray* GetSignatureValue(const ClassData& klass, +static ObjPtr> GetSignatureValue( + const ClassData& klass, const DexFile::AnnotationSetItem* annotation_set) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile& dex_file = klass.GetDexFile(); @@ -860,12 +861,9 @@ mirror::ObjectArray* GetSignatureValue(const ClassData& klass, if (annotation_item == nullptr) { return nullptr; } - ObjPtr string_class = mirror::String::GetJavaLangString(); - Handle string_array_class(hs.NewHandle( - Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &string_class))); - if (string_array_class == nullptr) { - return nullptr; - } + Handle string_array_class = + hs.NewHandle(GetClassRoot>()); + DCHECK(string_array_class != nullptr); ObjPtr obj = GetAnnotationValue(klass, annotation_item, "value", string_array_class, DexFile::kDexAnnotationArray); @@ -880,19 +878,16 @@ ObjPtr> GetThrowsValue( const DexFile::AnnotationSetItem* annotation_set) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile& dex_file = klass.GetDexFile(); - StackHandleScope<1> hs(Thread::Current()); const DexFile::AnnotationItem* annotation_item = SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/Throws;", DexFile::kDexVisibilitySystem); if (annotation_item == nullptr) { return nullptr; } - ObjPtr class_class = mirror::Class::GetJavaLangClass(); - Handle class_array_class(hs.NewHandle( - Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &class_class))); - if (class_array_class == nullptr) { - return nullptr; - } + StackHandleScope<1> hs(Thread::Current()); + Handle class_array_class = + hs.NewHandle(GetClassRoot>()); + DCHECK(class_array_class != nullptr); ObjPtr obj = GetAnnotationValue(klass, annotation_item, "value", class_array_class, DexFile::kDexAnnotationArray); @@ -1020,7 +1015,7 @@ ObjPtr> GetAnnotationsForField(ArtField* fie return ProcessAnnotationSet(field_class, annotation_set, DexFile::kDexVisibilityRuntime); } -mirror::ObjectArray* GetSignatureAnnotationForField(ArtField* field) { +ObjPtr> GetSignatureAnnotationForField(ArtField* field) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForField(field); if (annotation_set == nullptr) { return nullptr; @@ -1171,9 +1166,10 @@ ObjPtr GetAnnotationForMethodParameter(ArtMethod* method, annotation_class); } -bool GetParametersMetadataForMethod(ArtMethod* method, - MutableHandle>* names, - MutableHandle* access_flags) { +bool GetParametersMetadataForMethod( + ArtMethod* method, + /*out*/ MutableHandle>* names, + /*out*/ MutableHandle* access_flags) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method); if (annotation_set == nullptr) { @@ -1193,12 +1189,10 @@ bool GetParametersMetadataForMethod(ArtMethod* method, StackHandleScope<4> hs(Thread::Current()); // Extract the parameters' names String[]. - ObjPtr string_class = mirror::String::GetJavaLangString(); - Handle string_array_class(hs.NewHandle( - Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &string_class))); - if (UNLIKELY(string_array_class == nullptr)) { - return false; - } + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + Handle string_array_class = + hs.NewHandle(GetClassRoot>(class_linker)); + DCHECK(string_array_class != nullptr); ClassData data(method); Handle names_obj = @@ -1212,10 +1206,8 @@ bool GetParametersMetadataForMethod(ArtMethod* method, } // Extract the parameters' access flags int[]. - Handle int_array_class(hs.NewHandle(GetClassRoot())); - if (UNLIKELY(int_array_class == nullptr)) { - return false; - } + Handle int_array_class(hs.NewHandle(GetClassRoot(class_linker))); + DCHECK(int_array_class != nullptr); Handle access_flags_obj = hs.NewHandle(GetAnnotationValue(data, annotation_item, @@ -1226,12 +1218,12 @@ bool GetParametersMetadataForMethod(ArtMethod* method, return false; } - names->Assign(names_obj.Get()->AsObjectArray()); - access_flags->Assign(access_flags_obj.Get()->AsIntArray()); + names->Assign(names_obj->AsObjectArray()); + access_flags->Assign(access_flags_obj->AsIntArray()); return true; } -mirror::ObjectArray* GetSignatureAnnotationForMethod(ArtMethod* method) { +ObjPtr> GetSignatureAnnotationForMethod(ArtMethod* method) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method); if (annotation_set == nullptr) { return nullptr; @@ -1345,12 +1337,9 @@ ObjPtr> GetDeclaredClasses(Handle hs(Thread::Current()); - ObjPtr class_class = mirror::Class::GetJavaLangClass(); - Handle class_array_class(hs.NewHandle( - Runtime::Current()->GetClassLinker()->FindArrayClass(hs.Self(), &class_class))); - if (class_array_class == nullptr) { - return nullptr; - } + Handle class_array_class = + hs.NewHandle(GetClassRoot>()); + DCHECK(class_array_class != nullptr); ObjPtr obj = GetAnnotationValue(data, annotation_item, "value", class_array_class, DexFile::kDexAnnotationArray); @@ -1446,7 +1435,7 @@ ObjPtr GetEnclosingMethod(Handle klass) { DexFile::kDexAnnotationMethod); } -bool GetInnerClass(Handle klass, ObjPtr* name) { +bool GetInnerClass(Handle klass, /*out*/ ObjPtr* name) { ClassData data(klass); const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { @@ -1513,7 +1502,8 @@ bool GetInnerClassFlags(Handle klass, uint32_t* flags) { return true; } -mirror::ObjectArray* GetSignatureAnnotationForClass(Handle klass) { +ObjPtr> GetSignatureAnnotationForClass( + Handle klass) { ClassData data(klass); const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data); if (annotation_set == nullptr) { diff --git a/runtime/dex/dex_file_annotations.h b/runtime/dex/dex_file_annotations.h index 9645a7febd..bde7891091 100644 --- a/runtime/dex/dex_file_annotations.h +++ b/runtime/dex/dex_file_annotations.h @@ -41,7 +41,7 @@ ObjPtr GetAnnotationForField(ArtField* field, REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr> GetAnnotationsForField(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::ObjectArray* GetSignatureAnnotationForField(ArtField* field) +ObjPtr> GetSignatureAnnotationForField(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_); bool IsFieldAnnotationPresent(ArtField* field, Handle annotation_class) REQUIRES_SHARED(Locks::mutator_lock_); @@ -64,11 +64,11 @@ ObjPtr GetAnnotationForMethodParameter(ArtMethod* method, uint32_t parameter_idx, Handle annotation_class) REQUIRES_SHARED(Locks::mutator_lock_); -bool GetParametersMetadataForMethod(ArtMethod* method, - MutableHandle>* names, - MutableHandle* access_flags) - REQUIRES_SHARED(Locks::mutator_lock_); -mirror::ObjectArray* GetSignatureAnnotationForMethod(ArtMethod* method) +bool GetParametersMetadataForMethod( + ArtMethod* method, + /*out*/ MutableHandle>* names, + /*out*/ MutableHandle* access_flags) REQUIRES_SHARED(Locks::mutator_lock_); +ObjPtr> GetSignatureAnnotationForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); // Check whether `method` is annotated with `annotation_class`. // If `lookup_in_resolved_boot_classes` is true, look up any of the @@ -101,12 +101,12 @@ ObjPtr GetEnclosingClass(Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr GetEnclosingMethod(Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); -bool GetInnerClass(Handle klass, ObjPtr* name) +bool GetInnerClass(Handle klass, /*out*/ ObjPtr* name) REQUIRES_SHARED(Locks::mutator_lock_); bool GetInnerClassFlags(Handle klass, uint32_t* flags) REQUIRES_SHARED(Locks::mutator_lock_); -mirror::ObjectArray* GetSignatureAnnotationForClass(Handle klass) - REQUIRES_SHARED(Locks::mutator_lock_); +ObjPtr> GetSignatureAnnotationForClass( + Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); const char* GetSourceDebugExtension(Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); bool IsClassAnnotationPresent(Handle klass, diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 37234e1462..0ee780d32d 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -35,6 +35,7 @@ #include "base/macros.h" #include "base/mutex.h" #include "class_linker-inl.h" +#include "class_root.h" #include "common_dex_operations.h" #include "common_throws.h" #include "dex/dex_file-inl.h" @@ -328,7 +329,7 @@ static inline ObjPtr ResolveString(Thread* self, ShadowFrame& shadow_frame, dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr java_lang_string_class = mirror::String::GetJavaLangString(); + ObjPtr java_lang_string_class = GetClassRoot(); if (UNLIKELY(!java_lang_string_class->IsInitialized())) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); StackHandleScope<1> hs(self); diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc index 9bb760c6b7..88cfafba47 100644 --- a/runtime/interpreter/unstarted_runtime_test.cc +++ b/runtime/interpreter/unstarted_runtime_test.cc @@ -388,7 +388,7 @@ TEST_F(UnstartedRuntimeTest, StringCharAt) { TEST_F(UnstartedRuntimeTest, StringInit) { Thread* self = Thread::Current(); ScopedObjectAccess soa(self); - ObjPtr klass = mirror::String::GetJavaLangString(); + ObjPtr klass = GetClassRoot(); ArtMethod* method = klass->FindConstructor("(Ljava/lang/String;)V", Runtime::Current()->GetClassLinker()->GetImagePointerSize()); @@ -537,7 +537,7 @@ TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTest) { tmp, false, object_class.Get(), - mirror::String::GetJavaLangString(), + GetClassRoot(), hs_src, 1, hs_dst, @@ -551,7 +551,7 @@ TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTest) { { StackHandleScope<3> hs_src(self); hs_src.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "1")); - hs_src.NewHandle(mirror::String::GetJavaLangString()); + hs_src.NewHandle(GetClassRoot()); hs_src.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "3")); StackHandleScope<3> hs_dst(self); @@ -568,7 +568,7 @@ TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTest) { tmp, true, object_class.Get(), - mirror::String::GetJavaLangString(), + GetClassRoot(), hs_src, 0, hs_dst, diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index e6bfe5551a..cb2708d0cb 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -1461,12 +1461,12 @@ template void Class::GetAccessFlagsDCheck() { // circularity issue during loading the names of its members DCHECK(IsIdxLoaded() || IsRetired() || IsErroneous(kVerifyFlags & ~kVerifyThis)>() || - this == String::GetJavaLangString()) + this == GetClassRoot()) << "IsIdxLoaded=" << IsIdxLoaded() << " IsRetired=" << IsRetired() << " IsErroneous=" << IsErroneous(kVerifyFlags & ~kVerifyThis)>() - << " IsString=" << (this == String::GetJavaLangString()) + << " IsString=" << (this == GetClassRoot()) << " status= " << GetStatus() << " descriptor=" << PrettyDescriptor(); } diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index e6079c0cba..8fa2c6cf7f 100644 --- a/runtime/mirror/string-inl.h +++ b/runtime/mirror/string-inl.h @@ -25,6 +25,7 @@ #include "base/globals.h" #include "base/utils.h" #include "class.h" +#include "class_root.h" #include "common_throws.h" #include "dex/utf.h" #include "gc/heap-inl.h" @@ -211,7 +212,8 @@ inline String* String::Alloc(Thread* self, int32_t utf16_length_with_flag, // http://b/23528461 size_t alloc_size = RoundUp(size, kObjectAlignment); - Class* string_class = GetJavaLangString(); + Runtime* runtime = Runtime::Current(); + ObjPtr string_class = GetClassRoot(runtime->GetClassLinker()); // Check for overflow and throw OutOfMemoryError if this was an unreasonable request. // Do this by comparing with the maximum length that will _not_ cause an overflow. const size_t overflow_length = (-header_size) / block_size; // Unsigned arithmetic. @@ -227,7 +229,7 @@ inline String* String::Alloc(Thread* self, int32_t utf16_length_with_flag, return nullptr; } - gc::Heap* heap = Runtime::Current()->GetHeap(); + gc::Heap* heap = runtime->GetHeap(); return down_cast( heap->AllocObjectWithAllocator(self, string_class, alloc_size, allocator_type, pre_fence_visitor)); diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc index 6208a962e5..b76ca1968a 100644 --- a/runtime/mirror/string.cc +++ b/runtime/mirror/string.cc @@ -35,9 +35,6 @@ namespace art { namespace mirror { -// TODO: get global references for these -GcRoot String::java_lang_String_; - int32_t String::FastIndexOf(int32_t ch, int32_t start) { int32_t count = GetLength(); if (start < 0) { @@ -52,18 +49,6 @@ int32_t String::FastIndexOf(int32_t ch, int32_t start) { } } -void String::SetClass(ObjPtr java_lang_String) { - CHECK(java_lang_String_.IsNull()); - CHECK(java_lang_String != nullptr); - CHECK(java_lang_String->IsStringClass()); - java_lang_String_ = GcRoot(java_lang_String); -} - -void String::ResetClass() { - CHECK(!java_lang_String_.IsNull()); - java_lang_String_ = GcRoot(nullptr); -} - int String::ComputeHashCode() { int32_t hash_code = 0; if (IsCompressed()) { @@ -372,10 +357,6 @@ int32_t String::CompareTo(ObjPtr rhs) { return count_diff; } -void String::VisitRoots(RootVisitor* visitor) { - java_lang_String_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - CharArray* String::ToCharArray(Thread* self) { StackHandleScope<1> hs(self); Handle string(hs.NewHandle(this)); diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index 270ace1036..598175b749 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -228,15 +228,6 @@ class MANAGED String FINAL : public Object { : length; } - static Class* GetJavaLangString() REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(!java_lang_String_.IsNull()); - return java_lang_String_.Read(); - } - - static void SetClass(ObjPtr java_lang_String) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - // Returns a human-readable equivalent of 'descriptor'. So "I" would be "int", // "[[I" would be "int[][]", "[Ljava/lang/String;" would be // "java.lang.String[]", and so forth. @@ -281,10 +272,7 @@ class MANAGED String FINAL : public Object { uint8_t value_compressed_[0]; }; - static GcRoot java_lang_String_; - friend struct art::StringOffsets; // for verifying offset information - ART_FRIEND_TEST(art::StubTest, ReadBarrierForRoot); // For java_lang_String_. DISALLOW_IMPLICIT_CONSTRUCTORS(String); }; diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc index 2c1283225d..cb2d628b5b 100644 --- a/runtime/mirror/var_handle_test.cc +++ b/runtime/mirror/var_handle_test.cc @@ -92,8 +92,7 @@ class VarHandleTest : public CommonRuntimeTest { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle var_type = hs.NewHandle(view_array_class->GetComponentType()); Handle index_type = hs.NewHandle(class_linker->FindPrimitiveClass('I')); - ObjPtr byte_class = class_linker->FindPrimitiveClass('B'); - Handle byte_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &byte_class))); + Handle byte_array_class(hs.NewHandle(GetClassRoot())); InitializeVarHandle(bvh.Get(), var_type, byte_array_class, index_type, access_modes_bit_mask); bvh->SetFieldBoolean(ByteArrayViewVarHandle::NativeByteOrderOffset(), native_byte_order); return bvh.Get(); @@ -234,8 +233,7 @@ static MethodType* MethodTypeOf(const std::string& method_descriptor) { ScopedObjectAccess soa(self); StackHandleScope<3> hs(self); int ptypes_count = static_cast(descriptors.size()) - 1; - ObjPtr class_type = mirror::Class::GetJavaLangClass(); - ObjPtr array_of_class = class_linker->FindArrayClass(self, &class_type); + ObjPtr array_of_class = GetClassRoot>(); Handle> ptypes = hs.NewHandle( ObjectArray::Alloc(Thread::Current(), array_of_class, ptypes_count)); Handle boot_class_loader = hs.NewHandle(nullptr); @@ -599,10 +597,10 @@ TEST_F(VarHandleTest, ArrayElementVarHandle) { VarHandle::AccessMode::kGetAndBitwiseXorRelease, VarHandle::AccessMode::kGetAndBitwiseXorAcquire); - ObjPtr string_class = mirror::String::GetJavaLangString(); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - Handle string_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &string_class))); - Handle vh(hs.NewHandle(CreateArrayElementVarHandle(self, string_array_class, mask))); + Handle string_array_class = hs.NewHandle( + GetClassRoot>()); + Handle vh( + hs.NewHandle(CreateArrayElementVarHandle(self, string_array_class, mask))); EXPECT_FALSE(vh.IsNull()); // Check access modes @@ -746,11 +744,10 @@ TEST_F(VarHandleTest, ByteArrayViewVarHandle) { VarHandle::AccessMode::kGetAndBitwiseXor, VarHandle::AccessMode::kGetAndBitwiseXorAcquire); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ObjPtr char_class = class_linker->FindPrimitiveClass('C'); - Handle char_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &char_class))); + Handle char_array_class(hs.NewHandle(GetClassRoot())); const bool native_byte_order = true; - Handle vh(hs.NewHandle(CreateByteArrayViewVarHandle(self, char_array_class, native_byte_order, mask))); + Handle vh( + hs.NewHandle(CreateByteArrayViewVarHandle(self, char_array_class, native_byte_order, mask))); EXPECT_FALSE(vh.IsNull()); EXPECT_EQ(native_byte_order, vh->GetNativeByteOrder()); @@ -895,11 +892,10 @@ TEST_F(VarHandleTest, ByteBufferViewVarHandle) { VarHandle::AccessMode::kGetAndBitwiseXor, VarHandle::AccessMode::kGetAndBitwiseXorAcquire); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ObjPtr double_class = class_linker->FindPrimitiveClass('D'); - Handle double_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &double_class))); + Handle double_array_class(hs.NewHandle(GetClassRoot())); const bool native_byte_order = false; - Handle vh(hs.NewHandle(CreateByteBufferViewVarHandle(self, double_array_class, native_byte_order, mask))); + Handle vh(hs.NewHandle( + CreateByteBufferViewVarHandle(self, double_array_class, native_byte_order, mask))); EXPECT_FALSE(vh.IsNull()); EXPECT_EQ(native_byte_order, vh->GetNativeByteOrder()); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 32d9d68d0d..6384d01aaf 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1979,7 +1979,6 @@ void Runtime::VisitConstantRoots(RootVisitor* visitor) { // Visit the classes held as static in mirror classes, these can be visited concurrently and only // need to be visited once per GC since they never change. mirror::Class::VisitRoots(visitor); - mirror::String::VisitRoots(visitor); mirror::ClassExt::VisitRoots(visitor); // Visiting the roots of these ArtMethods is not currently required since all the GcRoots are // null. diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index cbfbb4000a..73e516c7bf 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -723,6 +723,7 @@ const RegType& RegType::Merge(const RegType& incoming_type, // mechanics to continue. return reg_types->FromUnresolvedMerge(*this, incoming_type, verifier); } else { // Two reference types, compute Join + // Do not cache the classes as ClassJoin() can suspend and invalidate ObjPtr<>s. DCHECK(GetClass() != nullptr && !GetClass()->IsPrimitive()); DCHECK(incoming_type.GetClass() != nullptr && !incoming_type.GetClass()->IsPrimitive()); ObjPtr join_class = ClassJoin(GetClass(), incoming_type.GetClass()); -- GitLab From d02b23f7ee9664213216a82bfdcb0ee83824de04 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Tue, 29 May 2018 23:27:22 +0100 Subject: [PATCH 495/749] Remove the CodeOffset helper class. I need to reduce the StackMapEntry to a POD type so that it can be used in BitTableBuilder. Test: test-art-host-gtest-stack_map_test Change-Id: I5f9ad7fdc9c9405f22669a11aea14f925ef06ef7 --- compiler/optimizing/code_generator.cc | 7 +- compiler/optimizing/code_generator_mips.cc | 3 +- compiler/optimizing/code_generator_mips64.cc | 3 +- compiler/optimizing/stack_map_stream.cc | 18 ++-- compiler/optimizing/stack_map_stream.h | 12 +-- compiler/optimizing/stack_map_test.cc | 45 +++++----- oatdump/oatdump.cc | 2 +- runtime/arch/code_offset.h | 94 -------------------- runtime/stack_map.cc | 2 +- runtime/stack_map.h | 22 +++-- 10 files changed, 63 insertions(+), 145 deletions(-) delete mode 100644 runtime/arch/code_offset.h diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index de1be5b871..b358bfabe0 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -1161,8 +1161,8 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, // last emitted is different than the native pc of the stack map just emitted. size_t number_of_stack_maps = stack_map_stream->GetNumberOfStackMaps(); if (number_of_stack_maps > 1) { - DCHECK_NE(stack_map_stream->GetStackMap(number_of_stack_maps - 1).native_pc_code_offset, - stack_map_stream->GetStackMap(number_of_stack_maps - 2).native_pc_code_offset); + DCHECK_NE(stack_map_stream->GetStackMapNativePcOffset(number_of_stack_maps - 1), + stack_map_stream->GetStackMapNativePcOffset(number_of_stack_maps - 2)); } } } @@ -1174,8 +1174,7 @@ bool CodeGenerator::HasStackMapAtCurrentPc() { if (count == 0) { return false; } - CodeOffset native_pc_offset = stack_map_stream->GetStackMap(count - 1).native_pc_code_offset; - return (native_pc_offset.Uint32Value(GetInstructionSet()) == pc); + return stack_map_stream->GetStackMapNativePcOffset(count - 1) == pc; } void CodeGenerator::MaybeRecordNativeDebugInfo(HInstruction* instruction, diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 7f3441fdf4..8be84a15bd 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -1042,8 +1042,7 @@ void CodeGeneratorMIPS::Finalize(CodeAllocator* allocator) { // Adjust native pc offsets in stack maps. StackMapStream* stack_map_stream = GetStackMapStream(); for (size_t i = 0, num = stack_map_stream->GetNumberOfStackMaps(); i != num; ++i) { - uint32_t old_position = - stack_map_stream->GetStackMap(i).native_pc_code_offset.Uint32Value(InstructionSet::kMips); + uint32_t old_position = stack_map_stream->GetStackMapNativePcOffset(i); uint32_t new_position = __ GetAdjustedPosition(old_position); DCHECK_GE(new_position, old_position); stack_map_stream->SetStackMapNativePcOffset(i, new_position); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index ee32b96daf..cd9e0e521e 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -988,8 +988,7 @@ void CodeGeneratorMIPS64::Finalize(CodeAllocator* allocator) { // Adjust native pc offsets in stack maps. StackMapStream* stack_map_stream = GetStackMapStream(); for (size_t i = 0, num = stack_map_stream->GetNumberOfStackMaps(); i != num; ++i) { - uint32_t old_position = - stack_map_stream->GetStackMap(i).native_pc_code_offset.Uint32Value(InstructionSet::kMips64); + uint32_t old_position = stack_map_stream->GetStackMapNativePcOffset(i); uint32_t new_position = __ GetAdjustedPosition(old_position); DCHECK_GE(new_position, old_position); stack_map_stream->SetStackMapNativePcOffset(i, new_position); diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index b40ea3768a..5dc2acd512 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -25,6 +25,14 @@ namespace art { +uint32_t StackMapStream::GetStackMapNativePcOffset(size_t i) { + return StackMap::UnpackNativePc(stack_maps_[i].packed_native_pc, instruction_set_); +} + +void StackMapStream::SetStackMapNativePcOffset(size_t i, uint32_t native_pc_offset) { + stack_maps_[i].packed_native_pc = StackMap::PackNativePc(native_pc_offset, instruction_set_); +} + void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, uint32_t native_pc_offset, uint32_t register_mask, @@ -33,7 +41,7 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, uint8_t inlining_depth) { DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry"; current_entry_.dex_pc = dex_pc; - current_entry_.native_pc_code_offset = CodeOffset::FromOffset(native_pc_offset, instruction_set_); + current_entry_.packed_native_pc = StackMap::PackNativePc(native_pc_offset, instruction_set_); current_entry_.register_mask = register_mask; current_entry_.sp_mask = sp_mask; current_entry_.inlining_depth = inlining_depth; @@ -278,7 +286,7 @@ size_t StackMapStream::PrepareForFillIn() { for (const StackMapEntry& entry : stack_maps_) { if (entry.dex_method_index != dex::kDexNoIndex) { std::array invoke_info_entry { - entry.native_pc_code_offset.CompressedValue(), + entry.packed_native_pc, entry.invoke_type, entry.dex_method_index_idx }; @@ -306,7 +314,7 @@ size_t StackMapStream::PrepareForFillIn() { inline_info_builder.Add(inline_info_entry); } std::array stack_map_entry { - entry.native_pc_code_offset.CompressedValue(), + entry.packed_native_pc, entry.dex_pc, dex_register_entries_[entry.dex_register_map_index].offset, entry.inlining_depth != 0 ? inline_info_index : InlineInfo::kNoValue, @@ -476,7 +484,7 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { // Check main stack map fields. DCHECK_EQ(stack_map.GetNativePcOffset(instruction_set_), - entry.native_pc_code_offset.Uint32Value(instruction_set_)); + StackMap::UnpackNativePc(entry.packed_native_pc, instruction_set_)); DCHECK_EQ(stack_map.GetDexPc(), entry.dex_pc); DCHECK_EQ(stack_map.GetRegisterMaskIndex(), entry.register_mask_index); DCHECK_EQ(code_info.GetRegisterMaskOf(stack_map), entry.register_mask); @@ -493,7 +501,7 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { if (entry.dex_method_index != dex::kDexNoIndex) { InvokeInfo invoke_info = code_info.GetInvokeInfo(invoke_info_index); DCHECK_EQ(invoke_info.GetNativePcOffset(instruction_set_), - entry.native_pc_code_offset.Uint32Value(instruction_set_)); + StackMap::UnpackNativePc(entry.packed_native_pc, instruction_set_)); DCHECK_EQ(invoke_info.GetInvokeType(), entry.invoke_type); DCHECK_EQ(invoke_info.GetMethodIndexIdx(), entry.dex_method_index_idx); invoke_info_index++; diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index 19863d882a..37a9bfc3ca 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -103,7 +103,7 @@ class StackMapStream : public ValueObject { // See runtime/stack_map.h to know what these fields contain. struct StackMapEntry { uint32_t dex_pc; - CodeOffset native_pc_code_offset; + uint32_t packed_native_pc; uint32_t register_mask; BitVector* sp_mask; uint32_t inlining_depth; @@ -148,14 +148,8 @@ class StackMapStream : public ValueObject { return stack_maps_.size(); } - const StackMapEntry& GetStackMap(size_t i) const { - return stack_maps_[i]; - } - - void SetStackMapNativePcOffset(size_t i, uint32_t native_pc_offset) { - stack_maps_[i].native_pc_code_offset = - CodeOffset::FromOffset(native_pc_offset, instruction_set_); - } + uint32_t GetStackMapNativePcOffset(size_t i); + void SetStackMapNativePcOffset(size_t i, uint32_t native_pc_offset); // Prepares the stream to fill in a memory region. Must be called before FillIn. // Returns the size (in bytes) needed to store this stream. diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index c372bb9b22..45466d869e 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -918,26 +918,31 @@ TEST(StackMapTest, InlineTest) { } } -TEST(StackMapTest, CodeOffsetTest) { - // Test minimum alignments, and decoding. - CodeOffset offset_thumb2 = - CodeOffset::FromOffset(kThumb2InstructionAlignment, InstructionSet::kThumb2); - CodeOffset offset_arm64 = - CodeOffset::FromOffset(kArm64InstructionAlignment, InstructionSet::kArm64); - CodeOffset offset_x86 = - CodeOffset::FromOffset(kX86InstructionAlignment, InstructionSet::kX86); - CodeOffset offset_x86_64 = - CodeOffset::FromOffset(kX86_64InstructionAlignment, InstructionSet::kX86_64); - CodeOffset offset_mips = - CodeOffset::FromOffset(kMipsInstructionAlignment, InstructionSet::kMips); - CodeOffset offset_mips64 = - CodeOffset::FromOffset(kMips64InstructionAlignment, InstructionSet::kMips64); - EXPECT_EQ(offset_thumb2.Uint32Value(InstructionSet::kThumb2), kThumb2InstructionAlignment); - EXPECT_EQ(offset_arm64.Uint32Value(InstructionSet::kArm64), kArm64InstructionAlignment); - EXPECT_EQ(offset_x86.Uint32Value(InstructionSet::kX86), kX86InstructionAlignment); - EXPECT_EQ(offset_x86_64.Uint32Value(InstructionSet::kX86_64), kX86_64InstructionAlignment); - EXPECT_EQ(offset_mips.Uint32Value(InstructionSet::kMips), kMipsInstructionAlignment); - EXPECT_EQ(offset_mips64.Uint32Value(InstructionSet::kMips64), kMips64InstructionAlignment); +TEST(StackMapTest, PackedNativePcTest) { + uint32_t packed_thumb2 = + StackMap::PackNativePc(kThumb2InstructionAlignment, InstructionSet::kThumb2); + uint32_t packed_arm64 = + StackMap::PackNativePc(kArm64InstructionAlignment, InstructionSet::kArm64); + uint32_t packed_x86 = + StackMap::PackNativePc(kX86InstructionAlignment, InstructionSet::kX86); + uint32_t packed_x86_64 = + StackMap::PackNativePc(kX86_64InstructionAlignment, InstructionSet::kX86_64); + uint32_t packed_mips = + StackMap::PackNativePc(kMipsInstructionAlignment, InstructionSet::kMips); + uint32_t packed_mips64 = + StackMap::PackNativePc(kMips64InstructionAlignment, InstructionSet::kMips64); + EXPECT_EQ(StackMap::UnpackNativePc(packed_thumb2, InstructionSet::kThumb2), + kThumb2InstructionAlignment); + EXPECT_EQ(StackMap::UnpackNativePc(packed_arm64, InstructionSet::kArm64), + kArm64InstructionAlignment); + EXPECT_EQ(StackMap::UnpackNativePc(packed_x86, InstructionSet::kX86), + kX86InstructionAlignment); + EXPECT_EQ(StackMap::UnpackNativePc(packed_x86_64, InstructionSet::kX86_64), + kX86_64InstructionAlignment); + EXPECT_EQ(StackMap::UnpackNativePc(packed_mips, InstructionSet::kMips), + kMipsInstructionAlignment); + EXPECT_EQ(StackMap::UnpackNativePc(packed_mips64, InstructionSet::kMips64), + kMips64InstructionAlignment); } TEST(StackMapTest, TestDeduplicateStackMask) { diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 6688cc1bb9..1f197b81f5 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1699,7 +1699,7 @@ class OatDumper { // Stack maps stats_.AddBits( Stats::kByteKindStackMapNativePc, - stack_maps.NumColumnBits(StackMap::kNativePcOffset) * num_stack_maps); + stack_maps.NumColumnBits(StackMap::kPackedNativePc) * num_stack_maps); stats_.AddBits( Stats::kByteKindStackMapDexPc, stack_maps.NumColumnBits(StackMap::kDexPc) * num_stack_maps); diff --git a/runtime/arch/code_offset.h b/runtime/arch/code_offset.h deleted file mode 100644 index f0c6d22ef2..0000000000 --- a/runtime/arch/code_offset.h +++ /dev/null @@ -1,94 +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_ARCH_CODE_OFFSET_H_ -#define ART_RUNTIME_ARCH_CODE_OFFSET_H_ - -#include - -#include - -#include "arch/instruction_set.h" -#include "base/bit_utils.h" -#include "base/macros.h" - -namespace art { - -// CodeOffset is a holder for compressed code offsets. Since some architectures have alignment -// requirements it is possible to compress code offsets to reduce stack map sizes. -class CodeOffset { - public: - ALWAYS_INLINE static CodeOffset FromOffset(uint32_t offset, InstructionSet isa = kRuntimeISA) { - return CodeOffset(offset / GetInstructionSetInstructionAlignment(isa)); - } - - ALWAYS_INLINE static CodeOffset FromCompressedOffset(uint32_t offset) { - return CodeOffset(offset); - } - - ALWAYS_INLINE uint32_t Uint32Value(InstructionSet isa = kRuntimeISA) const { - uint32_t decoded = value_ * GetInstructionSetInstructionAlignment(isa); - DCHECK_GE(decoded, value_) << "Integer overflow"; - return decoded; - } - - // Return compressed internal value. - ALWAYS_INLINE uint32_t CompressedValue() const { - return value_; - } - - ALWAYS_INLINE CodeOffset() = default; - ALWAYS_INLINE CodeOffset(const CodeOffset&) = default; - ALWAYS_INLINE CodeOffset& operator=(const CodeOffset&) = default; - ALWAYS_INLINE CodeOffset& operator=(CodeOffset&&) = default; - - private: - ALWAYS_INLINE explicit CodeOffset(uint32_t value) : value_(value) {} - - uint32_t value_ = 0u; -}; - -inline bool operator==(const CodeOffset& a, const CodeOffset& b) { - return a.CompressedValue() == b.CompressedValue(); -} - -inline bool operator!=(const CodeOffset& a, const CodeOffset& b) { - return !(a == b); -} - -inline bool operator<(const CodeOffset& a, const CodeOffset& b) { - return a.CompressedValue() < b.CompressedValue(); -} - -inline bool operator<=(const CodeOffset& a, const CodeOffset& b) { - return a.CompressedValue() <= b.CompressedValue(); -} - -inline bool operator>(const CodeOffset& a, const CodeOffset& b) { - return a.CompressedValue() > b.CompressedValue(); -} - -inline bool operator>=(const CodeOffset& a, const CodeOffset& b) { - return a.CompressedValue() >= b.CompressedValue(); -} - -inline std::ostream& operator<<(std::ostream& os, const CodeOffset& offset) { - return os << offset.Uint32Value(); -} - -} // namespace art - -#endif // ART_RUNTIME_ARCH_CODE_OFFSET_H_ diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index fd0e28d9ac..4ed60bf475 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -90,7 +90,7 @@ void StackMap::DumpEncoding(const BitTable<6>& table, VariableIndentationOutputStream* vios) { vios->Stream() << "StackMapEncoding" - << " (NativePcOffsetBits=" << table.NumColumnBits(kNativePcOffset) + << " (PackedNativePcBits=" << table.NumColumnBits(kPackedNativePc) << ", DexPcBits=" << table.NumColumnBits(kDexPc) << ", DexRegisterMapOffsetBits=" << table.NumColumnBits(kDexRegisterMapOffset) << ", InlineInfoIndexBits=" << table.NumColumnBits(kInlineInfoIndex) diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 1cb9a399f8..363884a21b 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -19,7 +19,6 @@ #include -#include "arch/code_offset.h" #include "base/bit_memory_region.h" #include "base/bit_table.h" #include "base/bit_utils.h" @@ -658,7 +657,7 @@ class DexRegisterMap { class StackMap : public BitTable<6>::Accessor { public: enum Field { - kNativePcOffset, + kPackedNativePc, kDexPc, kDexRegisterMapOffset, kInlineInfoIndex, @@ -672,8 +671,7 @@ class StackMap : public BitTable<6>::Accessor { : BitTable::Accessor(table, row) {} ALWAYS_INLINE uint32_t GetNativePcOffset(InstructionSet instruction_set) const { - CodeOffset offset(CodeOffset::FromCompressedOffset(Get())); - return offset.Uint32Value(instruction_set); + return UnpackNativePc(Get(), instruction_set); } uint32_t GetDexPc() const { return Get(); } @@ -688,6 +686,17 @@ class StackMap : public BitTable<6>::Accessor { uint32_t GetStackMaskIndex() const { return Get(); } + static uint32_t PackNativePc(uint32_t native_pc, InstructionSet isa) { + // TODO: DCHECK_ALIGNED_PARAM(native_pc, GetInstructionSetInstructionAlignment(isa)); + return native_pc / GetInstructionSetInstructionAlignment(isa); + } + + static uint32_t UnpackNativePc(uint32_t packed_native_pc, InstructionSet isa) { + uint32_t native_pc = packed_native_pc * GetInstructionSetInstructionAlignment(isa); + DCHECK_EQ(native_pc / GetInstructionSetInstructionAlignment(isa), packed_native_pc); + return native_pc; + } + static void DumpEncoding(const BitTable<6>& table, VariableIndentationOutputStream* vios); void Dump(VariableIndentationOutputStream* vios, const CodeInfo& code_info, @@ -776,7 +785,7 @@ class InlineInfo : public BitTable<5>::Accessor { class InvokeInfo : public BitTable<3>::Accessor { public: enum Field { - kNativePcOffset, + kPackedNativePc, kInvokeType, kMethodIndexIdx, kCount, @@ -786,8 +795,7 @@ class InvokeInfo : public BitTable<3>::Accessor { : BitTable::Accessor(table, row) {} ALWAYS_INLINE uint32_t GetNativePcOffset(InstructionSet instruction_set) const { - CodeOffset offset(CodeOffset::FromCompressedOffset(Get())); - return offset.Uint32Value(instruction_set); + return StackMap::UnpackNativePc(Get(), instruction_set); } uint32_t GetInvokeType() const { return Get(); } -- GitLab From 21d45b4f4ef3661ac33d622aaac5dd6d2ce7deb8 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Wed, 30 May 2018 06:35:05 +0100 Subject: [PATCH 496/749] Simplify DexRegisterMap API. Store some of the needed decoding state explicitly to avoid passing it around all the time. The DexRegisterMap class is rewritten in next CL. Test: test-art-host-gtest-stack_map_test Change-Id: Ie268dff2a1c1da2e08f0e6799ae51c30e11f350b --- compiler/debug/elf_debug_loc_writer.h | 6 +- compiler/optimizing/stack_map_stream.cc | 45 +++-- compiler/optimizing/stack_map_stream.h | 3 +- compiler/optimizing/stack_map_test.cc | 222 +++++++++--------------- runtime/check_reference_map_visitor.h | 3 +- runtime/jit/jit.cc | 7 +- runtime/quick_exception_handler.cc | 26 +-- runtime/stack.cc | 16 +- runtime/stack_map.cc | 37 ++-- runtime/stack_map.h | 87 +++++----- runtime/thread.cc | 3 +- 11 files changed, 183 insertions(+), 272 deletions(-) diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h index c1bf915212..8cb4e55bbc 100644 --- a/compiler/debug/elf_debug_loc_writer.h +++ b/compiler/debug/elf_debug_loc_writer.h @@ -149,11 +149,9 @@ static std::vector GetVariableLocations( DexRegisterMap dex_register_map = dex_register_maps[stack_map_index]; DCHECK(dex_register_map.IsValid()); CodeItemDataAccessor accessor(*method_info->dex_file, method_info->code_item); - reg_lo = dex_register_map.GetDexRegisterLocation( - vreg, accessor.RegistersSize(), code_info); + reg_lo = dex_register_map.GetDexRegisterLocation(vreg); if (is64bitValue) { - reg_hi = dex_register_map.GetDexRegisterLocation( - vreg + 1, accessor.RegistersSize(), code_info); + reg_hi = dex_register_map.GetDexRegisterLocation(vreg + 1); } // Add location entry for this address range. diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 5dc2acd512..b1dcb68415 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -248,20 +248,8 @@ size_t StackMapStream::PrepareForFillIn() { } } - // Write dex register maps. - MemoryRegion dex_register_map_region = - EncodeMemoryRegion(&out_, &bit_offset, dex_register_map_bytes * kBitsPerByte); - for (DexRegisterMapEntry& entry : dex_register_entries_) { - size_t entry_size = entry.ComputeSize(location_catalog_entries_.size()); - if (entry_size != 0) { - DexRegisterMap dex_register_map( - dex_register_map_region.Subregion(entry.offset, entry_size)); - FillInDexRegisterMap(dex_register_map, - entry.num_dex_registers, - *entry.live_dex_registers_mask, - entry.locations_start_index); - } - } + // Allocate space for dex register maps. + EncodeMemoryRegion(&out_, &bit_offset, dex_register_map_bytes * kBitsPerByte); // Write dex register catalog. EncodeVarintBits(&out_, &bit_offset, location_catalog_entries_.size()); @@ -340,6 +328,22 @@ void StackMapStream::FillInCodeInfo(MemoryRegion region) { uint8_t* ptr = EncodeUnsignedLeb128(region.begin(), out_.size()); region.CopyFromVector(ptr - region.begin(), out_); + // Write dex register maps. + CodeInfo code_info(region); + for (DexRegisterMapEntry& entry : dex_register_entries_) { + size_t entry_size = entry.ComputeSize(location_catalog_entries_.size()); + if (entry_size != 0) { + DexRegisterMap dex_register_map( + code_info.dex_register_maps_.Subregion(entry.offset, entry_size), + entry.num_dex_registers, + code_info); + FillInDexRegisterMap(dex_register_map, + entry.num_dex_registers, + *entry.live_dex_registers_mask, + entry.locations_start_index); + } + } + // Verify all written data in debug build. if (kIsDebugBuild) { CheckCodeInfo(region); @@ -364,7 +368,6 @@ void StackMapStream::FillInDexRegisterMap(DexRegisterMap dex_register_map, dex_register_map.SetLocationCatalogEntryIndex( index_in_dex_register_locations, location_catalog_entry_index, - num_dex_registers, location_catalog_entries_.size()); } } @@ -421,8 +424,7 @@ bool StackMapStream::DexRegisterMapEntryEquals(const DexRegisterMapEntry& a, } // Helper for CheckCodeInfo - check that register map has the expected content. -void StackMapStream::CheckDexRegisterMap(const CodeInfo& code_info, - const DexRegisterMap& dex_register_map, +void StackMapStream::CheckDexRegisterMap(const DexRegisterMap& dex_register_map, size_t num_dex_registers, BitVector* live_dex_registers_mask, size_t dex_register_locations_index) const { @@ -439,8 +441,7 @@ void StackMapStream::CheckDexRegisterMap(const CodeInfo& code_info, << dex_register_map.IsValid() << " " << dex_register_map.IsDexRegisterLive(reg); } else { DCHECK(dex_register_map.IsDexRegisterLive(reg)); - DexRegisterLocation seen = dex_register_map.GetDexRegisterLocation( - reg, num_dex_registers, code_info); + DexRegisterLocation seen = dex_register_map.GetDexRegisterLocation(reg); DCHECK_EQ(expected.GetKind(), seen.GetKind()); DCHECK_EQ(expected.GetValue(), seen.GetValue()); } @@ -506,8 +507,7 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { DCHECK_EQ(invoke_info.GetMethodIndexIdx(), entry.dex_method_index_idx); invoke_info_index++; } - CheckDexRegisterMap(code_info, - code_info.GetDexRegisterMapOf( + CheckDexRegisterMap(code_info.GetDexRegisterMapOf( stack_map, entry.dex_register_entry.num_dex_registers), entry.dex_register_entry.num_dex_registers, entry.dex_register_entry.live_dex_registers_mask, @@ -533,8 +533,7 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { DCHECK_EQ(method_indices_[method_index_idx], inline_entry.method_index); } - CheckDexRegisterMap(code_info, - code_info.GetDexRegisterMapAtDepth( + CheckDexRegisterMap(code_info.GetDexRegisterMapAtDepth( d, inline_info, inline_entry.dex_register_entry.num_dex_registers), diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index 37a9bfc3ca..6d505b95db 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -178,8 +178,7 @@ class StackMapStream : public ValueObject { const BitVector& live_dex_registers_mask, uint32_t start_index_in_dex_register_locations) const; - void CheckDexRegisterMap(const CodeInfo& code_info, - const DexRegisterMap& dex_register_map, + void CheckDexRegisterMap(const DexRegisterMap& dex_register_map, size_t num_dex_registers, BitVector* live_dex_registers_mask, size_t dex_register_locations_index) const; diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index 45466d869e..112771847c 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -96,22 +96,15 @@ TEST(StackMapTest, Test1) { size_t expected_dex_register_map_size = 1u + 1u; ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); - ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationKind( - 0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind( - 1, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationInternalKind( - 0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind( - 1, number_of_dex_registers, code_info)); - ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes( - 0, number_of_dex_registers, code_info)); - ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info)); - - size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( - 0, number_of_dex_registers, number_of_catalog_entries); - size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( - 1, number_of_dex_registers, number_of_catalog_entries); + ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationKind(0)); + ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind(1)); + ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationInternalKind(0)); + ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind(1)); + ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes(0)); + ASSERT_EQ(-2, dex_register_map.GetConstant(1)); + + size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(0, number_of_catalog_entries); + size_t index1 = dex_register_map.GetLocationCatalogEntryIndex(1, number_of_catalog_entries); ASSERT_EQ(0u, index0); ASSERT_EQ(1u, index1); DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); @@ -211,22 +204,15 @@ TEST(StackMapTest, Test2) { size_t expected_dex_register_map_size = 1u + 1u; ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); - ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationKind( - 0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind( - 1, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationInternalKind( - 0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind( - 1, number_of_dex_registers, code_info)); - ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes( - 0, number_of_dex_registers, code_info)); - ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info)); - - size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( - 0, number_of_dex_registers, number_of_catalog_entries); - size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( - 1, number_of_dex_registers, number_of_catalog_entries); + ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationKind(0)); + ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind(1)); + ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationInternalKind(0)); + ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind(1)); + ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes(0)); + ASSERT_EQ(-2, dex_register_map.GetConstant(1)); + + size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(0, number_of_catalog_entries); + size_t index1 = dex_register_map.GetLocationCatalogEntryIndex(1, number_of_catalog_entries); ASSERT_EQ(0u, index0); ASSERT_EQ(1u, index1); DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); @@ -270,23 +256,15 @@ TEST(StackMapTest, Test2) { size_t expected_dex_register_map_size = 1u + 1u; ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); - ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationKind( - 0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationKind( - 1, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationInternalKind( - 0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationInternalKind( - 1, number_of_dex_registers, code_info)); - ASSERT_EQ(18, dex_register_map.GetMachineRegister( - 0, number_of_dex_registers, code_info)); - ASSERT_EQ(3, dex_register_map.GetMachineRegister( - 1, number_of_dex_registers, code_info)); - - size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( - 0, number_of_dex_registers, number_of_catalog_entries); - size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( - 1, number_of_dex_registers, number_of_catalog_entries); + ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationKind(0)); + ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationKind(1)); + ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationInternalKind(0)); + ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationInternalKind(1)); + ASSERT_EQ(18, dex_register_map.GetMachineRegister(0)); + ASSERT_EQ(3, dex_register_map.GetMachineRegister(1)); + + size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(0, number_of_catalog_entries); + size_t index1 = dex_register_map.GetLocationCatalogEntryIndex(1, number_of_catalog_entries); ASSERT_EQ(2u, index0); ASSERT_EQ(3u, index1); DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); @@ -324,23 +302,15 @@ TEST(StackMapTest, Test2) { size_t expected_dex_register_map_size = 1u + 1u; ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); - ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationKind( - 0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kInRegisterHigh, dex_register_map.GetLocationKind( - 1, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationInternalKind( - 0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kInRegisterHigh, dex_register_map.GetLocationInternalKind( - 1, number_of_dex_registers, code_info)); - ASSERT_EQ(6, dex_register_map.GetMachineRegister( - 0, number_of_dex_registers, code_info)); - ASSERT_EQ(8, dex_register_map.GetMachineRegister( - 1, number_of_dex_registers, code_info)); - - size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( - 0, number_of_dex_registers, number_of_catalog_entries); - size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( - 1, number_of_dex_registers, number_of_catalog_entries); + ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationKind(0)); + ASSERT_EQ(Kind::kInRegisterHigh, dex_register_map.GetLocationKind(1)); + ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationInternalKind(0)); + ASSERT_EQ(Kind::kInRegisterHigh, dex_register_map.GetLocationInternalKind(1)); + ASSERT_EQ(6, dex_register_map.GetMachineRegister(0)); + ASSERT_EQ(8, dex_register_map.GetMachineRegister(1)); + + size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(0, number_of_catalog_entries); + size_t index1 = dex_register_map.GetLocationCatalogEntryIndex(1, number_of_catalog_entries); ASSERT_EQ(4u, index0); ASSERT_EQ(5u, index1); DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); @@ -378,23 +348,15 @@ TEST(StackMapTest, Test2) { size_t expected_dex_register_map_size = 1u + 1u; ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); - ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationKind( - 0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kInFpuRegisterHigh, dex_register_map.GetLocationKind( - 1, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationInternalKind( - 0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kInFpuRegisterHigh, dex_register_map.GetLocationInternalKind( - 1, number_of_dex_registers, code_info)); - ASSERT_EQ(3, dex_register_map.GetMachineRegister( - 0, number_of_dex_registers, code_info)); - ASSERT_EQ(1, dex_register_map.GetMachineRegister( - 1, number_of_dex_registers, code_info)); - - size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( - 0, number_of_dex_registers, number_of_catalog_entries); - size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( - 1, number_of_dex_registers, number_of_catalog_entries); + ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationKind(0)); + ASSERT_EQ(Kind::kInFpuRegisterHigh, dex_register_map.GetLocationKind(1)); + ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationInternalKind(0)); + ASSERT_EQ(Kind::kInFpuRegisterHigh, dex_register_map.GetLocationInternalKind(1)); + ASSERT_EQ(3, dex_register_map.GetMachineRegister(0)); + ASSERT_EQ(1, dex_register_map.GetMachineRegister(1)); + + size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(0, number_of_catalog_entries); + size_t index1 = dex_register_map.GetLocationCatalogEntryIndex(1, number_of_catalog_entries); ASSERT_EQ(3u, index0); // Shared with second stack map. ASSERT_EQ(6u, index1); DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); @@ -470,20 +432,15 @@ TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { size_t expected_map_size = 1u + 1u; ASSERT_EQ(expected_map_size, map.Size()); - ASSERT_EQ(Kind::kInStack, map.GetLocationKind(0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kConstant, - map.GetLocationKind(1, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kInStack, - map.GetLocationInternalKind(0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kConstantLargeValue, - map.GetLocationInternalKind(1, number_of_dex_registers, code_info)); - ASSERT_EQ(0, map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info)); - ASSERT_EQ(-2, map.GetConstant(1, number_of_dex_registers, code_info)); - - const size_t index0 = - map.GetLocationCatalogEntryIndex(0, number_of_dex_registers, number_of_catalog_entries); - const size_t index1 = - map.GetLocationCatalogEntryIndex(1, number_of_dex_registers, number_of_catalog_entries); + ASSERT_EQ(Kind::kInStack, map.GetLocationKind(0)); + ASSERT_EQ(Kind::kConstant, map.GetLocationKind(1)); + ASSERT_EQ(Kind::kInStack, map.GetLocationInternalKind(0)); + ASSERT_EQ(Kind::kConstantLargeValue, map.GetLocationInternalKind(1)); + ASSERT_EQ(0, map.GetStackOffsetInBytes(0)); + ASSERT_EQ(-2, map.GetConstant(1)); + + const size_t index0 = map.GetLocationCatalogEntryIndex(0, number_of_catalog_entries); + const size_t index1 = map.GetLocationCatalogEntryIndex(1, number_of_catalog_entries); ASSERT_EQ(0u, index0); ASSERT_EQ(1u, index1); DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); @@ -552,20 +509,14 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { size_t expected_dex_register_map_size = 1u + 0u; ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); - ASSERT_EQ(Kind::kNone, dex_register_map.GetLocationKind( - 0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind( - 1, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kNone, dex_register_map.GetLocationInternalKind( - 0, number_of_dex_registers, code_info)); - ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind( - 1, number_of_dex_registers, code_info)); - ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info)); - - size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( - 0, number_of_dex_registers, number_of_catalog_entries); - size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( - 1, number_of_dex_registers, number_of_catalog_entries); + ASSERT_EQ(Kind::kNone, dex_register_map.GetLocationKind(0)); + ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind(1)); + ASSERT_EQ(Kind::kNone, dex_register_map.GetLocationInternalKind(0)); + ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind(1)); + ASSERT_EQ(-2, dex_register_map.GetConstant(1)); + + size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(0, number_of_catalog_entries); + size_t index1 = dex_register_map.GetLocationCatalogEntryIndex(1, number_of_catalog_entries); ASSERT_EQ(DexRegisterLocationCatalog::kNoLocationEntryIndex, index0); ASSERT_EQ(0u, index1); DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); @@ -632,8 +583,7 @@ TEST(StackMapTest, DexRegisterMapOffsetOverflow) { StackMap stack_map0 = code_info.GetStackMapAt(0); DexRegisterMap dex_register_map0 = code_info.GetDexRegisterMapOf(stack_map0, number_of_dex_registers); - ASSERT_EQ(127u, dex_register_map0.GetLocationMappingDataSize(number_of_dex_registers, - number_of_catalog_entries)); + ASSERT_EQ(127u, dex_register_map0.GetLocationMappingDataSize(number_of_catalog_entries)); ASSERT_EQ(255u, dex_register_map0.Size()); StackMap stack_map1 = code_info.GetStackMapAt(1); @@ -680,20 +630,20 @@ TEST(StackMapTest, TestShareDexRegisterMap) { // Verify first stack map. StackMap sm0 = ci.GetStackMapAt(0); DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, number_of_dex_registers); - ASSERT_EQ(0, dex_registers0.GetMachineRegister(0, number_of_dex_registers, ci)); - ASSERT_EQ(-2, dex_registers0.GetConstant(1, number_of_dex_registers, ci)); + ASSERT_EQ(0, dex_registers0.GetMachineRegister(0)); + ASSERT_EQ(-2, dex_registers0.GetConstant(1)); // Verify second stack map. StackMap sm1 = ci.GetStackMapAt(1); DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1, number_of_dex_registers); - ASSERT_EQ(0, dex_registers1.GetMachineRegister(0, number_of_dex_registers, ci)); - ASSERT_EQ(-2, dex_registers1.GetConstant(1, number_of_dex_registers, ci)); + ASSERT_EQ(0, dex_registers1.GetMachineRegister(0)); + ASSERT_EQ(-2, dex_registers1.GetConstant(1)); // Verify third stack map. StackMap sm2 = ci.GetStackMapAt(2); DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2, number_of_dex_registers); - ASSERT_EQ(2, dex_registers2.GetMachineRegister(0, number_of_dex_registers, ci)); - ASSERT_EQ(-2, dex_registers2.GetConstant(1, number_of_dex_registers, ci)); + ASSERT_EQ(2, dex_registers2.GetMachineRegister(0)); + ASSERT_EQ(-2, dex_registers2.GetConstant(1)); // Verify dex register map offsets. ASSERT_EQ(sm0.GetDexRegisterMapOffset(), @@ -833,8 +783,8 @@ TEST(StackMapTest, InlineTest) { StackMap sm0 = ci.GetStackMapAt(0); DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, 2); - ASSERT_EQ(0, dex_registers0.GetStackOffsetInBytes(0, 2, ci)); - ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci)); + ASSERT_EQ(0, dex_registers0.GetStackOffsetInBytes(0)); + ASSERT_EQ(4, dex_registers0.GetConstant(1)); InlineInfo if0 = ci.GetInlineInfoOf(sm0); ASSERT_EQ(2u, if0.GetDepth()); @@ -844,12 +794,12 @@ TEST(StackMapTest, InlineTest) { ASSERT_TRUE(if0.EncodesArtMethodAtDepth(1)); DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if0, 1); - ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0, 1, ci)); + ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0)); DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if0, 3); - ASSERT_EQ(16, dex_registers2.GetStackOffsetInBytes(0, 3, ci)); - ASSERT_EQ(20, dex_registers2.GetConstant(1, 3, ci)); - ASSERT_EQ(15, dex_registers2.GetMachineRegister(2, 3, ci)); + ASSERT_EQ(16, dex_registers2.GetStackOffsetInBytes(0)); + ASSERT_EQ(20, dex_registers2.GetConstant(1)); + ASSERT_EQ(15, dex_registers2.GetMachineRegister(2)); } { @@ -857,8 +807,8 @@ TEST(StackMapTest, InlineTest) { StackMap sm1 = ci.GetStackMapAt(1); DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm1, 2); - ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci)); - ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci)); + ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0)); + ASSERT_EQ(0, dex_registers0.GetConstant(1)); InlineInfo if1 = ci.GetInlineInfoOf(sm1); ASSERT_EQ(3u, if1.GetDepth()); @@ -870,12 +820,12 @@ TEST(StackMapTest, InlineTest) { ASSERT_TRUE(if1.EncodesArtMethodAtDepth(2)); DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if1, 1); - ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0, 1, ci)); + ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0)); DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if1, 3); - ASSERT_EQ(80, dex_registers2.GetStackOffsetInBytes(0, 3, ci)); - ASSERT_EQ(10, dex_registers2.GetConstant(1, 3, ci)); - ASSERT_EQ(5, dex_registers2.GetMachineRegister(2, 3, ci)); + ASSERT_EQ(80, dex_registers2.GetStackOffsetInBytes(0)); + ASSERT_EQ(10, dex_registers2.GetConstant(1)); + ASSERT_EQ(5, dex_registers2.GetMachineRegister(2)); ASSERT_FALSE(if1.HasDexRegisterMapAtDepth(2)); } @@ -886,7 +836,7 @@ TEST(StackMapTest, InlineTest) { DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm2, 2); ASSERT_FALSE(dex_registers0.IsDexRegisterLive(0)); - ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci)); + ASSERT_EQ(4, dex_registers0.GetConstant(1)); ASSERT_FALSE(sm2.HasInlineInfo()); } @@ -895,8 +845,8 @@ TEST(StackMapTest, InlineTest) { StackMap sm3 = ci.GetStackMapAt(3); DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm3, 2); - ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci)); - ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci)); + ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0)); + ASSERT_EQ(0, dex_registers0.GetConstant(1)); InlineInfo if2 = ci.GetInlineInfoOf(sm3); ASSERT_EQ(3u, if2.GetDepth()); @@ -910,11 +860,11 @@ TEST(StackMapTest, InlineTest) { ASSERT_FALSE(if2.HasDexRegisterMapAtDepth(0)); DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(1, if2, 1); - ASSERT_EQ(2, dex_registers1.GetMachineRegister(0, 1, ci)); + ASSERT_EQ(2, dex_registers1.GetMachineRegister(0)); DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(2, if2, 2); ASSERT_FALSE(dex_registers2.IsDexRegisterLive(0)); - ASSERT_EQ(3, dex_registers2.GetMachineRegister(1, 2, ci)); + ASSERT_EQ(3, dex_registers2.GetMachineRegister(1)); } } diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h index 6917899bff..acdb235f8c 100644 --- a/runtime/check_reference_map_visitor.h +++ b/runtime/check_reference_map_visitor.h @@ -75,8 +75,7 @@ class CheckReferenceMapVisitor : public StackVisitor { for (int i = 0; i < number_of_references; ++i) { int reg = registers[i]; CHECK_LT(reg, accessor.RegistersSize()); - DexRegisterLocation location = dex_register_map.GetDexRegisterLocation( - reg, number_of_dex_registers, code_info); + DexRegisterLocation location = dex_register_map.GetDexRegisterLocation(reg); switch (location.GetKind()) { case DexRegisterLocation::Kind::kNone: // Not set, should not be a reference. diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 9c02dce98a..b7b779ce31 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -515,8 +515,7 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, // this dex pc. } else { for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) { - DexRegisterLocation::Kind location = - vreg_map.GetLocationKind(vreg, number_of_vregs, code_info); + DexRegisterLocation::Kind location = vreg_map.GetLocationKind(vreg); if (location == DexRegisterLocation::Kind::kNone) { // Dex register is dead or uninitialized. continue; @@ -530,9 +529,7 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, DCHECK_EQ(location, DexRegisterLocation::Kind::kInStack); int32_t vreg_value = shadow_frame->GetVReg(vreg); - int32_t slot_offset = vreg_map.GetStackOffsetInBytes(vreg, - number_of_vregs, - code_info); + int32_t slot_offset = vreg_map.GetStackOffsetInBytes(vreg); DCHECK_LT(slot_offset, static_cast(frame_size)); DCHECK_GT(slot_offset, 0); (reinterpret_cast(memory))[slot_offset / sizeof(int32_t)] = vreg_value; diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 26489209b8..63a09f25a4 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -246,8 +246,7 @@ void QuickExceptionHandler::SetCatchEnvironmentForOptimizedHandler(StackVisitor* // Copy values between them. for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) { - DexRegisterLocation::Kind catch_location = - catch_vreg_map.GetLocationKind(vreg, number_of_vregs, code_info); + DexRegisterLocation::Kind catch_location = catch_vreg_map.GetLocationKind(vreg); if (catch_location == DexRegisterLocation::Kind::kNone) { continue; } @@ -255,9 +254,7 @@ void QuickExceptionHandler::SetCatchEnvironmentForOptimizedHandler(StackVisitor* // Get vreg value from its current location. uint32_t vreg_value; - VRegKind vreg_kind = ToVRegKind(throw_vreg_map.GetLocationKind(vreg, - number_of_vregs, - code_info)); + VRegKind vreg_kind = ToVRegKind(throw_vreg_map.GetLocationKind(vreg)); bool get_vreg_success = stack_visitor->GetVReg(stack_visitor->GetMethod(), vreg, vreg_kind, @@ -268,9 +265,7 @@ void QuickExceptionHandler::SetCatchEnvironmentForOptimizedHandler(StackVisitor* << "native_pc_offset=" << stack_visitor->GetNativePcOffset() << ")"; // Copy value to the catch phi's stack slot. - int32_t slot_offset = catch_vreg_map.GetStackOffsetInBytes(vreg, - number_of_vregs, - code_info); + int32_t slot_offset = catch_vreg_map.GetStackOffsetInBytes(vreg); ArtMethod** frame_top = stack_visitor->GetCurrentQuickFrame(); uint8_t* slot_address = reinterpret_cast(frame_top) + slot_offset; uint32_t* slot_ptr = reinterpret_cast(slot_address); @@ -425,17 +420,14 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { continue; } - DexRegisterLocation::Kind location = - vreg_map.GetLocationKind(vreg, number_of_vregs, code_info); + DexRegisterLocation::Kind location = vreg_map.GetLocationKind(vreg); static constexpr uint32_t kDeadValue = 0xEBADDE09; uint32_t value = kDeadValue; bool is_reference = false; switch (location) { case DexRegisterLocation::Kind::kInStack: { - const int32_t offset = vreg_map.GetStackOffsetInBytes(vreg, - number_of_vregs, - code_info); + const int32_t offset = vreg_map.GetStackOffsetInBytes(vreg); const uint8_t* addr = reinterpret_cast(GetCurrentQuickFrame()) + offset; value = *reinterpret_cast(addr); uint32_t bit = (offset >> 2); @@ -448,7 +440,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { case DexRegisterLocation::Kind::kInRegisterHigh: case DexRegisterLocation::Kind::kInFpuRegister: case DexRegisterLocation::Kind::kInFpuRegisterHigh: { - uint32_t reg = vreg_map.GetMachineRegister(vreg, number_of_vregs, code_info); + uint32_t reg = vreg_map.GetMachineRegister(vreg); bool result = GetRegisterIfAccessible(reg, ToVRegKind(location), &value); CHECK(result); if (location == DexRegisterLocation::Kind::kInRegister) { @@ -459,7 +451,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { break; } case DexRegisterLocation::Kind::kConstant: { - value = vreg_map.GetConstant(vreg, number_of_vregs, code_info); + value = vreg_map.GetConstant(vreg); if (value == 0) { // Make it a reference for extra safety. is_reference = true; @@ -472,9 +464,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { default: { LOG(FATAL) << "Unexpected location kind " - << vreg_map.GetLocationInternalKind(vreg, - number_of_vregs, - code_info); + << vreg_map.GetLocationInternalKind(vreg); UNREACHABLE(); } } diff --git a/runtime/stack.cc b/runtime/stack.cc index 8cb0700ce2..6da7dcb697 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -245,13 +245,10 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin if (!dex_register_map.IsValid()) { return false; } - DexRegisterLocation::Kind location_kind = - dex_register_map.GetLocationKind(vreg, number_of_dex_registers, code_info); + DexRegisterLocation::Kind location_kind = dex_register_map.GetLocationKind(vreg); switch (location_kind) { case DexRegisterLocation::Kind::kInStack: { - const int32_t offset = dex_register_map.GetStackOffsetInBytes(vreg, - number_of_dex_registers, - code_info); + const int32_t offset = dex_register_map.GetStackOffsetInBytes(vreg); const uint8_t* addr = reinterpret_cast(cur_quick_frame_) + offset; *val = *reinterpret_cast(addr); return true; @@ -260,21 +257,18 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin case DexRegisterLocation::Kind::kInRegisterHigh: case DexRegisterLocation::Kind::kInFpuRegister: case DexRegisterLocation::Kind::kInFpuRegisterHigh: { - uint32_t reg = - dex_register_map.GetMachineRegister(vreg, number_of_dex_registers, code_info); + uint32_t reg = dex_register_map.GetMachineRegister(vreg); return GetRegisterIfAccessible(reg, kind, val); } case DexRegisterLocation::Kind::kConstant: - *val = dex_register_map.GetConstant(vreg, number_of_dex_registers, code_info); + *val = dex_register_map.GetConstant(vreg); return true; case DexRegisterLocation::Kind::kNone: return false; default: LOG(FATAL) << "Unexpected location kind " - << dex_register_map.GetLocationInternalKind(vreg, - number_of_dex_registers, - code_info); + << dex_register_map.GetLocationInternalKind(vreg); UNREACHABLE(); } } diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index 4ed60bf475..61fe2e7965 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -52,27 +52,21 @@ std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation::Kind& } DexRegisterLocation::Kind DexRegisterMap::GetLocationInternalKind( - uint16_t dex_register_number, - uint16_t number_of_dex_registers, - const CodeInfo& code_info) const { + uint16_t dex_register_number) const { DexRegisterLocationCatalog dex_register_location_catalog = - code_info.GetDexRegisterLocationCatalog(); + code_info_.GetDexRegisterLocationCatalog(); size_t location_catalog_entry_index = GetLocationCatalogEntryIndex( dex_register_number, - number_of_dex_registers, - code_info.GetNumberOfLocationCatalogEntries()); + code_info_.GetNumberOfLocationCatalogEntries()); return dex_register_location_catalog.GetLocationInternalKind(location_catalog_entry_index); } -DexRegisterLocation DexRegisterMap::GetDexRegisterLocation(uint16_t dex_register_number, - uint16_t number_of_dex_registers, - const CodeInfo& code_info) const { +DexRegisterLocation DexRegisterMap::GetDexRegisterLocation(uint16_t dex_register_number) const { DexRegisterLocationCatalog dex_register_location_catalog = - code_info.GetDexRegisterLocationCatalog(); + code_info_.GetDexRegisterLocationCatalog(); size_t location_catalog_entry_index = GetLocationCatalogEntryIndex( dex_register_number, - number_of_dex_registers, - code_info.GetNumberOfLocationCatalogEntries()); + code_info_.GetNumberOfLocationCatalogEntries()); return dex_register_location_catalog.GetDexRegisterLocation(location_catalog_entry_index); } @@ -160,18 +154,15 @@ void DexRegisterLocationCatalog::Dump(VariableIndentationOutputStream* vios, } } -void DexRegisterMap::Dump(VariableIndentationOutputStream* vios, - const CodeInfo& code_info, - uint16_t number_of_dex_registers) const { - size_t number_of_location_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); +void DexRegisterMap::Dump(VariableIndentationOutputStream* vios) const { + size_t number_of_location_catalog_entries = code_info_.GetNumberOfLocationCatalogEntries(); // TODO: Display the bit mask of live Dex registers. - for (size_t j = 0; j < number_of_dex_registers; ++j) { + for (size_t j = 0; j < number_of_dex_registers_; ++j) { if (IsDexRegisterLive(j)) { size_t location_catalog_entry_index = GetLocationCatalogEntryIndex( - j, number_of_dex_registers, number_of_location_catalog_entries); - DexRegisterLocation location = GetDexRegisterLocation(j, - number_of_dex_registers, - code_info); + j, + number_of_location_catalog_entries); + DexRegisterLocation location = GetDexRegisterLocation(j); ScopedIndentation indent1(vios); DumpRegisterMapping( vios->Stream(), j, location, "v", @@ -207,7 +198,7 @@ void StackMap::Dump(VariableIndentationOutputStream* vios, if (HasDexRegisterMap()) { DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf( *this, number_of_dex_registers); - dex_register_map.Dump(vios, code_info, number_of_dex_registers); + dex_register_map.Dump(vios); } if (HasInlineInfo()) { InlineInfo inline_info = code_info.GetInlineInfoOf(*this); @@ -244,7 +235,7 @@ void InlineInfo::Dump(VariableIndentationOutputStream* vios, DexRegisterMap dex_register_map = code_info.GetDexRegisterMapAtDepth(i, *this, number_of_dex_registers[i]); ScopedIndentation indent1(vios); - dex_register_map.Dump(vios, code_info, number_of_dex_registers[i]); + dex_register_map.Dump(vios); } } } diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 363884a21b..9d66b3181c 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -445,53 +445,39 @@ class DexRegisterLocationCatalog { */ class DexRegisterMap { public: - explicit DexRegisterMap(MemoryRegion region) : region_(region) {} - DexRegisterMap() {} + DexRegisterMap(MemoryRegion region, uint16_t number_of_dex_registers, const CodeInfo& code_info) + : region_(region), + number_of_dex_registers_(number_of_dex_registers), + code_info_(code_info) {} bool IsValid() const { return region_.IsValid(); } // Get the surface kind of Dex register `dex_register_number`. - DexRegisterLocation::Kind GetLocationKind(uint16_t dex_register_number, - uint16_t number_of_dex_registers, - const CodeInfo& code_info) const { - return DexRegisterLocation::ConvertToSurfaceKind( - GetLocationInternalKind(dex_register_number, number_of_dex_registers, code_info)); + DexRegisterLocation::Kind GetLocationKind(uint16_t dex_register_number) const { + return DexRegisterLocation::ConvertToSurfaceKind(GetLocationInternalKind(dex_register_number)); } // Get the internal kind of Dex register `dex_register_number`. - DexRegisterLocation::Kind GetLocationInternalKind(uint16_t dex_register_number, - uint16_t number_of_dex_registers, - const CodeInfo& code_info) const; + DexRegisterLocation::Kind GetLocationInternalKind(uint16_t dex_register_number) const; // Get the Dex register location `dex_register_number`. - DexRegisterLocation GetDexRegisterLocation(uint16_t dex_register_number, - uint16_t number_of_dex_registers, - const CodeInfo& code_info) const; - - int32_t GetStackOffsetInBytes(uint16_t dex_register_number, - uint16_t number_of_dex_registers, - const CodeInfo& code_info) const { - DexRegisterLocation location = - GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info); + DexRegisterLocation GetDexRegisterLocation(uint16_t dex_register_number) const; + + int32_t GetStackOffsetInBytes(uint16_t dex_register_number) const { + DexRegisterLocation location = GetDexRegisterLocation(dex_register_number); DCHECK(location.GetKind() == DexRegisterLocation::Kind::kInStack); // GetDexRegisterLocation returns the offset in bytes. return location.GetValue(); } - int32_t GetConstant(uint16_t dex_register_number, - uint16_t number_of_dex_registers, - const CodeInfo& code_info) const { - DexRegisterLocation location = - GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info); + int32_t GetConstant(uint16_t dex_register_number) const { + DexRegisterLocation location = GetDexRegisterLocation(dex_register_number); DCHECK_EQ(location.GetKind(), DexRegisterLocation::Kind::kConstant); return location.GetValue(); } - int32_t GetMachineRegister(uint16_t dex_register_number, - uint16_t number_of_dex_registers, - const CodeInfo& code_info) const { - DexRegisterLocation location = - GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info); + int32_t GetMachineRegister(uint16_t dex_register_number) const { + DexRegisterLocation location = GetDexRegisterLocation(dex_register_number); DCHECK(location.GetInternalKind() == DexRegisterLocation::Kind::kInRegister || location.GetInternalKind() == DexRegisterLocation::Kind::kInRegisterHigh || location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegister || @@ -503,7 +489,6 @@ class DexRegisterMap { // Get the index of the entry in the Dex register location catalog // corresponding to `dex_register_number`. size_t GetLocationCatalogEntryIndex(uint16_t dex_register_number, - uint16_t number_of_dex_registers, size_t number_of_location_catalog_entries) const { if (!IsDexRegisterLive(dex_register_number)) { return DexRegisterLocationCatalog::kNoLocationEntryIndex; @@ -518,9 +503,9 @@ class DexRegisterMap { // The bit offset of the beginning of the map locations. size_t map_locations_offset_in_bits = - GetLocationMappingDataOffset(number_of_dex_registers) * kBitsPerByte; + GetLocationMappingDataOffset(number_of_dex_registers_) * kBitsPerByte; size_t index_in_dex_register_map = GetIndexInDexRegisterMap(dex_register_number); - DCHECK_LT(index_in_dex_register_map, GetNumberOfLiveDexRegisters(number_of_dex_registers)); + DCHECK_LT(index_in_dex_register_map, GetNumberOfLiveDexRegisters()); // The bit size of an entry. size_t map_entry_size_in_bits = SingleEntrySizeInBits(number_of_location_catalog_entries); // The bit offset where `index_in_dex_register_map` is located. @@ -535,9 +520,8 @@ class DexRegisterMap { // Map entry at `index_in_dex_register_map` to `location_catalog_entry_index`. void SetLocationCatalogEntryIndex(size_t index_in_dex_register_map, size_t location_catalog_entry_index, - uint16_t number_of_dex_registers, size_t number_of_location_catalog_entries) { - DCHECK_LT(index_in_dex_register_map, GetNumberOfLiveDexRegisters(number_of_dex_registers)); + DCHECK_LT(index_in_dex_register_map, GetNumberOfLiveDexRegisters()); DCHECK_LT(location_catalog_entry_index, number_of_location_catalog_entries); if (number_of_location_catalog_entries == 1) { @@ -548,7 +532,7 @@ class DexRegisterMap { // The bit offset of the beginning of the map locations. size_t map_locations_offset_in_bits = - GetLocationMappingDataOffset(number_of_dex_registers) * kBitsPerByte; + GetLocationMappingDataOffset(number_of_dex_registers_) * kBitsPerByte; // The bit size of an entry. size_t map_entry_size_in_bits = SingleEntrySizeInBits(number_of_location_catalog_entries); // The bit offset where `index_in_dex_register_map` is located. @@ -580,6 +564,10 @@ class DexRegisterMap { return number_of_live_dex_registers; } + size_t GetNumberOfLiveDexRegisters() const { + return GetNumberOfLiveDexRegisters(number_of_dex_registers_); + } + static size_t GetLiveBitMaskOffset() { return kFixedSize; } @@ -594,10 +582,9 @@ class DexRegisterMap { return GetLiveBitMaskOffset() + GetLiveBitMaskSize(number_of_dex_registers); } - size_t GetLocationMappingDataSize(uint16_t number_of_dex_registers, - size_t number_of_location_catalog_entries) const { + size_t GetLocationMappingDataSize(size_t number_of_location_catalog_entries) const { size_t location_mapping_data_size_in_bits = - GetNumberOfLiveDexRegisters(number_of_dex_registers) + GetNumberOfLiveDexRegisters() * SingleEntrySizeInBits(number_of_location_catalog_entries); return RoundUp(location_mapping_data_size_in_bits, kBitsPerByte) / kBitsPerByte; } @@ -620,8 +607,7 @@ class DexRegisterMap { return BitsToBytesRoundUp(region_.size_in_bits()); } - void Dump(VariableIndentationOutputStream* vios, - const CodeInfo& code_info, uint16_t number_of_dex_registers) const; + void Dump(VariableIndentationOutputStream* vios) const; private: // Return the index in the Dex register map corresponding to the Dex @@ -641,6 +627,8 @@ class DexRegisterMap { static constexpr int kFixedSize = 0; BitMemoryRegion region_; + uint16_t number_of_dex_registers_; + const CodeInfo& code_info_; friend class CodeInfo; friend class StackMapStream; @@ -896,11 +884,13 @@ class CodeInfo { DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, size_t number_of_dex_registers) const { if (!stack_map.HasDexRegisterMap()) { - return DexRegisterMap(); + return DexRegisterMap(MemoryRegion(), 0, *this); } const uint32_t offset = stack_map.GetDexRegisterMapOffset(); size_t size = ComputeDexRegisterMapSizeOf(offset, number_of_dex_registers); - return DexRegisterMap(dex_register_maps_.Subregion(offset, size)); + return DexRegisterMap(dex_register_maps_.Subregion(offset, size), + number_of_dex_registers, + *this); } size_t GetDexRegisterMapsSize(uint32_t number_of_dex_registers) const { @@ -918,11 +908,13 @@ class CodeInfo { InlineInfo inline_info, uint32_t number_of_dex_registers) const { if (!inline_info.HasDexRegisterMapAtDepth(depth)) { - return DexRegisterMap(); + return DexRegisterMap(MemoryRegion(), 0, *this); } else { uint32_t offset = inline_info.GetDexRegisterMapOffsetAtDepth(depth); size_t size = ComputeDexRegisterMapSizeOf(offset, number_of_dex_registers); - return DexRegisterMap(dex_register_maps_.Subregion(offset, size)); + return DexRegisterMap(dex_register_maps_.Subregion(offset, size), + number_of_dex_registers, + *this); } } @@ -1036,9 +1028,11 @@ class CodeInfo { // art::DexRegisterMap::GetNumberOfLiveDexRegisters and DexRegisterMap dex_register_map_without_locations( MemoryRegion(dex_register_maps_.Subregion(dex_register_map_offset, - location_mapping_data_offset_in_dex_register_map))); + location_mapping_data_offset_in_dex_register_map)), + number_of_dex_registers, + *this); size_t number_of_live_dex_registers = - dex_register_map_without_locations.GetNumberOfLiveDexRegisters(number_of_dex_registers); + dex_register_map_without_locations.GetNumberOfLiveDexRegisters(); size_t location_mapping_data_size_in_bits = DexRegisterMap::SingleEntrySizeInBits(GetNumberOfLocationCatalogEntries()) * number_of_live_dex_registers; @@ -1084,6 +1078,7 @@ class CodeInfo { BitTable<1> stack_masks_; friend class OatDumper; + friend class StackMapStream; }; #undef ELEMENT_BYTE_OFFSET_AFTER diff --git a/runtime/thread.cc b/runtime/thread.cc index 95efc8a42e..ab1a4bba6c 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -3679,8 +3679,7 @@ class ReferenceMapVisitor : public StackVisitor { REQUIRES_SHARED(Locks::mutator_lock_) { bool found = false; for (size_t dex_reg = 0; dex_reg != number_of_dex_registers; ++dex_reg) { - DexRegisterLocation location = dex_register_map.GetDexRegisterLocation( - dex_reg, number_of_dex_registers, code_info); + DexRegisterLocation location = dex_register_map.GetDexRegisterLocation(dex_reg); if (location.GetKind() == kind && static_cast(location.GetValue()) == index) { visitor(ref, dex_reg, stack_visitor); found = true; -- GitLab From 863c8808b53c36afea8fffd8ce9c38c70d59bd62 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 30 May 2018 10:13:21 -0700 Subject: [PATCH 497/749] ART: Add missing .hidden for ExecuteMterpImpl Only the arm32 version was correctly marked. Other instruction sets would generate a PLT call. Test: mmma art Test: m test-art-host Change-Id: I434f4292cd9f24371bdd2c3b34c71e8c442768b7 --- runtime/interpreter/mterp/arm64/header.S | 1 + runtime/interpreter/mterp/out/mterp_arm64.S | 1 + runtime/interpreter/mterp/out/mterp_x86.S | 3 +++ runtime/interpreter/mterp/out/mterp_x86_64.S | 3 +++ runtime/interpreter/mterp/x86/entry.S | 1 + runtime/interpreter/mterp/x86/header.S | 2 ++ runtime/interpreter/mterp/x86_64/entry.S | 1 + runtime/interpreter/mterp/x86_64/header.S | 2 ++ 8 files changed, 14 insertions(+) diff --git a/runtime/interpreter/mterp/arm64/header.S b/runtime/interpreter/mterp/arm64/header.S index 7017dd149c..0722804265 100644 --- a/runtime/interpreter/mterp/arm64/header.S +++ b/runtime/interpreter/mterp/arm64/header.S @@ -339,6 +339,7 @@ codes. */ .macro ENTRY name .type \name, #function + .hidden \name // Hide this as a global symbol, so we do not incur plt calls. .global \name /* Cache alignment for function entry */ .balign 16 diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S index d5374d2a8a..70f71ff2bc 100644 --- a/runtime/interpreter/mterp/out/mterp_arm64.S +++ b/runtime/interpreter/mterp/out/mterp_arm64.S @@ -346,6 +346,7 @@ codes. */ .macro ENTRY name .type \name, #function + .hidden \name // Hide this as a global symbol, so we do not incur plt calls. .global \name /* Cache alignment for function entry */ .balign 16 diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S index 6f4752f312..1eacfe8736 100644 --- a/runtime/interpreter/mterp/out/mterp_x86.S +++ b/runtime/interpreter/mterp/out/mterp_x86.S @@ -106,11 +106,13 @@ unspecified registers or condition codes. #define SIZE(start,end) // Mac OS' symbols have an _ prefix. #define SYMBOL(name) _ ## name + #define ASM_HIDDEN .private_extern #else #define MACRO_LITERAL(value) $value #define FUNCTION_TYPE(name) .type name, @function #define SIZE(start,end) .size start, .-end #define SYMBOL(name) name + #define ASM_HIDDEN .hidden #endif .macro PUSH _reg @@ -339,6 +341,7 @@ unspecified registers or condition codes. */ .text + ASM_HIDDEN SYMBOL(ExecuteMterpImpl) .global SYMBOL(ExecuteMterpImpl) FUNCTION_TYPE(ExecuteMterpImpl) diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S index fca2515698..ea8f483e95 100644 --- a/runtime/interpreter/mterp/out/mterp_x86_64.S +++ b/runtime/interpreter/mterp/out/mterp_x86_64.S @@ -102,11 +102,13 @@ unspecified registers or condition codes. #define SIZE(start,end) // Mac OS' symbols have an _ prefix. #define SYMBOL(name) _ ## name + #define ASM_HIDDEN .private_extern #else #define MACRO_LITERAL(value) $value #define FUNCTION_TYPE(name) .type name, @function #define SIZE(start,end) .size start, .-end #define SYMBOL(name) name + #define ASM_HIDDEN .hidden #endif .macro PUSH _reg @@ -325,6 +327,7 @@ unspecified registers or condition codes. */ .text + ASM_HIDDEN SYMBOL(ExecuteMterpImpl) .global SYMBOL(ExecuteMterpImpl) FUNCTION_TYPE(ExecuteMterpImpl) diff --git a/runtime/interpreter/mterp/x86/entry.S b/runtime/interpreter/mterp/x86/entry.S index 324637bf9a..939dc61d95 100644 --- a/runtime/interpreter/mterp/x86/entry.S +++ b/runtime/interpreter/mterp/x86/entry.S @@ -18,6 +18,7 @@ */ .text + ASM_HIDDEN SYMBOL(ExecuteMterpImpl) .global SYMBOL(ExecuteMterpImpl) FUNCTION_TYPE(ExecuteMterpImpl) diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S index 9d826c2ce2..6f31228005 100644 --- a/runtime/interpreter/mterp/x86/header.S +++ b/runtime/interpreter/mterp/x86/header.S @@ -99,11 +99,13 @@ unspecified registers or condition codes. #define SIZE(start,end) // Mac OS' symbols have an _ prefix. #define SYMBOL(name) _ ## name + #define ASM_HIDDEN .private_extern #else #define MACRO_LITERAL(value) $$value #define FUNCTION_TYPE(name) .type name, @function #define SIZE(start,end) .size start, .-end #define SYMBOL(name) name + #define ASM_HIDDEN .hidden #endif .macro PUSH _reg diff --git a/runtime/interpreter/mterp/x86_64/entry.S b/runtime/interpreter/mterp/x86_64/entry.S index 2f69226206..b08419b219 100644 --- a/runtime/interpreter/mterp/x86_64/entry.S +++ b/runtime/interpreter/mterp/x86_64/entry.S @@ -18,6 +18,7 @@ */ .text + ASM_HIDDEN SYMBOL(ExecuteMterpImpl) .global SYMBOL(ExecuteMterpImpl) FUNCTION_TYPE(ExecuteMterpImpl) diff --git a/runtime/interpreter/mterp/x86_64/header.S b/runtime/interpreter/mterp/x86_64/header.S index 55638106ed..4ebe95e987 100644 --- a/runtime/interpreter/mterp/x86_64/header.S +++ b/runtime/interpreter/mterp/x86_64/header.S @@ -95,11 +95,13 @@ unspecified registers or condition codes. #define SIZE(start,end) // Mac OS' symbols have an _ prefix. #define SYMBOL(name) _ ## name + #define ASM_HIDDEN .private_extern #else #define MACRO_LITERAL(value) $$value #define FUNCTION_TYPE(name) .type name, @function #define SIZE(start,end) .size start, .-end #define SYMBOL(name) name + #define ASM_HIDDEN .hidden #endif .macro PUSH _reg -- GitLab From b7ae0b1a9874c65682c22883061ec8e7fae1fea3 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 29 May 2018 09:25:57 -0700 Subject: [PATCH 498/749] Add experiments for Dex bytecode analysis Count how many move-result instructions are directly following invokes with less than 5 args. Bug: 77721545 Test: test-art-host Change-Id: Icd3be78260b9117660d734f50303a8e3bc030325 --- tools/dexanalyze/dexanalyze.cc | 4 + tools/dexanalyze/dexanalyze_experiments.cc | 153 +++++++++++++++++---- tools/dexanalyze/dexanalyze_experiments.h | 23 +++- 3 files changed, 147 insertions(+), 33 deletions(-) diff --git a/tools/dexanalyze/dexanalyze.cc b/tools/dexanalyze/dexanalyze.cc index 083de7066d..38725d428b 100644 --- a/tools/dexanalyze/dexanalyze.cc +++ b/tools/dexanalyze/dexanalyze.cc @@ -88,6 +88,7 @@ class DexAnalyze { bool run_dex_file_verifier_ = true; bool dump_per_input_dex_ = false; bool exp_count_indices_ = false; + bool exp_code_metrics_ = false; bool exp_analyze_strings_ = false; bool run_all_experiments_ = false; std::vector filenames_; @@ -102,6 +103,9 @@ class DexAnalyze { if (options->run_all_experiments_ || options->exp_analyze_strings_) { experiments_.emplace_back(new AnalyzeStrings); } + if (options->run_all_experiments_ || options->exp_code_metrics_) { + experiments_.emplace_back(new CodeMetrics); + } } bool ProcessDexFile(const DexFile& dex_file) { diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc index 0f20a99f05..7006370c0b 100644 --- a/tools/dexanalyze/dexanalyze_experiments.cc +++ b/tools/dexanalyze/dexanalyze_experiments.cc @@ -32,13 +32,41 @@ namespace art { +static inline bool IsRange(Instruction::Code code) { + return code == Instruction::INVOKE_VIRTUAL_RANGE || + code == Instruction::INVOKE_DIRECT_RANGE || + code == Instruction::INVOKE_SUPER_RANGE || + code == Instruction::INVOKE_STATIC_RANGE || + code == Instruction::INVOKE_INTERFACE_RANGE; +} + +static inline uint16_t NumberOfArgs(const Instruction& inst) { + return IsRange(inst.Opcode()) ? inst.VRegA_3rc() : inst.VRegA_35c(); +} + +static inline uint16_t DexMethodIndex(const Instruction& inst) { + return IsRange(inst.Opcode()) ? inst.VRegB_3rc() : inst.VRegB_35c(); +} + std::string Percent(uint64_t value, uint64_t max) { if (max == 0) { - ++max; + return "0"; } - return android::base::StringPrintf("%" PRId64 "(%.2f%%)", - value, - static_cast(value * 100) / static_cast(max)); + return android::base::StringPrintf( + "%" PRId64 "(%.2f%%)", + value, + static_cast(value * 100) / static_cast(max)); +} + +std::string PercentDivide(uint64_t value, uint64_t max) { + if (max == 0) { + return "0"; + } + return android::base::StringPrintf( + "%" PRId64 "/%" PRId64 "(%.2f%%)", + value, + max, + static_cast(value * 100) / static_cast(max)); } static size_t PrefixLen(const std::string& a, const std::string& b) { @@ -150,38 +178,52 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { // Invoke cases. case Instruction::INVOKE_VIRTUAL: case Instruction::INVOKE_VIRTUAL_RANGE: { - bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE); - uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); + uint32_t method_idx = DexMethodIndex(inst.Inst()); if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) { ++same_class_virtual_; - } else { - ++other_class_virtual_; - unique_method_ids.insert(method_idx); } + ++total_virtual_; + unique_method_ids.insert(method_idx); break; } case Instruction::INVOKE_DIRECT: case Instruction::INVOKE_DIRECT_RANGE: { - bool is_range = (inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE); - uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + uint32_t method_idx = DexMethodIndex(inst.Inst()); if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) { ++same_class_direct_; - } else { - ++other_class_direct_; - unique_method_ids.insert(method_idx); } + ++total_direct_; + unique_method_ids.insert(method_idx); break; } case Instruction::INVOKE_STATIC: case Instruction::INVOKE_STATIC_RANGE: { - bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE); - uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + uint32_t method_idx = DexMethodIndex(inst.Inst()); if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) { ++same_class_static_; - } else { - ++other_class_static_; - unique_method_ids.insert(method_idx); } + ++total_static_; + unique_method_ids.insert(method_idx); + break; + } + case Instruction::INVOKE_INTERFACE: + case Instruction::INVOKE_INTERFACE_RANGE: { + uint32_t method_idx = DexMethodIndex(inst.Inst()); + if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) { + ++same_class_interface_; + } + ++total_interface_; + unique_method_ids.insert(method_idx); + break; + } + case Instruction::INVOKE_SUPER: + case Instruction::INVOKE_SUPER_RANGE: { + uint32_t method_idx = DexMethodIndex(inst.Inst()); + if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) { + ++same_class_super_; + } + ++total_super_; + unique_method_ids.insert(method_idx); break; } default: @@ -201,24 +243,75 @@ void CountDexIndices::Dump(std::ostream& os, uint64_t total_size) const { os << "Num field ids: " << num_field_ids_ << "\n"; os << "Num type ids: " << num_type_ids_ << "\n"; os << "Num class defs: " << num_class_defs_ << "\n"; - os << "Same class direct: " << same_class_direct_ << "\n"; - os << "Other class direct: " << other_class_direct_ << "\n"; - os << "Same class virtual: " << same_class_virtual_ << "\n"; - os << "Other class virtual: " << other_class_virtual_ << "\n"; - os << "Same class static: " << same_class_static_ << "\n"; - os << "Other class static: " << other_class_static_ << "\n"; + os << "Direct same class: " << PercentDivide(same_class_direct_, total_direct_) << "\n"; + os << "Virtual same class: " << PercentDivide(same_class_virtual_, total_virtual_) << "\n"; + os << "Static same class: " << PercentDivide(same_class_static_, total_static_) << "\n"; + os << "Interface same class: " << PercentDivide(same_class_interface_, total_interface_) << "\n"; + os << "Super same class: " << PercentDivide(same_class_super_, total_super_) << "\n"; os << "Num strings accessed from code: " << num_string_ids_from_code_ << "\n"; os << "Unique(per class) method ids accessed from code: " << total_unique_method_idx_ << "\n"; os << "Unique(per class) string ids accessed from code: " << total_unique_string_ids_ << "\n"; - size_t same_class_total = same_class_direct_ + same_class_virtual_ + same_class_static_; - size_t other_class_total = other_class_direct_ + other_class_virtual_ + other_class_static_; - os << "Same class invoke: " << same_class_total << "\n"; - os << "Other class invoke: " << other_class_total << "\n"; + const size_t same_class_total = + same_class_direct_ + + same_class_virtual_ + + same_class_static_ + + same_class_interface_ + + same_class_super_; + const size_t other_class_total = + total_direct_ + + total_virtual_ + + total_static_ + + total_interface_ + + total_super_; + os << "Same class invokes: " << PercentDivide(same_class_total, other_class_total) << "\n"; os << "Invokes from code: " << (same_class_total + other_class_total) << "\n"; os << "Total Dex code bytes: " << Percent(dex_code_bytes_, total_size) << "\n"; os << "Total unique code items: " << total_unique_code_items_ << "\n"; os << "Total Dex size: " << total_size << "\n"; } -} // namespace art +void CodeMetrics::ProcessDexFile(const DexFile& dex_file) { + for (ClassAccessor accessor : dex_file.GetClasses()) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + bool space_for_out_arg = false; + for (const DexInstructionPcPair& inst : method.GetInstructions()) { + switch (inst->Opcode()) { + case Instruction::INVOKE_VIRTUAL: + case Instruction::INVOKE_DIRECT: + case Instruction::INVOKE_SUPER: + case Instruction::INVOKE_INTERFACE: + case Instruction::INVOKE_STATIC: { + const uint32_t args = NumberOfArgs(inst.Inst()); + CHECK_LT(args, kMaxArgCount); + ++arg_counts_[args]; + space_for_out_arg = args < kMaxArgCount - 1; + break; + } + case Instruction::MOVE_RESULT: + case Instruction::MOVE_RESULT_OBJECT: { + if (space_for_out_arg) { + move_result_savings_ += inst->SizeInCodeUnits() * 2; + } + break; + } + default: + space_for_out_arg = false; + break; + } + } + } + } +} +void CodeMetrics::Dump(std::ostream& os, uint64_t total_size) const { + const uint64_t total = std::accumulate(arg_counts_, arg_counts_ + kMaxArgCount, 0u); + for (size_t i = 0; i < kMaxArgCount; ++i) { + os << "args=" << i << ": " << Percent(arg_counts_[i], total) << "\n"; + } + os << "Move result savings: " << Percent(move_result_savings_, total_size) << "\n"; + os << "One byte invoke savings: " << Percent(total, total_size) << "\n"; + const uint64_t low_arg_total = std::accumulate(arg_counts_, arg_counts_ + 3, 0u); + os << "Low arg savings: " << Percent(low_arg_total * 2, total_size) << "\n"; +} + +} // namespace art diff --git a/tools/dexanalyze/dexanalyze_experiments.h b/tools/dexanalyze/dexanalyze_experiments.h index c84b082955..7ba2a49372 100644 --- a/tools/dexanalyze/dexanalyze_experiments.h +++ b/tools/dexanalyze/dexanalyze_experiments.h @@ -75,11 +75,28 @@ class CountDexIndices : public Experiment { // Invokes size_t same_class_direct_ = 0; - size_t other_class_direct_ = 0; + size_t total_direct_ = 0; size_t same_class_virtual_ = 0; - size_t other_class_virtual_ = 0; + size_t total_virtual_ = 0; size_t same_class_static_ = 0; - size_t other_class_static_ = 0; + size_t total_static_ = 0; + size_t same_class_interface_ = 0; + size_t total_interface_ = 0; + size_t same_class_super_ = 0; + size_t total_super_ = 0; +}; + +// Measure various code metrics including args per invoke-virtual, fill/spill move paterns. +class CodeMetrics : public Experiment { + public: + void ProcessDexFile(const DexFile& dex_file); + + void Dump(std::ostream& os, uint64_t total_size) const; + + private: + static constexpr size_t kMaxArgCount = 6; + uint64_t arg_counts_[kMaxArgCount] = {}; + uint64_t move_result_savings_ = 0u; }; } // namespace art -- GitLab From d775f96090a588efbc837961434b10c57bcf189c Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Wed, 30 May 2018 18:12:52 +0100 Subject: [PATCH 499/749] Ensure all PCs in stack_map_test are aligned. It is invalid to try to encode improperly aligned PC. Test: test-art-target-gtest-stack_map_test Change-Id: I73e7b6225bfee87b0d6161298e19648ee6e1d499 --- compiler/optimizing/stack_map_test.cc | 100 +++++++++++++------------- runtime/stack_map.h | 2 +- 2 files changed, 52 insertions(+), 50 deletions(-) diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index 112771847c..7178e6683f 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -45,6 +45,8 @@ static bool CheckStackMask( using Kind = DexRegisterLocation::Kind; +constexpr static uint32_t kPcAlign = GetInstructionSetInstructionAlignment(kRuntimeISA); + TEST(StackMapTest, Test1) { MallocArenaPool pool; ArenaStack arena_stack(&pool); @@ -53,7 +55,7 @@ TEST(StackMapTest, Test1) { ArenaBitVector sp_mask(&allocator, 0, false); size_t number_of_dex_registers = 2; - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask, number_of_dex_registers, 0); stream.AddDexRegisterEntry(Kind::kInStack, 0); // Short location. stream.AddDexRegisterEntry(Kind::kConstant, -2); // Short location. stream.EndStackMapEntry(); @@ -77,9 +79,9 @@ TEST(StackMapTest, Test1) { StackMap stack_map = code_info.GetStackMapAt(0); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64 * kPcAlign))); ASSERT_EQ(0u, stack_map.GetDexPc()); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(64u * kPcAlign, stack_map.GetNativePcOffset(kRuntimeISA)); ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map)); ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask)); @@ -131,7 +133,7 @@ TEST(StackMapTest, Test2) { sp_mask1.SetBit(4); size_t number_of_dex_registers = 2; size_t number_of_dex_registers_in_inline_info = 0; - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 2); + stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask1, number_of_dex_registers, 2); stream.AddDexRegisterEntry(Kind::kInStack, 0); // Short location. stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. stream.BeginInlineInfoEntry(&art_method, 3, number_of_dex_registers_in_inline_info); @@ -143,7 +145,7 @@ TEST(StackMapTest, Test2) { ArenaBitVector sp_mask2(&allocator, 0, true); sp_mask2.SetBit(3); sp_mask2.SetBit(8); - stream.BeginStackMapEntry(1, 128, 0xFF, &sp_mask2, number_of_dex_registers, 0); + stream.BeginStackMapEntry(1, 128 * kPcAlign, 0xFF, &sp_mask2, number_of_dex_registers, 0); stream.AddDexRegisterEntry(Kind::kInRegister, 18); // Short location. stream.AddDexRegisterEntry(Kind::kInFpuRegister, 3); // Short location. stream.EndStackMapEntry(); @@ -151,7 +153,7 @@ TEST(StackMapTest, Test2) { ArenaBitVector sp_mask3(&allocator, 0, true); sp_mask3.SetBit(1); sp_mask3.SetBit(5); - stream.BeginStackMapEntry(2, 192, 0xAB, &sp_mask3, number_of_dex_registers, 0); + stream.BeginStackMapEntry(2, 192 * kPcAlign, 0xAB, &sp_mask3, number_of_dex_registers, 0); stream.AddDexRegisterEntry(Kind::kInRegister, 6); // Short location. stream.AddDexRegisterEntry(Kind::kInRegisterHigh, 8); // Short location. stream.EndStackMapEntry(); @@ -159,7 +161,7 @@ TEST(StackMapTest, Test2) { ArenaBitVector sp_mask4(&allocator, 0, true); sp_mask4.SetBit(6); sp_mask4.SetBit(7); - stream.BeginStackMapEntry(3, 256, 0xCD, &sp_mask4, number_of_dex_registers, 0); + stream.BeginStackMapEntry(3, 256 * kPcAlign, 0xCD, &sp_mask4, number_of_dex_registers, 0); stream.AddDexRegisterEntry(Kind::kInFpuRegister, 3); // Short location, same in stack map 2. stream.AddDexRegisterEntry(Kind::kInFpuRegisterHigh, 1); // Short location. stream.EndStackMapEntry(); @@ -185,9 +187,9 @@ TEST(StackMapTest, Test2) { { StackMap stack_map = code_info.GetStackMapAt(0); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64 * kPcAlign))); ASSERT_EQ(0u, stack_map.GetDexPc()); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(64u * kPcAlign, stack_map.GetNativePcOffset(kRuntimeISA)); ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map)); ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask1)); @@ -237,9 +239,9 @@ TEST(StackMapTest, Test2) { { StackMap stack_map = code_info.GetStackMapAt(1); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u * kPcAlign))); ASSERT_EQ(1u, stack_map.GetDexPc()); - ASSERT_EQ(128u, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(128u * kPcAlign, stack_map.GetNativePcOffset(kRuntimeISA)); ASSERT_EQ(0xFFu, code_info.GetRegisterMaskOf(stack_map)); ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask2)); @@ -283,9 +285,9 @@ TEST(StackMapTest, Test2) { { StackMap stack_map = code_info.GetStackMapAt(2); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(2u))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(192u))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(192u * kPcAlign))); ASSERT_EQ(2u, stack_map.GetDexPc()); - ASSERT_EQ(192u, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(192u * kPcAlign, stack_map.GetNativePcOffset(kRuntimeISA)); ASSERT_EQ(0xABu, code_info.GetRegisterMaskOf(stack_map)); ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask3)); @@ -329,9 +331,9 @@ TEST(StackMapTest, Test2) { { StackMap stack_map = code_info.GetStackMapAt(3); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(3u))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(256u))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(256u * kPcAlign))); ASSERT_EQ(3u, stack_map.GetDexPc()); - ASSERT_EQ(256u, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(256u * kPcAlign, stack_map.GetNativePcOffset(kRuntimeISA)); ASSERT_EQ(0xCDu, code_info.GetRegisterMaskOf(stack_map)); ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask4)); @@ -384,7 +386,7 @@ TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { sp_mask1.SetBit(4); const size_t number_of_dex_registers = 2; const size_t number_of_dex_registers_in_inline_info = 2; - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 1); + stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask1, number_of_dex_registers, 1); stream.AddDexRegisterEntry(Kind::kInStack, 0); // Short location. stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. stream.BeginInlineInfoEntry(&art_method, 3, number_of_dex_registers_in_inline_info); @@ -414,9 +416,9 @@ TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { { StackMap stack_map = code_info.GetStackMapAt(0); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64 * kPcAlign))); ASSERT_EQ(0u, stack_map.GetDexPc()); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(64u * kPcAlign, stack_map.GetNativePcOffset(kRuntimeISA)); ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map)); ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask1)); @@ -469,7 +471,7 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { ArenaBitVector sp_mask(&allocator, 0, false); uint32_t number_of_dex_registers = 2; - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask, number_of_dex_registers, 0); stream.AddDexRegisterEntry(Kind::kNone, 0); // No location. stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. stream.EndStackMapEntry(); @@ -492,9 +494,9 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { StackMap stack_map = code_info.GetStackMapAt(0); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64 * kPcAlign))); ASSERT_EQ(0u, stack_map.GetDexPc()); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(64u * kPcAlign, stack_map.GetNativePcOffset(kRuntimeISA)); ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map)); ASSERT_TRUE(stack_map.HasDexRegisterMap()); @@ -543,7 +545,7 @@ TEST(StackMapTest, DexRegisterMapOffsetOverflow) { ArenaBitVector sp_mask(&allocator, 0, false); uint32_t number_of_dex_registers = 1024; // Create the first stack map (and its Dex register map). - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask, number_of_dex_registers, 0); uint32_t number_of_dex_live_registers_in_dex_register_map_0 = number_of_dex_registers - 8; for (uint32_t i = 0; i < number_of_dex_live_registers_in_dex_register_map_0; ++i) { // Use two different Dex register locations to populate this map, @@ -554,7 +556,7 @@ TEST(StackMapTest, DexRegisterMapOffsetOverflow) { } stream.EndStackMapEntry(); // Create the second stack map (and its Dex register map). - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask, number_of_dex_registers, 0); for (uint32_t i = 0; i < number_of_dex_registers; ++i) { stream.AddDexRegisterEntry(Kind::kConstant, 0); // Short location. } @@ -605,17 +607,17 @@ TEST(StackMapTest, TestShareDexRegisterMap) { ArenaBitVector sp_mask(&allocator, 0, false); uint32_t number_of_dex_registers = 2; // First stack map. - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask, number_of_dex_registers, 0); stream.AddDexRegisterEntry(Kind::kInRegister, 0); // Short location. stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. stream.EndStackMapEntry(); // Second stack map, which should share the same dex register map. - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask, number_of_dex_registers, 0); stream.AddDexRegisterEntry(Kind::kInRegister, 0); // Short location. stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. stream.EndStackMapEntry(); // Third stack map (doesn't share the dex register map). - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask, number_of_dex_registers, 0); stream.AddDexRegisterEntry(Kind::kInRegister, 2); // Short location. stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. stream.EndStackMapEntry(); @@ -662,11 +664,11 @@ TEST(StackMapTest, TestNoDexRegisterMap) { ArenaBitVector sp_mask(&allocator, 0, false); uint32_t number_of_dex_registers = 0; - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask, number_of_dex_registers, 0); stream.EndStackMapEntry(); number_of_dex_registers = 1; - stream.BeginStackMapEntry(1, 68, 0x4, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(1, 68 * kPcAlign, 0x4, &sp_mask, number_of_dex_registers, 0); stream.EndStackMapEntry(); size_t size = stream.PrepareForFillIn(); @@ -684,9 +686,9 @@ TEST(StackMapTest, TestNoDexRegisterMap) { StackMap stack_map = code_info.GetStackMapAt(0); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64 * kPcAlign))); ASSERT_EQ(0u, stack_map.GetDexPc()); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(64u * kPcAlign, stack_map.GetNativePcOffset(kRuntimeISA)); ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map)); ASSERT_FALSE(stack_map.HasDexRegisterMap()); @@ -694,9 +696,9 @@ TEST(StackMapTest, TestNoDexRegisterMap) { stack_map = code_info.GetStackMapAt(1); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(68))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(68 * kPcAlign))); ASSERT_EQ(1u, stack_map.GetDexPc()); - ASSERT_EQ(68u, stack_map.GetNativePcOffset(kRuntimeISA)); + ASSERT_EQ(68u * kPcAlign, stack_map.GetNativePcOffset(kRuntimeISA)); ASSERT_EQ(0x4u, code_info.GetRegisterMaskOf(stack_map)); ASSERT_FALSE(stack_map.HasDexRegisterMap()); @@ -715,7 +717,7 @@ TEST(StackMapTest, InlineTest) { sp_mask1.SetBit(4); // First stack map. - stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask1, 2, 2); + stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask1, 2, 2); stream.AddDexRegisterEntry(Kind::kInStack, 0); stream.AddDexRegisterEntry(Kind::kConstant, 4); @@ -731,7 +733,7 @@ TEST(StackMapTest, InlineTest) { stream.EndStackMapEntry(); // Second stack map. - stream.BeginStackMapEntry(2, 22, 0x3, &sp_mask1, 2, 3); + stream.BeginStackMapEntry(2, 22 * kPcAlign, 0x3, &sp_mask1, 2, 3); stream.AddDexRegisterEntry(Kind::kInStack, 56); stream.AddDexRegisterEntry(Kind::kConstant, 0); @@ -749,13 +751,13 @@ TEST(StackMapTest, InlineTest) { stream.EndStackMapEntry(); // Third stack map. - stream.BeginStackMapEntry(4, 56, 0x3, &sp_mask1, 2, 0); + stream.BeginStackMapEntry(4, 56 * kPcAlign, 0x3, &sp_mask1, 2, 0); stream.AddDexRegisterEntry(Kind::kNone, 0); stream.AddDexRegisterEntry(Kind::kConstant, 4); stream.EndStackMapEntry(); // Fourth stack map. - stream.BeginStackMapEntry(6, 78, 0x3, &sp_mask1, 2, 3); + stream.BeginStackMapEntry(6, 78 * kPcAlign, 0x3, &sp_mask1, 2, 3); stream.AddDexRegisterEntry(Kind::kInStack, 56); stream.AddDexRegisterEntry(Kind::kConstant, 0); @@ -904,9 +906,9 @@ TEST(StackMapTest, TestDeduplicateStackMask) { ArenaBitVector sp_mask(&allocator, 0, true); sp_mask.SetBit(1); sp_mask.SetBit(4); - stream.BeginStackMapEntry(0, 4, 0x3, &sp_mask, 0, 0); + stream.BeginStackMapEntry(0, 4 * kPcAlign, 0x3, &sp_mask, 0, 0); stream.EndStackMapEntry(); - stream.BeginStackMapEntry(0, 8, 0x3, &sp_mask, 0, 0); + stream.BeginStackMapEntry(0, 8 * kPcAlign, 0x3, &sp_mask, 0, 0); stream.EndStackMapEntry(); size_t size = stream.PrepareForFillIn(); @@ -917,8 +919,8 @@ TEST(StackMapTest, TestDeduplicateStackMask) { CodeInfo code_info(region); ASSERT_EQ(2u, code_info.GetNumberOfStackMaps()); - StackMap stack_map1 = code_info.GetStackMapForNativePcOffset(4); - StackMap stack_map2 = code_info.GetStackMapForNativePcOffset(8); + StackMap stack_map1 = code_info.GetStackMapForNativePcOffset(4 * kPcAlign); + StackMap stack_map2 = code_info.GetStackMapForNativePcOffset(8 * kPcAlign); EXPECT_EQ(stack_map1.GetStackMaskIndex(), stack_map2.GetStackMaskIndex()); } @@ -931,13 +933,13 @@ TEST(StackMapTest, TestInvokeInfo) { ArenaBitVector sp_mask(&allocator, 0, true); sp_mask.SetBit(1); - stream.BeginStackMapEntry(0, 4, 0x3, &sp_mask, 0, 0); + stream.BeginStackMapEntry(0, 4 * kPcAlign, 0x3, &sp_mask, 0, 0); stream.AddInvoke(kSuper, 1); stream.EndStackMapEntry(); - stream.BeginStackMapEntry(0, 8, 0x3, &sp_mask, 0, 0); + stream.BeginStackMapEntry(0, 8 * kPcAlign, 0x3, &sp_mask, 0, 0); stream.AddInvoke(kStatic, 3); stream.EndStackMapEntry(); - stream.BeginStackMapEntry(0, 16, 0x3, &sp_mask, 0, 0); + stream.BeginStackMapEntry(0, 16 * kPcAlign, 0x3, &sp_mask, 0, 0); stream.AddInvoke(kDirect, 65535); stream.EndStackMapEntry(); @@ -954,9 +956,9 @@ TEST(StackMapTest, TestInvokeInfo) { MethodInfo method_info(method_info_region.begin()); ASSERT_EQ(3u, code_info.GetNumberOfStackMaps()); - InvokeInfo invoke1(code_info.GetInvokeInfoForNativePcOffset(4)); - InvokeInfo invoke2(code_info.GetInvokeInfoForNativePcOffset(8)); - InvokeInfo invoke3(code_info.GetInvokeInfoForNativePcOffset(16)); + InvokeInfo invoke1(code_info.GetInvokeInfoForNativePcOffset(4 * kPcAlign)); + InvokeInfo invoke2(code_info.GetInvokeInfoForNativePcOffset(8 * kPcAlign)); + InvokeInfo invoke3(code_info.GetInvokeInfoForNativePcOffset(16 * kPcAlign)); InvokeInfo invoke_invalid(code_info.GetInvokeInfoForNativePcOffset(12)); EXPECT_FALSE(invoke_invalid.IsValid()); // No entry for that index. EXPECT_TRUE(invoke1.IsValid()); @@ -964,13 +966,13 @@ TEST(StackMapTest, TestInvokeInfo) { EXPECT_TRUE(invoke3.IsValid()); EXPECT_EQ(invoke1.GetInvokeType(), kSuper); EXPECT_EQ(invoke1.GetMethodIndex(method_info), 1u); - EXPECT_EQ(invoke1.GetNativePcOffset(kRuntimeISA), 4u); + EXPECT_EQ(invoke1.GetNativePcOffset(kRuntimeISA), 4u * kPcAlign); EXPECT_EQ(invoke2.GetInvokeType(), kStatic); EXPECT_EQ(invoke2.GetMethodIndex(method_info), 3u); - EXPECT_EQ(invoke2.GetNativePcOffset(kRuntimeISA), 8u); + EXPECT_EQ(invoke2.GetNativePcOffset(kRuntimeISA), 8u * kPcAlign); EXPECT_EQ(invoke3.GetInvokeType(), kDirect); EXPECT_EQ(invoke3.GetMethodIndex(method_info), 65535u); - EXPECT_EQ(invoke3.GetNativePcOffset(kRuntimeISA), 16u); + EXPECT_EQ(invoke3.GetNativePcOffset(kRuntimeISA), 16u * kPcAlign); } } // namespace art diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 9d66b3181c..c558846bb3 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -675,7 +675,7 @@ class StackMap : public BitTable<6>::Accessor { uint32_t GetStackMaskIndex() const { return Get(); } static uint32_t PackNativePc(uint32_t native_pc, InstructionSet isa) { - // TODO: DCHECK_ALIGNED_PARAM(native_pc, GetInstructionSetInstructionAlignment(isa)); + DCHECK_ALIGNED_PARAM(native_pc, GetInstructionSetInstructionAlignment(isa)); return native_pc / GetInstructionSetInstructionAlignment(isa); } -- GitLab From ecec8284f56d01f360762402d572740d88c244c6 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 30 May 2018 12:04:26 -0700 Subject: [PATCH 500/749] ART: Refactor mterp slightly Move marker labels to assembly to allow simpler machine-dependent changes. Update the generator script. Regenerate the output. Test: mmma art Test: m test-art-host Change-Id: Id32e060af9b2ef3ba5558a89e05ca0edfe4974c6 --- .../interpreter/mterp/arm/instruction_end.S | 3 ++ .../mterp/arm/instruction_end_alt.S | 3 ++ .../mterp/arm/instruction_end_sister.S | 3 ++ .../interpreter/mterp/arm/instruction_start.S | 4 ++ .../mterp/arm/instruction_start_alt.S | 4 ++ .../mterp/arm/instruction_start_sister.S | 5 +++ .../interpreter/mterp/arm64/instruction_end.S | 3 ++ .../mterp/arm64/instruction_end_alt.S | 3 ++ .../mterp/arm64/instruction_end_sister.S | 3 ++ .../mterp/arm64/instruction_start.S | 4 ++ .../mterp/arm64/instruction_start_alt.S | 4 ++ .../mterp/arm64/instruction_start_sister.S | 5 +++ runtime/interpreter/mterp/gen_mterp.py | 38 ++++++++----------- .../interpreter/mterp/mips/instruction_end.S | 3 ++ .../mterp/mips/instruction_end_alt.S | 3 ++ .../mterp/mips/instruction_end_sister.S | 3 ++ .../mterp/mips/instruction_start.S | 4 ++ .../mterp/mips/instruction_start_alt.S | 4 ++ .../mterp/mips/instruction_start_sister.S | 5 +++ .../mterp/mips64/instruction_end.S | 3 ++ .../mterp/mips64/instruction_end_alt.S | 3 ++ .../mterp/mips64/instruction_end_sister.S | 3 ++ .../mterp/mips64/instruction_start.S | 4 ++ .../mterp/mips64/instruction_start_alt.S | 4 ++ .../mterp/mips64/instruction_start_sister.S | 5 +++ runtime/interpreter/mterp/out/mterp_arm.S | 15 +++++++- runtime/interpreter/mterp/out/mterp_arm64.S | 15 +++++++- runtime/interpreter/mterp/out/mterp_mips.S | 15 +++++++- runtime/interpreter/mterp/out/mterp_mips64.S | 15 +++++++- runtime/interpreter/mterp/out/mterp_x86.S | 15 +++++++- runtime/interpreter/mterp/out/mterp_x86_64.S | 15 +++++++- .../interpreter/mterp/x86/instruction_end.S | 3 ++ .../mterp/x86/instruction_end_alt.S | 3 ++ .../mterp/x86/instruction_end_sister.S | 3 ++ .../interpreter/mterp/x86/instruction_start.S | 4 ++ .../mterp/x86/instruction_start_alt.S | 4 ++ .../mterp/x86/instruction_start_sister.S | 5 +++ .../mterp/x86_64/instruction_end.S | 3 ++ .../mterp/x86_64/instruction_end_alt.S | 3 ++ .../mterp/x86_64/instruction_end_sister.S | 3 ++ .../mterp/x86_64/instruction_start.S | 4 ++ .../mterp/x86_64/instruction_start_alt.S | 4 ++ .../mterp/x86_64/instruction_start_sister.S | 5 +++ 43 files changed, 232 insertions(+), 28 deletions(-) create mode 100644 runtime/interpreter/mterp/arm/instruction_end.S create mode 100644 runtime/interpreter/mterp/arm/instruction_end_alt.S create mode 100644 runtime/interpreter/mterp/arm/instruction_end_sister.S create mode 100644 runtime/interpreter/mterp/arm/instruction_start.S create mode 100644 runtime/interpreter/mterp/arm/instruction_start_alt.S create mode 100644 runtime/interpreter/mterp/arm/instruction_start_sister.S create mode 100644 runtime/interpreter/mterp/arm64/instruction_end.S create mode 100644 runtime/interpreter/mterp/arm64/instruction_end_alt.S create mode 100644 runtime/interpreter/mterp/arm64/instruction_end_sister.S create mode 100644 runtime/interpreter/mterp/arm64/instruction_start.S create mode 100644 runtime/interpreter/mterp/arm64/instruction_start_alt.S create mode 100644 runtime/interpreter/mterp/arm64/instruction_start_sister.S create mode 100644 runtime/interpreter/mterp/mips/instruction_end.S create mode 100644 runtime/interpreter/mterp/mips/instruction_end_alt.S create mode 100644 runtime/interpreter/mterp/mips/instruction_end_sister.S create mode 100644 runtime/interpreter/mterp/mips/instruction_start.S create mode 100644 runtime/interpreter/mterp/mips/instruction_start_alt.S create mode 100644 runtime/interpreter/mterp/mips/instruction_start_sister.S create mode 100644 runtime/interpreter/mterp/mips64/instruction_end.S create mode 100644 runtime/interpreter/mterp/mips64/instruction_end_alt.S create mode 100644 runtime/interpreter/mterp/mips64/instruction_end_sister.S create mode 100644 runtime/interpreter/mterp/mips64/instruction_start.S create mode 100644 runtime/interpreter/mterp/mips64/instruction_start_alt.S create mode 100644 runtime/interpreter/mterp/mips64/instruction_start_sister.S create mode 100644 runtime/interpreter/mterp/x86/instruction_end.S create mode 100644 runtime/interpreter/mterp/x86/instruction_end_alt.S create mode 100644 runtime/interpreter/mterp/x86/instruction_end_sister.S create mode 100644 runtime/interpreter/mterp/x86/instruction_start.S create mode 100644 runtime/interpreter/mterp/x86/instruction_start_alt.S create mode 100644 runtime/interpreter/mterp/x86/instruction_start_sister.S create mode 100644 runtime/interpreter/mterp/x86_64/instruction_end.S create mode 100644 runtime/interpreter/mterp/x86_64/instruction_end_alt.S create mode 100644 runtime/interpreter/mterp/x86_64/instruction_end_sister.S create mode 100644 runtime/interpreter/mterp/x86_64/instruction_start.S create mode 100644 runtime/interpreter/mterp/x86_64/instruction_start_alt.S create mode 100644 runtime/interpreter/mterp/x86_64/instruction_start_sister.S diff --git a/runtime/interpreter/mterp/arm/instruction_end.S b/runtime/interpreter/mterp/arm/instruction_end.S new file mode 100644 index 0000000000..32c725c7d9 --- /dev/null +++ b/runtime/interpreter/mterp/arm/instruction_end.S @@ -0,0 +1,3 @@ + + .global artMterpAsmInstructionEnd +artMterpAsmInstructionEnd: diff --git a/runtime/interpreter/mterp/arm/instruction_end_alt.S b/runtime/interpreter/mterp/arm/instruction_end_alt.S new file mode 100644 index 0000000000..f90916fc02 --- /dev/null +++ b/runtime/interpreter/mterp/arm/instruction_end_alt.S @@ -0,0 +1,3 @@ + + .global artMterpAsmAltInstructionEnd +artMterpAsmAltInstructionEnd: diff --git a/runtime/interpreter/mterp/arm/instruction_end_sister.S b/runtime/interpreter/mterp/arm/instruction_end_sister.S new file mode 100644 index 0000000000..c5f4886697 --- /dev/null +++ b/runtime/interpreter/mterp/arm/instruction_end_sister.S @@ -0,0 +1,3 @@ + + .global artMterpAsmSisterEnd +artMterpAsmSisterEnd: diff --git a/runtime/interpreter/mterp/arm/instruction_start.S b/runtime/interpreter/mterp/arm/instruction_start.S new file mode 100644 index 0000000000..8874c20540 --- /dev/null +++ b/runtime/interpreter/mterp/arm/instruction_start.S @@ -0,0 +1,4 @@ + + .global artMterpAsmInstructionStart +artMterpAsmInstructionStart = .L_op_nop + .text diff --git a/runtime/interpreter/mterp/arm/instruction_start_alt.S b/runtime/interpreter/mterp/arm/instruction_start_alt.S new file mode 100644 index 0000000000..0c9ffdb7d6 --- /dev/null +++ b/runtime/interpreter/mterp/arm/instruction_start_alt.S @@ -0,0 +1,4 @@ + + .global artMterpAsmAltInstructionStart +artMterpAsmAltInstructionStart = .L_ALT_op_nop + .text diff --git a/runtime/interpreter/mterp/arm/instruction_start_sister.S b/runtime/interpreter/mterp/arm/instruction_start_sister.S new file mode 100644 index 0000000000..2ec51f7261 --- /dev/null +++ b/runtime/interpreter/mterp/arm/instruction_start_sister.S @@ -0,0 +1,5 @@ + + .global artMterpAsmSisterStart + .text + .balign 4 +artMterpAsmSisterStart: diff --git a/runtime/interpreter/mterp/arm64/instruction_end.S b/runtime/interpreter/mterp/arm64/instruction_end.S new file mode 100644 index 0000000000..32c725c7d9 --- /dev/null +++ b/runtime/interpreter/mterp/arm64/instruction_end.S @@ -0,0 +1,3 @@ + + .global artMterpAsmInstructionEnd +artMterpAsmInstructionEnd: diff --git a/runtime/interpreter/mterp/arm64/instruction_end_alt.S b/runtime/interpreter/mterp/arm64/instruction_end_alt.S new file mode 100644 index 0000000000..f90916fc02 --- /dev/null +++ b/runtime/interpreter/mterp/arm64/instruction_end_alt.S @@ -0,0 +1,3 @@ + + .global artMterpAsmAltInstructionEnd +artMterpAsmAltInstructionEnd: diff --git a/runtime/interpreter/mterp/arm64/instruction_end_sister.S b/runtime/interpreter/mterp/arm64/instruction_end_sister.S new file mode 100644 index 0000000000..c5f4886697 --- /dev/null +++ b/runtime/interpreter/mterp/arm64/instruction_end_sister.S @@ -0,0 +1,3 @@ + + .global artMterpAsmSisterEnd +artMterpAsmSisterEnd: diff --git a/runtime/interpreter/mterp/arm64/instruction_start.S b/runtime/interpreter/mterp/arm64/instruction_start.S new file mode 100644 index 0000000000..8874c20540 --- /dev/null +++ b/runtime/interpreter/mterp/arm64/instruction_start.S @@ -0,0 +1,4 @@ + + .global artMterpAsmInstructionStart +artMterpAsmInstructionStart = .L_op_nop + .text diff --git a/runtime/interpreter/mterp/arm64/instruction_start_alt.S b/runtime/interpreter/mterp/arm64/instruction_start_alt.S new file mode 100644 index 0000000000..0c9ffdb7d6 --- /dev/null +++ b/runtime/interpreter/mterp/arm64/instruction_start_alt.S @@ -0,0 +1,4 @@ + + .global artMterpAsmAltInstructionStart +artMterpAsmAltInstructionStart = .L_ALT_op_nop + .text diff --git a/runtime/interpreter/mterp/arm64/instruction_start_sister.S b/runtime/interpreter/mterp/arm64/instruction_start_sister.S new file mode 100644 index 0000000000..2ec51f7261 --- /dev/null +++ b/runtime/interpreter/mterp/arm64/instruction_start_sister.S @@ -0,0 +1,5 @@ + + .global artMterpAsmSisterStart + .text + .balign 4 +artMterpAsmSisterStart: diff --git a/runtime/interpreter/mterp/gen_mterp.py b/runtime/interpreter/mterp/gen_mterp.py index 64114d747a..75c5174bcb 100755 --- a/runtime/interpreter/mterp/gen_mterp.py +++ b/runtime/interpreter/mterp/gen_mterp.py @@ -279,13 +279,8 @@ def loadAndEmitOpcodes(): sister_list = [] assert len(opcodes) == kNumPackedOpcodes need_dummy_start = False - start_label = global_name_format % "artMterpAsmInstructionStart" - end_label = global_name_format % "artMterpAsmInstructionEnd" - # point MterpAsmInstructionStart at the first handler or stub - asm_fp.write("\n .global %s\n" % start_label) - asm_fp.write("%s = " % start_label + label_prefix + "_op_nop\n") - asm_fp.write(" .text\n\n") + loadAndEmitGenericAsm("instruction_start") for i in xrange(kNumPackedOpcodes): op = opcodes[i] @@ -309,20 +304,14 @@ def loadAndEmitOpcodes(): asm_fp.write(label_prefix + "_op_nop: /* dummy */\n"); emitAlign() - asm_fp.write(" .global %s\n" % end_label) - asm_fp.write("%s:\n" % end_label) + + loadAndEmitGenericAsm("instruction_end") if style == "computed-goto": - start_sister_label = global_name_format % "artMterpAsmSisterStart" - end_sister_label = global_name_format % "artMterpAsmSisterEnd" emitSectionComment("Sister implementations", asm_fp) - asm_fp.write(" .global %s\n" % start_sister_label) - asm_fp.write(" .text\n") - asm_fp.write(" .balign 4\n") - asm_fp.write("%s:\n" % start_sister_label) + loadAndEmitGenericAsm("instruction_start_sister") asm_fp.writelines(sister_list) - asm_fp.write(" .global %s\n" % end_sister_label) - asm_fp.write("%s:\n\n" % end_sister_label) + loadAndEmitGenericAsm("instruction_end_sister") # # Load an alternate entry stub @@ -345,10 +334,7 @@ def loadAndEmitAltOpcodes(): start_label = global_name_format % "artMterpAsmAltInstructionStart" end_label = global_name_format % "artMterpAsmAltInstructionEnd" - # point MterpAsmInstructionStart at the first handler or stub - asm_fp.write("\n .global %s\n" % start_label) - asm_fp.write(" .text\n\n") - asm_fp.write("%s = " % start_label + label_prefix + "_ALT_op_nop\n") + loadAndEmitGenericAsm("instruction_start_alt") for i in xrange(kNumPackedOpcodes): op = opcodes[i] @@ -359,8 +345,8 @@ def loadAndEmitAltOpcodes(): loadAndEmitAltStub(source, i) emitAlign() - asm_fp.write(" .global %s\n" % end_label) - asm_fp.write("%s:\n" % end_label) + + loadAndEmitGenericAsm("instruction_end_alt") # # Load an assembly fragment and emit it. @@ -376,6 +362,14 @@ def loadAndEmitAsm(location, opindex, sister_list): emitAsmHeader(asm_fp, dict, label_prefix) appendSourceFile(source, dict, asm_fp, sister_list) +# +# Load a non-handler assembly fragment and emit it. +# +def loadAndEmitGenericAsm(name): + source = "%s/%s.S" % (default_op_dir, name) + dict = getGlobalSubDict() + appendSourceFile(source, dict, asm_fp, None) + # # Emit fallback fragment # diff --git a/runtime/interpreter/mterp/mips/instruction_end.S b/runtime/interpreter/mterp/mips/instruction_end.S new file mode 100644 index 0000000000..32c725c7d9 --- /dev/null +++ b/runtime/interpreter/mterp/mips/instruction_end.S @@ -0,0 +1,3 @@ + + .global artMterpAsmInstructionEnd +artMterpAsmInstructionEnd: diff --git a/runtime/interpreter/mterp/mips/instruction_end_alt.S b/runtime/interpreter/mterp/mips/instruction_end_alt.S new file mode 100644 index 0000000000..f90916fc02 --- /dev/null +++ b/runtime/interpreter/mterp/mips/instruction_end_alt.S @@ -0,0 +1,3 @@ + + .global artMterpAsmAltInstructionEnd +artMterpAsmAltInstructionEnd: diff --git a/runtime/interpreter/mterp/mips/instruction_end_sister.S b/runtime/interpreter/mterp/mips/instruction_end_sister.S new file mode 100644 index 0000000000..c5f4886697 --- /dev/null +++ b/runtime/interpreter/mterp/mips/instruction_end_sister.S @@ -0,0 +1,3 @@ + + .global artMterpAsmSisterEnd +artMterpAsmSisterEnd: diff --git a/runtime/interpreter/mterp/mips/instruction_start.S b/runtime/interpreter/mterp/mips/instruction_start.S new file mode 100644 index 0000000000..8874c20540 --- /dev/null +++ b/runtime/interpreter/mterp/mips/instruction_start.S @@ -0,0 +1,4 @@ + + .global artMterpAsmInstructionStart +artMterpAsmInstructionStart = .L_op_nop + .text diff --git a/runtime/interpreter/mterp/mips/instruction_start_alt.S b/runtime/interpreter/mterp/mips/instruction_start_alt.S new file mode 100644 index 0000000000..0c9ffdb7d6 --- /dev/null +++ b/runtime/interpreter/mterp/mips/instruction_start_alt.S @@ -0,0 +1,4 @@ + + .global artMterpAsmAltInstructionStart +artMterpAsmAltInstructionStart = .L_ALT_op_nop + .text diff --git a/runtime/interpreter/mterp/mips/instruction_start_sister.S b/runtime/interpreter/mterp/mips/instruction_start_sister.S new file mode 100644 index 0000000000..2ec51f7261 --- /dev/null +++ b/runtime/interpreter/mterp/mips/instruction_start_sister.S @@ -0,0 +1,5 @@ + + .global artMterpAsmSisterStart + .text + .balign 4 +artMterpAsmSisterStart: diff --git a/runtime/interpreter/mterp/mips64/instruction_end.S b/runtime/interpreter/mterp/mips64/instruction_end.S new file mode 100644 index 0000000000..32c725c7d9 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/instruction_end.S @@ -0,0 +1,3 @@ + + .global artMterpAsmInstructionEnd +artMterpAsmInstructionEnd: diff --git a/runtime/interpreter/mterp/mips64/instruction_end_alt.S b/runtime/interpreter/mterp/mips64/instruction_end_alt.S new file mode 100644 index 0000000000..f90916fc02 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/instruction_end_alt.S @@ -0,0 +1,3 @@ + + .global artMterpAsmAltInstructionEnd +artMterpAsmAltInstructionEnd: diff --git a/runtime/interpreter/mterp/mips64/instruction_end_sister.S b/runtime/interpreter/mterp/mips64/instruction_end_sister.S new file mode 100644 index 0000000000..c5f4886697 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/instruction_end_sister.S @@ -0,0 +1,3 @@ + + .global artMterpAsmSisterEnd +artMterpAsmSisterEnd: diff --git a/runtime/interpreter/mterp/mips64/instruction_start.S b/runtime/interpreter/mterp/mips64/instruction_start.S new file mode 100644 index 0000000000..8874c20540 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/instruction_start.S @@ -0,0 +1,4 @@ + + .global artMterpAsmInstructionStart +artMterpAsmInstructionStart = .L_op_nop + .text diff --git a/runtime/interpreter/mterp/mips64/instruction_start_alt.S b/runtime/interpreter/mterp/mips64/instruction_start_alt.S new file mode 100644 index 0000000000..0c9ffdb7d6 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/instruction_start_alt.S @@ -0,0 +1,4 @@ + + .global artMterpAsmAltInstructionStart +artMterpAsmAltInstructionStart = .L_ALT_op_nop + .text diff --git a/runtime/interpreter/mterp/mips64/instruction_start_sister.S b/runtime/interpreter/mterp/mips64/instruction_start_sister.S new file mode 100644 index 0000000000..2ec51f7261 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/instruction_start_sister.S @@ -0,0 +1,5 @@ + + .global artMterpAsmSisterStart + .text + .balign 4 +artMterpAsmSisterStart: diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S index 7ea79821b4..b2702a9ffc 100644 --- a/runtime/interpreter/mterp/out/mterp_arm.S +++ b/runtime/interpreter/mterp/out/mterp_arm.S @@ -396,6 +396,7 @@ ENTRY ExecuteMterpImpl GOTO_OPCODE ip @ jump to next instruction /* NOTE: no fallthrough */ +/* File: arm/instruction_start.S */ .global artMterpAsmInstructionStart artMterpAsmInstructionStart = .L_op_nop @@ -7509,19 +7510,25 @@ constvalop_long_to_double: .balign 128 +/* File: arm/instruction_end.S */ + .global artMterpAsmInstructionEnd artMterpAsmInstructionEnd: + /* * =========================================================================== * Sister implementations * =========================================================================== */ +/* File: arm/instruction_start_sister.S */ + .global artMterpAsmSisterStart .text .balign 4 artMterpAsmSisterStart: + /* continuation for op_float_to_long */ /* * Convert the float in r0 to a long in r0/r1. @@ -7583,14 +7590,17 @@ d2l_maybeNaN: mov r0, #0 mov r1, #0 bx lr @ return 0 for NaN +/* File: arm/instruction_end_sister.S */ + .global artMterpAsmSisterEnd artMterpAsmSisterEnd: +/* File: arm/instruction_start_alt.S */ .global artMterpAsmAltInstructionStart +artMterpAsmAltInstructionStart = .L_ALT_op_nop .text -artMterpAsmAltInstructionStart = .L_ALT_op_nop /* ------------------------------ */ .balign 128 .L_ALT_op_nop: /* 0x00 */ @@ -11944,8 +11954,11 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop b MterpCheckBefore @ (self, shadow_frame, dex_pc_ptr) @ Tail call. .balign 128 +/* File: arm/instruction_end_alt.S */ + .global artMterpAsmAltInstructionEnd artMterpAsmAltInstructionEnd: + /* File: arm/footer.S */ /* * =========================================================================== diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S index 70f71ff2bc..2a0c4df3e2 100644 --- a/runtime/interpreter/mterp/out/mterp_arm64.S +++ b/runtime/interpreter/mterp/out/mterp_arm64.S @@ -427,6 +427,7 @@ ENTRY ExecuteMterpImpl GOTO_OPCODE ip // jump to next instruction /* NOTE: no fallthrough */ +/* File: arm64/instruction_start.S */ .global artMterpAsmInstructionStart artMterpAsmInstructionStart = .L_op_nop @@ -7075,18 +7076,26 @@ artMterpAsmInstructionStart = .L_op_nop .balign 128 +/* File: arm64/instruction_end.S */ + .global artMterpAsmInstructionEnd artMterpAsmInstructionEnd: + /* * =========================================================================== * Sister implementations * =========================================================================== */ +/* File: arm64/instruction_start_sister.S */ + .global artMterpAsmSisterStart .text .balign 4 artMterpAsmSisterStart: + +/* File: arm64/instruction_end_sister.S */ + .global artMterpAsmSisterEnd artMterpAsmSisterEnd: @@ -7398,11 +7407,12 @@ MterpProfileActive: ret +/* File: arm64/instruction_start_alt.S */ .global artMterpAsmAltInstructionStart +artMterpAsmAltInstructionStart = .L_ALT_op_nop .text -artMterpAsmAltInstructionStart = .L_ALT_op_nop /* ------------------------------ */ .balign 128 .L_ALT_op_nop: /* 0x00 */ @@ -11756,8 +11766,11 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop b MterpCheckBefore // (self, shadow_frame, dex_pc_ptr) Note: tail call. .balign 128 +/* File: arm64/instruction_end_alt.S */ + .global artMterpAsmAltInstructionEnd artMterpAsmAltInstructionEnd: + /* File: arm64/close_cfi.S */ // Close out the cfi info. We're treating mterp as a single function. diff --git a/runtime/interpreter/mterp/out/mterp_mips.S b/runtime/interpreter/mterp/out/mterp_mips.S index 69568eaf44..3b86279b47 100644 --- a/runtime/interpreter/mterp/out/mterp_mips.S +++ b/runtime/interpreter/mterp/out/mterp_mips.S @@ -810,6 +810,7 @@ ExecuteMterpImpl: GOTO_OPCODE(t0) # jump to next instruction /* NOTE: no fallthrough */ +/* File: mips/instruction_start.S */ .global artMterpAsmInstructionStart artMterpAsmInstructionStart = .L_op_nop @@ -7873,19 +7874,25 @@ artMterpAsmInstructionStart = .L_op_nop .balign 128 +/* File: mips/instruction_end.S */ + .global artMterpAsmInstructionEnd artMterpAsmInstructionEnd: + /* * =========================================================================== * Sister implementations * =========================================================================== */ +/* File: mips/instruction_start_sister.S */ + .global artMterpAsmSisterStart .text .balign 4 artMterpAsmSisterStart: + /* continuation for op_float_to_long */ #ifndef MIPS32REVGE6 @@ -7941,14 +7948,17 @@ artMterpAsmSisterStart: .Lop_ushr_long_2addr_finish: SET_VREG64_GOTO(v1, zero, t3, t0) # vA/vA+1 <- rlo/rhi +/* File: mips/instruction_end_sister.S */ + .global artMterpAsmSisterEnd artMterpAsmSisterEnd: +/* File: mips/instruction_start_alt.S */ .global artMterpAsmAltInstructionStart +artMterpAsmAltInstructionStart = .L_ALT_op_nop .text -artMterpAsmAltInstructionStart = .L_ALT_op_nop /* ------------------------------ */ .balign 128 .L_ALT_op_nop: /* 0x00 */ @@ -12558,8 +12568,11 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop jalr zero, t9 # Tail call to Mterp(self, shadow_frame, dex_pc_ptr) .balign 128 +/* File: mips/instruction_end_alt.S */ + .global artMterpAsmAltInstructionEnd artMterpAsmAltInstructionEnd: + /* File: mips/footer.S */ /* * =========================================================================== diff --git a/runtime/interpreter/mterp/out/mterp_mips64.S b/runtime/interpreter/mterp/out/mterp_mips64.S index 83a6613e37..58f98dfabb 100644 --- a/runtime/interpreter/mterp/out/mterp_mips64.S +++ b/runtime/interpreter/mterp/out/mterp_mips64.S @@ -430,6 +430,7 @@ ExecuteMterpImpl: /* NOTE: no fallthrough */ +/* File: mips64/instruction_start.S */ .global artMterpAsmInstructionStart artMterpAsmInstructionStart = .L_op_nop @@ -7299,26 +7300,35 @@ artMterpAsmInstructionStart = .L_op_nop .balign 128 +/* File: mips64/instruction_end.S */ + .global artMterpAsmInstructionEnd artMterpAsmInstructionEnd: + /* * =========================================================================== * Sister implementations * =========================================================================== */ +/* File: mips64/instruction_start_sister.S */ + .global artMterpAsmSisterStart .text .balign 4 artMterpAsmSisterStart: + +/* File: mips64/instruction_end_sister.S */ + .global artMterpAsmSisterEnd artMterpAsmSisterEnd: +/* File: mips64/instruction_start_alt.S */ .global artMterpAsmAltInstructionStart +artMterpAsmAltInstructionStart = .L_ALT_op_nop .text -artMterpAsmAltInstructionStart = .L_ALT_op_nop /* ------------------------------ */ .balign 128 .L_ALT_op_nop: /* 0x00 */ @@ -12184,8 +12194,11 @@ artMterpAsmAltInstructionStart = .L_ALT_op_nop jalr zero, t9 # (self, shadow_frame, dex_pc_ptr) Note: tail call. .balign 128 +/* File: mips64/instruction_end_alt.S */ + .global artMterpAsmAltInstructionEnd artMterpAsmAltInstructionEnd: + /* File: mips64/footer.S */ /* * We've detected a condition that will result in an exception, but the exception diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S index 1eacfe8736..6be70cce4c 100644 --- a/runtime/interpreter/mterp/out/mterp_x86.S +++ b/runtime/interpreter/mterp/out/mterp_x86.S @@ -405,6 +405,7 @@ SYMBOL(ExecuteMterpImpl): GOTO_NEXT /* NOTE: no fallthrough */ +/* File: x86/instruction_start.S */ .global SYMBOL(artMterpAsmInstructionStart) SYMBOL(artMterpAsmInstructionStart) = .L_op_nop @@ -6470,26 +6471,35 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop .balign 128 +/* File: x86/instruction_end.S */ + .global SYMBOL(artMterpAsmInstructionEnd) SYMBOL(artMterpAsmInstructionEnd): + /* * =========================================================================== * Sister implementations * =========================================================================== */ +/* File: x86/instruction_start_sister.S */ + .global SYMBOL(artMterpAsmSisterStart) .text .balign 4 SYMBOL(artMterpAsmSisterStart): + +/* File: x86/instruction_end_sister.S */ + .global SYMBOL(artMterpAsmSisterEnd) SYMBOL(artMterpAsmSisterEnd): +/* File: x86/instruction_start_alt.S */ .global SYMBOL(artMterpAsmAltInstructionStart) .text - SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop + /* ------------------------------ */ .balign 128 .L_ALT_op_nop: /* 0x00 */ @@ -12635,8 +12645,11 @@ SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop jmp .L_op_nop+(255*128) .balign 128 +/* File: x86/instruction_end_alt.S */ + .global SYMBOL(artMterpAsmAltInstructionEnd) SYMBOL(artMterpAsmAltInstructionEnd): + /* File: x86/footer.S */ /* * =========================================================================== diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S index ea8f483e95..562cf7ceb6 100644 --- a/runtime/interpreter/mterp/out/mterp_x86_64.S +++ b/runtime/interpreter/mterp/out/mterp_x86_64.S @@ -387,6 +387,7 @@ SYMBOL(ExecuteMterpImpl): GOTO_NEXT /* NOTE: no fallthrough */ +/* File: x86_64/instruction_start.S */ .global SYMBOL(artMterpAsmInstructionStart) SYMBOL(artMterpAsmInstructionStart) = .L_op_nop @@ -6217,26 +6218,35 @@ movswl %ax, %eax .balign 128 +/* File: x86_64/instruction_end.S */ + .global SYMBOL(artMterpAsmInstructionEnd) SYMBOL(artMterpAsmInstructionEnd): + /* * =========================================================================== * Sister implementations * =========================================================================== */ +/* File: x86_64/instruction_start_sister.S */ + .global SYMBOL(artMterpAsmSisterStart) .text .balign 4 SYMBOL(artMterpAsmSisterStart): + +/* File: x86_64/instruction_end_sister.S */ + .global SYMBOL(artMterpAsmSisterEnd) SYMBOL(artMterpAsmSisterEnd): +/* File: x86_64/instruction_start_alt.S */ .global SYMBOL(artMterpAsmAltInstructionStart) .text - SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop + /* ------------------------------ */ .balign 128 .L_ALT_op_nop: /* 0x00 */ @@ -11870,8 +11880,11 @@ SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop jmp .L_op_nop+(255*128) .balign 128 +/* File: x86_64/instruction_end_alt.S */ + .global SYMBOL(artMterpAsmAltInstructionEnd) SYMBOL(artMterpAsmAltInstructionEnd): + /* File: x86_64/footer.S */ /* * =========================================================================== diff --git a/runtime/interpreter/mterp/x86/instruction_end.S b/runtime/interpreter/mterp/x86/instruction_end.S new file mode 100644 index 0000000000..3a02a212e6 --- /dev/null +++ b/runtime/interpreter/mterp/x86/instruction_end.S @@ -0,0 +1,3 @@ + + .global SYMBOL(artMterpAsmInstructionEnd) +SYMBOL(artMterpAsmInstructionEnd): diff --git a/runtime/interpreter/mterp/x86/instruction_end_alt.S b/runtime/interpreter/mterp/x86/instruction_end_alt.S new file mode 100644 index 0000000000..33c2b8e2a0 --- /dev/null +++ b/runtime/interpreter/mterp/x86/instruction_end_alt.S @@ -0,0 +1,3 @@ + + .global SYMBOL(artMterpAsmAltInstructionEnd) +SYMBOL(artMterpAsmAltInstructionEnd): diff --git a/runtime/interpreter/mterp/x86/instruction_end_sister.S b/runtime/interpreter/mterp/x86/instruction_end_sister.S new file mode 100644 index 0000000000..ea14b11ede --- /dev/null +++ b/runtime/interpreter/mterp/x86/instruction_end_sister.S @@ -0,0 +1,3 @@ + + .global SYMBOL(artMterpAsmSisterEnd) +SYMBOL(artMterpAsmSisterEnd): diff --git a/runtime/interpreter/mterp/x86/instruction_start.S b/runtime/interpreter/mterp/x86/instruction_start.S new file mode 100644 index 0000000000..ca711de00c --- /dev/null +++ b/runtime/interpreter/mterp/x86/instruction_start.S @@ -0,0 +1,4 @@ + + .global SYMBOL(artMterpAsmInstructionStart) +SYMBOL(artMterpAsmInstructionStart) = .L_op_nop + .text diff --git a/runtime/interpreter/mterp/x86/instruction_start_alt.S b/runtime/interpreter/mterp/x86/instruction_start_alt.S new file mode 100644 index 0000000000..9272a6a7b0 --- /dev/null +++ b/runtime/interpreter/mterp/x86/instruction_start_alt.S @@ -0,0 +1,4 @@ + + .global SYMBOL(artMterpAsmAltInstructionStart) + .text +SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop diff --git a/runtime/interpreter/mterp/x86/instruction_start_sister.S b/runtime/interpreter/mterp/x86/instruction_start_sister.S new file mode 100644 index 0000000000..b9ac994d32 --- /dev/null +++ b/runtime/interpreter/mterp/x86/instruction_start_sister.S @@ -0,0 +1,5 @@ + + .global SYMBOL(artMterpAsmSisterStart) + .text + .balign 4 +SYMBOL(artMterpAsmSisterStart): diff --git a/runtime/interpreter/mterp/x86_64/instruction_end.S b/runtime/interpreter/mterp/x86_64/instruction_end.S new file mode 100644 index 0000000000..3a02a212e6 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/instruction_end.S @@ -0,0 +1,3 @@ + + .global SYMBOL(artMterpAsmInstructionEnd) +SYMBOL(artMterpAsmInstructionEnd): diff --git a/runtime/interpreter/mterp/x86_64/instruction_end_alt.S b/runtime/interpreter/mterp/x86_64/instruction_end_alt.S new file mode 100644 index 0000000000..33c2b8e2a0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/instruction_end_alt.S @@ -0,0 +1,3 @@ + + .global SYMBOL(artMterpAsmAltInstructionEnd) +SYMBOL(artMterpAsmAltInstructionEnd): diff --git a/runtime/interpreter/mterp/x86_64/instruction_end_sister.S b/runtime/interpreter/mterp/x86_64/instruction_end_sister.S new file mode 100644 index 0000000000..ea14b11ede --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/instruction_end_sister.S @@ -0,0 +1,3 @@ + + .global SYMBOL(artMterpAsmSisterEnd) +SYMBOL(artMterpAsmSisterEnd): diff --git a/runtime/interpreter/mterp/x86_64/instruction_start.S b/runtime/interpreter/mterp/x86_64/instruction_start.S new file mode 100644 index 0000000000..ca711de00c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/instruction_start.S @@ -0,0 +1,4 @@ + + .global SYMBOL(artMterpAsmInstructionStart) +SYMBOL(artMterpAsmInstructionStart) = .L_op_nop + .text diff --git a/runtime/interpreter/mterp/x86_64/instruction_start_alt.S b/runtime/interpreter/mterp/x86_64/instruction_start_alt.S new file mode 100644 index 0000000000..9272a6a7b0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/instruction_start_alt.S @@ -0,0 +1,4 @@ + + .global SYMBOL(artMterpAsmAltInstructionStart) + .text +SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop diff --git a/runtime/interpreter/mterp/x86_64/instruction_start_sister.S b/runtime/interpreter/mterp/x86_64/instruction_start_sister.S new file mode 100644 index 0000000000..b9ac994d32 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/instruction_start_sister.S @@ -0,0 +1,5 @@ + + .global SYMBOL(artMterpAsmSisterStart) + .text + .balign 4 +SYMBOL(artMterpAsmSisterStart): -- GitLab From 9e4bbfb8c912dde29f01a76494fe6c253118e1d6 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Thu, 31 May 2018 08:52:16 +0100 Subject: [PATCH 501/749] Fix build: Extend the scope of CodeInfo object in ELF writer. CodeInfo is referenced from DexRegisterMap now, so keep it alive. Test: ./art/test/testrunner/run_build_test_target.py -j30 art-asan Change-Id: I47d0eb2a5be26fdaf2e2f6efc1c0909b84b38b31 --- compiler/debug/elf_debug_info_writer.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index 87e679fbea..f2002a0af6 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -204,12 +204,13 @@ class ElfCompilationUnitWriter { // Decode dex register locations for all stack maps. // It might be expensive, so do it just once and reuse the result. + std::unique_ptr code_info; std::vector dex_reg_maps; if (accessor.HasCodeItem() && mi->code_info != nullptr) { - const CodeInfo code_info(mi->code_info); - for (size_t s = 0; s < code_info.GetNumberOfStackMaps(); ++s) { - const StackMap stack_map = code_info.GetStackMapAt(s); - dex_reg_maps.push_back(code_info.GetDexRegisterMapOf( + code_info.reset(new CodeInfo(mi->code_info)); + for (size_t s = 0; s < code_info->GetNumberOfStackMaps(); ++s) { + const StackMap stack_map = code_info->GetStackMapAt(s); + dex_reg_maps.push_back(code_info->GetDexRegisterMapOf( stack_map, accessor.RegistersSize())); } } -- GitLab From a8bba7d0853b372aea3ed3ea154fb2b2a23c2c9d Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 30 May 2018 15:18:48 +0100 Subject: [PATCH 502/749] ObjPtr<>-ify ClassLinker::FindClass(), fix 1 stale reference use. Thread::CreateAnnotatedStackTrace() was using a stale reference `aste_array_class`. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Bug: 31113334 Change-Id: I191907c0053456bb57de425aa6ccd9668df818a2 --- compiler/driver/compiler_driver.cc | 2 +- compiler/driver/compiler_driver_test.cc | 14 +-- compiler/exception_test.cc | 3 +- compiler/optimizing/intrinsics.cc | 5 +- compiler/verifier_deps_test.cc | 46 +++---- dex2oat/linker/image_test.cc | 20 +-- dex2oat/linker/image_test.h | 2 +- oatdump/oatdump.cc | 20 +-- profman/profile_assistant_test.cc | 58 +++++---- runtime/class_linker-inl.h | 4 +- runtime/class_linker.cc | 114 +++++++++--------- runtime/class_linker.h | 86 ++++++------- runtime/class_linker_test.cc | 13 +- runtime/gc/space/space_test.h | 8 +- runtime/hidden_api_test.cc | 4 +- runtime/interpreter/interpreter_common.cc | 4 +- runtime/interpreter/unstarted_runtime_test.cc | 2 +- runtime/method_handles_test.cc | 6 +- runtime/mirror/method_type.cc | 4 +- runtime/mirror/method_type_test.cc | 5 +- runtime/mirror/var_handle.cc | 40 +++--- runtime/mirror/var_handle.h | 15 +-- runtime/mirror/var_handle_test.cc | 12 +- runtime/native/java_lang_Class.cc | 41 +++---- runtime/native/java_lang_VMClassLoader.cc | 10 +- .../native/java_lang_reflect_Constructor.cc | 10 +- .../native/java_lang_reflect_Executable.cc | 16 +-- runtime/native/java_lang_reflect_Method.cc | 9 +- runtime/proxy_test.cc | 14 +-- runtime/proxy_test.h | 35 +++--- runtime/reflection_test.cc | 4 +- runtime/thread.cc | 25 ++-- runtime/verifier/method_verifier.cc | 2 +- runtime/verifier/method_verifier.h | 2 +- runtime/verifier/method_verifier_test.cc | 2 +- 35 files changed, 320 insertions(+), 337 deletions(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 16f2d0f2cc..653e9edb45 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -391,7 +391,7 @@ static optimizer::DexToDexCompiler::CompilationLevel GetDexToDexCompilationLevel DCHECK(driver.GetCompilerOptions().IsQuickeningCompilationEnabled()); const char* descriptor = dex_file.GetClassDescriptor(class_def); ClassLinker* class_linker = runtime->GetClassLinker(); - mirror::Class* klass = class_linker->FindClass(self, descriptor, class_loader); + ObjPtr klass = class_linker->FindClass(self, descriptor, class_loader); if (klass == nullptr) { CHECK(self->IsExceptionPending()); self->ClearException(); diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index 856cb36266..491e61f9b5 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -88,7 +88,7 @@ class CompilerDriverTest : public CommonCompilerTest { StackHandleScope<1> hs(soa.Self()); Handle loader( hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* c = class_linker->FindClass(soa.Self(), descriptor, loader); + ObjPtr c = class_linker->FindClass(soa.Self(), descriptor, loader); CHECK(c != nullptr); const auto pointer_size = class_linker->GetImagePointerSize(); for (auto& m : c->GetMethods(pointer_size)) { @@ -115,14 +115,14 @@ TEST_F(CompilerDriverTest, DISABLED_LARGE_CompileDexLibCore) { ObjPtr dex_cache = class_linker_->FindDexCache(soa.Self(), dex); EXPECT_EQ(dex.NumStringIds(), dex_cache->NumStrings()); for (size_t i = 0; i < dex_cache->NumStrings(); i++) { - const mirror::String* string = dex_cache->GetResolvedString(dex::StringIndex(i)); + const ObjPtr string = dex_cache->GetResolvedString(dex::StringIndex(i)); EXPECT_TRUE(string != nullptr) << "string_idx=" << i; } EXPECT_EQ(dex.NumTypeIds(), dex_cache->NumResolvedTypes()); for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { - mirror::Class* type = dex_cache->GetResolvedType(dex::TypeIndex(i)); - EXPECT_TRUE(type != nullptr) << "type_idx=" << i - << " " << dex.GetTypeDescriptor(dex.GetTypeId(dex::TypeIndex(i))); + const ObjPtr type = dex_cache->GetResolvedType(dex::TypeIndex(i)); + EXPECT_TRUE(type != nullptr) + << "type_idx=" << i << " " << dex.GetTypeDescriptor(dex.GetTypeId(dex::TypeIndex(i))); } EXPECT_TRUE(dex_cache->StaticMethodSize() == dex_cache->NumResolvedMethods() || dex.NumMethodIds() == dex_cache->NumResolvedMethods()); @@ -228,7 +228,7 @@ class CompilerDriverProfileTest : public CompilerDriverTest { StackHandleScope<1> hs(self); Handle h_loader( hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(self, clazz.c_str(), h_loader); + ObjPtr klass = class_linker->FindClass(self, clazz.c_str(), h_loader); ASSERT_NE(klass, nullptr); const auto pointer_size = class_linker->GetImagePointerSize(); @@ -289,7 +289,7 @@ class CompilerDriverVerifyTest : public CompilerDriverTest { StackHandleScope<1> hs(self); Handle h_loader( hs.NewHandle(soa.Decode(class_loader))); - mirror::Class* klass = class_linker->FindClass(self, clazz.c_str(), h_loader); + ObjPtr klass = class_linker->FindClass(self, clazz.c_str(), h_loader); ASSERT_NE(klass, nullptr); EXPECT_TRUE(klass->IsVerified()); diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc index da1db4593b..15c07870a1 100644 --- a/compiler/exception_test.cc +++ b/compiler/exception_test.cc @@ -34,6 +34,7 @@ #include "mirror/object_array-inl.h" #include "mirror/stack_trace_element.h" #include "oat_quick_method_header.h" +#include "obj_ptr-inl.h" #include "optimizing/stack_map_stream.h" #include "runtime-inl.h" #include "scoped_thread_state_change-inl.h" @@ -122,7 +123,7 @@ class ExceptionTest : public CommonRuntimeTest { ArtMethod* method_g_; private: - mirror::Class* my_klass_; + ObjPtr my_klass_; }; TEST_F(ExceptionTest, FindCatchHandler) { diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index dfe6d791c6..056f533398 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -272,7 +272,8 @@ IntrinsicVisitor::IntegerValueOfInfo IntrinsicVisitor::ComputeIntegerValueOfInfo ClassLinker* class_linker = runtime->GetClassLinker(); gc::Heap* heap = runtime->GetHeap(); IntegerValueOfInfo info; - info.integer_cache = class_linker->FindSystemClass(self, "Ljava/lang/Integer$IntegerCache;"); + info.integer_cache = + class_linker->FindSystemClass(self, "Ljava/lang/Integer$IntegerCache;").Ptr(); if (info.integer_cache == nullptr) { self->ClearException(); return info; @@ -281,7 +282,7 @@ IntrinsicVisitor::IntegerValueOfInfo IntrinsicVisitor::ComputeIntegerValueOfInfo // Optimization only works if the class is initialized and in the boot image. return info; } - info.integer = class_linker->FindSystemClass(self, "Ljava/lang/Integer;"); + info.integer = class_linker->FindSystemClass(self, "Ljava/lang/Integer;").Ptr(); if (info.integer == nullptr) { self->ClearException(); return info; diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index c0892ff466..3fe2ec0ac0 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -65,17 +65,16 @@ class VerifierDepsTest : public CommonCompilerTest { callbacks_.reset(new VerifierDepsCompilerCallbacks()); } - mirror::Class* FindClassByName(const std::string& name, ScopedObjectAccess* soa) + ObjPtr FindClassByName(ScopedObjectAccess& soa, const std::string& name) REQUIRES_SHARED(Locks::mutator_lock_) { - StackHandleScope<1> hs(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); Handle class_loader_handle( - hs.NewHandle(soa->Decode(class_loader_))); - mirror::Class* klass = class_linker_->FindClass(Thread::Current(), - name.c_str(), - class_loader_handle); + hs.NewHandle(soa.Decode(class_loader_))); + ObjPtr klass = + class_linker_->FindClass(soa.Self(), name.c_str(), class_loader_handle); if (klass == nullptr) { - DCHECK(Thread::Current()->IsExceptionPending()); - Thread::Current()->ClearException(); + DCHECK(soa.Self()->IsExceptionPending()); + soa.Self()->ClearException(); } return klass; } @@ -114,16 +113,16 @@ class VerifierDepsTest : public CommonCompilerTest { callbacks->SetVerifierDeps(verifier_deps_.get()); } - void LoadDexFile(ScopedObjectAccess* soa, const char* name1, const char* name2 = nullptr) + void LoadDexFile(ScopedObjectAccess& soa, const char* name1, const char* name2 = nullptr) REQUIRES_SHARED(Locks::mutator_lock_) { class_loader_ = (name2 == nullptr) ? LoadDex(name1) : LoadMultiDex(name1, name2); dex_files_ = GetDexFiles(class_loader_); primary_dex_file_ = dex_files_.front(); SetVerifierDeps(dex_files_); - StackHandleScope<1> hs(soa->Self()); + StackHandleScope<1> hs(soa.Self()); Handle loader = - hs.NewHandle(soa->Decode(class_loader_)); + hs.NewHandle(soa.Decode(class_loader_)); for (const DexFile* dex_file : dex_files_) { class_linker_->RegisterDexFile(*dex_file, loader.Get()); } @@ -133,16 +132,16 @@ class VerifierDepsTest : public CommonCompilerTest { compiler_driver_->SetDexFilesForOatFile(dex_files_); } - void LoadDexFile(ScopedObjectAccess* soa) REQUIRES_SHARED(Locks::mutator_lock_) { + void LoadDexFile(ScopedObjectAccess& soa) REQUIRES_SHARED(Locks::mutator_lock_) { LoadDexFile(soa, "VerifierDeps"); CHECK_EQ(dex_files_.size(), 1u); - klass_Main_ = FindClassByName("LMain;", soa); + klass_Main_ = FindClassByName(soa, "LMain;"); CHECK(klass_Main_ != nullptr); } bool VerifyMethod(const std::string& method_name) { ScopedObjectAccess soa(Thread::Current()); - LoadDexFile(&soa); + LoadDexFile(soa); StackHandleScope<2> hs(soa.Self()); Handle class_loader_handle( @@ -193,7 +192,7 @@ class VerifierDepsTest : public CommonCompilerTest { void VerifyDexFile(const char* multidex = nullptr) { { ScopedObjectAccess soa(Thread::Current()); - LoadDexFile(&soa, "VerifierDeps", multidex); + LoadDexFile(soa, "VerifierDeps", multidex); } SetupCompilerDriver(); VerifyWithCompilerDriver(/* verifier_deps */ nullptr); @@ -204,13 +203,14 @@ class VerifierDepsTest : public CommonCompilerTest { bool is_strict, bool is_assignable) { ScopedObjectAccess soa(Thread::Current()); - LoadDexFile(&soa); - mirror::Class* klass_dst = FindClassByName(dst, &soa); + LoadDexFile(soa); + StackHandleScope<1> hs(soa.Self()); + Handle klass_dst = hs.NewHandle(FindClassByName(soa, dst)); DCHECK(klass_dst != nullptr) << dst; - mirror::Class* klass_src = FindClassByName(src, &soa); + ObjPtr klass_src = FindClassByName(soa, src); DCHECK(klass_src != nullptr) << src; verifier_deps_->AddAssignability(*primary_dex_file_, - klass_dst, + klass_dst.Get(), klass_src, is_strict, is_assignable); @@ -453,12 +453,12 @@ class VerifierDepsTest : public CommonCompilerTest { std::vector dex_files_; const DexFile* primary_dex_file_; jobject class_loader_; - mirror::Class* klass_Main_; + ObjPtr klass_Main_; }; TEST_F(VerifierDepsTest, StringToId) { ScopedObjectAccess soa(Thread::Current()); - LoadDexFile(&soa); + LoadDexFile(soa); dex::StringIndex id_Main1 = verifier_deps_->GetIdFromString(*primary_dex_file_, "LMain;"); ASSERT_LT(id_Main1.index_, primary_dex_file_->NumStringIds()); @@ -1441,7 +1441,7 @@ TEST_F(VerifierDepsTest, CompilerDriver) { for (bool verify_failure : { false, true }) { { ScopedObjectAccess soa(Thread::Current()); - LoadDexFile(&soa, "VerifierDeps", multi); + LoadDexFile(soa, "VerifierDeps", multi); } VerifyWithCompilerDriver(/* verifier_deps */ nullptr); @@ -1450,7 +1450,7 @@ TEST_F(VerifierDepsTest, CompilerDriver) { { ScopedObjectAccess soa(Thread::Current()); - LoadDexFile(&soa, "VerifierDeps", multi); + LoadDexFile(soa, "VerifierDeps", multi); } verifier::VerifierDeps decoded_deps(dex_files_, ArrayRef(buffer)); if (verify_failure) { diff --git a/dex2oat/linker/image_test.cc b/dex2oat/linker/image_test.cc index ab6e7a875a..96c48b8798 100644 --- a/dex2oat/linker/image_test.cc +++ b/dex2oat/linker/image_test.cc @@ -111,18 +111,18 @@ TEST_F(ImageTest, TestDefaultMethods) { // Test the pointer to quick code is the same in origin method // and in the copied method form the same oat file. - mirror::Class* iface_klass = class_linker_->LookupClass( - self, "LIface;", ObjPtr()); + ObjPtr iface_klass = + class_linker_->LookupClass(self, "LIface;", /* class_loader */ nullptr); ASSERT_NE(nullptr, iface_klass); ArtMethod* origin = iface_klass->FindInterfaceMethod("defaultMethod", "()V", pointer_size); ASSERT_NE(nullptr, origin); - ASSERT_TRUE(origin->GetDeclaringClass() == iface_klass); + ASSERT_OBJ_PTR_EQ(origin->GetDeclaringClass(), iface_klass); const void* code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); // The origin method should have a pointer to quick code ASSERT_NE(nullptr, code); ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code)); - mirror::Class* impl_klass = class_linker_->LookupClass( - self, "LImpl;", ObjPtr()); + ObjPtr impl_klass = + class_linker_->LookupClass(self, "LImpl;", /* class_loader */ nullptr); ASSERT_NE(nullptr, impl_klass); ArtMethod* copied = FindCopiedMethod(origin, impl_klass); ASSERT_NE(nullptr, copied); @@ -132,20 +132,20 @@ TEST_F(ImageTest, TestDefaultMethods) { // Test the origin method has pointer to quick code // but the copied method has pointer to interpreter // because these methods are in different oat files. - mirror::Class* iterable_klass = class_linker_->LookupClass( - self, "Ljava/lang/Iterable;", ObjPtr()); + ObjPtr iterable_klass = + class_linker_->LookupClass(self, "Ljava/lang/Iterable;", /* class_loader */ nullptr); ASSERT_NE(nullptr, iterable_klass); origin = iterable_klass->FindClassMethod( "forEach", "(Ljava/util/function/Consumer;)V", pointer_size); ASSERT_NE(nullptr, origin); ASSERT_FALSE(origin->IsDirect()); - ASSERT_TRUE(origin->GetDeclaringClass() == iterable_klass); + ASSERT_OBJ_PTR_EQ(origin->GetDeclaringClass(), iterable_klass); code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); // the origin method should have a pointer to quick code ASSERT_NE(nullptr, code); ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code)); - mirror::Class* iterablebase_klass = class_linker_->LookupClass( - self, "LIterableBase;", ObjPtr()); + ObjPtr iterablebase_klass = + class_linker_->LookupClass(self, "LIterableBase;", /* class_loader */ nullptr); ASSERT_NE(nullptr, iterablebase_klass); copied = FindCopiedMethod(origin, iterablebase_klass); ASSERT_NE(nullptr, copied); diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index 4b231ed35c..f0daf69850 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -97,7 +97,7 @@ class ImageTest : public CommonCompilerTest { return new std::unordered_set(image_classes_); } - ArtMethod* FindCopiedMethod(ArtMethod* origin, mirror::Class* klass) + ArtMethod* FindCopiedMethod(ArtMethod* origin, ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_) { PointerSize pointer_size = class_linker_->GetImagePointerSize(); for (ArtMethod& m : klass->GetCopiedMethods(pointer_size)) { diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 1f197b81f5..6b626c21cf 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -41,6 +41,7 @@ #include "base/unix_file/fd_file.h" #include "class_linker-inl.h" #include "class_linker.h" +#include "class_root.h" #include "compiled_method.h" #include "debug/debug_info.h" #include "debug/elf_debug_writer.h" @@ -3253,7 +3254,7 @@ class IMTDumper { PrepareClass(runtime, klass, prepared); } - mirror::Class* object_class = mirror::Class::GetJavaLangClass()->GetSuperClass(); + ObjPtr object_class = GetClassRoot(); DCHECK(object_class->IsObjectClass()); bool result = klass->GetImt(pointer_size) == object_class->GetImt(pointer_size); @@ -3287,8 +3288,8 @@ class IMTDumper { Handle h_loader, const std::string& class_name, const PointerSize pointer_size, - mirror::Class** klass_out, - std::unordered_set* prepared) + /*out*/ ObjPtr* klass_out, + /*inout*/ std::unordered_set* prepared) REQUIRES_SHARED(Locks::mutator_lock_) { if (class_name.empty()) { return nullptr; @@ -3301,7 +3302,8 @@ class IMTDumper { descriptor = DotToDescriptor(class_name.c_str()); } - mirror::Class* klass = runtime->GetClassLinker()->FindClass(self, descriptor.c_str(), h_loader); + ObjPtr klass = + runtime->GetClassLinker()->FindClass(self, descriptor.c_str(), h_loader); if (klass == nullptr) { self->ClearException(); @@ -3321,7 +3323,7 @@ class IMTDumper { static ImTable* PrepareAndGetImTable(Runtime* runtime, Handle h_klass, const PointerSize pointer_size, - std::unordered_set* prepared) + /*inout*/ std::unordered_set* prepared) REQUIRES_SHARED(Locks::mutator_lock_) { PrepareClass(runtime, h_klass, prepared); return h_klass->GetImt(pointer_size); @@ -3333,7 +3335,7 @@ class IMTDumper { std::unordered_set* prepared) REQUIRES_SHARED(Locks::mutator_lock_) { const PointerSize pointer_size = runtime->GetClassLinker()->GetImagePointerSize(); - mirror::Class* klass; + ObjPtr klass; ImTable* imt = PrepareAndGetImTable(runtime, Thread::Current(), h_loader, @@ -3389,10 +3391,10 @@ class IMTDumper { const std::string& class_name, const std::string& method, Handle h_loader, - std::unordered_set* prepared) + /*inout*/ std::unordered_set* prepared) REQUIRES_SHARED(Locks::mutator_lock_) { const PointerSize pointer_size = runtime->GetClassLinker()->GetImagePointerSize(); - mirror::Class* klass; + ObjPtr klass; ImTable* imt = PrepareAndGetImTable(runtime, Thread::Current(), h_loader, @@ -3495,7 +3497,7 @@ class IMTDumper { // and note in the given set that the work was done. static void PrepareClass(Runtime* runtime, Handle h_klass, - std::unordered_set* done) + /*inout*/ std::unordered_set* done) REQUIRES_SHARED(Locks::mutator_lock_) { if (!h_klass->ShouldHaveImt()) { return; diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index bd44e491b0..370f59dc8a 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -22,6 +22,7 @@ #include "base/utils.h" #include "common_runtime_test.h" #include "dex/descriptors_names.h" +#include "dex/type_reference.h" #include "exec_utils.h" #include "linear_alloc.h" #include "mirror/class-inl.h" @@ -33,6 +34,7 @@ namespace art { using Hotness = ProfileCompilationInfo::MethodHotness; +using TypeReferenceSet = std::set; static constexpr size_t kMaxMethodIds = 65535; @@ -308,25 +310,24 @@ class ProfileAssistantTest : public CommonRuntimeTest { return true; } - mirror::Class* GetClass(jobject class_loader, const std::string& clazz) { + ObjPtr GetClass(ScopedObjectAccess& soa, + jobject class_loader, + const std::string& clazz) REQUIRES_SHARED(Locks::mutator_lock_) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); - StackHandleScope<1> hs(self); - Handle h_loader( - hs.NewHandle(ObjPtr::DownCast(self->DecodeJObject(class_loader)))); - return class_linker->FindClass(self, clazz.c_str(), h_loader); + StackHandleScope<1> hs(soa.Self()); + Handle h_loader(hs.NewHandle( + ObjPtr::DownCast(soa.Self()->DecodeJObject(class_loader)))); + return class_linker->FindClass(soa.Self(), clazz.c_str(), h_loader); } ArtMethod* GetVirtualMethod(jobject class_loader, const std::string& clazz, const std::string& name) { - mirror::Class* klass = GetClass(class_loader, clazz); + ScopedObjectAccess soa(Thread::Current()); + ObjPtr klass = GetClass(soa, class_loader, clazz); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); const auto pointer_size = class_linker->GetImagePointerSize(); ArtMethod* method = nullptr; - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); for (auto& m : klass->GetVirtualMethods(pointer_size)) { if (name == m.GetName()) { EXPECT_TRUE(method == nullptr); @@ -336,9 +337,14 @@ class ProfileAssistantTest : public CommonRuntimeTest { return method; } + static TypeReference MakeTypeReference(ObjPtr klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + return TypeReference(&klass->GetDexFile(), klass->GetDexTypeIndex()); + } + // Verify that given method has the expected inline caches and nothing else. void AssertInlineCaches(ArtMethod* method, - const std::set& expected_clases, + const TypeReferenceSet& expected_clases, const ProfileCompilationInfo& info, bool is_megamorphic, bool is_missing_types) @@ -355,12 +361,11 @@ class ProfileAssistantTest : public CommonRuntimeTest { ASSERT_EQ(dex_pc_data.is_missing_types, is_missing_types); ASSERT_EQ(expected_clases.size(), dex_pc_data.classes.size()); size_t found = 0; - for (mirror::Class* it : expected_clases) { + for (const TypeReference& type_ref : expected_clases) { for (const auto& class_ref : dex_pc_data.classes) { ProfileCompilationInfo::DexReference dex_ref = pmi->dex_references[class_ref.dex_profile_index]; - if (dex_ref.MatchesDex(&(it->GetDexFile())) && - class_ref.type_index == it->GetDexTypeIndex()) { + if (dex_ref.MatchesDex(type_ref.dex_file) && class_ref.type_index == type_ref.TypeIndex()) { found++; } } @@ -715,7 +720,7 @@ TEST_F(ProfileAssistantTest, TestProfileCreationGenerateMethods) { ASSERT_TRUE(info.Load(GetFd(profile_file))); // Verify that the profile has matching methods. ScopedObjectAccess soa(Thread::Current()); - ObjPtr klass = GetClass(nullptr, "Ljava/lang/Math;"); + ObjPtr klass = GetClass(soa, /* class_loader */ nullptr, "Ljava/lang/Math;"); ASSERT_TRUE(klass != nullptr); size_t method_count = 0; for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) { @@ -907,9 +912,10 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) { jobject class_loader = LoadDex("ProfileTestMultiDex"); ASSERT_NE(class_loader, nullptr); - mirror::Class* sub_a = GetClass(class_loader, "LSubA;"); - mirror::Class* sub_b = GetClass(class_loader, "LSubB;"); - mirror::Class* sub_c = GetClass(class_loader, "LSubC;"); + StackHandleScope<3> hs(soa.Self()); + Handle sub_a = hs.NewHandle(GetClass(soa, class_loader, "LSubA;")); + Handle sub_b = hs.NewHandle(GetClass(soa, class_loader, "LSubB;")); + Handle sub_c = hs.NewHandle(GetClass(soa, class_loader, "LSubC;")); ASSERT_TRUE(sub_a != nullptr); ASSERT_TRUE(sub_b != nullptr); @@ -921,8 +927,8 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) { "LTestInline;", "inlineMonomorphic"); ASSERT_TRUE(inline_monomorphic != nullptr); - std::set expected_monomorphic; - expected_monomorphic.insert(sub_a); + TypeReferenceSet expected_monomorphic; + expected_monomorphic.insert(MakeTypeReference(sub_a.Get())); AssertInlineCaches(inline_monomorphic, expected_monomorphic, info, @@ -936,10 +942,10 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) { "LTestInline;", "inlinePolymorphic"); ASSERT_TRUE(inline_polymorhic != nullptr); - std::set expected_polymorphic; - expected_polymorphic.insert(sub_a); - expected_polymorphic.insert(sub_b); - expected_polymorphic.insert(sub_c); + TypeReferenceSet expected_polymorphic; + expected_polymorphic.insert(MakeTypeReference(sub_a.Get())); + expected_polymorphic.insert(MakeTypeReference(sub_b.Get())); + expected_polymorphic.insert(MakeTypeReference(sub_c.Get())); AssertInlineCaches(inline_polymorhic, expected_polymorphic, info, @@ -953,7 +959,7 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) { "LTestInline;", "inlineMegamorphic"); ASSERT_TRUE(inline_megamorphic != nullptr); - std::set expected_megamorphic; + TypeReferenceSet expected_megamorphic; AssertInlineCaches(inline_megamorphic, expected_megamorphic, info, @@ -967,7 +973,7 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) { "LTestInline;", "inlineMissingTypes"); ASSERT_TRUE(inline_missing_types != nullptr); - std::set expected_missing_Types; + TypeReferenceSet expected_missing_Types; AssertInlineCaches(inline_missing_types, expected_missing_Types, info, diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index 7a99d3dc5e..25eb85d675 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -33,8 +33,8 @@ namespace art { -inline mirror::Class* ClassLinker::FindArrayClass(Thread* self, - ObjPtr* element_class) { +inline ObjPtr ClassLinker::FindArrayClass(Thread* self, + ObjPtr* element_class) { for (size_t i = 0; i < kFindArrayCacheSize; ++i) { // Read the cached array class once to avoid races with other threads setting it. ObjPtr array_class = find_array_class_cache_[i].Read(); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 095272394a..cd03401cf9 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2059,8 +2059,7 @@ void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor) { // Add 100 in case new classes get loaded when we are filling in the object array. class_table_size = NumZygoteClasses() + NumNonZygoteClasses() + 100; } - ObjPtr class_type = mirror::Class::GetJavaLangClass(); - ObjPtr array_of_class = FindArrayClass(self, &class_type); + ObjPtr array_of_class = GetClassRoot>(this); classes.Assign( mirror::ObjectArray::Alloc(self, array_of_class, class_table_size)); CHECK(classes != nullptr); // OOME. @@ -2163,9 +2162,9 @@ mirror::DexCache* ClassLinker::AllocAndInitializeDexCache(Thread* self, return dex_cache.Ptr(); } -mirror::Class* ClassLinker::AllocClass(Thread* self, - ObjPtr java_lang_Class, - uint32_t class_size) { +ObjPtr ClassLinker::AllocClass(Thread* self, + ObjPtr java_lang_Class, + uint32_t class_size) { DCHECK_GE(class_size, sizeof(mirror::Class)); gc::Heap* heap = Runtime::Current()->GetHeap(); mirror::Class::InitializeClassVisitor visitor(class_size); @@ -2179,7 +2178,7 @@ mirror::Class* ClassLinker::AllocClass(Thread* self, return k->AsClass(); } -mirror::Class* ClassLinker::AllocClass(Thread* self, uint32_t class_size) { +ObjPtr ClassLinker::AllocClass(Thread* self, uint32_t class_size) { return AllocClass(self, GetClassRoot(this), class_size); } @@ -2190,9 +2189,9 @@ mirror::ObjectArray* ClassLinker::AllocStackTraceElem self, GetClassRoot>(this), length); } -mirror::Class* ClassLinker::EnsureResolved(Thread* self, - const char* descriptor, - ObjPtr klass) { +ObjPtr ClassLinker::EnsureResolved(Thread* self, + const char* descriptor, + ObjPtr klass) { DCHECK(klass != nullptr); if (kIsDebugBuild) { StackHandleScope<1> hs(self); @@ -2400,9 +2399,9 @@ ObjPtr ClassLinker::FindClassInBaseDexClassLoaderClassPath( return ret; } -mirror::Class* ClassLinker::FindClass(Thread* self, - const char* descriptor, - Handle class_loader) { +ObjPtr ClassLinker::FindClass(Thread* self, + const char* descriptor, + Handle class_loader) { DCHECK_NE(*descriptor, '\0') << "descriptor is empty string"; DCHECK(self != nullptr); self->AssertNoPendingException(); @@ -2571,12 +2570,12 @@ mirror::Class* ClassLinker::FindClass(Thread* self, return result_ptr.Ptr(); } -mirror::Class* ClassLinker::DefineClass(Thread* self, - const char* descriptor, - size_t hash, - Handle class_loader, - const DexFile& dex_file, - const DexFile::ClassDef& dex_class_def) { +ObjPtr ClassLinker::DefineClass(Thread* self, + const char* descriptor, + size_t hash, + Handle class_loader, + const DexFile& dex_file, + const DexFile::ClassDef& dex_class_def) { StackHandleScope<3> hs(self); auto klass = hs.NewHandle(nullptr); @@ -3534,7 +3533,7 @@ ClassLinker::DexCacheData ClassLinker::FindDexCacheDataLocked(const DexFile& dex return DexCacheData(); } -mirror::Class* ClassLinker::CreatePrimitiveClass(Thread* self, Primitive::Type type) { +ObjPtr ClassLinker::CreatePrimitiveClass(Thread* self, Primitive::Type type) { ObjPtr primitive_class = AllocClass(self, mirror::Class::PrimitiveClassSize(image_pointer_size_)); if (UNLIKELY(primitive_class == nullptr)) { @@ -3570,8 +3569,10 @@ mirror::Class* ClassLinker::CreatePrimitiveClass(Thread* self, Primitive::Type t // array class; that always comes from the base element class. // // Returns null with an exception raised on failure. -mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descriptor, size_t hash, - Handle class_loader) { +ObjPtr ClassLinker::CreateArrayClass(Thread* self, + const char* descriptor, + size_t hash, + Handle class_loader) { // Identify the underlying component type CHECK_EQ('[', descriptor[0]); StackHandleScope<2> hs(self); @@ -3718,27 +3719,27 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto return existing.Ptr(); } -mirror::Class* ClassLinker::FindPrimitiveClass(char type) { +ObjPtr ClassLinker::FindPrimitiveClass(char type) { ObjPtr> class_roots = GetClassRoots(); switch (type) { case 'B': - return GetClassRoot(ClassRoot::kPrimitiveByte, class_roots).Ptr(); + return GetClassRoot(ClassRoot::kPrimitiveByte, class_roots); case 'C': - return GetClassRoot(ClassRoot::kPrimitiveChar, class_roots).Ptr(); + return GetClassRoot(ClassRoot::kPrimitiveChar, class_roots); case 'D': - return GetClassRoot(ClassRoot::kPrimitiveDouble, class_roots).Ptr(); + return GetClassRoot(ClassRoot::kPrimitiveDouble, class_roots); case 'F': - return GetClassRoot(ClassRoot::kPrimitiveFloat, class_roots).Ptr(); + return GetClassRoot(ClassRoot::kPrimitiveFloat, class_roots); case 'I': - return GetClassRoot(ClassRoot::kPrimitiveInt, class_roots).Ptr(); + return GetClassRoot(ClassRoot::kPrimitiveInt, class_roots); case 'J': - return GetClassRoot(ClassRoot::kPrimitiveLong, class_roots).Ptr(); + return GetClassRoot(ClassRoot::kPrimitiveLong, class_roots); case 'S': - return GetClassRoot(ClassRoot::kPrimitiveShort, class_roots).Ptr(); + return GetClassRoot(ClassRoot::kPrimitiveShort, class_roots); case 'Z': - return GetClassRoot(ClassRoot::kPrimitiveBoolean, class_roots).Ptr(); + return GetClassRoot(ClassRoot::kPrimitiveBoolean, class_roots); case 'V': - return GetClassRoot(ClassRoot::kPrimitiveVoid, class_roots).Ptr(); + return GetClassRoot(ClassRoot::kPrimitiveVoid, class_roots); default: break; } @@ -3747,7 +3748,9 @@ mirror::Class* ClassLinker::FindPrimitiveClass(char type) { return nullptr; } -mirror::Class* ClassLinker::InsertClass(const char* descriptor, ObjPtr klass, size_t hash) { +ObjPtr ClassLinker::InsertClass(const char* descriptor, + ObjPtr klass, + size_t hash) { if (VLOG_IS_ON(class_linker)) { ObjPtr dex_cache = klass->GetDexCache(); std::string source; @@ -3802,16 +3805,16 @@ void ClassLinker::UpdateClassMethods(ObjPtr klass, Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(klass); } -mirror::Class* ClassLinker::LookupClass(Thread* self, - const char* descriptor, - ObjPtr class_loader) { +ObjPtr ClassLinker::LookupClass(Thread* self, + const char* descriptor, + ObjPtr class_loader) { return LookupClass(self, descriptor, ComputeModifiedUtf8Hash(descriptor), class_loader); } -mirror::Class* ClassLinker::LookupClass(Thread* self, - const char* descriptor, - size_t hash, - ObjPtr class_loader) { +ObjPtr ClassLinker::LookupClass(Thread* self, + const char* descriptor, + size_t hash, + ObjPtr class_loader) { ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); ClassTable* const class_table = ClassTableForClassLoader(class_loader); if (class_table != nullptr) { @@ -4264,12 +4267,12 @@ void ClassLinker::ResolveMethodExceptionHandlerTypes(ArtMethod* method) { } } -mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa, - jstring name, - jobjectArray interfaces, - jobject loader, - jobjectArray methods, - jobjectArray throws) { +ObjPtr ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa, + jstring name, + jobjectArray interfaces, + jobject loader, + jobjectArray methods, + jobjectArray throws) { Thread* self = soa.Self(); StackHandleScope<10> hs(self); MutableHandle temp_klass(hs.NewHandle( @@ -8112,8 +8115,7 @@ ObjPtr ClassLinker::ResolveMethodType( // other than by looking at the shorty ? const size_t num_method_args = strlen(dex_file.StringDataByIdx(proto_id.shorty_idx_)) - 1; - ObjPtr class_type = mirror::Class::GetJavaLangClass(); - ObjPtr array_of_class = FindArrayClass(self, &class_type); + ObjPtr array_of_class = GetClassRoot>(this); Handle> method_params(hs.NewHandle( mirror::ObjectArray::Alloc(self, array_of_class, num_method_args))); if (method_params == nullptr) { @@ -8219,11 +8221,10 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForField( } StackHandleScope<4> hs(self); - ObjPtr class_type = mirror::Class::GetJavaLangClass(); - ObjPtr array_of_class = FindArrayClass(self, &class_type); + ObjPtr array_of_class = GetClassRoot>(this); Handle> method_params(hs.NewHandle( mirror::ObjectArray::Alloc(self, array_of_class, num_params))); - if (UNLIKELY(method_params.Get() == nullptr)) { + if (UNLIKELY(method_params == nullptr)) { DCHECK(self->IsExceptionPending()); return nullptr; } @@ -8398,8 +8399,7 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod( int32_t num_params = static_cast(shorty_length + receiver_count - 1); StackHandleScope<7> hs(self); - ObjPtr class_type = mirror::Class::GetJavaLangClass(); - ObjPtr array_of_class = FindArrayClass(self, &class_type); + ObjPtr array_of_class = GetClassRoot>(this); Handle> method_params(hs.NewHandle( mirror::ObjectArray::Alloc(self, array_of_class, num_params))); if (method_params.Get() == nullptr) { @@ -8897,19 +8897,19 @@ class ClassLinker::FindVirtualMethodHolderVisitor : public ClassVisitor { const PointerSize pointer_size_; }; -mirror::Class* ClassLinker::GetHoldingClassOfCopiedMethod(ArtMethod* method) { +ObjPtr ClassLinker::GetHoldingClassOfCopiedMethod(ArtMethod* method) { ScopedTrace trace(__FUNCTION__); // Since this function is slow, have a trace to notify people. CHECK(method->IsCopied()); FindVirtualMethodHolderVisitor visitor(method, image_pointer_size_); VisitClasses(&visitor); - return visitor.holder_.Ptr(); + return visitor.holder_; } -mirror::IfTable* ClassLinker::AllocIfTable(Thread* self, size_t ifcount) { - return down_cast( +ObjPtr ClassLinker::AllocIfTable(Thread* self, size_t ifcount) { + return ObjPtr::DownCast(ObjPtr>( mirror::IfTable::Alloc(self, GetClassRoot>(this), - ifcount * mirror::IfTable::kMax)); + ifcount * mirror::IfTable::kMax))); } // Instantiate ResolveMethod. diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 1f94c43408..b38f01d835 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -146,22 +146,22 @@ class ClassLinker { // Finds a class by its descriptor, loading it if necessary. // If class_loader is null, searches boot_class_path_. - mirror::Class* FindClass(Thread* self, - const char* descriptor, - Handle class_loader) + ObjPtr FindClass(Thread* self, + const char* descriptor, + Handle class_loader) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); // Finds a class by its descriptor using the "system" class loader, ie by searching the // boot_class_path_. - mirror::Class* FindSystemClass(Thread* self, const char* descriptor) + ObjPtr FindSystemClass(Thread* self, const char* descriptor) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_) { return FindClass(self, descriptor, ScopedNullHandle()); } // Finds the array class given for the element class. - mirror::Class* FindArrayClass(Thread* self, ObjPtr* element_class) + ObjPtr FindArrayClass(Thread* self, ObjPtr* element_class) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); @@ -171,20 +171,20 @@ class ClassLinker { } // Define a new a class based on a ClassDef from a DexFile - mirror::Class* DefineClass(Thread* self, - const char* descriptor, - size_t hash, - Handle class_loader, - const DexFile& dex_file, - const DexFile::ClassDef& dex_class_def) + ObjPtr DefineClass(Thread* self, + const char* descriptor, + size_t hash, + Handle class_loader, + const DexFile& dex_file, + const DexFile::ClassDef& dex_class_def) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); // Finds a class by its descriptor, returning null if it isn't wasn't loaded // by the given 'class_loader'. - mirror::Class* LookupClass(Thread* self, - const char* descriptor, - ObjPtr class_loader) + ObjPtr LookupClass(Thread* self, + const char* descriptor, + ObjPtr class_loader) REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -193,7 +193,7 @@ class ClassLinker { REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - mirror::Class* FindPrimitiveClass(char type) REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr FindPrimitiveClass(char type) REQUIRES_SHARED(Locks::mutator_lock_); void DumpForSigQuit(std::ostream& os) REQUIRES(!Locks::classlinker_classes_lock_); @@ -456,7 +456,7 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - mirror::IfTable* AllocIfTable(Thread* self, size_t ifcount) + ObjPtr AllocIfTable(Thread* self, size_t ifcount) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); @@ -483,12 +483,12 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); - mirror::Class* CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa, - jstring name, - jobjectArray interfaces, - jobject loader, - jobjectArray methods, - jobjectArray throws) + ObjPtr CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa, + jstring name, + jobjectArray interfaces, + jobject loader, + jobjectArray methods, + jobjectArray throws) REQUIRES_SHARED(Locks::mutator_lock_); std::string GetDescriptorForProxy(ObjPtr proxy_class) REQUIRES_SHARED(Locks::mutator_lock_); @@ -531,7 +531,9 @@ class ClassLinker { // Attempts to insert a class into a class table. Returns null if // the class was inserted, otherwise returns an existing class with // the same descriptor and ClassLoader. - mirror::Class* InsertClass(const char* descriptor, ObjPtr klass, size_t hash) + ObjPtr InsertClass(const char* descriptor, + ObjPtr klass, + size_t hash) REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -659,7 +661,7 @@ class ClassLinker { REQUIRES(!Locks::dex_lock_); // Get the actual holding class for a copied method. Pretty slow, don't call often. - mirror::Class* GetHoldingClassOfCopiedMethod(ArtMethod* method) + ObjPtr GetHoldingClassOfCopiedMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); // Returns null if not found. @@ -763,16 +765,16 @@ class ClassLinker { REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); // For early bootstrapping by Init - mirror::Class* AllocClass(Thread* self, - ObjPtr java_lang_Class, - uint32_t class_size) + ObjPtr AllocClass(Thread* self, + ObjPtr java_lang_Class, + uint32_t class_size) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - // Alloc* convenience functions to avoid needing to pass in mirror::Class* - // values that are known to the ClassLinker such as - // kObjectArrayClass and kJavaLangString etc. - mirror::Class* AllocClass(Thread* self, uint32_t class_size) + // Alloc* convenience functions to avoid needing to pass in ObjPtr + // values that are known to the ClassLinker such as classes corresponding to + // ClassRoot::kObjectArrayClass and ClassRoot::kJavaLangString etc. + ObjPtr AllocClass(Thread* self, uint32_t class_size) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); @@ -790,14 +792,14 @@ class ClassLinker { REQUIRES(!Locks::dex_lock_) REQUIRES(!Roles::uninterruptible_); - mirror::Class* CreatePrimitiveClass(Thread* self, Primitive::Type type) + ObjPtr CreatePrimitiveClass(Thread* self, Primitive::Type type) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - mirror::Class* CreateArrayClass(Thread* self, - const char* descriptor, - size_t hash, - Handle class_loader) + ObjPtr CreateArrayClass(Thread* self, + const char* descriptor, + size_t hash, + Handle class_loader) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); @@ -889,10 +891,10 @@ class ClassLinker { // Finds a class by its descriptor, returning NULL if it isn't wasn't loaded // by the given 'class_loader'. Uses the provided hash for the descriptor. - mirror::Class* LookupClass(Thread* self, - const char* descriptor, - size_t hash, - ObjPtr class_loader) + ObjPtr LookupClass(Thread* self, + const char* descriptor, + size_t hash, + ObjPtr class_loader) REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -1163,7 +1165,9 @@ class ClassLinker { // when resolution has occurred. This happens in mirror::Class::SetStatus. As resolution may // retire a class, the version of the class in the table is returned and this may differ from // the class passed in. - mirror::Class* EnsureResolved(Thread* self, const char* descriptor, ObjPtr klass) + ObjPtr EnsureResolved(Thread* self, + const char* descriptor, + ObjPtr klass) WARN_UNUSED REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 48ec6b6c27..5d420aae04 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -198,7 +198,8 @@ class ClassLinkerTest : public CommonRuntimeTest { ASSERT_STREQ(array_descriptor.c_str(), array->GetDescriptor(&temp)); EXPECT_TRUE(array->GetSuperClass() != nullptr); Thread* self = Thread::Current(); - EXPECT_EQ(class_linker_->FindSystemClass(self, "Ljava/lang/Object;"), array->GetSuperClass()); + EXPECT_OBJ_PTR_EQ(class_linker_->FindSystemClass(self, "Ljava/lang/Object;"), + array->GetSuperClass()); EXPECT_TRUE(array->HasSuperClass()); ASSERT_TRUE(array->GetComponentType() != nullptr); ASSERT_GT(strlen(array->GetComponentType()->GetDescriptor(&temp)), 0U); @@ -1079,27 +1080,27 @@ TEST_F(ClassLinkerTest, ValidatePrimitiveArrayElementsOffset) { ScopedObjectAccess soa(Thread::Current()); StackHandleScope<5> hs(soa.Self()); Handle long_array(hs.NewHandle(mirror::LongArray::Alloc(soa.Self(), 0))); - EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[J"), long_array->GetClass()); + EXPECT_OBJ_PTR_EQ(class_linker_->FindSystemClass(soa.Self(), "[J"), long_array->GetClass()); uintptr_t data_offset = reinterpret_cast(long_array->GetData()); EXPECT_TRUE(IsAligned<8>(data_offset)); // Longs require 8 byte alignment Handle double_array(hs.NewHandle(mirror::DoubleArray::Alloc(soa.Self(), 0))); - EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[D"), double_array->GetClass()); + EXPECT_OBJ_PTR_EQ(class_linker_->FindSystemClass(soa.Self(), "[D"), double_array->GetClass()); data_offset = reinterpret_cast(double_array->GetData()); EXPECT_TRUE(IsAligned<8>(data_offset)); // Doubles require 8 byte alignment Handle int_array(hs.NewHandle(mirror::IntArray::Alloc(soa.Self(), 0))); - EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[I"), int_array->GetClass()); + EXPECT_OBJ_PTR_EQ(class_linker_->FindSystemClass(soa.Self(), "[I"), int_array->GetClass()); data_offset = reinterpret_cast(int_array->GetData()); EXPECT_TRUE(IsAligned<4>(data_offset)); // Ints require 4 byte alignment Handle char_array(hs.NewHandle(mirror::CharArray::Alloc(soa.Self(), 0))); - EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[C"), char_array->GetClass()); + EXPECT_OBJ_PTR_EQ(class_linker_->FindSystemClass(soa.Self(), "[C"), char_array->GetClass()); data_offset = reinterpret_cast(char_array->GetData()); EXPECT_TRUE(IsAligned<2>(data_offset)); // Chars require 2 byte alignment Handle short_array(hs.NewHandle(mirror::ShortArray::Alloc(soa.Self(), 0))); - EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[S"), short_array->GetClass()); + EXPECT_OBJ_PTR_EQ(class_linker_->FindSystemClass(soa.Self(), "[S"), short_array->GetClass()); data_offset = reinterpret_cast(short_array->GetData()); EXPECT_TRUE(IsAligned<2>(data_offset)); // Shorts require 2 byte alignment diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h index d5861ed5d8..c94b666695 100644 --- a/runtime/gc/space/space_test.h +++ b/runtime/gc/space/space_test.h @@ -53,13 +53,11 @@ class SpaceTest : public Super { } mirror::Class* GetByteArrayClass(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - StackHandleScope<1> hs(self); - auto null_loader(hs.NewHandle(nullptr)); if (byte_array_class_ == nullptr) { - mirror::Class* byte_array_class = - Runtime::Current()->GetClassLinker()->FindClass(self, "[B", null_loader); + ObjPtr byte_array_class = + Runtime::Current()->GetClassLinker()->FindSystemClass(self, "[B"); EXPECT_TRUE(byte_array_class != nullptr); - byte_array_class_ = self->GetJniEnv()->NewLocalRef(byte_array_class); + byte_array_class_ = self->GetJniEnv()->NewLocalRef(byte_array_class.Ptr()); EXPECT_TRUE(byte_array_class_ != nullptr); } return self->DecodeJObject(byte_array_class_)->AsClass(); diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc index ab0c2901ff..a41d28492d 100644 --- a/runtime/hidden_api_test.cc +++ b/runtime/hidden_api_test.cc @@ -325,8 +325,8 @@ TEST_F(HiddenApiTest, CheckMemberSignatureForProxyClass) { ASSERT_TRUE(h_iface != nullptr); // Create the proxy class. - std::vector interfaces; - interfaces.push_back(h_iface.Get()); + std::vector> interfaces; + interfaces.push_back(h_iface); Handle proxyClass = hs.NewHandle(proxy_test::GenerateProxyClass( soa, jclass_loader_, runtime_->GetClassLinker(), "$Proxy1234", interfaces)); ASSERT_TRUE(proxyClass != nullptr); diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index fab350942a..56658c3005 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -1128,9 +1128,9 @@ static ObjPtr BuildCallSiteForBootstrapMethod(Thread* self, StackHandleScope<2> hs(self); // Create array for parameter types. - ObjPtr class_type = mirror::Class::GetJavaLangClass(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ObjPtr class_array_type = class_linker->FindArrayClass(self, &class_type); + ObjPtr class_array_type = + GetClassRoot>(class_linker); Handle> ptypes = hs.NewHandle( mirror::ObjectArray::Alloc(self, class_array_type, diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc index 88cfafba47..fee837572b 100644 --- a/runtime/interpreter/unstarted_runtime_test.cc +++ b/runtime/interpreter/unstarted_runtime_test.cc @@ -196,7 +196,7 @@ class UnstartedRuntimeTest : public CommonRuntimeTest { // Prepare for aborts. Aborts assume that the exception class is already resolved, as the // loading code doesn't work under transactions. void PrepareForAborts() REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::Object* result = Runtime::Current()->GetClassLinker()->FindClass( + ObjPtr result = Runtime::Current()->GetClassLinker()->FindClass( Thread::Current(), Transaction::kAbortExceptionSignature, ScopedNullHandle()); diff --git a/runtime/method_handles_test.cc b/runtime/method_handles_test.cc index a9473421cb..0db9551265 100644 --- a/runtime/method_handles_test.cc +++ b/runtime/method_handles_test.cc @@ -17,6 +17,7 @@ #include "method_handles.h" #include "class_linker-inl.h" +#include "class_root.h" #include "common_runtime_test.h" #include "handle_scope-inl.h" #include "jvalue-inl.h" @@ -49,12 +50,11 @@ namespace { REQUIRES_SHARED(Locks::mutator_lock_) { ClassLinker* cl = Runtime::Current()->GetClassLinker(); StackHandleScope<2> hs(self); - ObjPtr class_type = mirror::Class::GetJavaLangClass(); - ObjPtr class_array_type = cl->FindArrayClass(self, &class_type); + ObjPtr class_array_type = GetClassRoot>(cl); auto parameter_types = hs.NewHandle( mirror::ObjectArray::Alloc(self, class_array_type, 1)); parameter_types->Set(0, parameter_type.Get()); - Handle void_class = hs.NewHandle(cl->FindPrimitiveClass('V')); + Handle void_class = hs.NewHandle(GetClassRoot(ClassRoot::kPrimitiveVoid, cl)); return mirror::MethodType::Create(self, void_class, parameter_types); } diff --git a/runtime/mirror/method_type.cc b/runtime/mirror/method_type.cc index a8be8b7019..483fff5b26 100644 --- a/runtime/mirror/method_type.cc +++ b/runtime/mirror/method_type.cc @@ -28,9 +28,7 @@ namespace { ObjPtr> AllocatePTypesArray(Thread* self, int count) REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr class_type = Class::GetJavaLangClass(); - ObjPtr class_array_type = - Runtime::Current()->GetClassLinker()->FindArrayClass(self, &class_type); + ObjPtr class_array_type = GetClassRoot>(); return ObjectArray::Alloc(self, class_array_type, count); } diff --git a/runtime/mirror/method_type_test.cc b/runtime/mirror/method_type_test.cc index 16bfc73e04..2bdea72f14 100644 --- a/runtime/mirror/method_type_test.cc +++ b/runtime/mirror/method_type_test.cc @@ -22,6 +22,7 @@ #include "class-inl.h" #include "class_linker-inl.h" #include "class_loader.h" +#include "class_root.h" #include "common_runtime_test.h" #include "handle_scope-inl.h" #include "object_array-inl.h" @@ -53,8 +54,8 @@ static mirror::MethodType* CreateMethodType(const std::string& return_type, soa.Self(), FullyQualifiedType(return_type).c_str(), boot_class_loader)); CHECK(return_clazz != nullptr); - ObjPtr class_type = mirror::Class::GetJavaLangClass(); - ObjPtr class_array_type = class_linker->FindArrayClass(self, &class_type); + ObjPtr class_array_type = + GetClassRoot>(class_linker); Handle> param_classes = hs.NewHandle( mirror::ObjectArray::Alloc(self, class_array_type, param_types.size())); diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc index 8311d911cc..71f41b9d12 100644 --- a/runtime/mirror/var_handle.cc +++ b/runtime/mirror/var_handle.cc @@ -27,6 +27,7 @@ #include "jvalue-inl.h" #include "method_handles-inl.h" #include "method_type.h" +#include "obj_ptr-inl.h" #include "well_known_classes.h" namespace art { @@ -266,31 +267,22 @@ int32_t BuildParameterArray(ObjPtr (¶meters)[VarHandle::kMaxAccessorP // Returns the return type associated with an AccessModeTemplate based // on the template and the variable type specified. -Class* GetReturnType(AccessModeTemplate access_mode_template, ObjPtr varType) +static ObjPtr GetReturnType(AccessModeTemplate access_mode_template, ObjPtr varType) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(varType != nullptr); switch (access_mode_template) { case AccessModeTemplate::kCompareAndSet: - return Runtime::Current()->GetClassLinker()->FindPrimitiveClass('Z'); + return GetClassRoot(ClassRoot::kPrimitiveBoolean); case AccessModeTemplate::kCompareAndExchange: case AccessModeTemplate::kGet: case AccessModeTemplate::kGetAndUpdate: - return varType.Ptr(); + return varType; case AccessModeTemplate::kSet: - return Runtime::Current()->GetClassLinker()->FindPrimitiveClass('V'); + return GetClassRoot(ClassRoot::kPrimitiveVoid); } return nullptr; } -ObjectArray* NewArrayOfClasses(Thread* self, int count) - REQUIRES_SHARED(Locks::mutator_lock_) { - Runtime* const runtime = Runtime::Current(); - ClassLinker* const class_linker = runtime->GetClassLinker(); - ObjPtr class_type = mirror::Class::GetJavaLangClass(); - ObjPtr array_of_class = class_linker->FindArrayClass(self, &class_type); - return ObjectArray::Alloc(Thread::Current(), array_of_class, count); -} - // Method to insert a read barrier for accessors to reference fields. inline void ReadBarrierForVarHandleAccess(ObjPtr obj, MemberOffset field_offset) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -1410,15 +1402,15 @@ class ByteArrayViewAccessor { } // namespace -Class* VarHandle::GetVarType() { +ObjPtr VarHandle::GetVarType() { return GetFieldObject(VarTypeOffset()); } -Class* VarHandle::GetCoordinateType0() { +ObjPtr VarHandle::GetCoordinateType0() { return GetFieldObject(CoordinateType0Offset()); } -Class* VarHandle::GetCoordinateType1() { +ObjPtr VarHandle::GetCoordinateType1() { return GetFieldObject(CoordinateType1Offset()); } @@ -1438,7 +1430,7 @@ VarHandle::MatchKind VarHandle::GetMethodTypeMatchForAccessMode(AccessMode acces // Check return type first. If the return type of the method // of the VarHandle is immaterial. if (mt_rtype->GetPrimitiveType() != Primitive::Type::kPrimVoid) { - ObjPtr vh_rtype = GetReturnType(access_mode_template, var_type.Ptr()); + ObjPtr vh_rtype = GetReturnType(access_mode_template, var_type); if (vh_rtype != mt_rtype) { if (!IsReturnTypeConvertible(vh_rtype, mt_rtype)) { return MatchKind::kNone; @@ -1513,9 +1505,9 @@ bool VarHandle::IsInvokerMethodTypeCompatible(AccessMode access_mode, return true; } -MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self, - ObjPtr var_handle, - AccessMode access_mode) { +ObjPtr VarHandle::GetMethodTypeForAccessMode(Thread* self, + ObjPtr var_handle, + AccessMode access_mode) { // This is a static method as the var_handle might be moved by the GC during it's execution. AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); @@ -1525,7 +1517,9 @@ MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self, const int32_t ptypes_count = GetNumberOfParameters(access_mode_template, vh->GetCoordinateType0(), vh->GetCoordinateType1()); - Handle> ptypes = hs.NewHandle(NewArrayOfClasses(self, ptypes_count)); + ObjPtr array_of_class = GetClassRoot>(); + Handle> ptypes = + hs.NewHandle(ObjectArray::Alloc(Thread::Current(), array_of_class, ptypes_count)); if (ptypes == nullptr) { return nullptr; } @@ -1537,12 +1531,12 @@ MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self, vh->GetCoordinateType0(), vh->GetCoordinateType1()); for (int32_t i = 0; i < ptypes_count; ++i) { - ptypes->Set(i, ptypes_array[i].Ptr()); + ptypes->Set(i, ptypes_array[i]); } return MethodType::Create(self, rtype, ptypes); } -MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self, AccessMode access_mode) { +ObjPtr VarHandle::GetMethodTypeForAccessMode(Thread* self, AccessMode access_mode) { return GetMethodTypeForAccessMode(self, this, access_mode); } diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h index 9829456854..48c9d74e30 100644 --- a/runtime/mirror/var_handle.h +++ b/runtime/mirror/var_handle.h @@ -26,6 +26,7 @@ namespace art { template class Handle; class InstructionOperands; +template class ObjPtr; enum class Intrinsics; @@ -120,7 +121,7 @@ class MANAGED VarHandle : public Object { // AccessMode. No check is made for whether the AccessMode is a // supported operation so the MethodType can be used when raising a // WrongMethodTypeException exception. - MethodType* GetMethodTypeForAccessMode(Thread* self, AccessMode accessMode) + ObjPtr GetMethodTypeForAccessMode(Thread* self, AccessMode accessMode) REQUIRES_SHARED(Locks::mutator_lock_); // Returns a string representing the descriptor of the MethodType associated with @@ -135,7 +136,7 @@ class MANAGED VarHandle : public Object { REQUIRES_SHARED(Locks::mutator_lock_); // Gets the variable type that is operated on by this VarHandle instance. - Class* GetVarType() REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr GetVarType() REQUIRES_SHARED(Locks::mutator_lock_); // Gets the return type descriptor for a named accessor method, // nullptr if accessor_method is not supported. @@ -149,13 +150,13 @@ class MANAGED VarHandle : public Object { static bool GetAccessModeByMethodName(const char* method_name, AccessMode* access_mode); private: - Class* GetCoordinateType0() REQUIRES_SHARED(Locks::mutator_lock_); - Class* GetCoordinateType1() REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr GetCoordinateType0() REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr GetCoordinateType1() REQUIRES_SHARED(Locks::mutator_lock_); int32_t GetAccessModesBitMask() REQUIRES_SHARED(Locks::mutator_lock_); - static MethodType* GetMethodTypeForAccessMode(Thread* self, - ObjPtr var_handle, - AccessMode access_mode) + static ObjPtr GetMethodTypeForAccessMode(Thread* self, + ObjPtr var_handle, + AccessMode access_mode) REQUIRES_SHARED(Locks::mutator_lock_); static MemberOffset VarTypeOffset() { diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc index cb2d628b5b..9df96ddbd1 100644 --- a/runtime/mirror/var_handle_test.cc +++ b/runtime/mirror/var_handle_test.cc @@ -130,17 +130,17 @@ class VarHandleTest : public CommonRuntimeTest { } // Helper to get the VarType of a VarHandle. - static Class* GetVarType(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) { + static ObjPtr GetVarType(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) { return vh->GetVarType(); } // Helper to get the CoordinateType0 of a VarHandle. - static Class* GetCoordinateType0(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) { + static ObjPtr GetCoordinateType0(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) { return vh->GetCoordinateType0(); } // Helper to get the CoordinateType1 of a VarHandle. - static Class* GetCoordinateType1(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) { + static ObjPtr GetCoordinateType1(VarHandle* vh) REQUIRES_SHARED(Locks::mutator_lock_) { return vh->GetCoordinateType1(); } @@ -150,7 +150,7 @@ class VarHandleTest : public CommonRuntimeTest { } private: - static void InitializeVarHandle(VarHandle* vh, + static void InitializeVarHandle(ObjPtr vh, Handle var_type, int32_t access_modes_bit_mask) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -158,7 +158,7 @@ class VarHandleTest : public CommonRuntimeTest { vh->SetField32(VarHandle::AccessModesBitMaskOffset(), access_modes_bit_mask); } - static void InitializeVarHandle(VarHandle* vh, + static void InitializeVarHandle(ObjPtr vh, Handle var_type, Handle coordinate_type0, int32_t access_modes_bit_mask) @@ -167,7 +167,7 @@ class VarHandleTest : public CommonRuntimeTest { vh->SetFieldObject(VarHandle::CoordinateType0Offset(), coordinate_type0.Get()); } - static void InitializeVarHandle(VarHandle* vh, + static void InitializeVarHandle(ObjPtr vh, Handle var_type, Handle coordinate_type0, Handle coordinate_type1, diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 261178b0ee..c6bdfa10c6 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -215,17 +215,9 @@ static jstring Class_getNameNative(JNIEnv* env, jobject javaThis) { return soa.AddLocalReference(mirror::Class::ComputeName(hs.NewHandle(c))); } -// TODO: Move this to mirror::Class ? Other mirror types that commonly appear -// as arrays have a GetArrayClass() method. -static ObjPtr GetClassArrayClass(Thread* self) - REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr class_class = mirror::Class::GetJavaLangClass(); - return Runtime::Current()->GetClassLinker()->FindArrayClass(self, &class_class); -} - static jobjectArray Class_getInterfacesInternal(JNIEnv* env, jobject javaThis) { ScopedFastNativeObjectAccess soa(env); - StackHandleScope<4> hs(soa.Self()); + StackHandleScope<1> hs(soa.Self()); Handle klass = hs.NewHandle(DecodeClass(soa, javaThis)); if (klass->IsProxyClass()) { @@ -237,10 +229,12 @@ static jobjectArray Class_getInterfacesInternal(JNIEnv* env, jobject javaThis) { return nullptr; } + ClassLinker* linker = Runtime::Current()->GetClassLinker(); const uint32_t num_ifaces = iface_list->Size(); - Handle class_array_class = hs.NewHandle(GetClassArrayClass(soa.Self())); - Handle> ifaces = hs.NewHandle( - mirror::ObjectArray::Alloc(soa.Self(), class_array_class.Get(), num_ifaces)); + ObjPtr class_array_class = + GetClassRoot>(linker); + ObjPtr> ifaces = + mirror::ObjectArray::Alloc(soa.Self(), class_array_class, num_ifaces); if (ifaces.IsNull()) { DCHECK(soa.Self()->IsExceptionPending()); return nullptr; @@ -250,20 +244,21 @@ static jobjectArray Class_getInterfacesInternal(JNIEnv* env, jobject javaThis) { // with kActiveTransaction == false. DCHECK(!Runtime::Current()->IsActiveTransaction()); - ClassLinker* linker = Runtime::Current()->GetClassLinker(); - MutableHandle interface(hs.NewHandle(nullptr)); for (uint32_t i = 0; i < num_ifaces; ++i) { const dex::TypeIndex type_idx = iface_list->GetTypeItem(i).type_idx_; - interface.Assign(linker->LookupResolvedType(type_idx, klass.Get())); - ifaces->SetWithoutChecks(i, interface.Get()); + ObjPtr interface = linker->LookupResolvedType(type_idx, klass.Get()); + DCHECK(interface != nullptr); + ifaces->SetWithoutChecks(i, interface); } - return soa.AddLocalReference(ifaces.Get()); + return soa.AddLocalReference(ifaces); } -static mirror::ObjectArray* GetDeclaredFields( - Thread* self, ObjPtr klass, bool public_only, bool force_resolve) - REQUIRES_SHARED(Locks::mutator_lock_) { +static ObjPtr> GetDeclaredFields( + Thread* self, + ObjPtr klass, + bool public_only, + bool force_resolve) REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<1> hs(self); IterationRange> ifields = klass->GetIFields(); IterationRange> sfields = klass->GetSFields(); @@ -672,10 +667,8 @@ static jobjectArray Class_getDeclaredClasses(JNIEnv* env, jobject javaThis) { // Pending exception from GetDeclaredClasses. return nullptr; } - ObjPtr class_array_class = GetClassArrayClass(soa.Self()); - if (class_array_class == nullptr) { - return nullptr; - } + ObjPtr class_array_class = GetClassRoot>(); + DCHECK(class_array_class != nullptr); ObjPtr> empty_array = mirror::ObjectArray::Alloc(soa.Self(), class_array_class, 0); return soa.AddLocalReference(empty_array); diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc index b1511c0d88..42c7ad5650 100644 --- a/runtime/native/java_lang_VMClassLoader.cc +++ b/runtime/native/java_lang_VMClassLoader.cc @@ -37,11 +37,11 @@ namespace art { // A class so we can be friends with ClassLinker and access internal methods. class VMClassLoader { public: - static mirror::Class* LookupClass(ClassLinker* cl, - Thread* self, - const char* descriptor, - size_t hash, - ObjPtr class_loader) + static ObjPtr LookupClass(ClassLinker* cl, + Thread* self, + const char* descriptor, + size_t hash, + ObjPtr class_loader) REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { return cl->LookupClass(self, descriptor, hash, class_loader); diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc index 13a8d28267..a961cb2597 100644 --- a/runtime/native/java_lang_reflect_Constructor.cc +++ b/runtime/native/java_lang_reflect_Constructor.cc @@ -20,8 +20,8 @@ #include "art_method-inl.h" #include "base/enums.h" -#include "class_linker-inl.h" #include "class_linker.h" +#include "class_root.h" #include "dex/dex_file_annotations.h" #include "jni/jni_internal.h" #include "mirror/class-inl.h" @@ -42,12 +42,8 @@ static jobjectArray Constructor_getExceptionTypes(JNIEnv* env, jobject javaMetho annotations::GetExceptionTypesForMethod(method); if (result_array == nullptr) { // Return an empty array instead of a null pointer. - ObjPtr class_class = mirror::Class::GetJavaLangClass(); - ObjPtr class_array_class = - Runtime::Current()->GetClassLinker()->FindArrayClass(soa.Self(), &class_class); - if (class_array_class == nullptr) { - return nullptr; - } + ObjPtr class_array_class = GetClassRoot>(); + DCHECK(class_array_class != nullptr); ObjPtr> empty_array = mirror::ObjectArray::Alloc(soa.Self(), class_array_class, 0); return soa.AddLocalReference(empty_array); diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc index 9a2d3020c0..a40cb9b2e6 100644 --- a/runtime/native/java_lang_reflect_Executable.cc +++ b/runtime/native/java_lang_reflect_Executable.cc @@ -20,6 +20,7 @@ #include "nativehelper/jni_macros.h" #include "art_method-inl.h" +#include "class_root.h" #include "dex/dex_file_annotations.h" #include "handle.h" #include "jni/jni_internal.h" @@ -335,15 +336,6 @@ static jclass Executable_getMethodReturnTypeInternal(JNIEnv* env, jobject javaMe return soa.AddLocalReference(return_type); } -// TODO: Move this to mirror::Class ? Other mirror types that commonly appear -// as arrays have a GetArrayClass() method. This is duplicated in -// java_lang_Class.cc as well. -static ObjPtr GetClassArrayClass(Thread* self) - REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr class_class = mirror::Class::GetJavaLangClass(); - return Runtime::Current()->GetClassLinker()->FindArrayClass(self, &class_class); -} - static jobjectArray Executable_getParameterTypesInternal(JNIEnv* env, jobject javaMethod) { ScopedFastNativeObjectAccess soa(env); ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); @@ -356,10 +348,10 @@ static jobjectArray Executable_getParameterTypesInternal(JNIEnv* env, jobject ja const uint32_t num_params = params->Size(); - StackHandleScope<3> hs(soa.Self()); - Handle class_array_class = hs.NewHandle(GetClassArrayClass(soa.Self())); + StackHandleScope<2> hs(soa.Self()); + ObjPtr class_array_class = GetClassRoot>(); Handle> ptypes = hs.NewHandle( - mirror::ObjectArray::Alloc(soa.Self(), class_array_class.Get(), num_params)); + mirror::ObjectArray::Alloc(soa.Self(), class_array_class, num_params)); if (ptypes.IsNull()) { DCHECK(soa.Self()->IsExceptionPending()); return nullptr; diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc index 52e04941c6..34455fe00f 100644 --- a/runtime/native/java_lang_reflect_Method.cc +++ b/runtime/native/java_lang_reflect_Method.cc @@ -22,6 +22,7 @@ #include "base/enums.h" #include "class_linker-inl.h" #include "class_linker.h" +#include "class_root.h" #include "dex/dex_file_annotations.h" #include "jni/jni_internal.h" #include "mirror/class-inl.h" @@ -66,12 +67,8 @@ static jobjectArray Method_getExceptionTypes(JNIEnv* env, jobject javaMethod) { annotations::GetExceptionTypesForMethod(method); if (result_array == nullptr) { // Return an empty array instead of a null pointer - ObjPtr class_class = mirror::Class::GetJavaLangClass(); - ObjPtr class_array_class = - Runtime::Current()->GetClassLinker()->FindArrayClass(soa.Self(), &class_class); - if (class_array_class == nullptr) { - return nullptr; - } + ObjPtr class_array_class = GetClassRoot>(); + DCHECK(class_array_class != nullptr); mirror::ObjectArray* empty_array = mirror::ObjectArray::Alloc(soa.Self(), class_array_class, 0); return soa.AddLocalReference(empty_array); diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc index 4e0bf890db..946ea018f3 100644 --- a/runtime/proxy_test.cc +++ b/runtime/proxy_test.cc @@ -44,9 +44,9 @@ TEST_F(ProxyTest, ProxyClassHelper) { ASSERT_TRUE(I != nullptr); ASSERT_TRUE(J != nullptr); - std::vector interfaces; - interfaces.push_back(I.Get()); - interfaces.push_back(J.Get()); + std::vector> interfaces; + interfaces.push_back(I); + interfaces.push_back(J); Handle proxy_class(hs.NewHandle( GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy1234", interfaces))); interfaces.clear(); // Don't least possibly stale objects in the array as good practice. @@ -80,9 +80,9 @@ TEST_F(ProxyTest, ProxyFieldHelper) { Handle proxyClass; { - std::vector interfaces; - interfaces.push_back(I.Get()); - interfaces.push_back(J.Get()); + std::vector> interfaces; + interfaces.push_back(I); + interfaces.push_back(J); proxyClass = hs.NewHandle( GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy1234", interfaces)); } @@ -131,7 +131,7 @@ TEST_F(ProxyTest, CheckArtMirrorFieldsOfProxyStaticFields) { Handle proxyClass0; Handle proxyClass1; { - std::vector interfaces; + std::vector> interfaces; proxyClass0 = hs.NewHandle( GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy0", interfaces)); proxyClass1 = hs.NewHandle( diff --git a/runtime/proxy_test.h b/runtime/proxy_test.h index fa5a449e31..860d96c116 100644 --- a/runtime/proxy_test.h +++ b/runtime/proxy_test.h @@ -25,6 +25,7 @@ #include "class_root.h" #include "mirror/class-inl.h" #include "mirror/method.h" +#include "obj_ptr-inl.h" namespace art { namespace proxy_test { @@ -32,11 +33,11 @@ namespace proxy_test { // Generate a proxy class with the given name and interfaces. This is a simplification from what // libcore does to fit to our test needs. We do not check for duplicated interfaces or methods and // we do not declare exceptions. -mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, - jobject jclass_loader, - ClassLinker* class_linker, - const char* className, - const std::vector& interfaces) +ObjPtr GenerateProxyClass(ScopedObjectAccess& soa, + jobject jclass_loader, + ClassLinker* class_linker, + const char* className, + const std::vector>& interfaces) REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<1> hs(soa.Self()); Handle javaLangObject = hs.NewHandle( @@ -46,21 +47,23 @@ mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, jclass javaLangClass = soa.AddLocalReference(mirror::Class::GetJavaLangClass()); // Builds the interfaces array. - jobjectArray proxyClassInterfaces = soa.Env()->NewObjectArray(interfaces.size(), javaLangClass, - nullptr); + jobjectArray proxyClassInterfaces = + soa.Env()->NewObjectArray(interfaces.size(), javaLangClass, /* initialElement */ nullptr); soa.Self()->AssertNoPendingException(); for (size_t i = 0; i < interfaces.size(); ++i) { soa.Env()->SetObjectArrayElement(proxyClassInterfaces, i, - soa.AddLocalReference(interfaces[i])); + soa.AddLocalReference(interfaces[i].Get())); } // Builds the method array. jsize methods_count = 3; // Object.equals, Object.hashCode and Object.toString. - for (mirror::Class* interface : interfaces) { + for (Handle interface : interfaces) { methods_count += interface->NumVirtualMethods(); } jobjectArray proxyClassMethods = soa.Env()->NewObjectArray( - methods_count, soa.AddLocalReference(GetClassRoot()), nullptr); + methods_count, + soa.AddLocalReference(GetClassRoot()), + /* initialElement */ nullptr); soa.Self()->AssertNoPendingException(); jsize array_index = 0; @@ -91,7 +94,7 @@ mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, proxyClassMethods, array_index++, soa.AddLocalReference( mirror::Method::CreateFromArtMethod(soa.Self(), method))); // Now adds all interfaces virtual methods. - for (mirror::Class* interface : interfaces) { + for (Handle interface : interfaces) { for (auto& m : interface->GetDeclaredVirtualMethods(kRuntimePointerSize)) { soa.Env()->SetObjectArrayElement( proxyClassMethods, array_index++, soa.AddLocalReference( @@ -104,9 +107,13 @@ mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, jobjectArray proxyClassThrows = soa.Env()->NewObjectArray(0, javaLangClass, nullptr); soa.Self()->AssertNoPendingException(); - mirror::Class* proxyClass = class_linker->CreateProxyClass( - soa, soa.Env()->NewStringUTF(className), proxyClassInterfaces, jclass_loader, - proxyClassMethods, proxyClassThrows); + ObjPtr proxyClass = class_linker->CreateProxyClass( + soa, + soa.Env()->NewStringUTF(className), + proxyClassInterfaces, + jclass_loader, + proxyClassMethods, + proxyClassThrows); soa.Self()->AssertNoPendingException(); return proxyClass; } diff --git a/runtime/reflection_test.cc b/runtime/reflection_test.cc index d2d720f722..424ee0681a 100644 --- a/runtime/reflection_test.cc +++ b/runtime/reflection_test.cc @@ -80,7 +80,7 @@ class ReflectionTest : public CommonCompilerTest { jclass GetPrimitiveClass(char descriptor) { ScopedObjectAccess soa(env_); - mirror::Class* c = class_linker_->FindPrimitiveClass(descriptor); + ObjPtr c = class_linker_->FindPrimitiveClass(descriptor); CHECK(c != nullptr); return soa.AddLocalReference(c); } @@ -518,7 +518,7 @@ TEST_F(ReflectionTest, StaticMainMethod) { hs.NewHandle(soa.Decode(jclass_loader))); CompileDirectMethod(class_loader, "Main", "main", "([Ljava/lang/String;)V"); - mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader); + ObjPtr klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader); ASSERT_TRUE(klass != nullptr); ArtMethod* method = klass->FindClassMethod("main", diff --git a/runtime/thread.cc b/runtime/thread.cc index ab1a4bba6c..b59606a06b 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -2862,27 +2862,18 @@ jobjectArray Thread::CreateAnnotatedStackTrace(const ScopedObjectAccessAlreadyRu ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); StackHandleScope<6> hs(soa.Self()); - mirror::Class* aste_array_class = class_linker->FindClass( + Handle h_aste_array_class = hs.NewHandle(class_linker->FindSystemClass( soa.Self(), - "[Ldalvik/system/AnnotatedStackTraceElement;", - ScopedNullHandle()); - if (aste_array_class == nullptr) { + "[Ldalvik/system/AnnotatedStackTraceElement;")); + if (h_aste_array_class == nullptr) { return nullptr; } - Handle h_aste_array_class(hs.NewHandle(aste_array_class)); + Handle h_aste_class = hs.NewHandle(h_aste_array_class->GetComponentType()); - mirror::Class* o_array_class = class_linker->FindClass(soa.Self(), - "[Ljava/lang/Object;", - ScopedNullHandle()); - if (o_array_class == nullptr) { - // This should not fail in a healthy runtime. - soa.Self()->AssertPendingException(); - return nullptr; - } - Handle h_o_array_class(hs.NewHandle(o_array_class)); + Handle h_o_array_class = + hs.NewHandle(GetClassRoot>(class_linker)); + DCHECK(h_o_array_class != nullptr); // Class roots must be already initialized. - Handle h_aste_class(hs.NewHandle( - h_aste_array_class->GetComponentType())); // Make sure the AnnotatedStackTraceElement.class is initialized, b/76208924 . class_linker->EnsureInitialized(soa.Self(), @@ -2906,7 +2897,7 @@ jobjectArray Thread::CreateAnnotatedStackTrace(const ScopedObjectAccessAlreadyRu size_t length = dumper.stack_trace_elements_.size(); ObjPtr> array = - mirror::ObjectArray::Alloc(soa.Self(), aste_array_class, length); + mirror::ObjectArray::Alloc(soa.Self(), h_aste_array_class.Get(), length); if (array == nullptr) { soa.Self()->AssertPendingOOMException(); return nullptr; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index cc71dc5f84..2e3a6590e4 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -144,7 +144,7 @@ static void SafelyMarkAllRegistersAsConflicts(MethodVerifier* verifier, Register } FailureKind MethodVerifier::VerifyClass(Thread* self, - mirror::Class* klass, + ObjPtr klass, CompilerCallbacks* callbacks, bool allow_soft_failures, HardFailLogMode log_level, diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index b2adc62a97..ae7481c6b1 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -96,7 +96,7 @@ class MethodVerifier { public: // Verify a class. Returns "kNoFailure" on success. static FailureKind VerifyClass(Thread* self, - mirror::Class* klass, + ObjPtr klass, CompilerCallbacks* callbacks, bool allow_soft_failures, HardFailLogMode log_level, diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc index db3f093905..d1be9fa6f8 100644 --- a/runtime/verifier/method_verifier_test.cc +++ b/runtime/verifier/method_verifier_test.cc @@ -37,7 +37,7 @@ class MethodVerifierTest : public CommonRuntimeTest { REQUIRES_SHARED(Locks::mutator_lock_) { ASSERT_TRUE(descriptor != nullptr); Thread* self = Thread::Current(); - mirror::Class* klass = class_linker_->FindSystemClass(self, descriptor.c_str()); + ObjPtr klass = class_linker_->FindSystemClass(self, descriptor.c_str()); // Verify the class std::string error_msg; -- GitLab From d32ead213f01c16c3ddd7c7a33c0f0d259e4c4dc Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Wed, 30 May 2018 17:38:21 +0100 Subject: [PATCH 503/749] Aesthetic changes in art::mirror::Object and art::mirror::Class. Test: mmma art Change-Id: I1c7bf2e3b0420238ee2ae249aca9b7fce492a3e6 --- runtime/mirror/class-inl.h | 36 +++---- runtime/mirror/class.cc | 2 +- runtime/mirror/class.h | 11 +- runtime/mirror/object-inl.h | 105 ++++++++++++------- runtime/mirror/object.cc | 2 +- runtime/mirror/object.h | 195 ++++++++++++++++++++++-------------- 6 files changed, 209 insertions(+), 142 deletions(-) diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index ab50973e89..5328ad979f 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -145,6 +145,7 @@ inline ArraySlice Class::GetDeclaredMethodsSliceUnchecked(PointerSize GetDirectMethodsStartOffset(), GetCopiedMethodsStartOffset()); } + template inline ArraySlice Class::GetDeclaredVirtualMethodsSlice(PointerSize pointer_size) { DCHECK(IsLoaded() || IsErroneous()); @@ -281,8 +282,7 @@ inline ArtMethod* Class::GetVirtualMethodUnchecked(size_t i, PointerSize pointer return &GetVirtualMethodsSliceUnchecked(pointer_size)[i]; } -template +template inline PointerArray* Class::GetVTable() { DCHECK(IsLoaded() || IsErroneous()); return GetFieldObject( @@ -302,8 +302,7 @@ inline bool Class::HasVTable() { return GetVTable() != nullptr || ShouldHaveEmbeddedVTable(); } - template +template inline int32_t Class::GetVTableLength() { if (ShouldHaveEmbeddedVTable()) { return GetEmbeddedVTableLength(); @@ -312,15 +311,15 @@ inline int32_t Class::GetVTableLength() { GetVTable()->GetLength() : 0; } - template +template inline ArtMethod* Class::GetVTableEntry(uint32_t i, PointerSize pointer_size) { if (ShouldHaveEmbeddedVTable()) { return GetEmbeddedVTableEntry(i, pointer_size); } auto* vtable = GetVTable(); DCHECK(vtable != nullptr); - return vtable->template GetElementPtrSize(i, pointer_size); + return vtable->template GetElementPtrSize( + i, pointer_size); } inline int32_t Class::GetEmbeddedVTableLength() { @@ -410,7 +409,7 @@ inline void Class::SetObjectSize(uint32_t new_object_size) { // Object[] = int[] --> false // inline bool Class::IsArrayAssignableFromArray(ObjPtr src) { - DCHECK(IsArrayClass()) << PrettyClass(); + DCHECK(IsArrayClass()) << PrettyClass(); DCHECK(src->IsArrayClass()) << src->PrettyClass(); return GetComponentType()->IsAssignableFrom(src->GetComponentType()); } @@ -622,16 +621,14 @@ inline ArtMethod* Class::FindVirtualMethodForVirtualOrInterface(ArtMethod* metho return FindVirtualMethodForVirtual(method, pointer_size); } -template +template inline IfTable* Class::GetIfTable() { ObjPtr ret = GetFieldObject(IfTableOffset()); DCHECK(ret != nullptr) << PrettyClass(this); return ret.Ptr(); } -template +template inline int32_t Class::GetIfTableCount() { return GetIfTable()->Count(); } @@ -734,7 +731,7 @@ inline String* Class::GetName() { } inline void Class::SetName(ObjPtr name) { - SetFieldObjectTransaction(OFFSET_OF_OBJECT_MEMBER(Class, name_), name); + SetFieldObjectTransaction(OFFSET_OF_OBJECT_MEMBER(Class, name_), name); } template @@ -887,8 +884,8 @@ inline bool Class::DescriptorEquals(const char* match) { inline void Class::AssertInitializedOrInitializingInThread(Thread* self) { if (kIsDebugBuild && !IsInitialized()) { CHECK(IsInitializing()) << PrettyClass() << " is not initializing: " << GetStatus(); - CHECK_EQ(GetClinitThreadId(), self->GetTid()) << PrettyClass() - << " is initializing in a different thread"; + CHECK_EQ(GetClinitThreadId(), self->GetTid()) + << PrettyClass() << " is initializing in a different thread"; } } @@ -964,18 +961,15 @@ inline ArraySlice Class::GetDirectMethods(PointerSize pointer_size) { return GetDirectMethodsSliceUnchecked(pointer_size); } -inline ArraySlice Class::GetDeclaredMethods( - PointerSize pointer_size) { +inline ArraySlice Class::GetDeclaredMethods(PointerSize pointer_size) { return GetDeclaredMethodsSliceUnchecked(pointer_size); } -inline ArraySlice Class::GetDeclaredVirtualMethods( - PointerSize pointer_size) { +inline ArraySlice Class::GetDeclaredVirtualMethods(PointerSize pointer_size) { return GetDeclaredVirtualMethodsSliceUnchecked(pointer_size); } -inline ArraySlice Class::GetVirtualMethods( - PointerSize pointer_size) { +inline ArraySlice Class::GetVirtualMethods(PointerSize pointer_size) { CheckPointerSize(pointer_size); return GetVirtualMethodsSliceUnchecked(pointer_size); } diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index e6bfe5551a..81b96e1c39 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -1251,7 +1251,7 @@ ArtMethod* Class::GetDeclaredConstructor( uint32_t Class::Depth() { uint32_t depth = 0; - for (ObjPtr klass = this; klass->GetSuperClass() != nullptr; klass = klass->GetSuperClass()) { + for (ObjPtr cls = this; cls->GetSuperClass() != nullptr; cls = cls->GetSuperClass()) { depth++; } return depth; diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 7d5f539576..cf2d5a438c 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -933,12 +933,10 @@ class MANAGED Class FINAL : public Object { ArtMethod* FindConstructor(const StringPiece& signature, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); - ArtMethod* FindDeclaredVirtualMethodByName(const StringPiece& name, - PointerSize pointer_size) + ArtMethod* FindDeclaredVirtualMethodByName(const StringPiece& name, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); - ArtMethod* FindDeclaredDirectMethodByName(const StringPiece& name, - PointerSize pointer_size) + ArtMethod* FindDeclaredDirectMethodByName(const StringPiece& name, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); ArtMethod* FindClassInitializer(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); @@ -1197,10 +1195,7 @@ class MANAGED Class FINAL : public Object { void AssertInitializedOrInitializingInThread(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); - Class* CopyOf(Thread* self, - int32_t new_length, - ImTable* imt, - PointerSize pointer_size) + Class* CopyOf(Thread* self, int32_t new_length, ImTable* imt, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); // For proxy class only. diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index bfebd5d365..cd822c244e 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -412,17 +412,21 @@ inline int8_t Object::GetFieldByteVolatile(MemberOffset field_offset) { return GetFieldByte(field_offset); } -template +template inline void Object::SetFieldBoolean(MemberOffset field_offset, uint8_t new_value) REQUIRES_SHARED(Locks::mutator_lock_) { if (kCheckTransaction) { DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); } if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldBoolean(this, field_offset, - GetFieldBoolean(field_offset), - kIsVolatile); + Runtime::Current()->RecordWriteFieldBoolean( + this, + field_offset, + GetFieldBoolean(field_offset), + kIsVolatile); } if (kVerifyFlags & kVerifyThis) { VerifyObject(this); @@ -430,17 +434,20 @@ inline void Object::SetFieldBoolean(MemberOffset field_offset, uint8_t new_value SetField(field_offset, new_value); } -template +template inline void Object::SetFieldByte(MemberOffset field_offset, int8_t new_value) REQUIRES_SHARED(Locks::mutator_lock_) { if (kCheckTransaction) { DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); } if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldByte(this, field_offset, - GetFieldByte(field_offset), - kIsVolatile); + Runtime::Current()->RecordWriteFieldByte(this, + field_offset, + GetFieldByte(field_offset), + kIsVolatile); } if (kVerifyFlags & kVerifyThis) { VerifyObject(this); @@ -486,16 +493,19 @@ inline int16_t Object::GetFieldShortVolatile(MemberOffset field_offset) { return GetFieldShort(field_offset); } -template +template inline void Object::SetFieldChar(MemberOffset field_offset, uint16_t new_value) { if (kCheckTransaction) { DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); } if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldChar(this, field_offset, - GetFieldChar(field_offset), - kIsVolatile); + Runtime::Current()->RecordWriteFieldChar(this, + field_offset, + GetFieldChar(field_offset), + kIsVolatile); } if (kVerifyFlags & kVerifyThis) { VerifyObject(this); @@ -503,16 +513,19 @@ inline void Object::SetFieldChar(MemberOffset field_offset, uint16_t new_value) SetField(field_offset, new_value); } -template +template inline void Object::SetFieldShort(MemberOffset field_offset, int16_t new_value) { if (kCheckTransaction) { DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); } if (kTransactionActive) { - Runtime::Current()->RecordWriteFieldChar(this, field_offset, - GetFieldShort(field_offset), - kIsVolatile); + Runtime::Current()->RecordWriteFieldChar(this, + field_offset, + GetFieldShort(field_offset), + kIsVolatile); } if (kVerifyFlags & kVerifyThis) { VerifyObject(this); @@ -532,14 +545,17 @@ inline void Object::SetFieldShortVolatile(MemberOffset field_offset, int16_t new field_offset, new_value); } -template +template inline void Object::SetField32(MemberOffset field_offset, int32_t new_value) { if (kCheckTransaction) { DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); } if (kTransactionActive) { - Runtime::Current()->RecordWriteField32(this, field_offset, + Runtime::Current()->RecordWriteField32(this, + field_offset, GetField32(field_offset), kIsVolatile); } @@ -567,7 +583,8 @@ inline void Object::SetField32Transaction(MemberOffset field_offset, int32_t new template inline bool Object::CasFieldWeakSequentiallyConsistent32(MemberOffset field_offset, - int32_t old_value, int32_t new_value) { + int32_t old_value, + int32_t new_value) { if (kCheckTransaction) { DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); } @@ -585,7 +602,8 @@ inline bool Object::CasFieldWeakSequentiallyConsistent32(MemberOffset field_offs template inline bool Object::CasFieldWeakAcquire32(MemberOffset field_offset, - int32_t old_value, int32_t new_value) { + int32_t old_value, + int32_t new_value) { if (kCheckTransaction) { DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); } @@ -603,7 +621,8 @@ inline bool Object::CasFieldWeakAcquire32(MemberOffset field_offset, template inline bool Object::CasFieldWeakRelease32(MemberOffset field_offset, - int32_t old_value, int32_t new_value) { + int32_t old_value, + int32_t new_value) { if (kCheckTransaction) { DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); } @@ -621,7 +640,8 @@ inline bool Object::CasFieldWeakRelease32(MemberOffset field_offset, template inline bool Object::CasFieldStrongSequentiallyConsistent32(MemberOffset field_offset, - int32_t old_value, int32_t new_value) { + int32_t old_value, + int32_t new_value) { if (kCheckTransaction) { DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); } @@ -637,14 +657,17 @@ inline bool Object::CasFieldStrongSequentiallyConsistent32(MemberOffset field_of return atomic_addr->CompareAndSetStrongSequentiallyConsistent(old_value, new_value); } -template +template inline void Object::SetField64(MemberOffset field_offset, int64_t new_value) { if (kCheckTransaction) { DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); } if (kTransactionActive) { - Runtime::Current()->RecordWriteField64(this, field_offset, + Runtime::Current()->RecordWriteField64(this, + field_offset, GetField64(field_offset), kIsVolatile); } @@ -678,7 +701,8 @@ inline kSize Object::GetFieldAcquire(MemberOffset field_offset) { template inline bool Object::CasFieldWeakSequentiallyConsistent64(MemberOffset field_offset, - int64_t old_value, int64_t new_value) { + int64_t old_value, + int64_t new_value) { if (kCheckTransaction) { DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); } @@ -695,7 +719,8 @@ inline bool Object::CasFieldWeakSequentiallyConsistent64(MemberOffset field_offs template inline bool Object::CasFieldStrongSequentiallyConsistent64(MemberOffset field_offset, - int64_t old_value, int64_t new_value) { + int64_t old_value, + int64_t new_value) { if (kCheckTransaction) { DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); } @@ -710,7 +735,9 @@ inline bool Object::CasFieldStrongSequentiallyConsistent64(MemberOffset field_of return atomic_addr->CompareAndSetStrongSequentiallyConsistent(old_value, new_value); } -template inline T* Object::GetFieldObject(MemberOffset field_offset) { if (kVerifyFlags & kVerifyThis) { @@ -733,8 +760,10 @@ inline T* Object::GetFieldObjectVolatile(MemberOffset field_offset) { return GetFieldObject(field_offset); } -template +template inline void Object::SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, ObjPtr new_value) { if (kCheckTransaction) { @@ -760,8 +789,10 @@ inline void Object::SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, objref_addr->Assign(new_value.Ptr()); } -template +template inline void Object::SetFieldObject(MemberOffset field_offset, ObjPtr new_value) { SetFieldObjectWithoutWriteBarrier(field_offset, new_value); diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index 0e03e3741c..4240e702b5 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -271,7 +271,7 @@ void Object::CheckFieldAssignmentImpl(MemberOffset field_offset, ObjPtr } } LOG(FATAL) << "Failed to find field for assignment to " << reinterpret_cast(this) - << " of type " << c->PrettyDescriptor() << " at offset " << field_offset; + << " of type " << c->PrettyDescriptor() << " at offset " << field_offset; UNREACHABLE(); } diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index 82045c7b66..8584b8a56f 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -282,13 +282,16 @@ class MANAGED LOCKABLE Object { bool IsPhantomReferenceInstance() REQUIRES_SHARED(Locks::mutator_lock_); // Accessor for Java type fields. - template + template ALWAYS_INLINE T* GetFieldObject(MemberOffset field_offset) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE T* GetFieldObjectVolatile(MemberOffset field_offset) REQUIRES_SHARED(Locks::mutator_lock_); @@ -310,11 +313,11 @@ class MANAGED LOCKABLE Object { template - ALWAYS_INLINE void SetFieldObjectVolatile(MemberOffset field_offset, - ObjPtr new_value) + ALWAYS_INLINE void SetFieldObjectVolatile(MemberOffset field_offset, ObjPtr new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template ALWAYS_INLINE void SetFieldObjectTransaction(MemberOffset field_offset, ObjPtr new_value) REQUIRES_SHARED(Locks::mutator_lock_); @@ -416,23 +419,29 @@ class MANAGED LOCKABLE Object { ALWAYS_INLINE int8_t GetFieldByteVolatile(MemberOffset field_offset) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetFieldBoolean(MemberOffset field_offset, uint8_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetFieldByte(MemberOffset field_offset, int8_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetFieldBooleanVolatile(MemberOffset field_offset, uint8_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetFieldByteVolatile(MemberOffset field_offset, int8_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); @@ -452,23 +461,29 @@ class MANAGED LOCKABLE Object { ALWAYS_INLINE int16_t GetFieldShortVolatile(MemberOffset field_offset) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetFieldChar(MemberOffset field_offset, uint16_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetFieldShort(MemberOffset field_offset, int16_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetFieldCharVolatile(MemberOffset field_offset, uint16_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetFieldShortVolatile(MemberOffset field_offset, int16_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); @@ -487,13 +502,16 @@ class MANAGED LOCKABLE Object { return GetField32(field_offset); } - template + template ALWAYS_INLINE void SetField32(MemberOffset field_offset, int32_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetField32Volatile(MemberOffset field_offset, int32_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); @@ -503,34 +521,44 @@ class MANAGED LOCKABLE Object { ALWAYS_INLINE void SetField32Transaction(MemberOffset field_offset, int32_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE bool CasFieldWeakSequentiallyConsistent32(MemberOffset field_offset, - int32_t old_value, int32_t new_value) + int32_t old_value, + int32_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template - bool CasFieldWeakRelaxed32(MemberOffset field_offset, int32_t old_value, - int32_t new_value) ALWAYS_INLINE + template + ALWAYS_INLINE bool CasFieldWeakRelaxed32(MemberOffset field_offset, + int32_t old_value, + int32_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template - bool CasFieldWeakAcquire32(MemberOffset field_offset, int32_t old_value, - int32_t new_value) ALWAYS_INLINE + template + ALWAYS_INLINE bool CasFieldWeakAcquire32(MemberOffset field_offset, + int32_t old_value, + int32_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template - bool CasFieldWeakRelease32(MemberOffset field_offset, int32_t old_value, - int32_t new_value) ALWAYS_INLINE + template + ALWAYS_INLINE bool CasFieldWeakRelease32(MemberOffset field_offset, + int32_t old_value, + int32_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template - bool CasFieldStrongSequentiallyConsistent32(MemberOffset field_offset, int32_t old_value, - int32_t new_value) ALWAYS_INLINE + template + ALWAYS_INLINE bool CasFieldStrongSequentiallyConsistent32(MemberOffset field_offset, + int32_t old_value, + int32_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); template @@ -548,13 +576,16 @@ class MANAGED LOCKABLE Object { return GetField64(field_offset); } - template + template ALWAYS_INLINE void SetField64(MemberOffset field_offset, int64_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template + template ALWAYS_INLINE void SetField64Volatile(MemberOffset field_offset, int64_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); @@ -564,35 +595,45 @@ class MANAGED LOCKABLE Object { ALWAYS_INLINE void SetField64Transaction(MemberOffset field_offset, int32_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template - bool CasFieldWeakSequentiallyConsistent64(MemberOffset field_offset, int64_t old_value, + template + bool CasFieldWeakSequentiallyConsistent64(MemberOffset field_offset, + int64_t old_value, int64_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template - bool CasFieldStrongSequentiallyConsistent64(MemberOffset field_offset, int64_t old_value, + template + bool CasFieldStrongSequentiallyConsistent64(MemberOffset field_offset, + int64_t old_value, int64_t new_value) REQUIRES_SHARED(Locks::mutator_lock_); - template + template void SetFieldPtr(MemberOffset field_offset, T new_value) REQUIRES_SHARED(Locks::mutator_lock_) { SetFieldPtrWithSize( field_offset, new_value, kRuntimePointerSize); } - template + template void SetFieldPtr64(MemberOffset field_offset, T new_value) REQUIRES_SHARED(Locks::mutator_lock_) { SetFieldPtrWithSize( field_offset, new_value, 8u); } - template + template ALWAYS_INLINE void SetFieldPtrWithSize(MemberOffset field_offset, T new_value, PointerSize pointer_size) @@ -628,28 +669,34 @@ class MANAGED LOCKABLE Object { // Update methods that expose the raw address of a primitive value-type to an Accessor instance // that will attempt to update the field. These are used by VarHandle accessor methods to // atomically update fields with a wider range of memory orderings than usually required. - template + template void UpdateFieldBooleanViaAccessor(MemberOffset field_offset, Accessor* accessor) REQUIRES_SHARED(Locks::mutator_lock_); - template + template void UpdateFieldByteViaAccessor(MemberOffset field_offset, Accessor* accessor) REQUIRES_SHARED(Locks::mutator_lock_); - template + template void UpdateFieldCharViaAccessor(MemberOffset field_offset, Accessor* accessor) REQUIRES_SHARED(Locks::mutator_lock_); - template + template void UpdateFieldShortViaAccessor(MemberOffset field_offset, Accessor* accessor) REQUIRES_SHARED(Locks::mutator_lock_); - template + template void UpdateField32ViaAccessor(MemberOffset field_offset, Accessor* accessor) REQUIRES_SHARED(Locks::mutator_lock_); - template + template void UpdateField64ViaAccessor(MemberOffset field_offset, Accessor* accessor) REQUIRES_SHARED(Locks::mutator_lock_); -- GitLab From 317892b756cd2a87c01928b09e99e020c86dea10 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 31 May 2018 11:11:32 +0100 Subject: [PATCH 504/749] Remove static GcRoot<>s from Class and ClassExt. And clean up gc_root includes in runtime/mirror/. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Bug: 31113334 Change-Id: Ib5c42a3a892ced4440720350a63a94bcf3a1ca75 --- runtime/class_linker.cc | 9 ++---- runtime/gc/heap.cc | 4 +-- runtime/interpreter/unstarted_runtime_test.cc | 12 ++++---- runtime/mirror/call_site.cc | 9 +++--- runtime/mirror/class.cc | 28 ++++--------------- runtime/mirror/class.h | 19 ------------- runtime/mirror/class_ext.cc | 20 ++----------- runtime/mirror/class_ext.h | 7 ----- runtime/mirror/dex_cache-inl.h | 2 +- runtime/mirror/emulated_stack_frame.cc | 1 - runtime/mirror/executable.h | 1 - runtime/mirror/method.cc | 1 - runtime/mirror/method_handle_impl.cc | 1 - runtime/mirror/method_handles_lookup.cc | 1 - runtime/mirror/method_handles_lookup.h | 1 - runtime/mirror/method_type.cc | 1 - runtime/mirror/reference-inl.h | 1 - runtime/mirror/stack_trace_element.cc | 1 - runtime/mirror/stack_trace_element.h | 1 - runtime/mirror/string.cc | 1 - runtime/mirror/string.h | 1 - runtime/mirror/throwable.h | 1 - runtime/mirror/var_handle.cc | 1 - runtime/proxy_test.h | 5 ++-- runtime/runtime.cc | 4 --- 25 files changed, 24 insertions(+), 109 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index cd03401cf9..e46b980682 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -435,7 +435,7 @@ bool ClassLinker::InitWithoutImage(std::vector> b Handle java_lang_Class(hs.NewHandle(down_cast( heap->AllocNonMovableObject(self, nullptr, class_class_size, VoidFunctor())))); CHECK(java_lang_Class != nullptr); - mirror::Class::SetClassClass(java_lang_Class.Get()); + java_lang_Class->SetClassFlags(mirror::kClassFlagClass); java_lang_Class->SetClass(java_lang_Class.Get()); if (kUseBakerReadBarrier) { java_lang_Class->AssertReadBarrierState(); @@ -553,7 +553,6 @@ bool ClassLinker::InitWithoutImage(std::vector> b Handle dalvik_system_ClassExt(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::ClassExt::ClassSize(image_pointer_size_)))); SetClassRoot(ClassRoot::kDalvikSystemClassExt, dalvik_system_ClassExt.Get()); - mirror::ClassExt::SetClass(dalvik_system_ClassExt.Get()); mirror::Class::SetStatus(dalvik_system_ClassExt, ClassStatus::kResolved, self); // Set up array classes for string, field, method @@ -991,7 +990,8 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { class_roots_ = GcRoot>( down_cast*>( spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots))); - mirror::Class::SetClassClass(GetClassRoot(ClassRoot::kJavaLangClass, this)); + DCHECK_EQ(GetClassRoot(ClassRoot::kJavaLangClass, this)->GetClassFlags(), + mirror::kClassFlagClass); ObjPtr java_lang_Object = GetClassRoot(this); java_lang_Object->SetObjectSize(sizeof(mirror::Object)); @@ -1004,8 +1004,6 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { array_iftable_ = GcRoot(GetClassRoot(ClassRoot::kObjectArrayClass, this)->GetIfTable()); DCHECK_EQ(array_iftable_.Read(), GetClassRoot(ClassRoot::kBooleanArrayClass, this)->GetIfTable()); - // String class root was set above - mirror::ClassExt::SetClass(GetClassRoot(ClassRoot::kDalvikSystemClassExt, this)); for (gc::space::ImageSpace* image_space : spaces) { // Boot class loader, use a null handle. @@ -2082,7 +2080,6 @@ void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor) { } ClassLinker::~ClassLinker() { - mirror::Class::ResetClass(); Thread* const self = Thread::Current(); for (const ClassLoaderData& data : class_loaders_) { // CHA unloading analysis is not needed. No negative consequences are expected because diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index b004566ed1..25ed652b41 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -3913,9 +3913,9 @@ void Heap::BroadcastForNewAllocationRecords() const { } void Heap::CheckGcStressMode(Thread* self, ObjPtr* obj) { + DCHECK(gc_stress_mode_); auto* const runtime = Runtime::Current(); - if (gc_stress_mode_ && runtime->GetClassLinker()->IsInitialized() && - !runtime->IsActiveTransaction() && mirror::Class::HasJavaLangClass()) { + if (runtime->GetClassLinker()->IsInitialized() && !runtime->IsActiveTransaction()) { // Check if we should GC. bool new_backtrace = false; { diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc index fee837572b..449458ce6f 100644 --- a/runtime/interpreter/unstarted_runtime_test.cc +++ b/runtime/interpreter/unstarted_runtime_test.cc @@ -448,8 +448,7 @@ TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTestExceptions) { // Note: all tests are not GC safe. Assume there's no GC running here with the few objects we // allocate. StackHandleScope<3> hs_misc(self); - Handle object_class( - hs_misc.NewHandle(mirror::Class::GetJavaLangClass()->GetSuperClass())); + Handle object_class(hs_misc.NewHandle(GetClassRoot())); StackHandleScope<3> hs_data(self); hs_data.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "1")); @@ -481,8 +480,7 @@ TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTest) { ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); StackHandleScope<1> hs_object(self); - Handle object_class( - hs_object.NewHandle(mirror::Class::GetJavaLangClass()->GetSuperClass())); + Handle object_class(hs_object.NewHandle(GetClassRoot())); // Simple test: // [1,2,3]{1 @ 2} into [4,5,6] = [4,2,6] @@ -902,7 +900,7 @@ TEST_F(UnstartedRuntimeTest, IsAnonymousClass) { JValue result; ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); - ObjPtr class_klass = mirror::Class::GetJavaLangClass(); + ObjPtr class_klass = GetClassRoot(); shadow_frame->SetVRegReference(0, class_klass); UnstartedClassIsAnonymousClass(self, shadow_frame, &result, 0); EXPECT_EQ(result.GetZ(), 0); @@ -996,7 +994,7 @@ TEST_F(UnstartedRuntimeTest, ThreadLocalGet) { { // Just use a method in Class. - ObjPtr class_class = mirror::Class::GetJavaLangClass(); + ObjPtr class_class = GetClassRoot(); ArtMethod* caller_method = &*class_class->GetDeclaredMethods(class_linker->GetImagePointerSize()).begin(); ShadowFrame* caller_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, caller_method, 0); @@ -1111,7 +1109,7 @@ class UnstartedClassForNameTest : public UnstartedRuntimeTest { { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); StackHandleScope<1> hs(self); - Handle h_class = hs.NewHandle(mirror::Class::GetJavaLangClass()); + Handle h_class = hs.NewHandle(GetClassRoot()); CHECK(class_linker->EnsureInitialized(self, h_class, true, true)); } diff --git a/runtime/mirror/call_site.cc b/runtime/mirror/call_site.cc index 808f77cde1..738106c0e4 100644 --- a/runtime/mirror/call_site.cc +++ b/runtime/mirror/call_site.cc @@ -18,18 +18,17 @@ #include "class-inl.h" #include "class_root.h" -#include "gc_root-inl.h" +#include "obj_ptr-inl.h" namespace art { namespace mirror { mirror::CallSite* CallSite::Create(Thread* const self, Handle target) { - StackHandleScope<1> hs(self); - Handle cs( - hs.NewHandle(ObjPtr::DownCast(GetClassRoot()->AllocObject(self)))); + ObjPtr cs = + ObjPtr::DownCast(GetClassRoot()->AllocObject(self)); CHECK(!Runtime::Current()->IsActiveTransaction()); cs->SetFieldObject(TargetOffset(), target.Get()); - return cs.Get(); + return cs.Ptr(); } } // namespace mirror diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index cb2708d0cb..996461b3b2 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -55,26 +55,6 @@ namespace mirror { using android::base::StringPrintf; -GcRoot Class::java_lang_Class_; - -void Class::SetClassClass(ObjPtr java_lang_Class) { - CHECK(java_lang_Class_.IsNull()) - << java_lang_Class_.Read() - << " " << java_lang_Class; - CHECK(java_lang_Class != nullptr); - java_lang_Class->SetClassFlags(kClassFlagClass); - java_lang_Class_ = GcRoot(java_lang_Class); -} - -void Class::ResetClass() { - CHECK(!java_lang_Class_.IsNull()); - java_lang_Class_ = GcRoot(nullptr); -} - -void Class::VisitRoots(RootVisitor* visitor) { - java_lang_Class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - ObjPtr Class::GetPrimitiveClass(ObjPtr name) { const char* expected_name = nullptr; ClassRoot class_root = ClassRoot::kJavaLangObject; // Invalid. @@ -1211,13 +1191,15 @@ Class* Class::CopyOf(Thread* self, int32_t new_length, ImTable* imt, PointerSize // We may get copied by a compacting GC. StackHandleScope<1> hs(self); Handle h_this(hs.NewHandle(this)); - gc::Heap* heap = Runtime::Current()->GetHeap(); + Runtime* runtime = Runtime::Current(); + gc::Heap* heap = runtime->GetHeap(); // The num_bytes (3rd param) is sizeof(Class) as opposed to SizeOf() // to skip copying the tail part that we will overwrite here. CopyClassVisitor visitor(self, &h_this, new_length, sizeof(Class), imt, pointer_size); + ObjPtr java_lang_Class = GetClassRoot(runtime->GetClassLinker()); ObjPtr new_class = kMovingClasses ? - heap->AllocObject(self, java_lang_Class_.Read(), new_length, visitor) : - heap->AllocNonMovableObject(self, java_lang_Class_.Read(), new_length, visitor); + heap->AllocObject(self, java_lang_Class, new_length, visitor) : + heap->AllocNonMovableObject(self, java_lang_Class, new_length, visitor); if (UNLIKELY(new_class == nullptr)) { self->AssertPendingOOMException(); return nullptr; diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 7d5f539576..a637c86823 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -30,7 +30,6 @@ #include "dex/modifiers.h" #include "dex/primitive.h" #include "gc/allocator_type.h" -#include "gc_root.h" #include "imtable.h" #include "object.h" #include "object_array.h" @@ -1130,21 +1129,6 @@ class MANAGED Class FINAL : public Object { dex::TypeIndex FindTypeIndexInOtherDexFile(const DexFile& dex_file) REQUIRES_SHARED(Locks::mutator_lock_); - static Class* GetJavaLangClass() REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(HasJavaLangClass()); - return java_lang_Class_.Read(); - } - - static bool HasJavaLangClass() REQUIRES_SHARED(Locks::mutator_lock_) { - return !java_lang_Class_.IsNull(); - } - - // Can't call this SetClass or else gets called instead of Object::SetClass in places. - static void SetClassClass(ObjPtr java_lang_Class) REQUIRES_SHARED(Locks::mutator_lock_); - static void ResetClass(); - static void VisitRoots(RootVisitor* visitor) - REQUIRES_SHARED(Locks::mutator_lock_); - // Visit native roots visits roots which are keyed off the native pointers such as ArtFields and // ArtMethods. template @@ -1504,9 +1488,6 @@ class MANAGED Class FINAL : public Object { // Static fields, variable size. // uint32_t fields_[0]; - // java.lang.Class - static GcRoot java_lang_Class_; - ART_FRIEND_TEST(DexCacheTest, TestResolvedFieldAccess); // For ResolvedFieldAccessTest friend struct art::ClassOffsets; // for verifying offset information friend class Object; // For VisitReferences diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc index 081957964c..7214620c93 100644 --- a/runtime/mirror/class_ext.cc +++ b/runtime/mirror/class_ext.cc @@ -21,6 +21,7 @@ #include "base/enums.h" #include "base/utils.h" #include "class-inl.h" +#include "class_root.h" #include "dex/dex_file-inl.h" #include "gc/accounting/card_table-inl.h" #include "object-inl.h" @@ -31,8 +32,6 @@ namespace art { namespace mirror { -GcRoot ClassExt::dalvik_system_ClassExt_; - uint32_t ClassExt::ClassSize(PointerSize pointer_size) { uint32_t vtable_entries = Object::kVTableLength; return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0, pointer_size); @@ -102,8 +101,7 @@ bool ClassExt::ExtendObsoleteArrays(Thread* self, uint32_t increase) { } ClassExt* ClassExt::Alloc(Thread* self) { - DCHECK(dalvik_system_ClassExt_.Read() != nullptr); - return down_cast(dalvik_system_ClassExt_.Read()->AllocObject(self).Ptr()); + return down_cast(GetClassRoot()->AllocObject(self).Ptr()); } void ClassExt::SetVerifyError(ObjPtr err) { @@ -119,19 +117,5 @@ void ClassExt::SetOriginalDexFile(ObjPtr bytes) { SetFieldObject(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_), bytes); } -void ClassExt::SetClass(ObjPtr dalvik_system_ClassExt) { - CHECK(dalvik_system_ClassExt != nullptr); - dalvik_system_ClassExt_ = GcRoot(dalvik_system_ClassExt); -} - -void ClassExt::ResetClass() { - CHECK(!dalvik_system_ClassExt_.IsNull()); - dalvik_system_ClassExt_ = GcRoot(nullptr); -} - -void ClassExt::VisitRoots(RootVisitor* visitor) { - dalvik_system_ClassExt_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); -} - } // namespace mirror } // namespace art diff --git a/runtime/mirror/class_ext.h b/runtime/mirror/class_ext.h index 75a3800989..612fd0f256 100644 --- a/runtime/mirror/class_ext.h +++ b/runtime/mirror/class_ext.h @@ -20,7 +20,6 @@ #include "array.h" #include "class.h" #include "dex_cache.h" -#include "gc_root.h" #include "object.h" #include "object_array.h" #include "string.h" @@ -72,10 +71,6 @@ class MANAGED ClassExt : public Object { bool ExtendObsoleteArrays(Thread* self, uint32_t increase) REQUIRES_SHARED(Locks::mutator_lock_); - static void SetClass(ObjPtr dalvik_system_ClassExt); - static void ResetClass(); - static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - template inline void VisitNativeRoots(Visitor& visitor, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); @@ -93,8 +88,6 @@ class MANAGED ClassExt : public Object { // The saved verification error of this class. HeapReference verify_error_; - static GcRoot dalvik_system_ClassExt_; - friend struct art::ClassExtOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(ClassExt); }; diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index 72f1443dfa..96778aa98d 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -28,7 +28,7 @@ #include "class_linker.h" #include "dex/dex_file.h" #include "gc/heap-inl.h" -#include "gc_root.h" +#include "gc_root-inl.h" #include "mirror/call_site.h" #include "mirror/class.h" #include "mirror/method_type.h" diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc index 5595102866..ce39049e11 100644 --- a/runtime/mirror/emulated_stack_frame.cc +++ b/runtime/mirror/emulated_stack_frame.cc @@ -18,7 +18,6 @@ #include "class-inl.h" #include "class_root.h" -#include "gc_root-inl.h" #include "jvalue-inl.h" #include "method_handles-inl.h" #include "method_handles.h" diff --git a/runtime/mirror/executable.h b/runtime/mirror/executable.h index 8a28f66868..23dd787c80 100644 --- a/runtime/mirror/executable.h +++ b/runtime/mirror/executable.h @@ -18,7 +18,6 @@ #define ART_RUNTIME_MIRROR_EXECUTABLE_H_ #include "accessible_object.h" -#include "gc_root.h" #include "object.h" #include "read_barrier_option.h" diff --git a/runtime/mirror/method.cc b/runtime/mirror/method.cc index e5d3403107..cf03b95d5e 100644 --- a/runtime/mirror/method.cc +++ b/runtime/mirror/method.cc @@ -18,7 +18,6 @@ #include "art_method.h" #include "class_root.h" -#include "gc_root-inl.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" diff --git a/runtime/mirror/method_handle_impl.cc b/runtime/mirror/method_handle_impl.cc index a6c1609d01..88ccbc947d 100644 --- a/runtime/mirror/method_handle_impl.cc +++ b/runtime/mirror/method_handle_impl.cc @@ -18,7 +18,6 @@ #include "class-inl.h" #include "class_root.h" -#include "gc_root-inl.h" namespace art { namespace mirror { diff --git a/runtime/mirror/method_handles_lookup.cc b/runtime/mirror/method_handles_lookup.cc index 1ac38dad24..d1e7a6dbfa 100644 --- a/runtime/mirror/method_handles_lookup.cc +++ b/runtime/mirror/method_handles_lookup.cc @@ -19,7 +19,6 @@ #include "class-inl.h" #include "class_root.h" #include "dex/modifiers.h" -#include "gc_root-inl.h" #include "handle_scope.h" #include "jni/jni_internal.h" #include "mirror/method_handle_impl.h" diff --git a/runtime/mirror/method_handles_lookup.h b/runtime/mirror/method_handles_lookup.h index aa94f95ae0..56261eca67 100644 --- a/runtime/mirror/method_handles_lookup.h +++ b/runtime/mirror/method_handles_lookup.h @@ -18,7 +18,6 @@ #define ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_ #include "base/utils.h" -#include "gc_root.h" #include "handle.h" #include "obj_ptr.h" #include "object.h" diff --git a/runtime/mirror/method_type.cc b/runtime/mirror/method_type.cc index 483fff5b26..bc62ebdc8b 100644 --- a/runtime/mirror/method_type.cc +++ b/runtime/mirror/method_type.cc @@ -18,7 +18,6 @@ #include "class-inl.h" #include "class_root.h" -#include "gc_root-inl.h" #include "method_handles.h" namespace art { diff --git a/runtime/mirror/reference-inl.h b/runtime/mirror/reference-inl.h index c65f740a78..f8de6e6d90 100644 --- a/runtime/mirror/reference-inl.h +++ b/runtime/mirror/reference-inl.h @@ -19,7 +19,6 @@ #include "reference.h" -#include "gc_root-inl.h" #include "obj_ptr-inl.h" #include "runtime.h" diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc index ff353d8939..5a7575a027 100644 --- a/runtime/mirror/stack_trace_element.cc +++ b/runtime/mirror/stack_trace_element.cc @@ -20,7 +20,6 @@ #include "class.h" #include "class_root.h" #include "gc/accounting/card_table-inl.h" -#include "gc_root-inl.h" #include "handle_scope-inl.h" #include "object-inl.h" #include "string.h" diff --git a/runtime/mirror/stack_trace_element.h b/runtime/mirror/stack_trace_element.h index f25211c397..55a2ef0b49 100644 --- a/runtime/mirror/stack_trace_element.h +++ b/runtime/mirror/stack_trace_element.h @@ -17,7 +17,6 @@ #ifndef ART_RUNTIME_MIRROR_STACK_TRACE_ELEMENT_H_ #define ART_RUNTIME_MIRROR_STACK_TRACE_ELEMENT_H_ -#include "gc_root.h" #include "object.h" namespace art { diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc index b76ca1968a..d5ef039273 100644 --- a/runtime/mirror/string.cc +++ b/runtime/mirror/string.cc @@ -24,7 +24,6 @@ #include "dex/descriptors_names.h" #include "dex/utf-inl.h" #include "gc/accounting/card_table-inl.h" -#include "gc_root-inl.h" #include "handle_scope-inl.h" #include "intern_table.h" #include "object-inl.h" diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index 598175b749..0e2fc903b5 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -20,7 +20,6 @@ #include "base/bit_utils.h" #include "base/globals.h" #include "gc/allocator_type.h" -#include "gc_root-inl.h" #include "class.h" #include "object.h" diff --git a/runtime/mirror/throwable.h b/runtime/mirror/throwable.h index 42c612f8b6..a9e5d1a30b 100644 --- a/runtime/mirror/throwable.h +++ b/runtime/mirror/throwable.h @@ -17,7 +17,6 @@ #ifndef ART_RUNTIME_MIRROR_THROWABLE_H_ #define ART_RUNTIME_MIRROR_THROWABLE_H_ -#include "gc_root.h" #include "object.h" namespace art { diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc index 71f41b9d12..4319c5df25 100644 --- a/runtime/mirror/var_handle.cc +++ b/runtime/mirror/var_handle.cc @@ -21,7 +21,6 @@ #include "class-inl.h" #include "class_linker.h" #include "class_root.h" -#include "gc_root-inl.h" #include "intrinsics_enum.h" #include "jni/jni_internal.h" #include "jvalue-inl.h" diff --git a/runtime/proxy_test.h b/runtime/proxy_test.h index 860d96c116..411dc7af82 100644 --- a/runtime/proxy_test.h +++ b/runtime/proxy_test.h @@ -40,11 +40,10 @@ ObjPtr GenerateProxyClass(ScopedObjectAccess& soa, const std::vector>& interfaces) REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<1> hs(soa.Self()); - Handle javaLangObject = hs.NewHandle( - class_linker->FindSystemClass(soa.Self(), "Ljava/lang/Object;")); + Handle javaLangObject = hs.NewHandle(GetClassRoot()); CHECK(javaLangObject != nullptr); - jclass javaLangClass = soa.AddLocalReference(mirror::Class::GetJavaLangClass()); + jclass javaLangClass = soa.AddLocalReference(GetClassRoot()); // Builds the interfaces array. jobjectArray proxyClassInterfaces = diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 6384d01aaf..0d9d16cd01 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1976,10 +1976,6 @@ mirror::Throwable* Runtime::GetPreAllocatedNoClassDefFoundError() { } void Runtime::VisitConstantRoots(RootVisitor* visitor) { - // Visit the classes held as static in mirror classes, these can be visited concurrently and only - // need to be visited once per GC since they never change. - mirror::Class::VisitRoots(visitor); - mirror::ClassExt::VisitRoots(visitor); // Visiting the roots of these ArtMethods is not currently required since all the GcRoots are // null. BufferedRootVisitor<16> buffered_visitor(visitor, RootInfo(kRootVMInternal)); -- GitLab From 4b9bac7ee87d9c41ce3d129ff0db42f602c98259 Mon Sep 17 00:00:00 2001 From: Tamas Kenez Date: Thu, 31 May 2018 15:54:18 +0200 Subject: [PATCH 505/749] ART-tests: Remove DX-dependency from 530-checker-lse The test relied on DX-specific code patterns and was failing with D8. This CL moves the compiler-sensitive test cases to smali code and enables D8. Test: art/test.py --host -r -b -t 530-checker-lse tested with --target and --gcstress locally. Bug: 65168732 Change-Id: I1d5ef4545456a6b5c6bae6954dba76766fa2a5db --- test/530-checker-lse/build | 20 -- test/530-checker-lse/smali/Main.smali | 260 ++++++++++++++++++++++++++ test/530-checker-lse/src/Main.java | 130 ++----------- 3 files changed, 277 insertions(+), 133 deletions(-) delete mode 100755 test/530-checker-lse/build create mode 100644 test/530-checker-lse/smali/Main.smali diff --git a/test/530-checker-lse/build b/test/530-checker-lse/build deleted file mode 100755 index 10ffcc537d..0000000000 --- a/test/530-checker-lse/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/530-checker-lse/smali/Main.smali b/test/530-checker-lse/smali/Main.smali new file mode 100644 index 0000000000..267801760f --- /dev/null +++ b/test/530-checker-lse/smali/Main.smali @@ -0,0 +1,260 @@ +# 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. + +.class public LMain2; +.super Ljava/lang/Object; +.source "Main.java" + +# direct methods + +## CHECK-START: int Main2.test4(TestClass, boolean) load_store_elimination (before) +## CHECK: InstanceFieldSet +## CHECK: InstanceFieldGet +## CHECK: Return +## CHECK: InstanceFieldSet + +## CHECK-START: int Main2.test4(TestClass, boolean) load_store_elimination (after) +## CHECK: InstanceFieldSet +## CHECK-NOT: NullCheck +## CHECK-NOT: InstanceFieldGet +## CHECK: Return +## CHECK: InstanceFieldSet + +# Set and merge the same value in two branches. + +# Original java source: +# +# static int test4(TestClass obj, boolean b) { +# if (b) { +# obj.i = 1; +# } else { +# obj.i = 1; +# } +# return obj.i; +# } + +.method public static test4(LTestClass;Z)I + .registers 3 + .param p0, "obj" # LTestClass; + .param p1, "b" # Z + + .prologue + const/4 v0, 0x1 + + .line 185 + if-eqz p1, :cond_8 + + .line 186 + iput v0, p0, LTestClass;->i:I + + .line 190 + :goto_5 + iget v0, p0, LTestClass;->i:I + + return v0 + + .line 188 + :cond_8 + iput v0, p0, LTestClass;->i:I + + goto :goto_5 +.end method + +## CHECK-START: int Main2.test5(TestClass, boolean) load_store_elimination (before) +## CHECK: InstanceFieldSet +## CHECK: InstanceFieldGet +## CHECK: Return +## CHECK: InstanceFieldSet + +## CHECK-START: int Main2.test5(TestClass, boolean) load_store_elimination (after) +## CHECK: InstanceFieldSet +## CHECK: InstanceFieldGet +## CHECK: Return +## CHECK: InstanceFieldSet + +# Set and merge different values in two branches. +# Original java source: +# +# static int test5(TestClass obj, boolean b) { +# if (b) { +# obj.i = 1; +# } else { +# obj.i = 2; +# } +# return obj.i; +# } + +.method public static test5(LTestClass;Z)I + .registers 3 + .param p0, "obj" # LTestClass; + .param p1, "b" # Z + + .prologue + .line 207 + if-eqz p1, :cond_8 + + .line 208 + const/4 v0, 0x1 + + iput v0, p0, LTestClass;->i:I + + .line 212 + :goto_5 + iget v0, p0, LTestClass;->i:I + + return v0 + + .line 210 + :cond_8 + const/4 v0, 0x2 + + iput v0, p0, LTestClass;->i:I + + goto :goto_5 +.end method + +## CHECK-START: int Main2.test23(boolean) load_store_elimination (before) +## CHECK: NewInstance +## CHECK: InstanceFieldSet +## CHECK: InstanceFieldGet +## CHECK: InstanceFieldSet +## CHECK: InstanceFieldGet +## CHECK: Return +## CHECK: InstanceFieldGet +## CHECK: InstanceFieldSet + +## CHECK-START: int Main2.test23(boolean) load_store_elimination (after) +## CHECK: NewInstance +## CHECK-NOT: InstanceFieldSet +## CHECK-NOT: InstanceFieldGet +## CHECK: InstanceFieldSet +## CHECK: InstanceFieldGet +## CHECK: Return +## CHECK-NOT: InstanceFieldGet +## CHECK: InstanceFieldSet + +# Test store elimination on merging. + +# Original java source: +# +# static int test23(boolean b) { +# TestClass obj = new TestClass(); +# obj.i = 3; // This store can be eliminated since the value flows into each branch. +# if (b) { +# obj.i += 1; // This store cannot be eliminated due to the merge later. +# } else { +# obj.i += 2; // This store cannot be eliminated due to the merge later. +# } +# return obj.i; +# } + +.method public static test23(Z)I + .registers 3 + .param p0, "b" # Z + + .prologue + .line 582 + new-instance v0, LTestClass; + + invoke-direct {v0}, LTestClass;->()V + + .line 583 + .local v0, "obj":LTestClass; + const/4 v1, 0x3 + + iput v1, v0, LTestClass;->i:I + + .line 584 + if-eqz p0, :cond_13 + + .line 585 + iget v1, v0, LTestClass;->i:I + + add-int/lit8 v1, v1, 0x1 + + iput v1, v0, LTestClass;->i:I + + .line 589 + :goto_10 + iget v1, v0, LTestClass;->i:I + + return v1 + + .line 587 + :cond_13 + iget v1, v0, LTestClass;->i:I + + add-int/lit8 v1, v1, 0x2 + + iput v1, v0, LTestClass;->i:I + + goto :goto_10 +.end method + +## CHECK-START: float Main2.test24() load_store_elimination (before) +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> FloatConstant 8 +## CHECK-DAG: <> FloatConstant 42 +## CHECK-DAG: <> NewInstance +## CHECK-DAG: InstanceFieldSet [<>,<>] +## CHECK-DAG: InstanceFieldSet [<>,<>] +## CHECK-DAG: <> InstanceFieldGet [<>] +## CHECK-DAG: <> InstanceFieldGet [<>] +## CHECK-DAG: <> Select [<>,<>,<>] +## CHECK-DAG: Return [<>] + +# Original java source: +# +# static float test24() { +# float a = 42.0f; +# TestClass3 obj = new TestClass3(); +# if (obj.test1) { +# a = obj.floatField; +# } +# return a; +# } + +.method public static test24()F + .registers 3 + + .prologue + .line 612 + const/high16 v0, 0x42280000 # 42.0f + + .line 613 + .local v0, "a":F + new-instance v1, LTestClass3; + + invoke-direct {v1}, LTestClass3;->()V + + .line 614 + .local v1, "obj":LTestClass3; + iget-boolean v2, v1, LTestClass3;->test1:Z + + if-eqz v2, :cond_d + + .line 615 + iget v0, v1, LTestClass3;->floatField:F + + .line 617 + :cond_d + return v0 +.end method diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java index 93c153821b..bd1744cc5f 100644 --- a/test/530-checker-lse/src/Main.java +++ b/test/530-checker-lse/src/Main.java @@ -14,6 +14,8 @@ * limitations under the License. */ +import java.lang.reflect.Method; + class Circle { Circle(double radius) { this.radius = radius; @@ -167,51 +169,6 @@ public class Main { return obj.i + obj1.j + obj2.i + obj2.j; } - /// CHECK-START: int Main.test4(TestClass, boolean) load_store_elimination (before) - /// CHECK: InstanceFieldSet - /// CHECK: InstanceFieldGet - /// CHECK: Return - /// CHECK: InstanceFieldSet - - /// CHECK-START: int Main.test4(TestClass, boolean) load_store_elimination (after) - /// CHECK: InstanceFieldSet - /// CHECK-NOT: NullCheck - /// CHECK-NOT: InstanceFieldGet - /// CHECK: Return - /// CHECK: InstanceFieldSet - - // Set and merge the same value in two branches. - static int test4(TestClass obj, boolean b) { - if (b) { - obj.i = 1; - } else { - obj.i = 1; - } - return obj.i; - } - - /// CHECK-START: int Main.test5(TestClass, boolean) load_store_elimination (before) - /// CHECK: InstanceFieldSet - /// CHECK: InstanceFieldGet - /// CHECK: Return - /// CHECK: InstanceFieldSet - - /// CHECK-START: int Main.test5(TestClass, boolean) load_store_elimination (after) - /// CHECK: InstanceFieldSet - /// CHECK: InstanceFieldGet - /// CHECK: Return - /// CHECK: InstanceFieldSet - - // Set and merge different values in two branches. - static int test5(TestClass obj, boolean b) { - if (b) { - obj.i = 1; - } else { - obj.i = 2; - } - return obj.i; - } - /// CHECK-START: int Main.test6(TestClass, TestClass, boolean) load_store_elimination (before) /// CHECK: InstanceFieldSet /// CHECK: InstanceFieldSet @@ -557,66 +514,6 @@ public class Main { return sum; } - /// CHECK-START: int Main.test23(boolean) load_store_elimination (before) - /// CHECK: NewInstance - /// CHECK: InstanceFieldSet - /// CHECK: InstanceFieldGet - /// CHECK: InstanceFieldSet - /// CHECK: InstanceFieldGet - /// CHECK: Return - /// CHECK: InstanceFieldGet - /// CHECK: InstanceFieldSet - - /// CHECK-START: int Main.test23(boolean) load_store_elimination (after) - /// CHECK: NewInstance - /// CHECK-NOT: InstanceFieldSet - /// CHECK-NOT: InstanceFieldGet - /// CHECK: InstanceFieldSet - /// CHECK: InstanceFieldGet - /// CHECK: Return - /// CHECK-NOT: InstanceFieldGet - /// CHECK: InstanceFieldSet - - // Test store elimination on merging. - static int test23(boolean b) { - TestClass obj = new TestClass(); - obj.i = 3; // This store can be eliminated since the value flows into each branch. - if (b) { - obj.i += 1; // This store cannot be eliminated due to the merge later. - } else { - obj.i += 2; // This store cannot be eliminated due to the merge later. - } - return obj.i; - } - - /// CHECK-START: float Main.test24() load_store_elimination (before) - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> FloatConstant 8 - /// CHECK-DAG: <> FloatConstant 42 - /// CHECK-DAG: <> NewInstance - /// CHECK-DAG: InstanceFieldSet [<>,<>] - /// CHECK-DAG: InstanceFieldSet [<>,<>] - /// CHECK-DAG: <> InstanceFieldGet [<>] - /// CHECK-DAG: <> InstanceFieldGet [<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - static float test24() { - float a = 42.0f; - TestClass3 obj = new TestClass3(); - if (obj.test1) { - a = obj.floatField; - } - return a; - } - /// CHECK-START: void Main.testFinalizable() load_store_elimination (before) /// CHECK: NewInstance /// CHECK: InstanceFieldSet @@ -1275,7 +1172,14 @@ public class Main { } } - public static void main(String[] args) { + public static void main(String[] args) throws Exception { + + Class main2 = Class.forName("Main2"); + Method test4 = main2.getMethod("test4", TestClass.class, boolean.class); + Method test5 = main2.getMethod("test5", TestClass.class, boolean.class); + Method test23 = main2.getMethod("test23", boolean.class); + Method test24 = main2.getMethod("test24"); + assertDoubleEquals(Math.PI * Math.PI * Math.PI, calcCircleArea(Math.PI)); assertIntEquals(test1(new TestClass(), new TestClass()), 3); assertIntEquals(test2(new TestClass()), 1); @@ -1283,10 +1187,10 @@ public class Main { TestClass obj2 = new TestClass(); obj1.next = obj2; assertIntEquals(test3(obj1), 10); - assertIntEquals(test4(new TestClass(), true), 1); - assertIntEquals(test4(new TestClass(), false), 1); - assertIntEquals(test5(new TestClass(), true), 1); - assertIntEquals(test5(new TestClass(), false), 2); + assertIntEquals((int)test4.invoke(null, new TestClass(), true), 1); + assertIntEquals((int)test4.invoke(null, new TestClass(), false), 1); + assertIntEquals((int)test5.invoke(null, new TestClass(), true), 1); + assertIntEquals((int)test5.invoke(null, new TestClass(), false), 2); assertIntEquals(test6(new TestClass(), new TestClass(), true), 4); assertIntEquals(test6(new TestClass(), new TestClass(), false), 2); assertIntEquals(test7(new TestClass()), 1); @@ -1312,9 +1216,9 @@ public class Main { assertFloatEquals(test20().i, 0); test21(new TestClass()); assertIntEquals(test22(), 13); - assertIntEquals(test23(true), 4); - assertIntEquals(test23(false), 5); - assertFloatEquals(test24(), 8.0f); + assertIntEquals((int)test23.invoke(null, true), 4); + assertIntEquals((int)test23.invoke(null, false), 5); + assertFloatEquals((float)test24.invoke(null), 8.0f); testFinalizableByForcingGc(); assertIntEquals($noinline$testHSelect(true), 0xdead); int[] array = {2, 5, 9, -1, -3, 10, 8, 4}; -- GitLab From 21bd011c4d5364161baabd4dabc06c1eaed2ef7e Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Thu, 31 May 2018 13:13:44 +0100 Subject: [PATCH 506/749] Fix exit status of ART script tools/teardown-buildbot-device.sh. Simplify the logic deleting property_context files from the chroot and prevent it from making the script return a non-zero exit status. Test: sh -x art/tools/teardown-buildbot-device.sh; echo $? Bug: 34729697 Change-Id: Ia9f34eda2c167b26a6396937c7c4ecec01c86718 --- tools/teardown-buildbot-device.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/teardown-buildbot-device.sh b/tools/teardown-buildbot-device.sh index df239a28bc..be2cb274fc 100755 --- a/tools/teardown-buildbot-device.sh +++ b/tools/teardown-buildbot-device.sh @@ -65,6 +65,6 @@ if [[ -n "$ART_TEST_CHROOT" ]]; then /plat_property_contexts \ /nonplat_property_contexts" for f in $property_context_files; do - adb shell test -f "$f" "&&" rm -f "$ART_TEST_CHROOT$f" + adb shell rm -f "$ART_TEST_CHROOT$f" done fi -- GitLab From e2e5e66a9cec84f83c2a47a815bae28f9c849d8d Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Thu, 31 May 2018 15:29:05 +0100 Subject: [PATCH 507/749] Improve diagnostics in ART script tools/teardown-buildbot-device.sh. In case of unmounting failure, show the list of open files in the directory that we tried to unmount. Also refactor the logic of unmounting and removing directories under the chroot dir. Test: art/tools/setup-buildbot-device.sh; sh -x art/tools/teardown-buildbot-device.sh Bug: 34729697 Change-Id: I22f9414c3a6e2ae37f90a7eeff058e2c4252dbbf --- tools/teardown-buildbot-device.sh | 38 ++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/tools/teardown-buildbot-device.sh b/tools/teardown-buildbot-device.sh index be2cb274fc..bf14ca4f9f 100755 --- a/tools/teardown-buildbot-device.sh +++ b/tools/teardown-buildbot-device.sh @@ -25,6 +25,27 @@ adb root adb wait-for-device if [[ -n "$ART_TEST_CHROOT" ]]; then + + # remove_filesystem_from_chroot DIR-IN-CHROOT FSTYPE REMOVE-DIR-IN-CHROOT + # ----------------------------------------------------------------------- + # Unmount filesystem with type FSTYPE mounted in directory DIR-IN-CHROOT + # under the chroot directory. + # Remove DIR-IN-CHROOT under the chroot if REMOVE-DIR-IN-CHROOT is + # true. + remove_filesystem_from_chroot() { + local dir_in_chroot=$1 + local fstype=$2 + local remove_dir=$3 + local dir="$ART_TEST_CHROOT/$dir_in_chroot" + adb shell test -d "$dir" \ + && adb shell mount | grep -q "^$fstype on $dir type $fstype " \ + && if adb shell umount "$dir"; then + $remove_dir && adb shell rmdir "$dir" + else + adb shell lsof "$dir" + fi + } + # Tear down the chroot dir. echo -e "${green}Tear down the chroot dir in $ART_TEST_CHROOT${nc}" @@ -32,22 +53,17 @@ if [[ -n "$ART_TEST_CHROOT" ]]; then [[ "x$ART_TEST_CHROOT" = x/* ]] || { echo "$ART_TEST_CHROOT is not an absolute path"; exit 1; } # Remove /dev from chroot. - adb shell mount | grep -q "^tmpfs on $ART_TEST_CHROOT/dev type tmpfs " \ - && adb shell umount "$ART_TEST_CHROOT/dev" \ - && adb shell rmdir "$ART_TEST_CHROOT/dev" + remove_filesystem_from_chroot dev tmpfs true # Remove /sys/kernel/debug from chroot. - adb shell mount | grep -q "^debugfs on $ART_TEST_CHROOT/sys/kernel/debug type debugfs " \ - && adb shell umount "$ART_TEST_CHROOT/sys/kernel/debug" + # The /sys/kernel/debug directory under the chroot dir cannot be + # deleted, as it is part of the host device's /sys filesystem. + remove_filesystem_from_chroot sys/kernel/debug debugfs false # Remove /sys from chroot. - adb shell mount | grep -q "^sysfs on $ART_TEST_CHROOT/sys type sysfs " \ - && adb shell umount "$ART_TEST_CHROOT/sys" \ - && adb shell rmdir "$ART_TEST_CHROOT/sys" + remove_filesystem_from_chroot sys sysfs true # Remove /proc from chroot. - adb shell mount | grep -q "^proc on $ART_TEST_CHROOT/proc type proc " \ - && adb shell umount "$ART_TEST_CHROOT/proc" \ - && adb shell rmdir "$ART_TEST_CHROOT/proc" + remove_filesystem_from_chroot proc proc true # Remove /etc from chroot. adb shell rm -f "$ART_TEST_CHROOT/etc" -- GitLab From 2649ecf6c59a29262556aa356fbf894d49df8fe7 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 29 May 2018 14:07:52 -0700 Subject: [PATCH 508/749] Move runtime/ to ClassAccessor Added more iterator helpers, added logic for dealing with hidden API flags. Bug: 77709234 Bug: 79758018 Test: test-art-host Change-Id: I3e6d34dd3fe61f1a3256a1cc4c74b63a6bdf514c --- libdexfile/dex/class_accessor-inl.h | 101 +++++--- libdexfile/dex/class_accessor.h | 78 ++++-- libdexfile/dex/class_accessor_test.cc | 10 + libdexfile/dex/dex_file.cc | 17 +- libdexfile/dex/dex_file.h | 4 +- openjdkjvmti/fixed_up_dex_file.cc | 15 +- runtime/art_method.cc | 23 +- runtime/class_linker.cc | 290 +++++++++++----------- runtime/class_linker.h | 13 +- runtime/native/dalvik_system_VMRuntime.cc | 29 +-- runtime/vdex_file.cc | 40 ++- runtime/verifier/method_verifier.cc | 164 ++++-------- runtime/verifier/method_verifier.h | 17 -- 13 files changed, 384 insertions(+), 417 deletions(-) diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h index 49ca98d47f..3bb9e93e5a 100644 --- a/libdexfile/dex/class_accessor-inl.h +++ b/libdexfile/dex/class_accessor-inl.h @@ -37,30 +37,26 @@ inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const DexFile::Clas num_direct_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u), num_virtual_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u) {} -inline const uint8_t* ClassAccessor::Method::Read(const uint8_t* ptr) { - index_ += DecodeUnsignedLeb128(&ptr); - access_flags_ = DecodeUnsignedLeb128(&ptr); - code_off_ = DecodeUnsignedLeb128(&ptr); - return ptr; +inline void ClassAccessor::Method::Read() { + index_ += DecodeUnsignedLeb128(&ptr_pos_); + access_flags_ = DecodeUnsignedLeb128(&ptr_pos_); + code_off_ = DecodeUnsignedLeb128(&ptr_pos_); } -inline const uint8_t* ClassAccessor::Field::Read(const uint8_t* ptr) { - index_ += DecodeUnsignedLeb128(&ptr); - access_flags_ = DecodeUnsignedLeb128(&ptr); - return ptr; +inline void ClassAccessor::Field::Read() { + index_ += DecodeUnsignedLeb128(&ptr_pos_); + access_flags_ = DecodeUnsignedLeb128(&ptr_pos_); } template -inline const uint8_t* ClassAccessor::VisitMembers(size_t count, - const Visitor& visitor, - const uint8_t* ptr, - DataType* data) const { +inline void ClassAccessor::VisitMembers(size_t count, + const Visitor& visitor, + DataType* data) const { DCHECK(data != nullptr); for ( ; count != 0; --count) { - ptr = data->Read(ptr); + data->Read(); visitor(*data); } - return ptr; } template > + ClassAccessor::GetFieldsInternal(size_t count) const { + return { DataIterator(dex_file_, 0u, num_static_fields_, count, ptr_pos_), + DataIterator(dex_file_, count, num_static_fields_, count, ptr_pos_) }; +} + +// Return an iteration range for the first methods. +inline IterationRange> + ClassAccessor::GetMethodsInternal(size_t count) const { + // Skip over the fields. + Field field(dex_file_, ptr_pos_); + VisitMembers(NumFields(), VoidFunctor(), &field); + // Return the iterator pair. + return { DataIterator(dex_file_, 0u, num_direct_methods_, count, field.ptr_pos_), + DataIterator(dex_file_, count, num_direct_methods_, count, field.ptr_pos_) }; +} + inline IterationRange> ClassAccessor::GetFields() const { - const uint32_t limit = num_static_fields_ + num_instance_fields_; - return { DataIterator(dex_file_, 0u, num_static_fields_, limit, ptr_pos_), - DataIterator(dex_file_, limit, num_static_fields_, limit, ptr_pos_) }; + return GetFieldsInternal(num_static_fields_ + num_instance_fields_); +} + +inline IterationRange> + ClassAccessor::GetStaticFields() const { + return GetFieldsInternal(num_static_fields_); +} + + +inline IterationRange> + ClassAccessor::GetInstanceFields() const { + IterationRange> fields = GetFields(); + // Skip the static fields. + return { std::next(fields.begin(), NumStaticFields()), fields.end() }; } inline IterationRange> ClassAccessor::GetMethods() const { - // Skip over the fields. - Field field(dex_file_); - const size_t skip_count = num_static_fields_ + num_instance_fields_; - const uint8_t* ptr_pos = VisitMembers(skip_count, VoidFunctor(), ptr_pos_, &field); - // Return the iterator pair for all the methods. - const uint32_t limit = num_direct_methods_ + num_virtual_methods_; - return { DataIterator(dex_file_, 0u, num_direct_methods_, limit, ptr_pos), - DataIterator(dex_file_, limit, num_direct_methods_, limit, ptr_pos) }; + return GetMethodsInternal(NumMethods()); +} + +inline IterationRange> + ClassAccessor::GetDirectMethods() const { + return GetMethodsInternal(NumDirectMethods()); +} + +inline IterationRange> + ClassAccessor::GetVirtualMethods() const { + IterationRange> methods = GetMethods(); + // Skip the direct fields. + return { std::next(methods.begin(), NumDirectMethods()), methods.end() }; +} + +inline void ClassAccessor::Field::UnHideAccessFlags() const { + DexFile::UnHideAccessFlags(const_cast(ptr_pos_), GetAccessFlags(), /*is_method*/ false); +} + +inline void ClassAccessor::Method::UnHideAccessFlags() const { + DexFile::UnHideAccessFlags(const_cast(ptr_pos_), GetAccessFlags(), /*is_method*/ true); } } // namespace art diff --git a/libdexfile/dex/class_accessor.h b/libdexfile/dex/class_accessor.h index dda6e1c1a6..4f0fd32e31 100644 --- a/libdexfile/dex/class_accessor.h +++ b/libdexfile/dex/class_accessor.h @@ -20,6 +20,7 @@ #include "base/utils.h" #include "code_item_accessors.h" #include "dex_file.h" +#include "hidden_api_access_flags.h" #include "invoke_type.h" #include "method_reference.h" #include "modifiers.h" @@ -33,12 +34,18 @@ class ClassAccessor { private: class BaseItem { public: + explicit BaseItem(const uint8_t* ptr_pos) : ptr_pos_(ptr_pos) {} + uint32_t GetIndex() const { return index_; } uint32_t GetAccessFlags() const { - return access_flags_; + return HiddenApiAccessFlags::RemoveFromDex(access_flags_); + } + + HiddenApiAccessFlags::ApiList DecodeHiddenAccessFlags() const { + return HiddenApiAccessFlags::DecodeFromDex(access_flags_); } bool IsFinal() const { @@ -46,6 +53,8 @@ class ClassAccessor { } protected: + // Internal data pointer for reading. + const uint8_t* ptr_pos_ = nullptr; uint32_t index_ = 0u; uint32_t access_flags_ = 0u; }; @@ -76,13 +85,18 @@ class ClassAccessor { return is_static_or_direct_; } + // Unhide the hidden API access flags at the iterator position. TODO: Deprecate. + void UnHideAccessFlags() const; + private: explicit Method(const DexFile& dex_file, + const uint8_t* ptr_pos, bool is_static_or_direct = true) - : dex_file_(dex_file), + : BaseItem(ptr_pos), + dex_file_(dex_file), is_static_or_direct_(is_static_or_direct) {} - const uint8_t* Read(const uint8_t* ptr); + void Read(); InvokeType GetDirectMethodInvokeType() const { return (GetAccessFlags() & kAccStatic) != 0 ? kStatic : kDirect; @@ -99,6 +113,7 @@ class ClassAccessor { } } + // Move to virtual method section. void NextSection() { DCHECK(is_static_or_direct_) << "Already in the virtual methods section"; is_static_or_direct_ = false; @@ -115,20 +130,31 @@ class ClassAccessor { // A decoded version of the field of a class_data_item. class Field : public BaseItem { public: - explicit Field(const DexFile& dex_file) : dex_file_(dex_file) {} + explicit Field(const DexFile& dex_file, + const uint8_t* ptr_pos) : BaseItem(ptr_pos), dex_file_(dex_file) {} const DexFile& GetDexFile() const { return dex_file_; } + bool IsStatic() const { + return is_static_; + } + + // Unhide the hidden API access flags at the iterator position. TODO: Deprecate. + void UnHideAccessFlags() const; + private: - const uint8_t* Read(const uint8_t* ptr); + void Read(); + // Move to instance fields section. void NextSection() { index_ = 0u; + is_static_ = false; } const DexFile& dex_file_; + bool is_static_ = true; friend class ClassAccessor; }; @@ -144,11 +170,10 @@ class ClassAccessor { uint32_t partition_pos, uint32_t iterator_end, const uint8_t* ptr_pos) - : data_(dex_file), + : data_(dex_file, ptr_pos), position_(position), partition_pos_(partition_pos), - iterator_end_(iterator_end), - ptr_pos_(ptr_pos) { + iterator_end_(iterator_end) { ReadData(); } @@ -205,8 +230,7 @@ class ClassAccessor { if (position_ == partition_pos_) { data_.NextSection(); } - DCHECK(ptr_pos_ != nullptr); - ptr_pos_ = data_.Read(ptr_pos_); + data_.Read(); } } @@ -217,8 +241,6 @@ class ClassAccessor { const uint32_t partition_pos_; // At iterator_end_, the iterator is no longer valid. const uint32_t iterator_end_; - // Internal data pointer. - const uint8_t* ptr_pos_; }; // Not explicit specifically for range-based loops. @@ -252,9 +274,21 @@ class ClassAccessor { // Return the iteration range for all the fields. IterationRange> GetFields() const; + // Return the iteration range for all the static fields. + IterationRange> GetStaticFields() const; + + // Return the iteration range for all the instance fields. + IterationRange> GetInstanceFields() const; + // Return the iteration range for all the methods. IterationRange> GetMethods() const; + // Return the iteration range for the direct methods. + IterationRange> GetDirectMethods() const; + + // Return the iteration range for the virtual methods. + IterationRange> GetVirtualMethods() const; + uint32_t NumStaticFields() const { return num_static_fields_; } @@ -263,6 +297,10 @@ class ClassAccessor { return num_instance_fields_; } + uint32_t NumFields() const { + return NumStaticFields() + NumInstanceFields(); + } + uint32_t NumDirectMethods() const { return num_direct_methods_; } @@ -285,14 +323,22 @@ class ClassAccessor { return dex_file_; } + bool HasClassData() const { + return ptr_pos_ != nullptr; + } + protected: // Template visitor to reduce copy paste for visiting elements. // No thread safety analysis since the visitor may require capabilities. template - const uint8_t* VisitMembers(size_t count, - const Visitor& visitor, - const uint8_t* ptr, - DataType* data) const NO_THREAD_SAFETY_ANALYSIS; + void VisitMembers(size_t count, const Visitor& visitor, DataType* data) const + NO_THREAD_SAFETY_ANALYSIS; + + // Return an iteration range for the first fields. + IterationRange> GetFieldsInternal(size_t count) const; + + // Return an iteration range for the first methods. + IterationRange> GetMethodsInternal(size_t count) const; const DexFile& dex_file_; const dex::TypeIndex descriptor_index_ = {}; diff --git a/libdexfile/dex/class_accessor_test.cc b/libdexfile/dex/class_accessor_test.cc index 95380d8140..d0533c1811 100644 --- a/libdexfile/dex/class_accessor_test.cc +++ b/libdexfile/dex/class_accessor_test.cc @@ -38,18 +38,27 @@ TEST_F(ClassAccessorTest, TestVisiting) { auto fields = accessor.GetFields(); auto method_it = methods.begin(); auto field_it = fields.begin(); + auto instance_fields = accessor.GetInstanceFields(); + auto instance_field_it = instance_fields.begin(); accessor.VisitFieldsAndMethods( // Static fields. [&](const ClassAccessor::Field& field) { + EXPECT_TRUE(field.IsStatic()); + EXPECT_TRUE(field_it->IsStatic()); EXPECT_EQ(field.GetIndex(), field_it->GetIndex()); EXPECT_EQ(field.GetAccessFlags(), field_it->GetAccessFlags()); ++field_it; }, // Instance fields. [&](const ClassAccessor::Field& field) { + EXPECT_FALSE(field.IsStatic()); + EXPECT_FALSE(field_it->IsStatic()); EXPECT_EQ(field.GetIndex(), field_it->GetIndex()); EXPECT_EQ(field.GetAccessFlags(), field_it->GetAccessFlags()); + EXPECT_EQ(field.GetIndex(), instance_field_it->GetIndex()); + EXPECT_EQ(field.GetAccessFlags(), instance_field_it->GetAccessFlags()); ++field_it; + ++instance_field_it; }, // Direct methods. [&](const ClassAccessor::Method& method) { @@ -71,6 +80,7 @@ TEST_F(ClassAccessorTest, TestVisiting) { }); ASSERT_TRUE(field_it == fields.end()); ASSERT_TRUE(method_it == methods.end()); + ASSERT_TRUE(instance_field_it == instance_fields.end()); } EXPECT_EQ(class_def_idx, dex_file->NumClassDefs()); } diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc index 9de260c862..f570158dfb 100644 --- a/libdexfile/dex/dex_file.cc +++ b/libdexfile/dex/dex_file.cc @@ -45,19 +45,18 @@ static_assert(std::is_trivially_copyable::value, "StringIndex static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wrong"); static_assert(std::is_trivially_copyable::value, "TypeIndex not trivial"); -void DexFile::UnHideAccessFlags(ClassDataItemIterator& class_it) { - uint8_t* data = const_cast(class_it.DataPointer()); - uint32_t new_flag = class_it.GetMemberAccessFlags(); - bool is_method = class_it.IsAtMethod(); +void DexFile::UnHideAccessFlags(uint8_t* data_ptr, + uint32_t new_access_flags, + bool is_method) { // Go back 1 uleb to start. - data = ReverseSearchUnsignedLeb128(data); + data_ptr = ReverseSearchUnsignedLeb128(data_ptr); if (is_method) { // Methods have another uleb field before the access flags - data = ReverseSearchUnsignedLeb128(data); + data_ptr = ReverseSearchUnsignedLeb128(data_ptr); } - DCHECK_EQ(HiddenApiAccessFlags::RemoveFromDex(DecodeUnsignedLeb128WithoutMovingCursor(data)), - new_flag); - UpdateUnsignedLeb128(data, new_flag); + DCHECK_EQ(HiddenApiAccessFlags::RemoveFromDex(DecodeUnsignedLeb128WithoutMovingCursor(data_ptr)), + new_access_flags); + UpdateUnsignedLeb128(data_ptr, new_access_flags); } uint32_t DexFile::CalculateChecksum() const { diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index f1f8b505bd..ed219808d2 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -1010,8 +1010,8 @@ class DexFile { return container_.get(); } - // Changes the dex file pointed to by class_it to not have any hiddenapi flags. - static void UnHideAccessFlags(ClassDataItemIterator& class_it); + // Changes the dex class data pointed to by data_ptr it to not have any hiddenapi flags. + static void UnHideAccessFlags(uint8_t* data_ptr, uint32_t new_access_flags, bool is_method); inline IterationRange GetClasses() const; diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc index fcbafe7e71..a660fb56c4 100644 --- a/openjdkjvmti/fixed_up_dex_file.cc +++ b/openjdkjvmti/fixed_up_dex_file.cc @@ -31,6 +31,7 @@ #include "base/leb128.h" #include "fixed_up_dex_file.h" +#include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "dex/dex_file_verifier.h" @@ -51,14 +52,12 @@ static void RecomputeDexChecksum(art::DexFile* dex_file) { } static void UnhideApis(const art::DexFile& target_dex_file) { - for (uint32_t i = 0; i < target_dex_file.NumClassDefs(); ++i) { - const uint8_t* class_data = target_dex_file.GetClassData(target_dex_file.GetClassDef(i)); - if (class_data != nullptr) { - for (art::ClassDataItemIterator class_it(target_dex_file, class_data); - class_it.HasNext(); - class_it.Next()) { - art::DexFile::UnHideAccessFlags(class_it); - } + for (art::ClassAccessor accessor : target_dex_file.GetClasses()) { + for (const art::ClassAccessor::Field& field : accessor.GetFields()) { + field.UnHideAccessFlags(); + } + for (const art::ClassAccessor::Method& method : accessor.GetMethods()) { + method.UnHideAccessFlags(); } } } diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 151c36f3bc..4e9f3c52e2 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -26,6 +26,7 @@ #include "class_linker-inl.h" #include "class_root.h" #include "debugger.h" +#include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_exception_helpers.h" @@ -434,28 +435,14 @@ bool ArtMethod::IsPolymorphicSignature() { static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx); - const uint8_t* class_data = dex_file.GetClassData(class_def); - CHECK(class_data != nullptr); - ClassDataItemIterator it(dex_file, class_data); - it.SkipAllFields(); - // Process methods - size_t class_def_method_index = 0; - while (it.HasNextDirectMethod()) { - if (it.GetMemberIndex() == method_idx) { + ClassAccessor accessor(dex_file, dex_file.GetClassDef(class_def_idx)); + uint32_t class_def_method_index = 0u; + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + if (method.GetIndex() == method_idx) { return class_def_method_index; } class_def_method_index++; - it.Next(); } - while (it.HasNextVirtualMethod()) { - if (it.GetMemberIndex() == method_idx) { - return class_def_method_index; - } - class_def_method_index++; - it.Next(); - } - DCHECK(!it.HasNext()); LOG(FATAL) << "Failed to find method index " << method_idx << " in " << dex_file.GetLocation(); UNREACHABLE(); } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 095272394a..d6ac3bad52 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -56,6 +56,7 @@ #include "compiler_callbacks.h" #include "debug_print.h" #include "debugger.h" +#include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_exception_helpers.h" @@ -2734,52 +2735,50 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, uint32_t ClassLinker::SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def) { - const uint8_t* class_data = dex_file.GetClassData(dex_class_def); size_t num_ref = 0; size_t num_8 = 0; size_t num_16 = 0; size_t num_32 = 0; size_t num_64 = 0; - if (class_data != nullptr) { - // We allow duplicate definitions of the same field in a class_data_item - // but ignore the repeated indexes here, b/21868015. - uint32_t last_field_idx = dex::kDexNoIndex; - for (ClassDataItemIterator it(dex_file, class_data); it.HasNextStaticField(); it.Next()) { - uint32_t field_idx = it.GetMemberIndex(); - // Ordering enforced by DexFileVerifier. - DCHECK(last_field_idx == dex::kDexNoIndex || last_field_idx <= field_idx); - if (UNLIKELY(field_idx == last_field_idx)) { - continue; - } - last_field_idx = field_idx; - const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); - const char* descriptor = dex_file.GetFieldTypeDescriptor(field_id); - char c = descriptor[0]; - switch (c) { - case 'L': - case '[': - num_ref++; - break; - case 'J': - case 'D': - num_64++; - break; - case 'I': - case 'F': - num_32++; - break; - case 'S': - case 'C': - num_16++; - break; - case 'B': - case 'Z': - num_8++; - break; - default: - LOG(FATAL) << "Unknown descriptor: " << c; - UNREACHABLE(); - } + ClassAccessor accessor(dex_file, dex_class_def); + // We allow duplicate definitions of the same field in a class_data_item + // but ignore the repeated indexes here, b/21868015. + uint32_t last_field_idx = dex::kDexNoIndex; + for (const ClassAccessor::Field& field : accessor.GetStaticFields()) { + uint32_t field_idx = field.GetIndex(); + // Ordering enforced by DexFileVerifier. + DCHECK(last_field_idx == dex::kDexNoIndex || last_field_idx <= field_idx); + if (UNLIKELY(field_idx == last_field_idx)) { + continue; + } + last_field_idx = field_idx; + const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); + const char* descriptor = dex_file.GetFieldTypeDescriptor(field_id); + char c = descriptor[0]; + switch (c) { + case 'L': + case '[': + num_ref++; + break; + case 'J': + case 'D': + num_64++; + break; + case 'I': + case 'F': + num_32++; + break; + case 'S': + case 'C': + num_16++; + break; + case 'B': + case 'Z': + num_8++; + break; + default: + LOG(FATAL) << "Unknown descriptor: " << c; + UNREACHABLE(); } } return mirror::Class::ComputeClassSize(false, @@ -2877,17 +2876,15 @@ void ClassLinker::FixupStaticTrampolines(ObjPtr klass) { const DexFile& dex_file = klass->GetDexFile(); const DexFile::ClassDef* dex_class_def = klass->GetClassDef(); CHECK(dex_class_def != nullptr); - const uint8_t* class_data = dex_file.GetClassData(*dex_class_def); + ClassAccessor accessor(dex_file, *dex_class_def); // There should always be class data if there were direct methods. - CHECK(class_data != nullptr) << klass->PrettyDescriptor(); - ClassDataItemIterator it(dex_file, class_data); - it.SkipAllFields(); + CHECK(accessor.HasClassData()) << klass->PrettyDescriptor(); bool has_oat_class; OatFile::OatClass oat_class = OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class); // Link the code of methods skipped by LinkCode. - for (size_t method_index = 0; it.HasNextDirectMethod(); ++method_index, it.Next()) { + for (size_t method_index = 0; method_index < accessor.NumDirectMethods(); ++method_index) { ArtMethod* method = klass->GetDirectMethod(method_index, image_pointer_size_); if (!method->IsStatic()) { // Only update static methods. @@ -2996,17 +2993,6 @@ void ClassLinker::SetupClass(const DexFile& dex_file, klass->SetDexTypeIndex(dex_class_def.class_idx_); } -void ClassLinker::LoadClass(Thread* self, - const DexFile& dex_file, - const DexFile::ClassDef& dex_class_def, - Handle klass) { - const uint8_t* class_data = dex_file.GetClassData(dex_class_def); - if (class_data == nullptr) { - return; // no fields or methods - for example a marker interface - } - LoadClassMembers(self, dex_file, class_data, klass); -} - LengthPrefixedArray* ClassLinker::AllocArtFieldArray(Thread* self, LinearAlloc* allocator, size_t length) { @@ -3065,10 +3051,15 @@ LinearAlloc* ClassLinker::GetOrCreateAllocatorForClassLoader(ObjPtr klass) { +void ClassLinker::LoadClass(Thread* self, + const DexFile& dex_file, + const DexFile::ClassDef& dex_class_def, + Handle klass) { + ClassAccessor accessor(dex_file, dex_class_def); + if (!accessor.HasClassData()) { + return; + } + Runtime* const runtime = Runtime::Current(); { // Note: We cannot have thread suspension until the field and method arrays are setup or else // Class::VisitFieldRoots may miss some fields or methods. @@ -3077,45 +3068,79 @@ void ClassLinker::LoadClassMembers(Thread* self, // We allow duplicate definitions of the same field in a class_data_item // but ignore the repeated indexes here, b/21868015. LinearAlloc* const allocator = GetAllocatorForClassLoader(klass->GetClassLoader()); - ClassDataItemIterator it(dex_file, class_data); LengthPrefixedArray* sfields = AllocArtFieldArray(self, allocator, - it.NumStaticFields()); - size_t num_sfields = 0; - uint32_t last_field_idx = 0u; - for (; it.HasNextStaticField(); it.Next()) { - uint32_t field_idx = it.GetMemberIndex(); - DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier. - if (num_sfields == 0 || LIKELY(field_idx > last_field_idx)) { - DCHECK_LT(num_sfields, it.NumStaticFields()); - LoadField(it, klass, &sfields->At(num_sfields)); - ++num_sfields; - last_field_idx = field_idx; - } - } - - // Load instance fields. + accessor.NumStaticFields()); LengthPrefixedArray* ifields = AllocArtFieldArray(self, allocator, - it.NumInstanceFields()); + accessor.NumInstanceFields()); + size_t num_sfields = 0u; size_t num_ifields = 0u; - last_field_idx = 0u; - for (; it.HasNextInstanceField(); it.Next()) { - uint32_t field_idx = it.GetMemberIndex(); - DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier. - if (num_ifields == 0 || LIKELY(field_idx > last_field_idx)) { - DCHECK_LT(num_ifields, it.NumInstanceFields()); - LoadField(it, klass, &ifields->At(num_ifields)); - ++num_ifields; - last_field_idx = field_idx; - } - } + uint32_t last_static_field_idx = 0u; + uint32_t last_instance_field_idx = 0u; + + // Methods + bool has_oat_class = false; + const OatFile::OatClass oat_class = (runtime->IsStarted() && !runtime->IsAotCompiler()) + ? OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class) + : OatFile::OatClass::Invalid(); + const OatFile::OatClass* oat_class_ptr = has_oat_class ? &oat_class : nullptr; + klass->SetMethodsPtr( + AllocArtMethodArray(self, allocator, accessor.NumMethods()), + accessor.NumDirectMethods(), + accessor.NumVirtualMethods()); + size_t class_def_method_index = 0; + uint32_t last_dex_method_index = dex::kDexNoIndex; + size_t last_class_def_method_index = 0; - if (UNLIKELY(num_sfields != it.NumStaticFields()) || - UNLIKELY(num_ifields != it.NumInstanceFields())) { + // Use the visitor since the ranged based loops are bit slower from seeking. Seeking to the + // methods needs to decode all of the fields. + accessor.VisitFieldsAndMethods([&]( + const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t field_idx = field.GetIndex(); + DCHECK_GE(field_idx, last_static_field_idx); // Ordering enforced by DexFileVerifier. + if (num_sfields == 0 || LIKELY(field_idx > last_static_field_idx)) { + LoadField(field, klass, &sfields->At(num_sfields)); + ++num_sfields; + last_static_field_idx = field_idx; + } + }, [&](const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t field_idx = field.GetIndex(); + DCHECK_GE(field_idx, last_instance_field_idx); // Ordering enforced by DexFileVerifier. + if (num_ifields == 0 || LIKELY(field_idx > last_instance_field_idx)) { + LoadField(field, klass, &ifields->At(num_ifields)); + ++num_ifields; + last_instance_field_idx = field_idx; + } + }, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* art_method = klass->GetDirectMethodUnchecked(class_def_method_index, + image_pointer_size_); + LoadMethod(dex_file, method, klass, art_method); + LinkCode(this, art_method, oat_class_ptr, class_def_method_index); + uint32_t it_method_index = method.GetIndex(); + if (last_dex_method_index == it_method_index) { + // duplicate case + art_method->SetMethodIndex(last_class_def_method_index); + } else { + art_method->SetMethodIndex(class_def_method_index); + last_dex_method_index = it_method_index; + last_class_def_method_index = class_def_method_index; + } + ++class_def_method_index; + }, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* art_method = klass->GetVirtualMethodUnchecked( + class_def_method_index - accessor.NumDirectMethods(), + image_pointer_size_); + LoadMethod(dex_file, method, klass, art_method); + LinkCode(this, art_method, oat_class_ptr, class_def_method_index); + ++class_def_method_index; + }); + + if (UNLIKELY(num_ifields + num_sfields != accessor.NumFields())) { LOG(WARNING) << "Duplicate fields in class " << klass->PrettyDescriptor() - << " (unique static fields: " << num_sfields << "/" << it.NumStaticFields() - << ", unique instance fields: " << num_ifields << "/" << it.NumInstanceFields() << ")"; + << " (unique static fields: " << num_sfields << "/" << accessor.NumStaticFields() + << ", unique instance fields: " << num_ifields << "/" << accessor.NumInstanceFields() + << ")"; // NOTE: Not shrinking the over-allocated sfields/ifields, just setting size. if (sfields != nullptr) { sfields->SetSize(num_sfields); @@ -3129,87 +3154,49 @@ void ClassLinker::LoadClassMembers(Thread* self, DCHECK_EQ(klass->NumStaticFields(), num_sfields); klass->SetIFieldsPtr(ifields); DCHECK_EQ(klass->NumInstanceFields(), num_ifields); - // Load methods. - bool has_oat_class = false; - const OatFile::OatClass oat_class = - (Runtime::Current()->IsStarted() && !Runtime::Current()->IsAotCompiler()) - ? OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class) - : OatFile::OatClass::Invalid(); - const OatFile::OatClass* oat_class_ptr = has_oat_class ? &oat_class : nullptr; - klass->SetMethodsPtr( - AllocArtMethodArray(self, allocator, it.NumDirectMethods() + it.NumVirtualMethods()), - it.NumDirectMethods(), - it.NumVirtualMethods()); - size_t class_def_method_index = 0; - uint32_t last_dex_method_index = dex::kDexNoIndex; - size_t last_class_def_method_index = 0; - // TODO These should really use the iterators. - for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) { - ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_); - LoadMethod(dex_file, it, klass, method); - LinkCode(this, method, oat_class_ptr, class_def_method_index); - uint32_t it_method_index = it.GetMemberIndex(); - if (last_dex_method_index == it_method_index) { - // duplicate case - method->SetMethodIndex(last_class_def_method_index); - } else { - method->SetMethodIndex(class_def_method_index); - last_dex_method_index = it_method_index; - last_class_def_method_index = class_def_method_index; - } - class_def_method_index++; - } - for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) { - ArtMethod* method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_); - LoadMethod(dex_file, it, klass, method); - DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i); - LinkCode(this, method, oat_class_ptr, class_def_method_index); - class_def_method_index++; - } - DCHECK(!it.HasNext()); } // Ensure that the card is marked so that remembered sets pick up native roots. Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(klass.Get()); self->AllowThreadSuspension(); } -void ClassLinker::LoadField(const ClassDataItemIterator& it, +void ClassLinker::LoadField(const ClassAccessor::Field& field, Handle klass, ArtField* dst) { - const uint32_t field_idx = it.GetMemberIndex(); + const uint32_t field_idx = field.GetIndex(); dst->SetDexFieldIndex(field_idx); dst->SetDeclaringClass(klass.Get()); // Get access flags from the DexFile. If this is a boot class path class, // also set its runtime hidden API access flags. - uint32_t access_flags = it.GetFieldAccessFlags(); + uint32_t access_flags = field.GetAccessFlags(); if (klass->IsBootStrapClassLoaded()) { access_flags = - HiddenApiAccessFlags::EncodeForRuntime(access_flags, it.DecodeHiddenAccessFlags()); + HiddenApiAccessFlags::EncodeForRuntime(access_flags, field.DecodeHiddenAccessFlags()); } dst->SetAccessFlags(access_flags); } void ClassLinker::LoadMethod(const DexFile& dex_file, - const ClassDataItemIterator& it, + const ClassAccessor::Method& method, Handle klass, ArtMethod* dst) { - uint32_t dex_method_idx = it.GetMemberIndex(); + const uint32_t dex_method_idx = method.GetIndex(); const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_); ScopedAssertNoThreadSuspension ants("LoadMethod"); dst->SetDexMethodIndex(dex_method_idx); dst->SetDeclaringClass(klass.Get()); - dst->SetCodeItemOffset(it.GetMethodCodeItemOffset()); + dst->SetCodeItemOffset(method.GetCodeItemOffset()); // Get access flags from the DexFile. If this is a boot class path class, // also set its runtime hidden API access flags. - uint32_t access_flags = it.GetMethodAccessFlags(); + uint32_t access_flags = method.GetAccessFlags(); if (klass->IsBootStrapClassLoaded()) { access_flags = - HiddenApiAccessFlags::EncodeForRuntime(access_flags, it.DecodeHiddenAccessFlags()); + HiddenApiAccessFlags::EncodeForRuntime(access_flags, method.DecodeHiddenAccessFlags()); } if (UNLIKELY(strcmp("finalize", method_name) == 0)) { @@ -4772,24 +4759,29 @@ bool ClassLinker::InitializeClass(Thread* self, Handle klass, this, *dex_class_def); const DexFile& dex_file = *dex_cache->GetDexFile(); - const uint8_t* class_data = dex_file.GetClassData(*dex_class_def); - ClassDataItemIterator field_it(dex_file, class_data); + if (value_it.HasNext()) { - DCHECK(field_it.HasNextStaticField()); + ClassAccessor accessor(dex_file, *dex_class_def); CHECK(can_init_statics); - for ( ; value_it.HasNext(); value_it.Next(), field_it.Next()) { - ArtField* field = ResolveField( - field_it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ true); + for (const ClassAccessor::Field& field : accessor.GetStaticFields()) { + if (!value_it.HasNext()) { + break; + } + ArtField* art_field = ResolveField(field.GetIndex(), + dex_cache, + class_loader, + /* is_static */ true); if (Runtime::Current()->IsActiveTransaction()) { - value_it.ReadValueToField(field); + value_it.ReadValueToField(art_field); } else { - value_it.ReadValueToField(field); + value_it.ReadValueToField(art_field); } if (self->IsExceptionPending()) { break; } - DCHECK(!value_it.HasNext() || field_it.HasNextStaticField()); + value_it.Next(); } + DCHECK(self->IsExceptionPending() || !value_it.HasNext()); } } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 1f94c43408..1912d21d98 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -27,6 +27,7 @@ #include "base/enums.h" #include "base/macros.h" #include "base/mutex.h" +#include "dex/class_accessor.h" #include "dex/dex_cache_resolved_classes.h" #include "dex/dex_file.h" #include "dex/dex_file_types.h" @@ -823,18 +824,14 @@ class ClassLinker { const DexFile::ClassDef& dex_class_def, Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); - void LoadClassMembers(Thread* self, - const DexFile& dex_file, - const uint8_t* class_data, - Handle klass) - REQUIRES_SHARED(Locks::mutator_lock_); - void LoadField(const ClassDataItemIterator& it, Handle klass, ArtField* dst) + void LoadField(const ClassAccessor::Field& field, Handle klass, ArtField* dst) REQUIRES_SHARED(Locks::mutator_lock_); void LoadMethod(const DexFile& dex_file, - const ClassDataItemIterator& it, - Handle klass, ArtMethod* dst) + const ClassAccessor::Method& method, + Handle klass, + ArtMethod* dst) REQUIRES_SHARED(Locks::mutator_lock_); void FixupStaticTrampolines(ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 6c820190b4..c9deb526c2 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -32,6 +32,7 @@ extern "C" void android_set_application_target_sdk_version(uint32_t version); #include "class_linker-inl.h" #include "common_throws.h" #include "debugger.h" +#include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_types.h" #include "gc/accounting/card_table-inl.h" @@ -573,30 +574,12 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { } if (kPreloadDexCachesFieldsAndMethods) { - for (size_t class_def_index = 0; - class_def_index < dex_file->NumClassDefs(); - class_def_index++) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - continue; + for (ClassAccessor accessor : dex_file->GetClasses()) { + for (const ClassAccessor::Field& field : accessor.GetFields()) { + PreloadDexCachesResolveField(dex_cache, field.GetIndex(), field.IsStatic()); } - ClassDataItemIterator it(*dex_file, class_data); - for (; it.HasNextStaticField(); it.Next()) { - uint32_t field_idx = it.GetMemberIndex(); - PreloadDexCachesResolveField(dex_cache, field_idx, true); - } - for (; it.HasNextInstanceField(); it.Next()) { - uint32_t field_idx = it.GetMemberIndex(); - PreloadDexCachesResolveField(dex_cache, field_idx, false); - } - for (; it.HasNextDirectMethod(); it.Next()) { - uint32_t method_idx = it.GetMemberIndex(); - PreloadDexCachesResolveMethod(dex_cache, method_idx); - } - for (; it.HasNextVirtualMethod(); it.Next()) { - uint32_t method_idx = it.GetMemberIndex(); - PreloadDexCachesResolveMethod(dex_cache, method_idx); + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + PreloadDexCachesResolveMethod(dex_cache, method.GetIndex()); } } } diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index 838d7f14bc..e2f42c937d 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -28,6 +28,7 @@ #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "dex/art_dex_file_loader.h" +#include "dex/class_accessor-inl.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "dex/hidden_api_access_flags.h" @@ -283,31 +284,26 @@ void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, std::unordered_set unquickened_code_item; CompactOffsetTable::Accessor accessor(GetQuickenInfoOffsetTable(source_dex_begin, quickening_info)); - for (uint32_t i = 0; i < target_dex_file.NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = target_dex_file.GetClassDef(i); - const uint8_t* class_data = target_dex_file.GetClassData(class_def); - if (class_data != nullptr) { - for (ClassDataItemIterator class_it(target_dex_file, class_data); - class_it.HasNext(); - class_it.Next()) { - if (class_it.IsAtMethod()) { - const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem(); - if (code_item != nullptr && unquickened_code_item.emplace(code_item).second) { - const uint32_t offset = accessor.GetOffset(class_it.GetMemberIndex()); - // Offset being 0 means not quickened. - if (offset != 0u) { - ArrayRef quicken_data = GetQuickeningInfoAt(quickening_info, offset); - optimizer::ArtDecompileDEX( - target_dex_file, - *code_item, - quicken_data, - decompile_return_instruction); - } - } + for (ClassAccessor class_accessor : target_dex_file.GetClasses()) { + for (const ClassAccessor::Method& method : class_accessor.GetMethods()) { + const DexFile::CodeItem* code_item = method.GetCodeItem(); + if (code_item != nullptr && unquickened_code_item.emplace(code_item).second) { + const uint32_t offset = accessor.GetOffset(method.GetIndex()); + // Offset being 0 means not quickened. + if (offset != 0u) { + ArrayRef quicken_data = GetQuickeningInfoAt(quickening_info, offset); + optimizer::ArtDecompileDEX( + target_dex_file, + *code_item, + quicken_data, + decompile_return_instruction); } - DexFile::UnHideAccessFlags(class_it); + method.UnHideAccessFlags(); } } + for (const ClassAccessor::Field& field : class_accessor.GetFields()) { + field.UnHideAccessFlags(); + } } } diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index cc71dc5f84..8169568413 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -35,6 +35,7 @@ #include "class_linker.h" #include "class_root.h" #include "compiler_callbacks.h" +#include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_exception_helpers.h" @@ -190,11 +191,6 @@ FailureKind MethodVerifier::VerifyClass(Thread* self, error); } -template -static bool HasNextMethod(ClassDataItemIterator* it) { - return kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod(); -} - static FailureKind FailureKindMax(FailureKind fk1, FailureKind fk2) { static_assert(FailureKind::kNoFailure < FailureKind::kSoftFailure && FailureKind::kSoftFailure < FailureKind::kHardFailure, @@ -207,45 +203,51 @@ void MethodVerifier::FailureData::Merge(const MethodVerifier::FailureData& fd) { types |= fd.types; } -template -MethodVerifier::FailureData MethodVerifier::VerifyMethods(Thread* self, - ClassLinker* linker, - const DexFile* dex_file, - const DexFile::ClassDef& class_def, - ClassDataItemIterator* it, - Handle dex_cache, - Handle class_loader, - CompilerCallbacks* callbacks, - bool allow_soft_failures, - HardFailLogMode log_level, - bool need_precise_constants, - std::string* error_string) { - DCHECK(it != nullptr); +FailureKind MethodVerifier::VerifyClass(Thread* self, + const DexFile* dex_file, + Handle dex_cache, + Handle class_loader, + const DexFile::ClassDef& class_def, + CompilerCallbacks* callbacks, + bool allow_soft_failures, + HardFailLogMode log_level, + std::string* error) { + SCOPED_TRACE << "VerifyClass " << PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); + // A class must not be abstract and final. + if ((class_def.access_flags_ & (kAccAbstract | kAccFinal)) == (kAccAbstract | kAccFinal)) { + *error = "Verifier rejected class "; + *error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); + *error += ": class is abstract and final."; + return FailureKind::kHardFailure; + } + + ClassAccessor accessor(*dex_file, class_def); + + int64_t previous_method_idx[2] = { -1, -1 }; MethodVerifier::FailureData failure_data; + ClassLinker* const linker = Runtime::Current()->GetClassLinker(); - int64_t previous_method_idx = -1; - while (HasNextMethod(it)) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + int64_t* previous_idx = &previous_method_idx[method.IsStaticOrDirect() ? 0u : 1u]; self->AllowThreadSuspension(); - uint32_t method_idx = it->GetMemberIndex(); - if (method_idx == previous_method_idx) { + const uint32_t method_idx = method.GetIndex(); + if (method_idx == *previous_idx) { // smali can create dex files with two encoded_methods sharing the same method_idx // http://code.google.com/p/smali/issues/detail?id=119 - it->Next(); continue; } - previous_method_idx = method_idx; - InvokeType type = it->GetMethodInvokeType(class_def); - ArtMethod* method = linker->ResolveMethod( + *previous_idx = method_idx; + const InvokeType type = method.GetInvokeType(class_def.access_flags_); + ArtMethod* resolved_method = linker->ResolveMethod( method_idx, dex_cache, class_loader, /* referrer */ nullptr, type); - if (method == nullptr) { + if (resolved_method == nullptr) { DCHECK(self->IsExceptionPending()); // We couldn't resolve the method, but continue regardless. self->ClearException(); } else { - DCHECK(method->GetDeclaringClassUnchecked() != nullptr) << type; + DCHECK(resolved_method->GetDeclaringClassUnchecked() != nullptr) << type; } - StackHandleScope<1> hs(self); std::string hard_failure_msg; MethodVerifier::FailureData result = VerifyMethod(self, method_idx, @@ -253,99 +255,39 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethods(Thread* self, dex_cache, class_loader, class_def, - it->GetMethodCodeItem(), - method, - it->GetMethodAccessFlags(), + method.GetCodeItem(), + resolved_method, + method.GetAccessFlags(), callbacks, allow_soft_failures, log_level, - need_precise_constants, + /*need_precise_constants*/ false, &hard_failure_msg); if (result.kind == FailureKind::kHardFailure) { if (failure_data.kind == FailureKind::kHardFailure) { // If we logged an error before, we need a newline. - *error_string += "\n"; + *error += "\n"; } else { // If we didn't log a hard failure before, print the header of the message. - *error_string += "Verifier rejected class "; - *error_string += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); - *error_string += ":"; + *error += "Verifier rejected class "; + *error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); + *error += ":"; } - *error_string += " "; - *error_string += hard_failure_msg; + *error += " "; + *error += hard_failure_msg; } failure_data.Merge(result); - it->Next(); - } - - return failure_data; -} - -FailureKind MethodVerifier::VerifyClass(Thread* self, - const DexFile* dex_file, - Handle dex_cache, - Handle class_loader, - const DexFile::ClassDef& class_def, - CompilerCallbacks* callbacks, - bool allow_soft_failures, - HardFailLogMode log_level, - std::string* error) { - SCOPED_TRACE << "VerifyClass " << PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); - - // A class must not be abstract and final. - if ((class_def.access_flags_ & (kAccAbstract | kAccFinal)) == (kAccAbstract | kAccFinal)) { - *error = "Verifier rejected class "; - *error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); - *error += ": class is abstract and final."; - return FailureKind::kHardFailure; } - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - // empty class, probably a marker interface - return FailureKind::kNoFailure; - } - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - ClassLinker* linker = Runtime::Current()->GetClassLinker(); - // Direct methods. - MethodVerifier::FailureData data1 = VerifyMethods(self, - linker, - dex_file, - class_def, - &it, - dex_cache, - class_loader, - callbacks, - allow_soft_failures, - log_level, - false /* need precise constants */, - error); - // Virtual methods. - MethodVerifier::FailureData data2 = VerifyMethods(self, - linker, - dex_file, - class_def, - &it, - dex_cache, - class_loader, - callbacks, - allow_soft_failures, - log_level, - false /* need precise constants */, - error); - - data1.Merge(data2); - - if (data1.kind == FailureKind::kNoFailure) { + if (failure_data.kind == FailureKind::kNoFailure) { return FailureKind::kNoFailure; } else { - if ((data1.types & VERIFY_ERROR_LOCKING) != 0) { + if ((failure_data.types & VERIFY_ERROR_LOCKING) != 0) { // Print a warning about expected slow-down. Use a string temporary to print one contiguous // warning. std::string tmp = StringPrintf("Class %s failed lock verification and will run slower.", - PrettyDescriptor(dex_file->GetClassDescriptor(class_def)).c_str()); + PrettyDescriptor(accessor.GetDescriptor()).c_str()); if (!gPrintedDxMonitorText) { tmp = tmp + "\nCommon causes for lock verification issues are non-optimized dex code\n" "and incorrect proguard optimizations."; @@ -353,7 +295,7 @@ FailureKind MethodVerifier::VerifyClass(Thread* self, } LOG(WARNING) << tmp; } - return data1.kind; + return failure_data.kind; } } @@ -1924,15 +1866,11 @@ bool MethodVerifier::CodeFlowVerifyMethod() { static uint32_t GetFirstFinalInstanceFieldIndex(const DexFile& dex_file, dex::TypeIndex type_idx) { const DexFile::ClassDef* class_def = dex_file.FindClassDef(type_idx); DCHECK(class_def != nullptr); - const uint8_t* class_data = dex_file.GetClassData(*class_def); - DCHECK(class_data != nullptr); - ClassDataItemIterator it(dex_file, class_data); - it.SkipStaticFields(); - while (it.HasNextInstanceField()) { - if ((it.GetFieldAccessFlags() & kAccFinal) != 0) { - return it.GetMemberIndex(); - } - it.Next(); + ClassAccessor accessor(dex_file, *class_def); + for (const ClassAccessor::Field& field : accessor.GetInstanceFields()) { + if (field.IsFinal()) { + return field.GetIndex(); + } } return dex::kDexNoIndex; } diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index b2adc62a97..c7368d7b39 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -275,23 +275,6 @@ class MethodVerifier { void Merge(const FailureData& src); }; - // Verify all direct or virtual methods of a class. The method assumes that the iterator is - // positioned correctly, and the iterator will be updated. - template - static FailureData VerifyMethods(Thread* self, - ClassLinker* linker, - const DexFile* dex_file, - const DexFile::ClassDef& class_def, - ClassDataItemIterator* it, - Handle dex_cache, - Handle class_loader, - CompilerCallbacks* callbacks, - bool allow_soft_failures, - HardFailLogMode log_level, - bool need_precise_constants, - std::string* error_string) - REQUIRES_SHARED(Locks::mutator_lock_); - /* * Perform verification on a single method. * -- GitLab From 2bd4fff4b58d724832a8593bfcd15244cefcc395 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Fri, 1 Jun 2018 06:27:09 +0100 Subject: [PATCH 509/749] ART: Remove unused WellKnownClasses Test: art/test.py --host Bug: 74943277 Change-Id: I7bd7f3b05bc802579211a31a08ab494021b1ccbd --- runtime/well_known_classes.cc | 31 ------------------------------- runtime/well_known_classes.h | 10 ---------- 2 files changed, 41 deletions(-) diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index f7cdf3920a..c64e7bbca1 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -55,8 +55,6 @@ jclass WellKnownClasses::java_lang_ClassLoader; jclass WellKnownClasses::java_lang_ClassNotFoundException; jclass WellKnownClasses::java_lang_Daemons; jclass WellKnownClasses::java_lang_Error; -jclass WellKnownClasses::java_lang_invoke_MethodHandle; -jclass WellKnownClasses::java_lang_invoke_VarHandle; jclass WellKnownClasses::java_lang_IllegalAccessError; jclass WellKnownClasses::java_lang_NoClassDefFoundError; jclass WellKnownClasses::java_lang_Object; @@ -74,7 +72,6 @@ jclass WellKnownClasses::java_lang_ThreadGroup; jclass WellKnownClasses::java_lang_Throwable; jclass WellKnownClasses::java_nio_ByteBuffer; jclass WellKnownClasses::java_nio_DirectByteBuffer; -jclass WellKnownClasses::java_util_ArrayList; jclass WellKnownClasses::java_util_Collections; jclass WellKnownClasses::java_util_function_Consumer; jclass WellKnownClasses::libcore_reflect_AnnotationFactory; @@ -90,14 +87,11 @@ jmethodID WellKnownClasses::java_lang_Byte_valueOf; jmethodID WellKnownClasses::java_lang_Character_valueOf; jmethodID WellKnownClasses::java_lang_ClassLoader_loadClass; jmethodID WellKnownClasses::java_lang_ClassNotFoundException_init; -jmethodID WellKnownClasses::java_lang_Daemons_requestHeapTrim; jmethodID WellKnownClasses::java_lang_Daemons_start; jmethodID WellKnownClasses::java_lang_Daemons_stop; jmethodID WellKnownClasses::java_lang_Double_valueOf; jmethodID WellKnownClasses::java_lang_Float_valueOf; jmethodID WellKnownClasses::java_lang_Integer_valueOf; -jmethodID WellKnownClasses::java_lang_invoke_MethodHandle_invoke; -jmethodID WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact; jmethodID WellKnownClasses::java_lang_invoke_MethodHandles_lookup; jmethodID WellKnownClasses::java_lang_invoke_MethodHandles_Lookup_findConstructor; jmethodID WellKnownClasses::java_lang_Long_valueOf; @@ -108,7 +102,6 @@ jmethodID WellKnownClasses::java_lang_reflect_Proxy_invoke; jmethodID WellKnownClasses::java_lang_Runtime_nativeLoad; jmethodID WellKnownClasses::java_lang_Short_valueOf; jmethodID WellKnownClasses::java_lang_String_charAt; -jmethodID WellKnownClasses::java_lang_System_runFinalization = nullptr; jmethodID WellKnownClasses::java_lang_Thread_dispatchUncaughtException; jmethodID WellKnownClasses::java_lang_Thread_init; jmethodID WellKnownClasses::java_lang_Thread_run; @@ -144,7 +137,6 @@ jfieldID WellKnownClasses::java_lang_Throwable_detailMessage; jfieldID WellKnownClasses::java_lang_Throwable_stackTrace; jfieldID WellKnownClasses::java_lang_Throwable_stackState; jfieldID WellKnownClasses::java_lang_Throwable_suppressedExceptions; -jfieldID WellKnownClasses::java_lang_reflect_Proxy_h; jfieldID WellKnownClasses::java_nio_ByteBuffer_address; jfieldID WellKnownClasses::java_nio_ByteBuffer_hb; jfieldID WellKnownClasses::java_nio_ByteBuffer_isReadOnly; @@ -152,8 +144,6 @@ jfieldID WellKnownClasses::java_nio_ByteBuffer_limit; jfieldID WellKnownClasses::java_nio_ByteBuffer_offset; jfieldID WellKnownClasses::java_nio_DirectByteBuffer_capacity; jfieldID WellKnownClasses::java_nio_DirectByteBuffer_effectiveDirectAddress; -jfieldID WellKnownClasses::java_util_ArrayList_array; -jfieldID WellKnownClasses::java_util_ArrayList_size; jfieldID WellKnownClasses::java_util_Collections_EMPTY_LIST; jfieldID WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT; jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data; @@ -323,8 +313,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_OutOfMemoryError = CacheClass(env, "java/lang/OutOfMemoryError"); java_lang_Error = CacheClass(env, "java/lang/Error"); java_lang_IllegalAccessError = CacheClass(env, "java/lang/IllegalAccessError"); - java_lang_invoke_MethodHandle = CacheClass(env, "java/lang/invoke/MethodHandle"); - java_lang_invoke_VarHandle = CacheClass(env, "java/lang/invoke/VarHandle"); java_lang_NoClassDefFoundError = CacheClass(env, "java/lang/NoClassDefFoundError"); java_lang_reflect_Parameter = CacheClass(env, "java/lang/reflect/Parameter"); java_lang_reflect_Parameter__array = CacheClass(env, "[Ljava/lang/reflect/Parameter;"); @@ -339,7 +327,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Throwable = CacheClass(env, "java/lang/Throwable"); java_nio_ByteBuffer = CacheClass(env, "java/nio/ByteBuffer"); java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer"); - java_util_ArrayList = CacheClass(env, "java/util/ArrayList"); java_util_Collections = CacheClass(env, "java/util/Collections"); java_util_function_Consumer = CacheClass(env, "java/util/function/Consumer"); libcore_reflect_AnnotationFactory = CacheClass(env, "libcore/reflect/AnnotationFactory"); @@ -353,11 +340,8 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_ClassNotFoundException_init = CacheMethod(env, java_lang_ClassNotFoundException, false, "", "(Ljava/lang/String;Ljava/lang/Throwable;)V"); java_lang_ClassLoader_loadClass = CacheMethod(env, java_lang_ClassLoader, false, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); - java_lang_Daemons_requestHeapTrim = CacheMethod(env, java_lang_Daemons, true, "requestHeapTrim", "()V"); java_lang_Daemons_start = CacheMethod(env, java_lang_Daemons, true, "start", "()V"); java_lang_Daemons_stop = CacheMethod(env, java_lang_Daemons, true, "stop", "()V"); - java_lang_invoke_MethodHandle_invoke = CacheMethod(env, java_lang_invoke_MethodHandle, false, "invoke", "([Ljava/lang/Object;)Ljava/lang/Object;"); - java_lang_invoke_MethodHandle_invokeExact = CacheMethod(env, java_lang_invoke_MethodHandle, false, "invokeExact", "([Ljava/lang/Object;)Ljava/lang/Object;"); java_lang_invoke_MethodHandles_lookup = CacheMethod(env, "java/lang/invoke/MethodHandles", true, "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;"); java_lang_invoke_MethodHandles_Lookup_findConstructor = CacheMethod(env, "java/lang/invoke/MethodHandles$Lookup", false, "findConstructor", "(Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;"); @@ -408,8 +392,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_nio_ByteBuffer_offset = CacheField(env, java_nio_ByteBuffer, false, "offset", "I"); java_nio_DirectByteBuffer_capacity = CacheField(env, java_nio_DirectByteBuffer, false, "capacity", "I"); java_nio_DirectByteBuffer_effectiveDirectAddress = CacheField(env, java_nio_DirectByteBuffer, false, "address", "J"); - java_util_ArrayList_array = CacheField(env, java_util_ArrayList, false, "elementData", "[Ljava/lang/Object;"); - java_util_ArrayList_size = CacheField(env, java_util_ArrayList, false, "size", "I"); java_util_Collections_EMPTY_LIST = CacheField(env, java_util_Collections, true, "EMPTY_LIST", "Ljava/util/List;"); libcore_util_EmptyArray_STACK_TRACE_ELEMENT = CacheField(env, libcore_util_EmptyArray, true, "STACK_TRACE_ELEMENT", "[Ljava/lang/StackTraceElement;"); org_apache_harmony_dalvik_ddmc_Chunk_data = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "data", "[B"); @@ -440,9 +422,6 @@ void WellKnownClasses::LateInit(JNIEnv* env) { CacheMethod(env, java_lang_reflect_Proxy, true, "invoke", "(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;" "[Ljava/lang/Object;)Ljava/lang/Object;"); - java_lang_reflect_Proxy_h = - CacheField(env, java_lang_reflect_Proxy, false, "h", - "Ljava/lang/reflect/InvocationHandler;"); } void WellKnownClasses::Clear() { @@ -464,8 +443,6 @@ void WellKnownClasses::Clear() { java_lang_Daemons = nullptr; java_lang_Error = nullptr; java_lang_IllegalAccessError = nullptr; - java_lang_invoke_MethodHandle = nullptr; - java_lang_invoke_VarHandle = nullptr; java_lang_NoClassDefFoundError = nullptr; java_lang_Object = nullptr; java_lang_OutOfMemoryError = nullptr; @@ -480,7 +457,6 @@ void WellKnownClasses::Clear() { java_lang_Thread = nullptr; java_lang_ThreadGroup = nullptr; java_lang_Throwable = nullptr; - java_util_ArrayList = nullptr; java_util_Collections = nullptr; java_nio_ByteBuffer = nullptr; java_nio_DirectByteBuffer = nullptr; @@ -497,14 +473,11 @@ void WellKnownClasses::Clear() { java_lang_Character_valueOf = nullptr; java_lang_ClassLoader_loadClass = nullptr; java_lang_ClassNotFoundException_init = nullptr; - java_lang_Daemons_requestHeapTrim = nullptr; java_lang_Daemons_start = nullptr; java_lang_Daemons_stop = nullptr; java_lang_Double_valueOf = nullptr; java_lang_Float_valueOf = nullptr; java_lang_Integer_valueOf = nullptr; - java_lang_invoke_MethodHandle_invoke = nullptr; - java_lang_invoke_MethodHandle_invokeExact = nullptr; java_lang_invoke_MethodHandles_lookup = nullptr; java_lang_invoke_MethodHandles_Lookup_findConstructor = nullptr; java_lang_Long_valueOf = nullptr; @@ -515,7 +488,6 @@ void WellKnownClasses::Clear() { java_lang_Runtime_nativeLoad = nullptr; java_lang_Short_valueOf = nullptr; java_lang_String_charAt = nullptr; - java_lang_System_runFinalization = nullptr; java_lang_Thread_dispatchUncaughtException = nullptr; java_lang_Thread_init = nullptr; java_lang_Thread_run = nullptr; @@ -533,7 +505,6 @@ void WellKnownClasses::Clear() { dalvik_system_DexPathList_dexElements = nullptr; dalvik_system_DexPathList__Element_dexFile = nullptr; dalvik_system_VMRuntime_nonSdkApiUsageConsumer = nullptr; - java_lang_reflect_Proxy_h = nullptr; java_lang_Thread_daemon = nullptr; java_lang_Thread_group = nullptr; java_lang_Thread_lock = nullptr; @@ -558,8 +529,6 @@ void WellKnownClasses::Clear() { java_nio_ByteBuffer_offset = nullptr; java_nio_DirectByteBuffer_capacity = nullptr; java_nio_DirectByteBuffer_effectiveDirectAddress = nullptr; - java_util_ArrayList_array = nullptr; - java_util_ArrayList_size = nullptr; java_util_Collections_EMPTY_LIST = nullptr; libcore_util_EmptyArray_STACK_TRACE_ELEMENT = nullptr; org_apache_harmony_dalvik_ddmc_Chunk_data = nullptr; diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index c06e4a71ce..c81062f594 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -66,8 +66,6 @@ struct WellKnownClasses { static jclass java_lang_Daemons; static jclass java_lang_Error; static jclass java_lang_IllegalAccessError; - static jclass java_lang_invoke_MethodHandle; - static jclass java_lang_invoke_VarHandle; static jclass java_lang_NoClassDefFoundError; static jclass java_lang_Object; static jclass java_lang_OutOfMemoryError; @@ -82,7 +80,6 @@ struct WellKnownClasses { static jclass java_lang_Thread; static jclass java_lang_ThreadGroup; static jclass java_lang_Throwable; - static jclass java_util_ArrayList; static jclass java_util_Collections; static jclass java_util_function_Consumer; static jclass java_nio_ByteBuffer; @@ -100,14 +97,11 @@ struct WellKnownClasses { static jmethodID java_lang_Character_valueOf; static jmethodID java_lang_ClassLoader_loadClass; static jmethodID java_lang_ClassNotFoundException_init; - static jmethodID java_lang_Daemons_requestHeapTrim; static jmethodID java_lang_Daemons_start; static jmethodID java_lang_Daemons_stop; static jmethodID java_lang_Double_valueOf; static jmethodID java_lang_Float_valueOf; static jmethodID java_lang_Integer_valueOf; - static jmethodID java_lang_invoke_MethodHandle_invoke; - static jmethodID java_lang_invoke_MethodHandle_invokeExact; static jmethodID java_lang_invoke_MethodHandles_lookup; static jmethodID java_lang_invoke_MethodHandles_Lookup_findConstructor; static jmethodID java_lang_Long_valueOf; @@ -118,7 +112,6 @@ struct WellKnownClasses { static jmethodID java_lang_Runtime_nativeLoad; static jmethodID java_lang_Short_valueOf; static jmethodID java_lang_String_charAt; - static jmethodID java_lang_System_runFinalization; static jmethodID java_lang_Thread_dispatchUncaughtException; static jmethodID java_lang_Thread_init; static jmethodID java_lang_Thread_run; @@ -137,7 +130,6 @@ struct WellKnownClasses { static jfieldID dalvik_system_DexPathList_dexElements; static jfieldID dalvik_system_DexPathList__Element_dexFile; static jfieldID dalvik_system_VMRuntime_nonSdkApiUsageConsumer; - static jfieldID java_lang_reflect_Proxy_h; static jfieldID java_lang_Thread_daemon; static jfieldID java_lang_Thread_group; static jfieldID java_lang_Thread_lock; @@ -163,8 +155,6 @@ struct WellKnownClasses { static jfieldID java_nio_DirectByteBuffer_capacity; static jfieldID java_nio_DirectByteBuffer_effectiveDirectAddress; - static jfieldID java_util_ArrayList_array; - static jfieldID java_util_ArrayList_size; static jfieldID java_util_Collections_EMPTY_LIST; static jfieldID libcore_util_EmptyArray_STACK_TRACE_ELEMENT; static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_data; -- GitLab From 5dd3aa71eca714a83aa26dbbcbd51a6145da5fec Mon Sep 17 00:00:00 2001 From: Tamas Kenez Date: Tue, 1 May 2018 09:36:50 +0200 Subject: [PATCH 510/749] Enable D8 for ART tests already working with D8. Bug: 65168732 Test: art/test.py -j20 --host -b Tested with --gcstress and --target locally. Change-Id: Ie598973c97bbbd78d441d448c51e59599023404b --- test/549-checker-types-merge/build | 20 -------------------- test/567-checker-compare/build | 20 -------------------- test/910-methods/build | 20 -------------------- 3 files changed, 60 deletions(-) delete mode 100644 test/549-checker-types-merge/build delete mode 100644 test/567-checker-compare/build delete mode 100644 test/910-methods/build diff --git a/test/549-checker-types-merge/build b/test/549-checker-types-merge/build deleted file mode 100644 index 10ffcc537d..0000000000 --- a/test/549-checker-types-merge/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/567-checker-compare/build b/test/567-checker-compare/build deleted file mode 100644 index 10ffcc537d..0000000000 --- a/test/567-checker-compare/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/910-methods/build b/test/910-methods/build deleted file mode 100644 index 10ffcc537d..0000000000 --- a/test/910-methods/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" -- GitLab From 09c5ca40635faee00f40f6ca0581dd475efd545e Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 31 May 2018 15:15:31 +0100 Subject: [PATCH 511/749] Clean up class resolution and lookup. Simplify the code and avoid read barriers when appropriate. Relax class status check in ArtField::GetDeclaringClass() because we can see ClassStatus::kIdx from the from-space class object for kReadBarrierOption=kWithoutReadBarrier. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Change-Id: I83886a64fe5a99a1c3c30eab3b35dae449e6b4bc --- runtime/art_field-inl.h | 24 +++++----- runtime/art_field.h | 3 ++ runtime/art_method-inl.h | 2 +- runtime/class_linker-inl.h | 56 +++++++++++++++-------- runtime/class_linker.cc | 13 ++++++ runtime/class_linker.h | 22 +++++---- runtime/interpreter/interpreter_common.cc | 5 +- 7 files changed, 81 insertions(+), 44 deletions(-) diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h index 384581fc4f..baa5102f5d 100644 --- a/runtime/art_field-inl.h +++ b/runtime/art_field-inl.h @@ -34,12 +34,16 @@ namespace art { +inline bool ArtField::IsProxyField() { + return GetDeclaringClass()->IsProxyClass(); +} + template inline ObjPtr ArtField::GetDeclaringClass() { GcRootSource gc_root_source(this); ObjPtr result = declaring_class_.Read(&gc_root_source); DCHECK(result != nullptr); - DCHECK(result->IsLoaded() || result->IsErroneous()) << result->GetStatus(); + DCHECK(result->IsIdxLoaded() || result->IsErroneous()) << result->GetStatus(); return result; } @@ -302,25 +306,21 @@ inline bool ArtField::IsPrimitiveType() REQUIRES_SHARED(Locks::mutator_lock_) { inline ObjPtr ArtField::LookupResolvedType() { ScopedAssertNoThreadSuspension ants(__FUNCTION__); - const uint32_t field_index = GetDexFieldIndex(); - ObjPtr declaring_class = GetDeclaringClass(); - if (UNLIKELY(declaring_class->IsProxyClass())) { + if (UNLIKELY(IsProxyField())) { return ProxyFindSystemClass(GetTypeDescriptor()); } ObjPtr type = Runtime::Current()->GetClassLinker()->LookupResolvedType( - declaring_class->GetDexFile().GetFieldId(field_index).type_idx_, declaring_class); + GetDexFile()->GetFieldId(GetDexFieldIndex()).type_idx_, this); DCHECK(!Thread::Current()->IsExceptionPending()); return type; } inline ObjPtr ArtField::ResolveType() { - const uint32_t field_index = GetDexFieldIndex(); - ObjPtr declaring_class = GetDeclaringClass(); - if (UNLIKELY(declaring_class->IsProxyClass())) { + if (UNLIKELY(IsProxyField())) { return ProxyFindSystemClass(GetTypeDescriptor()); } ObjPtr type = Runtime::Current()->GetClassLinker()->ResolveType( - declaring_class->GetDexFile().GetFieldId(field_index).type_idx_, declaring_class); + GetDexFile()->GetFieldId(GetDexFieldIndex()).type_idx_, this); DCHECK_EQ(type == nullptr, Thread::Current()->IsExceptionPending()); return type; } @@ -329,12 +329,14 @@ inline size_t ArtField::FieldSize() REQUIRES_SHARED(Locks::mutator_lock_) { return Primitive::ComponentSize(GetTypeAsPrimitiveType()); } +template inline ObjPtr ArtField::GetDexCache() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetDeclaringClass()->GetDexCache(); + ObjPtr klass = GetDeclaringClass(); + return klass->GetDexCache(); } inline const DexFile* ArtField::GetDexFile() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetDexCache()->GetDexFile(); + return GetDexCache()->GetDexFile(); } inline ObjPtr ArtField::GetStringName(Thread* self, bool resolve) { diff --git a/runtime/art_field.h b/runtime/art_field.h index f39af3900c..784a862425 100644 --- a/runtime/art_field.h +++ b/runtime/art_field.h @@ -215,6 +215,7 @@ class ArtField FINAL { size_t FieldSize() REQUIRES_SHARED(Locks::mutator_lock_); + template ObjPtr GetDexCache() REQUIRES_SHARED(Locks::mutator_lock_); const DexFile* GetDexFile() REQUIRES_SHARED(Locks::mutator_lock_); @@ -236,6 +237,8 @@ class ArtField FINAL { REQUIRES_SHARED(Locks::mutator_lock_); private: + bool IsProxyField() REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr ProxyFindSystemClass(const char* descriptor) REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr ResolveGetStringName(Thread* self, diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index c1fac364bb..ec66966869 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -324,7 +324,7 @@ inline mirror::ClassLoader* ArtMethod::GetClassLoader() { template inline mirror::DexCache* ArtMethod::GetDexCache() { if (LIKELY(!IsObsolete())) { - mirror::Class* klass = GetDeclaringClass(); + ObjPtr klass = GetDeclaringClass(); return klass->GetDexCache(); } else { DCHECK(!IsProxyMethod()); diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index 25eb85d675..888f713d8f 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -17,7 +17,10 @@ #ifndef ART_RUNTIME_CLASS_LINKER_INL_H_ #define ART_RUNTIME_CLASS_LINKER_INL_H_ -#include "art_field.h" +#include + +#include "art_field-inl.h" +#include "art_method-inl.h" #include "class_linker.h" #include "gc/heap-inl.h" #include "gc_root-inl.h" @@ -29,8 +32,6 @@ #include "obj_ptr-inl.h" #include "scoped_thread_state_change-inl.h" -#include - namespace art { inline ObjPtr ClassLinker::FindArrayClass(Thread* self, @@ -68,38 +69,41 @@ inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, HandleWrapperObjPtr referrer_wrapper = hs.NewHandleWrapper(&referrer); Thread::Current()->PoisonObjectPointers(); } - if (kIsDebugBuild) { - Thread::Current()->AssertNoPendingException(); - } + DCHECK(!Thread::Current()->IsExceptionPending()); // We do not need the read barrier for getting the DexCache for the initial resolved type // lookup as both from-space and to-space copies point to the same native resolved types array. ObjPtr resolved_type = referrer->GetDexCache()->GetResolvedType(type_idx); if (resolved_type == nullptr) { - StackHandleScope<2> hs(Thread::Current()); - Handle h_dex_cache(hs.NewHandle(referrer->GetDexCache())); - Handle class_loader(hs.NewHandle(referrer->GetClassLoader())); - resolved_type = DoResolveType(type_idx, h_dex_cache, class_loader); + resolved_type = DoResolveType(type_idx, referrer); } return resolved_type; } inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, - ArtMethod* referrer) { + ArtField* referrer) { Thread::PoisonObjectPointersIfDebug(); - if (kIsDebugBuild) { - Thread::Current()->AssertNoPendingException(); + DCHECK(!Thread::Current()->IsExceptionPending()); + // We do not need the read barrier for getting the DexCache for the initial resolved type + // lookup as both from-space and to-space copies point to the same native resolved types array. + ObjPtr resolved_type = + referrer->GetDexCache()->GetResolvedType(type_idx); + if (UNLIKELY(resolved_type == nullptr)) { + resolved_type = DoResolveType(type_idx, referrer->GetDeclaringClass()); } + return resolved_type; +} + +inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, + ArtMethod* referrer) { + Thread::PoisonObjectPointersIfDebug(); + DCHECK(!Thread::Current()->IsExceptionPending()); // We do not need the read barrier for getting the DexCache for the initial resolved type // lookup as both from-space and to-space copies point to the same native resolved types array. ObjPtr resolved_type = referrer->GetDexCache()->GetResolvedType(type_idx); if (UNLIKELY(resolved_type == nullptr)) { - StackHandleScope<2> hs(Thread::Current()); - ObjPtr referring_class = referrer->GetDeclaringClass(); - Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); - Handle class_loader(hs.NewHandle(referring_class->GetClassLoader())); - resolved_type = DoResolveType(type_idx, dex_cache, class_loader); + resolved_type = DoResolveType(type_idx, referrer->GetDeclaringClass()); } return resolved_type; } @@ -123,7 +127,19 @@ inline ObjPtr ClassLinker::LookupResolvedType(dex::TypeIndex type ObjPtr type = referrer->GetDexCache()->GetResolvedType(type_idx); if (type == nullptr) { - type = DoLookupResolvedType(type_idx, referrer->GetDexCache(), referrer->GetClassLoader()); + type = DoLookupResolvedType(type_idx, referrer); + } + return type; +} + +inline ObjPtr ClassLinker::LookupResolvedType(dex::TypeIndex type_idx, + ArtField* referrer) { + // We do not need the read barrier for getting the DexCache for the initial resolved type + // lookup as both from-space and to-space copies point to the same native resolved types array. + ObjPtr type = + referrer->GetDexCache()->GetResolvedType(type_idx); + if (type == nullptr) { + type = DoLookupResolvedType(type_idx, referrer->GetDeclaringClass()); } return type; } @@ -135,7 +151,7 @@ inline ObjPtr ClassLinker::LookupResolvedType(dex::TypeIndex type ObjPtr type = referrer->GetDexCache()->GetResolvedType(type_idx); if (type == nullptr) { - type = DoLookupResolvedType(type_idx, referrer->GetDexCache(), referrer->GetClassLoader()); + type = DoLookupResolvedType(type_idx, referrer->GetDeclaringClass()); } return type; } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index e46b980682..dccdff0a5d 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -7700,6 +7700,11 @@ ObjPtr ClassLinker::LookupString(dex::StringIndex string_idx, return string; } +ObjPtr ClassLinker::DoLookupResolvedType(dex::TypeIndex type_idx, + ObjPtr referrer) { + return DoLookupResolvedType(type_idx, referrer->GetDexCache(), referrer->GetClassLoader()); +} + ObjPtr ClassLinker::DoLookupResolvedType(dex::TypeIndex type_idx, ObjPtr dex_cache, ObjPtr class_loader) { @@ -7728,6 +7733,14 @@ ObjPtr ClassLinker::DoLookupResolvedType(dex::TypeIndex type_idx, return type; } +ObjPtr ClassLinker::DoResolveType(dex::TypeIndex type_idx, + ObjPtr referrer) { + StackHandleScope<2> hs(Thread::Current()); + Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); + Handle class_loader(hs.NewHandle(referrer->GetClassLoader())); + return DoResolveType(type_idx, dex_cache, class_loader); +} + ObjPtr ClassLinker::DoResolveType(dex::TypeIndex type_idx, Handle dex_cache, Handle class_loader) { diff --git a/runtime/class_linker.h b/runtime/class_linker.h index b38f01d835..32016fa12a 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -67,6 +67,8 @@ using MethodDexCachePair = NativeDexCachePair; using MethodDexCacheType = std::atomic; } // namespace mirror +class ArtField; +class ArtMethod; class ClassHierarchyAnalysis; enum class ClassRoot : uint32_t; class ClassTable; @@ -219,10 +221,9 @@ class ClassLinker { ObjPtr ResolveType(dex::TypeIndex type_idx, ObjPtr referrer) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - - // Resolve a type with the given index from the DexFile associated with the given `referrer`, - // storing the result in the DexCache. The `referrer` is used to identify the target DexCache - // and ClassLoader to use for resolution. + ObjPtr ResolveType(dex::TypeIndex type_idx, ArtField* referrer) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); ObjPtr ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); @@ -242,10 +243,8 @@ class ClassLinker { ObjPtr LookupResolvedType(dex::TypeIndex type_idx, ObjPtr referrer) REQUIRES_SHARED(Locks::mutator_lock_); - - // Look up a resolved type with the given index from the DexFile associated with the given - // `referrer`, storing the result in the DexCache. The `referrer` is used to identify the - // target DexCache and ClassLoader to use for lookup. + ObjPtr LookupResolvedType(dex::TypeIndex type_idx, ArtField* referrer) + REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr LookupResolvedType(dex::TypeIndex type_idx, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_); @@ -877,12 +876,19 @@ class ClassLinker { REQUIRES(!Locks::dex_lock_); // Implementation of LookupResolvedType() called when the type was not found in the dex cache. + ObjPtr DoLookupResolvedType(dex::TypeIndex type_idx, + ObjPtr referrer) + REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr DoLookupResolvedType(dex::TypeIndex type_idx, ObjPtr dex_cache, ObjPtr class_loader) REQUIRES_SHARED(Locks::mutator_lock_); // Implementation of ResolveType() called when the type was not found in the dex cache. + ObjPtr DoResolveType(dex::TypeIndex type_idx, + ObjPtr referrer) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); ObjPtr DoResolveType(dex::TypeIndex type_idx, Handle dex_cache, Handle class_loader) diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 56658c3005..90e89cf3db 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -958,12 +958,9 @@ static bool GetArgumentForBootstrapMethod(Thread* self, return true; } case EncodedArrayValueIterator::ValueType::kType: { - StackHandleScope<2> hs(self); - Handle class_loader(hs.NewHandle(referrer->GetClassLoader())); - Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); dex::TypeIndex index(static_cast(encoded_value->GetI())); ClassLinker* cl = Runtime::Current()->GetClassLinker(); - ObjPtr o = cl->ResolveType(index, dex_cache, class_loader); + ObjPtr o = cl->ResolveType(index, referrer); if (UNLIKELY(o.IsNull())) { DCHECK(self->IsExceptionPending()); return false; -- GitLab From 0f22ea560a4f282bf29ae59f291e4e9835b71dfa Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 31 May 2018 15:51:21 +0100 Subject: [PATCH 512/749] Add system and oahl stubs to veridex.zip Test: m dist Change-Id: I354d2f90c42c0cfa7e7d67052c68b455e12fdd5c --- tools/veridex/Android.mk | 13 +++++++++++-- tools/veridex/appcompat.sh | 6 +++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/tools/veridex/Android.mk b/tools/veridex/Android.mk index 83fa0d6397..f8463c1c33 100644 --- a/tools/veridex/Android.mk +++ b/tools/veridex/Android.mk @@ -46,10 +46,19 @@ VERIDEX_FILES := $(LOCAL_PATH)/appcompat.sh $(VERIDEX_FILES_PATH): PRIVATE_VERIDEX_FILES := $(VERIDEX_FILES) $(VERIDEX_FILES_PATH): PRIVATE_APP_COMPAT_LISTS := $(app_compat_lists) -$(VERIDEX_FILES_PATH) : $(SOONG_ZIP) $(VERIDEX_FILES) $(app_compat_lists) $(HOST_OUT_EXECUTABLES)/veridex +$(VERIDEX_FILES_PATH): PRIVATE_SYSTEM_STUBS_ZIP := $(dir $(VERIDEX_FILES_PATH))/system-stubs.zip +$(VERIDEX_FILES_PATH): PRIVATE_OAHL_STUBS_ZIP := $(dir $(VERIDEX_FILES_PATH))/org.apache.http.legacy-stubs.zip +$(VERIDEX_FILES_PATH) : $(SOONG_ZIP) $(VERIDEX_FILES) $(app_compat_lists) $(HOST_OUT_EXECUTABLES)/veridex $(system_stub_dex) $(oahl_stub_dex) + $(hide) rm -f $(PRIVATE_SYSTEM_STUBS_ZIP) $(PRIVATE_OAHL_STUBS_ZIP) + $(hide) zip -j $(PRIVATE_SYSTEM_STUBS_ZIP) $(dir $(system_stub_dex))/classes*.dex + $(hide) zip -j $(PRIVATE_OAHL_STUBS_ZIP) $(dir $(oahl_stub_dex))/classes*.dex $(hide) $(SOONG_ZIP) -o $@ -C art/tools/veridex -f $(PRIVATE_VERIDEX_FILES) \ -C $(dir $(lastword $(PRIVATE_APP_COMPAT_LISTS))) $(addprefix -f , $(PRIVATE_APP_COMPAT_LISTS)) \ - -C $(HOST_OUT_EXECUTABLES) -f $(HOST_OUT_EXECUTABLES)/veridex + -C $(HOST_OUT_EXECUTABLES) -f $(HOST_OUT_EXECUTABLES)/veridex \ + -C $(dir $(PRIVATE_SYSTEM_STUBS_ZIP)) -f $(PRIVATE_SYSTEM_STUBS_ZIP) \ + -C $(dir $(PRIVATE_OAHL_STUBS_ZIP)) -f $(PRIVATE_OAHL_STUBS_ZIP) + $(hide) rm -f $(PRIVATE_SYSTEM_STUBS_ZIP) + $(hide) rm -f $(PRIVATE_OAHL_STUBS_ZIP) # Make the zip file available for prebuilts. $(call dist-for-goals,sdk,$(VERIDEX_FILES_PATH)) diff --git a/tools/veridex/appcompat.sh b/tools/veridex/appcompat.sh index c07ab21a4b..e7b735d30b 100755 --- a/tools/veridex/appcompat.sh +++ b/tools/veridex/appcompat.sh @@ -25,10 +25,10 @@ if [[ -e ${SCRIPT_DIR}/veridex && \ -e ${SCRIPT_DIR}/hiddenapi-blacklist.txt && \ -e ${SCRIPT_DIR}/hiddenapi-light-greylist.txt && \ -e ${SCRIPT_DIR}/hiddenapi-dark-greylist.txt && \ - -e ${SCRIPT_DIR}/org.apache.http.legacy-stubs.dex && \ - -e ${SCRIPT_DIR}/system-stubs.dex ]]; then + -e ${SCRIPT_DIR}/org.apache.http.legacy-stubs.zip && \ + -e ${SCRIPT_DIR}/system-stubs.zip ]]; then exec ${SCRIPT_DIR}/veridex \ - --core-stubs=${SCRIPT_DIR}/system-stubs.dex:${SCRIPT_DIR}/org.apache.http.legacy-stubs.dex \ + --core-stubs=${SCRIPT_DIR}/system-stubs.zip:${SCRIPT_DIR}/org.apache.http.legacy-stubs.zip \ --blacklist=${SCRIPT_DIR}/hiddenapi-blacklist.txt \ --light-greylist=${SCRIPT_DIR}/hiddenapi-light-greylist.txt \ --dark-greylist=${SCRIPT_DIR}/hiddenapi-dark-greylist.txt \ -- GitLab From 71ec1cc0665cdb9d39f4fd284d68962020417a53 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Fri, 18 May 2018 15:57:25 +0100 Subject: [PATCH 513/749] Rewrite dex register map encoding in stackmaps. Simplify code by encoding dex register maps using BitTables. The overall design is unchanged (bitmask+indices+catalogue). This CL saves ~0.4% of .oat file size. The dex register map decoding is factor of 3 faster now (based on the time to verify the register maps on Arm). This is not too surprising as the old version was O(n^2). It also reduces compiler arena memory usage by 11% since the BitTableBuilder is more memory efficient, we store less intermediate data, and we deduplicate most data on the fly. Test: test-art-host-gtest-stack_map_test Change-Id: Ib703a5ddf7f581280522d589e4a2bfebe53c26a9 --- compiler/optimizing/code_generator.cc | 1 + compiler/optimizing/stack_map_stream.cc | 582 ++++++----------- compiler/optimizing/stack_map_stream.h | 220 +++---- compiler/optimizing/stack_map_test.cc | 244 +------ oatdump/oatdump.cc | 31 +- runtime/dex_register_location.h | 79 +++ runtime/oat.h | 4 +- runtime/quick_exception_handler.cc | 2 +- runtime/stack_map.cc | 229 +++---- runtime/stack_map.h | 834 +++++------------------- 10 files changed, 655 insertions(+), 1571 deletions(-) create mode 100644 runtime/dex_register_location.h diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index b358bfabe0..4791fa3fba 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -63,6 +63,7 @@ #include "parallel_move_resolver.h" #include "scoped_thread_state_change-inl.h" #include "ssa_liveness_analysis.h" +#include "stack_map.h" #include "stack_map_stream.h" #include "thread-current-inl.h" #include "utils/assembler.h" diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index b1dcb68415..fad0d7be1b 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -22,6 +22,7 @@ #include "optimizing/optimizing_compiler.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" +#include "stack_map.h" namespace art { @@ -36,404 +37,234 @@ void StackMapStream::SetStackMapNativePcOffset(size_t i, uint32_t native_pc_offs void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, uint32_t native_pc_offset, uint32_t register_mask, - BitVector* sp_mask, + BitVector* stack_mask, uint32_t num_dex_registers, - uint8_t inlining_depth) { - DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry"; - current_entry_.dex_pc = dex_pc; - current_entry_.packed_native_pc = StackMap::PackNativePc(native_pc_offset, instruction_set_); - current_entry_.register_mask = register_mask; - current_entry_.sp_mask = sp_mask; - current_entry_.inlining_depth = inlining_depth; - current_entry_.inline_infos_start_index = inline_infos_.size(); - current_entry_.stack_mask_index = 0; - current_entry_.dex_method_index = dex::kDexNoIndex; - current_entry_.dex_register_entry.num_dex_registers = num_dex_registers; - current_entry_.dex_register_entry.locations_start_index = dex_register_locations_.size(); - current_entry_.dex_register_entry.live_dex_registers_mask = nullptr; - if (num_dex_registers != 0u) { - current_entry_.dex_register_entry.live_dex_registers_mask = - ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream); - current_entry_.dex_register_entry.live_dex_registers_mask->ClearAllBits(); + uint8_t inlining_depth ATTRIBUTE_UNUSED) { + DCHECK(!in_stack_map_) << "Mismatched Begin/End calls"; + in_stack_map_ = true; + + current_stack_map_ = StackMapEntry { + .packed_native_pc = StackMap::PackNativePc(native_pc_offset, instruction_set_), + .dex_pc = dex_pc, + .register_mask_index = kNoValue, + .stack_mask_index = kNoValue, + .inline_info_index = kNoValue, + .dex_register_mask_index = kNoValue, + .dex_register_map_index = kNoValue, + }; + if (register_mask != 0) { + uint32_t shift = LeastSignificantBit(register_mask); + RegisterMaskEntry entry = { register_mask >> shift, shift }; + current_stack_map_.register_mask_index = register_masks_.Dedup(&entry); + } + // The compiler assumes the bit vector will be read during PrepareForFillIn(), + // and it might modify the data before that. Therefore, just store the pointer. + // See ClearSpillSlotsFromLoopPhisInStackMap in code_generator.h. + lazy_stack_masks_.push_back(stack_mask); + current_inline_infos_ = 0; + current_dex_registers_.clear(); + expected_num_dex_registers_ = num_dex_registers; + + if (kIsDebugBuild) { + dcheck_num_dex_registers_.push_back(num_dex_registers); } - current_dex_register_ = 0; } void StackMapStream::EndStackMapEntry() { - current_entry_.dex_register_map_index = AddDexRegisterMapEntry(current_entry_.dex_register_entry); - stack_maps_.push_back(current_entry_); - current_entry_ = StackMapEntry(); + DCHECK(in_stack_map_) << "Mismatched Begin/End calls"; + in_stack_map_ = false; + DCHECK_EQ(expected_num_dex_registers_, current_dex_registers_.size()); + + // Mark the last inline info as last in the list for the stack map. + if (current_inline_infos_ > 0) { + inline_infos_[inline_infos_.size() - 1].is_last = InlineInfo::kLast; + } + + stack_maps_.Add(current_stack_map_); } void StackMapStream::AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value) { - if (kind != DexRegisterLocation::Kind::kNone) { - // Ensure we only use non-compressed location kind at this stage. - DCHECK(DexRegisterLocation::IsShortLocationKind(kind)) << kind; - DexRegisterLocation location(kind, value); - - // Look for Dex register `location` in the location catalog (using the - // companion hash map of locations to indices). Use its index if it - // is already in the location catalog. If not, insert it (in the - // location catalog and the hash map) and use the newly created index. - auto it = location_catalog_entries_indices_.Find(location); - if (it != location_catalog_entries_indices_.end()) { - // Retrieve the index from the hash map. - dex_register_locations_.push_back(it->second); - } else { - // Create a new entry in the location catalog and the hash map. - size_t index = location_catalog_entries_.size(); - location_catalog_entries_.push_back(location); - dex_register_locations_.push_back(index); - location_catalog_entries_indices_.Insert(std::make_pair(location, index)); - } - DexRegisterMapEntry* const entry = in_inline_frame_ - ? ¤t_inline_info_.dex_register_entry - : ¤t_entry_.dex_register_entry; - DCHECK_LT(current_dex_register_, entry->num_dex_registers); - entry->live_dex_registers_mask->SetBit(current_dex_register_); - entry->hash += (1 << - (current_dex_register_ % (sizeof(DexRegisterMapEntry::hash) * kBitsPerByte))); - entry->hash += static_cast(value); - entry->hash += static_cast(kind); + current_dex_registers_.push_back(DexRegisterLocation(kind, value)); + + // We have collected all the dex registers for StackMap/InlineInfo - create the map. + if (current_dex_registers_.size() == expected_num_dex_registers_) { + CreateDexRegisterMap(); } - current_dex_register_++; } void StackMapStream::AddInvoke(InvokeType invoke_type, uint32_t dex_method_index) { - current_entry_.invoke_type = invoke_type; - current_entry_.dex_method_index = dex_method_index; + uint32_t packed_native_pc = current_stack_map_.packed_native_pc; + invoke_infos_.Add(InvokeInfoEntry { + .packed_native_pc = packed_native_pc, + .invoke_type = invoke_type, + .method_info_index = method_infos_.Dedup(&dex_method_index), + }); } void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, uint32_t dex_pc, uint32_t num_dex_registers, const DexFile* outer_dex_file) { - DCHECK(!in_inline_frame_); - in_inline_frame_ = true; + DCHECK(!in_inline_info_) << "Mismatched Begin/End calls"; + in_inline_info_ = true; + DCHECK_EQ(expected_num_dex_registers_, current_dex_registers_.size()); + + InlineInfoEntry entry = { + .is_last = InlineInfo::kMore, + .dex_pc = dex_pc, + .method_info_index = kNoValue, + .art_method_hi = kNoValue, + .art_method_lo = kNoValue, + .dex_register_mask_index = kNoValue, + .dex_register_map_index = kNoValue, + }; if (EncodeArtMethodInInlineInfo(method)) { - current_inline_info_.method = method; + entry.art_method_hi = High32Bits(reinterpret_cast(method)); + entry.art_method_lo = Low32Bits(reinterpret_cast(method)); } else { if (dex_pc != static_cast(-1) && kIsDebugBuild) { ScopedObjectAccess soa(Thread::Current()); DCHECK(IsSameDexFile(*outer_dex_file, *method->GetDexFile())); } - current_inline_info_.method_index = method->GetDexMethodIndexUnchecked(); + uint32_t dex_method_index = method->GetDexMethodIndexUnchecked(); + entry.method_info_index = method_infos_.Dedup(&dex_method_index); } - current_inline_info_.dex_pc = dex_pc; - current_inline_info_.dex_register_entry.num_dex_registers = num_dex_registers; - current_inline_info_.dex_register_entry.locations_start_index = dex_register_locations_.size(); - current_inline_info_.dex_register_entry.live_dex_registers_mask = nullptr; - if (num_dex_registers != 0) { - current_inline_info_.dex_register_entry.live_dex_registers_mask = - ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream); - current_inline_info_.dex_register_entry.live_dex_registers_mask->ClearAllBits(); + if (current_inline_infos_++ == 0) { + current_stack_map_.inline_info_index = inline_infos_.size(); + } + inline_infos_.Add(entry); + + current_dex_registers_.clear(); + expected_num_dex_registers_ = num_dex_registers; + + if (kIsDebugBuild) { + dcheck_num_dex_registers_.push_back(num_dex_registers); } - current_dex_register_ = 0; } void StackMapStream::EndInlineInfoEntry() { - current_inline_info_.dex_register_map_index = - AddDexRegisterMapEntry(current_inline_info_.dex_register_entry); - DCHECK(in_inline_frame_); - DCHECK_EQ(current_dex_register_, current_inline_info_.dex_register_entry.num_dex_registers) - << "Inline information contains less registers than expected"; - in_inline_frame_ = false; - inline_infos_.push_back(current_inline_info_); - current_inline_info_ = InlineInfoEntry(); + DCHECK(in_inline_info_) << "Mismatched Begin/End calls"; + in_inline_info_ = false; + DCHECK_EQ(expected_num_dex_registers_, current_dex_registers_.size()); } -size_t StackMapStream::ComputeDexRegisterLocationCatalogSize() const { - size_t size = DexRegisterLocationCatalog::kFixedSize; - for (const DexRegisterLocation& dex_register_location : location_catalog_entries_) { - size += DexRegisterLocationCatalog::EntrySize(dex_register_location); +// Create dex register map (bitmap + indices + catalogue entries) +// based on the currently accumulated list of DexRegisterLocations. +void StackMapStream::CreateDexRegisterMap() { + // Create mask and map based on current registers. + temp_dex_register_mask_.ClearAllBits(); + temp_dex_register_map_.clear(); + for (size_t i = 0; i < current_dex_registers_.size(); i++) { + DexRegisterLocation reg = current_dex_registers_[i]; + if (reg.IsLive()) { + DexRegisterEntry entry = DexRegisterEntry { + .kind = static_cast(reg.GetKind()), + .packed_value = DexRegisterInfo::PackValue(reg.GetKind(), reg.GetValue()), + }; + temp_dex_register_mask_.SetBit(i); + temp_dex_register_map_.push_back(dex_register_catalog_.Dedup(&entry)); + } } - return size; -} -size_t StackMapStream::DexRegisterMapEntry::ComputeSize(size_t catalog_size) const { - // For num_dex_registers == 0u live_dex_registers_mask may be null. - if (num_dex_registers == 0u) { - return 0u; // No register map will be emitted. + // Set the mask and map for the current StackMap/InlineInfo. + uint32_t mask_index = StackMap::kNoValue; // Represents mask with all zero bits. + if (temp_dex_register_mask_.GetNumberOfBits() != 0) { + mask_index = dex_register_masks_.Dedup(temp_dex_register_mask_.GetRawStorage(), + temp_dex_register_mask_.GetNumberOfBits()); } - size_t number_of_live_dex_registers = live_dex_registers_mask->NumSetBits(); - if (live_dex_registers_mask->NumSetBits() == 0) { - return 0u; // No register map will be emitted. + uint32_t map_index = dex_register_maps_.Dedup(temp_dex_register_map_.data(), + temp_dex_register_map_.size()); + if (current_inline_infos_ > 0) { + inline_infos_[inline_infos_.size() - 1].dex_register_mask_index = mask_index; + inline_infos_[inline_infos_.size() - 1].dex_register_map_index = map_index; + } else { + current_stack_map_.dex_register_mask_index = mask_index; + current_stack_map_.dex_register_map_index = map_index; } - DCHECK(live_dex_registers_mask != nullptr); - - // Size of the map in bytes. - size_t size = DexRegisterMap::kFixedSize; - // Add the live bit mask for the Dex register liveness. - size += DexRegisterMap::GetLiveBitMaskSize(num_dex_registers); - // Compute the size of the set of live Dex register entries. - size_t map_entries_size_in_bits = - DexRegisterMap::SingleEntrySizeInBits(catalog_size) * number_of_live_dex_registers; - size_t map_entries_size_in_bytes = - RoundUp(map_entries_size_in_bits, kBitsPerByte) / kBitsPerByte; - size += map_entries_size_in_bytes; - return size; } void StackMapStream::FillInMethodInfo(MemoryRegion region) { { - MethodInfo info(region.begin(), method_indices_.size()); - for (size_t i = 0; i < method_indices_.size(); ++i) { - info.SetMethodIndex(i, method_indices_[i]); + MethodInfo info(region.begin(), method_infos_.size()); + for (size_t i = 0; i < method_infos_.size(); ++i) { + info.SetMethodIndex(i, method_infos_[i]); } } if (kIsDebugBuild) { // Check the data matches. MethodInfo info(region.begin()); const size_t count = info.NumMethodIndices(); - DCHECK_EQ(count, method_indices_.size()); + DCHECK_EQ(count, method_infos_.size()); for (size_t i = 0; i < count; ++i) { - DCHECK_EQ(info.GetMethodIndex(i), method_indices_[i]); + DCHECK_EQ(info.GetMethodIndex(i), method_infos_[i]); } } } -template -static MemoryRegion EncodeMemoryRegion(Vector* out, size_t* bit_offset, uint32_t bit_length) { - uint32_t byte_length = BitsToBytesRoundUp(bit_length); - EncodeVarintBits(out, bit_offset, byte_length); - *bit_offset = RoundUp(*bit_offset, kBitsPerByte); - out->resize(out->size() + byte_length); - MemoryRegion region(out->data() + *bit_offset / kBitsPerByte, byte_length); - *bit_offset += kBitsPerByte * byte_length; - return region; -} - size_t StackMapStream::PrepareForFillIn() { - size_t bit_offset = 0; - out_.clear(); - - // Decide the offsets of dex register map entries, but do not write them out yet. - // Needs to be done first as it modifies the stack map entry. - size_t dex_register_map_bytes = 0; - for (DexRegisterMapEntry& entry : dex_register_entries_) { - size_t size = entry.ComputeSize(location_catalog_entries_.size()); - entry.offset = size == 0 ? DexRegisterMapEntry::kOffsetUnassigned : dex_register_map_bytes; - dex_register_map_bytes += size; - } - - // Must be done before calling ComputeInlineInfoEncoding since ComputeInlineInfoEncoding requires - // dex_method_index_idx to be filled in. - PrepareMethodIndices(); - - // Dedup stack masks. Needs to be done first as it modifies the stack map entry. - BitmapTableBuilder stack_mask_builder(allocator_); - for (StackMapEntry& stack_map : stack_maps_) { - BitVector* mask = stack_map.sp_mask; - size_t num_bits = (mask != nullptr) ? mask->GetNumberOfBits() : 0; - if (num_bits != 0) { - stack_map.stack_mask_index = stack_mask_builder.Dedup(mask->GetRawStorage(), num_bits); - } else { - stack_map.stack_mask_index = StackMap::kNoValue; - } - } - - // Dedup register masks. Needs to be done first as it modifies the stack map entry. - BitTableBuilder> register_mask_builder(allocator_); - for (StackMapEntry& stack_map : stack_maps_) { - uint32_t register_mask = stack_map.register_mask; - if (register_mask != 0) { - uint32_t shift = LeastSignificantBit(register_mask); - std::array entry = { - register_mask >> shift, - shift, - }; - stack_map.register_mask_index = register_mask_builder.Dedup(&entry); - } else { - stack_map.register_mask_index = StackMap::kNoValue; + static_assert(sizeof(StackMapEntry) == StackMap::kCount * sizeof(uint32_t), "Layout"); + static_assert(sizeof(InvokeInfoEntry) == InvokeInfo::kCount * sizeof(uint32_t), "Layout"); + static_assert(sizeof(InlineInfoEntry) == InlineInfo::kCount * sizeof(uint32_t), "Layout"); + static_assert(sizeof(DexRegisterEntry) == DexRegisterInfo::kCount * sizeof(uint32_t), "Layout"); + DCHECK_EQ(out_.size(), 0u); + + // Read the stack masks now. The compiler might have updated them. + for (size_t i = 0; i < lazy_stack_masks_.size(); i++) { + BitVector* stack_mask = lazy_stack_masks_[i]; + if (stack_mask != nullptr && stack_mask->GetNumberOfBits() != 0) { + stack_maps_[i].stack_mask_index = + stack_masks_.Dedup(stack_mask->GetRawStorage(), stack_mask->GetNumberOfBits()); } } - // Allocate space for dex register maps. - EncodeMemoryRegion(&out_, &bit_offset, dex_register_map_bytes * kBitsPerByte); - - // Write dex register catalog. - EncodeVarintBits(&out_, &bit_offset, location_catalog_entries_.size()); - size_t location_catalog_bytes = ComputeDexRegisterLocationCatalogSize(); - MemoryRegion dex_register_location_catalog_region = - EncodeMemoryRegion(&out_, &bit_offset, location_catalog_bytes * kBitsPerByte); - DexRegisterLocationCatalog dex_register_location_catalog(dex_register_location_catalog_region); - // Offset in `dex_register_location_catalog` where to store the next - // register location. - size_t location_catalog_offset = DexRegisterLocationCatalog::kFixedSize; - for (DexRegisterLocation dex_register_location : location_catalog_entries_) { - dex_register_location_catalog.SetRegisterInfo(location_catalog_offset, dex_register_location); - location_catalog_offset += DexRegisterLocationCatalog::EntrySize(dex_register_location); - } - // Ensure we reached the end of the Dex registers location_catalog. - DCHECK_EQ(location_catalog_offset, dex_register_location_catalog_region.size()); - - // Write stack maps. - BitTableBuilder> stack_map_builder(allocator_); - BitTableBuilder> invoke_info_builder(allocator_); - BitTableBuilder> inline_info_builder(allocator_); - for (const StackMapEntry& entry : stack_maps_) { - if (entry.dex_method_index != dex::kDexNoIndex) { - std::array invoke_info_entry { - entry.packed_native_pc, - entry.invoke_type, - entry.dex_method_index_idx - }; - invoke_info_builder.Add(invoke_info_entry); - } - - // Set the inlining info. - uint32_t inline_info_index = inline_info_builder.size(); - DCHECK_LE(entry.inline_infos_start_index + entry.inlining_depth, inline_infos_.size()); - for (size_t depth = 0; depth < entry.inlining_depth; ++depth) { - InlineInfoEntry inline_entry = inline_infos_[depth + entry.inline_infos_start_index]; - uint32_t method_index_idx = inline_entry.dex_method_index_idx; - uint32_t extra_data = 1; - if (inline_entry.method != nullptr) { - method_index_idx = High32Bits(reinterpret_cast(inline_entry.method)); - extra_data = Low32Bits(reinterpret_cast(inline_entry.method)); - } - std::array inline_info_entry { - (depth == entry.inlining_depth - 1) ? InlineInfo::kLast : InlineInfo::kMore, - method_index_idx, - inline_entry.dex_pc, - extra_data, - dex_register_entries_[inline_entry.dex_register_map_index].offset, - }; - inline_info_builder.Add(inline_info_entry); - } - std::array stack_map_entry { - entry.packed_native_pc, - entry.dex_pc, - dex_register_entries_[entry.dex_register_map_index].offset, - entry.inlining_depth != 0 ? inline_info_index : InlineInfo::kNoValue, - entry.register_mask_index, - entry.stack_mask_index, - }; - stack_map_builder.Add(stack_map_entry); - } - stack_map_builder.Encode(&out_, &bit_offset); - invoke_info_builder.Encode(&out_, &bit_offset); - inline_info_builder.Encode(&out_, &bit_offset); - register_mask_builder.Encode(&out_, &bit_offset); - stack_mask_builder.Encode(&out_, &bit_offset); + size_t bit_offset = 0; + stack_maps_.Encode(&out_, &bit_offset); + register_masks_.Encode(&out_, &bit_offset); + stack_masks_.Encode(&out_, &bit_offset); + invoke_infos_.Encode(&out_, &bit_offset); + inline_infos_.Encode(&out_, &bit_offset); + dex_register_masks_.Encode(&out_, &bit_offset); + dex_register_maps_.Encode(&out_, &bit_offset); + dex_register_catalog_.Encode(&out_, &bit_offset); return UnsignedLeb128Size(out_.size()) + out_.size(); } void StackMapStream::FillInCodeInfo(MemoryRegion region) { - DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry"; + DCHECK(in_stack_map_ == false) << "Mismatched Begin/End calls"; + DCHECK(in_inline_info_ == false) << "Mismatched Begin/End calls"; DCHECK_NE(0u, out_.size()) << "PrepareForFillIn not called before FillIn"; DCHECK_EQ(region.size(), UnsignedLeb128Size(out_.size()) + out_.size()); uint8_t* ptr = EncodeUnsignedLeb128(region.begin(), out_.size()); region.CopyFromVector(ptr - region.begin(), out_); - // Write dex register maps. - CodeInfo code_info(region); - for (DexRegisterMapEntry& entry : dex_register_entries_) { - size_t entry_size = entry.ComputeSize(location_catalog_entries_.size()); - if (entry_size != 0) { - DexRegisterMap dex_register_map( - code_info.dex_register_maps_.Subregion(entry.offset, entry_size), - entry.num_dex_registers, - code_info); - FillInDexRegisterMap(dex_register_map, - entry.num_dex_registers, - *entry.live_dex_registers_mask, - entry.locations_start_index); - } - } - // Verify all written data in debug build. if (kIsDebugBuild) { CheckCodeInfo(region); } } -void StackMapStream::FillInDexRegisterMap(DexRegisterMap dex_register_map, - uint32_t num_dex_registers, - const BitVector& live_dex_registers_mask, - uint32_t start_index_in_dex_register_locations) const { - dex_register_map.SetLiveBitMask(num_dex_registers, live_dex_registers_mask); - // Set the dex register location mapping data. - size_t number_of_live_dex_registers = live_dex_registers_mask.NumSetBits(); - DCHECK_LE(number_of_live_dex_registers, dex_register_locations_.size()); - DCHECK_LE(start_index_in_dex_register_locations, - dex_register_locations_.size() - number_of_live_dex_registers); - for (size_t index_in_dex_register_locations = 0; - index_in_dex_register_locations != number_of_live_dex_registers; - ++index_in_dex_register_locations) { - size_t location_catalog_entry_index = dex_register_locations_[ - start_index_in_dex_register_locations + index_in_dex_register_locations]; - dex_register_map.SetLocationCatalogEntryIndex( - index_in_dex_register_locations, - location_catalog_entry_index, - location_catalog_entries_.size()); - } -} - -size_t StackMapStream::AddDexRegisterMapEntry(const DexRegisterMapEntry& entry) { - const size_t current_entry_index = dex_register_entries_.size(); - auto entries_it = dex_map_hash_to_stack_map_indices_.find(entry.hash); - if (entries_it == dex_map_hash_to_stack_map_indices_.end()) { - // We don't have a perfect hash functions so we need a list to collect all stack maps - // which might have the same dex register map. - ScopedArenaVector stack_map_indices(allocator_->Adapter(kArenaAllocStackMapStream)); - stack_map_indices.push_back(current_entry_index); - dex_map_hash_to_stack_map_indices_.Put(entry.hash, std::move(stack_map_indices)); - } else { - // We might have collisions, so we need to check whether or not we really have a match. - for (uint32_t test_entry_index : entries_it->second) { - if (DexRegisterMapEntryEquals(dex_register_entries_[test_entry_index], entry)) { - return test_entry_index; - } - } - entries_it->second.push_back(current_entry_index); - } - dex_register_entries_.push_back(entry); - return current_entry_index; -} - -bool StackMapStream::DexRegisterMapEntryEquals(const DexRegisterMapEntry& a, - const DexRegisterMapEntry& b) const { - if ((a.live_dex_registers_mask == nullptr) != (b.live_dex_registers_mask == nullptr)) { - return false; - } - if (a.num_dex_registers != b.num_dex_registers) { - return false; - } - if (a.num_dex_registers != 0u) { - DCHECK(a.live_dex_registers_mask != nullptr); - DCHECK(b.live_dex_registers_mask != nullptr); - if (!a.live_dex_registers_mask->Equal(b.live_dex_registers_mask)) { - return false; - } - size_t number_of_live_dex_registers = a.live_dex_registers_mask->NumSetBits(); - DCHECK_LE(number_of_live_dex_registers, dex_register_locations_.size()); - DCHECK_LE(a.locations_start_index, - dex_register_locations_.size() - number_of_live_dex_registers); - DCHECK_LE(b.locations_start_index, - dex_register_locations_.size() - number_of_live_dex_registers); - auto a_begin = dex_register_locations_.begin() + a.locations_start_index; - auto b_begin = dex_register_locations_.begin() + b.locations_start_index; - if (!std::equal(a_begin, a_begin + number_of_live_dex_registers, b_begin)) { - return false; - } - } - return true; -} - // Helper for CheckCodeInfo - check that register map has the expected content. void StackMapStream::CheckDexRegisterMap(const DexRegisterMap& dex_register_map, - size_t num_dex_registers, - BitVector* live_dex_registers_mask, - size_t dex_register_locations_index) const { - for (size_t reg = 0; reg < num_dex_registers; reg++) { + size_t dex_register_mask_index, + size_t dex_register_map_index) const { + if (dex_register_map_index == kNoValue) { + DCHECK(!dex_register_map.IsValid()); + return; + } + BitMemoryRegion live_dex_registers_mask = (dex_register_mask_index == kNoValue) + ? BitMemoryRegion() + : BitMemoryRegion(dex_register_masks_[dex_register_mask_index]); + for (size_t reg = 0; reg < dex_register_map.size(); reg++) { // Find the location we tried to encode. DexRegisterLocation expected = DexRegisterLocation::None(); - if (live_dex_registers_mask->IsBitSet(reg)) { - size_t catalog_index = dex_register_locations_[dex_register_locations_index++]; - expected = location_catalog_entries_[catalog_index]; + if (reg < live_dex_registers_mask.size_in_bits() && live_dex_registers_mask.LoadBit(reg)) { + size_t catalog_index = dex_register_maps_[dex_register_map_index++]; + DexRegisterLocation::Kind kind = + static_cast(dex_register_catalog_[catalog_index].kind); + uint32_t packed_value = dex_register_catalog_[catalog_index].packed_value; + expected = DexRegisterLocation(kind, DexRegisterInfo::UnpackValue(kind, packed_value)); } // Compare to the seen location. if (expected.GetKind() == DexRegisterLocation::Kind::kNone) { @@ -446,108 +277,75 @@ void StackMapStream::CheckDexRegisterMap(const DexRegisterMap& dex_register_map, DCHECK_EQ(expected.GetValue(), seen.GetValue()); } } - if (num_dex_registers == 0) { - DCHECK(!dex_register_map.IsValid()); - } -} - -void StackMapStream::PrepareMethodIndices() { - CHECK(method_indices_.empty()); - method_indices_.resize(stack_maps_.size() + inline_infos_.size()); - ScopedArenaUnorderedMap dedupe(allocator_->Adapter(kArenaAllocStackMapStream)); - for (StackMapEntry& stack_map : stack_maps_) { - const size_t index = dedupe.size(); - const uint32_t method_index = stack_map.dex_method_index; - if (method_index != dex::kDexNoIndex) { - stack_map.dex_method_index_idx = dedupe.emplace(method_index, index).first->second; - method_indices_[index] = method_index; - } - } - for (InlineInfoEntry& inline_info : inline_infos_) { - const size_t index = dedupe.size(); - const uint32_t method_index = inline_info.method_index; - CHECK_NE(method_index, dex::kDexNoIndex); - inline_info.dex_method_index_idx = dedupe.emplace(method_index, index).first->second; - method_indices_[index] = method_index; - } - method_indices_.resize(dedupe.size()); } // Check that all StackMapStream inputs are correctly encoded by trying to read them back. void StackMapStream::CheckCodeInfo(MemoryRegion region) const { CodeInfo code_info(region); DCHECK_EQ(code_info.GetNumberOfStackMaps(), stack_maps_.size()); - DCHECK_EQ(code_info.GetNumberOfLocationCatalogEntries(), location_catalog_entries_.size()); - size_t invoke_info_index = 0; + const uint32_t* num_dex_registers = dcheck_num_dex_registers_.data(); for (size_t s = 0; s < stack_maps_.size(); ++s) { const StackMap stack_map = code_info.GetStackMapAt(s); - StackMapEntry entry = stack_maps_[s]; + const StackMapEntry& entry = stack_maps_[s]; // Check main stack map fields. DCHECK_EQ(stack_map.GetNativePcOffset(instruction_set_), StackMap::UnpackNativePc(entry.packed_native_pc, instruction_set_)); DCHECK_EQ(stack_map.GetDexPc(), entry.dex_pc); DCHECK_EQ(stack_map.GetRegisterMaskIndex(), entry.register_mask_index); - DCHECK_EQ(code_info.GetRegisterMaskOf(stack_map), entry.register_mask); + RegisterMaskEntry expected_register_mask = (entry.register_mask_index == kNoValue) + ? RegisterMaskEntry{} + : register_masks_[entry.register_mask_index]; + DCHECK_EQ(code_info.GetRegisterMaskOf(stack_map), + expected_register_mask.value << expected_register_mask.shift); DCHECK_EQ(stack_map.GetStackMaskIndex(), entry.stack_mask_index); + BitMemoryRegion expected_stack_mask = (entry.stack_mask_index == kNoValue) + ? BitMemoryRegion() + : BitMemoryRegion(stack_masks_[entry.stack_mask_index]); BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map); - if (entry.sp_mask != nullptr) { - DCHECK_GE(stack_mask.size_in_bits(), entry.sp_mask->GetNumberOfBits()); - for (size_t b = 0; b < stack_mask.size_in_bits(); b++) { - DCHECK_EQ(stack_mask.LoadBit(b), entry.sp_mask->IsBitSet(b)) << b; - } - } else { - DCHECK_EQ(stack_mask.size_in_bits(), 0u); + for (size_t b = 0; b < expected_stack_mask.size_in_bits(); b++) { + bool seen = b < stack_mask.size_in_bits() && stack_mask.LoadBit(b); + DCHECK_EQ(expected_stack_mask.LoadBit(b), seen); } - if (entry.dex_method_index != dex::kDexNoIndex) { - InvokeInfo invoke_info = code_info.GetInvokeInfo(invoke_info_index); - DCHECK_EQ(invoke_info.GetNativePcOffset(instruction_set_), - StackMap::UnpackNativePc(entry.packed_native_pc, instruction_set_)); - DCHECK_EQ(invoke_info.GetInvokeType(), entry.invoke_type); - DCHECK_EQ(invoke_info.GetMethodIndexIdx(), entry.dex_method_index_idx); - invoke_info_index++; - } - CheckDexRegisterMap(code_info.GetDexRegisterMapOf( - stack_map, entry.dex_register_entry.num_dex_registers), - entry.dex_register_entry.num_dex_registers, - entry.dex_register_entry.live_dex_registers_mask, - entry.dex_register_entry.locations_start_index); + CheckDexRegisterMap(code_info.GetDexRegisterMapOf(stack_map, *(num_dex_registers++)), + entry.dex_register_mask_index, + entry.dex_register_map_index); // Check inline info. - DCHECK_EQ(stack_map.HasInlineInfo(), (entry.inlining_depth != 0)); - if (entry.inlining_depth != 0) { + DCHECK_EQ(stack_map.HasInlineInfo(), (entry.inline_info_index != kNoValue)); + if (stack_map.HasInlineInfo()) { InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); - DCHECK_EQ(inline_info.GetDepth(), entry.inlining_depth); - for (size_t d = 0; d < entry.inlining_depth; ++d) { - size_t inline_info_index = entry.inline_infos_start_index + d; + size_t inlining_depth = inline_info.GetDepth(); + for (size_t d = 0; d < inlining_depth; ++d) { + size_t inline_info_index = entry.inline_info_index + d; DCHECK_LT(inline_info_index, inline_infos_.size()); - InlineInfoEntry inline_entry = inline_infos_[inline_info_index]; + const InlineInfoEntry& inline_entry = inline_infos_[inline_info_index]; DCHECK_EQ(inline_info.GetDexPcAtDepth(d), inline_entry.dex_pc); - if (inline_info.EncodesArtMethodAtDepth(d)) { - DCHECK_EQ(inline_info.GetArtMethodAtDepth(d), - inline_entry.method); - } else { + if (!inline_info.EncodesArtMethodAtDepth(d)) { const size_t method_index_idx = inline_info.GetMethodIndexIdxAtDepth(d); - DCHECK_EQ(method_index_idx, inline_entry.dex_method_index_idx); - DCHECK_EQ(method_indices_[method_index_idx], inline_entry.method_index); + DCHECK_EQ(method_index_idx, inline_entry.method_info_index); } - CheckDexRegisterMap(code_info.GetDexRegisterMapAtDepth( - d, - inline_info, - inline_entry.dex_register_entry.num_dex_registers), - inline_entry.dex_register_entry.num_dex_registers, - inline_entry.dex_register_entry.live_dex_registers_mask, - inline_entry.dex_register_entry.locations_start_index); + d, inline_info, *(num_dex_registers++)), + inline_entry.dex_register_mask_index, + inline_entry.dex_register_map_index); } } } + for (size_t i = 0; i < invoke_infos_.size(); i++) { + InvokeInfo invoke_info = code_info.GetInvokeInfo(i); + const InvokeInfoEntry& entry = invoke_infos_[i]; + DCHECK_EQ(invoke_info.GetNativePcOffset(instruction_set_), + StackMap::UnpackNativePc(entry.packed_native_pc, instruction_set_)); + DCHECK_EQ(invoke_info.GetInvokeType(), entry.invoke_type); + DCHECK_EQ(invoke_info.GetMethodIndexIdx(), entry.method_info_index); + } } size_t StackMapStream::ComputeMethodInfoSize() const { DCHECK_NE(0u, out_.size()) << "PrepareForFillIn not called before " << __FUNCTION__; - return MethodInfo::ComputeSize(method_indices_.size()); + return MethodInfo::ComputeSize(method_infos_.size()); } } // namespace art diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index 6d505b95db..cefe165a67 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -17,42 +17,20 @@ #ifndef ART_COMPILER_OPTIMIZING_STACK_MAP_STREAM_H_ #define ART_COMPILER_OPTIMIZING_STACK_MAP_STREAM_H_ +#include "base/allocator.h" +#include "base/arena_bit_vector.h" +#include "base/bit_table.h" #include "base/bit_vector-inl.h" -#include "base/hash_map.h" #include "base/memory_region.h" #include "base/scoped_arena_containers.h" #include "base/value_object.h" +#include "dex_register_location.h" #include "method_info.h" #include "nodes.h" -#include "stack_map.h" namespace art { -// Helper to build art::StackMapStream::LocationCatalogEntriesIndices. -class LocationCatalogEntriesIndicesEmptyFn { - public: - void MakeEmpty(std::pair& item) const { - item.first = DexRegisterLocation::None(); - } - bool IsEmpty(const std::pair& item) const { - return item.first == DexRegisterLocation::None(); - } -}; - -// Hash function for art::StackMapStream::LocationCatalogEntriesIndices. -// This hash function does not create collisions. -class DexRegisterLocationHashFn { - public: - size_t operator()(DexRegisterLocation key) const { - // Concatenate `key`s fields to create a 64-bit value to be hashed. - int64_t kind_and_value = - (static_cast(key.kind_) << 32) | static_cast(key.value_); - return inner_hash_fn_(kind_and_value); - } - private: - std::hash inner_hash_fn_; -}; - +class DexRegisterMap; /** * Collects and builds stack maps for a method. All the stack maps @@ -61,71 +39,26 @@ class DexRegisterLocationHashFn { class StackMapStream : public ValueObject { public: explicit StackMapStream(ScopedArenaAllocator* allocator, InstructionSet instruction_set) - : allocator_(allocator), - instruction_set_(instruction_set), - stack_maps_(allocator->Adapter(kArenaAllocStackMapStream)), - location_catalog_entries_(allocator->Adapter(kArenaAllocStackMapStream)), - location_catalog_entries_indices_(allocator->Adapter(kArenaAllocStackMapStream)), - dex_register_locations_(allocator->Adapter(kArenaAllocStackMapStream)), - inline_infos_(allocator->Adapter(kArenaAllocStackMapStream)), - method_indices_(allocator->Adapter(kArenaAllocStackMapStream)), - dex_register_entries_(allocator->Adapter(kArenaAllocStackMapStream)), + : instruction_set_(instruction_set), + stack_maps_(allocator), + register_masks_(allocator), + stack_masks_(allocator), + invoke_infos_(allocator), + inline_infos_(allocator), + dex_register_masks_(allocator), + dex_register_maps_(allocator), + dex_register_catalog_(allocator), out_(allocator->Adapter(kArenaAllocStackMapStream)), - dex_map_hash_to_stack_map_indices_(std::less(), - allocator->Adapter(kArenaAllocStackMapStream)), - current_entry_(), - current_inline_info_(), - current_dex_register_(0), - in_inline_frame_(false) { - stack_maps_.reserve(10); - out_.reserve(64); - location_catalog_entries_.reserve(4); - dex_register_locations_.reserve(10 * 4); - inline_infos_.reserve(2); + method_infos_(allocator), + lazy_stack_masks_(allocator->Adapter(kArenaAllocStackMapStream)), + in_stack_map_(false), + in_inline_info_(false), + current_inline_infos_(0), + current_dex_registers_(allocator->Adapter(kArenaAllocStackMapStream)), + temp_dex_register_mask_(allocator, 32, true, kArenaAllocStackMapStream), + temp_dex_register_map_(allocator->Adapter(kArenaAllocStackMapStream)) { } - // A dex register map entry for a single stack map entry, contains what registers are live as - // well as indices into the location catalog. - class DexRegisterMapEntry { - public: - static const uint32_t kOffsetUnassigned = -1; - - BitVector* live_dex_registers_mask; - uint32_t num_dex_registers; - size_t locations_start_index; - // Computed fields - size_t hash = 0; - uint32_t offset = kOffsetUnassigned; - - size_t ComputeSize(size_t catalog_size) const; - }; - - // See runtime/stack_map.h to know what these fields contain. - struct StackMapEntry { - uint32_t dex_pc; - uint32_t packed_native_pc; - uint32_t register_mask; - BitVector* sp_mask; - uint32_t inlining_depth; - size_t inline_infos_start_index; - uint32_t stack_mask_index; - uint32_t register_mask_index; - DexRegisterMapEntry dex_register_entry; - size_t dex_register_map_index; - InvokeType invoke_type; - uint32_t dex_method_index; - uint32_t dex_method_index_idx; // Index into dex method index table. - }; - - struct InlineInfoEntry { - uint32_t dex_pc; // dex::kDexNoIndex for intrinsified native methods. - ArtMethod* method; - uint32_t method_index; - DexRegisterMapEntry dex_register_entry; - size_t dex_register_map_index; - uint32_t dex_method_index_idx; // Index into the dex method index table. - }; - void BeginStackMapEntry(uint32_t dex_pc, uint32_t native_pc_offset, uint32_t register_mask, @@ -160,58 +93,87 @@ class StackMapStream : public ValueObject { size_t ComputeMethodInfoSize() const; private: - size_t ComputeDexRegisterLocationCatalogSize() const; + static constexpr uint32_t kNoValue = -1; + + // The fields must be uint32_t and mirror the StackMap accessor in stack_map.h! + struct StackMapEntry { + uint32_t packed_native_pc; + uint32_t dex_pc; + uint32_t register_mask_index; + uint32_t stack_mask_index; + uint32_t inline_info_index; + uint32_t dex_register_mask_index; + uint32_t dex_register_map_index; + }; + + // The fields must be uint32_t and mirror the InlineInfo accessor in stack_map.h! + struct InlineInfoEntry { + uint32_t is_last; + uint32_t dex_pc; + uint32_t method_info_index; + uint32_t art_method_hi; + uint32_t art_method_lo; + uint32_t dex_register_mask_index; + uint32_t dex_register_map_index; + }; - // Prepare and deduplicate method indices. - void PrepareMethodIndices(); + // The fields must be uint32_t and mirror the InvokeInfo accessor in stack_map.h! + struct InvokeInfoEntry { + uint32_t packed_native_pc; + uint32_t invoke_type; + uint32_t method_info_index; + }; - // Deduplicate entry if possible and return the corresponding index into dex_register_entries_ - // array. If entry is not a duplicate, a new entry is added to dex_register_entries_. - size_t AddDexRegisterMapEntry(const DexRegisterMapEntry& entry); + // The fields must be uint32_t and mirror the DexRegisterInfo accessor in stack_map.h! + struct DexRegisterEntry { + uint32_t kind; + uint32_t packed_value; + }; - // Return true if the two dex register map entries are equal. - bool DexRegisterMapEntryEquals(const DexRegisterMapEntry& a, const DexRegisterMapEntry& b) const; + // The fields must be uint32_t and mirror the RegisterMask accessor in stack_map.h! + struct RegisterMaskEntry { + uint32_t value; + uint32_t shift; + }; - // Fill in the corresponding entries of a register map. - void FillInDexRegisterMap(DexRegisterMap dex_register_map, - uint32_t num_dex_registers, - const BitVector& live_dex_registers_mask, - uint32_t start_index_in_dex_register_locations) const; + void CreateDexRegisterMap(); void CheckDexRegisterMap(const DexRegisterMap& dex_register_map, - size_t num_dex_registers, - BitVector* live_dex_registers_mask, - size_t dex_register_locations_index) const; + size_t dex_register_mask_index, + size_t dex_register_map_index) const; void CheckCodeInfo(MemoryRegion region) const; - ScopedArenaAllocator* const allocator_; const InstructionSet instruction_set_; - ScopedArenaVector stack_maps_; - - // A catalog of unique [location_kind, register_value] pairs (per method). - ScopedArenaVector location_catalog_entries_; - // Map from Dex register location catalog entries to their indices in the - // location catalog. - using LocationCatalogEntriesIndices = ScopedArenaHashMap; - LocationCatalogEntriesIndices location_catalog_entries_indices_; - - // A set of concatenated maps of Dex register locations indices to `location_catalog_entries_`. - ScopedArenaVector dex_register_locations_; - ScopedArenaVector inline_infos_; - ScopedArenaVector method_indices_; - ScopedArenaVector dex_register_entries_; - + BitTableBuilder stack_maps_; + BitTableBuilder register_masks_; + BitmapTableBuilder stack_masks_; + BitTableBuilder invoke_infos_; + BitTableBuilder inline_infos_; + BitmapTableBuilder dex_register_masks_; + BitTableBuilder dex_register_maps_; + BitTableBuilder dex_register_catalog_; ScopedArenaVector out_; - ScopedArenaSafeMap> dex_map_hash_to_stack_map_indices_; + BitTableBuilder method_infos_; + + ScopedArenaVector lazy_stack_masks_; + + // Variables which track the current state between Begin/End calls; + bool in_stack_map_; + bool in_inline_info_; + StackMapEntry current_stack_map_; + uint32_t current_inline_infos_; + ScopedArenaVector current_dex_registers_; + size_t expected_num_dex_registers_; + + // Temporary variables used in CreateDexRegisterMap. + // They are here so that we can reuse the reserved memory. + ArenaBitVector temp_dex_register_mask_; + ScopedArenaVector temp_dex_register_map_; - StackMapEntry current_entry_; - InlineInfoEntry current_inline_info_; - uint32_t current_dex_register_; - bool in_inline_frame_; + // Records num_dex_registers for every StackMapEntry and InlineInfoEntry. + // Only used in debug builds to verify the dex registers at the end. + std::vector dcheck_num_dex_registers_; DISALLOW_COPY_AND_ASSIGN(StackMapStream); }; diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index 7178e6683f..262c240bc7 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -70,12 +70,6 @@ TEST(StackMapTest, Test1) { uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); ASSERT_EQ(2u, number_of_catalog_entries); - DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); - // The Dex register location catalog contains: - // - one 1-byte short Dex register location, and - // - one 5-byte large Dex register location. - size_t expected_location_catalog_size = 1u + 5u; - ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); StackMap stack_map = code_info.GetStackMapAt(0); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); @@ -91,30 +85,17 @@ TEST(StackMapTest, Test1) { code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); - ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); - // The Dex register map contains: - // - one 1-byte live bit mask, and - // - one 1-byte set of location catalog entry indices composed of two 2-bit values. - size_t expected_dex_register_map_size = 1u + 1u; - ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); + ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters()); ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationKind(0)); ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind(1)); - ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationInternalKind(0)); - ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind(1)); ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes(0)); ASSERT_EQ(-2, dex_register_map.GetConstant(1)); - size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(0, number_of_catalog_entries); - size_t index1 = dex_register_map.GetLocationCatalogEntryIndex(1, number_of_catalog_entries); - ASSERT_EQ(0u, index0); - ASSERT_EQ(1u, index1); - DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); - DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + DexRegisterLocation location0 = code_info.GetDexRegisterCatalogEntry(0); + DexRegisterLocation location1 = code_info.GetDexRegisterCatalogEntry(1); ASSERT_EQ(Kind::kInStack, location0.GetKind()); ASSERT_EQ(Kind::kConstant, location1.GetKind()); - ASSERT_EQ(Kind::kInStack, location0.GetInternalKind()); - ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind()); ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); @@ -176,12 +157,6 @@ TEST(StackMapTest, Test2) { uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); ASSERT_EQ(7u, number_of_catalog_entries); - DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); - // The Dex register location catalog contains: - // - six 1-byte short Dex register locations, and - // - one 5-byte large Dex register location. - size_t expected_location_catalog_size = 6u * 1u + 5u; - ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); // First stack map. { @@ -199,30 +174,17 @@ TEST(StackMapTest, Test2) { code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); - ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); - // The Dex register map contains: - // - one 1-byte live bit mask, and - // - one 1-byte set of location catalog entry indices composed of two 2-bit values. - size_t expected_dex_register_map_size = 1u + 1u; - ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); + ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters()); ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationKind(0)); ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind(1)); - ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationInternalKind(0)); - ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind(1)); ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes(0)); ASSERT_EQ(-2, dex_register_map.GetConstant(1)); - size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(0, number_of_catalog_entries); - size_t index1 = dex_register_map.GetLocationCatalogEntryIndex(1, number_of_catalog_entries); - ASSERT_EQ(0u, index0); - ASSERT_EQ(1u, index1); - DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); - DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + DexRegisterLocation location0 = code_info.GetDexRegisterCatalogEntry(0); + DexRegisterLocation location1 = code_info.GetDexRegisterCatalogEntry(1); ASSERT_EQ(Kind::kInStack, location0.GetKind()); ASSERT_EQ(Kind::kConstant, location1.GetKind()); - ASSERT_EQ(Kind::kInStack, location0.GetInternalKind()); - ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind()); ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); @@ -251,30 +213,17 @@ TEST(StackMapTest, Test2) { code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); - ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); - // The Dex register map contains: - // - one 1-byte live bit mask, and - // - one 1-byte set of location catalog entry indices composed of two 2-bit values. - size_t expected_dex_register_map_size = 1u + 1u; - ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); + ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters()); ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationKind(0)); ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationKind(1)); - ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationInternalKind(0)); - ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationInternalKind(1)); ASSERT_EQ(18, dex_register_map.GetMachineRegister(0)); ASSERT_EQ(3, dex_register_map.GetMachineRegister(1)); - size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(0, number_of_catalog_entries); - size_t index1 = dex_register_map.GetLocationCatalogEntryIndex(1, number_of_catalog_entries); - ASSERT_EQ(2u, index0); - ASSERT_EQ(3u, index1); - DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); - DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + DexRegisterLocation location0 = code_info.GetDexRegisterCatalogEntry(2); + DexRegisterLocation location1 = code_info.GetDexRegisterCatalogEntry(3); ASSERT_EQ(Kind::kInRegister, location0.GetKind()); ASSERT_EQ(Kind::kInFpuRegister, location1.GetKind()); - ASSERT_EQ(Kind::kInRegister, location0.GetInternalKind()); - ASSERT_EQ(Kind::kInFpuRegister, location1.GetInternalKind()); ASSERT_EQ(18, location0.GetValue()); ASSERT_EQ(3, location1.GetValue()); @@ -297,30 +246,17 @@ TEST(StackMapTest, Test2) { code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); - ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); - // The Dex register map contains: - // - one 1-byte live bit mask, and - // - one 1-byte set of location catalog entry indices composed of two 2-bit values. - size_t expected_dex_register_map_size = 1u + 1u; - ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); + ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters()); ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationKind(0)); ASSERT_EQ(Kind::kInRegisterHigh, dex_register_map.GetLocationKind(1)); - ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationInternalKind(0)); - ASSERT_EQ(Kind::kInRegisterHigh, dex_register_map.GetLocationInternalKind(1)); ASSERT_EQ(6, dex_register_map.GetMachineRegister(0)); ASSERT_EQ(8, dex_register_map.GetMachineRegister(1)); - size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(0, number_of_catalog_entries); - size_t index1 = dex_register_map.GetLocationCatalogEntryIndex(1, number_of_catalog_entries); - ASSERT_EQ(4u, index0); - ASSERT_EQ(5u, index1); - DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); - DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + DexRegisterLocation location0 = code_info.GetDexRegisterCatalogEntry(4); + DexRegisterLocation location1 = code_info.GetDexRegisterCatalogEntry(5); ASSERT_EQ(Kind::kInRegister, location0.GetKind()); ASSERT_EQ(Kind::kInRegisterHigh, location1.GetKind()); - ASSERT_EQ(Kind::kInRegister, location0.GetInternalKind()); - ASSERT_EQ(Kind::kInRegisterHigh, location1.GetInternalKind()); ASSERT_EQ(6, location0.GetValue()); ASSERT_EQ(8, location1.GetValue()); @@ -343,30 +279,17 @@ TEST(StackMapTest, Test2) { code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); - ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); - // The Dex register map contains: - // - one 1-byte live bit mask, and - // - one 1-byte set of location catalog entry indices composed of two 2-bit values. - size_t expected_dex_register_map_size = 1u + 1u; - ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); + ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters()); ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationKind(0)); ASSERT_EQ(Kind::kInFpuRegisterHigh, dex_register_map.GetLocationKind(1)); - ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationInternalKind(0)); - ASSERT_EQ(Kind::kInFpuRegisterHigh, dex_register_map.GetLocationInternalKind(1)); ASSERT_EQ(3, dex_register_map.GetMachineRegister(0)); ASSERT_EQ(1, dex_register_map.GetMachineRegister(1)); - size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(0, number_of_catalog_entries); - size_t index1 = dex_register_map.GetLocationCatalogEntryIndex(1, number_of_catalog_entries); - ASSERT_EQ(3u, index0); // Shared with second stack map. - ASSERT_EQ(6u, index1); - DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); - DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + DexRegisterLocation location0 = code_info.GetDexRegisterCatalogEntry(3); + DexRegisterLocation location1 = code_info.GetDexRegisterCatalogEntry(6); ASSERT_EQ(Kind::kInFpuRegister, location0.GetKind()); ASSERT_EQ(Kind::kInFpuRegisterHigh, location1.GetKind()); - ASSERT_EQ(Kind::kInFpuRegister, location0.GetInternalKind()); - ASSERT_EQ(Kind::kInFpuRegisterHigh, location1.GetInternalKind()); ASSERT_EQ(3, location0.GetValue()); ASSERT_EQ(1, location1.GetValue()); @@ -405,12 +328,6 @@ TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); ASSERT_EQ(2u, number_of_catalog_entries); - DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); - // The Dex register location catalog contains: - // - one 1-byte short Dex register locations, and - // - one 5-byte large Dex register location. - const size_t expected_location_catalog_size = 1u + 5u; - ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); // First stack map. { @@ -427,30 +344,17 @@ TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { DexRegisterMap map(code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers)); ASSERT_TRUE(map.IsDexRegisterLive(0)); ASSERT_TRUE(map.IsDexRegisterLive(1)); - ASSERT_EQ(2u, map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); - // The Dex register map contains: - // - one 1-byte live bit mask, and - // - one 1-byte set of location catalog entry indices composed of two 2-bit values. - size_t expected_map_size = 1u + 1u; - ASSERT_EQ(expected_map_size, map.Size()); + ASSERT_EQ(2u, map.GetNumberOfLiveDexRegisters()); ASSERT_EQ(Kind::kInStack, map.GetLocationKind(0)); ASSERT_EQ(Kind::kConstant, map.GetLocationKind(1)); - ASSERT_EQ(Kind::kInStack, map.GetLocationInternalKind(0)); - ASSERT_EQ(Kind::kConstantLargeValue, map.GetLocationInternalKind(1)); ASSERT_EQ(0, map.GetStackOffsetInBytes(0)); ASSERT_EQ(-2, map.GetConstant(1)); - const size_t index0 = map.GetLocationCatalogEntryIndex(0, number_of_catalog_entries); - const size_t index1 = map.GetLocationCatalogEntryIndex(1, number_of_catalog_entries); - ASSERT_EQ(0u, index0); - ASSERT_EQ(1u, index1); - DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); - DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + DexRegisterLocation location0 = code_info.GetDexRegisterCatalogEntry(0); + DexRegisterLocation location1 = code_info.GetDexRegisterCatalogEntry(1); ASSERT_EQ(Kind::kInStack, location0.GetKind()); ASSERT_EQ(Kind::kConstant, location1.GetKind()); - ASSERT_EQ(Kind::kInStack, location0.GetInternalKind()); - ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind()); ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); @@ -458,8 +362,8 @@ TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { // one. ASSERT_TRUE(stack_map.HasInlineInfo()); InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); - EXPECT_EQ(inline_info.GetDexRegisterMapOffsetAtDepth(0), - stack_map.GetDexRegisterMapOffset()); + EXPECT_EQ(inline_info.GetDexRegisterMapIndexAtDepth(0), + stack_map.GetDexRegisterMapIndex()); } } @@ -486,11 +390,6 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); ASSERT_EQ(1u, number_of_catalog_entries); - DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); - // The Dex register location catalog contains: - // - one 5-byte large Dex register location. - size_t expected_location_catalog_size = 5u; - ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); StackMap stack_map = code_info.GetStackMapAt(0); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); @@ -504,100 +403,19 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); ASSERT_FALSE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); - ASSERT_EQ(1u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); - // The Dex register map contains: - // - one 1-byte live bit mask. - // No space is allocated for the sole location catalog entry index, as it is useless. - size_t expected_dex_register_map_size = 1u + 0u; - ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); + ASSERT_EQ(1u, dex_register_map.GetNumberOfLiveDexRegisters()); ASSERT_EQ(Kind::kNone, dex_register_map.GetLocationKind(0)); ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind(1)); - ASSERT_EQ(Kind::kNone, dex_register_map.GetLocationInternalKind(0)); - ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind(1)); ASSERT_EQ(-2, dex_register_map.GetConstant(1)); - size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(0, number_of_catalog_entries); - size_t index1 = dex_register_map.GetLocationCatalogEntryIndex(1, number_of_catalog_entries); - ASSERT_EQ(DexRegisterLocationCatalog::kNoLocationEntryIndex, index0); - ASSERT_EQ(0u, index1); - DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); - DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); - ASSERT_EQ(Kind::kNone, location0.GetKind()); + DexRegisterLocation location1 = code_info.GetDexRegisterCatalogEntry(0); ASSERT_EQ(Kind::kConstant, location1.GetKind()); - ASSERT_EQ(Kind::kNone, location0.GetInternalKind()); - ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind()); - ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); ASSERT_FALSE(stack_map.HasInlineInfo()); } -// Generate a stack map whose dex register offset is -// StackMap::kNoDexRegisterMapSmallEncoding, and ensure we do -// not treat it as kNoDexRegisterMap. -TEST(StackMapTest, DexRegisterMapOffsetOverflow) { - MallocArenaPool pool; - ArenaStack arena_stack(&pool); - ScopedArenaAllocator allocator(&arena_stack); - StackMapStream stream(&allocator, kRuntimeISA); - - ArenaBitVector sp_mask(&allocator, 0, false); - uint32_t number_of_dex_registers = 1024; - // Create the first stack map (and its Dex register map). - stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask, number_of_dex_registers, 0); - uint32_t number_of_dex_live_registers_in_dex_register_map_0 = number_of_dex_registers - 8; - for (uint32_t i = 0; i < number_of_dex_live_registers_in_dex_register_map_0; ++i) { - // Use two different Dex register locations to populate this map, - // as using a single value (in the whole CodeInfo object) would - // make this Dex register mapping data empty (see - // art::DexRegisterMap::SingleEntrySizeInBits). - stream.AddDexRegisterEntry(Kind::kConstant, i % 2); // Short location. - } - stream.EndStackMapEntry(); - // Create the second stack map (and its Dex register map). - stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask, number_of_dex_registers, 0); - for (uint32_t i = 0; i < number_of_dex_registers; ++i) { - stream.AddDexRegisterEntry(Kind::kConstant, 0); // Short location. - } - stream.EndStackMapEntry(); - - size_t size = stream.PrepareForFillIn(); - void* memory = allocator.Alloc(size, kArenaAllocMisc); - MemoryRegion region(memory, size); - stream.FillInCodeInfo(region); - - CodeInfo code_info(region); - // The location catalog contains two entries (DexRegisterLocation(kConstant, 0) - // and DexRegisterLocation(kConstant, 1)), therefore the location catalog index - // has a size of 1 bit. - uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); - ASSERT_EQ(2u, number_of_catalog_entries); - ASSERT_EQ(1u, DexRegisterMap::SingleEntrySizeInBits(number_of_catalog_entries)); - - // The first Dex register map contains: - // - a live register bit mask for 1024 registers (that is, 128 bytes of - // data); and - // - Dex register mapping information for 1016 1-bit Dex (live) register - // locations (that is, 127 bytes of data). - // Hence it has a size of 255 bytes, and therefore... - ASSERT_EQ(128u, DexRegisterMap::GetLiveBitMaskSize(number_of_dex_registers)); - StackMap stack_map0 = code_info.GetStackMapAt(0); - DexRegisterMap dex_register_map0 = - code_info.GetDexRegisterMapOf(stack_map0, number_of_dex_registers); - ASSERT_EQ(127u, dex_register_map0.GetLocationMappingDataSize(number_of_catalog_entries)); - ASSERT_EQ(255u, dex_register_map0.Size()); - - StackMap stack_map1 = code_info.GetStackMapAt(1); - ASSERT_TRUE(stack_map1.HasDexRegisterMap()); - // ...the offset of the second Dex register map (relative to the - // beginning of the Dex register maps region) is 255 (i.e., - // kNoDexRegisterMapSmallEncoding). - ASSERT_NE(stack_map1.GetDexRegisterMapOffset(), - StackMap::kNoValue); - ASSERT_EQ(stack_map1.GetDexRegisterMapOffset(), 0xFFu); -} - TEST(StackMapTest, TestShareDexRegisterMap) { MallocArenaPool pool; ArenaStack arena_stack(&pool); @@ -648,12 +466,12 @@ TEST(StackMapTest, TestShareDexRegisterMap) { ASSERT_EQ(-2, dex_registers2.GetConstant(1)); // Verify dex register map offsets. - ASSERT_EQ(sm0.GetDexRegisterMapOffset(), - sm1.GetDexRegisterMapOffset()); - ASSERT_NE(sm0.GetDexRegisterMapOffset(), - sm2.GetDexRegisterMapOffset()); - ASSERT_NE(sm1.GetDexRegisterMapOffset(), - sm2.GetDexRegisterMapOffset()); + ASSERT_EQ(sm0.GetDexRegisterMapIndex(), + sm1.GetDexRegisterMapIndex()); + ASSERT_NE(sm0.GetDexRegisterMapIndex(), + sm2.GetDexRegisterMapIndex()); + ASSERT_NE(sm1.GetDexRegisterMapIndex(), + sm2.GetDexRegisterMapIndex()); } TEST(StackMapTest, TestNoDexRegisterMap) { @@ -669,6 +487,7 @@ TEST(StackMapTest, TestNoDexRegisterMap) { number_of_dex_registers = 1; stream.BeginStackMapEntry(1, 68 * kPcAlign, 0x4, &sp_mask, number_of_dex_registers, 0); + stream.AddDexRegisterEntry(Kind::kNone, 0); stream.EndStackMapEntry(); size_t size = stream.PrepareForFillIn(); @@ -681,8 +500,6 @@ TEST(StackMapTest, TestNoDexRegisterMap) { uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); ASSERT_EQ(0u, number_of_catalog_entries); - DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); - ASSERT_EQ(0u, location_catalog.Size()); StackMap stack_map = code_info.GetStackMapAt(0); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); @@ -701,7 +518,7 @@ TEST(StackMapTest, TestNoDexRegisterMap) { ASSERT_EQ(68u * kPcAlign, stack_map.GetNativePcOffset(kRuntimeISA)); ASSERT_EQ(0x4u, code_info.GetRegisterMaskOf(stack_map)); - ASSERT_FALSE(stack_map.HasDexRegisterMap()); + ASSERT_TRUE(stack_map.HasDexRegisterMap()); ASSERT_FALSE(stack_map.HasInlineInfo()); } @@ -871,6 +688,7 @@ TEST(StackMapTest, InlineTest) { } TEST(StackMapTest, PackedNativePcTest) { + // Test minimum alignments, and decoding. uint32_t packed_thumb2 = StackMap::PackNativePc(kThumb2InstructionAlignment, InstructionSet::kThumb2); uint32_t packed_arm64 = diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 6b626c21cf..7ac9e984ff 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -738,6 +738,7 @@ class OatDumper { kByteKindCode, kByteKindQuickMethodHeader, kByteKindCodeInfoLocationCatalog, + kByteKindCodeInfoDexRegisterMask, kByteKindCodeInfoDexRegisterMap, kByteKindCodeInfo, kByteKindCodeInfoInvokeInfo, @@ -751,7 +752,7 @@ class OatDumper { kByteKindStackMapStackMaskIndex, kByteKindInlineInfoMethodIndexIdx, kByteKindInlineInfoDexPc, - kByteKindInlineInfoExtraData, + kByteKindInlineInfoArtMethod, kByteKindInlineInfoDexRegisterMap, kByteKindInlineInfoIsLast, kByteKindCount, @@ -788,6 +789,7 @@ class OatDumper { Dump(os, "QuickMethodHeader ", bits[kByteKindQuickMethodHeader], sum); Dump(os, "CodeInfo ", bits[kByteKindCodeInfo], sum); Dump(os, "CodeInfoLocationCatalog ", bits[kByteKindCodeInfoLocationCatalog], sum); + Dump(os, "CodeInfoDexRegisterMask ", bits[kByteKindCodeInfoDexRegisterMask], sum); Dump(os, "CodeInfoDexRegisterMap ", bits[kByteKindCodeInfoDexRegisterMap], sum); Dump(os, "CodeInfoStackMasks ", bits[kByteKindCodeInfoStackMasks], sum); Dump(os, "CodeInfoRegisterMasks ", bits[kByteKindCodeInfoRegisterMasks], sum); @@ -848,8 +850,8 @@ class OatDumper { inline_info_bits, "inline info"); Dump(os, - "InlineInfoExtraData ", - bits[kByteKindInlineInfoExtraData], + "InlineInfoArtMethod ", + bits[kByteKindInlineInfoArtMethod], inline_info_bits, "inline info"); Dump(os, @@ -1706,7 +1708,7 @@ class OatDumper { stack_maps.NumColumnBits(StackMap::kDexPc) * num_stack_maps); stats_.AddBits( Stats::kByteKindStackMapDexRegisterMap, - stack_maps.NumColumnBits(StackMap::kDexRegisterMapOffset) * num_stack_maps); + stack_maps.NumColumnBits(StackMap::kDexRegisterMapIndex) * num_stack_maps); stats_.AddBits( Stats::kByteKindStackMapInlineInfoIndex, stack_maps.NumColumnBits(StackMap::kInlineInfoIndex) * num_stack_maps); @@ -1733,16 +1735,12 @@ class OatDumper { code_info.invoke_infos_.DataBitSize()); // Location catalog - const size_t location_catalog_bytes = - helper.GetCodeInfo().GetDexRegisterLocationCatalogSize(); stats_.AddBits(Stats::kByteKindCodeInfoLocationCatalog, - kBitsPerByte * location_catalog_bytes); - // Dex register bytes. - const size_t dex_register_bytes = - helper.GetCodeInfo().GetDexRegisterMapsSize(code_item_accessor.RegistersSize()); - stats_.AddBits( - Stats::kByteKindCodeInfoDexRegisterMap, - kBitsPerByte * dex_register_bytes); + code_info.dex_register_catalog_.DataBitSize()); + stats_.AddBits(Stats::kByteKindCodeInfoDexRegisterMask, + code_info.dex_register_masks_.DataBitSize()); + stats_.AddBits(Stats::kByteKindCodeInfoDexRegisterMap, + code_info.dex_register_maps_.DataBitSize()); // Inline infos. const BitTable& inline_infos = code_info.inline_infos_; @@ -1755,11 +1753,12 @@ class OatDumper { Stats::kByteKindInlineInfoDexPc, inline_infos.NumColumnBits(InlineInfo::kDexPc) * num_inline_infos); stats_.AddBits( - Stats::kByteKindInlineInfoExtraData, - inline_infos.NumColumnBits(InlineInfo::kExtraData) * num_inline_infos); + Stats::kByteKindInlineInfoArtMethod, + inline_infos.NumColumnBits(InlineInfo::kArtMethodHi) * num_inline_infos + + inline_infos.NumColumnBits(InlineInfo::kArtMethodLo) * num_inline_infos); stats_.AddBits( Stats::kByteKindInlineInfoDexRegisterMap, - inline_infos.NumColumnBits(InlineInfo::kDexRegisterMapOffset) * num_inline_infos); + inline_infos.NumColumnBits(InlineInfo::kDexRegisterMapIndex) * num_inline_infos); stats_.AddBits(Stats::kByteKindInlineInfoIsLast, num_inline_infos); } } diff --git a/runtime/dex_register_location.h b/runtime/dex_register_location.h new file mode 100644 index 0000000000..c6d4ad2feb --- /dev/null +++ b/runtime/dex_register_location.h @@ -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. + */ + +#ifndef ART_RUNTIME_DEX_REGISTER_LOCATION_H_ +#define ART_RUNTIME_DEX_REGISTER_LOCATION_H_ + +#include +#include + +#include "base/dchecked_vector.h" +#include "base/memory_region.h" + +namespace art { + +// Dex register location container used by DexRegisterMap and StackMapStream. +class DexRegisterLocation { + public: + enum class Kind : int32_t { + kNone = -1, // vreg has not been set. + kInStack, // vreg is on the stack, value holds the stack offset. + kConstant, // vreg is a constant value. + kInRegister, // vreg is in low 32 bits of a core physical register. + kInRegisterHigh, // vreg is in high 32 bits of a core physical register. + kInFpuRegister, // vreg is in low 32 bits of an FPU register. + kInFpuRegisterHigh, // vreg is in high 32 bits of an FPU register. + }; + + DexRegisterLocation(Kind kind, int32_t value) : kind_(kind), value_(value) {} + + static DexRegisterLocation None() { + return DexRegisterLocation(Kind::kNone, 0); + } + + bool IsLive() const { return kind_ != Kind::kNone; } + + Kind GetKind() const { return kind_; } + + // TODO: Remove. + Kind GetInternalKind() const { return kind_; } + + int32_t GetValue() const { return value_; } + + bool operator==(DexRegisterLocation other) const { + return kind_ == other.kind_ && value_ == other.value_; + } + + bool operator!=(DexRegisterLocation other) const { + return !(*this == other); + } + + private: + DexRegisterLocation() {} + + Kind kind_; + int32_t value_; + + friend class DexRegisterMap; // Allow creation of uninitialized array of locations. +}; + +static inline std::ostream& operator<<(std::ostream& stream, DexRegisterLocation::Kind kind) { + return stream << "Kind<" << static_cast(kind) << ">"; +} + +} // namespace art + +#endif // ART_RUNTIME_DEX_REGISTER_LOCATION_H_ diff --git a/runtime/oat.h b/runtime/oat.h index 8069a15661..e7e5848dd6 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: Optimize masks in stack maps. - static constexpr uint8_t kOatVersion[] = { '1', '4', '5', '\0' }; + // Last oat version changed reason: Rewrite dex register map encoding. + static constexpr uint8_t kOatVersion[] = { '1', '4', '6', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 63a09f25a4..4f4abf7f7f 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -232,7 +232,7 @@ void QuickExceptionHandler::SetCatchEnvironmentForOptimizedHandler(StackVisitor* DCHECK(catch_stack_map.IsValid()); DexRegisterMap catch_vreg_map = code_info.GetDexRegisterMapOf(catch_stack_map, number_of_vregs); - if (!catch_vreg_map.IsValid()) { + if (!catch_vreg_map.IsValid() || !catch_vreg_map.HasAnyLiveDexRegisters()) { return; } diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index 61fe2e7965..923bb3559a 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -16,6 +16,7 @@ #include "stack_map.h" +#include #include #include "art_method.h" @@ -24,149 +25,102 @@ namespace art { -constexpr size_t DexRegisterLocationCatalog::kNoLocationEntryIndex; - -std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation::Kind& kind) { +std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation& reg) { using Kind = DexRegisterLocation::Kind; - switch (kind) { + switch (reg.GetKind()) { case Kind::kNone: - return stream << "none"; + return stream << "None"; case Kind::kInStack: - return stream << "in stack"; + return stream << "sp+" << reg.GetValue(); case Kind::kInRegister: - return stream << "in register"; + return stream << "r" << reg.GetValue(); case Kind::kInRegisterHigh: - return stream << "in register high"; + return stream << "r" << reg.GetValue() << "/hi"; case Kind::kInFpuRegister: - return stream << "in fpu register"; + return stream << "f" << reg.GetValue(); case Kind::kInFpuRegisterHigh: - return stream << "in fpu register high"; + return stream << "f" << reg.GetValue() << "/hi"; case Kind::kConstant: - return stream << "as constant"; - case Kind::kInStackLargeOffset: - return stream << "in stack (large offset)"; - case Kind::kConstantLargeValue: - return stream << "as constant (large value)"; + return stream << "#" << reg.GetValue(); + default: + return stream << "DexRegisterLocation(" << static_cast(reg.GetKind()) + << "," << reg.GetValue() << ")"; } - return stream << "Kind<" << static_cast(kind) << ">"; -} - -DexRegisterLocation::Kind DexRegisterMap::GetLocationInternalKind( - uint16_t dex_register_number) const { - DexRegisterLocationCatalog dex_register_location_catalog = - code_info_.GetDexRegisterLocationCatalog(); - size_t location_catalog_entry_index = GetLocationCatalogEntryIndex( - dex_register_number, - code_info_.GetNumberOfLocationCatalogEntries()); - return dex_register_location_catalog.GetLocationInternalKind(location_catalog_entry_index); -} - -DexRegisterLocation DexRegisterMap::GetDexRegisterLocation(uint16_t dex_register_number) const { - DexRegisterLocationCatalog dex_register_location_catalog = - code_info_.GetDexRegisterLocationCatalog(); - size_t location_catalog_entry_index = GetLocationCatalogEntryIndex( - dex_register_number, - code_info_.GetNumberOfLocationCatalogEntries()); - return dex_register_location_catalog.GetDexRegisterLocation(location_catalog_entry_index); } -static void DumpRegisterMapping(std::ostream& os, - size_t dex_register_num, - DexRegisterLocation location, - const std::string& prefix = "v", - const std::string& suffix = "") { - os << prefix << dex_register_num << ": " - << location.GetInternalKind() - << " (" << location.GetValue() << ")" << suffix << '\n'; -} - -void StackMap::DumpEncoding(const BitTable<6>& table, - VariableIndentationOutputStream* vios) { - vios->Stream() - << "StackMapEncoding" - << " (PackedNativePcBits=" << table.NumColumnBits(kPackedNativePc) - << ", DexPcBits=" << table.NumColumnBits(kDexPc) - << ", DexRegisterMapOffsetBits=" << table.NumColumnBits(kDexRegisterMapOffset) - << ", InlineInfoIndexBits=" << table.NumColumnBits(kInlineInfoIndex) - << ", RegisterMaskIndexBits=" << table.NumColumnBits(kRegisterMaskIndex) - << ", StackMaskIndexBits=" << table.NumColumnBits(kStackMaskIndex) - << ")\n"; +static void DumpDexRegisterMap(VariableIndentationOutputStream* vios, + const DexRegisterMap& map) { + if (map.IsValid()) { + ScopedIndentation indent1(vios); + for (size_t i = 0; i < map.size(); ++i) { + if (map.IsDexRegisterLive(i)) { + vios->Stream() << "v" << i << ":" << map.Get(i) << " "; + } + } + vios->Stream() << "\n"; + } } -void InlineInfo::DumpEncoding(const BitTable<5>& table, - VariableIndentationOutputStream* vios) { - vios->Stream() - << "InlineInfoEncoding" - << " (IsLastBits=" << table.NumColumnBits(kIsLast) - << ", MethodIndexIdxBits=" << table.NumColumnBits(kMethodIndexIdx) - << ", DexPcBits=" << table.NumColumnBits(kDexPc) - << ", ExtraDataBits=" << table.NumColumnBits(kExtraData) - << ", DexRegisterMapOffsetBits=" << table.NumColumnBits(kDexRegisterMapOffset) - << ")\n"; +template +static void DumpTable(VariableIndentationOutputStream* vios, + const char* table_name, + const BitTable& table, + bool verbose, + bool is_mask = false) { + if (table.NumRows() != 0) { + vios->Stream() << table_name << " BitSize=" << table.NumRows() * table.NumRowBits(); + vios->Stream() << " Rows=" << table.NumRows() << " Bits={"; + for (size_t c = 0; c < table.NumColumns(); c++) { + vios->Stream() << (c != 0 ? " " : ""); + vios->Stream() << table.NumColumnBits(c); + } + vios->Stream() << "}\n"; + if (verbose) { + ScopedIndentation indent1(vios); + for (size_t r = 0; r < table.NumRows(); r++) { + vios->Stream() << "[" << std::right << std::setw(3) << r << "]={"; + for (size_t c = 0; c < table.NumColumns(); c++) { + vios->Stream() << (c != 0 ? " " : ""); + if (is_mask) { + BitMemoryRegion bits = table.GetBitMemoryRegion(r, c); + for (size_t b = 0, e = bits.size_in_bits(); b < e; b++) { + vios->Stream() << bits.LoadBit(e - b - 1); + } + } else { + vios->Stream() << std::right << std::setw(8) << static_cast(table.Get(r, c)); + } + } + vios->Stream() << "}\n"; + } + } + } } void CodeInfo::Dump(VariableIndentationOutputStream* vios, uint32_t code_offset, - uint16_t number_of_dex_registers, - bool dump_stack_maps, + uint16_t num_dex_registers, + bool verbose, InstructionSet instruction_set, const MethodInfo& method_info) const { - size_t number_of_stack_maps = GetNumberOfStackMaps(); vios->Stream() - << "Optimized CodeInfo (number_of_dex_registers=" << number_of_dex_registers - << ", number_of_stack_maps=" << number_of_stack_maps - << ")\n"; + << "CodeInfo" + << " BitSize=" << size_ * kBitsPerByte + << "\n"; ScopedIndentation indent1(vios); - StackMap::DumpEncoding(stack_maps_, vios); - if (HasInlineInfo()) { - InlineInfo::DumpEncoding(inline_infos_, vios); - } - // Display the Dex register location catalog. - GetDexRegisterLocationCatalog().Dump(vios, *this); + DumpTable(vios, "StackMaps", stack_maps_, verbose); + DumpTable(vios, "RegisterMasks", register_masks_, verbose); + DumpTable(vios, "StackMasks", stack_masks_, verbose, true /* is_mask */); + DumpTable(vios, "InvokeInfos", invoke_infos_, verbose); + DumpTable(vios, "InlineInfos", inline_infos_, verbose); + DumpTable(vios, "DexRegisterMasks", dex_register_masks_, verbose, true /* is_mask */); + DumpTable(vios, "DexRegisterMaps", dex_register_maps_, verbose); + DumpTable(vios, "DexRegisterCatalog", dex_register_catalog_, verbose); + // Display stack maps along with (live) Dex register maps. - if (dump_stack_maps) { - for (size_t i = 0; i < number_of_stack_maps; ++i) { + if (verbose) { + for (size_t i = 0; i < GetNumberOfStackMaps(); ++i) { StackMap stack_map = GetStackMapAt(i); - stack_map.Dump(vios, - *this, - method_info, - code_offset, - number_of_dex_registers, - instruction_set, - " " + std::to_string(i)); - } - } - // TODO: Dump the stack map's inline information? We need to know more from the caller: - // we need to know the number of dex registers for each inlined method. -} - -void DexRegisterLocationCatalog::Dump(VariableIndentationOutputStream* vios, - const CodeInfo& code_info) { - size_t number_of_location_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(); - size_t location_catalog_size_in_bytes = code_info.GetDexRegisterLocationCatalogSize(); - vios->Stream() - << "DexRegisterLocationCatalog (number_of_entries=" << number_of_location_catalog_entries - << ", size_in_bytes=" << location_catalog_size_in_bytes << ")\n"; - for (size_t i = 0; i < number_of_location_catalog_entries; ++i) { - DexRegisterLocation location = GetDexRegisterLocation(i); - ScopedIndentation indent1(vios); - DumpRegisterMapping(vios->Stream(), i, location, "entry "); - } -} - -void DexRegisterMap::Dump(VariableIndentationOutputStream* vios) const { - size_t number_of_location_catalog_entries = code_info_.GetNumberOfLocationCatalogEntries(); - // TODO: Display the bit mask of live Dex registers. - for (size_t j = 0; j < number_of_dex_registers_; ++j) { - if (IsDexRegisterLive(j)) { - size_t location_catalog_entry_index = GetLocationCatalogEntryIndex( - j, - number_of_location_catalog_entries); - DexRegisterLocation location = GetDexRegisterLocation(j); - ScopedIndentation indent1(vios); - DumpRegisterMapping( - vios->Stream(), j, location, "v", - "\t[entry " + std::to_string(static_cast(location_catalog_entry_index)) + "]"); + stack_map.Dump(vios, *this, method_info, code_offset, num_dex_registers, instruction_set); } } } @@ -176,17 +130,13 @@ void StackMap::Dump(VariableIndentationOutputStream* vios, const MethodInfo& method_info, uint32_t code_offset, uint16_t number_of_dex_registers, - InstructionSet instruction_set, - const std::string& header_suffix) const { + InstructionSet instruction_set) const { const uint32_t pc_offset = GetNativePcOffset(instruction_set); vios->Stream() - << "StackMap" << header_suffix + << "StackMap[" << Row() << "]" << std::hex - << " [native_pc=0x" << code_offset + pc_offset << "]" - << " (dex_pc=0x" << GetDexPc() - << ", native_pc_offset=0x" << pc_offset - << ", dex_register_map_offset=0x" << GetDexRegisterMapOffset() - << ", inline_info_offset=0x" << GetInlineInfoIndex() + << " (native_pc=0x" << code_offset + pc_offset + << ", dex_pc=0x" << GetDexPc() << ", register_mask=0x" << code_info.GetRegisterMaskOf(*this) << std::dec << ", stack_mask=0b"; @@ -195,11 +145,7 @@ void StackMap::Dump(VariableIndentationOutputStream* vios, vios->Stream() << stack_mask.LoadBit(e - i - 1); } vios->Stream() << ")\n"; - if (HasDexRegisterMap()) { - DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf( - *this, number_of_dex_registers); - dex_register_map.Dump(vios); - } + DumpDexRegisterMap(vios, code_info.GetDexRegisterMapOf(*this, number_of_dex_registers)); if (HasInlineInfo()) { InlineInfo inline_info = code_info.GetInlineInfoOf(*this); // We do not know the length of the dex register maps of inlined frames @@ -213,15 +159,12 @@ void InlineInfo::Dump(VariableIndentationOutputStream* vios, const CodeInfo& code_info, const MethodInfo& method_info, uint16_t number_of_dex_registers[]) const { - vios->Stream() << "InlineInfo with depth " - << static_cast(GetDepth()) - << "\n"; - for (size_t i = 0; i < GetDepth(); ++i) { vios->Stream() - << " At depth " << i + << "InlineInfo[" << Row() + i << "]" + << " (depth=" << i << std::hex - << " (dex_pc=0x" << GetDexPcAtDepth(i); + << ", dex_pc=0x" << GetDexPcAtDepth(i); if (EncodesArtMethodAtDepth(i)) { ScopedObjectAccess soa(Thread::Current()); vios->Stream() << ", method=" << GetArtMethodAtDepth(i)->PrettyMethod(); @@ -231,11 +174,9 @@ void InlineInfo::Dump(VariableIndentationOutputStream* vios, << ", method_index=" << GetMethodIndexAtDepth(method_info, i); } vios->Stream() << ")\n"; - if (HasDexRegisterMapAtDepth(i) && (number_of_dex_registers != nullptr)) { - DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapAtDepth(i, *this, number_of_dex_registers[i]); - ScopedIndentation indent1(vios); - dex_register_map.Dump(vios); + if (number_of_dex_registers != nullptr) { + uint16_t vregs = number_of_dex_registers[i]; + DumpDexRegisterMap(vios, code_info.GetDexRegisterMapAtDepth(i, *this, vregs)); } } } diff --git a/runtime/stack_map.h b/runtime/stack_map.h index c558846bb3..9aac204e70 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -26,6 +26,7 @@ #include "base/leb128.h" #include "base/memory_region.h" #include "dex/dex_file_types.h" +#include "dex_register_location.h" #include "method_info.h" #include "oat_quick_method_header.h" @@ -41,522 +42,76 @@ static constexpr ssize_t kFrameSlotSize = 4; class ArtMethod; class CodeInfo; -/** - * Classes in the following file are wrapper on stack map information backed - * by a MemoryRegion. As such they read and write to the region, they don't have - * their own fields. - */ - -// Dex register location container used by DexRegisterMap and StackMapStream. -class DexRegisterLocation { - public: - /* - * The location kind used to populate the Dex register information in a - * StackMapStream can either be: - * - kStack: vreg stored on the stack, value holds the stack offset; - * - kInRegister: vreg stored in low 32 bits of a core physical register, - * value holds the register number; - * - kInRegisterHigh: vreg stored in high 32 bits of a core physical register, - * value holds the register number; - * - kInFpuRegister: vreg stored in low 32 bits of an FPU register, - * value holds the register number; - * - kInFpuRegisterHigh: vreg stored in high 32 bits of an FPU register, - * value holds the register number; - * - kConstant: value holds the constant; - * - * In addition, DexRegisterMap also uses these values: - * - kInStackLargeOffset: value holds a "large" stack offset (greater than - * or equal to 128 bytes); - * - kConstantLargeValue: value holds a "large" constant (lower than 0, or - * or greater than or equal to 32); - * - kNone: the register has no location, meaning it has not been set. - */ - enum class Kind : uint8_t { - // Short location kinds, for entries fitting on one byte (3 bits - // for the kind, 5 bits for the value) in a DexRegisterMap. - kInStack = 0, // 0b000 - kInRegister = 1, // 0b001 - kInRegisterHigh = 2, // 0b010 - kInFpuRegister = 3, // 0b011 - kInFpuRegisterHigh = 4, // 0b100 - kConstant = 5, // 0b101 - - // Large location kinds, requiring a 5-byte encoding (1 byte for the - // kind, 4 bytes for the value). - - // Stack location at a large offset, meaning that the offset value - // divided by the stack frame slot size (4 bytes) cannot fit on a - // 5-bit unsigned integer (i.e., this offset value is greater than - // or equal to 2^5 * 4 = 128 bytes). - kInStackLargeOffset = 6, // 0b110 - - // Large constant, that cannot fit on a 5-bit signed integer (i.e., - // lower than 0, or greater than or equal to 2^5 = 32). - kConstantLargeValue = 7, // 0b111 - - // Entries with no location are not stored and do not need own marker. - kNone = static_cast(-1), - - kLastLocationKind = kConstantLargeValue - }; - - static_assert( - sizeof(Kind) == 1u, - "art::DexRegisterLocation::Kind has a size different from one byte."); - - static bool IsShortLocationKind(Kind kind) { - switch (kind) { - case Kind::kInStack: - case Kind::kInRegister: - case Kind::kInRegisterHigh: - case Kind::kInFpuRegister: - case Kind::kInFpuRegisterHigh: - case Kind::kConstant: - return true; - - case Kind::kInStackLargeOffset: - case Kind::kConstantLargeValue: - return false; - - case Kind::kNone: - LOG(FATAL) << "Unexpected location kind"; - } - UNREACHABLE(); - } +std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation& reg); - // Convert `kind` to a "surface" kind, i.e. one that doesn't include - // any value with a "large" qualifier. - // TODO: Introduce another enum type for the surface kind? - static Kind ConvertToSurfaceKind(Kind kind) { - switch (kind) { - case Kind::kInStack: - case Kind::kInRegister: - case Kind::kInRegisterHigh: - case Kind::kInFpuRegister: - case Kind::kInFpuRegisterHigh: - case Kind::kConstant: - return kind; - - case Kind::kInStackLargeOffset: - return Kind::kInStack; - - case Kind::kConstantLargeValue: - return Kind::kConstant; - - case Kind::kNone: - return kind; - } - UNREACHABLE(); - } - - // Required by art::StackMapStream::LocationCatalogEntriesIndices. - DexRegisterLocation() : kind_(Kind::kNone), value_(0) {} - - DexRegisterLocation(Kind kind, int32_t value) : kind_(kind), value_(value) {} - - static DexRegisterLocation None() { - return DexRegisterLocation(Kind::kNone, 0); - } - - // Get the "surface" kind of the location, i.e., the one that doesn't - // include any value with a "large" qualifier. - Kind GetKind() const { - return ConvertToSurfaceKind(kind_); - } - - // Get the value of the location. - int32_t GetValue() const { return value_; } - - // Get the actual kind of the location. - Kind GetInternalKind() const { return kind_; } - - bool operator==(DexRegisterLocation other) const { - return kind_ == other.kind_ && value_ == other.value_; - } - - bool operator!=(DexRegisterLocation other) const { - return !(*this == other); - } - - private: - Kind kind_; - int32_t value_; - - friend class DexRegisterLocationHashFn; -}; - -std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation::Kind& kind); - -/** - * Store information on unique Dex register locations used in a method. - * The information is of the form: - * - * [DexRegisterLocation+]. - * - * DexRegisterLocations are either 1- or 5-byte wide (see art::DexRegisterLocation::Kind). - */ -class DexRegisterLocationCatalog { +// Information on Dex register locations for a specific PC. +// Effectively just a convenience wrapper for DexRegisterLocation vector. +// If the size is small enough, it keeps the data on the stack. +class DexRegisterMap { public: - explicit DexRegisterLocationCatalog(MemoryRegion region) : region_(region) {} - - // Short (compressed) location, fitting on one byte. - typedef uint8_t ShortLocation; - - void SetRegisterInfo(size_t offset, const DexRegisterLocation& dex_register_location) { - DexRegisterLocation::Kind kind = ComputeCompressedKind(dex_register_location); - int32_t value = dex_register_location.GetValue(); - if (DexRegisterLocation::IsShortLocationKind(kind)) { - // Short location. Compress the kind and the value as a single byte. - if (kind == DexRegisterLocation::Kind::kInStack) { - // Instead of storing stack offsets expressed in bytes for - // short stack locations, store slot offsets. A stack offset - // is a multiple of 4 (kFrameSlotSize). This means that by - // dividing it by 4, we can fit values from the [0, 128) - // interval in a short stack location, and not just values - // from the [0, 32) interval. - DCHECK_EQ(value % kFrameSlotSize, 0); - value /= kFrameSlotSize; - } - DCHECK(IsShortValue(value)) << value; - region_.StoreUnaligned(offset, MakeShortLocation(kind, value)); - } else { - // Large location. Write the location on one byte and the value - // on 4 bytes. - DCHECK(!IsShortValue(value)) << value; - if (kind == DexRegisterLocation::Kind::kInStackLargeOffset) { - // Also divide large stack offsets by 4 for the sake of consistency. - DCHECK_EQ(value % kFrameSlotSize, 0); - value /= kFrameSlotSize; - } - // Data can be unaligned as the written Dex register locations can - // either be 1-byte or 5-byte wide. Use - // art::MemoryRegion::StoreUnaligned instead of - // art::MemoryRegion::Store to prevent unligned word accesses on ARM. - region_.StoreUnaligned(offset, kind); - region_.StoreUnaligned(offset + sizeof(DexRegisterLocation::Kind), value); - } - } - - // Find the offset of the location catalog entry number `location_catalog_entry_index`. - size_t FindLocationOffset(size_t location_catalog_entry_index) const { - size_t offset = kFixedSize; - // Skip the first `location_catalog_entry_index - 1` entries. - for (uint16_t i = 0; i < location_catalog_entry_index; ++i) { - // Read the first next byte and inspect its first 3 bits to decide - // whether it is a short or a large location. - DexRegisterLocation::Kind kind = ExtractKindAtOffset(offset); - if (DexRegisterLocation::IsShortLocationKind(kind)) { - // Short location. Skip the current byte. - offset += SingleShortEntrySize(); - } else { - // Large location. Skip the 5 next bytes. - offset += SingleLargeEntrySize(); - } - } - return offset; - } - - // Get the internal kind of entry at `location_catalog_entry_index`. - DexRegisterLocation::Kind GetLocationInternalKind(size_t location_catalog_entry_index) const { - if (location_catalog_entry_index == kNoLocationEntryIndex) { - return DexRegisterLocation::Kind::kNone; - } - return ExtractKindAtOffset(FindLocationOffset(location_catalog_entry_index)); - } - - // Get the (surface) kind and value of entry at `location_catalog_entry_index`. - DexRegisterLocation GetDexRegisterLocation(size_t location_catalog_entry_index) const { - if (location_catalog_entry_index == kNoLocationEntryIndex) { - return DexRegisterLocation::None(); - } - size_t offset = FindLocationOffset(location_catalog_entry_index); - // Read the first byte and inspect its first 3 bits to get the location. - ShortLocation first_byte = region_.LoadUnaligned(offset); - DexRegisterLocation::Kind kind = ExtractKindFromShortLocation(first_byte); - if (DexRegisterLocation::IsShortLocationKind(kind)) { - // Short location. Extract the value from the remaining 5 bits. - int32_t value = ExtractValueFromShortLocation(first_byte); - if (kind == DexRegisterLocation::Kind::kInStack) { - // Convert the stack slot (short) offset to a byte offset value. - value *= kFrameSlotSize; - } - return DexRegisterLocation(kind, value); + // Create map for given number of registers and initialize all locations to None. + explicit DexRegisterMap(size_t count) : count_(count), regs_small_{} { + if (count_ <= kSmallCount) { + std::fill_n(regs_small_.begin(), count, DexRegisterLocation::None()); } else { - // Large location. Read the four next bytes to get the value. - int32_t value = region_.LoadUnaligned(offset + sizeof(DexRegisterLocation::Kind)); - if (kind == DexRegisterLocation::Kind::kInStackLargeOffset) { - // Convert the stack slot (large) offset to a byte offset value. - value *= kFrameSlotSize; - } - return DexRegisterLocation(kind, value); + regs_large_.resize(count, DexRegisterLocation::None()); } } - // Compute the compressed kind of `location`. - static DexRegisterLocation::Kind ComputeCompressedKind(const DexRegisterLocation& location) { - DexRegisterLocation::Kind kind = location.GetInternalKind(); - switch (kind) { - case DexRegisterLocation::Kind::kInStack: - return IsShortStackOffsetValue(location.GetValue()) - ? DexRegisterLocation::Kind::kInStack - : DexRegisterLocation::Kind::kInStackLargeOffset; - - case DexRegisterLocation::Kind::kInRegister: - case DexRegisterLocation::Kind::kInRegisterHigh: - DCHECK_GE(location.GetValue(), 0); - DCHECK_LT(location.GetValue(), 1 << kValueBits); - return kind; - - case DexRegisterLocation::Kind::kInFpuRegister: - case DexRegisterLocation::Kind::kInFpuRegisterHigh: - DCHECK_GE(location.GetValue(), 0); - DCHECK_LT(location.GetValue(), 1 << kValueBits); - return kind; - - case DexRegisterLocation::Kind::kConstant: - return IsShortConstantValue(location.GetValue()) - ? DexRegisterLocation::Kind::kConstant - : DexRegisterLocation::Kind::kConstantLargeValue; - - case DexRegisterLocation::Kind::kConstantLargeValue: - case DexRegisterLocation::Kind::kInStackLargeOffset: - case DexRegisterLocation::Kind::kNone: - LOG(FATAL) << "Unexpected location kind " << kind; - } - UNREACHABLE(); + DexRegisterLocation* data() { + return count_ <= kSmallCount ? regs_small_.data() : regs_large_.data(); } - // Can `location` be turned into a short location? - static bool CanBeEncodedAsShortLocation(const DexRegisterLocation& location) { - DexRegisterLocation::Kind kind = location.GetInternalKind(); - switch (kind) { - case DexRegisterLocation::Kind::kInStack: - return IsShortStackOffsetValue(location.GetValue()); - - case DexRegisterLocation::Kind::kInRegister: - case DexRegisterLocation::Kind::kInRegisterHigh: - case DexRegisterLocation::Kind::kInFpuRegister: - case DexRegisterLocation::Kind::kInFpuRegisterHigh: - return true; - - case DexRegisterLocation::Kind::kConstant: - return IsShortConstantValue(location.GetValue()); + size_t size() const { return count_; } - case DexRegisterLocation::Kind::kConstantLargeValue: - case DexRegisterLocation::Kind::kInStackLargeOffset: - case DexRegisterLocation::Kind::kNone: - LOG(FATAL) << "Unexpected location kind " << kind; - } - UNREACHABLE(); - } + bool IsValid() const { return count_ != 0; } - static size_t EntrySize(const DexRegisterLocation& location) { - return CanBeEncodedAsShortLocation(location) ? SingleShortEntrySize() : SingleLargeEntrySize(); + DexRegisterLocation Get(size_t index) const { + DCHECK_LT(index, count_); + return count_ <= kSmallCount ? regs_small_[index] : regs_large_[index]; } - static size_t SingleShortEntrySize() { - return sizeof(ShortLocation); - } - - static size_t SingleLargeEntrySize() { - return sizeof(DexRegisterLocation::Kind) + sizeof(int32_t); - } - - size_t Size() const { - return region_.size(); - } - - void Dump(VariableIndentationOutputStream* vios, - const CodeInfo& code_info); - - // Special (invalid) Dex register location catalog entry index meaning - // that there is no location for a given Dex register (i.e., it is - // mapped to a DexRegisterLocation::Kind::kNone location). - static constexpr size_t kNoLocationEntryIndex = -1; - - private: - static constexpr int kFixedSize = 0; - - // Width of the kind "field" in a short location, in bits. - static constexpr size_t kKindBits = 3; - // Width of the value "field" in a short location, in bits. - static constexpr size_t kValueBits = 5; - - static constexpr uint8_t kKindMask = (1 << kKindBits) - 1; - static constexpr int32_t kValueMask = (1 << kValueBits) - 1; - static constexpr size_t kKindOffset = 0; - static constexpr size_t kValueOffset = kKindBits; - - static bool IsShortStackOffsetValue(int32_t value) { - DCHECK_EQ(value % kFrameSlotSize, 0); - return IsShortValue(value / kFrameSlotSize); - } - - static bool IsShortConstantValue(int32_t value) { - return IsShortValue(value); - } - - static bool IsShortValue(int32_t value) { - return IsUint(value); - } - - static ShortLocation MakeShortLocation(DexRegisterLocation::Kind kind, int32_t value) { - uint8_t kind_integer_value = static_cast(kind); - DCHECK(IsUint(kind_integer_value)) << kind_integer_value; - DCHECK(IsShortValue(value)) << value; - return (kind_integer_value & kKindMask) << kKindOffset - | (value & kValueMask) << kValueOffset; - } - - static DexRegisterLocation::Kind ExtractKindFromShortLocation(ShortLocation location) { - uint8_t kind = (location >> kKindOffset) & kKindMask; - DCHECK_LE(kind, static_cast(DexRegisterLocation::Kind::kLastLocationKind)); - // We do not encode kNone locations in the stack map. - DCHECK_NE(kind, static_cast(DexRegisterLocation::Kind::kNone)); - return static_cast(kind); + DexRegisterLocation::Kind GetLocationKind(uint16_t dex_register_number) const { + return Get(dex_register_number).GetKind(); } - static int32_t ExtractValueFromShortLocation(ShortLocation location) { - return (location >> kValueOffset) & kValueMask; + // TODO: Remove. + DexRegisterLocation::Kind GetLocationInternalKind(uint16_t dex_register_number) const { + return Get(dex_register_number).GetKind(); } - // Extract a location kind from the byte at position `offset`. - DexRegisterLocation::Kind ExtractKindAtOffset(size_t offset) const { - ShortLocation first_byte = region_.LoadUnaligned(offset); - return ExtractKindFromShortLocation(first_byte); + DexRegisterLocation GetDexRegisterLocation(uint16_t dex_register_number) const { + return Get(dex_register_number); } - MemoryRegion region_; - - friend class CodeInfo; - friend class StackMapStream; -}; - -/* Information on Dex register locations for a specific PC, mapping a - * stack map's Dex register to a location entry in a DexRegisterLocationCatalog. - * The information is of the form: - * - * [live_bit_mask, entries*] - * - * where entries are concatenated unsigned integer values encoded on a number - * of bits (fixed per DexRegisterMap instances of a CodeInfo object) depending - * on the number of entries in the Dex register location catalog - * (see DexRegisterMap::SingleEntrySizeInBits). The map is 1-byte aligned. - */ -class DexRegisterMap { - public: - DexRegisterMap(MemoryRegion region, uint16_t number_of_dex_registers, const CodeInfo& code_info) - : region_(region), - number_of_dex_registers_(number_of_dex_registers), - code_info_(code_info) {} - - bool IsValid() const { return region_.IsValid(); } - - // Get the surface kind of Dex register `dex_register_number`. - DexRegisterLocation::Kind GetLocationKind(uint16_t dex_register_number) const { - return DexRegisterLocation::ConvertToSurfaceKind(GetLocationInternalKind(dex_register_number)); - } - - // Get the internal kind of Dex register `dex_register_number`. - DexRegisterLocation::Kind GetLocationInternalKind(uint16_t dex_register_number) const; - - // Get the Dex register location `dex_register_number`. - DexRegisterLocation GetDexRegisterLocation(uint16_t dex_register_number) const; - int32_t GetStackOffsetInBytes(uint16_t dex_register_number) const { - DexRegisterLocation location = GetDexRegisterLocation(dex_register_number); + DexRegisterLocation location = Get(dex_register_number); DCHECK(location.GetKind() == DexRegisterLocation::Kind::kInStack); - // GetDexRegisterLocation returns the offset in bytes. return location.GetValue(); } int32_t GetConstant(uint16_t dex_register_number) const { - DexRegisterLocation location = GetDexRegisterLocation(dex_register_number); - DCHECK_EQ(location.GetKind(), DexRegisterLocation::Kind::kConstant); + DexRegisterLocation location = Get(dex_register_number); + DCHECK(location.GetKind() == DexRegisterLocation::Kind::kConstant); return location.GetValue(); } int32_t GetMachineRegister(uint16_t dex_register_number) const { - DexRegisterLocation location = GetDexRegisterLocation(dex_register_number); - DCHECK(location.GetInternalKind() == DexRegisterLocation::Kind::kInRegister || - location.GetInternalKind() == DexRegisterLocation::Kind::kInRegisterHigh || - location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegister || - location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegisterHigh) - << location.GetInternalKind(); + DexRegisterLocation location = Get(dex_register_number); + DCHECK(location.GetKind() == DexRegisterLocation::Kind::kInRegister || + location.GetKind() == DexRegisterLocation::Kind::kInRegisterHigh || + location.GetKind() == DexRegisterLocation::Kind::kInFpuRegister || + location.GetKind() == DexRegisterLocation::Kind::kInFpuRegisterHigh); return location.GetValue(); } - // Get the index of the entry in the Dex register location catalog - // corresponding to `dex_register_number`. - size_t GetLocationCatalogEntryIndex(uint16_t dex_register_number, - size_t number_of_location_catalog_entries) const { - if (!IsDexRegisterLive(dex_register_number)) { - return DexRegisterLocationCatalog::kNoLocationEntryIndex; - } - - if (number_of_location_catalog_entries == 1) { - // We do not allocate space for location maps in the case of a - // single-entry location catalog, as it is useless. The only valid - // entry index is 0; - return 0; - } - - // The bit offset of the beginning of the map locations. - size_t map_locations_offset_in_bits = - GetLocationMappingDataOffset(number_of_dex_registers_) * kBitsPerByte; - size_t index_in_dex_register_map = GetIndexInDexRegisterMap(dex_register_number); - DCHECK_LT(index_in_dex_register_map, GetNumberOfLiveDexRegisters()); - // The bit size of an entry. - size_t map_entry_size_in_bits = SingleEntrySizeInBits(number_of_location_catalog_entries); - // The bit offset where `index_in_dex_register_map` is located. - size_t entry_offset_in_bits = - map_locations_offset_in_bits + index_in_dex_register_map * map_entry_size_in_bits; - size_t location_catalog_entry_index = - region_.LoadBits(entry_offset_in_bits, map_entry_size_in_bits); - DCHECK_LT(location_catalog_entry_index, number_of_location_catalog_entries); - return location_catalog_entry_index; - } - - // Map entry at `index_in_dex_register_map` to `location_catalog_entry_index`. - void SetLocationCatalogEntryIndex(size_t index_in_dex_register_map, - size_t location_catalog_entry_index, - size_t number_of_location_catalog_entries) { - DCHECK_LT(index_in_dex_register_map, GetNumberOfLiveDexRegisters()); - DCHECK_LT(location_catalog_entry_index, number_of_location_catalog_entries); - - if (number_of_location_catalog_entries == 1) { - // We do not allocate space for location maps in the case of a - // single-entry location catalog, as it is useless. - return; - } - - // The bit offset of the beginning of the map locations. - size_t map_locations_offset_in_bits = - GetLocationMappingDataOffset(number_of_dex_registers_) * kBitsPerByte; - // The bit size of an entry. - size_t map_entry_size_in_bits = SingleEntrySizeInBits(number_of_location_catalog_entries); - // The bit offset where `index_in_dex_register_map` is located. - size_t entry_offset_in_bits = - map_locations_offset_in_bits + index_in_dex_register_map * map_entry_size_in_bits; - region_.StoreBits(entry_offset_in_bits, location_catalog_entry_index, map_entry_size_in_bits); - } - - void SetLiveBitMask(uint16_t number_of_dex_registers, - const BitVector& live_dex_registers_mask) { - size_t live_bit_mask_offset_in_bits = GetLiveBitMaskOffset() * kBitsPerByte; - for (uint16_t i = 0; i < number_of_dex_registers; ++i) { - region_.StoreBit(live_bit_mask_offset_in_bits + i, live_dex_registers_mask.IsBitSet(i)); - } - } - ALWAYS_INLINE bool IsDexRegisterLive(uint16_t dex_register_number) const { - size_t live_bit_mask_offset_in_bits = GetLiveBitMaskOffset() * kBitsPerByte; - return region_.LoadBit(live_bit_mask_offset_in_bits + dex_register_number); + return Get(dex_register_number).IsLive(); } - size_t GetNumberOfLiveDexRegisters(uint16_t number_of_dex_registers) const { + size_t GetNumberOfLiveDexRegisters() const { size_t number_of_live_dex_registers = 0; - for (size_t i = 0; i < number_of_dex_registers; ++i) { + for (size_t i = 0; i < count_; ++i) { if (IsDexRegisterLive(i)) { ++number_of_live_dex_registers; } @@ -564,74 +119,22 @@ class DexRegisterMap { return number_of_live_dex_registers; } - size_t GetNumberOfLiveDexRegisters() const { - return GetNumberOfLiveDexRegisters(number_of_dex_registers_); - } - - static size_t GetLiveBitMaskOffset() { - return kFixedSize; - } - - // Compute the size of the live register bit mask (in bytes), for a - // method having `number_of_dex_registers` Dex registers. - static size_t GetLiveBitMaskSize(uint16_t number_of_dex_registers) { - return RoundUp(number_of_dex_registers, kBitsPerByte) / kBitsPerByte; - } - - static size_t GetLocationMappingDataOffset(uint16_t number_of_dex_registers) { - return GetLiveBitMaskOffset() + GetLiveBitMaskSize(number_of_dex_registers); - } - - size_t GetLocationMappingDataSize(size_t number_of_location_catalog_entries) const { - size_t location_mapping_data_size_in_bits = - GetNumberOfLiveDexRegisters() - * SingleEntrySizeInBits(number_of_location_catalog_entries); - return RoundUp(location_mapping_data_size_in_bits, kBitsPerByte) / kBitsPerByte; - } - - // Return the size of a map entry in bits. Note that if - // `number_of_location_catalog_entries` equals 1, this function returns 0, - // which is fine, as there is no need to allocate a map for a - // single-entry location catalog; the only valid location catalog entry index - // for a live register in this case is 0 and there is no need to - // store it. - static size_t SingleEntrySizeInBits(size_t number_of_location_catalog_entries) { - // Handle the case of 0, as we cannot pass 0 to art::WhichPowerOf2. - return number_of_location_catalog_entries == 0 - ? 0u - : WhichPowerOf2(RoundUpToPowerOfTwo(number_of_location_catalog_entries)); - } - - // Return the size of the DexRegisterMap object, in bytes. - size_t Size() const { - return BitsToBytesRoundUp(region_.size_in_bits()); - } - - void Dump(VariableIndentationOutputStream* vios) const; - - private: - // Return the index in the Dex register map corresponding to the Dex - // register number `dex_register_number`. - size_t GetIndexInDexRegisterMap(uint16_t dex_register_number) const { - if (!IsDexRegisterLive(dex_register_number)) { - return kInvalidIndexInDexRegisterMap; + bool HasAnyLiveDexRegisters() const { + for (size_t i = 0; i < count_; ++i) { + if (IsDexRegisterLive(i)) { + return true; + } } - return GetNumberOfLiveDexRegisters(dex_register_number); + return false; } - // Special (invalid) Dex register map entry index meaning that there - // is no index in the map for a given Dex register (i.e., it must - // have been mapped to a DexRegisterLocation::Kind::kNone location). - static constexpr size_t kInvalidIndexInDexRegisterMap = -1; - - static constexpr int kFixedSize = 0; - - BitMemoryRegion region_; - uint16_t number_of_dex_registers_; - const CodeInfo& code_info_; - - friend class CodeInfo; - friend class StackMapStream; + private: + // Store the data inline if the number of registers is small to avoid memory allocations. + // If count_ <= kSmallCount, we use the regs_small_ array, and regs_large_ otherwise. + static constexpr size_t kSmallCount = 16; + size_t count_; + std::array regs_small_; + dchecked_vector regs_large_; }; /** @@ -642,15 +145,16 @@ class DexRegisterMap { * - Knowing the inlining information, * - Knowing the values of dex registers. */ -class StackMap : public BitTable<6>::Accessor { +class StackMap : public BitTable<7>::Accessor { public: enum Field { kPackedNativePc, kDexPc, - kDexRegisterMapOffset, - kInlineInfoIndex, kRegisterMaskIndex, kStackMaskIndex, + kInlineInfoIndex, + kDexRegisterMaskIndex, + kDexRegisterMapIndex, kCount, }; @@ -664,8 +168,10 @@ class StackMap : public BitTable<6>::Accessor { uint32_t GetDexPc() const { return Get(); } - uint32_t GetDexRegisterMapOffset() const { return Get(); } - bool HasDexRegisterMap() const { return GetDexRegisterMapOffset() != kNoValue; } + uint32_t GetDexRegisterMaskIndex() const { return Get(); } + + uint32_t GetDexRegisterMapIndex() const { return Get(); } + bool HasDexRegisterMap() const { return GetDexRegisterMapIndex() != kNoValue; } uint32_t GetInlineInfoIndex() const { return Get(); } bool HasInlineInfo() const { return GetInlineInfoIndex() != kNoValue; } @@ -685,14 +191,12 @@ class StackMap : public BitTable<6>::Accessor { return native_pc; } - static void DumpEncoding(const BitTable<6>& table, VariableIndentationOutputStream* vios); void Dump(VariableIndentationOutputStream* vios, const CodeInfo& code_info, const MethodInfo& method_info, uint32_t code_offset, uint16_t number_of_dex_registers, - InstructionSet instruction_set, - const std::string& header_suffix = "") const; + InstructionSet instruction_set) const; }; /** @@ -700,14 +204,16 @@ class StackMap : public BitTable<6>::Accessor { * The row referenced from the StackMap holds information at depth 0. * Following rows hold information for further depths. */ -class InlineInfo : public BitTable<5>::Accessor { +class InlineInfo : public BitTable<7>::Accessor { public: enum Field { kIsLast, // Determines if there are further rows for further depths. - kMethodIndexIdx, // Method index or ArtMethod high bits. kDexPc, - kExtraData, // ArtMethod low bits or 1. - kDexRegisterMapOffset, + kMethodIndexIdx, + kArtMethodHi, // High bits of ArtMethod*. + kArtMethodLo, // Low bits of ArtMethod*. + kDexRegisterMaskIndex, + kDexRegisterMapIndex, kCount, }; static constexpr uint32_t kLast = -1; @@ -740,30 +246,26 @@ class InlineInfo : public BitTable<5>::Accessor { } bool EncodesArtMethodAtDepth(uint32_t depth) const { - return (AtDepth(depth).Get() & 1) == 0; + return AtDepth(depth).Get() != kNoValue; } ArtMethod* GetArtMethodAtDepth(uint32_t depth) const { - uint32_t low_bits = AtDepth(depth).Get(); - uint32_t high_bits = AtDepth(depth).Get(); - if (high_bits == 0) { - return reinterpret_cast(low_bits); - } else { - uint64_t address = high_bits; - address = address << 32; - return reinterpret_cast(address | low_bits); - } + uint64_t lo = AtDepth(depth).Get(); + uint64_t hi = AtDepth(depth).Get(); + return reinterpret_cast((hi << 32) | lo); } - uint32_t GetDexRegisterMapOffsetAtDepth(uint32_t depth) const { - return AtDepth(depth).Get(); + uint32_t GetDexRegisterMaskIndexAtDepth(uint32_t depth) const { + return AtDepth(depth).Get(); } + uint32_t GetDexRegisterMapIndexAtDepth(uint32_t depth) const { + return AtDepth(depth).Get(); + } bool HasDexRegisterMapAtDepth(uint32_t depth) const { - return GetDexRegisterMapOffsetAtDepth(depth) != StackMap::kNoValue; + return GetDexRegisterMapIndexAtDepth(depth) != kNoValue; } - static void DumpEncoding(const BitTable<5>& table, VariableIndentationOutputStream* vios); void Dump(VariableIndentationOutputStream* vios, const CodeInfo& info, const MethodInfo& method_info, @@ -795,6 +297,40 @@ class InvokeInfo : public BitTable<3>::Accessor { } }; +class DexRegisterInfo : public BitTable<2>::Accessor { + public: + enum Field { + kKind, + kPackedValue, + kCount, + }; + + DexRegisterInfo(const BitTable* table, uint32_t row) + : BitTable::Accessor(table, row) {} + + ALWAYS_INLINE DexRegisterLocation GetLocation() const { + DexRegisterLocation::Kind kind = static_cast(Get()); + return DexRegisterLocation(kind, UnpackValue(kind, Get())); + } + + static uint32_t PackValue(DexRegisterLocation::Kind kind, uint32_t value) { + uint32_t packed_value = value; + if (kind == DexRegisterLocation::Kind::kInStack) { + DCHECK(IsAligned(packed_value)); + packed_value /= kFrameSlotSize; + } + return packed_value; + } + + static uint32_t UnpackValue(DexRegisterLocation::Kind kind, uint32_t packed_value) { + uint32_t value = packed_value; + if (kind == DexRegisterLocation::Kind::kInStack) { + value *= kFrameSlotSize; + } + return value; + } +}; + // Register masks tend to have many trailing zero bits (caller-saves are usually not encoded), // therefore it is worth encoding the mask as value+shift. class RegisterMask : public BitTable<2>::Accessor { @@ -815,11 +351,7 @@ class RegisterMask : public BitTable<2>::Accessor { /** * Wrapper around all compiler information collected for a method. - * The information is of the form: - * - * [BitTable
, BitTable, BitTable, BitTable, - * BitTable, BitTable, DexRegisterMap, DexLocationCatalog] - * + * See the Decode method at the end for the precise binary format. */ class CodeInfo { public: @@ -840,11 +372,7 @@ class CodeInfo { } bool HasInlineInfo() const { - return stack_maps_.NumColumnBits(StackMap::kInlineInfoIndex) != 0; - } - - DexRegisterLocationCatalog GetDexRegisterLocationCatalog() const { - return DexRegisterLocationCatalog(location_catalog_); + return inline_infos_.NumRows() > 0; } ALWAYS_INLINE StackMap GetStackMapAt(size_t index) const { @@ -866,11 +394,11 @@ class CodeInfo { } uint32_t GetNumberOfLocationCatalogEntries() const { - return location_catalog_entries_; + return dex_register_catalog_.NumRows(); } - uint32_t GetDexRegisterLocationCatalogSize() const { - return location_catalog_.size(); + ALWAYS_INLINE DexRegisterLocation GetDexRegisterCatalogEntry(size_t index) const { + return DexRegisterInfo(&dex_register_catalog_, index).GetLocation(); } uint32_t GetNumberOfStackMaps() const { @@ -881,41 +409,19 @@ class CodeInfo { return InvokeInfo(&invoke_infos_, index); } - DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, - size_t number_of_dex_registers) const { - if (!stack_map.HasDexRegisterMap()) { - return DexRegisterMap(MemoryRegion(), 0, *this); - } - const uint32_t offset = stack_map.GetDexRegisterMapOffset(); - size_t size = ComputeDexRegisterMapSizeOf(offset, number_of_dex_registers); - return DexRegisterMap(dex_register_maps_.Subregion(offset, size), - number_of_dex_registers, - *this); - } - - size_t GetDexRegisterMapsSize(uint32_t number_of_dex_registers) const { - size_t total = 0; - for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) { - StackMap stack_map = GetStackMapAt(i); - DexRegisterMap map(GetDexRegisterMapOf(stack_map, number_of_dex_registers)); - total += map.Size(); - } - return total; + ALWAYS_INLINE DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, + size_t num_dex_registers) const { + return DecodeDexRegisterMap(stack_map.GetDexRegisterMaskIndex(), + stack_map.GetDexRegisterMapIndex(), + num_dex_registers); } - // Return the `DexRegisterMap` pointed by `inline_info` at depth `depth`. - DexRegisterMap GetDexRegisterMapAtDepth(uint8_t depth, - InlineInfo inline_info, - uint32_t number_of_dex_registers) const { - if (!inline_info.HasDexRegisterMapAtDepth(depth)) { - return DexRegisterMap(MemoryRegion(), 0, *this); - } else { - uint32_t offset = inline_info.GetDexRegisterMapOffsetAtDepth(depth); - size_t size = ComputeDexRegisterMapSizeOf(offset, number_of_dex_registers); - return DexRegisterMap(dex_register_maps_.Subregion(offset, size), - number_of_dex_registers, - *this); - } + ALWAYS_INLINE DexRegisterMap GetDexRegisterMapAtDepth(uint8_t depth, + InlineInfo inline_info, + size_t num_dex_registers) const { + return DecodeDexRegisterMap(inline_info.GetDexRegisterMaskIndexAtDepth(depth), + inline_info.GetDexRegisterMapIndexAtDepth(depth), + num_dex_registers); } InlineInfo GetInlineInfo(size_t index) const { @@ -965,8 +471,8 @@ class CodeInfo { if (other.GetDexPc() == dex_pc && other.GetNativePcOffset(kRuntimeISA) == stack_map.GetNativePcOffset(kRuntimeISA)) { - DCHECK_EQ(other.GetDexRegisterMapOffset(), - stack_map.GetDexRegisterMapOffset()); + DCHECK_EQ(other.GetDexRegisterMapIndex(), + stack_map.GetDexRegisterMapIndex()); DCHECK(!stack_map.HasInlineInfo()); if (i < e - 2) { // Make sure there are not three identical stack maps following each other. @@ -1004,81 +510,61 @@ class CodeInfo { return InvokeInfo(&invoke_infos_, -1); } - // Dump this CodeInfo object on `os`. `code_offset` is the (absolute) - // native PC of the compiled method and `number_of_dex_registers` the - // number of Dex virtual registers used in this method. If - // `dump_stack_maps` is true, also dump the stack maps and the - // associated Dex register maps. + // Dump this CodeInfo object on `vios`. + // `code_offset` is the (absolute) native PC of the compiled method. void Dump(VariableIndentationOutputStream* vios, uint32_t code_offset, uint16_t number_of_dex_registers, - bool dump_stack_maps, + bool verbose, InstructionSet instruction_set, const MethodInfo& method_info) const; private: - // Compute the size of the Dex register map associated to the stack map at - // `dex_register_map_offset_in_code_info`. - size_t ComputeDexRegisterMapSizeOf(uint32_t dex_register_map_offset, - uint16_t number_of_dex_registers) const { - // Offset where the actual mapping data starts within art::DexRegisterMap. - size_t location_mapping_data_offset_in_dex_register_map = - DexRegisterMap::GetLocationMappingDataOffset(number_of_dex_registers); - // Create a temporary art::DexRegisterMap to be able to call - // art::DexRegisterMap::GetNumberOfLiveDexRegisters and - DexRegisterMap dex_register_map_without_locations( - MemoryRegion(dex_register_maps_.Subregion(dex_register_map_offset, - location_mapping_data_offset_in_dex_register_map)), - number_of_dex_registers, - *this); - size_t number_of_live_dex_registers = - dex_register_map_without_locations.GetNumberOfLiveDexRegisters(); - size_t location_mapping_data_size_in_bits = - DexRegisterMap::SingleEntrySizeInBits(GetNumberOfLocationCatalogEntries()) - * number_of_live_dex_registers; - size_t location_mapping_data_size_in_bytes = - RoundUp(location_mapping_data_size_in_bits, kBitsPerByte) / kBitsPerByte; - size_t dex_register_map_size = - location_mapping_data_offset_in_dex_register_map + location_mapping_data_size_in_bytes; - return dex_register_map_size; - } - - MemoryRegion DecodeMemoryRegion(MemoryRegion& region, size_t* bit_offset) { - size_t length = DecodeVarintBits(BitMemoryRegion(region), bit_offset); - size_t offset = BitsToBytesRoundUp(*bit_offset);; - *bit_offset = (offset + length) * kBitsPerByte; - return region.Subregion(offset, length); + ALWAYS_INLINE DexRegisterMap DecodeDexRegisterMap(uint32_t mask_index, + uint32_t map_index, + uint32_t num_dex_registers) const { + DexRegisterMap map(map_index == StackMap::kNoValue ? 0 : num_dex_registers); + if (mask_index != StackMap::kNoValue) { + BitMemoryRegion mask = dex_register_masks_.GetBitMemoryRegion(mask_index); + num_dex_registers = std::min(num_dex_registers, mask.size_in_bits()); + DexRegisterLocation* regs = map.data(); + for (uint32_t r = 0; r < mask.size_in_bits(); r++) { + if (mask.LoadBit(r) /* is_live */) { + DCHECK_LT(r, map.size()); + regs[r] = GetDexRegisterCatalogEntry(dex_register_maps_.Get(map_index++)); + } + } + } + return map; } void Decode(const uint8_t* data) { size_t non_header_size = DecodeUnsignedLeb128(&data); - MemoryRegion region(const_cast(data), non_header_size); - BitMemoryRegion bit_region(region); + BitMemoryRegion region(MemoryRegion(const_cast(data), non_header_size)); size_t bit_offset = 0; size_ = UnsignedLeb128Size(non_header_size) + non_header_size; - dex_register_maps_ = DecodeMemoryRegion(region, &bit_offset); - location_catalog_entries_ = DecodeVarintBits(bit_region, &bit_offset); - location_catalog_ = DecodeMemoryRegion(region, &bit_offset); - stack_maps_.Decode(bit_region, &bit_offset); - invoke_infos_.Decode(bit_region, &bit_offset); - inline_infos_.Decode(bit_region, &bit_offset); - register_masks_.Decode(bit_region, &bit_offset); - stack_masks_.Decode(bit_region, &bit_offset); - CHECK_EQ(BitsToBytesRoundUp(bit_offset), non_header_size); + stack_maps_.Decode(region, &bit_offset); + register_masks_.Decode(region, &bit_offset); + stack_masks_.Decode(region, &bit_offset); + invoke_infos_.Decode(region, &bit_offset); + inline_infos_.Decode(region, &bit_offset); + dex_register_masks_.Decode(region, &bit_offset); + dex_register_maps_.Decode(region, &bit_offset); + dex_register_catalog_.Decode(region, &bit_offset); + CHECK_EQ(non_header_size, BitsToBytesRoundUp(bit_offset)) << "Invalid CodeInfo"; } size_t size_; - MemoryRegion dex_register_maps_; - uint32_t location_catalog_entries_; - MemoryRegion location_catalog_; BitTable stack_maps_; - BitTable invoke_infos_; - BitTable inline_infos_; BitTable register_masks_; BitTable<1> stack_masks_; + BitTable invoke_infos_; + BitTable inline_infos_; + BitTable<1> dex_register_masks_; + BitTable<1> dex_register_maps_; + BitTable dex_register_catalog_; friend class OatDumper; - friend class StackMapStream; }; #undef ELEMENT_BYTE_OFFSET_AFTER -- GitLab From 049d68181e4862271acc027bcb0b4cf5bcc122b6 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Fri, 18 May 2018 14:46:49 +0100 Subject: [PATCH 514/749] Rewrite stackmap verification code. The new version is more complicated but it gives much higher confidence about the correctness of the stackmap encoding. The old version was comparing the internal builder entries to the decoded information, which verified the bit-level manipulations, but it did not verify that we created the internal state correctly. The new version directly compares the parameters passed to the StackMapStream and the decoded values. This way, it really tests the whole system. It uses lambda captures to record the parameters. Test: test-art-host-gtest-stack_map_test Change-Id: Ib92819cc35ce0d790128392d303f6feabd7d9c74 --- compiler/optimizing/stack_map_stream.cc | 187 ++++++++++-------------- compiler/optimizing/stack_map_stream.h | 13 +- 2 files changed, 85 insertions(+), 115 deletions(-) diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index fad0d7be1b..d99beac59f 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -16,6 +16,8 @@ #include "stack_map_stream.h" +#include + #include "art_method-inl.h" #include "base/stl_util.h" #include "dex/dex_file_types.h" @@ -26,6 +28,8 @@ namespace art { +constexpr static bool kVerifyStackMaps = kIsDebugBuild; + uint32_t StackMapStream::GetStackMapNativePcOffset(size_t i) { return StackMap::UnpackNativePc(stack_maps_[i].packed_native_pc, instruction_set_); } @@ -39,7 +43,7 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, uint32_t register_mask, BitVector* stack_mask, uint32_t num_dex_registers, - uint8_t inlining_depth ATTRIBUTE_UNUSED) { + uint8_t inlining_depth) { DCHECK(!in_stack_map_) << "Mismatched Begin/End calls"; in_stack_map_ = true; @@ -65,8 +69,26 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, current_dex_registers_.clear(); expected_num_dex_registers_ = num_dex_registers; - if (kIsDebugBuild) { - dcheck_num_dex_registers_.push_back(num_dex_registers); + if (kVerifyStackMaps) { + size_t stack_map_index = stack_maps_.size(); + // Create lambda method, which will be executed at the very end to verify data. + // Parameters and local variables will be captured(stored) by the lambda "[=]". + dchecks_.emplace_back([=](const CodeInfo& code_info) { + StackMap stack_map = code_info.GetStackMapAt(stack_map_index); + CHECK_EQ(stack_map.GetNativePcOffset(instruction_set_), native_pc_offset); + CHECK_EQ(stack_map.GetDexPc(), dex_pc); + CHECK_EQ(code_info.GetRegisterMaskOf(stack_map), register_mask); + BitMemoryRegion seen_stack_mask = code_info.GetStackMaskOf(stack_map); + CHECK_GE(seen_stack_mask.size_in_bits(), stack_mask ? stack_mask->GetNumberOfBits() : 0); + for (size_t b = 0; b < seen_stack_mask.size_in_bits(); b++) { + CHECK_EQ(seen_stack_mask.LoadBit(b), stack_mask != nullptr && stack_mask->IsBitSet(b)); + } + CHECK_EQ(stack_map.HasInlineInfo(), (inlining_depth != 0)); + if (inlining_depth != 0) { + CHECK_EQ(code_info.GetInlineInfoOf(stack_map).GetDepth(), inlining_depth); + } + CHECK_EQ(stack_map.HasDexRegisterMap(), (num_dex_registers != 0)); + }); } } @@ -94,11 +116,22 @@ void StackMapStream::AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t void StackMapStream::AddInvoke(InvokeType invoke_type, uint32_t dex_method_index) { uint32_t packed_native_pc = current_stack_map_.packed_native_pc; + size_t invoke_info_index = invoke_infos_.size(); invoke_infos_.Add(InvokeInfoEntry { .packed_native_pc = packed_native_pc, .invoke_type = invoke_type, .method_info_index = method_infos_.Dedup(&dex_method_index), }); + + if (kVerifyStackMaps) { + dchecks_.emplace_back([=](const CodeInfo& code_info) { + InvokeInfo invoke_info = code_info.GetInvokeInfo(invoke_info_index); + CHECK_EQ(invoke_info.GetNativePcOffset(instruction_set_), + StackMap::UnpackNativePc(packed_native_pc, instruction_set_)); + CHECK_EQ(invoke_info.GetInvokeType(), invoke_type); + CHECK_EQ(method_infos_[invoke_info.GetMethodIndexIdx()], dex_method_index); + }); + } } void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, @@ -137,8 +170,23 @@ void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, current_dex_registers_.clear(); expected_num_dex_registers_ = num_dex_registers; - if (kIsDebugBuild) { - dcheck_num_dex_registers_.push_back(num_dex_registers); + if (kVerifyStackMaps) { + size_t stack_map_index = stack_maps_.size(); + size_t depth = current_inline_infos_ - 1; + dchecks_.emplace_back([=](const CodeInfo& code_info) { + StackMap stack_map = code_info.GetStackMapAt(stack_map_index); + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); + CHECK_EQ(inline_info.GetDexPcAtDepth(depth), dex_pc); + bool encode_art_method = EncodeArtMethodInInlineInfo(method); + CHECK_EQ(inline_info.EncodesArtMethodAtDepth(depth), encode_art_method); + if (encode_art_method) { + CHECK_EQ(inline_info.GetArtMethodAtDepth(depth), method); + } else { + CHECK_EQ(method_infos_[inline_info.GetMethodIndexIdxAtDepth(depth)], + method->GetDexMethodIndexUnchecked()); + } + CHECK_EQ(inline_info.HasDexRegisterMapAtDepth(depth), (num_dex_registers != 0)); + }); } } @@ -181,6 +229,27 @@ void StackMapStream::CreateDexRegisterMap() { current_stack_map_.dex_register_mask_index = mask_index; current_stack_map_.dex_register_map_index = map_index; } + + if (kVerifyStackMaps) { + size_t stack_map_index = stack_maps_.size(); + int32_t depth = current_inline_infos_ - 1; + // We need to make copy of the current registers for later (when the check is run). + auto expected_dex_registers = std::make_shared>( + current_dex_registers_.begin(), current_dex_registers_.end()); + dchecks_.emplace_back([=](const CodeInfo& code_info) { + StackMap stack_map = code_info.GetStackMapAt(stack_map_index); + size_t num_dex_registers = expected_dex_registers->size(); + DexRegisterMap map = (depth == -1) + ? code_info.GetDexRegisterMapOf(stack_map, num_dex_registers) + : code_info.GetDexRegisterMapAtDepth(depth, + code_info.GetInlineInfoOf(stack_map), + num_dex_registers); + CHECK_EQ(map.size(), num_dex_registers); + for (size_t r = 0; r < num_dex_registers; r++) { + CHECK_EQ(expected_dex_registers->at(r), map.Get(r)); + } + }); + } } void StackMapStream::FillInMethodInfo(MemoryRegion region) { @@ -190,7 +259,7 @@ void StackMapStream::FillInMethodInfo(MemoryRegion region) { info.SetMethodIndex(i, method_infos_[i]); } } - if (kIsDebugBuild) { + if (kVerifyStackMaps) { // Check the data matches. MethodInfo info(region.begin()); const size_t count = info.NumMethodIndices(); @@ -239,108 +308,14 @@ void StackMapStream::FillInCodeInfo(MemoryRegion region) { uint8_t* ptr = EncodeUnsignedLeb128(region.begin(), out_.size()); region.CopyFromVector(ptr - region.begin(), out_); - // Verify all written data in debug build. - if (kIsDebugBuild) { - CheckCodeInfo(region); - } -} - -// Helper for CheckCodeInfo - check that register map has the expected content. -void StackMapStream::CheckDexRegisterMap(const DexRegisterMap& dex_register_map, - size_t dex_register_mask_index, - size_t dex_register_map_index) const { - if (dex_register_map_index == kNoValue) { - DCHECK(!dex_register_map.IsValid()); - return; - } - BitMemoryRegion live_dex_registers_mask = (dex_register_mask_index == kNoValue) - ? BitMemoryRegion() - : BitMemoryRegion(dex_register_masks_[dex_register_mask_index]); - for (size_t reg = 0; reg < dex_register_map.size(); reg++) { - // Find the location we tried to encode. - DexRegisterLocation expected = DexRegisterLocation::None(); - if (reg < live_dex_registers_mask.size_in_bits() && live_dex_registers_mask.LoadBit(reg)) { - size_t catalog_index = dex_register_maps_[dex_register_map_index++]; - DexRegisterLocation::Kind kind = - static_cast(dex_register_catalog_[catalog_index].kind); - uint32_t packed_value = dex_register_catalog_[catalog_index].packed_value; - expected = DexRegisterLocation(kind, DexRegisterInfo::UnpackValue(kind, packed_value)); - } - // Compare to the seen location. - if (expected.GetKind() == DexRegisterLocation::Kind::kNone) { - DCHECK(!dex_register_map.IsValid() || !dex_register_map.IsDexRegisterLive(reg)) - << dex_register_map.IsValid() << " " << dex_register_map.IsDexRegisterLive(reg); - } else { - DCHECK(dex_register_map.IsDexRegisterLive(reg)); - DexRegisterLocation seen = dex_register_map.GetDexRegisterLocation(reg); - DCHECK_EQ(expected.GetKind(), seen.GetKind()); - DCHECK_EQ(expected.GetValue(), seen.GetValue()); - } - } -} - -// Check that all StackMapStream inputs are correctly encoded by trying to read them back. -void StackMapStream::CheckCodeInfo(MemoryRegion region) const { - CodeInfo code_info(region); - DCHECK_EQ(code_info.GetNumberOfStackMaps(), stack_maps_.size()); - const uint32_t* num_dex_registers = dcheck_num_dex_registers_.data(); - for (size_t s = 0; s < stack_maps_.size(); ++s) { - const StackMap stack_map = code_info.GetStackMapAt(s); - const StackMapEntry& entry = stack_maps_[s]; - - // Check main stack map fields. - DCHECK_EQ(stack_map.GetNativePcOffset(instruction_set_), - StackMap::UnpackNativePc(entry.packed_native_pc, instruction_set_)); - DCHECK_EQ(stack_map.GetDexPc(), entry.dex_pc); - DCHECK_EQ(stack_map.GetRegisterMaskIndex(), entry.register_mask_index); - RegisterMaskEntry expected_register_mask = (entry.register_mask_index == kNoValue) - ? RegisterMaskEntry{} - : register_masks_[entry.register_mask_index]; - DCHECK_EQ(code_info.GetRegisterMaskOf(stack_map), - expected_register_mask.value << expected_register_mask.shift); - DCHECK_EQ(stack_map.GetStackMaskIndex(), entry.stack_mask_index); - BitMemoryRegion expected_stack_mask = (entry.stack_mask_index == kNoValue) - ? BitMemoryRegion() - : BitMemoryRegion(stack_masks_[entry.stack_mask_index]); - BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map); - for (size_t b = 0; b < expected_stack_mask.size_in_bits(); b++) { - bool seen = b < stack_mask.size_in_bits() && stack_mask.LoadBit(b); - DCHECK_EQ(expected_stack_mask.LoadBit(b), seen); - } - CheckDexRegisterMap(code_info.GetDexRegisterMapOf(stack_map, *(num_dex_registers++)), - entry.dex_register_mask_index, - entry.dex_register_map_index); - - // Check inline info. - DCHECK_EQ(stack_map.HasInlineInfo(), (entry.inline_info_index != kNoValue)); - if (stack_map.HasInlineInfo()) { - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); - size_t inlining_depth = inline_info.GetDepth(); - for (size_t d = 0; d < inlining_depth; ++d) { - size_t inline_info_index = entry.inline_info_index + d; - DCHECK_LT(inline_info_index, inline_infos_.size()); - const InlineInfoEntry& inline_entry = inline_infos_[inline_info_index]; - DCHECK_EQ(inline_info.GetDexPcAtDepth(d), inline_entry.dex_pc); - if (!inline_info.EncodesArtMethodAtDepth(d)) { - const size_t method_index_idx = - inline_info.GetMethodIndexIdxAtDepth(d); - DCHECK_EQ(method_index_idx, inline_entry.method_info_index); - } - CheckDexRegisterMap(code_info.GetDexRegisterMapAtDepth( - d, inline_info, *(num_dex_registers++)), - inline_entry.dex_register_mask_index, - inline_entry.dex_register_map_index); - } + // Verify all written data (usually only in debug builds). + if (kVerifyStackMaps) { + CodeInfo code_info(region); + CHECK_EQ(code_info.GetNumberOfStackMaps(), stack_maps_.size()); + for (const auto& dcheck : dchecks_) { + dcheck(code_info); } } - for (size_t i = 0; i < invoke_infos_.size(); i++) { - InvokeInfo invoke_info = code_info.GetInvokeInfo(i); - const InvokeInfoEntry& entry = invoke_infos_[i]; - DCHECK_EQ(invoke_info.GetNativePcOffset(instruction_set_), - StackMap::UnpackNativePc(entry.packed_native_pc, instruction_set_)); - DCHECK_EQ(invoke_info.GetInvokeType(), entry.invoke_type); - DCHECK_EQ(invoke_info.GetMethodIndexIdx(), entry.method_info_index); - } } size_t StackMapStream::ComputeMethodInfoSize() const { diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index cefe165a67..c758bca951 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -30,7 +30,7 @@ namespace art { -class DexRegisterMap; +class CodeInfo; /** * Collects and builds stack maps for a method. All the stack maps @@ -138,11 +138,6 @@ class StackMapStream : public ValueObject { void CreateDexRegisterMap(); - void CheckDexRegisterMap(const DexRegisterMap& dex_register_map, - size_t dex_register_mask_index, - size_t dex_register_map_index) const; - void CheckCodeInfo(MemoryRegion region) const; - const InstructionSet instruction_set_; BitTableBuilder stack_maps_; BitTableBuilder register_masks_; @@ -171,9 +166,9 @@ class StackMapStream : public ValueObject { ArenaBitVector temp_dex_register_mask_; ScopedArenaVector temp_dex_register_map_; - // Records num_dex_registers for every StackMapEntry and InlineInfoEntry. - // Only used in debug builds to verify the dex registers at the end. - std::vector dcheck_num_dex_registers_; + // A set of lambda functions to be executed at the end to verify + // the encoded data. It is generally only used in debug builds. + std::vector> dchecks_; DISALLOW_COPY_AND_ASSIGN(StackMapStream); }; -- GitLab From bcf175247272d0e321c8d988c3c01c123b56e36e Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 1 Jun 2018 13:14:32 +0100 Subject: [PATCH 515/749] ObjPtr<>-ify array allocations. And remove some unnecessary calls to ObjPtr<>::Ptr(). Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Bug: 31113334 Change-Id: Ie313980f7f23b33b0ccea4fa8d5131d643c59080 --- compiler/dex/dex_to_dex_decompiler_test.cc | 2 +- dex2oat/linker/oat_writer_test.cc | 3 +- openjdkjvm/OpenjdkJvm.cc | 2 +- openjdkjvmti/ti_redefine.cc | 2 +- runtime/class_linker-inl.h | 15 ++-- runtime/class_linker.cc | 79 ++++++++++--------- runtime/class_linker.h | 27 ++++--- runtime/class_linker_test.cc | 3 +- runtime/class_root.h | 2 +- runtime/debug_print.cc | 2 +- runtime/debugger.cc | 11 ++- runtime/dex/dex_file_annotations.cc | 4 +- runtime/entrypoints/entrypoint_utils-inl.h | 16 ++-- runtime/entrypoints/entrypoint_utils.h | 10 +-- runtime/gc/heap_verification_test.cc | 2 +- runtime/indirect_reference_table-inl.h | 4 +- runtime/interpreter/mterp/mterp.cc | 2 +- runtime/interpreter/unstarted_runtime.cc | 6 +- runtime/interpreter/unstarted_runtime_test.cc | 2 +- runtime/jit/jit_code_cache.cc | 2 +- runtime/jni/jni_internal.cc | 6 +- runtime/method_handles.cc | 4 +- runtime/method_handles_test.cc | 14 ++-- runtime/mirror/array-inl.h | 26 +++--- runtime/mirror/array.cc | 56 ++++++------- runtime/mirror/array.h | 22 +++--- runtime/mirror/class-inl.h | 6 +- runtime/mirror/class.h | 2 +- runtime/mirror/class_ext.cc | 4 +- runtime/mirror/object-inl.h | 2 +- runtime/mirror/object_array-inl.h | 27 ++++--- runtime/mirror/object_array.h | 16 ++-- runtime/mirror/object_test.cc | 33 ++++---- runtime/native/dalvik_system_VMRuntime.cc | 4 +- runtime/native/dalvik_system_ZygoteHooks.cc | 2 +- runtime/native/java_lang_Class.cc | 4 +- runtime/native/java_lang_Thread.cc | 2 +- runtime/native/java_lang_VMClassLoader.cc | 2 +- runtime/native/java_lang_reflect_Array.cc | 11 ++- runtime/native/java_lang_reflect_Field.cc | 2 +- runtime/native/java_lang_reflect_Method.cc | 2 +- runtime/reference_table_test.cc | 19 +++-- runtime/reflection-inl.h | 2 +- runtime/reflection.cc | 2 +- runtime/thread.cc | 4 +- runtime/transaction_test.cc | 2 +- runtime/verifier/method_verifier.cc | 12 ++- runtime/verifier/reg_type.cc | 2 +- runtime/verifier/verifier_deps.cc | 2 +- .../clear_dex_cache.cc | 4 +- 50 files changed, 245 insertions(+), 247 deletions(-) diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc index 75de238211..1fe42ad531 100644 --- a/compiler/dex/dex_to_dex_decompiler_test.cc +++ b/compiler/dex/dex_to_dex_decompiler_test.cc @@ -67,7 +67,7 @@ class DexToDexDecompilerTest : public CommonCompilerTest { class_loader = LoadDex(dex_name); updated_dex_file = GetDexFiles(class_loader)[0]; Runtime::Current()->GetClassLinker()->RegisterDexFile( - *updated_dex_file, soa.Decode(class_loader).Ptr()); + *updated_dex_file, soa.Decode(class_loader)); } // The dex files should be identical. int cmp = memcmp(original_dex_file->Begin(), diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 0694c4ff9f..d0a6eb9ff2 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -545,8 +545,7 @@ TEST_F(OatTest, EmptyTextSection) { ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); for (const DexFile* dex_file : dex_files) { ScopedObjectAccess soa(Thread::Current()); - class_linker->RegisterDexFile(*dex_file, - soa.Decode(class_loader).Ptr()); + class_linker->RegisterDexFile(*dex_file, soa.Decode(class_loader)); } compiler_driver_->SetDexFilesForOatFile(dex_files); compiler_driver_->CompileAll(class_loader, dex_files, &timings); diff --git a/openjdkjvm/OpenjdkJvm.cc b/openjdkjvm/OpenjdkJvm.cc index be1ab7812a..765225ae95 100644 --- a/openjdkjvm/OpenjdkJvm.cc +++ b/openjdkjvm/OpenjdkJvm.cc @@ -401,7 +401,7 @@ JNIEXPORT jboolean JVM_HoldsLock(JNIEnv* env, jclass unused ATTRIBUTE_UNUSED, jo art::ThrowNullPointerException("object == null"); return JNI_FALSE; } - return soa.Self()->HoldsLock(object.Ptr()); + return soa.Self()->HoldsLock(object); } JNIEXPORT void JVM_SetNativeThreadName(JNIEnv* env, jobject jthread, jstring java_name) { diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index 73e37199ed..50d8dfeb70 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -526,7 +526,7 @@ art::mirror::Object* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFile( return art::mirror::ByteArray::AllocateAndFill( driver_->self_, reinterpret_cast(original_dex_file_.data()), - original_dex_file_.size()); + original_dex_file_.size()).Ptr(); } // See if we already have one set. diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index 888f713d8f..664b917543 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -35,20 +35,19 @@ namespace art { inline ObjPtr ClassLinker::FindArrayClass(Thread* self, - ObjPtr* element_class) { + ObjPtr element_class) { for (size_t i = 0; i < kFindArrayCacheSize; ++i) { // Read the cached array class once to avoid races with other threads setting it. ObjPtr array_class = find_array_class_cache_[i].Read(); - if (array_class != nullptr && array_class->GetComponentType() == *element_class) { - return array_class.Ptr(); + if (array_class != nullptr && array_class->GetComponentType() == element_class) { + return array_class; } } std::string descriptor = "["; std::string temp; - descriptor += (*element_class)->GetDescriptor(&temp); - StackHandleScope<2> hs(Thread::Current()); - Handle class_loader(hs.NewHandle((*element_class)->GetClassLoader())); - HandleWrapperObjPtr h_element_class(hs.NewHandleWrapper(element_class)); + descriptor += element_class->GetDescriptor(&temp); + StackHandleScope<1> hs(Thread::Current()); + Handle class_loader(hs.NewHandle(element_class->GetClassLoader())); ObjPtr array_class = FindClass(self, descriptor.c_str(), class_loader); if (array_class != nullptr) { // Benign races in storing array class and incrementing index. @@ -59,7 +58,7 @@ inline ObjPtr ClassLinker::FindArrayClass(Thread* self, // We should have a NoClassDefFoundError. self->AssertPendingException(); } - return array_class.Ptr(); + return array_class; } inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index dccdff0a5d..67987963e1 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -432,8 +432,8 @@ bool ClassLinker::InitWithoutImage(std::vector> b heap->IncrementDisableMovingGC(self); StackHandleScope<64> hs(self); // 64 is picked arbitrarily. auto class_class_size = mirror::Class::ClassClassSize(image_pointer_size_); - Handle java_lang_Class(hs.NewHandle(down_cast( - heap->AllocNonMovableObject(self, nullptr, class_class_size, VoidFunctor())))); + Handle java_lang_Class(hs.NewHandle(ObjPtr::DownCast(MakeObjPtr( + heap->AllocNonMovableObject(self, nullptr, class_class_size, VoidFunctor()))))); CHECK(java_lang_Class != nullptr); java_lang_Class->SetClassFlags(mirror::kClassFlagClass); java_lang_Class->SetClass(java_lang_Class.Get()); @@ -988,8 +988,8 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { } class_roots_ = GcRoot>( - down_cast*>( - spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots))); + ObjPtr>::DownCast(MakeObjPtr( + spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)))); DCHECK_EQ(GetClassRoot(ClassRoot::kJavaLangClass, this)->GetClassFlags(), mirror::kClassFlagClass); @@ -1094,7 +1094,7 @@ static bool FlattenPathClassLoader(ObjPtr class_loader, return false; // Stop the visit. } if (name != nullptr) { - out_dex_file_names->push_front(name.Ptr()); + out_dex_file_names->push_front(name); } return true; // Continue with the next Element. }; @@ -2114,16 +2114,16 @@ void ClassLinker::DeleteClassLoader(Thread* self, const ClassLoaderData& data, b delete data.class_table; } -mirror::PointerArray* ClassLinker::AllocPointerArray(Thread* self, size_t length) { - return down_cast( +ObjPtr ClassLinker::AllocPointerArray(Thread* self, size_t length) { + return ObjPtr::DownCast( image_pointer_size_ == PointerSize::k64 - ? static_cast(mirror::LongArray::Alloc(self, length)) - : static_cast(mirror::IntArray::Alloc(self, length))); + ? ObjPtr(mirror::LongArray::Alloc(self, length)) + : ObjPtr(mirror::IntArray::Alloc(self, length))); } -mirror::DexCache* ClassLinker::AllocDexCache(ObjPtr* out_location, - Thread* self, - const DexFile& dex_file) { +ObjPtr ClassLinker::AllocDexCache(/*out*/ ObjPtr* out_location, + Thread* self, + const DexFile& dex_file) { StackHandleScope<1> hs(self); DCHECK(out_location != nullptr); auto dex_cache(hs.NewHandle(ObjPtr::DownCast( @@ -2141,9 +2141,9 @@ mirror::DexCache* ClassLinker::AllocDexCache(ObjPtr* out_locatio return dex_cache.Get(); } -mirror::DexCache* ClassLinker::AllocAndInitializeDexCache(Thread* self, - const DexFile& dex_file, - LinearAlloc* linear_alloc) { +ObjPtr ClassLinker::AllocAndInitializeDexCache(Thread* self, + const DexFile& dex_file, + LinearAlloc* linear_alloc) { ObjPtr location = nullptr; ObjPtr dex_cache = AllocDexCache(&location, self, dex_file); if (dex_cache != nullptr) { @@ -2156,7 +2156,7 @@ mirror::DexCache* ClassLinker::AllocAndInitializeDexCache(Thread* self, linear_alloc, image_pointer_size_); } - return dex_cache.Ptr(); + return dex_cache; } ObjPtr ClassLinker::AllocClass(Thread* self, @@ -2179,7 +2179,7 @@ ObjPtr ClassLinker::AllocClass(Thread* self, uint32_t class_size) return AllocClass(self, GetClassRoot(this), class_size); } -mirror::ObjectArray* ClassLinker::AllocStackTraceElementArray( +ObjPtr> ClassLinker::AllocStackTraceElementArray( Thread* self, size_t length) { return mirror::ObjectArray::Alloc( @@ -2260,7 +2260,7 @@ ObjPtr ClassLinker::EnsureResolved(Thread* self, // Return the loaded class. No exceptions should be pending. CHECK(klass->IsResolved()) << klass->PrettyClass(); self->AssertNoPendingException(); - return klass.Ptr(); + return klass; } typedef std::pair ClassPathEntry; @@ -2282,7 +2282,7 @@ bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnabl const char* descriptor, size_t hash, Handle class_loader, - ObjPtr* result) { + /*out*/ ObjPtr* result) { // Termination case: boot class loader. if (IsBootClassLoader(soa, class_loader.Get())) { *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash); @@ -2535,7 +2535,7 @@ ObjPtr ClassLinker::FindClass(Thread* self, if (old == nullptr) { old = result_ptr; // For the comparison below, after releasing the lock. if (descriptor_equals) { - class_table->InsertWithHash(result_ptr.Ptr(), hash); + class_table->InsertWithHash(result_ptr, hash); Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get()); } // else throw below, after releasing the lock. } @@ -2563,8 +2563,8 @@ ObjPtr ClassLinker::FindClass(Thread* self, DescriptorToDot(descriptor).c_str()); return nullptr; } - // success, return mirror::Class* - return result_ptr.Ptr(); + // Success. + return result_ptr; } ObjPtr ClassLinker::DefineClass(Thread* self, @@ -3612,7 +3612,7 @@ ObjPtr ClassLinker::CreateArrayClass(Thread* self, ObjPtr new_class = LookupClass(self, descriptor, hash, component_type->GetClassLoader()); if (new_class != nullptr) { - return new_class.Ptr(); + return new_class; } } @@ -3713,7 +3713,7 @@ ObjPtr ClassLinker::CreateArrayClass(Thread* self, // // (Yes, this happens.) - return existing.Ptr(); + return existing; } ObjPtr ClassLinker::FindPrimitiveClass(char type) { @@ -3763,7 +3763,7 @@ ObjPtr ClassLinker::InsertClass(const char* descriptor, ClassTable* const class_table = InsertClassTableForClassLoader(class_loader); ObjPtr existing = class_table->Lookup(descriptor, hash); if (existing != nullptr) { - return existing.Ptr(); + return existing; } VerifyObject(klass); class_table->InsertWithHash(klass, hash); @@ -3817,7 +3817,7 @@ ObjPtr ClassLinker::LookupClass(Thread* self, if (class_table != nullptr) { ObjPtr result = class_table->Lookup(descriptor, hash); if (result != nullptr) { - return result.Ptr(); + return result; } } return nullptr; @@ -5723,8 +5723,8 @@ bool ClassLinker::LinkVirtualMethods( klass->SetVTable(super_vtable); return true; } - vtable = hs.NewHandle(down_cast( - super_vtable->CopyOf(self, max_count))); + vtable = hs.NewHandle( + ObjPtr::DownCast(super_vtable->CopyOf(self, max_count))); if (UNLIKELY(vtable == nullptr)) { self->AssertPendingOOMException(); return false; @@ -5860,7 +5860,7 @@ bool ClassLinker::LinkVirtualMethods( // Shrink vtable if possible CHECK_LE(actual_count, max_count); if (actual_count < max_count) { - vtable.Assign(down_cast(vtable->CopyOf(self, actual_count))); + vtable.Assign(ObjPtr::DownCast(vtable->CopyOf(self, actual_count))); if (UNLIKELY(vtable == nullptr)) { self->AssertPendingOOMException(); return false; @@ -5874,7 +5874,7 @@ bool ClassLinker::LinkVirtualMethods( static_cast(num_virtual_methods)); return false; } - auto* vtable = AllocPointerArray(self, num_virtual_methods); + ObjPtr vtable = AllocPointerArray(self, num_virtual_methods); if (UNLIKELY(vtable == nullptr)) { self->AssertPendingOOMException(); return false; @@ -6118,7 +6118,8 @@ bool ClassLinker::AllocateIfTableMethodArrays(Thread* self, DCHECK(if_table != nullptr); DCHECK(if_table->GetMethodArray(i) != nullptr); // If we are working on a super interface, try extending the existing method array. - method_array = down_cast(if_table->GetMethodArray(i)->Clone(self)); + method_array = ObjPtr::DownCast(MakeObjPtr( + if_table->GetMethodArray(i)->Clone(self))); } else { method_array = AllocPointerArray(self, num_methods); } @@ -6382,7 +6383,7 @@ static bool NotSubinterfaceOfAny( // iftable must be large enough to hold all interfaces without changing its size. static size_t FillIfTable(ObjPtr iftable, size_t super_ifcount, - std::vector to_process) + std::vector> to_process) REQUIRES(Roles::uninterruptible_) REQUIRES_SHARED(Locks::mutator_lock_) { // This is the set of all class's already in the iftable. Used to make checking if a class has @@ -6522,11 +6523,11 @@ bool ClassLinker::SetupInterfaceLookupTable(Thread* self, Handle size_t new_ifcount; { ScopedAssertNoThreadSuspension nts("Copying mirror::Class*'s for FillIfTable"); - std::vector to_add; + std::vector> to_add; for (size_t i = 0; i < num_interfaces; i++) { ObjPtr interface = have_interfaces ? interfaces->Get(i) : mirror::Class::GetDirectInterface(self, klass.Get(), i); - to_add.push_back(interface.Ptr()); + to_add.push_back(interface); } new_ifcount = FillIfTable(iftable.Get(), super_ifcount, std::move(to_add)); @@ -6537,7 +6538,7 @@ bool ClassLinker::SetupInterfaceLookupTable(Thread* self, Handle // Shrink iftable in case duplicates were found if (new_ifcount < ifcount) { DCHECK_NE(num_interfaces, 0U); - iftable.Assign(down_cast( + iftable.Assign(ObjPtr::DownCast( iftable->CopyOf(self, new_ifcount * mirror::IfTable::kMax))); if (UNLIKELY(iftable == nullptr)) { self->AssertPendingOOMException(); @@ -7051,7 +7052,7 @@ ObjPtr ClassLinker::LinkInterfaceMethodsHelper::UpdateVtab default_conflict_methods_.size(); ObjPtr vtable = - down_cast(old_vtable->CopyOf(self_, new_vtable_count)); + ObjPtr::DownCast(old_vtable->CopyOf(self_, new_vtable_count)); if (UNLIKELY(vtable == nullptr)) { self_->AssertPendingOOMException(); return nullptr; @@ -7721,7 +7722,7 @@ ObjPtr ClassLinker::DoLookupResolvedType(dex::TypeIndex type_idx, DCHECK(self != nullptr); const size_t hash = ComputeModifiedUtf8Hash(descriptor); // Find the class in the loaded classes table. - type = LookupClass(self, descriptor, hash, class_loader.Ptr()); + type = LookupClass(self, descriptor, hash, class_loader); } if (type != nullptr) { if (type->IsResolved()) { @@ -8104,7 +8105,7 @@ ObjPtr ClassLinker::ResolveMethodType( ObjPtr resolved = dex_cache->GetResolvedMethodType(proto_idx); if (resolved != nullptr) { - return resolved.Ptr(); + return resolved; } StackHandleScope<4> hs(self); @@ -8760,7 +8761,7 @@ void ClassLinker::InsertDexFileInToClassLoader(ObjPtr dex_file, DCHECK(dex_file != nullptr); Thread* const self = Thread::Current(); WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); - ClassTable* const table = ClassTableForClassLoader(class_loader.Ptr()); + ClassTable* const table = ClassTableForClassLoader(class_loader); DCHECK(table != nullptr); if (table->InsertStrongRoot(dex_file) && class_loader != nullptr) { // It was not already inserted, perform the write barrier to let the GC know the class loader's diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 32016fa12a..58ce6eb25c 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -163,7 +163,7 @@ class ClassLinker { } // Finds the array class given for the element class. - ObjPtr FindArrayClass(Thread* self, ObjPtr* element_class) + ObjPtr FindArrayClass(Thread* self, ObjPtr element_class) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); @@ -451,7 +451,7 @@ class ClassLinker { LinearAlloc* allocator, size_t length); - mirror::PointerArray* AllocPointerArray(Thread* self, size_t length) + ObjPtr AllocPointerArray(Thread* self, size_t length) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); @@ -459,8 +459,8 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - mirror::ObjectArray* AllocStackTraceElementArray(Thread* self, - size_t length) + ObjPtr> AllocStackTraceElementArray(Thread* self, + size_t length) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); @@ -543,8 +543,9 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_); template - mirror::ObjectArray* GetClassRoots() REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::ObjectArray* class_roots = class_roots_.Read(); + ObjPtr> GetClassRoots() REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr> class_roots = + class_roots_.Read(); DCHECK(class_roots != nullptr); return class_roots; } @@ -777,16 +778,16 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - mirror::DexCache* AllocDexCache(ObjPtr* out_location, - Thread* self, - const DexFile& dex_file) + ObjPtr AllocDexCache(/*out*/ ObjPtr* out_location, + Thread* self, + const DexFile& dex_file) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); // Used for tests and AppendToBootClassPath. - mirror::DexCache* AllocAndInitializeDexCache(Thread* self, - const DexFile& dex_file, - LinearAlloc* linear_alloc) + ObjPtr AllocAndInitializeDexCache(Thread* self, + const DexFile& dex_file, + LinearAlloc* linear_alloc) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_) REQUIRES(!Roles::uninterruptible_); @@ -850,7 +851,7 @@ class ClassLinker { const char* descriptor, size_t hash, Handle class_loader, - ObjPtr* result) + /*out*/ ObjPtr* result) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 5d420aae04..e40f1dbcdf 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -233,8 +233,7 @@ class ClassLinkerTest : public CommonRuntimeTest { ObjPtr direct_interface1 = mirror::Class::GetDirectInterface(self, array.Get(), 1); EXPECT_STREQ(direct_interface1->GetDescriptor(&temp), "Ljava/io/Serializable;"); - ObjPtr array_ptr = array->GetComponentType(); - EXPECT_OBJ_PTR_EQ(class_linker_->FindArrayClass(self, &array_ptr), array.Get()); + EXPECT_OBJ_PTR_EQ(class_linker_->FindArrayClass(self, array->GetComponentType()), array.Get()); PointerSize pointer_size = class_linker_->GetImagePointerSize(); ObjPtr JavaLangObject = diff --git a/runtime/class_root.h b/runtime/class_root.h index 5c7819841b..4aa9801ab4 100644 --- a/runtime/class_root.h +++ b/runtime/class_root.h @@ -127,7 +127,7 @@ inline ObjPtr GetClassRoot( ObjPtr klass = class_roots->GetWithoutChecks(index); DCHECK(klass != nullptr); - return klass.Ptr(); + return klass; } template diff --git a/runtime/debug_print.cc b/runtime/debug_print.cc index c5bb4d57e6..cb334b569f 100644 --- a/runtime/debug_print.cc +++ b/runtime/debug_print.cc @@ -37,7 +37,7 @@ std::string DescribeSpace(ObjPtr klass) { std::ostringstream oss; gc::Heap* heap = Runtime::Current()->GetHeap(); gc::space::ContinuousSpace* cs = - heap->FindContinuousSpaceFromObject(klass.Ptr(), /* fail_ok */ true); + heap->FindContinuousSpaceFromObject(klass, /* fail_ok */ true); if (cs != nullptr) { if (cs->IsImageSpace()) { gc::space::ImageSpace* ispace = cs->AsImageSpace(); diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 88628bbc50..f75f47c075 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -1388,7 +1388,7 @@ JDWP::JdwpError Dbg::CreateObject(JDWP::RefTypeId class_id, JDWP::ObjectId* new_ *new_object_id = 0; return JDWP::ERR_OUT_OF_MEMORY; } - *new_object_id = gRegistry->Add(new_object.Ptr()); + *new_object_id = gRegistry->Add(new_object); return JDWP::ERR_NONE; } @@ -1404,10 +1404,9 @@ JDWP::JdwpError Dbg::CreateArrayObject(JDWP::RefTypeId array_class_id, uint32_t return error; } Thread* self = Thread::Current(); - gc::Heap* heap = Runtime::Current()->GetHeap(); - mirror::Array* new_array = mirror::Array::Alloc(self, c, length, - c->GetComponentSizeShift(), - heap->GetCurrentAllocator()); + gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); + ObjPtr new_array = + mirror::Array::Alloc(self, c, length, c->GetComponentSizeShift(), allocator_type); if (new_array == nullptr) { DCHECK(self->IsExceptionPending()); self->ClearException(); @@ -1849,7 +1848,7 @@ static JValue GetArtFieldValue(ArtField* f, mirror::Object* o) return field_value; case Primitive::kPrimNot: - field_value.SetL(f->GetObject(o).Ptr()); + field_value.SetL(f->GetObject(o)); return field_value; case Primitive::kPrimVoid: diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc index 5cb08dc278..9358cbe5a9 100644 --- a/runtime/dex/dex_file_annotations.cc +++ b/runtime/dex/dex_file_annotations.cc @@ -359,7 +359,7 @@ ObjPtr ProcessEncodedAnnotation(const ClassData& klass, const ui ObjPtr annotation_member_class = soa.Decode(WellKnownClasses::libcore_reflect_AnnotationMember); ObjPtr annotation_member_array_class = - class_linker->FindArrayClass(self, &annotation_member_class); + class_linker->FindArrayClass(self, annotation_member_class); if (annotation_member_array_class == nullptr) { return nullptr; } @@ -967,7 +967,7 @@ ObjPtr> ProcessAnnotationSetRefList( ObjPtr annotation_array_class = soa.Decode(WellKnownClasses::java_lang_annotation_Annotation__array); ObjPtr annotation_array_array_class = - Runtime::Current()->GetClassLinker()->FindArrayClass(self, &annotation_array_class); + Runtime::Current()->GetClassLinker()->FindArrayClass(self, annotation_array_class); if (annotation_array_array_class == nullptr) { return nullptr; } diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index f6b1c73230..c533f9ca66 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -268,14 +268,14 @@ inline mirror::Class* CheckArrayAlloc(dex::TypeIndex type_idx, // check. template ALWAYS_INLINE -inline mirror::Array* AllocArrayFromCode(dex::TypeIndex type_idx, - int32_t component_count, - ArtMethod* method, - Thread* self, - gc::AllocatorType allocator_type) { +inline ObjPtr AllocArrayFromCode(dex::TypeIndex type_idx, + int32_t component_count, + ArtMethod* method, + Thread* self, + gc::AllocatorType allocator_type) { bool slow_path = false; - mirror::Class* klass = CheckArrayAlloc(type_idx, component_count, method, - &slow_path); + ObjPtr klass = + CheckArrayAlloc(type_idx, component_count, method, &slow_path); if (UNLIKELY(slow_path)) { if (klass == nullptr) { return nullptr; @@ -306,7 +306,7 @@ inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass, // No need to retry a slow-path allocation as the above code won't cause a GC or thread // suspension. return mirror::Array::Alloc(self, klass, component_count, - klass->GetComponentSizeShift(), allocator_type); + klass->GetComponentSizeShift(), allocator_type).Ptr(); } template diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index e33de9c45a..1f4475f6ff 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -87,11 +87,11 @@ ALWAYS_INLINE inline mirror::Class* CheckArrayAlloc(dex::TypeIndex type_idx, // When verification/compiler hasn't been able to verify access, optionally perform an access // check. template -ALWAYS_INLINE inline mirror::Array* AllocArrayFromCode(dex::TypeIndex type_idx, - int32_t component_count, - ArtMethod* method, - Thread* self, - gc::AllocatorType allocator_type) +ALWAYS_INLINE inline ObjPtr AllocArrayFromCode(dex::TypeIndex type_idx, + int32_t component_count, + ArtMethod* method, + Thread* self, + gc::AllocatorType allocator_type) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); diff --git a/runtime/gc/heap_verification_test.cc b/runtime/gc/heap_verification_test.cc index 4f06ee6910..38695332bb 100644 --- a/runtime/gc/heap_verification_test.cc +++ b/runtime/gc/heap_verification_test.cc @@ -35,7 +35,7 @@ class VerificationTest : public CommonRuntimeTest { VerificationTest() {} template - mirror::ObjectArray* AllocObjectArray(Thread* self, size_t length) + ObjPtr> AllocObjectArray(Thread* self, size_t length) REQUIRES_SHARED(Locks::mutator_lock_) { return mirror::ObjectArray::Alloc( self, diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h index 9673bd9728..2128f8cde8 100644 --- a/runtime/indirect_reference_table-inl.h +++ b/runtime/indirect_reference_table-inl.h @@ -111,12 +111,12 @@ inline void IrtEntry::Add(ObjPtr obj) { if (serial_ == kIRTPrevCount) { serial_ = 0; } - references_[serial_] = GcRoot(obj.Ptr()); + references_[serial_] = GcRoot(obj); } inline void IrtEntry::SetReference(ObjPtr obj) { DCHECK_LT(serial_, kIRTPrevCount); - references_[serial_] = GcRoot(obj.Ptr()); + references_[serial_] = GcRoot(obj); } } // namespace art diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index d62f511ad5..e4cc6d3f9f 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -559,7 +559,7 @@ extern "C" size_t MterpNewArray(ShadowFrame* shadow_frame, REQUIRES_SHARED(Locks::mutator_lock_) { const Instruction* inst = Instruction::At(dex_pc_ptr); int32_t length = shadow_frame->GetVReg(inst->VRegB_22c(inst_data)); - mirror::Object* obj = AllocArrayFromCode( + ObjPtr obj = AllocArrayFromCode( dex::TypeIndex(inst->VRegC_22c()), length, shadow_frame->GetMethod(), self, Runtime::Current()->GetHeap()->GetCurrentAllocator()); if (UNLIKELY(obj == nullptr)) { diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 7abb007838..667bd03c18 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -1691,7 +1691,7 @@ void UnstartedRuntime::UnstartedJNIVMRuntimeNewUnpaddedArray( ObjPtr element_class = reinterpret_cast(args[0])->AsClass(); Runtime* runtime = Runtime::Current(); ObjPtr array_class = - runtime->GetClassLinker()->FindArrayClass(self, &element_class); + runtime->GetClassLinker()->FindArrayClass(self, element_class); DCHECK(array_class != nullptr); gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentAllocator(); result->SetL(mirror::Array::Alloc(self, @@ -1818,13 +1818,13 @@ void UnstartedRuntime::UnstartedJNIArrayCreateObjectArray( ObjPtr element_class = reinterpret_cast(args[0])->AsClass(); Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); - ObjPtr array_class = class_linker->FindArrayClass(self, &element_class); + ObjPtr array_class = class_linker->FindArrayClass(self, element_class); if (UNLIKELY(array_class == nullptr)) { CHECK(self->IsExceptionPending()); return; } DCHECK(array_class->IsObjectArrayClass()); - mirror::Array* new_array = mirror::ObjectArray::Alloc( + ObjPtr new_array = mirror::ObjectArray::Alloc( self, array_class, length, runtime->GetHeap()->GetCurrentAllocator()); result->SetL(new_array); } diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc index 449458ce6f..655713e8c6 100644 --- a/runtime/interpreter/unstarted_runtime_test.cc +++ b/runtime/interpreter/unstarted_runtime_test.cc @@ -91,7 +91,7 @@ class UnstartedRuntimeTest : public CommonRuntimeTest { REQUIRES_SHARED(Locks::mutator_lock_) { Runtime* runtime = Runtime::Current(); ObjPtr array_type = - runtime->GetClassLinker()->FindArrayClass(self, &component_type); + runtime->GetClassLinker()->FindArrayClass(self, component_type); CHECK(array_type != nullptr); ObjPtr> result = mirror::ObjectArray::Alloc(self, array_type, 3); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index d8aa00c45e..b010650345 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -471,7 +471,7 @@ static void FillRootTable(uint8_t* roots_data, HandleIsString()) { - ObjPtr str = reinterpret_cast(object.Ptr()); + ObjPtr str = ObjPtr::DownCast(object); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); CHECK(class_linker->GetInternTable()->LookupStrong(Thread::Current(), str) != nullptr); } diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc index 987c8e974f..7290d638f3 100644 --- a/runtime/jni/jni_internal.cc +++ b/runtime/jni/jni_internal.cc @@ -2036,14 +2036,14 @@ class JNI { return nullptr; } ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - array_class = class_linker->FindArrayClass(soa.Self(), &element_class); + array_class = class_linker->FindArrayClass(soa.Self(), element_class); if (UNLIKELY(array_class == nullptr)) { return nullptr; } } // Allocate and initialize if necessary. - mirror::ObjectArray* result = + ObjPtr> result = mirror::ObjectArray::Alloc(soa.Self(), array_class, length); if (result != nullptr && initial_element != nullptr) { ObjPtr initial_object = soa.Decode(initial_element); @@ -2548,7 +2548,7 @@ class JNI { soa.Vm()->JniAbortF("NewPrimitiveArray", "negative array length: %d", length); return nullptr; } - ArtT* result = ArtT::Alloc(soa.Self(), length); + ObjPtr result = ArtT::Alloc(soa.Self(), length); return soa.AddLocalReference(result); } diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 28df1749a4..01a32a2288 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -268,7 +268,7 @@ bool ConvertJValueCommon( // Then perform the actual boxing, and then set the reference. ObjPtr boxed = BoxPrimitive(type, src_value); - value->SetL(boxed.Ptr()); + value->SetL(boxed); return true; } else { // The source type is a reference and the target type is a primitive, so we must unbox. @@ -323,7 +323,7 @@ inline void CopyArgumentsFromCallerFrame(const ShadowFrame& caller_frame, // Note: As an optimization, non-moving collectors leave a stale reference value // in the references array even after the original vreg was overwritten to a non-reference. if (src_value == reinterpret_cast(o.Ptr())) { - callee_frame->SetVRegReference(dst_reg, o.Ptr()); + callee_frame->SetVRegReference(dst_reg, o); } else { callee_frame->SetVReg(dst_reg, src_value); } diff --git a/runtime/method_handles_test.cc b/runtime/method_handles_test.cc index 0db9551265..d123754e47 100644 --- a/runtime/method_handles_test.cc +++ b/runtime/method_handles_test.cc @@ -179,7 +179,7 @@ TEST_F(MethodHandlesTest, SupportedReferenceCast) { StackHandleScope<3> hs(soa.Self()); static const int32_t kInitialValue = 101; JValue value = JValue::FromPrimitive(kInitialValue); - Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr()); + Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value)); Handle from = hs.NewHandle(boxed_value->GetClass()); Handle to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Number;")); value.SetL(boxed_value.Get()); @@ -195,8 +195,7 @@ TEST_F(MethodHandlesTest, UnsupportedReferenceCast) { ClassLinker* cl = Runtime::Current()->GetClassLinker(); StackHandleScope<3> hs(soa.Self()); JValue value = JValue::FromPrimitive(3.733e2); - Handle boxed_value = - hs.NewHandle(BoxPrimitive(Primitive::kPrimDouble, value).Ptr()); + Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimDouble, value)); Handle from = hs.NewHandle(boxed_value->GetClass()); Handle to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); value.SetL(boxed_value.Get()); @@ -293,7 +292,7 @@ TEST_F(MethodHandlesTest, SupportedBoxedToPrimitiveConversion) { StackHandleScope<3> hs(soa.Self()); const int32_t kInitialValue = 101; JValue value = JValue::FromPrimitive(kInitialValue); - Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr()); + Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value)); Handle from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); Handle to = hs.NewHandle(cl->FindPrimitiveClass('I')); value.SetL(boxed_value.Get()); @@ -308,7 +307,7 @@ TEST_F(MethodHandlesTest, SupportedBoxedToWiderPrimitiveConversion) { StackHandleScope<3> hs(soa.Self()); static const int32_t kInitialValue = 101; JValue value = JValue::FromPrimitive(kInitialValue); - Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr()); + Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value)); Handle from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); Handle to = hs.NewHandle(cl->FindPrimitiveClass('J')); value.SetL(boxed_value.Get()); @@ -352,7 +351,7 @@ TEST_F(MethodHandlesTest, UnsupportedBoxedToNarrowerPrimitiveConversionNoCast) { StackHandleScope<3> hs(soa.Self()); static const int32_t kInitialValue = 101; JValue value = JValue::FromPrimitive(kInitialValue); - Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr()); + Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value)); Handle from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;")); Handle to = hs.NewHandle(cl->FindPrimitiveClass('S')); value.SetL(boxed_value.Get()); @@ -368,8 +367,7 @@ TEST_F(MethodHandlesTest, UnsupportedBoxedToNarrowerPrimitiveConversionWithCast) StackHandleScope<3> hs(soa.Self()); static const double kInitialValue = 1e77; JValue value = JValue::FromPrimitive(kInitialValue); - Handle boxed_value = - hs.NewHandle(BoxPrimitive(Primitive::kPrimDouble, value).Ptr()); + Handle boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimDouble, value)); Handle from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Number;")); Handle to = hs.NewHandle(cl->FindPrimitiveClass('F')); value.SetL(boxed_value.Get()); diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index d2adcb4766..2e395302af 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -151,11 +151,11 @@ class SetLengthToUsableSizeVisitor { }; template -inline Array* Array::Alloc(Thread* self, - ObjPtr array_class, - int32_t component_count, - size_t component_size_shift, - gc::AllocatorType allocator_type) { +inline ObjPtr Array::Alloc(Thread* self, + ObjPtr array_class, + int32_t component_count, + size_t component_size_shift, + gc::AllocatorType allocator_type) { DCHECK(allocator_type != gc::kAllocatorTypeLOS); DCHECK(array_class != nullptr); DCHECK(array_class->IsArrayClass()); @@ -175,19 +175,19 @@ inline Array* Array::Alloc(Thread* self, } #endif gc::Heap* heap = Runtime::Current()->GetHeap(); - Array* result; + ObjPtr result; if (!kFillUsable) { SetLengthVisitor visitor(component_count); - result = down_cast( + result = ObjPtr::DownCast(MakeObjPtr( heap->AllocObjectWithAllocator(self, array_class, size, - allocator_type, visitor)); + allocator_type, visitor))); } else { SetLengthToUsableSizeVisitor visitor(component_count, DataOffset(1U << component_size_shift).SizeValue(), component_size_shift); - result = down_cast( + result = ObjPtr::DownCast(MakeObjPtr( heap->AllocObjectWithAllocator(self, array_class, size, - allocator_type, visitor)); + allocator_type, visitor))); } if (kIsDebugBuild && result != nullptr && Runtime::Current()->IsStarted()) { array_class = result->GetClass(); // In case the array class moved. @@ -202,9 +202,9 @@ inline Array* Array::Alloc(Thread* self, } template -inline PrimitiveArray* PrimitiveArray::AllocateAndFill(Thread* self, - const T* data, - size_t length) { +inline ObjPtr> PrimitiveArray::AllocateAndFill(Thread* self, + const T* data, + size_t length) { StackHandleScope<1> hs(self); Handle> arr(hs.NewHandle(PrimitiveArray::Alloc(self, length))); if (!arr.IsNull()) { diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc index 06ce0bb5dd..66ec368935 100644 --- a/runtime/mirror/array.cc +++ b/runtime/mirror/array.cc @@ -42,17 +42,18 @@ using android::base::StringPrintf; // piece and work our way in. // Recursively create an array with multiple dimensions. Elements may be // Objects or primitive types. -static Array* RecursiveCreateMultiArray(Thread* self, - Handle array_class, int current_dimension, - Handle dimensions) +static ObjPtr RecursiveCreateMultiArray(Thread* self, + Handle array_class, + int current_dimension, + Handle dimensions) REQUIRES_SHARED(Locks::mutator_lock_) { int32_t array_length = dimensions->Get(current_dimension); - StackHandleScope<1> hs(self); - Handle new_array( - hs.NewHandle( - Array::Alloc(self, array_class.Get(), array_length, - array_class->GetComponentSizeShift(), - Runtime::Current()->GetHeap()->GetCurrentAllocator()))); + StackHandleScope<2> hs(self); + Handle h_component_type(hs.NewHandle(array_class->GetComponentType())); + size_t component_size_shift = h_component_type->GetPrimitiveTypeSizeShift(); + gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); + Handle new_array(hs.NewHandle(Array::Alloc( + self, array_class.Get(), array_length, component_size_shift, allocator_type))); if (UNLIKELY(new_array == nullptr)) { CHECK(self->IsExceptionPending()); return nullptr; @@ -60,10 +61,8 @@ static Array* RecursiveCreateMultiArray(Thread* self, if (current_dimension + 1 < dimensions->GetLength()) { // Create a new sub-array in every element of the array. for (int32_t i = 0; i < array_length; i++) { - StackHandleScope<1> hs2(self); - Handle h_component_type(hs2.NewHandle(array_class->GetComponentType())); - ObjPtr sub_array = RecursiveCreateMultiArray(self, h_component_type, - current_dimension + 1, dimensions); + ObjPtr sub_array = + RecursiveCreateMultiArray(self, h_component_type, current_dimension + 1, dimensions); if (UNLIKELY(sub_array == nullptr)) { CHECK(self->IsExceptionPending()); return nullptr; @@ -75,8 +74,9 @@ static Array* RecursiveCreateMultiArray(Thread* self, return new_array.Get(); } -Array* Array::CreateMultiArray(Thread* self, Handle element_class, - Handle dimensions) { +ObjPtr Array::CreateMultiArray(Thread* self, + Handle element_class, + Handle dimensions) { // Verify dimensions. // // The caller is responsible for verifying that "dimArray" is non-null @@ -95,17 +95,15 @@ Array* Array::CreateMultiArray(Thread* self, Handle element_class, // Find/generate the array class. ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ObjPtr element_class_ptr = element_class.Get(); StackHandleScope<1> hs(self); MutableHandle array_class( - hs.NewHandle(class_linker->FindArrayClass(self, &element_class_ptr))); + hs.NewHandle(class_linker->FindArrayClass(self, element_class.Get()))); if (UNLIKELY(array_class == nullptr)) { CHECK(self->IsExceptionPending()); return nullptr; } for (int32_t i = 1; i < dimensions->GetLength(); ++i) { - ObjPtr array_class_ptr = array_class.Get(); - array_class.Assign(class_linker->FindArrayClass(self, &array_class_ptr)); + array_class.Assign(class_linker->FindArrayClass(self, array_class.Get())); if (UNLIKELY(array_class == nullptr)) { CHECK(self->IsExceptionPending()); return nullptr; @@ -120,13 +118,14 @@ Array* Array::CreateMultiArray(Thread* self, Handle element_class, } template -PrimitiveArray* PrimitiveArray::Alloc(Thread* self, size_t length) { - Array* raw_array = Array::Alloc(self, - GetClassRoot>(), - length, - ComponentSizeShiftWidth(sizeof(T)), - Runtime::Current()->GetHeap()->GetCurrentAllocator()); - return down_cast*>(raw_array); +ObjPtr> PrimitiveArray::Alloc(Thread* self, size_t length) { + gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); + ObjPtr raw_array = Array::Alloc(self, + GetClassRoot>(), + length, + ComponentSizeShiftWidth(sizeof(T)), + allocator_type); + return ObjPtr>::DownCast(raw_array); } void Array::ThrowArrayIndexOutOfBoundsException(int32_t index) { @@ -137,7 +136,7 @@ void Array::ThrowArrayStoreException(ObjPtr object) { art::ThrowArrayStoreException(object->GetClass(), this->GetClass()); } -Array* Array::CopyOf(Thread* self, int32_t new_length) { +ObjPtr Array::CopyOf(Thread* self, int32_t new_length) { CHECK(GetClass()->GetComponentType()->IsPrimitive()) << "Will miss write barriers"; DCHECK_GE(new_length, 0); // We may get copied by a compacting GC. @@ -148,7 +147,8 @@ Array* Array::CopyOf(Thread* self, int32_t new_length) { heap->GetCurrentNonMovingAllocator(); const auto component_size = GetClass()->GetComponentSize(); const auto component_shift = GetClass()->GetComponentSizeShift(); - ObjPtr new_array = Alloc(self, GetClass(), new_length, component_shift, allocator_type); + ObjPtr new_array = + Alloc(self, GetClass(), new_length, component_shift, allocator_type); if (LIKELY(new_array != nullptr)) { memcpy(new_array->GetRawData(component_size, 0), h_this->GetRawData(component_size, 0), diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h index aeaaf67310..8bdd561ec4 100644 --- a/runtime/mirror/array.h +++ b/runtime/mirror/array.h @@ -37,17 +37,17 @@ class MANAGED Array : public Object { // least component_count size, however, if there's usable space at the end of the allocation the // array will fill it. template - ALWAYS_INLINE static Array* Alloc(Thread* self, - ObjPtr array_class, - int32_t component_count, - size_t component_size_shift, - gc::AllocatorType allocator_type) + ALWAYS_INLINE static ObjPtr Alloc(Thread* self, + ObjPtr array_class, + int32_t component_count, + size_t component_size_shift, + gc::AllocatorType allocator_type) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - static Array* CreateMultiArray(Thread* self, - Handle element_class, - Handle dimensions) + static ObjPtr CreateMultiArray(Thread* self, + Handle element_class, + Handle dimensions) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); @@ -90,7 +90,7 @@ class MANAGED Array : public Object { template ALWAYS_INLINE bool CheckIsValidIndex(int32_t index) REQUIRES_SHARED(Locks::mutator_lock_); - Array* CopyOf(Thread* self, int32_t new_length) REQUIRES_SHARED(Locks::mutator_lock_) + ObjPtr CopyOf(Thread* self, int32_t new_length) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); protected: @@ -114,10 +114,10 @@ class MANAGED PrimitiveArray : public Array { public: typedef T ElementType; - static PrimitiveArray* Alloc(Thread* self, size_t length) + static ObjPtr> Alloc(Thread* self, size_t length) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - static PrimitiveArray* AllocateAndFill(Thread* self, const T* data, size_t length) + static ObjPtr> AllocateAndFill(Thread* self, const T* data, size_t length) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index ab50973e89..8bc7a812ad 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -294,7 +294,7 @@ inline PointerArray* Class::GetVTableDuringLinking() { return GetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, vtable_)); } -inline void Class::SetVTable(PointerArray* new_vtable) { +inline void Class::SetVTable(ObjPtr new_vtable) { SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, vtable_), new_vtable); } @@ -487,7 +487,7 @@ inline bool Class::ResolvedMethodAccessTest(ObjPtr access_to, if (UNLIKELY(!this->CanAccess(dex_access_to))) { if (throw_on_failure) { ThrowIllegalAccessErrorClassForMethodDispatch(this, - dex_access_to.Ptr(), + dex_access_to, method, throw_invoke_type); } @@ -800,7 +800,7 @@ inline ObjPtr Class::Alloc(Thread* self, gc::AllocatorType allocator_typ obj = nullptr; } } - return obj.Ptr(); + return obj; } inline ObjPtr Class::AllocObject(Thread* self) { diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index a637c86823..7adb0d0f0c 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -781,7 +781,7 @@ class MANAGED Class FINAL : public Object { ALWAYS_INLINE PointerArray* GetVTableDuringLinking() REQUIRES_SHARED(Locks::mutator_lock_); - void SetVTable(PointerArray* new_vtable) REQUIRES_SHARED(Locks::mutator_lock_); + void SetVTable(ObjPtr new_vtable) REQUIRES_SHARED(Locks::mutator_lock_); static MemberOffset VTableOffset() { return OFFSET_OF_OBJECT_MEMBER(Class, vtable_); diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc index 7214620c93..44bf9891cd 100644 --- a/runtime/mirror/class_ext.cc +++ b/runtime/mirror/class_ext.cc @@ -43,8 +43,8 @@ void ClassExt::SetObsoleteArrays(ObjPtr methods, auto obsolete_dex_cache_off = OFFSET_OF_OBJECT_MEMBER(ClassExt, obsolete_dex_caches_); auto obsolete_methods_off = OFFSET_OF_OBJECT_MEMBER(ClassExt, obsolete_methods_); DCHECK(!Runtime::Current()->IsActiveTransaction()); - SetFieldObject(obsolete_dex_cache_off, dex_caches.Ptr()); - SetFieldObject(obsolete_methods_off, methods.Ptr()); + SetFieldObject(obsolete_dex_cache_off, dex_caches); + SetFieldObject(obsolete_methods_off, methods); } // We really need to be careful how we update this. If we ever in the future make it so that diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index bfebd5d365..2d10b97d60 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -747,7 +747,7 @@ inline void Object::SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, } else { obj = GetFieldObject(field_offset); } - Runtime::Current()->RecordWriteFieldReference(this, field_offset, obj.Ptr(), true); + Runtime::Current()->RecordWriteFieldReference(this, field_offset, obj, true); } if (kVerifyFlags & kVerifyThis) { VerifyObject(this); diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h index 086d2f4672..ed3c567464 100644 --- a/runtime/mirror/object_array-inl.h +++ b/runtime/mirror/object_array-inl.h @@ -37,14 +37,15 @@ namespace art { namespace mirror { template -inline ObjectArray* ObjectArray::Alloc(Thread* self, - ObjPtr object_array_class, - int32_t length, gc::AllocatorType allocator_type) { - Array* array = Array::Alloc(self, - object_array_class.Ptr(), - length, - ComponentSizeShiftWidth(kHeapReferenceSize), - allocator_type); +inline ObjPtr> ObjectArray::Alloc(Thread* self, + ObjPtr object_array_class, + int32_t length, + gc::AllocatorType allocator_type) { + ObjPtr array = Array::Alloc(self, + object_array_class, + length, + ComponentSizeShiftWidth(kHeapReferenceSize), + allocator_type); if (UNLIKELY(array == nullptr)) { return nullptr; } @@ -54,9 +55,9 @@ inline ObjectArray* ObjectArray::Alloc(Thread* self, } template -inline ObjectArray* ObjectArray::Alloc(Thread* self, - ObjPtr object_array_class, - int32_t length) { +inline ObjPtr> ObjectArray::Alloc(Thread* self, + ObjPtr object_array_class, + int32_t length) { return Alloc(self, object_array_class, length, @@ -346,7 +347,7 @@ inline void ObjectArray::AssignableCheckingMemcpy(int32_t dst_pos, } template -inline ObjectArray* ObjectArray::CopyOf(Thread* self, int32_t new_length) { +inline ObjPtr> ObjectArray::CopyOf(Thread* self, int32_t new_length) { DCHECK_GE(new_length, 0); // We may get copied by a compacting GC. StackHandleScope<1> hs(self); @@ -354,7 +355,7 @@ inline ObjectArray* ObjectArray::CopyOf(Thread* self, int32_t new_length) gc::Heap* heap = Runtime::Current()->GetHeap(); gc::AllocatorType allocator_type = heap->IsMovableObject(this) ? heap->GetCurrentAllocator() : heap->GetCurrentNonMovingAllocator(); - ObjectArray* new_array = Alloc(self, GetClass(), new_length, allocator_type); + ObjPtr> new_array = Alloc(self, GetClass(), new_length, allocator_type); if (LIKELY(new_array != nullptr)) { new_array->AssignableMemcpy(0, h_this.Get(), 0, std::min(h_this->GetLength(), new_length)); } diff --git a/runtime/mirror/object_array.h b/runtime/mirror/object_array.h index b7a956176f..6506f6ea9a 100644 --- a/runtime/mirror/object_array.h +++ b/runtime/mirror/object_array.h @@ -31,15 +31,15 @@ class MANAGED ObjectArray: public Array { return Array::ClassSize(pointer_size); } - static ObjectArray* Alloc(Thread* self, - ObjPtr object_array_class, - int32_t length, - gc::AllocatorType allocator_type) + static ObjPtr> Alloc(Thread* self, + ObjPtr object_array_class, + int32_t length, + gc::AllocatorType allocator_type) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - static ObjectArray* Alloc(Thread* self, - ObjPtr object_array_class, - int32_t length) + static ObjPtr> Alloc(Thread* self, + ObjPtr object_array_class, + int32_t length) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); template* CopyOf(Thread* self, int32_t new_length) + ObjPtr> CopyOf(Thread* self, int32_t new_length) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index 8b7a1b6876..0b615a6b9a 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -76,7 +76,7 @@ class ObjectTest : public CommonRuntimeTest { } template - mirror::ObjectArray* AllocObjectArray(Thread* self, size_t length) + ObjPtr> AllocObjectArray(Thread* self, size_t length) REQUIRES_SHARED(Locks::mutator_lock_) { return mirror::ObjectArray::Alloc( self, GetClassRoot(ClassRoot::kObjectArrayClass, class_linker_), length); @@ -206,7 +206,8 @@ void TestPrimitiveArray(ClassLinker* cl) { ScopedObjectAccess soa(Thread::Current()); typedef typename ArrayT::ElementType T; - ArrayT* a = ArrayT::Alloc(soa.Self(), 2); + StackHandleScope<2> hs(soa.Self()); + Handle a = hs.NewHandle(ArrayT::Alloc(soa.Self(), 2)); EXPECT_EQ(2, a->GetLength()); EXPECT_EQ(0, a->Get(0)); EXPECT_EQ(0, a->Get(1)); @@ -217,7 +218,6 @@ void TestPrimitiveArray(ClassLinker* cl) { EXPECT_EQ(T(123), a->Get(0)); EXPECT_EQ(T(321), a->Get(1)); - StackHandleScope<1> hs(soa.Self()); Handle aioobe = hs.NewHandle( cl->FindSystemClass(soa.Self(), "Ljava/lang/ArrayIndexOutOfBoundsException;")); @@ -256,7 +256,8 @@ TEST_F(ObjectTest, PrimitiveArray_Double_Alloc) { ScopedObjectAccess soa(Thread::Current()); typedef typename ArrayT::ElementType T; - ArrayT* a = ArrayT::Alloc(soa.Self(), 2); + StackHandleScope<2> hs(soa.Self()); + Handle a = hs.NewHandle(ArrayT::Alloc(soa.Self(), 2)); EXPECT_EQ(2, a->GetLength()); EXPECT_DOUBLE_EQ(0, a->Get(0)); EXPECT_DOUBLE_EQ(0, a->Get(1)); @@ -267,7 +268,6 @@ TEST_F(ObjectTest, PrimitiveArray_Double_Alloc) { EXPECT_DOUBLE_EQ(T(123), a->Get(0)); EXPECT_DOUBLE_EQ(T(321), a->Get(1)); - StackHandleScope<1> hs(soa.Self()); Handle aioobe = hs.NewHandle( class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/ArrayIndexOutOfBoundsException;")); @@ -287,7 +287,8 @@ TEST_F(ObjectTest, PrimitiveArray_Float_Alloc) { ScopedObjectAccess soa(Thread::Current()); typedef typename ArrayT::ElementType T; - ArrayT* a = ArrayT::Alloc(soa.Self(), 2); + StackHandleScope<2> hs(soa.Self()); + Handle a = hs.NewHandle(ArrayT::Alloc(soa.Self(), 2)); EXPECT_FLOAT_EQ(2, a->GetLength()); EXPECT_FLOAT_EQ(0, a->Get(0)); EXPECT_FLOAT_EQ(0, a->Get(1)); @@ -298,7 +299,6 @@ TEST_F(ObjectTest, PrimitiveArray_Float_Alloc) { EXPECT_FLOAT_EQ(T(123), a->Get(0)); EXPECT_FLOAT_EQ(T(321), a->Get(1)); - StackHandleScope<1> hs(soa.Self()); Handle aioobe = hs.NewHandle( class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/ArrayIndexOutOfBoundsException;")); @@ -317,16 +317,17 @@ TEST_F(ObjectTest, PrimitiveArray_Float_Alloc) { TEST_F(ObjectTest, CreateMultiArray) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); - Handle c(hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "I"))); + StackHandleScope<4> hs(soa.Self()); + Handle int_class(hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "I"))); + Handle int_array_class = hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[I")); MutableHandle dims(hs.NewHandle(IntArray::Alloc(soa.Self(), 1))); dims->Set(0, 1); - Array* multi = Array::CreateMultiArray(soa.Self(), c, dims); - EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass(soa.Self(), "[I")); + MutableHandle multi = hs.NewHandle(Array::CreateMultiArray(soa.Self(), int_class, dims)); + EXPECT_OBJ_PTR_EQ(int_array_class.Get(), multi->GetClass()); EXPECT_EQ(1, multi->GetLength()); dims->Set(0, -1); - multi = Array::CreateMultiArray(soa.Self(), c, dims); + multi.Assign(Array::CreateMultiArray(soa.Self(), int_class, dims)); EXPECT_TRUE(soa.Self()->IsExceptionPending()); EXPECT_EQ(mirror::Class::PrettyDescriptor(soa.Self()->GetException()->GetClass()), "java.lang.NegativeArraySizeException"); @@ -337,12 +338,12 @@ TEST_F(ObjectTest, CreateMultiArray) { for (int j = 0; j < 20; ++j) { dims->Set(0, i); dims->Set(1, j); - multi = Array::CreateMultiArray(soa.Self(), c, dims); + multi.Assign(Array::CreateMultiArray(soa.Self(), int_class, dims)); EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass(soa.Self(), "[[I")); EXPECT_EQ(i, multi->GetLength()); for (int k = 0; k < i; ++k) { - Array* outer = multi->AsObjectArray()->Get(k); - EXPECT_TRUE(outer->GetClass() == class_linker_->FindSystemClass(soa.Self(), "[I")); + ObjPtr outer = multi->AsObjectArray()->Get(k); + EXPECT_OBJ_PTR_EQ(int_array_class.Get(), outer->GetClass()); EXPECT_EQ(j, outer->GetLength()); } } @@ -817,7 +818,7 @@ TEST_F(ObjectTest, PrettyTypeOf) { ObjPtr c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"); ASSERT_TRUE(c != nullptr); - mirror::Object* o = mirror::ObjectArray::Alloc(soa.Self(), c, 0); + ObjPtr o = mirror::ObjectArray::Alloc(soa.Self(), c, 0); EXPECT_EQ("java.lang.String[]", mirror::Object::PrettyTypeOf(o)); EXPECT_EQ("java.lang.Class", mirror::Object::PrettyTypeOf(o->GetClass())); } diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 6c820190b4..8aed4a806c 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -111,7 +111,7 @@ static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaEle } Runtime* runtime = Runtime::Current(); ObjPtr array_class = - runtime->GetClassLinker()->FindArrayClass(soa.Self(), &element_class); + runtime->GetClassLinker()->FindArrayClass(soa.Self(), element_class); if (UNLIKELY(array_class == nullptr)) { return nullptr; } @@ -138,7 +138,7 @@ static jobject VMRuntime_newUnpaddedArray(JNIEnv* env, jobject, jclass javaEleme } Runtime* runtime = Runtime::Current(); ObjPtr array_class = runtime->GetClassLinker()->FindArrayClass(soa.Self(), - &element_class); + element_class); if (UNLIKELY(array_class == nullptr)) { return nullptr; } diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index 38c65f5deb..5b47eaca86 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -100,7 +100,7 @@ class ClassSet { } void AddClass(ObjPtr klass) REQUIRES(Locks::mutator_lock_) { - class_set_.insert(self_->GetJniEnv()->AddLocalReference(klass.Ptr())); + class_set_.insert(self_->GetJniEnv()->AddLocalReference(klass)); } const std::unordered_set& GetClasses() const { diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index c6bdfa10c6..82e54e2f4c 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -646,8 +646,8 @@ static jobjectArray Class_getDeclaredAnnotations(JNIEnv* env, jobject javaThis) soa.Decode(WellKnownClasses::java_lang_annotation_Annotation__array); ObjPtr> empty_array = mirror::ObjectArray::Alloc(soa.Self(), - annotation_array_class.Ptr(), - 0); + annotation_array_class, + /* length */ 0); return soa.AddLocalReference(empty_array); } return soa.AddLocalReference(annotations::GetAnnotationsForClass(klass)); diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc index a37a76f809..13871f7be7 100644 --- a/runtime/native/java_lang_Thread.cc +++ b/runtime/native/java_lang_Thread.cc @@ -119,7 +119,7 @@ static jboolean Thread_holdsLock(JNIEnv* env, jclass, jobject java_object) { return JNI_FALSE; } Thread* thread = soa.Self(); - return thread->HoldsLock(object.Ptr()); + return thread->HoldsLock(object); } static void Thread_nativeInterrupt(JNIEnv* env, jobject java_thread) { diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc index 42c7ad5650..1ad233a6b2 100644 --- a/runtime/native/java_lang_VMClassLoader.cc +++ b/runtime/native/java_lang_VMClassLoader.cc @@ -86,7 +86,7 @@ static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoa } // If class is erroneous, throw the earlier failure, wrapped in certain cases. See b/28787733. if (c != nullptr && c->IsErroneous()) { - cl->ThrowEarlierClassFailure(c.Ptr()); + cl->ThrowEarlierClassFailure(c); Thread* self = soa.Self(); ObjPtr iae_class = self->DecodeJObject(WellKnownClasses::java_lang_IllegalAccessError)->AsClass(); diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc index 8bcda10f2a..452a66dca2 100644 --- a/runtime/native/java_lang_reflect_Array.cc +++ b/runtime/native/java_lang_reflect_Array.cc @@ -44,9 +44,8 @@ static jobject Array_createMultiArray( Primitive::kPrimInt); Handle dimensions_array( hs.NewHandle(ObjPtr::DownCast(dimensions_obj))); - mirror::Array* new_array = mirror::Array::CreateMultiArray(soa.Self(), - element_class, - dimensions_array); + ObjPtr new_array = + mirror::Array::CreateMultiArray(soa.Self(), element_class, dimensions_array); return soa.AddLocalReference(new_array); } @@ -57,16 +56,16 @@ static jobject Array_createObjectArray(JNIEnv* env, jclass, jclass javaElementCl ThrowNegativeArraySizeException(length); return nullptr; } - ObjPtr element_class = soa.Decode(javaElementClass); Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); - ObjPtr array_class = class_linker->FindArrayClass(soa.Self(), &element_class); + ObjPtr array_class = + class_linker->FindArrayClass(soa.Self(), soa.Decode(javaElementClass)); if (UNLIKELY(array_class == nullptr)) { CHECK(soa.Self()->IsExceptionPending()); return nullptr; } DCHECK(array_class->IsObjectArrayClass()); - ObjPtr new_array = mirror::ObjectArray::Alloc( + ObjPtr new_array = mirror::ObjectArray::Alloc( soa.Self(), array_class, length, diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 25599843e9..8766692a8c 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -476,7 +476,7 @@ static jobjectArray Field_getDeclaredAnnotations(JNIEnv* env, jobject javaField) ObjPtr annotation_array_class = soa.Decode(WellKnownClasses::java_lang_annotation_Annotation__array); ObjPtr> empty_array = - mirror::ObjectArray::Alloc(soa.Self(), annotation_array_class.Ptr(), 0); + mirror::ObjectArray::Alloc(soa.Self(), annotation_array_class, 0); return soa.AddLocalReference(empty_array); } return soa.AddLocalReference(annotations::GetAnnotationsForField(field)); diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc index 34455fe00f..87fda6bf5d 100644 --- a/runtime/native/java_lang_reflect_Method.cc +++ b/runtime/native/java_lang_reflect_Method.cc @@ -69,7 +69,7 @@ static jobjectArray Method_getExceptionTypes(JNIEnv* env, jobject javaMethod) { // Return an empty array instead of a null pointer ObjPtr class_array_class = GetClassRoot>(); DCHECK(class_array_class != nullptr); - mirror::ObjectArray* empty_array = + ObjPtr> empty_array = mirror::ObjectArray::Alloc(soa.Self(), class_array_class, 0); return soa.AddLocalReference(empty_array); } else { diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc index 06ea384aa3..0cb5e565e9 100644 --- a/runtime/reference_table_test.cc +++ b/runtime/reference_table_test.cc @@ -106,7 +106,7 @@ TEST_F(ReferenceTableTest, Basics) { } // Add a second object 10 times and check dumping is sane. - mirror::Object* o2 = mirror::ShortArray::Alloc(soa.Self(), 0); + ObjPtr o2 = mirror::ShortArray::Alloc(soa.Self(), 0); for (size_t i = 0; i < 10; ++i) { rt.Add(o2); EXPECT_EQ(i + 2, rt.Size()); @@ -276,23 +276,26 @@ TEST_F(ReferenceTableTest, SummaryOrder) { ReferenceTable rt("test", 0, 20); { - mirror::Object* s1 = mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello"); - mirror::Object* s2 = mirror::String::AllocFromModifiedUtf8(soa.Self(), "world"); + StackHandleScope<1> hs(soa.Self()); + Handle s1 = + hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello")); + ObjPtr s2 = mirror::String::AllocFromModifiedUtf8(soa.Self(), "world"); // 3 copies of s1, 2 copies of s2, interleaved. for (size_t i = 0; i != 2; ++i) { - rt.Add(s1); + rt.Add(s1.Get()); rt.Add(s2); } - rt.Add(s1); + rt.Add(s1.Get()); } { // Differently sized byte arrays. Should be sorted by identical (non-unique cound). - mirror::Object* b1_1 = mirror::ByteArray::Alloc(soa.Self(), 1); - rt.Add(b1_1); + StackHandleScope<1> hs(soa.Self()); + Handle b1_1 = hs.NewHandle(mirror::ByteArray::Alloc(soa.Self(), 1)); + rt.Add(b1_1.Get()); rt.Add(mirror::ByteArray::Alloc(soa.Self(), 2)); - rt.Add(b1_1); + rt.Add(b1_1.Get()); rt.Add(mirror::ByteArray::Alloc(soa.Self(), 2)); rt.Add(mirror::ByteArray::Alloc(soa.Self(), 1)); rt.Add(mirror::ByteArray::Alloc(soa.Self(), 2)); diff --git a/runtime/reflection-inl.h b/runtime/reflection-inl.h index 26fb021903..9fe4bca9dd 100644 --- a/runtime/reflection-inl.h +++ b/runtime/reflection-inl.h @@ -121,7 +121,7 @@ inline bool VerifyObjectIsClass(ObjPtr o, ObjPtr if (UNLIKELY(o == nullptr)) { ThrowNullPointerException("null receiver"); return false; - } else if (UNLIKELY(!o->InstanceOf(c.Ptr()))) { + } else if (UNLIKELY(!o->InstanceOf(c))) { InvalidReceiverError(o, c); return false; } diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 66eba1e1d4..6aeedd4f02 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -243,7 +243,7 @@ class ArgArray { // we've seen cases where it's not b/34440020. ObjPtr dst_class( m->ResolveClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_)); - if (dst_class.Ptr() == nullptr) { + if (dst_class == nullptr) { CHECK(self->IsExceptionPending()); return false; } diff --git a/runtime/thread.cc b/runtime/thread.cc index b59606a06b..a8133a1fda 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -491,7 +491,7 @@ Thread* Thread::FromManagedThread(const ScopedObjectAccessAlreadyRunnable& soa, Thread* Thread::FromManagedThread(const ScopedObjectAccessAlreadyRunnable& soa, jobject java_thread) { - return FromManagedThread(soa, soa.Decode(java_thread).Ptr()); + return FromManagedThread(soa, soa.Decode(java_thread)); } static size_t FixStackSize(size_t stack_size) { @@ -2728,7 +2728,7 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray( depth = std::min(depth, traces_length); } else { // Create java_trace array and place in local reference table - mirror::ObjectArray* java_traces = + ObjPtr> java_traces = class_linker->AllocStackTraceElementArray(soa.Self(), depth); if (java_traces == nullptr) { return nullptr; diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc index 02e61d76a1..370a619820 100644 --- a/runtime/transaction_test.cc +++ b/runtime/transaction_test.cc @@ -506,7 +506,7 @@ TEST_F(TransactionTest, ResolveString) { class_linker_->LookupString(string_idx, h_dex_cache.Get()); ASSERT_TRUE(s != nullptr); EXPECT_STREQ(s->ToModifiedUtf8().c_str(), kResolvedString); - EXPECT_EQ(s.Ptr(), h_dex_cache->GetResolvedString(string_idx)); + EXPECT_OBJ_PTR_EQ(s, h_dex_cache->GetResolvedString(string_idx)); } Runtime::Current()->RollbackAndExitTransactionMode(); // Check that the string did not stay resolved. diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 2e3a6590e4..f57f757f0b 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -2932,7 +2932,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { : called_method->LookupResolvedReturnType(); if (return_type_class != nullptr) { return_type = &FromClass(return_type_descriptor, - return_type_class.Ptr(), + return_type_class, return_type_class->CannotBeAssignedFromOtherTypes()); } else { DCHECK(!can_load_classes_ || self_->IsExceptionPending()); @@ -3685,7 +3685,7 @@ const RegType& MethodVerifier::ResolveClass(dex::TypeIndex class_idx) { } // Record result of class resolution attempt. - VerifierDeps::MaybeRecordClassResolution(*dex_file_, class_idx, klass.Ptr()); + VerifierDeps::MaybeRecordClassResolution(*dex_file_, class_idx, klass); // If requested, check if access is allowed. Unresolved types are included in this check, as the // interpreter only tests whether access is allowed when a class is not pre-verified and runs in @@ -4628,9 +4628,7 @@ ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_id std::string temp; ObjPtr klass = field->GetDeclaringClass(); const RegType& field_klass = - FromClass(klass->GetDescriptor(&temp), - klass.Ptr(), - klass->CannotBeAssignedFromOtherTypes()); + FromClass(klass->GetDescriptor(&temp), klass, klass->CannotBeAssignedFromOtherTypes()); if (obj_type.IsUninitializedTypes()) { // Field accesses through uninitialized references are only allowable for constructors where // the field is declared in this class. @@ -4731,7 +4729,7 @@ void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType& can_load_classes_ ? field->ResolveType() : field->LookupResolvedType(); if (field_type_class != nullptr) { field_type = &FromClass(field->GetTypeDescriptor(), - field_type_class.Ptr(), + field_type_class, field_type_class->CannotBeAssignedFromOtherTypes()); } else { DCHECK(!can_load_classes_ || self_->IsExceptionPending()); @@ -4919,7 +4917,7 @@ const RegType& MethodVerifier::GetMethodReturnType() { : method_being_verified_->LookupResolvedReturnType(); if (return_type_class != nullptr) { return_type_ = &FromClass(method_being_verified_->GetReturnTypeDescriptor(), - return_type_class.Ptr(), + return_type_class, return_type_class->CannotBeAssignedFromOtherTypes()); } else { DCHECK(!can_load_classes_ || self_->IsExceptionPending()); diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index 73e516c7bf..4a3f9e6365 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -807,7 +807,7 @@ ObjPtr RegType::ClassJoin(ObjPtr s, ObjPtrs. ObjPtr array_class = - Runtime::Current()->GetClassLinker()->FindArrayClass(self, &common_elem); + Runtime::Current()->GetClassLinker()->FindArrayClass(self, common_elem); if (UNLIKELY(array_class == nullptr)) { self->AssertPendingException(); return nullptr; diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc index 500cc37af4..fb91976781 100644 --- a/runtime/verifier/verifier_deps.cc +++ b/runtime/verifier/verifier_deps.cc @@ -384,7 +384,7 @@ ObjPtr VerifierDeps::FindOneClassPathBoundaryForInterface( // Find a boundary making `source` inherit from `destination`. We must find one. for (const ObjPtr& boundary : boundaries) { if (destination->IsAssignableFrom(boundary)) { - return boundary.Ptr(); + return boundary; } } LOG(FATAL) << "Should have found a classpath boundary"; diff --git a/test/497-inlining-and-class-loader/clear_dex_cache.cc b/test/497-inlining-and-class-loader/clear_dex_cache.cc index c113042c9c..c6fd56f20d 100644 --- a/test/497-inlining-and-class-loader/clear_dex_cache.cc +++ b/test/497-inlining-and-class-loader/clear_dex_cache.cc @@ -52,11 +52,11 @@ extern "C" JNIEXPORT jobject JNICALL Java_Main_cloneResolvedMethods(JNIEnv* env, uint32_t index = pair.index; ArtMethod* method = pair.object; if (sizeof(void*) == 4) { - ObjPtr int_array = down_cast(decoded_array.Ptr()); + ObjPtr int_array = ObjPtr::DownCast(decoded_array); int_array->Set(2u * i, index); int_array->Set(2u * i + 1u, static_cast(reinterpret_cast(method))); } else { - ObjPtr long_array = down_cast(decoded_array.Ptr()); + ObjPtr long_array = ObjPtr::DownCast(decoded_array); long_array->Set(2u * i, index); long_array->Set(2u * i + 1u, reinterpret_cast64(method)); } -- GitLab From 3a0eef03bc02b6e74db8f795cd9fc85164ec133d Mon Sep 17 00:00:00 2001 From: Alex Light Date: Fri, 1 Jun 2018 11:10:14 -0700 Subject: [PATCH 516/749] Run debuggable tests on art-jit We were not running jit-debuggable tests anywhere. Adding them to the art-jit column should give us coverage for this configuration. Test: ./test/testrunner/run_build_test_target.py art-jit Change-Id: I7df9c017c9237910c4950888f04dd0a45eeac8da --- test/knownfailures.json | 6 ++++++ test/testrunner/target_config.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/test/knownfailures.json b/test/knownfailures.json index a202044786..aac28408c3 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -360,6 +360,12 @@ "description": ["Profile driven dexlayout does not work with vdex or dex verifier."], "variant": "speed-profile" }, + { + "test_patterns": ["616-cha.*"], + "description": ["cha tests rely on knowing the exact set of optimizations available. ", + "Debuggable runtimes change the set of optimizations."], + "variant": "debuggable" + }, { "test_patterns": ["616-cha.*"], "description": ["cha tests rely on knowing more about the state of the JIT then is possible with jvmti-stress"], diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index e0757abbe0..71f4cc0731 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -44,7 +44,7 @@ target_config = { 'run-test' : ['--interp-ac'] }, 'art-jit' : { - 'run-test' : ['--jit'] + 'run-test' : ['--jit', '--debuggable', '--ndebuggable'] }, 'art-jit-on-first-use' : { 'run-test' : ['--jit', -- GitLab From 31380c788d77dc7efc451817c3619c454507007d Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 31 May 2018 18:12:27 -0700 Subject: [PATCH 517/749] Add experiment for analyzing debug info Analyze entropy of debug info to estimates a close upper bound savings from huffman encoding debug infos. Also measure how many debug info bytes are dedupable if you exclude the line number and parameter names. Sample output: Debug info bytes 96101012(5.70%) DBG_END_SEQUENCE: 6401069(0.38%) DBG_ADVANCE_PC: 3709064(0.22%) DBG_ADVANCE_LINE: 8620724(0.51%) DBG_START_LOCAL: 5244232(0.31%) DBG_START_LOCAL_EXTENDED: 1763845(0.10%) DBG_END_LOCAL: 1216044(0.07%) DBG_RESTART_LOCAL: 565412(0.03%) DBG_SET_PROLOGUE bytes 5768714(0.34%) DBG_SET_FILE bytes 0(0.00%) special: 36310220(2.15%) Debug info entropy 69199724(4.10%) Debug info opcode bytes 55613021(3.30%) Debug info opcode entropy 34792401(2.06%) Debug info non header bytes 69599324(4.13%) Debug info deduped non header bytes 52475493(3.11%) Bug: 77721545 Test: test-art-host Change-Id: I031322e3b79a1572fcbb9e513ded9708e3b48354 --- tools/dexanalyze/dexanalyze.cc | 9 ++ tools/dexanalyze/dexanalyze_experiments.cc | 122 +++++++++++++++++++++ tools/dexanalyze/dexanalyze_experiments.h | 26 +++++ 3 files changed, 157 insertions(+) diff --git a/tools/dexanalyze/dexanalyze.cc b/tools/dexanalyze/dexanalyze.cc index 38725d428b..7d7e5f28b3 100644 --- a/tools/dexanalyze/dexanalyze.cc +++ b/tools/dexanalyze/dexanalyze.cc @@ -49,6 +49,8 @@ class DexAnalyze { << "Usage " << argv[0] << " [options] \n" << " [options] is a combination of the following\n" << " -count_indices (Count dex indices accessed from code items)\n" + << " -analyze-strings (Analyze string data)\n" + << " -analyze-debug-info (Analyze debug info)\n" << " -i (Ignore Dex checksum and verification failures)\n" << " -a (Run all experiments)\n" << " -d (Dump on per DEX basis)\n"; @@ -69,6 +71,8 @@ class DexAnalyze { exp_count_indices_ = true; } else if (arg == "-analyze-strings") { exp_analyze_strings_ = true; + } else if (arg == "-analyze-debug-info") { + exp_debug_info_ = true; } else if (arg == "-d") { dump_per_input_dex_ = true; } else if (!arg.empty() && arg[0] == '-') { @@ -90,6 +94,7 @@ class DexAnalyze { bool exp_count_indices_ = false; bool exp_code_metrics_ = false; bool exp_analyze_strings_ = false; + bool exp_debug_info_ = false; bool run_all_experiments_ = false; std::vector filenames_; }; @@ -106,6 +111,9 @@ class DexAnalyze { if (options->run_all_experiments_ || options->exp_code_metrics_) { experiments_.emplace_back(new CodeMetrics); } + if (options->run_all_experiments_ || options->exp_debug_info_) { + experiments_.emplace_back(new AnalyzeDebugInfo); + } } bool ProcessDexFile(const DexFile& dex_file) { @@ -120,6 +128,7 @@ class DexAnalyze { void Dump(std::ostream& os) { for (std::unique_ptr& experiment : experiments_) { experiment->Dump(os, total_size_); + os << "\n"; } } diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc index 7006370c0b..1a3b89cbc7 100644 --- a/tools/dexanalyze/dexanalyze_experiments.cc +++ b/tools/dexanalyze/dexanalyze_experiments.cc @@ -75,6 +75,128 @@ static size_t PrefixLen(const std::string& a, const std::string& b) { return len; } +void AnalyzeDebugInfo::ProcessDexFile(const DexFile& dex_file) { + std::set seen; + std::vector counts(256, 0u); + std::vector opcode_counts(256, 0u); + std::set> unique_non_header; + for (ClassAccessor accessor : dex_file.GetClasses()) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + CodeItemDebugInfoAccessor code_item(dex_file, method.GetCodeItem(), method.GetIndex()); + const uint8_t* debug_info = dex_file.GetDebugInfoStream(code_item.DebugInfoOffset()); + if (debug_info != nullptr && seen.insert(debug_info).second) { + const uint8_t* stream = debug_info; + DecodeUnsignedLeb128(&stream); // line_start + uint32_t parameters_size = DecodeUnsignedLeb128(&stream); + for (uint32_t i = 0; i < parameters_size; ++i) { + DecodeUnsignedLeb128P1(&stream); // Parameter name. + } + bool done = false; + const uint8_t* after_header_start = stream; + while (!done) { + const uint8_t* const op_start = stream; + uint8_t opcode = *stream++; + ++opcode_counts[opcode]; + ++total_opcode_bytes_; + switch (opcode) { + case DexFile::DBG_END_SEQUENCE: + ++total_end_seq_bytes_; + done = true; + break; + case DexFile::DBG_ADVANCE_PC: + DecodeUnsignedLeb128(&stream); // addr_diff + total_advance_pc_bytes_ += stream - op_start; + break; + case DexFile::DBG_ADVANCE_LINE: + DecodeSignedLeb128(&stream); // line_diff + total_advance_line_bytes_ += stream - op_start; + break; + case DexFile::DBG_START_LOCAL: + DecodeUnsignedLeb128(&stream); // register_num + DecodeUnsignedLeb128P1(&stream); // name_idx + DecodeUnsignedLeb128P1(&stream); // type_idx + total_start_local_bytes_ += stream - op_start; + break; + case DexFile::DBG_START_LOCAL_EXTENDED: + DecodeUnsignedLeb128(&stream); // register_num + DecodeUnsignedLeb128P1(&stream); // name_idx + DecodeUnsignedLeb128P1(&stream); // type_idx + DecodeUnsignedLeb128P1(&stream); // sig_idx + total_start_local_extended_bytes_ += stream - op_start; + break; + case DexFile::DBG_END_LOCAL: + DecodeUnsignedLeb128(&stream); // register_num + total_end_local_bytes_ += stream - op_start; + break; + case DexFile::DBG_RESTART_LOCAL: + DecodeUnsignedLeb128(&stream); // register_num + total_restart_local_bytes_ += stream - op_start; + break; + case DexFile::DBG_SET_PROLOGUE_END: + case DexFile::DBG_SET_EPILOGUE_BEGIN: + total_epilogue_bytes_ += stream - op_start; + break; + case DexFile::DBG_SET_FILE: { + DecodeUnsignedLeb128P1(&stream); // name_idx + total_set_file_bytes_ += stream - op_start; + break; + } + default: { + total_other_bytes_ += stream - op_start; + break; + } + } + } + const size_t bytes = stream - debug_info; + total_bytes_ += bytes; + total_non_header_bytes_ += stream - after_header_start; + if (unique_non_header.insert(std::vector(after_header_start, stream)).second) { + total_unique_non_header_bytes_ += stream - after_header_start; + } + for (size_t i = 0; i < bytes; ++i) { + ++counts[debug_info[i]]; + } + } + } + } + auto calc_entropy = [](std::vector data) { + size_t total = std::accumulate(data.begin(), data.end(), 0u); + double avg_entropy = 0.0; + for (size_t c : data) { + if (c > 0) { + double ratio = static_cast(c) / static_cast(total); + avg_entropy -= ratio * log(ratio) / log(256.0); + } + } + return avg_entropy * total; + }; + total_entropy_ += calc_entropy(counts); + total_opcode_entropy_ += calc_entropy(opcode_counts); +} + +void AnalyzeDebugInfo::Dump(std::ostream& os, uint64_t total_size) const { + os << "Debug info bytes " << Percent(total_bytes_, total_size) << "\n"; + + os << " DBG_END_SEQUENCE: " << Percent(total_end_seq_bytes_, total_size) << "\n"; + os << " DBG_ADVANCE_PC: " << Percent(total_advance_pc_bytes_, total_size) << "\n"; + os << " DBG_ADVANCE_LINE: " << Percent(total_advance_line_bytes_, total_size) << "\n"; + os << " DBG_START_LOCAL: " << Percent(total_start_local_bytes_, total_size) << "\n"; + os << " DBG_START_LOCAL_EXTENDED: " + << Percent(total_start_local_extended_bytes_, total_size) << "\n"; + os << " DBG_END_LOCAL: " << Percent(total_end_local_bytes_, total_size) << "\n"; + os << " DBG_RESTART_LOCAL: " << Percent(total_restart_local_bytes_, total_size) << "\n"; + os << " DBG_SET_PROLOGUE bytes " << Percent(total_epilogue_bytes_, total_size) << "\n"; + os << " DBG_SET_FILE bytes " << Percent(total_set_file_bytes_, total_size) << "\n"; + os << " special: " + << Percent(total_other_bytes_, total_size) << "\n"; + os << "Debug info entropy " << Percent(total_entropy_, total_size) << "\n"; + os << "Debug info opcode bytes " << Percent(total_opcode_bytes_, total_size) << "\n"; + os << "Debug info opcode entropy " << Percent(total_opcode_entropy_, total_size) << "\n"; + os << "Debug info non header bytes " << Percent(total_non_header_bytes_, total_size) << "\n"; + os << "Debug info deduped non header bytes " + << Percent(total_unique_non_header_bytes_, total_size) << "\n"; +} + void AnalyzeStrings::ProcessDexFile(const DexFile& dex_file) { std::vector strings; for (size_t i = 0; i < dex_file.NumStringIds(); ++i) { diff --git a/tools/dexanalyze/dexanalyze_experiments.h b/tools/dexanalyze/dexanalyze_experiments.h index 7ba2a49372..a2621c85ca 100644 --- a/tools/dexanalyze/dexanalyze_experiments.h +++ b/tools/dexanalyze/dexanalyze_experiments.h @@ -51,6 +51,32 @@ class AnalyzeStrings : public Experiment { int64_t total_num_prefixes_ = 0u; }; +// Analyze debug info sizes. +class AnalyzeDebugInfo : public Experiment { + public: + void ProcessDexFile(const DexFile& dex_file); + void Dump(std::ostream& os, uint64_t total_size) const; + + private: + int64_t total_bytes_ = 0u; + int64_t total_entropy_ = 0u; + int64_t total_opcode_bytes_ = 0u; + int64_t total_opcode_entropy_ = 0u; + int64_t total_non_header_bytes_ = 0u; + int64_t total_unique_non_header_bytes_ = 0u; + // Opcode and related data. + int64_t total_end_seq_bytes_ = 0u; + int64_t total_advance_pc_bytes_ = 0u; + int64_t total_advance_line_bytes_ = 0u; + int64_t total_start_local_bytes_ = 0u; + int64_t total_start_local_extended_bytes_ = 0u; + int64_t total_end_local_bytes_ = 0u; + int64_t total_restart_local_bytes_ = 0u; + int64_t total_epilogue_bytes_ = 0u; + int64_t total_set_file_bytes_ = 0u; + int64_t total_other_bytes_ = 0u; +}; + // Count numbers of dex indices. class CountDexIndices : public Experiment { public: -- GitLab From 48de723721a9103bc3374c00f1e18ee7dcaea49a Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 1 Jun 2018 17:30:29 -0700 Subject: [PATCH 518/749] Refactor experiments to allow multidex analysis Analyze multidex deduplication for debug infos when excluding the header fields. Bug: 77721545 Test: test-art-host Change-Id: I247399aaecaf072cce1b3e035256db0f5e91e8c3 --- tools/dexanalyze/dexanalyze.cc | 28 ++-- tools/dexanalyze/dexanalyze_experiments.cc | 157 +++++++++++---------- tools/dexanalyze/dexanalyze_experiments.h | 9 +- 3 files changed, 103 insertions(+), 91 deletions(-) diff --git a/tools/dexanalyze/dexanalyze.cc b/tools/dexanalyze/dexanalyze.cc index 7d7e5f28b3..7a9b8fb018 100644 --- a/tools/dexanalyze/dexanalyze.cc +++ b/tools/dexanalyze/dexanalyze.cc @@ -116,12 +116,14 @@ class DexAnalyze { } } - bool ProcessDexFile(const DexFile& dex_file) { + bool ProcessDexFiles(const std::vector>& dex_files) { for (std::unique_ptr& experiment : experiments_) { - experiment->ProcessDexFile(dex_file); + experiment->ProcessDexFiles(dex_files); } - total_size_ += dex_file.Size(); - ++dex_count_; + for (const std::unique_ptr& dex_file : dex_files) { + total_size_ += dex_file->Size(); + } + dex_count_ += dex_files.size(); return true; } @@ -169,18 +171,16 @@ class DexAnalyze { LOG(ERROR) << "OpenAll failed for " + filename << " with " << error_msg << std::endl; return kExitCodeFailedToOpenDex; } - for (std::unique_ptr& dex_file : dex_files) { - if (options.dump_per_input_dex_) { - Analysis current(&options); - if (!current.ProcessDexFile(*dex_file)) { - LOG(ERROR) << "Failed to process " << filename << " with error " << error_msg; - return kExitCodeFailedToProcessDex; - } - LOG(INFO) << "Analysis for " << dex_file->GetLocation() << std::endl; - current.Dump(LOG_STREAM(INFO)); + if (options.dump_per_input_dex_) { + Analysis current(&options); + if (!current.ProcessDexFiles(dex_files)) { + LOG(ERROR) << "Failed to process " << filename << " with error " << error_msg; + return kExitCodeFailedToProcessDex; } - cumulative.ProcessDexFile(*dex_file); + LOG(INFO) << "Analysis for " << filename << std::endl; + current.Dump(LOG_STREAM(INFO)); } + cumulative.ProcessDexFiles(dex_files); } LOG(INFO) << "Cumulative analysis for " << cumulative.dex_count_ << " DEX files" << std::endl; cumulative.Dump(LOG_STREAM(INFO)); diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc index 1a3b89cbc7..244f45bbe6 100644 --- a/tools/dexanalyze/dexanalyze_experiments.cc +++ b/tools/dexanalyze/dexanalyze_experiments.cc @@ -75,86 +75,95 @@ static size_t PrefixLen(const std::string& a, const std::string& b) { return len; } -void AnalyzeDebugInfo::ProcessDexFile(const DexFile& dex_file) { +void Experiment::ProcessDexFiles(const std::vector>& dex_files) { + for (const std::unique_ptr& dex_file : dex_files) { + ProcessDexFile(*dex_file); + } +} + +void AnalyzeDebugInfo::ProcessDexFiles( + const std::vector>& dex_files) { std::set seen; std::vector counts(256, 0u); std::vector opcode_counts(256, 0u); std::set> unique_non_header; - for (ClassAccessor accessor : dex_file.GetClasses()) { - for (const ClassAccessor::Method& method : accessor.GetMethods()) { - CodeItemDebugInfoAccessor code_item(dex_file, method.GetCodeItem(), method.GetIndex()); - const uint8_t* debug_info = dex_file.GetDebugInfoStream(code_item.DebugInfoOffset()); - if (debug_info != nullptr && seen.insert(debug_info).second) { - const uint8_t* stream = debug_info; - DecodeUnsignedLeb128(&stream); // line_start - uint32_t parameters_size = DecodeUnsignedLeb128(&stream); - for (uint32_t i = 0; i < parameters_size; ++i) { - DecodeUnsignedLeb128P1(&stream); // Parameter name. - } - bool done = false; - const uint8_t* after_header_start = stream; - while (!done) { - const uint8_t* const op_start = stream; - uint8_t opcode = *stream++; - ++opcode_counts[opcode]; - ++total_opcode_bytes_; - switch (opcode) { - case DexFile::DBG_END_SEQUENCE: - ++total_end_seq_bytes_; - done = true; - break; - case DexFile::DBG_ADVANCE_PC: - DecodeUnsignedLeb128(&stream); // addr_diff - total_advance_pc_bytes_ += stream - op_start; - break; - case DexFile::DBG_ADVANCE_LINE: - DecodeSignedLeb128(&stream); // line_diff - total_advance_line_bytes_ += stream - op_start; - break; - case DexFile::DBG_START_LOCAL: - DecodeUnsignedLeb128(&stream); // register_num - DecodeUnsignedLeb128P1(&stream); // name_idx - DecodeUnsignedLeb128P1(&stream); // type_idx - total_start_local_bytes_ += stream - op_start; - break; - case DexFile::DBG_START_LOCAL_EXTENDED: - DecodeUnsignedLeb128(&stream); // register_num - DecodeUnsignedLeb128P1(&stream); // name_idx - DecodeUnsignedLeb128P1(&stream); // type_idx - DecodeUnsignedLeb128P1(&stream); // sig_idx - total_start_local_extended_bytes_ += stream - op_start; - break; - case DexFile::DBG_END_LOCAL: - DecodeUnsignedLeb128(&stream); // register_num - total_end_local_bytes_ += stream - op_start; - break; - case DexFile::DBG_RESTART_LOCAL: - DecodeUnsignedLeb128(&stream); // register_num - total_restart_local_bytes_ += stream - op_start; - break; - case DexFile::DBG_SET_PROLOGUE_END: - case DexFile::DBG_SET_EPILOGUE_BEGIN: - total_epilogue_bytes_ += stream - op_start; - break; - case DexFile::DBG_SET_FILE: { - DecodeUnsignedLeb128P1(&stream); // name_idx - total_set_file_bytes_ += stream - op_start; - break; - } - default: { - total_other_bytes_ += stream - op_start; - break; + for (const std::unique_ptr& dex_file : dex_files) { + for (ClassAccessor accessor : dex_file->GetClasses()) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + CodeItemDebugInfoAccessor code_item(*dex_file, method.GetCodeItem(), method.GetIndex()); + const uint8_t* debug_info = dex_file->GetDebugInfoStream(code_item.DebugInfoOffset()); + if (debug_info != nullptr && seen.insert(debug_info).second) { + const uint8_t* stream = debug_info; + DecodeUnsignedLeb128(&stream); // line_start + uint32_t parameters_size = DecodeUnsignedLeb128(&stream); + for (uint32_t i = 0; i < parameters_size; ++i) { + DecodeUnsignedLeb128P1(&stream); // Parameter name. + } + bool done = false; + const uint8_t* after_header_start = stream; + while (!done) { + const uint8_t* const op_start = stream; + uint8_t opcode = *stream++; + ++opcode_counts[opcode]; + ++total_opcode_bytes_; + switch (opcode) { + case DexFile::DBG_END_SEQUENCE: + ++total_end_seq_bytes_; + done = true; + break; + case DexFile::DBG_ADVANCE_PC: + DecodeUnsignedLeb128(&stream); // addr_diff + total_advance_pc_bytes_ += stream - op_start; + break; + case DexFile::DBG_ADVANCE_LINE: + DecodeSignedLeb128(&stream); // line_diff + total_advance_line_bytes_ += stream - op_start; + break; + case DexFile::DBG_START_LOCAL: + DecodeUnsignedLeb128(&stream); // register_num + DecodeUnsignedLeb128P1(&stream); // name_idx + DecodeUnsignedLeb128P1(&stream); // type_idx + total_start_local_bytes_ += stream - op_start; + break; + case DexFile::DBG_START_LOCAL_EXTENDED: + DecodeUnsignedLeb128(&stream); // register_num + DecodeUnsignedLeb128P1(&stream); // name_idx + DecodeUnsignedLeb128P1(&stream); // type_idx + DecodeUnsignedLeb128P1(&stream); // sig_idx + total_start_local_extended_bytes_ += stream - op_start; + break; + case DexFile::DBG_END_LOCAL: + DecodeUnsignedLeb128(&stream); // register_num + total_end_local_bytes_ += stream - op_start; + break; + case DexFile::DBG_RESTART_LOCAL: + DecodeUnsignedLeb128(&stream); // register_num + total_restart_local_bytes_ += stream - op_start; + break; + case DexFile::DBG_SET_PROLOGUE_END: + case DexFile::DBG_SET_EPILOGUE_BEGIN: + total_epilogue_bytes_ += stream - op_start; + break; + case DexFile::DBG_SET_FILE: { + DecodeUnsignedLeb128P1(&stream); // name_idx + total_set_file_bytes_ += stream - op_start; + break; + } + default: { + total_other_bytes_ += stream - op_start; + break; + } } } - } - const size_t bytes = stream - debug_info; - total_bytes_ += bytes; - total_non_header_bytes_ += stream - after_header_start; - if (unique_non_header.insert(std::vector(after_header_start, stream)).second) { - total_unique_non_header_bytes_ += stream - after_header_start; - } - for (size_t i = 0; i < bytes; ++i) { - ++counts[debug_info[i]]; + const size_t bytes = stream - debug_info; + total_bytes_ += bytes; + total_non_header_bytes_ += stream - after_header_start; + if (unique_non_header.insert(std::vector(after_header_start, stream)).second) { + total_unique_non_header_bytes_ += stream - after_header_start; + } + for (size_t i = 0; i < bytes; ++i) { + ++counts[debug_info[i]]; + } } } } diff --git a/tools/dexanalyze/dexanalyze_experiments.h b/tools/dexanalyze/dexanalyze_experiments.h index a2621c85ca..2be53d6216 100644 --- a/tools/dexanalyze/dexanalyze_experiments.h +++ b/tools/dexanalyze/dexanalyze_experiments.h @@ -18,7 +18,9 @@ #define ART_TOOLS_DEXANALYZE_DEXANALYZE_EXPERIMENTS_H_ #include +#include #include +#include namespace art { @@ -30,7 +32,8 @@ std::string Percent(uint64_t value, uint64_t max); class Experiment { public: virtual ~Experiment() {} - virtual void ProcessDexFile(const DexFile& dex_file) = 0; + virtual void ProcessDexFiles(const std::vector>& dex_files); + virtual void ProcessDexFile(const DexFile&) {} virtual void Dump(std::ostream& os, uint64_t total_size) const = 0; }; @@ -54,7 +57,7 @@ class AnalyzeStrings : public Experiment { // Analyze debug info sizes. class AnalyzeDebugInfo : public Experiment { public: - void ProcessDexFile(const DexFile& dex_file); + void ProcessDexFiles(const std::vector>& dex_files); void Dump(std::ostream& os, uint64_t total_size) const; private: @@ -112,7 +115,7 @@ class CountDexIndices : public Experiment { size_t total_super_ = 0; }; -// Measure various code metrics including args per invoke-virtual, fill/spill move paterns. +// Measure various code metrics including args per invoke-virtual, fill/spill move patterns. class CodeMetrics : public Experiment { public: void ProcessDexFile(const DexFile& dex_file); -- GitLab From 5f93710283ded435c2be2df16a8e04ab6f3ff5d7 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Sat, 2 Jun 2018 10:24:25 +0100 Subject: [PATCH 519/749] Fix asan build: Fix compare function in BitTable. The underlying memory does not have to be consecutive, so we need to use proper iterator in the equal method. Test: ./art/test/testrunner/run_build_test_target.py art-asan Change-Id: Ice5c29cf8fdc9e6d5f86b66b8bbc376cfe7b4ded --- libartbase/base/bit_table.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libartbase/base/bit_table.h b/libartbase/base/bit_table.h index 8cfd044703..bf3d3b032c 100644 --- a/libartbase/base/bit_table.h +++ b/libartbase/base/bit_table.h @@ -194,7 +194,7 @@ class BitTableBuilder { if (count <= size() - index && std::equal(values, values + count, - &rows_[index], + rows_.begin() + index, [](const T& lhs, const T& rhs) { return memcmp(&lhs, &rhs, sizeof(T)) == 0; })) { -- GitLab From cc7e20f9ec7b4a7a57f7196e5e8be67a727f21d3 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Sat, 2 Jun 2018 05:37:39 +0000 Subject: [PATCH 520/749] Revert "Move runtime/ to ClassAccessor" Seems to cause 'atest CtsInlineMockingTestCases' and other tests to fail due to sending agents dex files with hiddenapi flags still present. This reverts commit 2649ecf6c59a29262556aa356fbf894d49df8fe7. Reason for revert: Seems to be causing sysui test failures, maybe Bug: 77709234 Bug: 79758018 Bug: 91962648 Test: Tree-Hugger Change-Id: I2cab5d0d58808dd8beb38400d2811307f26e1021 --- libdexfile/dex/class_accessor-inl.h | 101 +++----- libdexfile/dex/class_accessor.h | 78 ++---- libdexfile/dex/class_accessor_test.cc | 10 - libdexfile/dex/dex_file.cc | 17 +- libdexfile/dex/dex_file.h | 4 +- openjdkjvmti/fixed_up_dex_file.cc | 15 +- runtime/art_method.cc | 23 +- runtime/class_linker.cc | 290 +++++++++++----------- runtime/class_linker.h | 13 +- runtime/native/dalvik_system_VMRuntime.cc | 29 ++- runtime/vdex_file.cc | 40 +-- runtime/verifier/method_verifier.cc | 164 ++++++++---- runtime/verifier/method_verifier.h | 17 ++ 13 files changed, 417 insertions(+), 384 deletions(-) diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h index 3bb9e93e5a..49ca98d47f 100644 --- a/libdexfile/dex/class_accessor-inl.h +++ b/libdexfile/dex/class_accessor-inl.h @@ -37,26 +37,30 @@ inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const DexFile::Clas num_direct_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u), num_virtual_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u) {} -inline void ClassAccessor::Method::Read() { - index_ += DecodeUnsignedLeb128(&ptr_pos_); - access_flags_ = DecodeUnsignedLeb128(&ptr_pos_); - code_off_ = DecodeUnsignedLeb128(&ptr_pos_); +inline const uint8_t* ClassAccessor::Method::Read(const uint8_t* ptr) { + index_ += DecodeUnsignedLeb128(&ptr); + access_flags_ = DecodeUnsignedLeb128(&ptr); + code_off_ = DecodeUnsignedLeb128(&ptr); + return ptr; } -inline void ClassAccessor::Field::Read() { - index_ += DecodeUnsignedLeb128(&ptr_pos_); - access_flags_ = DecodeUnsignedLeb128(&ptr_pos_); +inline const uint8_t* ClassAccessor::Field::Read(const uint8_t* ptr) { + index_ += DecodeUnsignedLeb128(&ptr); + access_flags_ = DecodeUnsignedLeb128(&ptr); + return ptr; } template -inline void ClassAccessor::VisitMembers(size_t count, - const Visitor& visitor, - DataType* data) const { +inline const uint8_t* ClassAccessor::VisitMembers(size_t count, + const Visitor& visitor, + const uint8_t* ptr, + DataType* data) const { DCHECK(data != nullptr); for ( ; count != 0; --count) { - data->Read(); + ptr = data->Read(ptr); visitor(*data); } + return ptr; } template > - ClassAccessor::GetFieldsInternal(size_t count) const { - return { DataIterator(dex_file_, 0u, num_static_fields_, count, ptr_pos_), - DataIterator(dex_file_, count, num_static_fields_, count, ptr_pos_) }; -} - -// Return an iteration range for the first methods. -inline IterationRange> - ClassAccessor::GetMethodsInternal(size_t count) const { - // Skip over the fields. - Field field(dex_file_, ptr_pos_); - VisitMembers(NumFields(), VoidFunctor(), &field); - // Return the iterator pair. - return { DataIterator(dex_file_, 0u, num_direct_methods_, count, field.ptr_pos_), - DataIterator(dex_file_, count, num_direct_methods_, count, field.ptr_pos_) }; -} - inline IterationRange> ClassAccessor::GetFields() const { - return GetFieldsInternal(num_static_fields_ + num_instance_fields_); -} - -inline IterationRange> - ClassAccessor::GetStaticFields() const { - return GetFieldsInternal(num_static_fields_); -} - - -inline IterationRange> - ClassAccessor::GetInstanceFields() const { - IterationRange> fields = GetFields(); - // Skip the static fields. - return { std::next(fields.begin(), NumStaticFields()), fields.end() }; + const uint32_t limit = num_static_fields_ + num_instance_fields_; + return { DataIterator(dex_file_, 0u, num_static_fields_, limit, ptr_pos_), + DataIterator(dex_file_, limit, num_static_fields_, limit, ptr_pos_) }; } inline IterationRange> ClassAccessor::GetMethods() const { - return GetMethodsInternal(NumMethods()); -} - -inline IterationRange> - ClassAccessor::GetDirectMethods() const { - return GetMethodsInternal(NumDirectMethods()); -} - -inline IterationRange> - ClassAccessor::GetVirtualMethods() const { - IterationRange> methods = GetMethods(); - // Skip the direct fields. - return { std::next(methods.begin(), NumDirectMethods()), methods.end() }; -} - -inline void ClassAccessor::Field::UnHideAccessFlags() const { - DexFile::UnHideAccessFlags(const_cast(ptr_pos_), GetAccessFlags(), /*is_method*/ false); -} - -inline void ClassAccessor::Method::UnHideAccessFlags() const { - DexFile::UnHideAccessFlags(const_cast(ptr_pos_), GetAccessFlags(), /*is_method*/ true); + // Skip over the fields. + Field field(dex_file_); + const size_t skip_count = num_static_fields_ + num_instance_fields_; + const uint8_t* ptr_pos = VisitMembers(skip_count, VoidFunctor(), ptr_pos_, &field); + // Return the iterator pair for all the methods. + const uint32_t limit = num_direct_methods_ + num_virtual_methods_; + return { DataIterator(dex_file_, 0u, num_direct_methods_, limit, ptr_pos), + DataIterator(dex_file_, limit, num_direct_methods_, limit, ptr_pos) }; } } // namespace art diff --git a/libdexfile/dex/class_accessor.h b/libdexfile/dex/class_accessor.h index 4f0fd32e31..dda6e1c1a6 100644 --- a/libdexfile/dex/class_accessor.h +++ b/libdexfile/dex/class_accessor.h @@ -20,7 +20,6 @@ #include "base/utils.h" #include "code_item_accessors.h" #include "dex_file.h" -#include "hidden_api_access_flags.h" #include "invoke_type.h" #include "method_reference.h" #include "modifiers.h" @@ -34,18 +33,12 @@ class ClassAccessor { private: class BaseItem { public: - explicit BaseItem(const uint8_t* ptr_pos) : ptr_pos_(ptr_pos) {} - uint32_t GetIndex() const { return index_; } uint32_t GetAccessFlags() const { - return HiddenApiAccessFlags::RemoveFromDex(access_flags_); - } - - HiddenApiAccessFlags::ApiList DecodeHiddenAccessFlags() const { - return HiddenApiAccessFlags::DecodeFromDex(access_flags_); + return access_flags_; } bool IsFinal() const { @@ -53,8 +46,6 @@ class ClassAccessor { } protected: - // Internal data pointer for reading. - const uint8_t* ptr_pos_ = nullptr; uint32_t index_ = 0u; uint32_t access_flags_ = 0u; }; @@ -85,18 +76,13 @@ class ClassAccessor { return is_static_or_direct_; } - // Unhide the hidden API access flags at the iterator position. TODO: Deprecate. - void UnHideAccessFlags() const; - private: explicit Method(const DexFile& dex_file, - const uint8_t* ptr_pos, bool is_static_or_direct = true) - : BaseItem(ptr_pos), - dex_file_(dex_file), + : dex_file_(dex_file), is_static_or_direct_(is_static_or_direct) {} - void Read(); + const uint8_t* Read(const uint8_t* ptr); InvokeType GetDirectMethodInvokeType() const { return (GetAccessFlags() & kAccStatic) != 0 ? kStatic : kDirect; @@ -113,7 +99,6 @@ class ClassAccessor { } } - // Move to virtual method section. void NextSection() { DCHECK(is_static_or_direct_) << "Already in the virtual methods section"; is_static_or_direct_ = false; @@ -130,31 +115,20 @@ class ClassAccessor { // A decoded version of the field of a class_data_item. class Field : public BaseItem { public: - explicit Field(const DexFile& dex_file, - const uint8_t* ptr_pos) : BaseItem(ptr_pos), dex_file_(dex_file) {} + explicit Field(const DexFile& dex_file) : dex_file_(dex_file) {} const DexFile& GetDexFile() const { return dex_file_; } - bool IsStatic() const { - return is_static_; - } - - // Unhide the hidden API access flags at the iterator position. TODO: Deprecate. - void UnHideAccessFlags() const; - private: - void Read(); + const uint8_t* Read(const uint8_t* ptr); - // Move to instance fields section. void NextSection() { index_ = 0u; - is_static_ = false; } const DexFile& dex_file_; - bool is_static_ = true; friend class ClassAccessor; }; @@ -170,10 +144,11 @@ class ClassAccessor { uint32_t partition_pos, uint32_t iterator_end, const uint8_t* ptr_pos) - : data_(dex_file, ptr_pos), + : data_(dex_file), position_(position), partition_pos_(partition_pos), - iterator_end_(iterator_end) { + iterator_end_(iterator_end), + ptr_pos_(ptr_pos) { ReadData(); } @@ -230,7 +205,8 @@ class ClassAccessor { if (position_ == partition_pos_) { data_.NextSection(); } - data_.Read(); + DCHECK(ptr_pos_ != nullptr); + ptr_pos_ = data_.Read(ptr_pos_); } } @@ -241,6 +217,8 @@ class ClassAccessor { const uint32_t partition_pos_; // At iterator_end_, the iterator is no longer valid. const uint32_t iterator_end_; + // Internal data pointer. + const uint8_t* ptr_pos_; }; // Not explicit specifically for range-based loops. @@ -274,21 +252,9 @@ class ClassAccessor { // Return the iteration range for all the fields. IterationRange> GetFields() const; - // Return the iteration range for all the static fields. - IterationRange> GetStaticFields() const; - - // Return the iteration range for all the instance fields. - IterationRange> GetInstanceFields() const; - // Return the iteration range for all the methods. IterationRange> GetMethods() const; - // Return the iteration range for the direct methods. - IterationRange> GetDirectMethods() const; - - // Return the iteration range for the virtual methods. - IterationRange> GetVirtualMethods() const; - uint32_t NumStaticFields() const { return num_static_fields_; } @@ -297,10 +263,6 @@ class ClassAccessor { return num_instance_fields_; } - uint32_t NumFields() const { - return NumStaticFields() + NumInstanceFields(); - } - uint32_t NumDirectMethods() const { return num_direct_methods_; } @@ -323,22 +285,14 @@ class ClassAccessor { return dex_file_; } - bool HasClassData() const { - return ptr_pos_ != nullptr; - } - protected: // Template visitor to reduce copy paste for visiting elements. // No thread safety analysis since the visitor may require capabilities. template - void VisitMembers(size_t count, const Visitor& visitor, DataType* data) const - NO_THREAD_SAFETY_ANALYSIS; - - // Return an iteration range for the first fields. - IterationRange> GetFieldsInternal(size_t count) const; - - // Return an iteration range for the first methods. - IterationRange> GetMethodsInternal(size_t count) const; + const uint8_t* VisitMembers(size_t count, + const Visitor& visitor, + const uint8_t* ptr, + DataType* data) const NO_THREAD_SAFETY_ANALYSIS; const DexFile& dex_file_; const dex::TypeIndex descriptor_index_ = {}; diff --git a/libdexfile/dex/class_accessor_test.cc b/libdexfile/dex/class_accessor_test.cc index d0533c1811..95380d8140 100644 --- a/libdexfile/dex/class_accessor_test.cc +++ b/libdexfile/dex/class_accessor_test.cc @@ -38,27 +38,18 @@ TEST_F(ClassAccessorTest, TestVisiting) { auto fields = accessor.GetFields(); auto method_it = methods.begin(); auto field_it = fields.begin(); - auto instance_fields = accessor.GetInstanceFields(); - auto instance_field_it = instance_fields.begin(); accessor.VisitFieldsAndMethods( // Static fields. [&](const ClassAccessor::Field& field) { - EXPECT_TRUE(field.IsStatic()); - EXPECT_TRUE(field_it->IsStatic()); EXPECT_EQ(field.GetIndex(), field_it->GetIndex()); EXPECT_EQ(field.GetAccessFlags(), field_it->GetAccessFlags()); ++field_it; }, // Instance fields. [&](const ClassAccessor::Field& field) { - EXPECT_FALSE(field.IsStatic()); - EXPECT_FALSE(field_it->IsStatic()); EXPECT_EQ(field.GetIndex(), field_it->GetIndex()); EXPECT_EQ(field.GetAccessFlags(), field_it->GetAccessFlags()); - EXPECT_EQ(field.GetIndex(), instance_field_it->GetIndex()); - EXPECT_EQ(field.GetAccessFlags(), instance_field_it->GetAccessFlags()); ++field_it; - ++instance_field_it; }, // Direct methods. [&](const ClassAccessor::Method& method) { @@ -80,7 +71,6 @@ TEST_F(ClassAccessorTest, TestVisiting) { }); ASSERT_TRUE(field_it == fields.end()); ASSERT_TRUE(method_it == methods.end()); - ASSERT_TRUE(instance_field_it == instance_fields.end()); } EXPECT_EQ(class_def_idx, dex_file->NumClassDefs()); } diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc index f570158dfb..9de260c862 100644 --- a/libdexfile/dex/dex_file.cc +++ b/libdexfile/dex/dex_file.cc @@ -45,18 +45,19 @@ static_assert(std::is_trivially_copyable::value, "StringIndex static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wrong"); static_assert(std::is_trivially_copyable::value, "TypeIndex not trivial"); -void DexFile::UnHideAccessFlags(uint8_t* data_ptr, - uint32_t new_access_flags, - bool is_method) { +void DexFile::UnHideAccessFlags(ClassDataItemIterator& class_it) { + uint8_t* data = const_cast(class_it.DataPointer()); + uint32_t new_flag = class_it.GetMemberAccessFlags(); + bool is_method = class_it.IsAtMethod(); // Go back 1 uleb to start. - data_ptr = ReverseSearchUnsignedLeb128(data_ptr); + data = ReverseSearchUnsignedLeb128(data); if (is_method) { // Methods have another uleb field before the access flags - data_ptr = ReverseSearchUnsignedLeb128(data_ptr); + data = ReverseSearchUnsignedLeb128(data); } - DCHECK_EQ(HiddenApiAccessFlags::RemoveFromDex(DecodeUnsignedLeb128WithoutMovingCursor(data_ptr)), - new_access_flags); - UpdateUnsignedLeb128(data_ptr, new_access_flags); + DCHECK_EQ(HiddenApiAccessFlags::RemoveFromDex(DecodeUnsignedLeb128WithoutMovingCursor(data)), + new_flag); + UpdateUnsignedLeb128(data, new_flag); } uint32_t DexFile::CalculateChecksum() const { diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index ed219808d2..f1f8b505bd 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -1010,8 +1010,8 @@ class DexFile { return container_.get(); } - // Changes the dex class data pointed to by data_ptr it to not have any hiddenapi flags. - static void UnHideAccessFlags(uint8_t* data_ptr, uint32_t new_access_flags, bool is_method); + // Changes the dex file pointed to by class_it to not have any hiddenapi flags. + static void UnHideAccessFlags(ClassDataItemIterator& class_it); inline IterationRange GetClasses() const; diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc index a660fb56c4..fcbafe7e71 100644 --- a/openjdkjvmti/fixed_up_dex_file.cc +++ b/openjdkjvmti/fixed_up_dex_file.cc @@ -31,7 +31,6 @@ #include "base/leb128.h" #include "fixed_up_dex_file.h" -#include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "dex/dex_file_verifier.h" @@ -52,12 +51,14 @@ static void RecomputeDexChecksum(art::DexFile* dex_file) { } static void UnhideApis(const art::DexFile& target_dex_file) { - for (art::ClassAccessor accessor : target_dex_file.GetClasses()) { - for (const art::ClassAccessor::Field& field : accessor.GetFields()) { - field.UnHideAccessFlags(); - } - for (const art::ClassAccessor::Method& method : accessor.GetMethods()) { - method.UnHideAccessFlags(); + for (uint32_t i = 0; i < target_dex_file.NumClassDefs(); ++i) { + const uint8_t* class_data = target_dex_file.GetClassData(target_dex_file.GetClassDef(i)); + if (class_data != nullptr) { + for (art::ClassDataItemIterator class_it(target_dex_file, class_data); + class_it.HasNext(); + class_it.Next()) { + art::DexFile::UnHideAccessFlags(class_it); + } } } } diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 4e9f3c52e2..151c36f3bc 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -26,7 +26,6 @@ #include "class_linker-inl.h" #include "class_root.h" #include "debugger.h" -#include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_exception_helpers.h" @@ -435,14 +434,28 @@ bool ArtMethod::IsPolymorphicSignature() { static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx) { - ClassAccessor accessor(dex_file, dex_file.GetClassDef(class_def_idx)); - uint32_t class_def_method_index = 0u; - for (const ClassAccessor::Method& method : accessor.GetMethods()) { - if (method.GetIndex() == method_idx) { + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx); + const uint8_t* class_data = dex_file.GetClassData(class_def); + CHECK(class_data != nullptr); + ClassDataItemIterator it(dex_file, class_data); + it.SkipAllFields(); + // Process methods + size_t class_def_method_index = 0; + while (it.HasNextDirectMethod()) { + if (it.GetMemberIndex() == method_idx) { return class_def_method_index; } class_def_method_index++; + it.Next(); } + while (it.HasNextVirtualMethod()) { + if (it.GetMemberIndex() == method_idx) { + return class_def_method_index; + } + class_def_method_index++; + it.Next(); + } + DCHECK(!it.HasNext()); LOG(FATAL) << "Failed to find method index " << method_idx << " in " << dex_file.GetLocation(); UNREACHABLE(); } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index d6ac3bad52..095272394a 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -56,7 +56,6 @@ #include "compiler_callbacks.h" #include "debug_print.h" #include "debugger.h" -#include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_exception_helpers.h" @@ -2735,50 +2734,52 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, uint32_t ClassLinker::SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def) { + const uint8_t* class_data = dex_file.GetClassData(dex_class_def); size_t num_ref = 0; size_t num_8 = 0; size_t num_16 = 0; size_t num_32 = 0; size_t num_64 = 0; - ClassAccessor accessor(dex_file, dex_class_def); - // We allow duplicate definitions of the same field in a class_data_item - // but ignore the repeated indexes here, b/21868015. - uint32_t last_field_idx = dex::kDexNoIndex; - for (const ClassAccessor::Field& field : accessor.GetStaticFields()) { - uint32_t field_idx = field.GetIndex(); - // Ordering enforced by DexFileVerifier. - DCHECK(last_field_idx == dex::kDexNoIndex || last_field_idx <= field_idx); - if (UNLIKELY(field_idx == last_field_idx)) { - continue; - } - last_field_idx = field_idx; - const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); - const char* descriptor = dex_file.GetFieldTypeDescriptor(field_id); - char c = descriptor[0]; - switch (c) { - case 'L': - case '[': - num_ref++; - break; - case 'J': - case 'D': - num_64++; - break; - case 'I': - case 'F': - num_32++; - break; - case 'S': - case 'C': - num_16++; - break; - case 'B': - case 'Z': - num_8++; - break; - default: - LOG(FATAL) << "Unknown descriptor: " << c; - UNREACHABLE(); + if (class_data != nullptr) { + // We allow duplicate definitions of the same field in a class_data_item + // but ignore the repeated indexes here, b/21868015. + uint32_t last_field_idx = dex::kDexNoIndex; + for (ClassDataItemIterator it(dex_file, class_data); it.HasNextStaticField(); it.Next()) { + uint32_t field_idx = it.GetMemberIndex(); + // Ordering enforced by DexFileVerifier. + DCHECK(last_field_idx == dex::kDexNoIndex || last_field_idx <= field_idx); + if (UNLIKELY(field_idx == last_field_idx)) { + continue; + } + last_field_idx = field_idx; + const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); + const char* descriptor = dex_file.GetFieldTypeDescriptor(field_id); + char c = descriptor[0]; + switch (c) { + case 'L': + case '[': + num_ref++; + break; + case 'J': + case 'D': + num_64++; + break; + case 'I': + case 'F': + num_32++; + break; + case 'S': + case 'C': + num_16++; + break; + case 'B': + case 'Z': + num_8++; + break; + default: + LOG(FATAL) << "Unknown descriptor: " << c; + UNREACHABLE(); + } } } return mirror::Class::ComputeClassSize(false, @@ -2876,15 +2877,17 @@ void ClassLinker::FixupStaticTrampolines(ObjPtr klass) { const DexFile& dex_file = klass->GetDexFile(); const DexFile::ClassDef* dex_class_def = klass->GetClassDef(); CHECK(dex_class_def != nullptr); - ClassAccessor accessor(dex_file, *dex_class_def); + const uint8_t* class_data = dex_file.GetClassData(*dex_class_def); // There should always be class data if there were direct methods. - CHECK(accessor.HasClassData()) << klass->PrettyDescriptor(); + CHECK(class_data != nullptr) << klass->PrettyDescriptor(); + ClassDataItemIterator it(dex_file, class_data); + it.SkipAllFields(); bool has_oat_class; OatFile::OatClass oat_class = OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class); // Link the code of methods skipped by LinkCode. - for (size_t method_index = 0; method_index < accessor.NumDirectMethods(); ++method_index) { + for (size_t method_index = 0; it.HasNextDirectMethod(); ++method_index, it.Next()) { ArtMethod* method = klass->GetDirectMethod(method_index, image_pointer_size_); if (!method->IsStatic()) { // Only update static methods. @@ -2993,6 +2996,17 @@ void ClassLinker::SetupClass(const DexFile& dex_file, klass->SetDexTypeIndex(dex_class_def.class_idx_); } +void ClassLinker::LoadClass(Thread* self, + const DexFile& dex_file, + const DexFile::ClassDef& dex_class_def, + Handle klass) { + const uint8_t* class_data = dex_file.GetClassData(dex_class_def); + if (class_data == nullptr) { + return; // no fields or methods - for example a marker interface + } + LoadClassMembers(self, dex_file, class_data, klass); +} + LengthPrefixedArray* ClassLinker::AllocArtFieldArray(Thread* self, LinearAlloc* allocator, size_t length) { @@ -3051,15 +3065,10 @@ LinearAlloc* ClassLinker::GetOrCreateAllocatorForClassLoader(ObjPtr klass) { - ClassAccessor accessor(dex_file, dex_class_def); - if (!accessor.HasClassData()) { - return; - } - Runtime* const runtime = Runtime::Current(); +void ClassLinker::LoadClassMembers(Thread* self, + const DexFile& dex_file, + const uint8_t* class_data, + Handle klass) { { // Note: We cannot have thread suspension until the field and method arrays are setup or else // Class::VisitFieldRoots may miss some fields or methods. @@ -3068,79 +3077,45 @@ void ClassLinker::LoadClass(Thread* self, // We allow duplicate definitions of the same field in a class_data_item // but ignore the repeated indexes here, b/21868015. LinearAlloc* const allocator = GetAllocatorForClassLoader(klass->GetClassLoader()); + ClassDataItemIterator it(dex_file, class_data); LengthPrefixedArray* sfields = AllocArtFieldArray(self, allocator, - accessor.NumStaticFields()); + it.NumStaticFields()); + size_t num_sfields = 0; + uint32_t last_field_idx = 0u; + for (; it.HasNextStaticField(); it.Next()) { + uint32_t field_idx = it.GetMemberIndex(); + DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier. + if (num_sfields == 0 || LIKELY(field_idx > last_field_idx)) { + DCHECK_LT(num_sfields, it.NumStaticFields()); + LoadField(it, klass, &sfields->At(num_sfields)); + ++num_sfields; + last_field_idx = field_idx; + } + } + + // Load instance fields. LengthPrefixedArray* ifields = AllocArtFieldArray(self, allocator, - accessor.NumInstanceFields()); - size_t num_sfields = 0u; + it.NumInstanceFields()); size_t num_ifields = 0u; - uint32_t last_static_field_idx = 0u; - uint32_t last_instance_field_idx = 0u; - - // Methods - bool has_oat_class = false; - const OatFile::OatClass oat_class = (runtime->IsStarted() && !runtime->IsAotCompiler()) - ? OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class) - : OatFile::OatClass::Invalid(); - const OatFile::OatClass* oat_class_ptr = has_oat_class ? &oat_class : nullptr; - klass->SetMethodsPtr( - AllocArtMethodArray(self, allocator, accessor.NumMethods()), - accessor.NumDirectMethods(), - accessor.NumVirtualMethods()); - size_t class_def_method_index = 0; - uint32_t last_dex_method_index = dex::kDexNoIndex; - size_t last_class_def_method_index = 0; + last_field_idx = 0u; + for (; it.HasNextInstanceField(); it.Next()) { + uint32_t field_idx = it.GetMemberIndex(); + DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier. + if (num_ifields == 0 || LIKELY(field_idx > last_field_idx)) { + DCHECK_LT(num_ifields, it.NumInstanceFields()); + LoadField(it, klass, &ifields->At(num_ifields)); + ++num_ifields; + last_field_idx = field_idx; + } + } - // Use the visitor since the ranged based loops are bit slower from seeking. Seeking to the - // methods needs to decode all of the fields. - accessor.VisitFieldsAndMethods([&]( - const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { - uint32_t field_idx = field.GetIndex(); - DCHECK_GE(field_idx, last_static_field_idx); // Ordering enforced by DexFileVerifier. - if (num_sfields == 0 || LIKELY(field_idx > last_static_field_idx)) { - LoadField(field, klass, &sfields->At(num_sfields)); - ++num_sfields; - last_static_field_idx = field_idx; - } - }, [&](const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { - uint32_t field_idx = field.GetIndex(); - DCHECK_GE(field_idx, last_instance_field_idx); // Ordering enforced by DexFileVerifier. - if (num_ifields == 0 || LIKELY(field_idx > last_instance_field_idx)) { - LoadField(field, klass, &ifields->At(num_ifields)); - ++num_ifields; - last_instance_field_idx = field_idx; - } - }, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* art_method = klass->GetDirectMethodUnchecked(class_def_method_index, - image_pointer_size_); - LoadMethod(dex_file, method, klass, art_method); - LinkCode(this, art_method, oat_class_ptr, class_def_method_index); - uint32_t it_method_index = method.GetIndex(); - if (last_dex_method_index == it_method_index) { - // duplicate case - art_method->SetMethodIndex(last_class_def_method_index); - } else { - art_method->SetMethodIndex(class_def_method_index); - last_dex_method_index = it_method_index; - last_class_def_method_index = class_def_method_index; - } - ++class_def_method_index; - }, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* art_method = klass->GetVirtualMethodUnchecked( - class_def_method_index - accessor.NumDirectMethods(), - image_pointer_size_); - LoadMethod(dex_file, method, klass, art_method); - LinkCode(this, art_method, oat_class_ptr, class_def_method_index); - ++class_def_method_index; - }); - - if (UNLIKELY(num_ifields + num_sfields != accessor.NumFields())) { + if (UNLIKELY(num_sfields != it.NumStaticFields()) || + UNLIKELY(num_ifields != it.NumInstanceFields())) { LOG(WARNING) << "Duplicate fields in class " << klass->PrettyDescriptor() - << " (unique static fields: " << num_sfields << "/" << accessor.NumStaticFields() - << ", unique instance fields: " << num_ifields << "/" << accessor.NumInstanceFields() - << ")"; + << " (unique static fields: " << num_sfields << "/" << it.NumStaticFields() + << ", unique instance fields: " << num_ifields << "/" << it.NumInstanceFields() << ")"; // NOTE: Not shrinking the over-allocated sfields/ifields, just setting size. if (sfields != nullptr) { sfields->SetSize(num_sfields); @@ -3154,49 +3129,87 @@ void ClassLinker::LoadClass(Thread* self, DCHECK_EQ(klass->NumStaticFields(), num_sfields); klass->SetIFieldsPtr(ifields); DCHECK_EQ(klass->NumInstanceFields(), num_ifields); + // Load methods. + bool has_oat_class = false; + const OatFile::OatClass oat_class = + (Runtime::Current()->IsStarted() && !Runtime::Current()->IsAotCompiler()) + ? OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class) + : OatFile::OatClass::Invalid(); + const OatFile::OatClass* oat_class_ptr = has_oat_class ? &oat_class : nullptr; + klass->SetMethodsPtr( + AllocArtMethodArray(self, allocator, it.NumDirectMethods() + it.NumVirtualMethods()), + it.NumDirectMethods(), + it.NumVirtualMethods()); + size_t class_def_method_index = 0; + uint32_t last_dex_method_index = dex::kDexNoIndex; + size_t last_class_def_method_index = 0; + // TODO These should really use the iterators. + for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) { + ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_); + LoadMethod(dex_file, it, klass, method); + LinkCode(this, method, oat_class_ptr, class_def_method_index); + uint32_t it_method_index = it.GetMemberIndex(); + if (last_dex_method_index == it_method_index) { + // duplicate case + method->SetMethodIndex(last_class_def_method_index); + } else { + method->SetMethodIndex(class_def_method_index); + last_dex_method_index = it_method_index; + last_class_def_method_index = class_def_method_index; + } + class_def_method_index++; + } + for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) { + ArtMethod* method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_); + LoadMethod(dex_file, it, klass, method); + DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i); + LinkCode(this, method, oat_class_ptr, class_def_method_index); + class_def_method_index++; + } + DCHECK(!it.HasNext()); } // Ensure that the card is marked so that remembered sets pick up native roots. Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(klass.Get()); self->AllowThreadSuspension(); } -void ClassLinker::LoadField(const ClassAccessor::Field& field, +void ClassLinker::LoadField(const ClassDataItemIterator& it, Handle klass, ArtField* dst) { - const uint32_t field_idx = field.GetIndex(); + const uint32_t field_idx = it.GetMemberIndex(); dst->SetDexFieldIndex(field_idx); dst->SetDeclaringClass(klass.Get()); // Get access flags from the DexFile. If this is a boot class path class, // also set its runtime hidden API access flags. - uint32_t access_flags = field.GetAccessFlags(); + uint32_t access_flags = it.GetFieldAccessFlags(); if (klass->IsBootStrapClassLoaded()) { access_flags = - HiddenApiAccessFlags::EncodeForRuntime(access_flags, field.DecodeHiddenAccessFlags()); + HiddenApiAccessFlags::EncodeForRuntime(access_flags, it.DecodeHiddenAccessFlags()); } dst->SetAccessFlags(access_flags); } void ClassLinker::LoadMethod(const DexFile& dex_file, - const ClassAccessor::Method& method, + const ClassDataItemIterator& it, Handle klass, ArtMethod* dst) { - const uint32_t dex_method_idx = method.GetIndex(); + uint32_t dex_method_idx = it.GetMemberIndex(); const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_); ScopedAssertNoThreadSuspension ants("LoadMethod"); dst->SetDexMethodIndex(dex_method_idx); dst->SetDeclaringClass(klass.Get()); - dst->SetCodeItemOffset(method.GetCodeItemOffset()); + dst->SetCodeItemOffset(it.GetMethodCodeItemOffset()); // Get access flags from the DexFile. If this is a boot class path class, // also set its runtime hidden API access flags. - uint32_t access_flags = method.GetAccessFlags(); + uint32_t access_flags = it.GetMethodAccessFlags(); if (klass->IsBootStrapClassLoaded()) { access_flags = - HiddenApiAccessFlags::EncodeForRuntime(access_flags, method.DecodeHiddenAccessFlags()); + HiddenApiAccessFlags::EncodeForRuntime(access_flags, it.DecodeHiddenAccessFlags()); } if (UNLIKELY(strcmp("finalize", method_name) == 0)) { @@ -4759,29 +4772,24 @@ bool ClassLinker::InitializeClass(Thread* self, Handle klass, this, *dex_class_def); const DexFile& dex_file = *dex_cache->GetDexFile(); - + const uint8_t* class_data = dex_file.GetClassData(*dex_class_def); + ClassDataItemIterator field_it(dex_file, class_data); if (value_it.HasNext()) { - ClassAccessor accessor(dex_file, *dex_class_def); + DCHECK(field_it.HasNextStaticField()); CHECK(can_init_statics); - for (const ClassAccessor::Field& field : accessor.GetStaticFields()) { - if (!value_it.HasNext()) { - break; - } - ArtField* art_field = ResolveField(field.GetIndex(), - dex_cache, - class_loader, - /* is_static */ true); + for ( ; value_it.HasNext(); value_it.Next(), field_it.Next()) { + ArtField* field = ResolveField( + field_it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ true); if (Runtime::Current()->IsActiveTransaction()) { - value_it.ReadValueToField(art_field); + value_it.ReadValueToField(field); } else { - value_it.ReadValueToField(art_field); + value_it.ReadValueToField(field); } if (self->IsExceptionPending()) { break; } - value_it.Next(); + DCHECK(!value_it.HasNext() || field_it.HasNextStaticField()); } - DCHECK(self->IsExceptionPending() || !value_it.HasNext()); } } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 1912d21d98..1f94c43408 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -27,7 +27,6 @@ #include "base/enums.h" #include "base/macros.h" #include "base/mutex.h" -#include "dex/class_accessor.h" #include "dex/dex_cache_resolved_classes.h" #include "dex/dex_file.h" #include "dex/dex_file_types.h" @@ -824,14 +823,18 @@ class ClassLinker { const DexFile::ClassDef& dex_class_def, Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); + void LoadClassMembers(Thread* self, + const DexFile& dex_file, + const uint8_t* class_data, + Handle klass) + REQUIRES_SHARED(Locks::mutator_lock_); - void LoadField(const ClassAccessor::Field& field, Handle klass, ArtField* dst) + void LoadField(const ClassDataItemIterator& it, Handle klass, ArtField* dst) REQUIRES_SHARED(Locks::mutator_lock_); void LoadMethod(const DexFile& dex_file, - const ClassAccessor::Method& method, - Handle klass, - ArtMethod* dst) + const ClassDataItemIterator& it, + Handle klass, ArtMethod* dst) REQUIRES_SHARED(Locks::mutator_lock_); void FixupStaticTrampolines(ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index c9deb526c2..6c820190b4 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -32,7 +32,6 @@ extern "C" void android_set_application_target_sdk_version(uint32_t version); #include "class_linker-inl.h" #include "common_throws.h" #include "debugger.h" -#include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_types.h" #include "gc/accounting/card_table-inl.h" @@ -574,12 +573,30 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { } if (kPreloadDexCachesFieldsAndMethods) { - for (ClassAccessor accessor : dex_file->GetClasses()) { - for (const ClassAccessor::Field& field : accessor.GetFields()) { - PreloadDexCachesResolveField(dex_cache, field.GetIndex(), field.IsStatic()); + for (size_t class_def_index = 0; + class_def_index < dex_file->NumClassDefs(); + class_def_index++) { + const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); + const uint8_t* class_data = dex_file->GetClassData(class_def); + if (class_data == nullptr) { + continue; } - for (const ClassAccessor::Method& method : accessor.GetMethods()) { - PreloadDexCachesResolveMethod(dex_cache, method.GetIndex()); + ClassDataItemIterator it(*dex_file, class_data); + for (; it.HasNextStaticField(); it.Next()) { + uint32_t field_idx = it.GetMemberIndex(); + PreloadDexCachesResolveField(dex_cache, field_idx, true); + } + for (; it.HasNextInstanceField(); it.Next()) { + uint32_t field_idx = it.GetMemberIndex(); + PreloadDexCachesResolveField(dex_cache, field_idx, false); + } + for (; it.HasNextDirectMethod(); it.Next()) { + uint32_t method_idx = it.GetMemberIndex(); + PreloadDexCachesResolveMethod(dex_cache, method_idx); + } + for (; it.HasNextVirtualMethod(); it.Next()) { + uint32_t method_idx = it.GetMemberIndex(); + PreloadDexCachesResolveMethod(dex_cache, method_idx); } } } diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index e2f42c937d..838d7f14bc 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -28,7 +28,6 @@ #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "dex/art_dex_file_loader.h" -#include "dex/class_accessor-inl.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "dex/hidden_api_access_flags.h" @@ -284,26 +283,31 @@ void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, std::unordered_set unquickened_code_item; CompactOffsetTable::Accessor accessor(GetQuickenInfoOffsetTable(source_dex_begin, quickening_info)); - for (ClassAccessor class_accessor : target_dex_file.GetClasses()) { - for (const ClassAccessor::Method& method : class_accessor.GetMethods()) { - const DexFile::CodeItem* code_item = method.GetCodeItem(); - if (code_item != nullptr && unquickened_code_item.emplace(code_item).second) { - const uint32_t offset = accessor.GetOffset(method.GetIndex()); - // Offset being 0 means not quickened. - if (offset != 0u) { - ArrayRef quicken_data = GetQuickeningInfoAt(quickening_info, offset); - optimizer::ArtDecompileDEX( - target_dex_file, - *code_item, - quicken_data, - decompile_return_instruction); + for (uint32_t i = 0; i < target_dex_file.NumClassDefs(); ++i) { + const DexFile::ClassDef& class_def = target_dex_file.GetClassDef(i); + const uint8_t* class_data = target_dex_file.GetClassData(class_def); + if (class_data != nullptr) { + for (ClassDataItemIterator class_it(target_dex_file, class_data); + class_it.HasNext(); + class_it.Next()) { + if (class_it.IsAtMethod()) { + const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem(); + if (code_item != nullptr && unquickened_code_item.emplace(code_item).second) { + const uint32_t offset = accessor.GetOffset(class_it.GetMemberIndex()); + // Offset being 0 means not quickened. + if (offset != 0u) { + ArrayRef quicken_data = GetQuickeningInfoAt(quickening_info, offset); + optimizer::ArtDecompileDEX( + target_dex_file, + *code_item, + quicken_data, + decompile_return_instruction); + } + } } - method.UnHideAccessFlags(); + DexFile::UnHideAccessFlags(class_it); } } - for (const ClassAccessor::Field& field : class_accessor.GetFields()) { - field.UnHideAccessFlags(); - } } } diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 8169568413..cc71dc5f84 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -35,7 +35,6 @@ #include "class_linker.h" #include "class_root.h" #include "compiler_callbacks.h" -#include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_exception_helpers.h" @@ -191,6 +190,11 @@ FailureKind MethodVerifier::VerifyClass(Thread* self, error); } +template +static bool HasNextMethod(ClassDataItemIterator* it) { + return kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod(); +} + static FailureKind FailureKindMax(FailureKind fk1, FailureKind fk2) { static_assert(FailureKind::kNoFailure < FailureKind::kSoftFailure && FailureKind::kSoftFailure < FailureKind::kHardFailure, @@ -203,51 +207,45 @@ void MethodVerifier::FailureData::Merge(const MethodVerifier::FailureData& fd) { types |= fd.types; } -FailureKind MethodVerifier::VerifyClass(Thread* self, - const DexFile* dex_file, - Handle dex_cache, - Handle class_loader, - const DexFile::ClassDef& class_def, - CompilerCallbacks* callbacks, - bool allow_soft_failures, - HardFailLogMode log_level, - std::string* error) { - SCOPED_TRACE << "VerifyClass " << PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); +template +MethodVerifier::FailureData MethodVerifier::VerifyMethods(Thread* self, + ClassLinker* linker, + const DexFile* dex_file, + const DexFile::ClassDef& class_def, + ClassDataItemIterator* it, + Handle dex_cache, + Handle class_loader, + CompilerCallbacks* callbacks, + bool allow_soft_failures, + HardFailLogMode log_level, + bool need_precise_constants, + std::string* error_string) { + DCHECK(it != nullptr); - // A class must not be abstract and final. - if ((class_def.access_flags_ & (kAccAbstract | kAccFinal)) == (kAccAbstract | kAccFinal)) { - *error = "Verifier rejected class "; - *error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); - *error += ": class is abstract and final."; - return FailureKind::kHardFailure; - } - - ClassAccessor accessor(*dex_file, class_def); - - int64_t previous_method_idx[2] = { -1, -1 }; MethodVerifier::FailureData failure_data; - ClassLinker* const linker = Runtime::Current()->GetClassLinker(); - for (const ClassAccessor::Method& method : accessor.GetMethods()) { - int64_t* previous_idx = &previous_method_idx[method.IsStaticOrDirect() ? 0u : 1u]; + int64_t previous_method_idx = -1; + while (HasNextMethod(it)) { self->AllowThreadSuspension(); - const uint32_t method_idx = method.GetIndex(); - if (method_idx == *previous_idx) { + uint32_t method_idx = it->GetMemberIndex(); + if (method_idx == previous_method_idx) { // smali can create dex files with two encoded_methods sharing the same method_idx // http://code.google.com/p/smali/issues/detail?id=119 + it->Next(); continue; } - *previous_idx = method_idx; - const InvokeType type = method.GetInvokeType(class_def.access_flags_); - ArtMethod* resolved_method = linker->ResolveMethod( + previous_method_idx = method_idx; + InvokeType type = it->GetMethodInvokeType(class_def); + ArtMethod* method = linker->ResolveMethod( method_idx, dex_cache, class_loader, /* referrer */ nullptr, type); - if (resolved_method == nullptr) { + if (method == nullptr) { DCHECK(self->IsExceptionPending()); // We couldn't resolve the method, but continue regardless. self->ClearException(); } else { - DCHECK(resolved_method->GetDeclaringClassUnchecked() != nullptr) << type; + DCHECK(method->GetDeclaringClassUnchecked() != nullptr) << type; } + StackHandleScope<1> hs(self); std::string hard_failure_msg; MethodVerifier::FailureData result = VerifyMethod(self, method_idx, @@ -255,39 +253,99 @@ FailureKind MethodVerifier::VerifyClass(Thread* self, dex_cache, class_loader, class_def, - method.GetCodeItem(), - resolved_method, - method.GetAccessFlags(), + it->GetMethodCodeItem(), + method, + it->GetMethodAccessFlags(), callbacks, allow_soft_failures, log_level, - /*need_precise_constants*/ false, + need_precise_constants, &hard_failure_msg); if (result.kind == FailureKind::kHardFailure) { if (failure_data.kind == FailureKind::kHardFailure) { // If we logged an error before, we need a newline. - *error += "\n"; + *error_string += "\n"; } else { // If we didn't log a hard failure before, print the header of the message. - *error += "Verifier rejected class "; - *error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); - *error += ":"; + *error_string += "Verifier rejected class "; + *error_string += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); + *error_string += ":"; } - *error += " "; - *error += hard_failure_msg; + *error_string += " "; + *error_string += hard_failure_msg; } failure_data.Merge(result); + it->Next(); + } + + return failure_data; +} + +FailureKind MethodVerifier::VerifyClass(Thread* self, + const DexFile* dex_file, + Handle dex_cache, + Handle class_loader, + const DexFile::ClassDef& class_def, + CompilerCallbacks* callbacks, + bool allow_soft_failures, + HardFailLogMode log_level, + std::string* error) { + SCOPED_TRACE << "VerifyClass " << PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); + + // A class must not be abstract and final. + if ((class_def.access_flags_ & (kAccAbstract | kAccFinal)) == (kAccAbstract | kAccFinal)) { + *error = "Verifier rejected class "; + *error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); + *error += ": class is abstract and final."; + return FailureKind::kHardFailure; } - if (failure_data.kind == FailureKind::kNoFailure) { + const uint8_t* class_data = dex_file->GetClassData(class_def); + if (class_data == nullptr) { + // empty class, probably a marker interface + return FailureKind::kNoFailure; + } + ClassDataItemIterator it(*dex_file, class_data); + it.SkipAllFields(); + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + // Direct methods. + MethodVerifier::FailureData data1 = VerifyMethods(self, + linker, + dex_file, + class_def, + &it, + dex_cache, + class_loader, + callbacks, + allow_soft_failures, + log_level, + false /* need precise constants */, + error); + // Virtual methods. + MethodVerifier::FailureData data2 = VerifyMethods(self, + linker, + dex_file, + class_def, + &it, + dex_cache, + class_loader, + callbacks, + allow_soft_failures, + log_level, + false /* need precise constants */, + error); + + data1.Merge(data2); + + if (data1.kind == FailureKind::kNoFailure) { return FailureKind::kNoFailure; } else { - if ((failure_data.types & VERIFY_ERROR_LOCKING) != 0) { + if ((data1.types & VERIFY_ERROR_LOCKING) != 0) { // Print a warning about expected slow-down. Use a string temporary to print one contiguous // warning. std::string tmp = StringPrintf("Class %s failed lock verification and will run slower.", - PrettyDescriptor(accessor.GetDescriptor()).c_str()); + PrettyDescriptor(dex_file->GetClassDescriptor(class_def)).c_str()); if (!gPrintedDxMonitorText) { tmp = tmp + "\nCommon causes for lock verification issues are non-optimized dex code\n" "and incorrect proguard optimizations."; @@ -295,7 +353,7 @@ FailureKind MethodVerifier::VerifyClass(Thread* self, } LOG(WARNING) << tmp; } - return failure_data.kind; + return data1.kind; } } @@ -1866,11 +1924,15 @@ bool MethodVerifier::CodeFlowVerifyMethod() { static uint32_t GetFirstFinalInstanceFieldIndex(const DexFile& dex_file, dex::TypeIndex type_idx) { const DexFile::ClassDef* class_def = dex_file.FindClassDef(type_idx); DCHECK(class_def != nullptr); - ClassAccessor accessor(dex_file, *class_def); - for (const ClassAccessor::Field& field : accessor.GetInstanceFields()) { - if (field.IsFinal()) { - return field.GetIndex(); - } + const uint8_t* class_data = dex_file.GetClassData(*class_def); + DCHECK(class_data != nullptr); + ClassDataItemIterator it(dex_file, class_data); + it.SkipStaticFields(); + while (it.HasNextInstanceField()) { + if ((it.GetFieldAccessFlags() & kAccFinal) != 0) { + return it.GetMemberIndex(); + } + it.Next(); } return dex::kDexNoIndex; } diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index c7368d7b39..b2adc62a97 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -275,6 +275,23 @@ class MethodVerifier { void Merge(const FailureData& src); }; + // Verify all direct or virtual methods of a class. The method assumes that the iterator is + // positioned correctly, and the iterator will be updated. + template + static FailureData VerifyMethods(Thread* self, + ClassLinker* linker, + const DexFile* dex_file, + const DexFile::ClassDef& class_def, + ClassDataItemIterator* it, + Handle dex_cache, + Handle class_loader, + CompilerCallbacks* callbacks, + bool allow_soft_failures, + HardFailLogMode log_level, + bool need_precise_constants, + std::string* error_string) + REQUIRES_SHARED(Locks::mutator_lock_); + /* * Perform verification on a single method. * -- GitLab From 6eb4d5e4bc2ce068004c1d7c85dbfff0c5efd11d Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Sun, 3 Jun 2018 12:00:20 +0100 Subject: [PATCH 521/749] De-duplicate inline info in stack maps. This saves 0.3% of .oat file size. Test: test-art-host-gtest-stack_map_test Change-Id: Ic7d5addf04fb9b7a2f29a7d1d99ea93b39388fd2 --- compiler/optimizing/stack_map_stream.cc | 25 ++++++++++++------------- compiler/optimizing/stack_map_stream.h | 4 ++-- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index d99beac59f..7a60d4aa20 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -65,7 +65,7 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, // and it might modify the data before that. Therefore, just store the pointer. // See ClearSpillSlotsFromLoopPhisInStackMap in code_generator.h. lazy_stack_masks_.push_back(stack_mask); - current_inline_infos_ = 0; + current_inline_infos_.clear(); current_dex_registers_.clear(); expected_num_dex_registers_ = num_dex_registers; @@ -97,9 +97,11 @@ void StackMapStream::EndStackMapEntry() { in_stack_map_ = false; DCHECK_EQ(expected_num_dex_registers_, current_dex_registers_.size()); - // Mark the last inline info as last in the list for the stack map. - if (current_inline_infos_ > 0) { - inline_infos_[inline_infos_.size() - 1].is_last = InlineInfo::kLast; + // Generate index into the InlineInfo table. + if (!current_inline_infos_.empty()) { + current_inline_infos_.back().is_last = InlineInfo::kLast; + current_stack_map_.inline_info_index = + inline_infos_.Dedup(current_inline_infos_.data(), current_inline_infos_.size()); } stack_maps_.Add(current_stack_map_); @@ -162,17 +164,14 @@ void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, uint32_t dex_method_index = method->GetDexMethodIndexUnchecked(); entry.method_info_index = method_infos_.Dedup(&dex_method_index); } - if (current_inline_infos_++ == 0) { - current_stack_map_.inline_info_index = inline_infos_.size(); - } - inline_infos_.Add(entry); + current_inline_infos_.push_back(entry); current_dex_registers_.clear(); expected_num_dex_registers_ = num_dex_registers; if (kVerifyStackMaps) { size_t stack_map_index = stack_maps_.size(); - size_t depth = current_inline_infos_ - 1; + size_t depth = current_inline_infos_.size() - 1; dchecks_.emplace_back([=](const CodeInfo& code_info) { StackMap stack_map = code_info.GetStackMapAt(stack_map_index); InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); @@ -222,9 +221,9 @@ void StackMapStream::CreateDexRegisterMap() { } uint32_t map_index = dex_register_maps_.Dedup(temp_dex_register_map_.data(), temp_dex_register_map_.size()); - if (current_inline_infos_ > 0) { - inline_infos_[inline_infos_.size() - 1].dex_register_mask_index = mask_index; - inline_infos_[inline_infos_.size() - 1].dex_register_map_index = map_index; + if (!current_inline_infos_.empty()) { + current_inline_infos_.back().dex_register_mask_index = mask_index; + current_inline_infos_.back().dex_register_map_index = map_index; } else { current_stack_map_.dex_register_mask_index = mask_index; current_stack_map_.dex_register_map_index = map_index; @@ -232,7 +231,7 @@ void StackMapStream::CreateDexRegisterMap() { if (kVerifyStackMaps) { size_t stack_map_index = stack_maps_.size(); - int32_t depth = current_inline_infos_ - 1; + int32_t depth = current_inline_infos_.size() - 1; // We need to make copy of the current registers for later (when the check is run). auto expected_dex_registers = std::make_shared>( current_dex_registers_.begin(), current_dex_registers_.end()); diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index c758bca951..d634c703ff 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -53,7 +53,7 @@ class StackMapStream : public ValueObject { lazy_stack_masks_(allocator->Adapter(kArenaAllocStackMapStream)), in_stack_map_(false), in_inline_info_(false), - current_inline_infos_(0), + current_inline_infos_(allocator->Adapter(kArenaAllocStackMapStream)), current_dex_registers_(allocator->Adapter(kArenaAllocStackMapStream)), temp_dex_register_mask_(allocator, 32, true, kArenaAllocStackMapStream), temp_dex_register_map_(allocator->Adapter(kArenaAllocStackMapStream)) { @@ -157,7 +157,7 @@ class StackMapStream : public ValueObject { bool in_stack_map_; bool in_inline_info_; StackMapEntry current_stack_map_; - uint32_t current_inline_infos_; + ScopedArenaVector current_inline_infos_; ScopedArenaVector current_dex_registers_; size_t expected_num_dex_registers_; -- GitLab From ea341d2830298b666d7c6faf369d2fc3fe94fef8 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 11 May 2018 10:33:37 +0100 Subject: [PATCH 522/749] Rewrite TypeLookupTable. Improve bit-packing of the data to store twice as many bits of the hash as previously. Check for bucket mismatch after the first partial hash conflict (previous comments alluded to the bucket check but it was not implemented). Avoid an unnecessary unique_ptr<> indirection by making the TypeLookupTable moveable. Test: Rely on Treehugger. Bug: 79514364 Change-Id: I9fa6f712b037a6e6904d09c88670966486f56621 --- dex2oat/linker/oat_writer.cc | 10 +- libdexfile/dex/type_lookup_table.cc | 201 ++++++++++++--------- libdexfile/dex/type_lookup_table.h | 211 ++++++++++++----------- libdexfile/dex/type_lookup_table_test.cc | 14 +- runtime/oat.h | 4 +- runtime/oat_file.cc | 8 +- runtime/oat_file.h | 8 +- 7 files changed, 247 insertions(+), 209 deletions(-) diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 4046dc101f..99516684e8 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -4032,13 +4032,13 @@ bool OatWriter::WriteTypeLookupTables( // TypeLookupTable allocates its own and OatDexFile takes ownership. const DexFile& dex_file = *opened_dex_files[i]; { - std::unique_ptr type_lookup_table = - TypeLookupTable::Create(dex_file, /* storage */ nullptr); + TypeLookupTable type_lookup_table = TypeLookupTable::Create(dex_file); type_lookup_table_oat_dex_files_.push_back( std::make_unique(std::move(type_lookup_table))); dex_file.SetOatDexFile(type_lookup_table_oat_dex_files_.back().get()); } - TypeLookupTable* const table = type_lookup_table_oat_dex_files_.back()->GetTypeLookupTable(); + const TypeLookupTable& table = type_lookup_table_oat_dex_files_.back()->GetTypeLookupTable(); + DCHECK(table.Valid()); // Type tables are required to be 4 byte aligned. size_t initial_offset = oat_size_; @@ -4057,9 +4057,9 @@ bool OatWriter::WriteTypeLookupTables( DCHECK_EQ(oat_data_offset_ + rodata_offset, static_cast(oat_rodata->Seek(0u, kSeekCurrent))); - DCHECK_EQ(table_size, table->RawDataLength()); + DCHECK_EQ(table_size, table.RawDataLength()); - if (!oat_rodata->WriteFully(table->RawData(), table_size)) { + if (!oat_rodata->WriteFully(table.RawData(), table_size)) { PLOG(ERROR) << "Failed to write lookup table." << " File: " << oat_dex_file->GetLocation() << " Output: " << oat_rodata->GetLocation(); diff --git a/libdexfile/dex/type_lookup_table.cc b/libdexfile/dex/type_lookup_table.cc index ca5ec2f798..00ec358b02 100644 --- a/libdexfile/dex/type_lookup_table.cc +++ b/libdexfile/dex/type_lookup_table.cc @@ -20,72 +20,43 @@ #include #include "base/bit_utils.h" +#include "base/leb128.h" #include "dex/dex_file-inl.h" #include "dex/utf-inl.h" namespace art { -static uint16_t MakeData(uint16_t class_def_idx, uint32_t hash, uint32_t mask) { - uint16_t hash_mask = static_cast(~mask); - return (static_cast(hash) & hash_mask) | class_def_idx; +static inline bool ModifiedUtf8StringEquals(const char* lhs, const char* rhs) { + return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(lhs, rhs) == 0; } -TypeLookupTable::~TypeLookupTable() { - if (!owns_entries_) { - // We don't actually own the entries, don't let the unique_ptr release them. - entries_.release(); +TypeLookupTable TypeLookupTable::Create(const DexFile& dex_file) { + uint32_t num_class_defs = dex_file.NumClassDefs(); + if (UNLIKELY(!SupportedSize(num_class_defs))) { + return TypeLookupTable(); } -} - -uint32_t TypeLookupTable::RawDataLength(uint32_t num_class_defs) { - return SupportedSize(num_class_defs) ? RoundUpToPowerOfTwo(num_class_defs) * sizeof(Entry) : 0u; -} - -uint32_t TypeLookupTable::CalculateMask(uint32_t num_class_defs) { - return SupportedSize(num_class_defs) ? RoundUpToPowerOfTwo(num_class_defs) - 1u : 0u; -} - -bool TypeLookupTable::SupportedSize(uint32_t num_class_defs) { - return num_class_defs != 0u && num_class_defs <= std::numeric_limits::max(); -} - -std::unique_ptr TypeLookupTable::Create(const DexFile& dex_file, - uint8_t* storage) { - const uint32_t num_class_defs = dex_file.NumClassDefs(); - return std::unique_ptr(SupportedSize(num_class_defs) - ? new TypeLookupTable(dex_file, storage) - : nullptr); -} - -std::unique_ptr TypeLookupTable::Open(const uint8_t* dex_file_pointer, - const uint8_t* raw_data, - uint32_t num_class_defs) { - return std::unique_ptr( - new TypeLookupTable(dex_file_pointer, raw_data, num_class_defs)); -} + size_t mask_bits = CalculateMaskBits(num_class_defs); + size_t size = 1u << mask_bits; + std::unique_ptr owned_entries(new Entry[size]); + Entry* entries = owned_entries.get(); -TypeLookupTable::TypeLookupTable(const DexFile& dex_file, uint8_t* storage) - : dex_data_begin_(dex_file.DataBegin()), - raw_data_length_(RawDataLength(dex_file.NumClassDefs())), - mask_(CalculateMask(dex_file.NumClassDefs())), - entries_(storage != nullptr ? reinterpret_cast(storage) : new Entry[mask_ + 1]), - owns_entries_(storage == nullptr) { static_assert(alignof(Entry) == 4u, "Expecting Entry to be 4-byte aligned."); - DCHECK_ALIGNED(storage, alignof(Entry)); + const uint32_t mask = Entry::GetMask(mask_bits); std::vector conflict_class_defs; // The first stage. Put elements on their initial positions. If an initial position is already // occupied then delay the insertion of the element to the second stage to reduce probing // distance. - for (size_t i = 0; i < dex_file.NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(i); + for (size_t class_def_idx = 0; class_def_idx < dex_file.NumClassDefs(); ++class_def_idx) { + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx); const DexFile::TypeId& type_id = dex_file.GetTypeId(class_def.class_idx_); const DexFile::StringId& str_id = dex_file.GetStringId(type_id.descriptor_idx_); const uint32_t hash = ComputeModifiedUtf8Hash(dex_file.GetStringData(str_id)); - Entry entry; - entry.str_offset = str_id.string_data_off_; - entry.data = MakeData(i, hash, GetSizeMask()); - if (!SetOnInitialPos(entry, hash)) { - conflict_class_defs.push_back(i); + const uint32_t pos = hash & mask; + if (entries[pos].IsEmpty()) { + entries[pos] = Entry(str_id.string_data_off_, hash, class_def_idx, mask_bits); + DCHECK(entries[pos].IsLast(mask_bits)); + } else { + conflict_class_defs.push_back(class_def_idx); } } // The second stage. The initial position of these elements had a collision. Put these elements @@ -95,51 +66,111 @@ TypeLookupTable::TypeLookupTable(const DexFile& dex_file, uint8_t* storage) const DexFile::TypeId& type_id = dex_file.GetTypeId(class_def.class_idx_); const DexFile::StringId& str_id = dex_file.GetStringId(type_id.descriptor_idx_); const uint32_t hash = ComputeModifiedUtf8Hash(dex_file.GetStringData(str_id)); - Entry entry; - entry.str_offset = str_id.string_data_off_; - entry.data = MakeData(class_def_idx, hash, GetSizeMask()); - Insert(entry, hash); + // Find the last entry in the chain. + uint32_t tail_pos = hash & mask; + DCHECK(!entries[tail_pos].IsEmpty()); + while (!entries[tail_pos].IsLast(mask_bits)) { + tail_pos = (tail_pos + entries[tail_pos].GetNextPosDelta(mask_bits)) & mask; + DCHECK(!entries[tail_pos].IsEmpty()); + } + // Find an empty entry for insertion. + uint32_t insert_pos = tail_pos; + do { + insert_pos = (insert_pos + 1) & mask; + } while (!entries[insert_pos].IsEmpty()); + // Insert and chain the new entry. + entries[insert_pos] = Entry(str_id.string_data_off_, hash, class_def_idx, mask_bits); + entries[tail_pos].SetNextPosDelta((insert_pos - tail_pos) & mask, mask_bits); + DCHECK(entries[insert_pos].IsLast(mask_bits)); + DCHECK(!entries[tail_pos].IsLast(mask_bits)); } -} -TypeLookupTable::TypeLookupTable(const uint8_t* dex_file_pointer, - const uint8_t* raw_data, - uint32_t num_class_defs) - : dex_data_begin_(dex_file_pointer), - raw_data_length_(RawDataLength(num_class_defs)), - mask_(CalculateMask(num_class_defs)), - entries_(reinterpret_cast(const_cast(raw_data))), - owns_entries_(false) {} - -bool TypeLookupTable::SetOnInitialPos(const Entry& entry, uint32_t hash) { - const uint32_t pos = hash & GetSizeMask(); - if (!entries_[pos].IsEmpty()) { - return false; - } - entries_[pos] = entry; - entries_[pos].next_pos_delta = 0; - return true; + return TypeLookupTable(dex_file.DataBegin(), mask_bits, entries, std::move(owned_entries)); } -void TypeLookupTable::Insert(const Entry& entry, uint32_t hash) { - uint32_t pos = FindLastEntryInBucket(hash & GetSizeMask()); - uint32_t next_pos = (pos + 1) & GetSizeMask(); - while (!entries_[next_pos].IsEmpty()) { - next_pos = (next_pos + 1) & GetSizeMask(); - } - const uint32_t delta = (next_pos >= pos) ? (next_pos - pos) : (next_pos + Size() - pos); - entries_[pos].next_pos_delta = delta; - entries_[next_pos] = entry; - entries_[next_pos].next_pos_delta = 0; +TypeLookupTable TypeLookupTable::Open(const uint8_t* dex_data_pointer, + const uint8_t* raw_data, + uint32_t num_class_defs) { + DCHECK_ALIGNED(raw_data, alignof(Entry)); + const Entry* entries = reinterpret_cast(raw_data); + size_t mask_bits = CalculateMaskBits(num_class_defs); + return TypeLookupTable(dex_data_pointer, mask_bits, entries, /* owned_entries */ nullptr); } -uint32_t TypeLookupTable::FindLastEntryInBucket(uint32_t pos) const { +uint32_t TypeLookupTable::Lookup(const char* str, uint32_t hash) const { + uint32_t mask = Entry::GetMask(mask_bits_); + uint32_t pos = hash & mask; + // Thanks to special insertion algorithm, the element at position pos can be empty + // or start of the right bucket, or anywhere in the wrong bucket's chain. const Entry* entry = &entries_[pos]; - while (!entry->IsLast()) { - pos = (pos + entry->next_pos_delta) & GetSizeMask(); + if (entry->IsEmpty()) { + return dex::kDexNoIndex; + } + // Look for the partial hash match first, even if traversing the wrong bucket's chain. + uint32_t compared_hash_bits = (hash << mask_bits_) >> (2 * mask_bits_); + while (compared_hash_bits != entry->GetHashBits(mask_bits_)) { + if (entry->IsLast(mask_bits_)) { + return dex::kDexNoIndex; + } + pos = (pos + entry->GetNextPosDelta(mask_bits_)) & mask; entry = &entries_[pos]; + DCHECK(!entry->IsEmpty()); + } + // Found partial hash match, compare strings (expecting this to succeed). + const char* first_checked_str = GetStringData(*entry); + if (ModifiedUtf8StringEquals(str, first_checked_str)) { + return entry->GetClassDefIdx(mask_bits_); + } + // If we're at the end of the chain, return before doing further expensive work. + if (entry->IsLast(mask_bits_)) { + return dex::kDexNoIndex; + } + // Check if we're traversing the right bucket. This is important if the compared + // partial hash has only a few bits (i.e. it can match frequently). + if (((ComputeModifiedUtf8Hash(first_checked_str) ^ hash) & mask) != 0u) { + return dex::kDexNoIndex; // Low hash bits mismatch. } - return pos; + // Continue looking for the string in the rest of the chain. + do { + pos = (pos + entry->GetNextPosDelta(mask_bits_)) & mask; + entry = &entries_[pos]; + DCHECK(!entry->IsEmpty()); + if (compared_hash_bits == entry->GetHashBits(mask_bits_) && + ModifiedUtf8StringEquals(str, GetStringData(*entry))) { + return entry->GetClassDefIdx(mask_bits_); + } + } while (!entry->IsLast(mask_bits_)); + // Not found. + return dex::kDexNoIndex; +} + +uint32_t TypeLookupTable::RawDataLength(uint32_t num_class_defs) { + return SupportedSize(num_class_defs) ? RoundUpToPowerOfTwo(num_class_defs) * sizeof(Entry) : 0u; +} + +uint32_t TypeLookupTable::CalculateMaskBits(uint32_t num_class_defs) { + return SupportedSize(num_class_defs) ? MinimumBitsToStore(num_class_defs - 1u) : 0u; +} + +bool TypeLookupTable::SupportedSize(uint32_t num_class_defs) { + return num_class_defs != 0u && num_class_defs <= std::numeric_limits::max(); +} + +TypeLookupTable::TypeLookupTable(const uint8_t* dex_data_pointer, + uint32_t mask_bits, + const Entry* entries, + std::unique_ptr owned_entries) + : dex_data_begin_(dex_data_pointer), + mask_bits_(mask_bits), + entries_(entries), + owned_entries_(std::move(owned_entries)) {} + +const char* TypeLookupTable::GetStringData(const Entry& entry) const { + DCHECK(dex_data_begin_ != nullptr); + const uint8_t* ptr = dex_data_begin_ + entry.GetStringOffset(); + // Skip string length. + DecodeUnsignedLeb128(&ptr); + return reinterpret_cast(ptr); } } // namespace art diff --git a/libdexfile/dex/type_lookup_table.h b/libdexfile/dex/type_lookup_table.h index 0ba2b75dc6..7005d34b88 100644 --- a/libdexfile/dex/type_lookup_table.h +++ b/libdexfile/dex/type_lookup_table.h @@ -17,9 +17,8 @@ #ifndef ART_LIBDEXFILE_DEX_TYPE_LOOKUP_TABLE_H_ #define ART_LIBDEXFILE_DEX_TYPE_LOOKUP_TABLE_H_ -#include "base/leb128.h" +#include "base/logging.h" #include "dex/dex_file_types.h" -#include "dex/utf.h" namespace art { @@ -34,140 +33,146 @@ class DexFile; */ class TypeLookupTable { public: - ~TypeLookupTable(); + // Method creates lookup table for dex file. + static TypeLookupTable Create(const DexFile& dex_file); + + // Method opens lookup table from binary data. Lookups will traverse strings and other + // data contained in dex_file as well. Lookup table does not own raw_data or dex_file. + static TypeLookupTable Open(const uint8_t* dex_data_pointer, + const uint8_t* raw_data, + uint32_t num_class_defs); + + // Create an invalid lookup table. + TypeLookupTable() + : dex_data_begin_(nullptr), + mask_bits_(0u), + entries_(nullptr), + owned_entries_(nullptr) {} + + TypeLookupTable(TypeLookupTable&& src) noexcept = default; + TypeLookupTable& operator=(TypeLookupTable&& src) noexcept = default; + + ~TypeLookupTable() { + // Implicit deallocation by std::unique_ptr<> destructor. + } + + // Returns whether the TypeLookupTable is valid. + bool Valid() const { + return entries_ != nullptr; + } // Return the number of buckets in the lookup table. uint32_t Size() const { - return mask_ + 1; + DCHECK(Valid()); + return 1u << mask_bits_; } // Method search class_def_idx by class descriptor and it's hash. // If no data found then the method returns dex::kDexNoIndex. - uint32_t Lookup(const char* str, uint32_t hash) const { - uint32_t pos = hash & GetSizeMask(); - // Thanks to special insertion algorithm, the element at position pos can be empty or start of - // bucket. - const Entry* entry = &entries_[pos]; - while (!entry->IsEmpty()) { - if (CmpHashBits(entry->data, hash) && IsStringsEquals(str, entry->str_offset)) { - return GetClassDefIdx(entry->data); - } - if (entry->IsLast()) { - return dex::kDexNoIndex; - } - pos = (pos + entry->next_pos_delta) & GetSizeMask(); - entry = &entries_[pos]; - } - return dex::kDexNoIndex; - } - - // Method creates lookup table for dex file - static std::unique_ptr Create(const DexFile& dex_file, - uint8_t* storage = nullptr); - - // Method opens lookup table from binary data. Lookups will traverse strings and other - // data contained in dex_file as well. Lookup table does not own raw_data or dex_file. - static std::unique_ptr Open(const uint8_t* dex_file_pointer, - const uint8_t* raw_data, - uint32_t num_class_defs); + uint32_t Lookup(const char* str, uint32_t hash) const; // Method returns pointer to binary data of lookup table. Used by the oat writer. const uint8_t* RawData() const { - return reinterpret_cast(entries_.get()); + DCHECK(Valid()); + return reinterpret_cast(entries_); } // Method returns length of binary data. Used by the oat writer. - uint32_t RawDataLength() const { return raw_data_length_; } + uint32_t RawDataLength() const { + DCHECK(Valid()); + return Size() * sizeof(Entry); + } // Method returns length of binary data for the specified number of class definitions. static uint32_t RawDataLength(uint32_t num_class_defs); private: - /** - * To find element we need to compare strings. - * It is faster to compare first hashes and then strings itself. - * But we have no full hash of element of table. But we can use 2 ideas. - * 1. All minor bits of hash inside one bucket are equals. - * 2. If dex file contains N classes and size of hash table is 2^n (where N <= 2^n) - * then 16-n bits are free. So we can encode part of element's hash into these bits. - * So hash of element can be divided on three parts: - * XXXX XXXX XXXX YYYY YZZZ ZZZZ ZZZZZ - * Z - a part of hash encoded in bucket (these bits of has are same for all elements in bucket) - - * n bits - * Y - a part of hash that we can write into free 16-n bits (because only n bits used to store - * class_def_idx) - * X - a part of has that we can't use without increasing increase - * So the data element of Entry used to store class_def_idx and part of hash of the entry. - */ - struct Entry { - uint32_t str_offset; - uint16_t data; - uint16_t next_pos_delta; - - Entry() : str_offset(0), data(0), next_pos_delta(0) {} + /** + * To find element we need to compare strings. + * It is faster to compare first hashes and then strings itself. + * But we have no full hash of element of table. But we can use 2 ideas. + * 1. All minor bits of hash inside one bucket are equal. + * (TODO: We're not actually using this, are we?) + * 2. If the dex file contains N classes and the size of the hash table is 2^n (where N <= 2^n) + * then we need n bits for the class def index and n bits for the next position delta. + * So we can encode part of element's hash into the remaining 32-2*n (n <= 16) bits which + * would be otherwise wasted as a padding. + * So hash of element can be divided on three parts: + * XXXX XXXX XXXY YYYY YYYY YZZZ ZZZZ ZZZZ (example with n=11) + * Z - a part of hash encoded implicitly in the bucket index + * (these bits are same for all elements in bucket) + * Y - a part of hash that we can write into free 32-2*n bits + * X - a part of hash that we can't use without increasing the size of the entry + * So the `data` element of Entry is used to store the next position delta, class_def_index + * and a part of hash of the entry. + */ + class Entry { + public: + Entry() : str_offset_(0u), data_(0u) {} + Entry(uint32_t str_offset, uint32_t hash, uint32_t class_def_index, uint32_t mask_bits) + : str_offset_(str_offset), + data_(((hash & ~GetMask(mask_bits)) | class_def_index) << mask_bits) { + DCHECK_EQ(class_def_index & ~GetMask(mask_bits), 0u); + } + + void SetNextPosDelta(uint32_t next_pos_delta, uint32_t mask_bits) { + DCHECK_EQ(GetNextPosDelta(mask_bits), 0u); + DCHECK_EQ(next_pos_delta & ~GetMask(mask_bits), 0u); + DCHECK_NE(next_pos_delta, 0u); + data_ |= next_pos_delta; + } bool IsEmpty() const { - return str_offset == 0; + return str_offset_ == 0u; } - bool IsLast() const { - return next_pos_delta == 0; + bool IsLast(uint32_t mask_bits) const { + return GetNextPosDelta(mask_bits) == 0u; } - }; - static uint32_t CalculateMask(uint32_t num_class_defs); - static bool SupportedSize(uint32_t num_class_defs); + uint32_t GetStringOffset() const { + return str_offset_; + } - // Construct from a dex file. - explicit TypeLookupTable(const DexFile& dex_file, uint8_t* storage); - - // Construct from a dex file with existing data. - TypeLookupTable(const uint8_t* dex_file_pointer, - const uint8_t* raw_data, - uint32_t num_class_defs); - - bool IsStringsEquals(const char* str, uint32_t str_offset) const { - const uint8_t* ptr = dex_data_begin_ + str_offset; - CHECK(dex_data_begin_ != nullptr); - // Skip string length. - DecodeUnsignedLeb128(&ptr); - return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues( - str, reinterpret_cast(ptr)) == 0; - } + uint32_t GetNextPosDelta(uint32_t mask_bits) const { + return data_ & GetMask(mask_bits); + } - // Method extracts hash bits from element's data and compare them with - // the corresponding bits of the specified hash - bool CmpHashBits(uint32_t data, uint32_t hash) const { - uint32_t mask = static_cast(~GetSizeMask()); - return (hash & mask) == (data & mask); - } + uint32_t GetClassDefIdx(uint32_t mask_bits) const { + return (data_ >> mask_bits) & GetMask(mask_bits); + } - uint32_t GetClassDefIdx(uint32_t data) const { - return data & mask_; - } + uint32_t GetHashBits(uint32_t mask_bits) const { + DCHECK_LE(mask_bits, 16u); + return data_ >> (2u * mask_bits); + } - uint32_t GetSizeMask() const { - return mask_; - } + static uint32_t GetMask(uint32_t mask_bits) { + DCHECK_LE(mask_bits, 16u); + return ~(std::numeric_limits::max() << mask_bits); + } - // Attempt to set an entry on its hash's slot. If there is already something there, return false. - // Otherwise return true. - bool SetOnInitialPos(const Entry& entry, uint32_t hash); + private: + uint32_t str_offset_; + uint32_t data_; + }; - // Insert an entry, probes until there is an empty slot. - void Insert(const Entry& entry, uint32_t hash); + static uint32_t CalculateMaskBits(uint32_t num_class_defs); + static bool SupportedSize(uint32_t num_class_defs); - // Find the last entry in a chain. - uint32_t FindLastEntryInBucket(uint32_t cur_pos) const; + // Construct the TypeLookupTable. + TypeLookupTable(const uint8_t* dex_data_pointer, + uint32_t mask_bits, + const Entry* entries, + std::unique_ptr owned_entries); - const uint8_t* dex_data_begin_; - const uint32_t raw_data_length_; - const uint32_t mask_; - std::unique_ptr entries_; - // owns_entries_ specifies if the lookup table owns the entries_ array. - const bool owns_entries_; + const char* GetStringData(const Entry& entry) const; - DISALLOW_IMPLICIT_CONSTRUCTORS(TypeLookupTable); + const uint8_t* dex_data_begin_; + uint32_t mask_bits_; + const Entry* entries_; + // `owned_entries_` is either null (not owning `entries_`) or same pointer as `entries_`. + std::unique_ptr owned_entries_; }; } // namespace art diff --git a/libdexfile/dex/type_lookup_table_test.cc b/libdexfile/dex/type_lookup_table_test.cc index 6c3d291332..4316be0bd6 100644 --- a/libdexfile/dex/type_lookup_table_test.cc +++ b/libdexfile/dex/type_lookup_table_test.cc @@ -30,20 +30,20 @@ class TypeLookupTableTest : public CommonArtTestWithParam dex_file(OpenTestDexFile("Lookup")); - std::unique_ptr table(TypeLookupTable::Create(*dex_file)); - ASSERT_NE(nullptr, table.get()); - ASSERT_NE(nullptr, table->RawData()); - ASSERT_EQ(32U, table->RawDataLength()); + TypeLookupTable table = TypeLookupTable::Create(*dex_file); + ASSERT_TRUE(table.Valid()); + ASSERT_NE(nullptr, table.RawData()); + ASSERT_EQ(32U, table.RawDataLength()); } TEST_P(TypeLookupTableTest, Find) { std::unique_ptr dex_file(OpenTestDexFile("Lookup")); - std::unique_ptr table(TypeLookupTable::Create(*dex_file)); - ASSERT_NE(nullptr, table.get()); + TypeLookupTable table(TypeLookupTable::Create(*dex_file)); + ASSERT_TRUE(table.Valid()); auto pair = GetParam(); const char* descriptor = pair.first; size_t hash = ComputeModifiedUtf8Hash(descriptor); - uint32_t class_def_idx = table->Lookup(descriptor, hash); + uint32_t class_def_idx = table.Lookup(descriptor, hash); ASSERT_EQ(pair.second, class_def_idx); } diff --git a/runtime/oat.h b/runtime/oat.h index e7e5848dd6..72eb27d69e 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: Rewrite dex register map encoding. - static constexpr uint8_t kOatVersion[] = { '1', '4', '6', '\0' }; + // Last oat version changed reason: Rewrite TypeLookupTable. + static constexpr uint8_t kOatVersion[] = { '1', '4', '7', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index ffbc26c647..2b05b0e3dd 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -1687,6 +1687,7 @@ OatFile::OatDexFile::OatDexFile(const OatFile* oat_file, type_bss_mapping_(type_bss_mapping_data), string_bss_mapping_(string_bss_mapping_data), oat_class_offsets_pointer_(oat_class_offsets_pointer), + lookup_table_(), dex_layout_sections_(dex_layout_sections) { // Initialize TypeLookupTable. if (lookup_table_data_ != nullptr) { @@ -1706,7 +1707,7 @@ OatFile::OatDexFile::OatDexFile(const OatFile* oat_file, } } -OatFile::OatDexFile::OatDexFile(std::unique_ptr&& lookup_table) +OatFile::OatDexFile::OatDexFile(TypeLookupTable&& lookup_table) : lookup_table_(std::move(lookup_table)) {} OatFile::OatDexFile::~OatDexFile() {} @@ -1783,9 +1784,9 @@ const DexFile::ClassDef* OatFile::OatDexFile::FindClassDef(const DexFile& dex_fi DCHECK_EQ(ComputeModifiedUtf8Hash(descriptor), hash); bool used_lookup_table = false; const DexFile::ClassDef* lookup_table_classdef = nullptr; - if (LIKELY((oat_dex_file != nullptr) && (oat_dex_file->GetTypeLookupTable() != nullptr))) { + if (LIKELY((oat_dex_file != nullptr) && oat_dex_file->GetTypeLookupTable().Valid())) { used_lookup_table = true; - const uint32_t class_def_idx = oat_dex_file->GetTypeLookupTable()->Lookup(descriptor, hash); + const uint32_t class_def_idx = oat_dex_file->GetTypeLookupTable().Lookup(descriptor, hash); lookup_table_classdef = (class_def_idx != dex::kDexNoIndex) ? &dex_file.GetClassDef(class_def_idx) : nullptr; @@ -1796,6 +1797,7 @@ const DexFile::ClassDef* OatFile::OatDexFile::FindClassDef(const DexFile& dex_fi // Fast path for rare no class defs case. const uint32_t num_class_defs = dex_file.NumClassDefs(); if (num_class_defs == 0) { + DCHECK(!used_lookup_table); return nullptr; } const DexFile::TypeId* type_id = dex_file.FindTypeId(descriptor); diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 8e18cee729..d72b6a8971 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -514,14 +514,14 @@ class OatDexFile FINAL { // Madvise the dex file based on the state we are moving to. static void MadviseDexFile(const DexFile& dex_file, MadviseState state); - TypeLookupTable* GetTypeLookupTable() const { - return lookup_table_.get(); + const TypeLookupTable& GetTypeLookupTable() const { + return lookup_table_; } ~OatDexFile(); // Create only with a type lookup table, used by the compiler to speed up compilation. - explicit OatDexFile(std::unique_ptr&& lookup_table); + explicit OatDexFile(TypeLookupTable&& lookup_table); // Return the dex layout sections. const DexLayoutSections* GetDexLayoutSections() const { @@ -553,7 +553,7 @@ class OatDexFile FINAL { const IndexBssMapping* const type_bss_mapping_ = nullptr; const IndexBssMapping* const string_bss_mapping_ = nullptr; const uint32_t* const oat_class_offsets_pointer_ = 0u; - mutable std::unique_ptr lookup_table_; + TypeLookupTable lookup_table_; const DexLayoutSections* const dex_layout_sections_ = nullptr; friend class OatFile; -- GitLab From 75c6fcac1e31ffc7fa59fdea1ddf04c4e7e870d1 Mon Sep 17 00:00:00 2001 From: Richard Uhler Date: Mon, 4 Jun 2018 15:44:17 +0100 Subject: [PATCH 523/749] Ignore comment lines in proguard mapping. Comment lines are now being generated by R8 in the proguard mapping. Update the proguard mapping parser to handle those. Fixes the ahat test failure: 1) diffMatchedHeap(com.android.ahat.DiffTest) java.io.IOException: Unable to load proguard map at com.android.ahat.TestDump.load(TestDump.java:116) at com.android.ahat.TestDump.getTestDump(TestDump.java:269) at com.android.ahat.TestDump.getTestDump(TestDump.java:241) at com.android.ahat.DiffTest.diffMatchedHeap(DiffTest.java:32) ... Caused by: java.text.ParseException: Error parsing class line: '# compiler: R8' at com.android.ahat.proguard.ProguardMap.parseException(ProguardMap.java:142) at com.android.ahat.proguard.ProguardMap.readFromReader(ProguardMap.java:191) at com.android.ahat.TestDump.load(TestDump.java:114) ... 33 more Change-Id: I70c9deb414086f454a1f3fefd3bace5b78953b44 Test: m ahat-test --- .../src/main/com/android/ahat/proguard/ProguardMap.java | 6 ++++++ tools/ahat/src/test/com/android/ahat/ProguardMapTest.java | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/tools/ahat/src/main/com/android/ahat/proguard/ProguardMap.java b/tools/ahat/src/main/com/android/ahat/proguard/ProguardMap.java index 79a737cc18..5c21a9ed0a 100644 --- a/tools/ahat/src/main/com/android/ahat/proguard/ProguardMap.java +++ b/tools/ahat/src/main/com/android/ahat/proguard/ProguardMap.java @@ -184,6 +184,12 @@ public class ProguardMap { BufferedReader reader = new BufferedReader(mapReader); String line = reader.readLine(); while (line != null) { + // Comment lines start with '#'. Skip over them. + if (line.startsWith("#")) { + line = reader.readLine(); + continue; + } + // Class lines are of the form: // 'clear.class.name -> obfuscated_class_name:' int sep = line.indexOf(" -> "); diff --git a/tools/ahat/src/test/com/android/ahat/ProguardMapTest.java b/tools/ahat/src/test/com/android/ahat/ProguardMapTest.java index 02976b5285..a9952ee3fd 100644 --- a/tools/ahat/src/test/com/android/ahat/ProguardMapTest.java +++ b/tools/ahat/src/test/com/android/ahat/ProguardMapTest.java @@ -25,7 +25,11 @@ import static org.junit.Assert.assertEquals; public class ProguardMapTest { private static final String TEST_MAP = - "class.that.is.Empty -> a:\n" + "# compiler: richard\n" + + "# compiler_version: 3.0-dev\n" + + "# min_api: 10000\n" + + "# compiler_hash: b7e25308967a577aa1f05a4b5a745c26\n" + + "class.that.is.Empty -> a:\n" + "class.that.is.Empty$subclass -> b:\n" + "class.with.only.Fields -> c:\n" + " int prim_type_field -> a\n" -- GitLab From 5816d63bca0a4e0522e06e044e516cdc32993c39 Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Wed, 30 May 2018 12:03:54 +0100 Subject: [PATCH 524/749] Remove special support for D8 in 911-get-stack-trace This test produced slightly different output when run using D8 compared with DX. As D8 is the default and DX is no longer used this removes the special support for D8 and updates the expected output to match. Test: art/test/testrunner/testrunner.py Change-Id: Icd1f4e547c13bf6641202ac086d567895e1b83a4 --- test/911-get-stack-trace/check | 31 -- test/911-get-stack-trace/expected.txt | 228 +++++------ test/911-get-stack-trace/expected_d8.diff | 456 ---------------------- 3 files changed, 114 insertions(+), 601 deletions(-) delete mode 100644 test/911-get-stack-trace/check delete mode 100644 test/911-get-stack-trace/expected_d8.diff diff --git a/test/911-get-stack-trace/check b/test/911-get-stack-trace/check deleted file mode 100644 index ee00266b36..0000000000 --- a/test/911-get-stack-trace/check +++ /dev/null @@ -1,31 +0,0 @@ -#!/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. - -if [[ "$DX" == 'd8' ]]; then - patch -p0 expected.txt < expected_d8.diff -fi - -./default-check "$@" -if [[ "$?" == "0" ]]; then - exit 0; -fi - -# We cannot always correctly determine if D8 was used because of (b/68406220). -# So we are just going to try to see it matches the expect output of D8 no -# matter what. -patch -p0 expected.txt < expected_d8.diff - -./default-check "$@" diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt index 8177f494ac..544fb7da76 100644 --- a/test/911-get-stack-trace/expected.txt +++ b/test/911-get-stack-trace/expected.txt @@ -9,19 +9,19 @@ From top baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - doTest ()V 34 25 + doTest ()V 33 25 run ()V 0 25 --------- print (Ljava/lang/Thread;II)V 0 38 @@ -29,19 +29,19 @@ From top baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - doTest ()V 38 26 + doTest ()V 37 26 run ()V 0 25 --------- getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2 @@ -54,12 +54,12 @@ From top baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 From bottom --------- run ()V 0 25 --------- - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 doTest ()V 60 32 @@ -67,7 +67,7 @@ From bottom --------- bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 @@ -81,16 +81,16 @@ From top baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 28 @@ -99,16 +99,16 @@ From top baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 28 @@ -122,22 +122,22 @@ From top baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 From bottom --------- run ()V 4 28 --------- foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 28 --------- - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 ########################### @@ -145,20 +145,20 @@ From bottom ########################### From top --------- - printOrWait (IILart/ControlData;)V 44 54 + printOrWait (IILart/ControlData;)V 45 54 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 61 @@ -166,29 +166,29 @@ From top baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 61 --------- - printOrWait (IILart/ControlData;)V 44 54 + printOrWait (IILart/ControlData;)V 45 54 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 --------- bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 From bottom @@ -196,15 +196,15 @@ From bottom run ()V 4 61 --------- foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 61 --------- - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 ################################ @@ -360,7 +360,7 @@ Signal Catcher Test911 getAllStackTraces (I)[[Ljava/lang/Object; -1 -2 printAll (I)V 0 75 - doTest ()V 122 59 + doTest ()V 120 59 run ()V 24 37 --------- @@ -373,16 +373,16 @@ AllTraces Thread 0 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 47 @@ -394,16 +394,16 @@ AllTraces Thread 1 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 47 @@ -415,16 +415,16 @@ AllTraces Thread 2 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 47 @@ -436,16 +436,16 @@ AllTraces Thread 3 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 47 @@ -457,16 +457,16 @@ AllTraces Thread 4 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 47 @@ -478,16 +478,16 @@ AllTraces Thread 5 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 47 @@ -499,16 +499,16 @@ AllTraces Thread 6 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 47 @@ -520,16 +520,16 @@ AllTraces Thread 7 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 47 @@ -541,16 +541,16 @@ AllTraces Thread 8 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 47 @@ -562,16 +562,16 @@ AllTraces Thread 9 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 47 @@ -595,7 +595,7 @@ Signal Catcher Test911 getAllStackTraces (I)[[Ljava/lang/Object; -1 -2 printAll (I)V 0 75 - doTest ()V 127 61 + doTest ()V 125 61 run ()V 24 37 --------- @@ -627,7 +627,7 @@ ThreadListTraces Thread 8 Test911 getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2 printList ([Ljava/lang/Thread;I)V 0 68 - doTest ()V 112 54 + doTest ()V 110 54 run ()V 32 41 --------- @@ -674,7 +674,7 @@ ThreadListTraces Thread 8 Test911 getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2 printList ([Ljava/lang/Thread;I)V 0 68 - doTest ()V 117 56 + doTest ()V 115 56 run ()V 32 41 --------- @@ -684,16 +684,16 @@ ThreadListTraces Thread 0 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 37 @@ -705,16 +705,16 @@ ThreadListTraces Thread 2 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 37 @@ -726,16 +726,16 @@ ThreadListTraces Thread 4 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 37 @@ -747,16 +747,16 @@ ThreadListTraces Thread 6 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 37 @@ -768,16 +768,16 @@ ThreadListTraces Thread 8 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 + baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 run ()V 4 37 @@ -789,7 +789,7 @@ ThreadListTraces Thread 8 4 JVMTI_ERROR_ILLEGAL_ARGUMENT [public static native java.lang.Object[] art.Frames.getFrameLocation(java.lang.Thread,int), ffffffff] -[public static void art.Frames.doTestSameThread(), 35] +[public static void art.Frames.doTestSameThread(), 40] [public static void art.Frames.doTest() throws java.lang.Exception, 0] [public void art.Test911$1.run(), 28] JVMTI_ERROR_NO_MORE_FRAMES @@ -804,16 +804,16 @@ JVMTI_ERROR_ILLEGAL_ARGUMENT [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 2] [private static long art.Recurse.bar(int,int,int,art.ControlData), 0] [public static int art.Recurse.foo(int,int,int,art.ControlData), 0] -[private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9] +[private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8] [private static long art.Recurse.bar(int,int,int,art.ControlData), 0] [public static int art.Recurse.foo(int,int,int,art.ControlData), 0] -[private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9] +[private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8] [private static long art.Recurse.bar(int,int,int,art.ControlData), 0] [public static int art.Recurse.foo(int,int,int,art.ControlData), 0] -[private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9] +[private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8] [private static long art.Recurse.bar(int,int,int,art.ControlData), 0] [public static int art.Recurse.foo(int,int,int,art.ControlData), 0] -[private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9] +[private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8] [private static long art.Recurse.bar(int,int,int,art.ControlData), 0] [public static int art.Recurse.foo(int,int,int,art.ControlData), 0] [public void art.Frames$1.run(), 4] @@ -824,20 +824,20 @@ JVMTI_ERROR_NO_MORE_FRAMES ########################### 17 JVMTI_ERROR_ILLEGAL_ARGUMENT -[private static void art.Recurse.printOrWait(int,int,art.ControlData), 2c] +[private static void art.Recurse.printOrWait(int,int,art.ControlData), 2d] [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 2] [private static long art.Recurse.bar(int,int,int,art.ControlData), 0] [public static int art.Recurse.foo(int,int,int,art.ControlData), 0] -[private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9] +[private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8] [private static long art.Recurse.bar(int,int,int,art.ControlData), 0] [public static int art.Recurse.foo(int,int,int,art.ControlData), 0] -[private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9] +[private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8] [private static long art.Recurse.bar(int,int,int,art.ControlData), 0] [public static int art.Recurse.foo(int,int,int,art.ControlData), 0] -[private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9] +[private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8] [private static long art.Recurse.bar(int,int,int,art.ControlData), 0] [public static int art.Recurse.foo(int,int,int,art.ControlData), 0] -[private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9] +[private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8] [private static long art.Recurse.bar(int,int,int,art.ControlData), 0] [public static int art.Recurse.foo(int,int,int,art.ControlData), 0] [public void art.Frames$2.run(), 4] diff --git a/test/911-get-stack-trace/expected_d8.diff b/test/911-get-stack-trace/expected_d8.diff deleted file mode 100644 index c12015a832..0000000000 --- a/test/911-get-stack-trace/expected_d8.diff +++ /dev/null @@ -1,456 +0,0 @@ -12c12 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -15c15 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -18c18 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -21c21 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -24c24 -< doTest ()V 34 25 ---- -> doTest ()V 33 25 -32c32 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -35c35 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -38c38 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -41c41 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -44c44 -< doTest ()V 38 26 ---- -> doTest ()V 37 26 -57c57 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -62c62 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -70c70 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -84c84 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -87c87 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -90c90 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -93c93 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -102c102 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -105c105 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -108c108 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -111c111 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -125c125 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -132c132 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -137c137 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -140c140 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -148c148 -< printOrWait (IILart/ControlData;)V 44 54 ---- -> printOrWait (IILart/ControlData;)V 45 54 -152c152 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -155c155 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -158c158 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -161c161 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -169c169 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -172c172 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -175c175 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -178c178 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -183c183 -< printOrWait (IILart/ControlData;)V 44 54 ---- -> printOrWait (IILart/ControlData;)V 45 54 -187c187 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -191c191 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -199c199 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -204c204 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -207c207 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -363c363 -< doTest ()V 122 59 ---- -> doTest ()V 120 59 -376c376 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -379c379 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -382c382 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -385c385 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -397c397 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -400c400 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -403c403 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -406c406 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -418c418 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -421c421 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -424c424 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -427c427 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -439c439 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -442c442 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -445c445 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -448c448 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -460c460 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -463c463 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -466c466 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -469c469 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -481c481 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -484c484 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -487c487 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -490c490 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -502c502 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -505c505 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -508c508 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -511c511 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -523c523 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -526c526 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -529c529 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -532c532 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -544c544 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -547c547 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -550c550 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -553c553 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -565c565 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -568c568 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -571c571 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -574c574 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -598c598 -< doTest ()V 127 61 ---- -> doTest ()V 125 61 -630c630 -< doTest ()V 112 54 ---- -> doTest ()V 110 54 -677c677 -< doTest ()V 117 56 ---- -> doTest ()V 115 56 -687c687 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -690c690 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -693c693 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -696c696 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -708c708 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -711c711 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -714c714 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -717c717 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -729c729 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -732c732 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -735c735 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -738c738 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -750c750 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -753c753 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -756c756 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -759c759 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -771c771 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -774c774 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -777c777 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -780c780 -< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34 ---- -> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 -792c792 -< [public static void art.Frames.doTestSameThread(), 35] ---- -> [public static void art.Frames.doTestSameThread(), 40] -807c807 -< [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9] ---- -> [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8] -810c810 -< [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9] ---- -> [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8] -813c813 -< [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9] ---- -> [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8] -816c816 -< [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9] ---- -> [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8] -827c827 -< [private static void art.Recurse.printOrWait(int,int,art.ControlData), 2c] ---- -> [private static void art.Recurse.printOrWait(int,int,art.ControlData), 2d] -831c831 -< [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9] ---- -> [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8] -834c834 -< [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9] ---- -> [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8] -837c837 -< [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9] ---- -> [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8] -840c840 -< [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9] ---- -> [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8] -- GitLab From 03004e66495ba4dff81c1d49522b79e95a829af7 Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Wed, 23 May 2018 11:52:10 +0100 Subject: [PATCH 525/749] Remove unused native implementation of Object.wait() As part of the process of verifying Java files against 8u121-b13 the Object.wait() method has been changed from a native method to a pure Java implementation. This removes the unused native implementation method and the supporting method in mirror::Object. Updated the 911-get-stack-trace test to handle the two extra stack elements added by this change. Bug: 74379469 Test: make checkbuild, flash Change-Id: Ifbc4612f7d949416d8c703628fa8cafaa296c350 --- runtime/mirror/object-inl.h | 4 - runtime/mirror/object.h | 1 - runtime/native/java_lang_Object.cc | 6 - test/911-get-stack-trace/expected.txt | 138 +++++++++++++----- .../src/art/AllTraces.java | 2 +- .../src/art/OtherThread.java | 4 +- .../src/art/ThreadListTraces.java | 2 +- 7 files changed, 108 insertions(+), 49 deletions(-) diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index e022db86fd..ee4f53b695 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -114,10 +114,6 @@ inline void Object::NotifyAll(Thread* self) { Monitor::NotifyAll(self, this); } -inline void Object::Wait(Thread* self) { - Monitor::Wait(self, this, 0, 0, true, kWaiting); -} - inline void Object::Wait(Thread* self, int64_t ms, int32_t ns) { Monitor::Wait(self, this, ms, ns, true, kTimedWaiting); } diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index 8584b8a56f..a89d6323a5 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -176,7 +176,6 @@ class MANAGED LOCKABLE Object { UNLOCK_FUNCTION(); void Notify(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); void NotifyAll(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); - void Wait(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); void Wait(Thread* self, int64_t timeout, int32_t nanos) REQUIRES_SHARED(Locks::mutator_lock_); template(java_this)->NotifyAll(soa.Self()); } -static void Object_wait(JNIEnv* env, jobject java_this) { - ScopedFastNativeObjectAccess soa(env); - soa.Decode(java_this)->Wait(soa.Self()); -} - static void Object_waitJI(JNIEnv* env, jobject java_this, jlong ms, jint ns) { ScopedFastNativeObjectAccess soa(env); soa.Decode(java_this)->Wait(soa.Self(), ms, ns); @@ -61,7 +56,6 @@ static JNINativeMethod gMethods[] = { FAST_NATIVE_METHOD(Object, internalClone, "()Ljava/lang/Object;"), FAST_NATIVE_METHOD(Object, notify, "()V"), FAST_NATIVE_METHOD(Object, notifyAll, "()V"), - OVERLOADED_FAST_NATIVE_METHOD(Object, wait, "()V", wait), OVERLOADED_FAST_NATIVE_METHOD(Object, wait, "(JI)V", waitJI), FAST_NATIVE_METHOD(Object, identityHashCodeNative, "(Ljava/lang/Object;)I"), }; diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt index 544fb7da76..b0a400ab75 100644 --- a/test/911-get-stack-trace/expected.txt +++ b/test/911-get-stack-trace/expected.txt @@ -76,7 +76,9 @@ From bottom ################################ From top --------- - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -95,6 +97,8 @@ From top foo (IIILart/ControlData;)I 0 21 run ()V 4 28 --------- + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -113,12 +117,16 @@ From top foo (IIILart/ControlData;)I 0 21 run ()V 4 28 --------- - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 --------- + wait ()V 2 568 + printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 @@ -263,7 +271,9 @@ main --------- AllTraces Thread 0 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -271,7 +281,9 @@ AllTraces Thread 0 --------- AllTraces Thread 1 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -279,7 +291,9 @@ AllTraces Thread 1 --------- AllTraces Thread 2 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -287,7 +301,9 @@ AllTraces Thread 2 --------- AllTraces Thread 3 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -295,7 +311,9 @@ AllTraces Thread 3 --------- AllTraces Thread 4 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -303,7 +321,9 @@ AllTraces Thread 4 --------- AllTraces Thread 5 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -311,7 +331,9 @@ AllTraces Thread 5 --------- AllTraces Thread 6 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -319,7 +341,9 @@ AllTraces Thread 6 --------- AllTraces Thread 7 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -327,7 +351,9 @@ AllTraces Thread 7 --------- AllTraces Thread 8 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -335,7 +361,9 @@ AllTraces Thread 8 --------- AllTraces Thread 9 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -368,7 +396,9 @@ main --------- AllTraces Thread 0 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -389,7 +419,9 @@ AllTraces Thread 0 --------- AllTraces Thread 1 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -410,7 +442,9 @@ AllTraces Thread 1 --------- AllTraces Thread 2 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -431,7 +465,9 @@ AllTraces Thread 2 --------- AllTraces Thread 3 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -452,7 +488,9 @@ AllTraces Thread 3 --------- AllTraces Thread 4 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -473,7 +511,9 @@ AllTraces Thread 4 --------- AllTraces Thread 5 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -494,7 +534,9 @@ AllTraces Thread 5 --------- AllTraces Thread 6 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -515,7 +557,9 @@ AllTraces Thread 6 --------- AllTraces Thread 7 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -536,7 +580,9 @@ AllTraces Thread 7 --------- AllTraces Thread 8 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -557,7 +603,9 @@ AllTraces Thread 8 --------- AllTraces Thread 9 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -632,7 +680,9 @@ Test911 --------- ThreadListTraces Thread 0 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -640,7 +690,9 @@ ThreadListTraces Thread 0 --------- ThreadListTraces Thread 2 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -648,7 +700,9 @@ ThreadListTraces Thread 2 --------- ThreadListTraces Thread 4 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -656,7 +710,9 @@ ThreadListTraces Thread 4 --------- ThreadListTraces Thread 6 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -664,7 +720,9 @@ ThreadListTraces Thread 6 --------- ThreadListTraces Thread 8 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -679,7 +737,9 @@ Test911 --------- ThreadListTraces Thread 0 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -700,7 +760,9 @@ ThreadListTraces Thread 0 --------- ThreadListTraces Thread 2 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -721,7 +783,9 @@ ThreadListTraces Thread 2 --------- ThreadListTraces Thread 4 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -742,7 +806,9 @@ ThreadListTraces Thread 4 --------- ThreadListTraces Thread 6 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -763,7 +829,9 @@ ThreadListTraces Thread 6 --------- ThreadListTraces Thread 8 - wait ()V -1 -2 + wait (JI)V -1 -2 + wait (J)V 1 442 + wait ()V 2 568 printOrWait (IILart/ControlData;)V 24 47 baz (IIILart/ControlData;)Ljava/lang/Object; 2 32 bar (IIILart/ControlData;)J 0 26 @@ -797,9 +865,11 @@ JVMTI_ERROR_NO_MORE_FRAMES ################################ ### Other thread (suspended) ### ################################ -18 +20 JVMTI_ERROR_ILLEGAL_ARGUMENT -[public final native void java.lang.Object.wait() throws java.lang.InterruptedException, ffffffff] +[public final native void java.lang.Object.wait(long,int) throws java.lang.InterruptedException, ffffffff] +[public final void java.lang.Object.wait(long) throws java.lang.InterruptedException, 1] +[public final void java.lang.Object.wait() throws java.lang.InterruptedException, 2] [private static void art.Recurse.printOrWait(int,int,art.ControlData), 18] [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 2] [private static long art.Recurse.bar(int,int,int,art.ControlData), 0] diff --git a/test/911-get-stack-trace/src/art/AllTraces.java b/test/911-get-stack-trace/src/art/AllTraces.java index d73f78bba1..507925c29e 100644 --- a/test/911-get-stack-trace/src/art/AllTraces.java +++ b/test/911-get-stack-trace/src/art/AllTraces.java @@ -56,7 +56,7 @@ public class AllTraces { printAll(0); - printAll(5); + printAll(7); printAll(25); diff --git a/test/911-get-stack-trace/src/art/OtherThread.java b/test/911-get-stack-trace/src/art/OtherThread.java index 675bff55a6..3f5ae59e18 100644 --- a/test/911-get-stack-trace/src/art/OtherThread.java +++ b/test/911-get-stack-trace/src/art/OtherThread.java @@ -36,8 +36,8 @@ public class OtherThread { System.out.println("From top"); PrintThread.print(t, 0, 25); PrintThread.print(t, 1, 25); - PrintThread.print(t, 0, 5); - PrintThread.print(t, 2, 5); + PrintThread.print(t, 0, 7); + PrintThread.print(t, 2, 7); System.out.println("From bottom"); PrintThread.print(t, -1, 25); diff --git a/test/911-get-stack-trace/src/art/ThreadListTraces.java b/test/911-get-stack-trace/src/art/ThreadListTraces.java index 0de93de706..9b27e72f22 100644 --- a/test/911-get-stack-trace/src/art/ThreadListTraces.java +++ b/test/911-get-stack-trace/src/art/ThreadListTraces.java @@ -51,7 +51,7 @@ public class ThreadListTraces { printList(list, 0); - printList(list, 5); + printList(list, 7); printList(list, 25); -- GitLab From 18090d118bfb04620aeef719e2d7780c26298bf8 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 1 Jun 2018 16:53:12 +0100 Subject: [PATCH 526/749] Refactor String resolution. Use the same pattern as type resolution and avoid some unnecessary read barriers in the fast path. Consolidate naming between ArtField and ArtMethod. Test: m test-art-host-gtest Test: testrunner.py --host Change-Id: Iea69129085f61f04a4add09edd0eadbb7ac9ecb2 --- runtime/art_field-inl.h | 15 ++---- runtime/art_field.cc | 7 --- runtime/art_field.h | 7 +-- runtime/art_method-inl.h | 6 +++ runtime/art_method.cc | 10 ---- runtime/art_method.h | 2 +- runtime/class_linker-inl.h | 48 +++++++++++++++++++ runtime/class_linker.cc | 25 +++++----- runtime/class_linker.h | 23 +++++++++ runtime/entrypoints/entrypoint_utils-inl.h | 27 ----------- runtime/entrypoints/entrypoint_utils.h | 5 -- .../quick/quick_dexcache_entrypoints.cc | 3 +- runtime/interpreter/interpreter_common.cc | 4 +- runtime/interpreter/interpreter_common.h | 8 +--- runtime/mirror/class.cc | 4 +- .../native/java_lang_reflect_Executable.cc | 2 +- runtime/native/java_lang_reflect_Field.cc | 3 +- runtime/reference_table_test.cc | 2 +- 18 files changed, 105 insertions(+), 96 deletions(-) diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h index baa5102f5d..c5fb7d5f40 100644 --- a/runtime/art_field-inl.h +++ b/runtime/art_field-inl.h @@ -21,7 +21,7 @@ #include -#include "class_linker.h" +#include "class_linker-inl.h" #include "dex/dex_file-inl.h" #include "dex/primitive.h" #include "gc/accounting/card_table-inl.h" @@ -339,16 +339,11 @@ inline const DexFile* ArtField::GetDexFile() REQUIRES_SHARED(Locks::mutator_lock return GetDexCache()->GetDexFile(); } -inline ObjPtr ArtField::GetStringName(Thread* self, bool resolve) { - auto dex_field_index = GetDexFieldIndex(); +inline ObjPtr ArtField::ResolveNameString() { + uint32_t dex_field_index = GetDexFieldIndex(); CHECK_NE(dex_field_index, dex::kDexNoIndex); - ObjPtr dex_cache = GetDexCache(); - const DexFile::FieldId& field_id = dex_cache->GetDexFile()->GetFieldId(dex_field_index); - ObjPtr name = dex_cache->GetResolvedString(field_id.name_idx_); - if (resolve && name == nullptr) { - name = ResolveGetStringName(self, field_id.name_idx_, dex_cache); - } - return name; + const DexFile::FieldId& field_id = GetDexFile()->GetFieldId(dex_field_index); + return Runtime::Current()->GetClassLinker()->ResolveString(field_id.name_idx_, this); } template diff --git a/runtime/art_field.cc b/runtime/art_field.cc index b867621f02..6cbd9e4cfc 100644 --- a/runtime/art_field.cc +++ b/runtime/art_field.cc @@ -52,13 +52,6 @@ ObjPtr ArtField::ProxyFindSystemClass(const char* descriptor) { return klass; } -ObjPtr ArtField::ResolveGetStringName(Thread* self, - dex::StringIndex string_idx, - ObjPtr dex_cache) { - StackHandleScope<1> hs(self); - return Runtime::Current()->GetClassLinker()->ResolveString(string_idx, hs.NewHandle(dex_cache)); -} - std::string ArtField::PrettyField(ArtField* f, bool with_type) { if (f == nullptr) { return "null"; diff --git a/runtime/art_field.h b/runtime/art_field.h index 784a862425..123595c6fe 100644 --- a/runtime/art_field.h +++ b/runtime/art_field.h @@ -201,8 +201,7 @@ class ArtField FINAL { const char* GetName() REQUIRES_SHARED(Locks::mutator_lock_); // Resolves / returns the name from the dex cache. - ObjPtr GetStringName(Thread* self, bool resolve) - REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr ResolveNameString() REQUIRES_SHARED(Locks::mutator_lock_); const char* GetTypeDescriptor() REQUIRES_SHARED(Locks::mutator_lock_); @@ -241,10 +240,6 @@ class ArtField FINAL { ObjPtr ProxyFindSystemClass(const char* descriptor) REQUIRES_SHARED(Locks::mutator_lock_); - ObjPtr ResolveGetStringName(Thread* self, - dex::StringIndex string_idx, - ObjPtr dex_cache) - REQUIRES_SHARED(Locks::mutator_lock_); void GetAccessFlagsDCheck() REQUIRES_SHARED(Locks::mutator_lock_); void GetOffsetDCheck() REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index ec66966869..18595cf17a 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -245,6 +245,12 @@ inline const char* ArtMethod::GetName() { } } +inline ObjPtr ArtMethod::ResolveNameString() { + DCHECK(!IsProxyMethod()); + const DexFile::MethodId& method_id = GetDexFile()->GetMethodId(GetDexMethodIndex()); + return Runtime::Current()->GetClassLinker()->ResolveString(method_id.name_idx_, this); +} + inline const DexFile::CodeItem* ArtMethod::GetCodeItem() { return GetDexFile()->GetCodeItem(GetCodeItemOffset()); } diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 151c36f3bc..af7881d6ce 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -142,16 +142,6 @@ uint16_t ArtMethod::FindObsoleteDexClassDefIndex() { return dex_file->GetIndexForClassDef(*class_def); } -ObjPtr ArtMethod::GetNameAsString(Thread* self) { - CHECK(!IsProxyMethod()); - StackHandleScope<1> hs(self); - Handle dex_cache(hs.NewHandle(GetDexCache())); - auto* dex_file = dex_cache->GetDexFile(); - uint32_t dex_method_idx = GetDexMethodIndex(); - const DexFile::MethodId& method_id = dex_file->GetMethodId(dex_method_idx); - return Runtime::Current()->GetClassLinker()->ResolveString(method_id.name_idx_, dex_cache); -} - void ArtMethod::ThrowInvocationTimeError() { DCHECK(!IsInvokable()); // NOTE: IsDefaultConflicting must be first since the actual method might or might not be abstract diff --git a/runtime/art_method.h b/runtime/art_method.h index 012d706756..09debb0c50 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -589,7 +589,7 @@ class ArtMethod FINAL { ALWAYS_INLINE const char* GetName() REQUIRES_SHARED(Locks::mutator_lock_); - ObjPtr GetNameAsString(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr ResolveNameString() REQUIRES_SHARED(Locks::mutator_lock_); const DexFile::CodeItem* GetCodeItem() REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index 664b917543..2536b23416 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -61,6 +61,54 @@ inline ObjPtr ClassLinker::FindArrayClass(Thread* self, return array_class; } +inline ObjPtr ClassLinker::ResolveString(dex::StringIndex string_idx, + ArtField* referrer) { + Thread::PoisonObjectPointersIfDebug(); + DCHECK(!Thread::Current()->IsExceptionPending()); + // We do not need the read barrier for getting the DexCache for the initial resolved type + // lookup as both from-space and to-space copies point to the same native resolved types array. + ObjPtr resolved = + referrer->GetDexCache()->GetResolvedString(string_idx); + if (resolved == nullptr) { + resolved = DoResolveString(string_idx, referrer->GetDexCache()); + } + return resolved; +} + +inline ObjPtr ClassLinker::ResolveString(dex::StringIndex string_idx, + ArtMethod* referrer) { + Thread::PoisonObjectPointersIfDebug(); + DCHECK(!Thread::Current()->IsExceptionPending()); + // We do not need the read barrier for getting the DexCache for the initial resolved type + // lookup as both from-space and to-space copies point to the same native resolved types array. + ObjPtr resolved = + referrer->GetDexCache()->GetResolvedString(string_idx); + if (resolved == nullptr) { + resolved = DoResolveString(string_idx, referrer->GetDexCache()); + } + return resolved; +} + +inline ObjPtr ClassLinker::ResolveString(dex::StringIndex string_idx, + Handle dex_cache) { + Thread::PoisonObjectPointersIfDebug(); + DCHECK(!Thread::Current()->IsExceptionPending()); + ObjPtr resolved = dex_cache->GetResolvedString(string_idx); + if (resolved == nullptr) { + resolved = DoResolveString(string_idx, dex_cache); + } + return resolved; +} + +inline ObjPtr ClassLinker::LookupString(dex::StringIndex string_idx, + ObjPtr dex_cache) { + ObjPtr resolved = dex_cache->GetResolvedString(string_idx); + if (resolved == nullptr) { + resolved = DoLookupString(string_idx, dex_cache); + } + return resolved; +} + inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, ObjPtr referrer) { if (kObjPtrPoisoning) { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 67987963e1..526c6850c6 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -7665,14 +7665,15 @@ void ClassLinker::CreateReferenceInstanceOffsets(Handle klass) { klass->SetReferenceInstanceOffsets(reference_offsets); } -ObjPtr ClassLinker::ResolveString(dex::StringIndex string_idx, - Handle dex_cache) { - DCHECK(dex_cache != nullptr); - Thread::PoisonObjectPointersIfDebug(); - ObjPtr resolved = dex_cache->GetResolvedString(string_idx); - if (resolved != nullptr) { - return resolved; - } +ObjPtr ClassLinker::DoResolveString(dex::StringIndex string_idx, + ObjPtr dex_cache) { + StackHandleScope<1> hs(Thread::Current()); + Handle h_dex_cache(hs.NewHandle(dex_cache)); + return DoResolveString(string_idx, h_dex_cache); +} + +ObjPtr ClassLinker::DoResolveString(dex::StringIndex string_idx, + Handle dex_cache) { const DexFile& dex_file = *dex_cache->GetDexFile(); uint32_t utf16_length; const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length); @@ -7683,13 +7684,9 @@ ObjPtr ClassLinker::ResolveString(dex::StringIndex string_idx, return string; } -ObjPtr ClassLinker::LookupString(dex::StringIndex string_idx, - ObjPtr dex_cache) { +ObjPtr ClassLinker::DoLookupString(dex::StringIndex string_idx, + ObjPtr dex_cache) { DCHECK(dex_cache != nullptr); - ObjPtr resolved = dex_cache->GetResolvedString(string_idx); - if (resolved != nullptr) { - return resolved; - } const DexFile& dex_file = *dex_cache->GetDexFile(); uint32_t utf16_length; const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 58ce6eb25c..85817ac6ac 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -203,6 +203,16 @@ class ClassLinker { REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + // Resolve a String with the given index from the DexFile associated with the given `referrer`, + // storing the result in the DexCache. The `referrer` is used to identify the target DexCache + // to use for resolution. + ObjPtr ResolveString(dex::StringIndex string_idx, + ArtField* referrer) + REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr ResolveString(dex::StringIndex string_idx, + ArtMethod* referrer) + REQUIRES_SHARED(Locks::mutator_lock_); + // Resolve a String with the given index from the DexFile associated with the given DexCache, // storing the result in the DexCache. ObjPtr ResolveString(dex::StringIndex string_idx, @@ -885,6 +895,19 @@ class ClassLinker { ObjPtr class_loader) REQUIRES_SHARED(Locks::mutator_lock_); + // Implementation of ResolveString() called when the string was not found in the dex cache. + ObjPtr DoResolveString(dex::StringIndex string_idx, + ObjPtr dex_cache) + REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr DoResolveString(dex::StringIndex string_idx, + Handle dex_cache) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Implementation of LookupString() called when the string was not found in the dex cache. + ObjPtr DoLookupString(dex::StringIndex string_idx, + ObjPtr dex_cache) + REQUIRES_SHARED(Locks::mutator_lock_); + // Implementation of ResolveType() called when the type was not found in the dex cache. ObjPtr DoResolveType(dex::TypeIndex type_idx, ObjPtr referrer) diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index c533f9ca66..022857a4d5 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -743,33 +743,6 @@ inline ObjPtr ResolveVerifyAndClinit(dex::TypeIndex type_idx, return h_class.Get(); } -static inline ObjPtr ResolveString(ClassLinker* class_linker, - dex::StringIndex string_idx, - ArtMethod* referrer) - REQUIRES_SHARED(Locks::mutator_lock_) { - Thread::PoisonObjectPointersIfDebug(); - ObjPtr string = referrer->GetDexCache()->GetResolvedString(string_idx); - if (UNLIKELY(string == nullptr)) { - StackHandleScope<1> hs(Thread::Current()); - Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); - string = class_linker->ResolveString(string_idx, dex_cache); - } - return string; -} - -inline ObjPtr ResolveStringFromCode(ArtMethod* referrer, - dex::StringIndex string_idx) { - Thread::PoisonObjectPointersIfDebug(); - ObjPtr string = referrer->GetDexCache()->GetResolvedString(string_idx); - if (UNLIKELY(string == nullptr)) { - StackHandleScope<1> hs(Thread::Current()); - Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - string = class_linker->ResolveString(string_idx, dex_cache); - } - return string; -} - inline void UnlockJniSynchronizedMethod(jobject locked, Thread* self) { // Save any pending exception over monitor exit call. mirror::Throwable* saved_exception = nullptr; diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index 1f4475f6ff..9d70b03dfa 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -162,11 +162,6 @@ ObjPtr ResolveMethodTypeFromCode(ArtMethod* referrer, dex::P REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); -inline ObjPtr ResolveStringFromCode(ArtMethod* referrer, - dex::StringIndex string_idx) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Roles::uninterruptible_); - // TODO: annotalysis disabled as monitor semantics are maintained in Java code. inline void UnlockJniSynchronizedMethod(jobject locked, Thread* self) NO_THREAD_SAFETY_ANALYSIS REQUIRES(!Roles::uninterruptible_); diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index fa536c77a9..62756123e1 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -210,7 +210,8 @@ extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, CalleeSaveType::kSaveEverything); ArtMethod* caller = caller_and_outer.caller; - ObjPtr result = ResolveStringFromCode(caller, dex::StringIndex(string_idx)); + ObjPtr result = + Runtime::Current()->GetClassLinker()->ResolveString(dex::StringIndex(string_idx), caller); if (LIKELY(result != nullptr) && CanReferenceBss(caller_and_outer.outer_method, caller)) { StoreStringInBss(caller_and_outer.outer_method, dex::StringIndex(string_idx), result); } diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 90e89cf3db..27f761a144 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -945,11 +945,9 @@ static bool GetArgumentForBootstrapMethod(Thread* self, return true; } case EncodedArrayValueIterator::ValueType::kString: { - StackHandleScope<1> hs(self); - Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); dex::StringIndex index(static_cast(encoded_value->GetI())); ClassLinker* cl = Runtime::Current()->GetClassLinker(); - ObjPtr o = cl->ResolveString(index, dex_cache); + ObjPtr o = cl->ResolveString(index, referrer); if (UNLIKELY(o.IsNull())) { DCHECK(self->IsExceptionPending()); return false; diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 0ee780d32d..60bf50546f 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -340,12 +340,8 @@ static inline ObjPtr ResolveString(Thread* self, } } ArtMethod* method = shadow_frame.GetMethod(); - ObjPtr string_ptr = method->GetDexCache()->GetResolvedString(string_idx); - if (UNLIKELY(string_ptr == nullptr)) { - StackHandleScope<1> hs(self); - Handle dex_cache(hs.NewHandle(method->GetDexCache())); - string_ptr = Runtime::Current()->GetClassLinker()->ResolveString(string_idx, dex_cache); - } + ObjPtr string_ptr = + Runtime::Current()->GetClassLinker()->ResolveString(string_idx, method); return string_ptr; } diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 31a83f8e48..227ace08c2 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -1269,7 +1269,7 @@ ObjPtr Class::GetDeclaredMethodInternal( for (auto& m : h_klass->GetDeclaredVirtualMethods(kPointerSize)) { auto* np_method = m.GetInterfaceMethodIfProxy(kPointerSize); // May cause thread suspension. - ObjPtr np_name = np_method->GetNameAsString(self); + ObjPtr np_name = np_method->ResolveNameString(); if (!np_name->Equals(h_method_name.Get()) || !np_method->EqualParameters(h_args)) { if (UNLIKELY(self->IsExceptionPending())) { return nullptr; @@ -1291,7 +1291,7 @@ ObjPtr Class::GetDeclaredMethodInternal( } auto* np_method = m.GetInterfaceMethodIfProxy(kPointerSize); // May cause thread suspension. - ObjPtr np_name = np_method->GetNameAsString(self); + ObjPtr np_name = np_method->ResolveNameString(); if (np_name == nullptr) { self->AssertPendingException(); return nullptr; diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc index a40cb9b2e6..a10db9115f 100644 --- a/runtime/native/java_lang_reflect_Executable.cc +++ b/runtime/native/java_lang_reflect_Executable.cc @@ -320,7 +320,7 @@ static jstring Executable_getMethodNameInternal(JNIEnv* env, jobject javaMethod) ScopedFastNativeObjectAccess soa(env); ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize); - return soa.AddLocalReference(method->GetNameAsString(soa.Self())); + return soa.AddLocalReference(method->ResolveNameString()); } static jclass Executable_getMethodReturnTypeInternal(JNIEnv* env, jobject javaMethod) { diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 8766692a8c..895b2f9fd7 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -464,8 +464,7 @@ static jlong Field_getArtField(JNIEnv* env, jobject javaField) { static jstring Field_getNameInternal(JNIEnv* env, jobject javaField) { ScopedFastNativeObjectAccess soa(env); ArtField* field = soa.Decode(javaField)->GetArtField(); - return soa.AddLocalReference( - field->GetStringName(soa.Self(), true /* resolve */)); + return soa.AddLocalReference(field->ResolveNameString()); } static jobjectArray Field_getDeclaredAnnotations(JNIEnv* env, jobject javaField) { diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc index 0cb5e565e9..1d54d21187 100644 --- a/runtime/reference_table_test.cc +++ b/runtime/reference_table_test.cc @@ -290,7 +290,7 @@ TEST_F(ReferenceTableTest, SummaryOrder) { } { - // Differently sized byte arrays. Should be sorted by identical (non-unique cound). + // Differently sized byte arrays. Should be sorted by identical (non-unique count). StackHandleScope<1> hs(soa.Self()); Handle b1_1 = hs.NewHandle(mirror::ByteArray::Alloc(soa.Self(), 1)); rt.Add(b1_1.Get()); -- GitLab From 026105570d8fcd4b3cb60c3d6b7bb7141cd8146c Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 4 Jun 2018 14:38:00 +0100 Subject: [PATCH 527/749] Remove ClassLinker::array_iftable_. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Change-Id: Ie0d084945757ec8fcbaa5d02bbc24288f649bac7 --- runtime/class_linker.cc | 26 ++++++++++++-------------- runtime/class_linker.h | 5 ++--- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 526c6850c6..941c7ec391 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -376,7 +376,6 @@ ClassLinker::ClassLinker(InternTable* intern_table) : boot_class_table_(new ClassTable()), failed_dex_cache_class_lookups_(0), class_roots_(nullptr), - array_iftable_(nullptr), find_array_class_cache_next_victim_(0), init_done_(false), log_new_roots_(false), @@ -512,6 +511,10 @@ bool ClassLinker::InitWithoutImage(std::vector> b // Fill in the empty iftable. Needs to be done after the kObjectArrayClass root is set. java_lang_Object->SetIfTable(AllocIfTable(self, 0)); + // Create array interface entries to populate once we can load system classes. + object_array_class->SetIfTable(AllocIfTable(self, 2)); + DCHECK_EQ(GetArrayIfTable(), object_array_class->GetIfTable()); + // Setup the primitive type classes. SetClassRoot(ClassRoot::kPrimitiveBoolean, CreatePrimitiveClass(self, Primitive::kPrimBoolean)); SetClassRoot(ClassRoot::kPrimitiveByte, CreatePrimitiveClass(self, Primitive::kPrimByte)); @@ -523,9 +526,6 @@ bool ClassLinker::InitWithoutImage(std::vector> b SetClassRoot(ClassRoot::kPrimitiveDouble, CreatePrimitiveClass(self, Primitive::kPrimDouble)); SetClassRoot(ClassRoot::kPrimitiveVoid, CreatePrimitiveClass(self, Primitive::kPrimVoid)); - // Create array interface entries to populate once we can load system classes. - array_iftable_ = GcRoot(AllocIfTable(self, 2)); - // Create int array type for native pointer arrays (for example vtables) on 32-bit archs. Handle int_array_class(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_)))); @@ -639,8 +639,8 @@ bool ClassLinker::InitWithoutImage(std::vector> b CHECK(java_io_Serializable != nullptr); // We assume that Cloneable/Serializable don't have superinterfaces -- normally we'd have to // crawl up and explicitly list all of the supers as well. - array_iftable_.Read()->SetInterface(0, java_lang_Cloneable.Get()); - array_iftable_.Read()->SetInterface(1, java_io_Serializable.Get()); + object_array_class->GetIfTable()->SetInterface(0, java_lang_Cloneable.Get()); + object_array_class->GetIfTable()->SetInterface(1, java_io_Serializable.Get()); // Sanity check Class[] and Object[]'s interfaces. GetDirectInterface may cause thread // suspension. @@ -841,7 +841,7 @@ void ClassLinker::FinishInit(Thread* self) { // if possible add new checks there to catch errors early } - CHECK(!array_iftable_.IsNull()); + CHECK(GetArrayIfTable() != nullptr); // disable the slow paths in FindClass and CreatePrimitiveClass now // that Object, Class, and Object[] are setup @@ -1000,11 +1000,6 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { runtime->SetSentinel(heap->AllocNonMovableObject( self, java_lang_Object, java_lang_Object->GetObjectSize(), VoidFunctor())); - // reinit array_iftable_ from any array class instance, they should be == - array_iftable_ = - GcRoot(GetClassRoot(ClassRoot::kObjectArrayClass, this)->GetIfTable()); - DCHECK_EQ(array_iftable_.Read(), GetClassRoot(ClassRoot::kBooleanArrayClass, this)->GetIfTable()); - for (gc::space::ImageSpace* image_space : spaces) { // Boot class loader, use a null handle. std::vector> dex_files; @@ -1931,7 +1926,6 @@ void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) { void ClassLinker::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) { class_roots_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); VisitClassRoots(visitor, flags); - array_iftable_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); // Instead of visiting the find_array_class_cache_ drop it so that it doesn't prevent class // unloading if we are marking roots. DropFindArrayClassCache(); @@ -3553,6 +3547,10 @@ ObjPtr ClassLinker::CreatePrimitiveClass(Thread* self, Primitive: return h_class.Get(); } +inline ObjPtr ClassLinker::GetArrayIfTable() { + return GetClassRoot>(this)->GetIfTable(); +} + // Create an array class (i.e. the class object for the array, not the // array itself). "descriptor" looks like "[C" or "[[[[B" or // "[Ljava/lang/String;". @@ -3680,7 +3678,7 @@ ObjPtr ClassLinker::CreateArrayClass(Thread* self, // Use the single, global copies of "interfaces" and "iftable" // (remember not to free them for arrays). { - ObjPtr array_iftable = array_iftable_.Read(); + ObjPtr array_iftable = GetArrayIfTable(); CHECK(array_iftable != nullptr); new_class->SetIfTable(array_iftable); } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 85817ac6ac..f129e0f971 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -1275,6 +1275,8 @@ class ClassLinker { ObjPtr class_loader) REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr GetArrayIfTable() REQUIRES_SHARED(Locks::mutator_lock_); + std::vector boot_class_path_; std::vector> boot_dex_files_; @@ -1304,9 +1306,6 @@ class ClassLinker { // Well known mirror::Class roots. GcRoot> class_roots_; - // The interface table used by all arrays. - GcRoot array_iftable_; - // A cache of the last FindArrayClass results. The cache serves to avoid creating array class // descriptors for the sake of performing FindClass. static constexpr size_t kFindArrayCacheSize = 16; -- GitLab From c13fbd8596988f1daf71197008007c2eaa380585 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 4 Jun 2018 16:16:28 +0100 Subject: [PATCH 528/749] Use pre-allocated Throwables from the boot image. The pre-allocated OOMEs and NoClassDefFoundError were stored in the boot image but they were not used, we instead used to allocate and use new objects. This change adds references to the image roots, so that these Throwables can be used when starting the runtime using the boot image. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Bug: 77947463 Change-Id: I2079344dee61242bf0bef5c32770c33ac8a6b7a4 --- dex2oat/linker/image_writer.cc | 8 +++ oatdump/oatdump.cc | 12 ++-- patchoat/patchoat.cc | 2 +- runtime/class_linker.cc | 3 +- runtime/gc/space/image_space.cc | 2 +- runtime/image-inl.h | 7 ++- runtime/image.cc | 2 +- runtime/image.h | 11 +++- runtime/mirror/class.cc | 2 +- runtime/runtime.cc | 102 +++++++++++++++++++------------- runtime/runtime.h | 5 -- 11 files changed, 95 insertions(+), 61 deletions(-) diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 028de34e96..dc0709013c 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -1342,6 +1342,14 @@ ObjectArray* ImageWriter::CreateImageRoots(size_t oat_index) const { ObjectArray::Alloc(self, object_array_class.Get(), image_roots_size))); image_roots->Set(ImageHeader::kDexCaches, dex_caches.Get()); image_roots->Set(ImageHeader::kClassRoots, class_linker->GetClassRoots()); + image_roots->Set(ImageHeader::kOomeWhenThrowingException, + runtime->GetPreAllocatedOutOfMemoryErrorWhenThrowingException()); + image_roots->Set(ImageHeader::kOomeWhenThrowingOome, + runtime->GetPreAllocatedOutOfMemoryErrorWhenThrowingOOME()); + image_roots->Set(ImageHeader::kOomeWhenHandlingStackOverflow, + runtime->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow()); + image_roots->Set(ImageHeader::kNoClassDefFoundError, + runtime->GetPreAllocatedNoClassDefFoundError()); // image_roots[ImageHeader::kClassLoader] will be set later for app image. static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax, "Class loader should be the last image root."); diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 7ac9e984ff..7b72e189b9 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -105,6 +105,10 @@ const char* image_methods_descriptions_[] = { const char* image_roots_descriptions_[] = { "kDexCaches", "kClassRoots", + "kOomeWhenThrowingException", + "kOomeWhenThrowingOome", + "kOomeWhenHandlingStackOverflow", + "kNoClassDefFoundError", "kClassLoader", }; @@ -1942,17 +1946,17 @@ class ImageDumper { os << "COMPILE PIC: " << (image_header_.CompilePic() ? "yes" : "no") << "\n\n"; { - os << "ROOTS: " << reinterpret_cast(image_header_.GetImageRoots()) << "\n"; + os << "ROOTS: " << reinterpret_cast(image_header_.GetImageRoots().Ptr()) << "\n"; static_assert(arraysize(image_roots_descriptions_) == static_cast(ImageHeader::kImageRootsMax), "sizes must match"); DCHECK_LE(image_header_.GetImageRoots()->GetLength(), ImageHeader::kImageRootsMax); for (int32_t i = 0, size = image_header_.GetImageRoots()->GetLength(); i != size; ++i) { ImageHeader::ImageRoot image_root = static_cast(i); const char* image_root_description = image_roots_descriptions_[i]; - mirror::Object* image_root_object = image_header_.GetImageRoot(image_root); - indent_os << StringPrintf("%s: %p\n", image_root_description, image_root_object); + ObjPtr image_root_object = image_header_.GetImageRoot(image_root); + indent_os << StringPrintf("%s: %p\n", image_root_description, image_root_object.Ptr()); if (image_root_object != nullptr && image_root_object->IsObjectArray()) { - mirror::ObjectArray* image_root_object_array + ObjPtr> image_root_object_array = image_root_object->AsObjectArray(); ScopedIndentation indent2(&vios_); for (int j = 0; j < image_root_object_array->GetLength(); j++) { diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index a6d3903f19..3c0b3e42c9 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -973,7 +973,7 @@ bool PatchOat::PatchImage(bool primary_image) { ImageHeader* image_header = reinterpret_cast(image_->Begin()); CHECK_GT(image_->Size(), sizeof(ImageHeader)); // These are the roots from the original file. - auto* img_roots = image_header->GetImageRoots(); + mirror::ObjectArray* img_roots = image_header->GetImageRoots().Ptr(); image_header->RelocateImage(delta_); PatchArtFields(image_header); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 941c7ec391..d0fe394858 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -197,8 +197,7 @@ static void HandleEarlierVerifyError(Thread* self, } } else { // Previous error has been stored as an instance. Just rethrow. - ObjPtr throwable_class = - self->DecodeJObject(WellKnownClasses::java_lang_Throwable)->AsClass(); + ObjPtr throwable_class = GetClassRoot(class_linker); ObjPtr error_class = obj->GetClass(); CHECK(throwable_class->IsAssignableFrom(error_class)); self->SetException(obj->AsThrowable()); diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index dbe09e8c5b..e754fbcbae 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1287,7 +1287,7 @@ class ImageSpaceLoader { bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_object_visitor); // Fixup image roots. CHECK(app_image.InSource(reinterpret_cast( - image_header.GetImageRoots()))); + image_header.GetImageRoots().Ptr()))); image_header.RelocateImageObjects(app_image.Delta()); CHECK_EQ(image_header.GetImageBegin(), target_base); // Fix up dex cache DexFile pointers. diff --git a/runtime/image-inl.h b/runtime/image-inl.h index 3a66a34cb3..c527f6fbcc 100644 --- a/runtime/image-inl.h +++ b/runtime/image-inl.h @@ -23,18 +23,19 @@ #include "imt_conflict_table.h" #include "imtable.h" #include "mirror/object_array-inl.h" +#include "obj_ptr-inl.h" #include "read_barrier-inl.h" namespace art { template -inline mirror::Object* ImageHeader::GetImageRoot(ImageRoot image_root) const { - mirror::ObjectArray* image_roots = GetImageRoots(); +inline ObjPtr ImageHeader::GetImageRoot(ImageRoot image_root) const { + ObjPtr> image_roots = GetImageRoots(); return image_roots->Get(static_cast(image_root)); } template -inline mirror::ObjectArray* ImageHeader::GetImageRoots() const { +inline ObjPtr> ImageHeader::GetImageRoots() const { // Need a read barrier as it's not visited during root scan. // Pass in the address of the local variable to the read barrier // rather than image_roots_ because it won't move (asserted below) diff --git a/runtime/image.cc b/runtime/image.cc index 7ad2e7bf95..17fc664bd7 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -26,7 +26,7 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '0', '\0' }; // ClassRoot::MethodHandle. +const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '1', '\0' }; // Pre-allocated Throwables. ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/image.h b/runtime/image.h index 8acd5bc4c4..c6fc052a60 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -27,6 +27,7 @@ namespace art { class ArtField; class ArtMethod; +template class ObjPtr; namespace linker { class ImageWriter; @@ -206,7 +207,11 @@ class PACKED(4) ImageHeader { enum ImageRoot { kDexCaches, kClassRoots, - kClassLoader, // App image only. + kOomeWhenThrowingException, // Pre-allocated OOME when throwing exception. + kOomeWhenThrowingOome, // Pre-allocated OOME when throwing OOME. + kOomeWhenHandlingStackOverflow, // Pre-allocated OOME when handling StackOverflowError. + kNoClassDefFoundError, // Pre-allocated NoClassDefFoundError. + kClassLoader, // App image only. kImageRootsMax, }; @@ -277,11 +282,11 @@ class PACKED(4) ImageHeader { } template - mirror::Object* GetImageRoot(ImageRoot image_root) const + ObjPtr GetImageRoot(ImageRoot image_root) const REQUIRES_SHARED(Locks::mutator_lock_); template - mirror::ObjectArray* GetImageRoots() const + ObjPtr> GetImageRoots() const REQUIRES_SHARED(Locks::mutator_lock_); void RelocateImage(off_t delta); diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 227ace08c2..44b0c2b007 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -419,7 +419,7 @@ bool Class::IsInSamePackage(ObjPtr that) { } bool Class::IsThrowableClass() { - return WellKnownClasses::ToClass(WellKnownClasses::java_lang_Throwable)->IsAssignableFrom(this); + return GetClassRoot()->IsAssignableFrom(this); } void Class::SetClassLoader(ObjPtr new_class_loader) { diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 0d9d16cd01..1e327fc8ed 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1090,6 +1090,17 @@ void Runtime::SetSentinel(mirror::Object* sentinel) { sentinel_ = GcRoot(sentinel); } +static inline void InitPreAllocatedException(Thread* self, + GcRoot* exception, + const char* exception_class_descriptor, + const char* msg) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_EQ(self, Thread::Current()); + self->ThrowNewException(exception_class_descriptor, msg); + *exception = GcRoot(self->GetException()); + self->ClearException(); +} + bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // (b/30160149): protect subprocesses from modifications to LD_LIBRARY_PATH, etc. // Take a snapshot of the environment at the time the runtime was created, for use by Exec, etc. @@ -1505,34 +1516,54 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // TODO: move this to just be an Trace::Start argument Trace::SetDefaultClockSource(runtime_options.GetOrDefault(Opt::ProfileClock)); - // Pre-allocate an OutOfMemoryError for the case when we fail to - // allocate the exception to be thrown. - InitPreAllocatedException(self, - &Runtime::pre_allocated_OutOfMemoryError_when_throwing_exception_, - "Ljava/lang/OutOfMemoryError;", - "OutOfMemoryError thrown while trying to throw an exception; " - "no stack trace available"); - // Pre-allocate an OutOfMemoryError for the double-OOME case. - InitPreAllocatedException(self, - &Runtime::pre_allocated_OutOfMemoryError_when_throwing_oome_, - "Ljava/lang/OutOfMemoryError;", - "OutOfMemoryError thrown while trying to throw OutOfMemoryError; " - "no stack trace available"); - // Pre-allocate an OutOfMemoryError for the case when we fail to - // allocate while handling a stack overflow. - InitPreAllocatedException(self, - &Runtime::pre_allocated_OutOfMemoryError_when_handling_stack_overflow_, - "Ljava/lang/OutOfMemoryError;", - "OutOfMemoryError thrown while trying to handle a stack overflow; " - "no stack trace available"); - - // Pre-allocate a NoClassDefFoundError for the common case of failing to find a system class - // ahead of checking the application's class loader. - InitPreAllocatedException(self, - &Runtime::pre_allocated_NoClassDefFoundError_, - "Ljava/lang/NoClassDefFoundError;", - "Class not found using the boot class loader; " - "no stack trace available"); + if (GetHeap()->HasBootImageSpace()) { + const ImageHeader& image_header = GetHeap()->GetBootImageSpaces()[0]->GetImageHeader(); + pre_allocated_OutOfMemoryError_when_throwing_exception_ = GcRoot( + image_header.GetImageRoot(ImageHeader::kOomeWhenThrowingException)->AsThrowable()); + DCHECK(pre_allocated_OutOfMemoryError_when_throwing_exception_.Read()->GetClass() + ->DescriptorEquals("Ljava/lang/OutOfMemoryError;")); + pre_allocated_OutOfMemoryError_when_throwing_oome_ = GcRoot( + image_header.GetImageRoot(ImageHeader::kOomeWhenThrowingOome)->AsThrowable()); + DCHECK(pre_allocated_OutOfMemoryError_when_throwing_oome_.Read()->GetClass() + ->DescriptorEquals("Ljava/lang/OutOfMemoryError;")); + pre_allocated_OutOfMemoryError_when_handling_stack_overflow_ = GcRoot( + image_header.GetImageRoot(ImageHeader::kOomeWhenHandlingStackOverflow)->AsThrowable()); + DCHECK(pre_allocated_OutOfMemoryError_when_handling_stack_overflow_.Read()->GetClass() + ->DescriptorEquals("Ljava/lang/OutOfMemoryError;")); + pre_allocated_NoClassDefFoundError_ = GcRoot( + image_header.GetImageRoot(ImageHeader::kNoClassDefFoundError)->AsThrowable()); + DCHECK(pre_allocated_NoClassDefFoundError_.Read()->GetClass() + ->DescriptorEquals("Ljava/lang/NoClassDefFoundError;")); + } else { + // Pre-allocate an OutOfMemoryError for the case when we fail to + // allocate the exception to be thrown. + InitPreAllocatedException(self, + &pre_allocated_OutOfMemoryError_when_throwing_exception_, + "Ljava/lang/OutOfMemoryError;", + "OutOfMemoryError thrown while trying to throw an exception; " + "no stack trace available"); + // Pre-allocate an OutOfMemoryError for the double-OOME case. + InitPreAllocatedException(self, + &pre_allocated_OutOfMemoryError_when_throwing_oome_, + "Ljava/lang/OutOfMemoryError;", + "OutOfMemoryError thrown while trying to throw OutOfMemoryError; " + "no stack trace available"); + // Pre-allocate an OutOfMemoryError for the case when we fail to + // allocate while handling a stack overflow. + InitPreAllocatedException(self, + &pre_allocated_OutOfMemoryError_when_handling_stack_overflow_, + "Ljava/lang/OutOfMemoryError;", + "OutOfMemoryError thrown while trying to handle a stack overflow; " + "no stack trace available"); + + // Pre-allocate a NoClassDefFoundError for the common case of failing to find a system class + // ahead of checking the application's class loader. + InitPreAllocatedException(self, + &pre_allocated_NoClassDefFoundError_, + "Ljava/lang/NoClassDefFoundError;", + "Class not found using the boot class loader; " + "no stack trace available"); + } // Runtime initialization is largely done now. // We load plugins first since that can modify the runtime state slightly. @@ -1682,16 +1713,6 @@ void Runtime::AttachAgent(JNIEnv* env, const std::string& agent_arg, jobject cla } } -void Runtime::InitPreAllocatedException(Thread* self, - GcRoot Runtime::* exception, - const char* exception_class_descriptor, - const char* msg) { - DCHECK_EQ(self, Thread::Current()); - self->ThrowNewException(exception_class_descriptor, msg); - this->*exception = GcRoot(self->GetException()); - self->ClearException(); -} - void Runtime::InitNativeMethods() { VLOG(startup) << "Runtime::InitNativeMethods entering"; Thread* self = Thread::Current(); @@ -2048,9 +2069,10 @@ void Runtime::VisitImageRoots(RootVisitor* visitor) { auto* image_space = space->AsImageSpace(); const auto& image_header = image_space->GetImageHeader(); for (int32_t i = 0, size = image_header.GetImageRoots()->GetLength(); i != size; ++i) { - auto* obj = image_header.GetImageRoot(static_cast(i)); + mirror::Object* obj = + image_header.GetImageRoot(static_cast(i)).Ptr(); if (obj != nullptr) { - auto* after_obj = obj; + mirror::Object* after_obj = obj; visitor->VisitRoot(&after_obj, RootInfo(kRootStickyClass)); CHECK_EQ(after_obj, obj); } diff --git a/runtime/runtime.h b/runtime/runtime.h index 10f72e7c5b..d85490c0a6 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -774,11 +774,6 @@ class Runtime { bool Init(RuntimeArgumentMap&& runtime_options) SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_); - void InitPreAllocatedException(Thread* self, - GcRoot Runtime::* exception, - const char* exception_class_descriptor, - const char* msg) - REQUIRES_SHARED(Locks::mutator_lock_); void InitNativeMethods() REQUIRES(!Locks::mutator_lock_); void RegisterRuntimeNativeMethods(JNIEnv* env); -- GitLab From 7eb9f14b5b6dc04f5e61e67b1e6b8ab96b88b49e Mon Sep 17 00:00:00 2001 From: Alex Light Date: Sat, 2 Jun 2018 05:37:39 +0000 Subject: [PATCH 529/749] Revert "Move runtime/ to ClassAccessor" Seems to cause 'atest CtsInlineMockingTestCases' and other tests to fail due to sending agents dex files with hiddenapi flags still present. This reverts commit 2649ecf6c59a29262556aa356fbf894d49df8fe7. Reason for revert: Seems to be causing sysui test failures, maybe Bug: 77709234 Bug: 79758018 Bug: 91962648 Test: Tree-Hugger (cherry picked from commit cc7e20f9ec7b4a7a57f7196e5e8be67a727f21d3) Change-Id: I3201382c432d211c84e8c176ec060b3b27e4f1a5 --- libdexfile/dex/class_accessor-inl.h | 101 +++----- libdexfile/dex/class_accessor.h | 78 ++---- libdexfile/dex/class_accessor_test.cc | 10 - libdexfile/dex/dex_file.cc | 17 +- libdexfile/dex/dex_file.h | 4 +- openjdkjvmti/fixed_up_dex_file.cc | 15 +- runtime/art_method.cc | 23 +- runtime/class_linker.cc | 290 +++++++++++----------- runtime/class_linker.h | 13 +- runtime/native/dalvik_system_VMRuntime.cc | 29 ++- runtime/vdex_file.cc | 40 +-- runtime/verifier/method_verifier.cc | 164 ++++++++---- runtime/verifier/method_verifier.h | 17 ++ 13 files changed, 417 insertions(+), 384 deletions(-) diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h index 3bb9e93e5a..49ca98d47f 100644 --- a/libdexfile/dex/class_accessor-inl.h +++ b/libdexfile/dex/class_accessor-inl.h @@ -37,26 +37,30 @@ inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const DexFile::Clas num_direct_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u), num_virtual_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u) {} -inline void ClassAccessor::Method::Read() { - index_ += DecodeUnsignedLeb128(&ptr_pos_); - access_flags_ = DecodeUnsignedLeb128(&ptr_pos_); - code_off_ = DecodeUnsignedLeb128(&ptr_pos_); +inline const uint8_t* ClassAccessor::Method::Read(const uint8_t* ptr) { + index_ += DecodeUnsignedLeb128(&ptr); + access_flags_ = DecodeUnsignedLeb128(&ptr); + code_off_ = DecodeUnsignedLeb128(&ptr); + return ptr; } -inline void ClassAccessor::Field::Read() { - index_ += DecodeUnsignedLeb128(&ptr_pos_); - access_flags_ = DecodeUnsignedLeb128(&ptr_pos_); +inline const uint8_t* ClassAccessor::Field::Read(const uint8_t* ptr) { + index_ += DecodeUnsignedLeb128(&ptr); + access_flags_ = DecodeUnsignedLeb128(&ptr); + return ptr; } template -inline void ClassAccessor::VisitMembers(size_t count, - const Visitor& visitor, - DataType* data) const { +inline const uint8_t* ClassAccessor::VisitMembers(size_t count, + const Visitor& visitor, + const uint8_t* ptr, + DataType* data) const { DCHECK(data != nullptr); for ( ; count != 0; --count) { - data->Read(); + ptr = data->Read(ptr); visitor(*data); } + return ptr; } template > - ClassAccessor::GetFieldsInternal(size_t count) const { - return { DataIterator(dex_file_, 0u, num_static_fields_, count, ptr_pos_), - DataIterator(dex_file_, count, num_static_fields_, count, ptr_pos_) }; -} - -// Return an iteration range for the first methods. -inline IterationRange> - ClassAccessor::GetMethodsInternal(size_t count) const { - // Skip over the fields. - Field field(dex_file_, ptr_pos_); - VisitMembers(NumFields(), VoidFunctor(), &field); - // Return the iterator pair. - return { DataIterator(dex_file_, 0u, num_direct_methods_, count, field.ptr_pos_), - DataIterator(dex_file_, count, num_direct_methods_, count, field.ptr_pos_) }; -} - inline IterationRange> ClassAccessor::GetFields() const { - return GetFieldsInternal(num_static_fields_ + num_instance_fields_); -} - -inline IterationRange> - ClassAccessor::GetStaticFields() const { - return GetFieldsInternal(num_static_fields_); -} - - -inline IterationRange> - ClassAccessor::GetInstanceFields() const { - IterationRange> fields = GetFields(); - // Skip the static fields. - return { std::next(fields.begin(), NumStaticFields()), fields.end() }; + const uint32_t limit = num_static_fields_ + num_instance_fields_; + return { DataIterator(dex_file_, 0u, num_static_fields_, limit, ptr_pos_), + DataIterator(dex_file_, limit, num_static_fields_, limit, ptr_pos_) }; } inline IterationRange> ClassAccessor::GetMethods() const { - return GetMethodsInternal(NumMethods()); -} - -inline IterationRange> - ClassAccessor::GetDirectMethods() const { - return GetMethodsInternal(NumDirectMethods()); -} - -inline IterationRange> - ClassAccessor::GetVirtualMethods() const { - IterationRange> methods = GetMethods(); - // Skip the direct fields. - return { std::next(methods.begin(), NumDirectMethods()), methods.end() }; -} - -inline void ClassAccessor::Field::UnHideAccessFlags() const { - DexFile::UnHideAccessFlags(const_cast(ptr_pos_), GetAccessFlags(), /*is_method*/ false); -} - -inline void ClassAccessor::Method::UnHideAccessFlags() const { - DexFile::UnHideAccessFlags(const_cast(ptr_pos_), GetAccessFlags(), /*is_method*/ true); + // Skip over the fields. + Field field(dex_file_); + const size_t skip_count = num_static_fields_ + num_instance_fields_; + const uint8_t* ptr_pos = VisitMembers(skip_count, VoidFunctor(), ptr_pos_, &field); + // Return the iterator pair for all the methods. + const uint32_t limit = num_direct_methods_ + num_virtual_methods_; + return { DataIterator(dex_file_, 0u, num_direct_methods_, limit, ptr_pos), + DataIterator(dex_file_, limit, num_direct_methods_, limit, ptr_pos) }; } } // namespace art diff --git a/libdexfile/dex/class_accessor.h b/libdexfile/dex/class_accessor.h index 4f0fd32e31..dda6e1c1a6 100644 --- a/libdexfile/dex/class_accessor.h +++ b/libdexfile/dex/class_accessor.h @@ -20,7 +20,6 @@ #include "base/utils.h" #include "code_item_accessors.h" #include "dex_file.h" -#include "hidden_api_access_flags.h" #include "invoke_type.h" #include "method_reference.h" #include "modifiers.h" @@ -34,18 +33,12 @@ class ClassAccessor { private: class BaseItem { public: - explicit BaseItem(const uint8_t* ptr_pos) : ptr_pos_(ptr_pos) {} - uint32_t GetIndex() const { return index_; } uint32_t GetAccessFlags() const { - return HiddenApiAccessFlags::RemoveFromDex(access_flags_); - } - - HiddenApiAccessFlags::ApiList DecodeHiddenAccessFlags() const { - return HiddenApiAccessFlags::DecodeFromDex(access_flags_); + return access_flags_; } bool IsFinal() const { @@ -53,8 +46,6 @@ class ClassAccessor { } protected: - // Internal data pointer for reading. - const uint8_t* ptr_pos_ = nullptr; uint32_t index_ = 0u; uint32_t access_flags_ = 0u; }; @@ -85,18 +76,13 @@ class ClassAccessor { return is_static_or_direct_; } - // Unhide the hidden API access flags at the iterator position. TODO: Deprecate. - void UnHideAccessFlags() const; - private: explicit Method(const DexFile& dex_file, - const uint8_t* ptr_pos, bool is_static_or_direct = true) - : BaseItem(ptr_pos), - dex_file_(dex_file), + : dex_file_(dex_file), is_static_or_direct_(is_static_or_direct) {} - void Read(); + const uint8_t* Read(const uint8_t* ptr); InvokeType GetDirectMethodInvokeType() const { return (GetAccessFlags() & kAccStatic) != 0 ? kStatic : kDirect; @@ -113,7 +99,6 @@ class ClassAccessor { } } - // Move to virtual method section. void NextSection() { DCHECK(is_static_or_direct_) << "Already in the virtual methods section"; is_static_or_direct_ = false; @@ -130,31 +115,20 @@ class ClassAccessor { // A decoded version of the field of a class_data_item. class Field : public BaseItem { public: - explicit Field(const DexFile& dex_file, - const uint8_t* ptr_pos) : BaseItem(ptr_pos), dex_file_(dex_file) {} + explicit Field(const DexFile& dex_file) : dex_file_(dex_file) {} const DexFile& GetDexFile() const { return dex_file_; } - bool IsStatic() const { - return is_static_; - } - - // Unhide the hidden API access flags at the iterator position. TODO: Deprecate. - void UnHideAccessFlags() const; - private: - void Read(); + const uint8_t* Read(const uint8_t* ptr); - // Move to instance fields section. void NextSection() { index_ = 0u; - is_static_ = false; } const DexFile& dex_file_; - bool is_static_ = true; friend class ClassAccessor; }; @@ -170,10 +144,11 @@ class ClassAccessor { uint32_t partition_pos, uint32_t iterator_end, const uint8_t* ptr_pos) - : data_(dex_file, ptr_pos), + : data_(dex_file), position_(position), partition_pos_(partition_pos), - iterator_end_(iterator_end) { + iterator_end_(iterator_end), + ptr_pos_(ptr_pos) { ReadData(); } @@ -230,7 +205,8 @@ class ClassAccessor { if (position_ == partition_pos_) { data_.NextSection(); } - data_.Read(); + DCHECK(ptr_pos_ != nullptr); + ptr_pos_ = data_.Read(ptr_pos_); } } @@ -241,6 +217,8 @@ class ClassAccessor { const uint32_t partition_pos_; // At iterator_end_, the iterator is no longer valid. const uint32_t iterator_end_; + // Internal data pointer. + const uint8_t* ptr_pos_; }; // Not explicit specifically for range-based loops. @@ -274,21 +252,9 @@ class ClassAccessor { // Return the iteration range for all the fields. IterationRange> GetFields() const; - // Return the iteration range for all the static fields. - IterationRange> GetStaticFields() const; - - // Return the iteration range for all the instance fields. - IterationRange> GetInstanceFields() const; - // Return the iteration range for all the methods. IterationRange> GetMethods() const; - // Return the iteration range for the direct methods. - IterationRange> GetDirectMethods() const; - - // Return the iteration range for the virtual methods. - IterationRange> GetVirtualMethods() const; - uint32_t NumStaticFields() const { return num_static_fields_; } @@ -297,10 +263,6 @@ class ClassAccessor { return num_instance_fields_; } - uint32_t NumFields() const { - return NumStaticFields() + NumInstanceFields(); - } - uint32_t NumDirectMethods() const { return num_direct_methods_; } @@ -323,22 +285,14 @@ class ClassAccessor { return dex_file_; } - bool HasClassData() const { - return ptr_pos_ != nullptr; - } - protected: // Template visitor to reduce copy paste for visiting elements. // No thread safety analysis since the visitor may require capabilities. template - void VisitMembers(size_t count, const Visitor& visitor, DataType* data) const - NO_THREAD_SAFETY_ANALYSIS; - - // Return an iteration range for the first fields. - IterationRange> GetFieldsInternal(size_t count) const; - - // Return an iteration range for the first methods. - IterationRange> GetMethodsInternal(size_t count) const; + const uint8_t* VisitMembers(size_t count, + const Visitor& visitor, + const uint8_t* ptr, + DataType* data) const NO_THREAD_SAFETY_ANALYSIS; const DexFile& dex_file_; const dex::TypeIndex descriptor_index_ = {}; diff --git a/libdexfile/dex/class_accessor_test.cc b/libdexfile/dex/class_accessor_test.cc index d0533c1811..95380d8140 100644 --- a/libdexfile/dex/class_accessor_test.cc +++ b/libdexfile/dex/class_accessor_test.cc @@ -38,27 +38,18 @@ TEST_F(ClassAccessorTest, TestVisiting) { auto fields = accessor.GetFields(); auto method_it = methods.begin(); auto field_it = fields.begin(); - auto instance_fields = accessor.GetInstanceFields(); - auto instance_field_it = instance_fields.begin(); accessor.VisitFieldsAndMethods( // Static fields. [&](const ClassAccessor::Field& field) { - EXPECT_TRUE(field.IsStatic()); - EXPECT_TRUE(field_it->IsStatic()); EXPECT_EQ(field.GetIndex(), field_it->GetIndex()); EXPECT_EQ(field.GetAccessFlags(), field_it->GetAccessFlags()); ++field_it; }, // Instance fields. [&](const ClassAccessor::Field& field) { - EXPECT_FALSE(field.IsStatic()); - EXPECT_FALSE(field_it->IsStatic()); EXPECT_EQ(field.GetIndex(), field_it->GetIndex()); EXPECT_EQ(field.GetAccessFlags(), field_it->GetAccessFlags()); - EXPECT_EQ(field.GetIndex(), instance_field_it->GetIndex()); - EXPECT_EQ(field.GetAccessFlags(), instance_field_it->GetAccessFlags()); ++field_it; - ++instance_field_it; }, // Direct methods. [&](const ClassAccessor::Method& method) { @@ -80,7 +71,6 @@ TEST_F(ClassAccessorTest, TestVisiting) { }); ASSERT_TRUE(field_it == fields.end()); ASSERT_TRUE(method_it == methods.end()); - ASSERT_TRUE(instance_field_it == instance_fields.end()); } EXPECT_EQ(class_def_idx, dex_file->NumClassDefs()); } diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc index f570158dfb..9de260c862 100644 --- a/libdexfile/dex/dex_file.cc +++ b/libdexfile/dex/dex_file.cc @@ -45,18 +45,19 @@ static_assert(std::is_trivially_copyable::value, "StringIndex static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wrong"); static_assert(std::is_trivially_copyable::value, "TypeIndex not trivial"); -void DexFile::UnHideAccessFlags(uint8_t* data_ptr, - uint32_t new_access_flags, - bool is_method) { +void DexFile::UnHideAccessFlags(ClassDataItemIterator& class_it) { + uint8_t* data = const_cast(class_it.DataPointer()); + uint32_t new_flag = class_it.GetMemberAccessFlags(); + bool is_method = class_it.IsAtMethod(); // Go back 1 uleb to start. - data_ptr = ReverseSearchUnsignedLeb128(data_ptr); + data = ReverseSearchUnsignedLeb128(data); if (is_method) { // Methods have another uleb field before the access flags - data_ptr = ReverseSearchUnsignedLeb128(data_ptr); + data = ReverseSearchUnsignedLeb128(data); } - DCHECK_EQ(HiddenApiAccessFlags::RemoveFromDex(DecodeUnsignedLeb128WithoutMovingCursor(data_ptr)), - new_access_flags); - UpdateUnsignedLeb128(data_ptr, new_access_flags); + DCHECK_EQ(HiddenApiAccessFlags::RemoveFromDex(DecodeUnsignedLeb128WithoutMovingCursor(data)), + new_flag); + UpdateUnsignedLeb128(data, new_flag); } uint32_t DexFile::CalculateChecksum() const { diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index ed219808d2..f1f8b505bd 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -1010,8 +1010,8 @@ class DexFile { return container_.get(); } - // Changes the dex class data pointed to by data_ptr it to not have any hiddenapi flags. - static void UnHideAccessFlags(uint8_t* data_ptr, uint32_t new_access_flags, bool is_method); + // Changes the dex file pointed to by class_it to not have any hiddenapi flags. + static void UnHideAccessFlags(ClassDataItemIterator& class_it); inline IterationRange GetClasses() const; diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc index a660fb56c4..fcbafe7e71 100644 --- a/openjdkjvmti/fixed_up_dex_file.cc +++ b/openjdkjvmti/fixed_up_dex_file.cc @@ -31,7 +31,6 @@ #include "base/leb128.h" #include "fixed_up_dex_file.h" -#include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "dex/dex_file_verifier.h" @@ -52,12 +51,14 @@ static void RecomputeDexChecksum(art::DexFile* dex_file) { } static void UnhideApis(const art::DexFile& target_dex_file) { - for (art::ClassAccessor accessor : target_dex_file.GetClasses()) { - for (const art::ClassAccessor::Field& field : accessor.GetFields()) { - field.UnHideAccessFlags(); - } - for (const art::ClassAccessor::Method& method : accessor.GetMethods()) { - method.UnHideAccessFlags(); + for (uint32_t i = 0; i < target_dex_file.NumClassDefs(); ++i) { + const uint8_t* class_data = target_dex_file.GetClassData(target_dex_file.GetClassDef(i)); + if (class_data != nullptr) { + for (art::ClassDataItemIterator class_it(target_dex_file, class_data); + class_it.HasNext(); + class_it.Next()) { + art::DexFile::UnHideAccessFlags(class_it); + } } } } diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 4e9f3c52e2..151c36f3bc 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -26,7 +26,6 @@ #include "class_linker-inl.h" #include "class_root.h" #include "debugger.h" -#include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_exception_helpers.h" @@ -435,14 +434,28 @@ bool ArtMethod::IsPolymorphicSignature() { static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx) { - ClassAccessor accessor(dex_file, dex_file.GetClassDef(class_def_idx)); - uint32_t class_def_method_index = 0u; - for (const ClassAccessor::Method& method : accessor.GetMethods()) { - if (method.GetIndex() == method_idx) { + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx); + const uint8_t* class_data = dex_file.GetClassData(class_def); + CHECK(class_data != nullptr); + ClassDataItemIterator it(dex_file, class_data); + it.SkipAllFields(); + // Process methods + size_t class_def_method_index = 0; + while (it.HasNextDirectMethod()) { + if (it.GetMemberIndex() == method_idx) { return class_def_method_index; } class_def_method_index++; + it.Next(); } + while (it.HasNextVirtualMethod()) { + if (it.GetMemberIndex() == method_idx) { + return class_def_method_index; + } + class_def_method_index++; + it.Next(); + } + DCHECK(!it.HasNext()); LOG(FATAL) << "Failed to find method index " << method_idx << " in " << dex_file.GetLocation(); UNREACHABLE(); } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 531c68a489..dccdff0a5d 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -56,7 +56,6 @@ #include "compiler_callbacks.h" #include "debug_print.h" #include "debugger.h" -#include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_exception_helpers.h" @@ -2731,50 +2730,52 @@ ObjPtr ClassLinker::DefineClass(Thread* self, uint32_t ClassLinker::SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def) { + const uint8_t* class_data = dex_file.GetClassData(dex_class_def); size_t num_ref = 0; size_t num_8 = 0; size_t num_16 = 0; size_t num_32 = 0; size_t num_64 = 0; - ClassAccessor accessor(dex_file, dex_class_def); - // We allow duplicate definitions of the same field in a class_data_item - // but ignore the repeated indexes here, b/21868015. - uint32_t last_field_idx = dex::kDexNoIndex; - for (const ClassAccessor::Field& field : accessor.GetStaticFields()) { - uint32_t field_idx = field.GetIndex(); - // Ordering enforced by DexFileVerifier. - DCHECK(last_field_idx == dex::kDexNoIndex || last_field_idx <= field_idx); - if (UNLIKELY(field_idx == last_field_idx)) { - continue; - } - last_field_idx = field_idx; - const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); - const char* descriptor = dex_file.GetFieldTypeDescriptor(field_id); - char c = descriptor[0]; - switch (c) { - case 'L': - case '[': - num_ref++; - break; - case 'J': - case 'D': - num_64++; - break; - case 'I': - case 'F': - num_32++; - break; - case 'S': - case 'C': - num_16++; - break; - case 'B': - case 'Z': - num_8++; - break; - default: - LOG(FATAL) << "Unknown descriptor: " << c; - UNREACHABLE(); + if (class_data != nullptr) { + // We allow duplicate definitions of the same field in a class_data_item + // but ignore the repeated indexes here, b/21868015. + uint32_t last_field_idx = dex::kDexNoIndex; + for (ClassDataItemIterator it(dex_file, class_data); it.HasNextStaticField(); it.Next()) { + uint32_t field_idx = it.GetMemberIndex(); + // Ordering enforced by DexFileVerifier. + DCHECK(last_field_idx == dex::kDexNoIndex || last_field_idx <= field_idx); + if (UNLIKELY(field_idx == last_field_idx)) { + continue; + } + last_field_idx = field_idx; + const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); + const char* descriptor = dex_file.GetFieldTypeDescriptor(field_id); + char c = descriptor[0]; + switch (c) { + case 'L': + case '[': + num_ref++; + break; + case 'J': + case 'D': + num_64++; + break; + case 'I': + case 'F': + num_32++; + break; + case 'S': + case 'C': + num_16++; + break; + case 'B': + case 'Z': + num_8++; + break; + default: + LOG(FATAL) << "Unknown descriptor: " << c; + UNREACHABLE(); + } } } return mirror::Class::ComputeClassSize(false, @@ -2872,15 +2873,17 @@ void ClassLinker::FixupStaticTrampolines(ObjPtr klass) { const DexFile& dex_file = klass->GetDexFile(); const DexFile::ClassDef* dex_class_def = klass->GetClassDef(); CHECK(dex_class_def != nullptr); - ClassAccessor accessor(dex_file, *dex_class_def); + const uint8_t* class_data = dex_file.GetClassData(*dex_class_def); // There should always be class data if there were direct methods. - CHECK(accessor.HasClassData()) << klass->PrettyDescriptor(); + CHECK(class_data != nullptr) << klass->PrettyDescriptor(); + ClassDataItemIterator it(dex_file, class_data); + it.SkipAllFields(); bool has_oat_class; OatFile::OatClass oat_class = OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class); // Link the code of methods skipped by LinkCode. - for (size_t method_index = 0; method_index < accessor.NumDirectMethods(); ++method_index) { + for (size_t method_index = 0; it.HasNextDirectMethod(); ++method_index, it.Next()) { ArtMethod* method = klass->GetDirectMethod(method_index, image_pointer_size_); if (!method->IsStatic()) { // Only update static methods. @@ -2989,6 +2992,17 @@ void ClassLinker::SetupClass(const DexFile& dex_file, klass->SetDexTypeIndex(dex_class_def.class_idx_); } +void ClassLinker::LoadClass(Thread* self, + const DexFile& dex_file, + const DexFile::ClassDef& dex_class_def, + Handle klass) { + const uint8_t* class_data = dex_file.GetClassData(dex_class_def); + if (class_data == nullptr) { + return; // no fields or methods - for example a marker interface + } + LoadClassMembers(self, dex_file, class_data, klass); +} + LengthPrefixedArray* ClassLinker::AllocArtFieldArray(Thread* self, LinearAlloc* allocator, size_t length) { @@ -3047,15 +3061,10 @@ LinearAlloc* ClassLinker::GetOrCreateAllocatorForClassLoader(ObjPtr klass) { - ClassAccessor accessor(dex_file, dex_class_def); - if (!accessor.HasClassData()) { - return; - } - Runtime* const runtime = Runtime::Current(); +void ClassLinker::LoadClassMembers(Thread* self, + const DexFile& dex_file, + const uint8_t* class_data, + Handle klass) { { // Note: We cannot have thread suspension until the field and method arrays are setup or else // Class::VisitFieldRoots may miss some fields or methods. @@ -3064,79 +3073,45 @@ void ClassLinker::LoadClass(Thread* self, // We allow duplicate definitions of the same field in a class_data_item // but ignore the repeated indexes here, b/21868015. LinearAlloc* const allocator = GetAllocatorForClassLoader(klass->GetClassLoader()); + ClassDataItemIterator it(dex_file, class_data); LengthPrefixedArray* sfields = AllocArtFieldArray(self, allocator, - accessor.NumStaticFields()); + it.NumStaticFields()); + size_t num_sfields = 0; + uint32_t last_field_idx = 0u; + for (; it.HasNextStaticField(); it.Next()) { + uint32_t field_idx = it.GetMemberIndex(); + DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier. + if (num_sfields == 0 || LIKELY(field_idx > last_field_idx)) { + DCHECK_LT(num_sfields, it.NumStaticFields()); + LoadField(it, klass, &sfields->At(num_sfields)); + ++num_sfields; + last_field_idx = field_idx; + } + } + + // Load instance fields. LengthPrefixedArray* ifields = AllocArtFieldArray(self, allocator, - accessor.NumInstanceFields()); - size_t num_sfields = 0u; + it.NumInstanceFields()); size_t num_ifields = 0u; - uint32_t last_static_field_idx = 0u; - uint32_t last_instance_field_idx = 0u; - - // Methods - bool has_oat_class = false; - const OatFile::OatClass oat_class = (runtime->IsStarted() && !runtime->IsAotCompiler()) - ? OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class) - : OatFile::OatClass::Invalid(); - const OatFile::OatClass* oat_class_ptr = has_oat_class ? &oat_class : nullptr; - klass->SetMethodsPtr( - AllocArtMethodArray(self, allocator, accessor.NumMethods()), - accessor.NumDirectMethods(), - accessor.NumVirtualMethods()); - size_t class_def_method_index = 0; - uint32_t last_dex_method_index = dex::kDexNoIndex; - size_t last_class_def_method_index = 0; + last_field_idx = 0u; + for (; it.HasNextInstanceField(); it.Next()) { + uint32_t field_idx = it.GetMemberIndex(); + DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier. + if (num_ifields == 0 || LIKELY(field_idx > last_field_idx)) { + DCHECK_LT(num_ifields, it.NumInstanceFields()); + LoadField(it, klass, &ifields->At(num_ifields)); + ++num_ifields; + last_field_idx = field_idx; + } + } - // Use the visitor since the ranged based loops are bit slower from seeking. Seeking to the - // methods needs to decode all of the fields. - accessor.VisitFieldsAndMethods([&]( - const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { - uint32_t field_idx = field.GetIndex(); - DCHECK_GE(field_idx, last_static_field_idx); // Ordering enforced by DexFileVerifier. - if (num_sfields == 0 || LIKELY(field_idx > last_static_field_idx)) { - LoadField(field, klass, &sfields->At(num_sfields)); - ++num_sfields; - last_static_field_idx = field_idx; - } - }, [&](const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { - uint32_t field_idx = field.GetIndex(); - DCHECK_GE(field_idx, last_instance_field_idx); // Ordering enforced by DexFileVerifier. - if (num_ifields == 0 || LIKELY(field_idx > last_instance_field_idx)) { - LoadField(field, klass, &ifields->At(num_ifields)); - ++num_ifields; - last_instance_field_idx = field_idx; - } - }, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* art_method = klass->GetDirectMethodUnchecked(class_def_method_index, - image_pointer_size_); - LoadMethod(dex_file, method, klass, art_method); - LinkCode(this, art_method, oat_class_ptr, class_def_method_index); - uint32_t it_method_index = method.GetIndex(); - if (last_dex_method_index == it_method_index) { - // duplicate case - art_method->SetMethodIndex(last_class_def_method_index); - } else { - art_method->SetMethodIndex(class_def_method_index); - last_dex_method_index = it_method_index; - last_class_def_method_index = class_def_method_index; - } - ++class_def_method_index; - }, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* art_method = klass->GetVirtualMethodUnchecked( - class_def_method_index - accessor.NumDirectMethods(), - image_pointer_size_); - LoadMethod(dex_file, method, klass, art_method); - LinkCode(this, art_method, oat_class_ptr, class_def_method_index); - ++class_def_method_index; - }); - - if (UNLIKELY(num_ifields + num_sfields != accessor.NumFields())) { + if (UNLIKELY(num_sfields != it.NumStaticFields()) || + UNLIKELY(num_ifields != it.NumInstanceFields())) { LOG(WARNING) << "Duplicate fields in class " << klass->PrettyDescriptor() - << " (unique static fields: " << num_sfields << "/" << accessor.NumStaticFields() - << ", unique instance fields: " << num_ifields << "/" << accessor.NumInstanceFields() - << ")"; + << " (unique static fields: " << num_sfields << "/" << it.NumStaticFields() + << ", unique instance fields: " << num_ifields << "/" << it.NumInstanceFields() << ")"; // NOTE: Not shrinking the over-allocated sfields/ifields, just setting size. if (sfields != nullptr) { sfields->SetSize(num_sfields); @@ -3150,49 +3125,87 @@ void ClassLinker::LoadClass(Thread* self, DCHECK_EQ(klass->NumStaticFields(), num_sfields); klass->SetIFieldsPtr(ifields); DCHECK_EQ(klass->NumInstanceFields(), num_ifields); + // Load methods. + bool has_oat_class = false; + const OatFile::OatClass oat_class = + (Runtime::Current()->IsStarted() && !Runtime::Current()->IsAotCompiler()) + ? OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class) + : OatFile::OatClass::Invalid(); + const OatFile::OatClass* oat_class_ptr = has_oat_class ? &oat_class : nullptr; + klass->SetMethodsPtr( + AllocArtMethodArray(self, allocator, it.NumDirectMethods() + it.NumVirtualMethods()), + it.NumDirectMethods(), + it.NumVirtualMethods()); + size_t class_def_method_index = 0; + uint32_t last_dex_method_index = dex::kDexNoIndex; + size_t last_class_def_method_index = 0; + // TODO These should really use the iterators. + for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) { + ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_); + LoadMethod(dex_file, it, klass, method); + LinkCode(this, method, oat_class_ptr, class_def_method_index); + uint32_t it_method_index = it.GetMemberIndex(); + if (last_dex_method_index == it_method_index) { + // duplicate case + method->SetMethodIndex(last_class_def_method_index); + } else { + method->SetMethodIndex(class_def_method_index); + last_dex_method_index = it_method_index; + last_class_def_method_index = class_def_method_index; + } + class_def_method_index++; + } + for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) { + ArtMethod* method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_); + LoadMethod(dex_file, it, klass, method); + DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i); + LinkCode(this, method, oat_class_ptr, class_def_method_index); + class_def_method_index++; + } + DCHECK(!it.HasNext()); } // Ensure that the card is marked so that remembered sets pick up native roots. Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(klass.Get()); self->AllowThreadSuspension(); } -void ClassLinker::LoadField(const ClassAccessor::Field& field, +void ClassLinker::LoadField(const ClassDataItemIterator& it, Handle klass, ArtField* dst) { - const uint32_t field_idx = field.GetIndex(); + const uint32_t field_idx = it.GetMemberIndex(); dst->SetDexFieldIndex(field_idx); dst->SetDeclaringClass(klass.Get()); // Get access flags from the DexFile. If this is a boot class path class, // also set its runtime hidden API access flags. - uint32_t access_flags = field.GetAccessFlags(); + uint32_t access_flags = it.GetFieldAccessFlags(); if (klass->IsBootStrapClassLoaded()) { access_flags = - HiddenApiAccessFlags::EncodeForRuntime(access_flags, field.DecodeHiddenAccessFlags()); + HiddenApiAccessFlags::EncodeForRuntime(access_flags, it.DecodeHiddenAccessFlags()); } dst->SetAccessFlags(access_flags); } void ClassLinker::LoadMethod(const DexFile& dex_file, - const ClassAccessor::Method& method, + const ClassDataItemIterator& it, Handle klass, ArtMethod* dst) { - const uint32_t dex_method_idx = method.GetIndex(); + uint32_t dex_method_idx = it.GetMemberIndex(); const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_); ScopedAssertNoThreadSuspension ants("LoadMethod"); dst->SetDexMethodIndex(dex_method_idx); dst->SetDeclaringClass(klass.Get()); - dst->SetCodeItemOffset(method.GetCodeItemOffset()); + dst->SetCodeItemOffset(it.GetMethodCodeItemOffset()); // Get access flags from the DexFile. If this is a boot class path class, // also set its runtime hidden API access flags. - uint32_t access_flags = method.GetAccessFlags(); + uint32_t access_flags = it.GetMethodAccessFlags(); if (klass->IsBootStrapClassLoaded()) { access_flags = - HiddenApiAccessFlags::EncodeForRuntime(access_flags, method.DecodeHiddenAccessFlags()); + HiddenApiAccessFlags::EncodeForRuntime(access_flags, it.DecodeHiddenAccessFlags()); } if (UNLIKELY(strcmp("finalize", method_name) == 0)) { @@ -4759,29 +4772,24 @@ bool ClassLinker::InitializeClass(Thread* self, Handle klass, this, *dex_class_def); const DexFile& dex_file = *dex_cache->GetDexFile(); - + const uint8_t* class_data = dex_file.GetClassData(*dex_class_def); + ClassDataItemIterator field_it(dex_file, class_data); if (value_it.HasNext()) { - ClassAccessor accessor(dex_file, *dex_class_def); + DCHECK(field_it.HasNextStaticField()); CHECK(can_init_statics); - for (const ClassAccessor::Field& field : accessor.GetStaticFields()) { - if (!value_it.HasNext()) { - break; - } - ArtField* art_field = ResolveField(field.GetIndex(), - dex_cache, - class_loader, - /* is_static */ true); + for ( ; value_it.HasNext(); value_it.Next(), field_it.Next()) { + ArtField* field = ResolveField( + field_it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ true); if (Runtime::Current()->IsActiveTransaction()) { - value_it.ReadValueToField(art_field); + value_it.ReadValueToField(field); } else { - value_it.ReadValueToField(art_field); + value_it.ReadValueToField(field); } if (self->IsExceptionPending()) { break; } - value_it.Next(); + DCHECK(!value_it.HasNext() || field_it.HasNextStaticField()); } - DCHECK(self->IsExceptionPending() || !value_it.HasNext()); } } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 479d04ba0e..32016fa12a 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -27,7 +27,6 @@ #include "base/enums.h" #include "base/macros.h" #include "base/mutex.h" -#include "dex/class_accessor.h" #include "dex/dex_cache_resolved_classes.h" #include "dex/dex_file.h" #include "dex/dex_file_types.h" @@ -825,14 +824,18 @@ class ClassLinker { const DexFile::ClassDef& dex_class_def, Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); + void LoadClassMembers(Thread* self, + const DexFile& dex_file, + const uint8_t* class_data, + Handle klass) + REQUIRES_SHARED(Locks::mutator_lock_); - void LoadField(const ClassAccessor::Field& field, Handle klass, ArtField* dst) + void LoadField(const ClassDataItemIterator& it, Handle klass, ArtField* dst) REQUIRES_SHARED(Locks::mutator_lock_); void LoadMethod(const DexFile& dex_file, - const ClassAccessor::Method& method, - Handle klass, - ArtMethod* dst) + const ClassDataItemIterator& it, + Handle klass, ArtMethod* dst) REQUIRES_SHARED(Locks::mutator_lock_); void FixupStaticTrampolines(ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 145ce0dca0..280f99c442 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -32,7 +32,6 @@ extern "C" void android_set_application_target_sdk_version(uint32_t version); #include "class_linker-inl.h" #include "common_throws.h" #include "debugger.h" -#include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_types.h" #include "gc/accounting/card_table-inl.h" @@ -574,12 +573,30 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { } if (kPreloadDexCachesFieldsAndMethods) { - for (ClassAccessor accessor : dex_file->GetClasses()) { - for (const ClassAccessor::Field& field : accessor.GetFields()) { - PreloadDexCachesResolveField(dex_cache, field.GetIndex(), field.IsStatic()); + for (size_t class_def_index = 0; + class_def_index < dex_file->NumClassDefs(); + class_def_index++) { + const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); + const uint8_t* class_data = dex_file->GetClassData(class_def); + if (class_data == nullptr) { + continue; } - for (const ClassAccessor::Method& method : accessor.GetMethods()) { - PreloadDexCachesResolveMethod(dex_cache, method.GetIndex()); + ClassDataItemIterator it(*dex_file, class_data); + for (; it.HasNextStaticField(); it.Next()) { + uint32_t field_idx = it.GetMemberIndex(); + PreloadDexCachesResolveField(dex_cache, field_idx, true); + } + for (; it.HasNextInstanceField(); it.Next()) { + uint32_t field_idx = it.GetMemberIndex(); + PreloadDexCachesResolveField(dex_cache, field_idx, false); + } + for (; it.HasNextDirectMethod(); it.Next()) { + uint32_t method_idx = it.GetMemberIndex(); + PreloadDexCachesResolveMethod(dex_cache, method_idx); + } + for (; it.HasNextVirtualMethod(); it.Next()) { + uint32_t method_idx = it.GetMemberIndex(); + PreloadDexCachesResolveMethod(dex_cache, method_idx); } } } diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index e2f42c937d..838d7f14bc 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -28,7 +28,6 @@ #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "dex/art_dex_file_loader.h" -#include "dex/class_accessor-inl.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "dex/hidden_api_access_flags.h" @@ -284,26 +283,31 @@ void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, std::unordered_set unquickened_code_item; CompactOffsetTable::Accessor accessor(GetQuickenInfoOffsetTable(source_dex_begin, quickening_info)); - for (ClassAccessor class_accessor : target_dex_file.GetClasses()) { - for (const ClassAccessor::Method& method : class_accessor.GetMethods()) { - const DexFile::CodeItem* code_item = method.GetCodeItem(); - if (code_item != nullptr && unquickened_code_item.emplace(code_item).second) { - const uint32_t offset = accessor.GetOffset(method.GetIndex()); - // Offset being 0 means not quickened. - if (offset != 0u) { - ArrayRef quicken_data = GetQuickeningInfoAt(quickening_info, offset); - optimizer::ArtDecompileDEX( - target_dex_file, - *code_item, - quicken_data, - decompile_return_instruction); + for (uint32_t i = 0; i < target_dex_file.NumClassDefs(); ++i) { + const DexFile::ClassDef& class_def = target_dex_file.GetClassDef(i); + const uint8_t* class_data = target_dex_file.GetClassData(class_def); + if (class_data != nullptr) { + for (ClassDataItemIterator class_it(target_dex_file, class_data); + class_it.HasNext(); + class_it.Next()) { + if (class_it.IsAtMethod()) { + const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem(); + if (code_item != nullptr && unquickened_code_item.emplace(code_item).second) { + const uint32_t offset = accessor.GetOffset(class_it.GetMemberIndex()); + // Offset being 0 means not quickened. + if (offset != 0u) { + ArrayRef quicken_data = GetQuickeningInfoAt(quickening_info, offset); + optimizer::ArtDecompileDEX( + target_dex_file, + *code_item, + quicken_data, + decompile_return_instruction); + } + } } - method.UnHideAccessFlags(); + DexFile::UnHideAccessFlags(class_it); } } - for (const ClassAccessor::Field& field : class_accessor.GetFields()) { - field.UnHideAccessFlags(); - } } } diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index f4967f70be..2e3a6590e4 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -35,7 +35,6 @@ #include "class_linker.h" #include "class_root.h" #include "compiler_callbacks.h" -#include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_exception_helpers.h" @@ -191,6 +190,11 @@ FailureKind MethodVerifier::VerifyClass(Thread* self, error); } +template +static bool HasNextMethod(ClassDataItemIterator* it) { + return kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod(); +} + static FailureKind FailureKindMax(FailureKind fk1, FailureKind fk2) { static_assert(FailureKind::kNoFailure < FailureKind::kSoftFailure && FailureKind::kSoftFailure < FailureKind::kHardFailure, @@ -203,51 +207,45 @@ void MethodVerifier::FailureData::Merge(const MethodVerifier::FailureData& fd) { types |= fd.types; } -FailureKind MethodVerifier::VerifyClass(Thread* self, - const DexFile* dex_file, - Handle dex_cache, - Handle class_loader, - const DexFile::ClassDef& class_def, - CompilerCallbacks* callbacks, - bool allow_soft_failures, - HardFailLogMode log_level, - std::string* error) { - SCOPED_TRACE << "VerifyClass " << PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); +template +MethodVerifier::FailureData MethodVerifier::VerifyMethods(Thread* self, + ClassLinker* linker, + const DexFile* dex_file, + const DexFile::ClassDef& class_def, + ClassDataItemIterator* it, + Handle dex_cache, + Handle class_loader, + CompilerCallbacks* callbacks, + bool allow_soft_failures, + HardFailLogMode log_level, + bool need_precise_constants, + std::string* error_string) { + DCHECK(it != nullptr); - // A class must not be abstract and final. - if ((class_def.access_flags_ & (kAccAbstract | kAccFinal)) == (kAccAbstract | kAccFinal)) { - *error = "Verifier rejected class "; - *error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); - *error += ": class is abstract and final."; - return FailureKind::kHardFailure; - } - - ClassAccessor accessor(*dex_file, class_def); - - int64_t previous_method_idx[2] = { -1, -1 }; MethodVerifier::FailureData failure_data; - ClassLinker* const linker = Runtime::Current()->GetClassLinker(); - for (const ClassAccessor::Method& method : accessor.GetMethods()) { - int64_t* previous_idx = &previous_method_idx[method.IsStaticOrDirect() ? 0u : 1u]; + int64_t previous_method_idx = -1; + while (HasNextMethod(it)) { self->AllowThreadSuspension(); - const uint32_t method_idx = method.GetIndex(); - if (method_idx == *previous_idx) { + uint32_t method_idx = it->GetMemberIndex(); + if (method_idx == previous_method_idx) { // smali can create dex files with two encoded_methods sharing the same method_idx // http://code.google.com/p/smali/issues/detail?id=119 + it->Next(); continue; } - *previous_idx = method_idx; - const InvokeType type = method.GetInvokeType(class_def.access_flags_); - ArtMethod* resolved_method = linker->ResolveMethod( + previous_method_idx = method_idx; + InvokeType type = it->GetMethodInvokeType(class_def); + ArtMethod* method = linker->ResolveMethod( method_idx, dex_cache, class_loader, /* referrer */ nullptr, type); - if (resolved_method == nullptr) { + if (method == nullptr) { DCHECK(self->IsExceptionPending()); // We couldn't resolve the method, but continue regardless. self->ClearException(); } else { - DCHECK(resolved_method->GetDeclaringClassUnchecked() != nullptr) << type; + DCHECK(method->GetDeclaringClassUnchecked() != nullptr) << type; } + StackHandleScope<1> hs(self); std::string hard_failure_msg; MethodVerifier::FailureData result = VerifyMethod(self, method_idx, @@ -255,39 +253,99 @@ FailureKind MethodVerifier::VerifyClass(Thread* self, dex_cache, class_loader, class_def, - method.GetCodeItem(), - resolved_method, - method.GetAccessFlags(), + it->GetMethodCodeItem(), + method, + it->GetMethodAccessFlags(), callbacks, allow_soft_failures, log_level, - /*need_precise_constants*/ false, + need_precise_constants, &hard_failure_msg); if (result.kind == FailureKind::kHardFailure) { if (failure_data.kind == FailureKind::kHardFailure) { // If we logged an error before, we need a newline. - *error += "\n"; + *error_string += "\n"; } else { // If we didn't log a hard failure before, print the header of the message. - *error += "Verifier rejected class "; - *error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); - *error += ":"; + *error_string += "Verifier rejected class "; + *error_string += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); + *error_string += ":"; } - *error += " "; - *error += hard_failure_msg; + *error_string += " "; + *error_string += hard_failure_msg; } failure_data.Merge(result); + it->Next(); + } + + return failure_data; +} + +FailureKind MethodVerifier::VerifyClass(Thread* self, + const DexFile* dex_file, + Handle dex_cache, + Handle class_loader, + const DexFile::ClassDef& class_def, + CompilerCallbacks* callbacks, + bool allow_soft_failures, + HardFailLogMode log_level, + std::string* error) { + SCOPED_TRACE << "VerifyClass " << PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); + + // A class must not be abstract and final. + if ((class_def.access_flags_ & (kAccAbstract | kAccFinal)) == (kAccAbstract | kAccFinal)) { + *error = "Verifier rejected class "; + *error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); + *error += ": class is abstract and final."; + return FailureKind::kHardFailure; } - if (failure_data.kind == FailureKind::kNoFailure) { + const uint8_t* class_data = dex_file->GetClassData(class_def); + if (class_data == nullptr) { + // empty class, probably a marker interface + return FailureKind::kNoFailure; + } + ClassDataItemIterator it(*dex_file, class_data); + it.SkipAllFields(); + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + // Direct methods. + MethodVerifier::FailureData data1 = VerifyMethods(self, + linker, + dex_file, + class_def, + &it, + dex_cache, + class_loader, + callbacks, + allow_soft_failures, + log_level, + false /* need precise constants */, + error); + // Virtual methods. + MethodVerifier::FailureData data2 = VerifyMethods(self, + linker, + dex_file, + class_def, + &it, + dex_cache, + class_loader, + callbacks, + allow_soft_failures, + log_level, + false /* need precise constants */, + error); + + data1.Merge(data2); + + if (data1.kind == FailureKind::kNoFailure) { return FailureKind::kNoFailure; } else { - if ((failure_data.types & VERIFY_ERROR_LOCKING) != 0) { + if ((data1.types & VERIFY_ERROR_LOCKING) != 0) { // Print a warning about expected slow-down. Use a string temporary to print one contiguous // warning. std::string tmp = StringPrintf("Class %s failed lock verification and will run slower.", - PrettyDescriptor(accessor.GetDescriptor()).c_str()); + PrettyDescriptor(dex_file->GetClassDescriptor(class_def)).c_str()); if (!gPrintedDxMonitorText) { tmp = tmp + "\nCommon causes for lock verification issues are non-optimized dex code\n" "and incorrect proguard optimizations."; @@ -295,7 +353,7 @@ FailureKind MethodVerifier::VerifyClass(Thread* self, } LOG(WARNING) << tmp; } - return failure_data.kind; + return data1.kind; } } @@ -1866,11 +1924,15 @@ bool MethodVerifier::CodeFlowVerifyMethod() { static uint32_t GetFirstFinalInstanceFieldIndex(const DexFile& dex_file, dex::TypeIndex type_idx) { const DexFile::ClassDef* class_def = dex_file.FindClassDef(type_idx); DCHECK(class_def != nullptr); - ClassAccessor accessor(dex_file, *class_def); - for (const ClassAccessor::Field& field : accessor.GetInstanceFields()) { - if (field.IsFinal()) { - return field.GetIndex(); - } + const uint8_t* class_data = dex_file.GetClassData(*class_def); + DCHECK(class_data != nullptr); + ClassDataItemIterator it(dex_file, class_data); + it.SkipStaticFields(); + while (it.HasNextInstanceField()) { + if ((it.GetFieldAccessFlags() & kAccFinal) != 0) { + return it.GetMemberIndex(); + } + it.Next(); } return dex::kDexNoIndex; } diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 9890af9d95..ae7481c6b1 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -275,6 +275,23 @@ class MethodVerifier { void Merge(const FailureData& src); }; + // Verify all direct or virtual methods of a class. The method assumes that the iterator is + // positioned correctly, and the iterator will be updated. + template + static FailureData VerifyMethods(Thread* self, + ClassLinker* linker, + const DexFile* dex_file, + const DexFile::ClassDef& class_def, + ClassDataItemIterator* it, + Handle dex_cache, + Handle class_loader, + CompilerCallbacks* callbacks, + bool allow_soft_failures, + HardFailLogMode log_level, + bool need_precise_constants, + std::string* error_string) + REQUIRES_SHARED(Locks::mutator_lock_); + /* * Perform verification on a single method. * -- GitLab From 1f1cb9f2f0945dbcf6b79d0795b035233dfd9131 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 4 Jun 2018 09:22:46 -0700 Subject: [PATCH 530/749] Revert "Revert "Move runtime/ to ClassAccessor"" Fixed misplaced UnhideAccessFlags to be outside of a conditional that the code item is null / not deduped. This fixes an issue where these methods would not have had their access flags restored. Bug: 77709234 Bug: 79758018 Bug: 91962648 This reverts commit cc7e20f9ec7b4a7a57f7196e5e8be67a727f21d3. Test: test-art-host Test: atest FrameworksUiServicesTests Test: atest CtsInlineMockingTestCases Change-Id: I7e5712cdcccef81e19ce81d26743c517b0b8a67d --- libdexfile/dex/class_accessor-inl.h | 101 +++++--- libdexfile/dex/class_accessor.h | 78 ++++-- libdexfile/dex/class_accessor_test.cc | 10 + libdexfile/dex/dex_file.cc | 17 +- libdexfile/dex/dex_file.h | 4 +- openjdkjvmti/fixed_up_dex_file.cc | 15 +- runtime/art_method.cc | 23 +- runtime/class_linker.cc | 290 +++++++++++----------- runtime/class_linker.h | 13 +- runtime/native/dalvik_system_VMRuntime.cc | 29 +-- runtime/vdex_file.cc | 40 ++- runtime/verifier/method_verifier.cc | 164 ++++-------- runtime/verifier/method_verifier.h | 17 -- 13 files changed, 384 insertions(+), 417 deletions(-) diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h index 49ca98d47f..3bb9e93e5a 100644 --- a/libdexfile/dex/class_accessor-inl.h +++ b/libdexfile/dex/class_accessor-inl.h @@ -37,30 +37,26 @@ inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const DexFile::Clas num_direct_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u), num_virtual_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u) {} -inline const uint8_t* ClassAccessor::Method::Read(const uint8_t* ptr) { - index_ += DecodeUnsignedLeb128(&ptr); - access_flags_ = DecodeUnsignedLeb128(&ptr); - code_off_ = DecodeUnsignedLeb128(&ptr); - return ptr; +inline void ClassAccessor::Method::Read() { + index_ += DecodeUnsignedLeb128(&ptr_pos_); + access_flags_ = DecodeUnsignedLeb128(&ptr_pos_); + code_off_ = DecodeUnsignedLeb128(&ptr_pos_); } -inline const uint8_t* ClassAccessor::Field::Read(const uint8_t* ptr) { - index_ += DecodeUnsignedLeb128(&ptr); - access_flags_ = DecodeUnsignedLeb128(&ptr); - return ptr; +inline void ClassAccessor::Field::Read() { + index_ += DecodeUnsignedLeb128(&ptr_pos_); + access_flags_ = DecodeUnsignedLeb128(&ptr_pos_); } template -inline const uint8_t* ClassAccessor::VisitMembers(size_t count, - const Visitor& visitor, - const uint8_t* ptr, - DataType* data) const { +inline void ClassAccessor::VisitMembers(size_t count, + const Visitor& visitor, + DataType* data) const { DCHECK(data != nullptr); for ( ; count != 0; --count) { - ptr = data->Read(ptr); + data->Read(); visitor(*data); } - return ptr; } template > + ClassAccessor::GetFieldsInternal(size_t count) const { + return { DataIterator(dex_file_, 0u, num_static_fields_, count, ptr_pos_), + DataIterator(dex_file_, count, num_static_fields_, count, ptr_pos_) }; +} + +// Return an iteration range for the first methods. +inline IterationRange> + ClassAccessor::GetMethodsInternal(size_t count) const { + // Skip over the fields. + Field field(dex_file_, ptr_pos_); + VisitMembers(NumFields(), VoidFunctor(), &field); + // Return the iterator pair. + return { DataIterator(dex_file_, 0u, num_direct_methods_, count, field.ptr_pos_), + DataIterator(dex_file_, count, num_direct_methods_, count, field.ptr_pos_) }; +} + inline IterationRange> ClassAccessor::GetFields() const { - const uint32_t limit = num_static_fields_ + num_instance_fields_; - return { DataIterator(dex_file_, 0u, num_static_fields_, limit, ptr_pos_), - DataIterator(dex_file_, limit, num_static_fields_, limit, ptr_pos_) }; + return GetFieldsInternal(num_static_fields_ + num_instance_fields_); +} + +inline IterationRange> + ClassAccessor::GetStaticFields() const { + return GetFieldsInternal(num_static_fields_); +} + + +inline IterationRange> + ClassAccessor::GetInstanceFields() const { + IterationRange> fields = GetFields(); + // Skip the static fields. + return { std::next(fields.begin(), NumStaticFields()), fields.end() }; } inline IterationRange> ClassAccessor::GetMethods() const { - // Skip over the fields. - Field field(dex_file_); - const size_t skip_count = num_static_fields_ + num_instance_fields_; - const uint8_t* ptr_pos = VisitMembers(skip_count, VoidFunctor(), ptr_pos_, &field); - // Return the iterator pair for all the methods. - const uint32_t limit = num_direct_methods_ + num_virtual_methods_; - return { DataIterator(dex_file_, 0u, num_direct_methods_, limit, ptr_pos), - DataIterator(dex_file_, limit, num_direct_methods_, limit, ptr_pos) }; + return GetMethodsInternal(NumMethods()); +} + +inline IterationRange> + ClassAccessor::GetDirectMethods() const { + return GetMethodsInternal(NumDirectMethods()); +} + +inline IterationRange> + ClassAccessor::GetVirtualMethods() const { + IterationRange> methods = GetMethods(); + // Skip the direct fields. + return { std::next(methods.begin(), NumDirectMethods()), methods.end() }; +} + +inline void ClassAccessor::Field::UnHideAccessFlags() const { + DexFile::UnHideAccessFlags(const_cast(ptr_pos_), GetAccessFlags(), /*is_method*/ false); +} + +inline void ClassAccessor::Method::UnHideAccessFlags() const { + DexFile::UnHideAccessFlags(const_cast(ptr_pos_), GetAccessFlags(), /*is_method*/ true); } } // namespace art diff --git a/libdexfile/dex/class_accessor.h b/libdexfile/dex/class_accessor.h index dda6e1c1a6..4f0fd32e31 100644 --- a/libdexfile/dex/class_accessor.h +++ b/libdexfile/dex/class_accessor.h @@ -20,6 +20,7 @@ #include "base/utils.h" #include "code_item_accessors.h" #include "dex_file.h" +#include "hidden_api_access_flags.h" #include "invoke_type.h" #include "method_reference.h" #include "modifiers.h" @@ -33,12 +34,18 @@ class ClassAccessor { private: class BaseItem { public: + explicit BaseItem(const uint8_t* ptr_pos) : ptr_pos_(ptr_pos) {} + uint32_t GetIndex() const { return index_; } uint32_t GetAccessFlags() const { - return access_flags_; + return HiddenApiAccessFlags::RemoveFromDex(access_flags_); + } + + HiddenApiAccessFlags::ApiList DecodeHiddenAccessFlags() const { + return HiddenApiAccessFlags::DecodeFromDex(access_flags_); } bool IsFinal() const { @@ -46,6 +53,8 @@ class ClassAccessor { } protected: + // Internal data pointer for reading. + const uint8_t* ptr_pos_ = nullptr; uint32_t index_ = 0u; uint32_t access_flags_ = 0u; }; @@ -76,13 +85,18 @@ class ClassAccessor { return is_static_or_direct_; } + // Unhide the hidden API access flags at the iterator position. TODO: Deprecate. + void UnHideAccessFlags() const; + private: explicit Method(const DexFile& dex_file, + const uint8_t* ptr_pos, bool is_static_or_direct = true) - : dex_file_(dex_file), + : BaseItem(ptr_pos), + dex_file_(dex_file), is_static_or_direct_(is_static_or_direct) {} - const uint8_t* Read(const uint8_t* ptr); + void Read(); InvokeType GetDirectMethodInvokeType() const { return (GetAccessFlags() & kAccStatic) != 0 ? kStatic : kDirect; @@ -99,6 +113,7 @@ class ClassAccessor { } } + // Move to virtual method section. void NextSection() { DCHECK(is_static_or_direct_) << "Already in the virtual methods section"; is_static_or_direct_ = false; @@ -115,20 +130,31 @@ class ClassAccessor { // A decoded version of the field of a class_data_item. class Field : public BaseItem { public: - explicit Field(const DexFile& dex_file) : dex_file_(dex_file) {} + explicit Field(const DexFile& dex_file, + const uint8_t* ptr_pos) : BaseItem(ptr_pos), dex_file_(dex_file) {} const DexFile& GetDexFile() const { return dex_file_; } + bool IsStatic() const { + return is_static_; + } + + // Unhide the hidden API access flags at the iterator position. TODO: Deprecate. + void UnHideAccessFlags() const; + private: - const uint8_t* Read(const uint8_t* ptr); + void Read(); + // Move to instance fields section. void NextSection() { index_ = 0u; + is_static_ = false; } const DexFile& dex_file_; + bool is_static_ = true; friend class ClassAccessor; }; @@ -144,11 +170,10 @@ class ClassAccessor { uint32_t partition_pos, uint32_t iterator_end, const uint8_t* ptr_pos) - : data_(dex_file), + : data_(dex_file, ptr_pos), position_(position), partition_pos_(partition_pos), - iterator_end_(iterator_end), - ptr_pos_(ptr_pos) { + iterator_end_(iterator_end) { ReadData(); } @@ -205,8 +230,7 @@ class ClassAccessor { if (position_ == partition_pos_) { data_.NextSection(); } - DCHECK(ptr_pos_ != nullptr); - ptr_pos_ = data_.Read(ptr_pos_); + data_.Read(); } } @@ -217,8 +241,6 @@ class ClassAccessor { const uint32_t partition_pos_; // At iterator_end_, the iterator is no longer valid. const uint32_t iterator_end_; - // Internal data pointer. - const uint8_t* ptr_pos_; }; // Not explicit specifically for range-based loops. @@ -252,9 +274,21 @@ class ClassAccessor { // Return the iteration range for all the fields. IterationRange> GetFields() const; + // Return the iteration range for all the static fields. + IterationRange> GetStaticFields() const; + + // Return the iteration range for all the instance fields. + IterationRange> GetInstanceFields() const; + // Return the iteration range for all the methods. IterationRange> GetMethods() const; + // Return the iteration range for the direct methods. + IterationRange> GetDirectMethods() const; + + // Return the iteration range for the virtual methods. + IterationRange> GetVirtualMethods() const; + uint32_t NumStaticFields() const { return num_static_fields_; } @@ -263,6 +297,10 @@ class ClassAccessor { return num_instance_fields_; } + uint32_t NumFields() const { + return NumStaticFields() + NumInstanceFields(); + } + uint32_t NumDirectMethods() const { return num_direct_methods_; } @@ -285,14 +323,22 @@ class ClassAccessor { return dex_file_; } + bool HasClassData() const { + return ptr_pos_ != nullptr; + } + protected: // Template visitor to reduce copy paste for visiting elements. // No thread safety analysis since the visitor may require capabilities. template - const uint8_t* VisitMembers(size_t count, - const Visitor& visitor, - const uint8_t* ptr, - DataType* data) const NO_THREAD_SAFETY_ANALYSIS; + void VisitMembers(size_t count, const Visitor& visitor, DataType* data) const + NO_THREAD_SAFETY_ANALYSIS; + + // Return an iteration range for the first fields. + IterationRange> GetFieldsInternal(size_t count) const; + + // Return an iteration range for the first methods. + IterationRange> GetMethodsInternal(size_t count) const; const DexFile& dex_file_; const dex::TypeIndex descriptor_index_ = {}; diff --git a/libdexfile/dex/class_accessor_test.cc b/libdexfile/dex/class_accessor_test.cc index 95380d8140..d0533c1811 100644 --- a/libdexfile/dex/class_accessor_test.cc +++ b/libdexfile/dex/class_accessor_test.cc @@ -38,18 +38,27 @@ TEST_F(ClassAccessorTest, TestVisiting) { auto fields = accessor.GetFields(); auto method_it = methods.begin(); auto field_it = fields.begin(); + auto instance_fields = accessor.GetInstanceFields(); + auto instance_field_it = instance_fields.begin(); accessor.VisitFieldsAndMethods( // Static fields. [&](const ClassAccessor::Field& field) { + EXPECT_TRUE(field.IsStatic()); + EXPECT_TRUE(field_it->IsStatic()); EXPECT_EQ(field.GetIndex(), field_it->GetIndex()); EXPECT_EQ(field.GetAccessFlags(), field_it->GetAccessFlags()); ++field_it; }, // Instance fields. [&](const ClassAccessor::Field& field) { + EXPECT_FALSE(field.IsStatic()); + EXPECT_FALSE(field_it->IsStatic()); EXPECT_EQ(field.GetIndex(), field_it->GetIndex()); EXPECT_EQ(field.GetAccessFlags(), field_it->GetAccessFlags()); + EXPECT_EQ(field.GetIndex(), instance_field_it->GetIndex()); + EXPECT_EQ(field.GetAccessFlags(), instance_field_it->GetAccessFlags()); ++field_it; + ++instance_field_it; }, // Direct methods. [&](const ClassAccessor::Method& method) { @@ -71,6 +80,7 @@ TEST_F(ClassAccessorTest, TestVisiting) { }); ASSERT_TRUE(field_it == fields.end()); ASSERT_TRUE(method_it == methods.end()); + ASSERT_TRUE(instance_field_it == instance_fields.end()); } EXPECT_EQ(class_def_idx, dex_file->NumClassDefs()); } diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc index 9de260c862..f570158dfb 100644 --- a/libdexfile/dex/dex_file.cc +++ b/libdexfile/dex/dex_file.cc @@ -45,19 +45,18 @@ static_assert(std::is_trivially_copyable::value, "StringIndex static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wrong"); static_assert(std::is_trivially_copyable::value, "TypeIndex not trivial"); -void DexFile::UnHideAccessFlags(ClassDataItemIterator& class_it) { - uint8_t* data = const_cast(class_it.DataPointer()); - uint32_t new_flag = class_it.GetMemberAccessFlags(); - bool is_method = class_it.IsAtMethod(); +void DexFile::UnHideAccessFlags(uint8_t* data_ptr, + uint32_t new_access_flags, + bool is_method) { // Go back 1 uleb to start. - data = ReverseSearchUnsignedLeb128(data); + data_ptr = ReverseSearchUnsignedLeb128(data_ptr); if (is_method) { // Methods have another uleb field before the access flags - data = ReverseSearchUnsignedLeb128(data); + data_ptr = ReverseSearchUnsignedLeb128(data_ptr); } - DCHECK_EQ(HiddenApiAccessFlags::RemoveFromDex(DecodeUnsignedLeb128WithoutMovingCursor(data)), - new_flag); - UpdateUnsignedLeb128(data, new_flag); + DCHECK_EQ(HiddenApiAccessFlags::RemoveFromDex(DecodeUnsignedLeb128WithoutMovingCursor(data_ptr)), + new_access_flags); + UpdateUnsignedLeb128(data_ptr, new_access_flags); } uint32_t DexFile::CalculateChecksum() const { diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index f1f8b505bd..ed219808d2 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -1010,8 +1010,8 @@ class DexFile { return container_.get(); } - // Changes the dex file pointed to by class_it to not have any hiddenapi flags. - static void UnHideAccessFlags(ClassDataItemIterator& class_it); + // Changes the dex class data pointed to by data_ptr it to not have any hiddenapi flags. + static void UnHideAccessFlags(uint8_t* data_ptr, uint32_t new_access_flags, bool is_method); inline IterationRange GetClasses() const; diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc index fcbafe7e71..a660fb56c4 100644 --- a/openjdkjvmti/fixed_up_dex_file.cc +++ b/openjdkjvmti/fixed_up_dex_file.cc @@ -31,6 +31,7 @@ #include "base/leb128.h" #include "fixed_up_dex_file.h" +#include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "dex/dex_file_verifier.h" @@ -51,14 +52,12 @@ static void RecomputeDexChecksum(art::DexFile* dex_file) { } static void UnhideApis(const art::DexFile& target_dex_file) { - for (uint32_t i = 0; i < target_dex_file.NumClassDefs(); ++i) { - const uint8_t* class_data = target_dex_file.GetClassData(target_dex_file.GetClassDef(i)); - if (class_data != nullptr) { - for (art::ClassDataItemIterator class_it(target_dex_file, class_data); - class_it.HasNext(); - class_it.Next()) { - art::DexFile::UnHideAccessFlags(class_it); - } + for (art::ClassAccessor accessor : target_dex_file.GetClasses()) { + for (const art::ClassAccessor::Field& field : accessor.GetFields()) { + field.UnHideAccessFlags(); + } + for (const art::ClassAccessor::Method& method : accessor.GetMethods()) { + method.UnHideAccessFlags(); } } } diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 151c36f3bc..4e9f3c52e2 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -26,6 +26,7 @@ #include "class_linker-inl.h" #include "class_root.h" #include "debugger.h" +#include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_exception_helpers.h" @@ -434,28 +435,14 @@ bool ArtMethod::IsPolymorphicSignature() { static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx); - const uint8_t* class_data = dex_file.GetClassData(class_def); - CHECK(class_data != nullptr); - ClassDataItemIterator it(dex_file, class_data); - it.SkipAllFields(); - // Process methods - size_t class_def_method_index = 0; - while (it.HasNextDirectMethod()) { - if (it.GetMemberIndex() == method_idx) { + ClassAccessor accessor(dex_file, dex_file.GetClassDef(class_def_idx)); + uint32_t class_def_method_index = 0u; + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + if (method.GetIndex() == method_idx) { return class_def_method_index; } class_def_method_index++; - it.Next(); } - while (it.HasNextVirtualMethod()) { - if (it.GetMemberIndex() == method_idx) { - return class_def_method_index; - } - class_def_method_index++; - it.Next(); - } - DCHECK(!it.HasNext()); LOG(FATAL) << "Failed to find method index " << method_idx << " in " << dex_file.GetLocation(); UNREACHABLE(); } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 67987963e1..9bb562bf49 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -56,6 +56,7 @@ #include "compiler_callbacks.h" #include "debug_print.h" #include "debugger.h" +#include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_exception_helpers.h" @@ -2730,52 +2731,50 @@ ObjPtr ClassLinker::DefineClass(Thread* self, uint32_t ClassLinker::SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def) { - const uint8_t* class_data = dex_file.GetClassData(dex_class_def); size_t num_ref = 0; size_t num_8 = 0; size_t num_16 = 0; size_t num_32 = 0; size_t num_64 = 0; - if (class_data != nullptr) { - // We allow duplicate definitions of the same field in a class_data_item - // but ignore the repeated indexes here, b/21868015. - uint32_t last_field_idx = dex::kDexNoIndex; - for (ClassDataItemIterator it(dex_file, class_data); it.HasNextStaticField(); it.Next()) { - uint32_t field_idx = it.GetMemberIndex(); - // Ordering enforced by DexFileVerifier. - DCHECK(last_field_idx == dex::kDexNoIndex || last_field_idx <= field_idx); - if (UNLIKELY(field_idx == last_field_idx)) { - continue; - } - last_field_idx = field_idx; - const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); - const char* descriptor = dex_file.GetFieldTypeDescriptor(field_id); - char c = descriptor[0]; - switch (c) { - case 'L': - case '[': - num_ref++; - break; - case 'J': - case 'D': - num_64++; - break; - case 'I': - case 'F': - num_32++; - break; - case 'S': - case 'C': - num_16++; - break; - case 'B': - case 'Z': - num_8++; - break; - default: - LOG(FATAL) << "Unknown descriptor: " << c; - UNREACHABLE(); - } + ClassAccessor accessor(dex_file, dex_class_def); + // We allow duplicate definitions of the same field in a class_data_item + // but ignore the repeated indexes here, b/21868015. + uint32_t last_field_idx = dex::kDexNoIndex; + for (const ClassAccessor::Field& field : accessor.GetStaticFields()) { + uint32_t field_idx = field.GetIndex(); + // Ordering enforced by DexFileVerifier. + DCHECK(last_field_idx == dex::kDexNoIndex || last_field_idx <= field_idx); + if (UNLIKELY(field_idx == last_field_idx)) { + continue; + } + last_field_idx = field_idx; + const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); + const char* descriptor = dex_file.GetFieldTypeDescriptor(field_id); + char c = descriptor[0]; + switch (c) { + case 'L': + case '[': + num_ref++; + break; + case 'J': + case 'D': + num_64++; + break; + case 'I': + case 'F': + num_32++; + break; + case 'S': + case 'C': + num_16++; + break; + case 'B': + case 'Z': + num_8++; + break; + default: + LOG(FATAL) << "Unknown descriptor: " << c; + UNREACHABLE(); } } return mirror::Class::ComputeClassSize(false, @@ -2873,17 +2872,15 @@ void ClassLinker::FixupStaticTrampolines(ObjPtr klass) { const DexFile& dex_file = klass->GetDexFile(); const DexFile::ClassDef* dex_class_def = klass->GetClassDef(); CHECK(dex_class_def != nullptr); - const uint8_t* class_data = dex_file.GetClassData(*dex_class_def); + ClassAccessor accessor(dex_file, *dex_class_def); // There should always be class data if there were direct methods. - CHECK(class_data != nullptr) << klass->PrettyDescriptor(); - ClassDataItemIterator it(dex_file, class_data); - it.SkipAllFields(); + CHECK(accessor.HasClassData()) << klass->PrettyDescriptor(); bool has_oat_class; OatFile::OatClass oat_class = OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class); // Link the code of methods skipped by LinkCode. - for (size_t method_index = 0; it.HasNextDirectMethod(); ++method_index, it.Next()) { + for (size_t method_index = 0; method_index < accessor.NumDirectMethods(); ++method_index) { ArtMethod* method = klass->GetDirectMethod(method_index, image_pointer_size_); if (!method->IsStatic()) { // Only update static methods. @@ -2992,17 +2989,6 @@ void ClassLinker::SetupClass(const DexFile& dex_file, klass->SetDexTypeIndex(dex_class_def.class_idx_); } -void ClassLinker::LoadClass(Thread* self, - const DexFile& dex_file, - const DexFile::ClassDef& dex_class_def, - Handle klass) { - const uint8_t* class_data = dex_file.GetClassData(dex_class_def); - if (class_data == nullptr) { - return; // no fields or methods - for example a marker interface - } - LoadClassMembers(self, dex_file, class_data, klass); -} - LengthPrefixedArray* ClassLinker::AllocArtFieldArray(Thread* self, LinearAlloc* allocator, size_t length) { @@ -3061,10 +3047,15 @@ LinearAlloc* ClassLinker::GetOrCreateAllocatorForClassLoader(ObjPtr klass) { +void ClassLinker::LoadClass(Thread* self, + const DexFile& dex_file, + const DexFile::ClassDef& dex_class_def, + Handle klass) { + ClassAccessor accessor(dex_file, dex_class_def); + if (!accessor.HasClassData()) { + return; + } + Runtime* const runtime = Runtime::Current(); { // Note: We cannot have thread suspension until the field and method arrays are setup or else // Class::VisitFieldRoots may miss some fields or methods. @@ -3073,45 +3064,79 @@ void ClassLinker::LoadClassMembers(Thread* self, // We allow duplicate definitions of the same field in a class_data_item // but ignore the repeated indexes here, b/21868015. LinearAlloc* const allocator = GetAllocatorForClassLoader(klass->GetClassLoader()); - ClassDataItemIterator it(dex_file, class_data); LengthPrefixedArray* sfields = AllocArtFieldArray(self, allocator, - it.NumStaticFields()); - size_t num_sfields = 0; - uint32_t last_field_idx = 0u; - for (; it.HasNextStaticField(); it.Next()) { - uint32_t field_idx = it.GetMemberIndex(); - DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier. - if (num_sfields == 0 || LIKELY(field_idx > last_field_idx)) { - DCHECK_LT(num_sfields, it.NumStaticFields()); - LoadField(it, klass, &sfields->At(num_sfields)); - ++num_sfields; - last_field_idx = field_idx; - } - } - - // Load instance fields. + accessor.NumStaticFields()); LengthPrefixedArray* ifields = AllocArtFieldArray(self, allocator, - it.NumInstanceFields()); + accessor.NumInstanceFields()); + size_t num_sfields = 0u; size_t num_ifields = 0u; - last_field_idx = 0u; - for (; it.HasNextInstanceField(); it.Next()) { - uint32_t field_idx = it.GetMemberIndex(); - DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier. - if (num_ifields == 0 || LIKELY(field_idx > last_field_idx)) { - DCHECK_LT(num_ifields, it.NumInstanceFields()); - LoadField(it, klass, &ifields->At(num_ifields)); - ++num_ifields; - last_field_idx = field_idx; - } - } + uint32_t last_static_field_idx = 0u; + uint32_t last_instance_field_idx = 0u; + + // Methods + bool has_oat_class = false; + const OatFile::OatClass oat_class = (runtime->IsStarted() && !runtime->IsAotCompiler()) + ? OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class) + : OatFile::OatClass::Invalid(); + const OatFile::OatClass* oat_class_ptr = has_oat_class ? &oat_class : nullptr; + klass->SetMethodsPtr( + AllocArtMethodArray(self, allocator, accessor.NumMethods()), + accessor.NumDirectMethods(), + accessor.NumVirtualMethods()); + size_t class_def_method_index = 0; + uint32_t last_dex_method_index = dex::kDexNoIndex; + size_t last_class_def_method_index = 0; - if (UNLIKELY(num_sfields != it.NumStaticFields()) || - UNLIKELY(num_ifields != it.NumInstanceFields())) { + // Use the visitor since the ranged based loops are bit slower from seeking. Seeking to the + // methods needs to decode all of the fields. + accessor.VisitFieldsAndMethods([&]( + const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t field_idx = field.GetIndex(); + DCHECK_GE(field_idx, last_static_field_idx); // Ordering enforced by DexFileVerifier. + if (num_sfields == 0 || LIKELY(field_idx > last_static_field_idx)) { + LoadField(field, klass, &sfields->At(num_sfields)); + ++num_sfields; + last_static_field_idx = field_idx; + } + }, [&](const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t field_idx = field.GetIndex(); + DCHECK_GE(field_idx, last_instance_field_idx); // Ordering enforced by DexFileVerifier. + if (num_ifields == 0 || LIKELY(field_idx > last_instance_field_idx)) { + LoadField(field, klass, &ifields->At(num_ifields)); + ++num_ifields; + last_instance_field_idx = field_idx; + } + }, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* art_method = klass->GetDirectMethodUnchecked(class_def_method_index, + image_pointer_size_); + LoadMethod(dex_file, method, klass, art_method); + LinkCode(this, art_method, oat_class_ptr, class_def_method_index); + uint32_t it_method_index = method.GetIndex(); + if (last_dex_method_index == it_method_index) { + // duplicate case + art_method->SetMethodIndex(last_class_def_method_index); + } else { + art_method->SetMethodIndex(class_def_method_index); + last_dex_method_index = it_method_index; + last_class_def_method_index = class_def_method_index; + } + ++class_def_method_index; + }, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* art_method = klass->GetVirtualMethodUnchecked( + class_def_method_index - accessor.NumDirectMethods(), + image_pointer_size_); + LoadMethod(dex_file, method, klass, art_method); + LinkCode(this, art_method, oat_class_ptr, class_def_method_index); + ++class_def_method_index; + }); + + if (UNLIKELY(num_ifields + num_sfields != accessor.NumFields())) { LOG(WARNING) << "Duplicate fields in class " << klass->PrettyDescriptor() - << " (unique static fields: " << num_sfields << "/" << it.NumStaticFields() - << ", unique instance fields: " << num_ifields << "/" << it.NumInstanceFields() << ")"; + << " (unique static fields: " << num_sfields << "/" << accessor.NumStaticFields() + << ", unique instance fields: " << num_ifields << "/" << accessor.NumInstanceFields() + << ")"; // NOTE: Not shrinking the over-allocated sfields/ifields, just setting size. if (sfields != nullptr) { sfields->SetSize(num_sfields); @@ -3125,87 +3150,49 @@ void ClassLinker::LoadClassMembers(Thread* self, DCHECK_EQ(klass->NumStaticFields(), num_sfields); klass->SetIFieldsPtr(ifields); DCHECK_EQ(klass->NumInstanceFields(), num_ifields); - // Load methods. - bool has_oat_class = false; - const OatFile::OatClass oat_class = - (Runtime::Current()->IsStarted() && !Runtime::Current()->IsAotCompiler()) - ? OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class) - : OatFile::OatClass::Invalid(); - const OatFile::OatClass* oat_class_ptr = has_oat_class ? &oat_class : nullptr; - klass->SetMethodsPtr( - AllocArtMethodArray(self, allocator, it.NumDirectMethods() + it.NumVirtualMethods()), - it.NumDirectMethods(), - it.NumVirtualMethods()); - size_t class_def_method_index = 0; - uint32_t last_dex_method_index = dex::kDexNoIndex; - size_t last_class_def_method_index = 0; - // TODO These should really use the iterators. - for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) { - ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_); - LoadMethod(dex_file, it, klass, method); - LinkCode(this, method, oat_class_ptr, class_def_method_index); - uint32_t it_method_index = it.GetMemberIndex(); - if (last_dex_method_index == it_method_index) { - // duplicate case - method->SetMethodIndex(last_class_def_method_index); - } else { - method->SetMethodIndex(class_def_method_index); - last_dex_method_index = it_method_index; - last_class_def_method_index = class_def_method_index; - } - class_def_method_index++; - } - for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) { - ArtMethod* method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_); - LoadMethod(dex_file, it, klass, method); - DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i); - LinkCode(this, method, oat_class_ptr, class_def_method_index); - class_def_method_index++; - } - DCHECK(!it.HasNext()); } // Ensure that the card is marked so that remembered sets pick up native roots. Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(klass.Get()); self->AllowThreadSuspension(); } -void ClassLinker::LoadField(const ClassDataItemIterator& it, +void ClassLinker::LoadField(const ClassAccessor::Field& field, Handle klass, ArtField* dst) { - const uint32_t field_idx = it.GetMemberIndex(); + const uint32_t field_idx = field.GetIndex(); dst->SetDexFieldIndex(field_idx); dst->SetDeclaringClass(klass.Get()); // Get access flags from the DexFile. If this is a boot class path class, // also set its runtime hidden API access flags. - uint32_t access_flags = it.GetFieldAccessFlags(); + uint32_t access_flags = field.GetAccessFlags(); if (klass->IsBootStrapClassLoaded()) { access_flags = - HiddenApiAccessFlags::EncodeForRuntime(access_flags, it.DecodeHiddenAccessFlags()); + HiddenApiAccessFlags::EncodeForRuntime(access_flags, field.DecodeHiddenAccessFlags()); } dst->SetAccessFlags(access_flags); } void ClassLinker::LoadMethod(const DexFile& dex_file, - const ClassDataItemIterator& it, + const ClassAccessor::Method& method, Handle klass, ArtMethod* dst) { - uint32_t dex_method_idx = it.GetMemberIndex(); + const uint32_t dex_method_idx = method.GetIndex(); const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_); ScopedAssertNoThreadSuspension ants("LoadMethod"); dst->SetDexMethodIndex(dex_method_idx); dst->SetDeclaringClass(klass.Get()); - dst->SetCodeItemOffset(it.GetMethodCodeItemOffset()); + dst->SetCodeItemOffset(method.GetCodeItemOffset()); // Get access flags from the DexFile. If this is a boot class path class, // also set its runtime hidden API access flags. - uint32_t access_flags = it.GetMethodAccessFlags(); + uint32_t access_flags = method.GetAccessFlags(); if (klass->IsBootStrapClassLoaded()) { access_flags = - HiddenApiAccessFlags::EncodeForRuntime(access_flags, it.DecodeHiddenAccessFlags()); + HiddenApiAccessFlags::EncodeForRuntime(access_flags, method.DecodeHiddenAccessFlags()); } if (UNLIKELY(strcmp("finalize", method_name) == 0)) { @@ -4772,24 +4759,29 @@ bool ClassLinker::InitializeClass(Thread* self, Handle klass, this, *dex_class_def); const DexFile& dex_file = *dex_cache->GetDexFile(); - const uint8_t* class_data = dex_file.GetClassData(*dex_class_def); - ClassDataItemIterator field_it(dex_file, class_data); + if (value_it.HasNext()) { - DCHECK(field_it.HasNextStaticField()); + ClassAccessor accessor(dex_file, *dex_class_def); CHECK(can_init_statics); - for ( ; value_it.HasNext(); value_it.Next(), field_it.Next()) { - ArtField* field = ResolveField( - field_it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ true); + for (const ClassAccessor::Field& field : accessor.GetStaticFields()) { + if (!value_it.HasNext()) { + break; + } + ArtField* art_field = ResolveField(field.GetIndex(), + dex_cache, + class_loader, + /* is_static */ true); if (Runtime::Current()->IsActiveTransaction()) { - value_it.ReadValueToField(field); + value_it.ReadValueToField(art_field); } else { - value_it.ReadValueToField(field); + value_it.ReadValueToField(art_field); } if (self->IsExceptionPending()) { break; } - DCHECK(!value_it.HasNext() || field_it.HasNextStaticField()); + value_it.Next(); } + DCHECK(self->IsExceptionPending() || !value_it.HasNext()); } } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 58ce6eb25c..0f76bf3722 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -27,6 +27,7 @@ #include "base/enums.h" #include "base/macros.h" #include "base/mutex.h" +#include "dex/class_accessor.h" #include "dex/dex_cache_resolved_classes.h" #include "dex/dex_file.h" #include "dex/dex_file_types.h" @@ -825,18 +826,14 @@ class ClassLinker { const DexFile::ClassDef& dex_class_def, Handle klass) REQUIRES_SHARED(Locks::mutator_lock_); - void LoadClassMembers(Thread* self, - const DexFile& dex_file, - const uint8_t* class_data, - Handle klass) - REQUIRES_SHARED(Locks::mutator_lock_); - void LoadField(const ClassDataItemIterator& it, Handle klass, ArtField* dst) + void LoadField(const ClassAccessor::Field& field, Handle klass, ArtField* dst) REQUIRES_SHARED(Locks::mutator_lock_); void LoadMethod(const DexFile& dex_file, - const ClassDataItemIterator& it, - Handle klass, ArtMethod* dst) + const ClassAccessor::Method& method, + Handle klass, + ArtMethod* dst) REQUIRES_SHARED(Locks::mutator_lock_); void FixupStaticTrampolines(ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 8aed4a806c..3227c69305 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -32,6 +32,7 @@ extern "C" void android_set_application_target_sdk_version(uint32_t version); #include "class_linker-inl.h" #include "common_throws.h" #include "debugger.h" +#include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_types.h" #include "gc/accounting/card_table-inl.h" @@ -573,30 +574,12 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { } if (kPreloadDexCachesFieldsAndMethods) { - for (size_t class_def_index = 0; - class_def_index < dex_file->NumClassDefs(); - class_def_index++) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - continue; + for (ClassAccessor accessor : dex_file->GetClasses()) { + for (const ClassAccessor::Field& field : accessor.GetFields()) { + PreloadDexCachesResolveField(dex_cache, field.GetIndex(), field.IsStatic()); } - ClassDataItemIterator it(*dex_file, class_data); - for (; it.HasNextStaticField(); it.Next()) { - uint32_t field_idx = it.GetMemberIndex(); - PreloadDexCachesResolveField(dex_cache, field_idx, true); - } - for (; it.HasNextInstanceField(); it.Next()) { - uint32_t field_idx = it.GetMemberIndex(); - PreloadDexCachesResolveField(dex_cache, field_idx, false); - } - for (; it.HasNextDirectMethod(); it.Next()) { - uint32_t method_idx = it.GetMemberIndex(); - PreloadDexCachesResolveMethod(dex_cache, method_idx); - } - for (; it.HasNextVirtualMethod(); it.Next()) { - uint32_t method_idx = it.GetMemberIndex(); - PreloadDexCachesResolveMethod(dex_cache, method_idx); + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + PreloadDexCachesResolveMethod(dex_cache, method.GetIndex()); } } } diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index 838d7f14bc..32aa86dc93 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -28,6 +28,7 @@ #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "dex/art_dex_file_loader.h" +#include "dex/class_accessor-inl.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "dex/hidden_api_access_flags.h" @@ -283,30 +284,25 @@ void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, std::unordered_set unquickened_code_item; CompactOffsetTable::Accessor accessor(GetQuickenInfoOffsetTable(source_dex_begin, quickening_info)); - for (uint32_t i = 0; i < target_dex_file.NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = target_dex_file.GetClassDef(i); - const uint8_t* class_data = target_dex_file.GetClassData(class_def); - if (class_data != nullptr) { - for (ClassDataItemIterator class_it(target_dex_file, class_data); - class_it.HasNext(); - class_it.Next()) { - if (class_it.IsAtMethod()) { - const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem(); - if (code_item != nullptr && unquickened_code_item.emplace(code_item).second) { - const uint32_t offset = accessor.GetOffset(class_it.GetMemberIndex()); - // Offset being 0 means not quickened. - if (offset != 0u) { - ArrayRef quicken_data = GetQuickeningInfoAt(quickening_info, offset); - optimizer::ArtDecompileDEX( - target_dex_file, - *code_item, - quicken_data, - decompile_return_instruction); - } - } + for (ClassAccessor class_accessor : target_dex_file.GetClasses()) { + for (const ClassAccessor::Method& method : class_accessor.GetMethods()) { + const DexFile::CodeItem* code_item = method.GetCodeItem(); + if (code_item != nullptr && unquickened_code_item.emplace(code_item).second) { + const uint32_t offset = accessor.GetOffset(method.GetIndex()); + // Offset being 0 means not quickened. + if (offset != 0u) { + ArrayRef quicken_data = GetQuickeningInfoAt(quickening_info, offset); + optimizer::ArtDecompileDEX( + target_dex_file, + *code_item, + quicken_data, + decompile_return_instruction); } - DexFile::UnHideAccessFlags(class_it); } + method.UnHideAccessFlags(); + } + for (const ClassAccessor::Field& field : class_accessor.GetFields()) { + field.UnHideAccessFlags(); } } } diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index f57f757f0b..59617481eb 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -35,6 +35,7 @@ #include "class_linker.h" #include "class_root.h" #include "compiler_callbacks.h" +#include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_exception_helpers.h" @@ -190,11 +191,6 @@ FailureKind MethodVerifier::VerifyClass(Thread* self, error); } -template -static bool HasNextMethod(ClassDataItemIterator* it) { - return kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod(); -} - static FailureKind FailureKindMax(FailureKind fk1, FailureKind fk2) { static_assert(FailureKind::kNoFailure < FailureKind::kSoftFailure && FailureKind::kSoftFailure < FailureKind::kHardFailure, @@ -207,45 +203,51 @@ void MethodVerifier::FailureData::Merge(const MethodVerifier::FailureData& fd) { types |= fd.types; } -template -MethodVerifier::FailureData MethodVerifier::VerifyMethods(Thread* self, - ClassLinker* linker, - const DexFile* dex_file, - const DexFile::ClassDef& class_def, - ClassDataItemIterator* it, - Handle dex_cache, - Handle class_loader, - CompilerCallbacks* callbacks, - bool allow_soft_failures, - HardFailLogMode log_level, - bool need_precise_constants, - std::string* error_string) { - DCHECK(it != nullptr); +FailureKind MethodVerifier::VerifyClass(Thread* self, + const DexFile* dex_file, + Handle dex_cache, + Handle class_loader, + const DexFile::ClassDef& class_def, + CompilerCallbacks* callbacks, + bool allow_soft_failures, + HardFailLogMode log_level, + std::string* error) { + SCOPED_TRACE << "VerifyClass " << PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); + // A class must not be abstract and final. + if ((class_def.access_flags_ & (kAccAbstract | kAccFinal)) == (kAccAbstract | kAccFinal)) { + *error = "Verifier rejected class "; + *error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); + *error += ": class is abstract and final."; + return FailureKind::kHardFailure; + } + + ClassAccessor accessor(*dex_file, class_def); + + int64_t previous_method_idx[2] = { -1, -1 }; MethodVerifier::FailureData failure_data; + ClassLinker* const linker = Runtime::Current()->GetClassLinker(); - int64_t previous_method_idx = -1; - while (HasNextMethod(it)) { + for (const ClassAccessor::Method& method : accessor.GetMethods()) { + int64_t* previous_idx = &previous_method_idx[method.IsStaticOrDirect() ? 0u : 1u]; self->AllowThreadSuspension(); - uint32_t method_idx = it->GetMemberIndex(); - if (method_idx == previous_method_idx) { + const uint32_t method_idx = method.GetIndex(); + if (method_idx == *previous_idx) { // smali can create dex files with two encoded_methods sharing the same method_idx // http://code.google.com/p/smali/issues/detail?id=119 - it->Next(); continue; } - previous_method_idx = method_idx; - InvokeType type = it->GetMethodInvokeType(class_def); - ArtMethod* method = linker->ResolveMethod( + *previous_idx = method_idx; + const InvokeType type = method.GetInvokeType(class_def.access_flags_); + ArtMethod* resolved_method = linker->ResolveMethod( method_idx, dex_cache, class_loader, /* referrer */ nullptr, type); - if (method == nullptr) { + if (resolved_method == nullptr) { DCHECK(self->IsExceptionPending()); // We couldn't resolve the method, but continue regardless. self->ClearException(); } else { - DCHECK(method->GetDeclaringClassUnchecked() != nullptr) << type; + DCHECK(resolved_method->GetDeclaringClassUnchecked() != nullptr) << type; } - StackHandleScope<1> hs(self); std::string hard_failure_msg; MethodVerifier::FailureData result = VerifyMethod(self, method_idx, @@ -253,99 +255,39 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethods(Thread* self, dex_cache, class_loader, class_def, - it->GetMethodCodeItem(), - method, - it->GetMethodAccessFlags(), + method.GetCodeItem(), + resolved_method, + method.GetAccessFlags(), callbacks, allow_soft_failures, log_level, - need_precise_constants, + /*need_precise_constants*/ false, &hard_failure_msg); if (result.kind == FailureKind::kHardFailure) { if (failure_data.kind == FailureKind::kHardFailure) { // If we logged an error before, we need a newline. - *error_string += "\n"; + *error += "\n"; } else { // If we didn't log a hard failure before, print the header of the message. - *error_string += "Verifier rejected class "; - *error_string += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); - *error_string += ":"; + *error += "Verifier rejected class "; + *error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); + *error += ":"; } - *error_string += " "; - *error_string += hard_failure_msg; + *error += " "; + *error += hard_failure_msg; } failure_data.Merge(result); - it->Next(); - } - - return failure_data; -} - -FailureKind MethodVerifier::VerifyClass(Thread* self, - const DexFile* dex_file, - Handle dex_cache, - Handle class_loader, - const DexFile::ClassDef& class_def, - CompilerCallbacks* callbacks, - bool allow_soft_failures, - HardFailLogMode log_level, - std::string* error) { - SCOPED_TRACE << "VerifyClass " << PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); - - // A class must not be abstract and final. - if ((class_def.access_flags_ & (kAccAbstract | kAccFinal)) == (kAccAbstract | kAccFinal)) { - *error = "Verifier rejected class "; - *error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); - *error += ": class is abstract and final."; - return FailureKind::kHardFailure; } - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - // empty class, probably a marker interface - return FailureKind::kNoFailure; - } - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - ClassLinker* linker = Runtime::Current()->GetClassLinker(); - // Direct methods. - MethodVerifier::FailureData data1 = VerifyMethods(self, - linker, - dex_file, - class_def, - &it, - dex_cache, - class_loader, - callbacks, - allow_soft_failures, - log_level, - false /* need precise constants */, - error); - // Virtual methods. - MethodVerifier::FailureData data2 = VerifyMethods(self, - linker, - dex_file, - class_def, - &it, - dex_cache, - class_loader, - callbacks, - allow_soft_failures, - log_level, - false /* need precise constants */, - error); - - data1.Merge(data2); - - if (data1.kind == FailureKind::kNoFailure) { + if (failure_data.kind == FailureKind::kNoFailure) { return FailureKind::kNoFailure; } else { - if ((data1.types & VERIFY_ERROR_LOCKING) != 0) { + if ((failure_data.types & VERIFY_ERROR_LOCKING) != 0) { // Print a warning about expected slow-down. Use a string temporary to print one contiguous // warning. std::string tmp = StringPrintf("Class %s failed lock verification and will run slower.", - PrettyDescriptor(dex_file->GetClassDescriptor(class_def)).c_str()); + PrettyDescriptor(accessor.GetDescriptor()).c_str()); if (!gPrintedDxMonitorText) { tmp = tmp + "\nCommon causes for lock verification issues are non-optimized dex code\n" "and incorrect proguard optimizations."; @@ -353,7 +295,7 @@ FailureKind MethodVerifier::VerifyClass(Thread* self, } LOG(WARNING) << tmp; } - return data1.kind; + return failure_data.kind; } } @@ -1924,15 +1866,11 @@ bool MethodVerifier::CodeFlowVerifyMethod() { static uint32_t GetFirstFinalInstanceFieldIndex(const DexFile& dex_file, dex::TypeIndex type_idx) { const DexFile::ClassDef* class_def = dex_file.FindClassDef(type_idx); DCHECK(class_def != nullptr); - const uint8_t* class_data = dex_file.GetClassData(*class_def); - DCHECK(class_data != nullptr); - ClassDataItemIterator it(dex_file, class_data); - it.SkipStaticFields(); - while (it.HasNextInstanceField()) { - if ((it.GetFieldAccessFlags() & kAccFinal) != 0) { - return it.GetMemberIndex(); - } - it.Next(); + ClassAccessor accessor(dex_file, *class_def); + for (const ClassAccessor::Field& field : accessor.GetInstanceFields()) { + if (field.IsFinal()) { + return field.GetIndex(); + } } return dex::kDexNoIndex; } diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index ae7481c6b1..9890af9d95 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -275,23 +275,6 @@ class MethodVerifier { void Merge(const FailureData& src); }; - // Verify all direct or virtual methods of a class. The method assumes that the iterator is - // positioned correctly, and the iterator will be updated. - template - static FailureData VerifyMethods(Thread* self, - ClassLinker* linker, - const DexFile* dex_file, - const DexFile::ClassDef& class_def, - ClassDataItemIterator* it, - Handle dex_cache, - Handle class_loader, - CompilerCallbacks* callbacks, - bool allow_soft_failures, - HardFailLogMode log_level, - bool need_precise_constants, - std::string* error_string) - REQUIRES_SHARED(Locks::mutator_lock_); - /* * Perform verification on a single method. * -- GitLab From 54f535a3b43186727ff84fb4d604f6c57473d8ce Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 4 Jun 2018 14:14:19 -0700 Subject: [PATCH 531/749] Pass SOCK_CLOEXEC to adb socket. We were not passing CLOEXEC to the adb-socket in adbconnection. This could lead to extra (unusable) jdwp PIDs in adb jdwp in some circumstances. Test: Build an app with wrap.sh. adb jdwp on a userdebug device. Ensure pid of wrap.sh is not present in list. Bug: 109505014 Change-Id: Icd41bf037666d6abe35897681aed98b00fb290ac --- adbconnection/adbconnection.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc index 8cd0d8bc9f..d399c2a25c 100644 --- a/adbconnection/adbconnection.cc +++ b/adbconnection/adbconnection.cc @@ -489,7 +489,7 @@ bool AdbConnectionState::SetupAdbConnection() { int sleep_ms = 500; const int sleep_max_ms = 2*1000; - android::base::unique_fd sock(socket(AF_UNIX, SOCK_SEQPACKET, 0)); + android::base::unique_fd sock(socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0)); if (sock < 0) { PLOG(ERROR) << "Could not create ADB control socket"; return false; -- GitLab From 5ebdc88a8469cb2bb9188c18b379ca486d338728 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 4 Jun 2018 16:42:30 -0700 Subject: [PATCH 532/749] Add missing ',' in default jdwp argument list We were incorrectly missing a ',' in the default jdwp argument list. This meant that a manual -XjdwpProvider:adbconnection will fail due to failing to parse the argument string. Test: run app with wrap.sh that adds '-XjdwpProvider:adbconnection -Xcompiler-option --debuggable' Attach debugger Bug: 109505014 Change-Id: Id09e04cd92c2f89a4bcdf95b05071bacd509b286 --- adbconnection/adbconnection.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc index d399c2a25c..ad941481fd 100644 --- a/adbconnection/adbconnection.cc +++ b/adbconnection/adbconnection.cc @@ -872,7 +872,7 @@ std::string AdbConnectionState::MakeAgentArg() { (ContainsArgument(opts, "server=y") ? "" : "server=y,") + // See the comment above for why we need to be suspend=n. Since the agent defaults to // suspend=y we will add it if it wasn't already present. - (ContainsArgument(opts, "suspend=n") ? "" : "suspend=n") + + (ContainsArgument(opts, "suspend=n") ? "" : "suspend=n,") + "transport=dt_fd_forward,address=" + std::to_string(remote_agent_control_sock_); } -- GitLab From 6e69e52a12883386f91d014324bebee867ca7877 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Sun, 3 Jun 2018 12:00:14 +0100 Subject: [PATCH 533/749] Remove depth argument from InlineInfo accessors in stack maps. The InlineInfo class actually represented a list of inlining information for a given stack map, and the depth argument was used everywhere to select to desired element from the list. This was verbose and inconsistent with the other classes. Change the InlineInfo class to represent a single inlining, and select the desired depth when getting it from CodeInfo. Test: test-art-host-gtest-stack_map_test Change-Id: I35b73e6704854f0203f51d4dbdbed5b1d1cd5a3b --- compiler/optimizing/stack_map_stream.cc | 20 ++--- compiler/optimizing/stack_map_test.cc | 84 ++++++++++--------- runtime/entrypoints/entrypoint_utils-inl.h | 19 +++-- runtime/entrypoints/entrypoint_utils.cc | 9 +- .../quick/quick_trampoline_entrypoints.cc | 21 ++--- runtime/quick_exception_handler.cc | 2 +- runtime/stack.cc | 26 +++--- runtime/stack_map.cc | 45 +++++----- runtime/stack_map.h | 72 ++++++++-------- 9 files changed, 150 insertions(+), 148 deletions(-) diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 7a60d4aa20..d80e2fc0e3 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -84,9 +84,7 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, CHECK_EQ(seen_stack_mask.LoadBit(b), stack_mask != nullptr && stack_mask->IsBitSet(b)); } CHECK_EQ(stack_map.HasInlineInfo(), (inlining_depth != 0)); - if (inlining_depth != 0) { - CHECK_EQ(code_info.GetInlineInfoOf(stack_map).GetDepth(), inlining_depth); - } + CHECK_EQ(code_info.GetInlineDepthOf(stack_map), inlining_depth); CHECK_EQ(stack_map.HasDexRegisterMap(), (num_dex_registers != 0)); }); } @@ -174,17 +172,17 @@ void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, size_t depth = current_inline_infos_.size() - 1; dchecks_.emplace_back([=](const CodeInfo& code_info) { StackMap stack_map = code_info.GetStackMapAt(stack_map_index); - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); - CHECK_EQ(inline_info.GetDexPcAtDepth(depth), dex_pc); + InlineInfo inline_info = code_info.GetInlineInfoAtDepth(stack_map, depth); + CHECK_EQ(inline_info.GetDexPc(), dex_pc); bool encode_art_method = EncodeArtMethodInInlineInfo(method); - CHECK_EQ(inline_info.EncodesArtMethodAtDepth(depth), encode_art_method); + CHECK_EQ(inline_info.EncodesArtMethod(), encode_art_method); if (encode_art_method) { - CHECK_EQ(inline_info.GetArtMethodAtDepth(depth), method); + CHECK_EQ(inline_info.GetArtMethod(), method); } else { - CHECK_EQ(method_infos_[inline_info.GetMethodIndexIdxAtDepth(depth)], + CHECK_EQ(method_infos_[inline_info.GetMethodIndexIdx()], method->GetDexMethodIndexUnchecked()); } - CHECK_EQ(inline_info.HasDexRegisterMapAtDepth(depth), (num_dex_registers != 0)); + CHECK_EQ(inline_info.HasDexRegisterMap(), (num_dex_registers != 0)); }); } } @@ -240,9 +238,7 @@ void StackMapStream::CreateDexRegisterMap() { size_t num_dex_registers = expected_dex_registers->size(); DexRegisterMap map = (depth == -1) ? code_info.GetDexRegisterMapOf(stack_map, num_dex_registers) - : code_info.GetDexRegisterMapAtDepth(depth, - code_info.GetInlineInfoOf(stack_map), - num_dex_registers); + : code_info.GetDexRegisterMapAtDepth(depth, stack_map, num_dex_registers); CHECK_EQ(map.size(), num_dex_registers); for (size_t r = 0; r < num_dex_registers; r++) { CHECK_EQ(expected_dex_registers->at(r), map.Get(r)); diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index 262c240bc7..77aa3ef965 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -189,12 +189,13 @@ TEST(StackMapTest, Test2) { ASSERT_EQ(-2, location1.GetValue()); ASSERT_TRUE(stack_map.HasInlineInfo()); - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); - ASSERT_EQ(2u, inline_info.GetDepth()); - ASSERT_EQ(3u, inline_info.GetDexPcAtDepth(0)); - ASSERT_EQ(2u, inline_info.GetDexPcAtDepth(1)); - ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(0)); - ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(1)); + InlineInfo inline_info0 = code_info.GetInlineInfoAtDepth(stack_map, 0); + InlineInfo inline_info1 = code_info.GetInlineInfoAtDepth(stack_map, 1); + ASSERT_EQ(2u, code_info.GetInlineDepthOf(stack_map)); + ASSERT_EQ(3u, inline_info0.GetDexPc()); + ASSERT_EQ(2u, inline_info1.GetDexPc()); + ASSERT_TRUE(inline_info0.EncodesArtMethod()); + ASSERT_TRUE(inline_info1.EncodesArtMethod()); } // Second stack map. @@ -361,8 +362,8 @@ TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { // Test that the inline info dex register map deduplicated to the same offset as the stack map // one. ASSERT_TRUE(stack_map.HasInlineInfo()); - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); - EXPECT_EQ(inline_info.GetDexRegisterMapIndexAtDepth(0), + InlineInfo inline_info = code_info.GetInlineInfoAtDepth(stack_map, 0); + EXPECT_EQ(inline_info.GetDexRegisterMapIndex(), stack_map.GetDexRegisterMapIndex()); } } @@ -605,17 +606,18 @@ TEST(StackMapTest, InlineTest) { ASSERT_EQ(0, dex_registers0.GetStackOffsetInBytes(0)); ASSERT_EQ(4, dex_registers0.GetConstant(1)); - InlineInfo if0 = ci.GetInlineInfoOf(sm0); - ASSERT_EQ(2u, if0.GetDepth()); - ASSERT_EQ(2u, if0.GetDexPcAtDepth(0)); - ASSERT_TRUE(if0.EncodesArtMethodAtDepth(0)); - ASSERT_EQ(3u, if0.GetDexPcAtDepth(1)); - ASSERT_TRUE(if0.EncodesArtMethodAtDepth(1)); + InlineInfo if0_0 = ci.GetInlineInfoAtDepth(sm0, 0); + InlineInfo if0_1 = ci.GetInlineInfoAtDepth(sm0, 1); + ASSERT_EQ(2u, ci.GetInlineDepthOf(sm0)); + ASSERT_EQ(2u, if0_0.GetDexPc()); + ASSERT_TRUE(if0_0.EncodesArtMethod()); + ASSERT_EQ(3u, if0_1.GetDexPc()); + ASSERT_TRUE(if0_1.EncodesArtMethod()); - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if0, 1); + DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, sm0, 1); ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0)); - DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if0, 3); + DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, sm0, 3); ASSERT_EQ(16, dex_registers2.GetStackOffsetInBytes(0)); ASSERT_EQ(20, dex_registers2.GetConstant(1)); ASSERT_EQ(15, dex_registers2.GetMachineRegister(2)); @@ -629,24 +631,26 @@ TEST(StackMapTest, InlineTest) { ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0)); ASSERT_EQ(0, dex_registers0.GetConstant(1)); - InlineInfo if1 = ci.GetInlineInfoOf(sm1); - ASSERT_EQ(3u, if1.GetDepth()); - ASSERT_EQ(2u, if1.GetDexPcAtDepth(0)); - ASSERT_TRUE(if1.EncodesArtMethodAtDepth(0)); - ASSERT_EQ(3u, if1.GetDexPcAtDepth(1)); - ASSERT_TRUE(if1.EncodesArtMethodAtDepth(1)); - ASSERT_EQ(5u, if1.GetDexPcAtDepth(2)); - ASSERT_TRUE(if1.EncodesArtMethodAtDepth(2)); - - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if1, 1); + InlineInfo if1_0 = ci.GetInlineInfoAtDepth(sm1, 0); + InlineInfo if1_1 = ci.GetInlineInfoAtDepth(sm1, 1); + InlineInfo if1_2 = ci.GetInlineInfoAtDepth(sm1, 2); + ASSERT_EQ(3u, ci.GetInlineDepthOf(sm1)); + ASSERT_EQ(2u, if1_0.GetDexPc()); + ASSERT_TRUE(if1_0.EncodesArtMethod()); + ASSERT_EQ(3u, if1_1.GetDexPc()); + ASSERT_TRUE(if1_1.EncodesArtMethod()); + ASSERT_EQ(5u, if1_2.GetDexPc()); + ASSERT_TRUE(if1_2.EncodesArtMethod()); + + DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, sm1, 1); ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0)); - DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if1, 3); + DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, sm1, 3); ASSERT_EQ(80, dex_registers2.GetStackOffsetInBytes(0)); ASSERT_EQ(10, dex_registers2.GetConstant(1)); ASSERT_EQ(5, dex_registers2.GetMachineRegister(2)); - ASSERT_FALSE(if1.HasDexRegisterMapAtDepth(2)); + ASSERT_FALSE(if1_2.HasDexRegisterMap()); } { @@ -667,21 +671,23 @@ TEST(StackMapTest, InlineTest) { ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0)); ASSERT_EQ(0, dex_registers0.GetConstant(1)); - InlineInfo if2 = ci.GetInlineInfoOf(sm3); - ASSERT_EQ(3u, if2.GetDepth()); - ASSERT_EQ(2u, if2.GetDexPcAtDepth(0)); - ASSERT_TRUE(if2.EncodesArtMethodAtDepth(0)); - ASSERT_EQ(5u, if2.GetDexPcAtDepth(1)); - ASSERT_TRUE(if2.EncodesArtMethodAtDepth(1)); - ASSERT_EQ(10u, if2.GetDexPcAtDepth(2)); - ASSERT_TRUE(if2.EncodesArtMethodAtDepth(2)); + InlineInfo if2_0 = ci.GetInlineInfoAtDepth(sm3, 0); + InlineInfo if2_1 = ci.GetInlineInfoAtDepth(sm3, 1); + InlineInfo if2_2 = ci.GetInlineInfoAtDepth(sm3, 2); + ASSERT_EQ(3u, ci.GetInlineDepthOf(sm3)); + ASSERT_EQ(2u, if2_0.GetDexPc()); + ASSERT_TRUE(if2_0.EncodesArtMethod()); + ASSERT_EQ(5u, if2_1.GetDexPc()); + ASSERT_TRUE(if2_1.EncodesArtMethod()); + ASSERT_EQ(10u, if2_2.GetDexPc()); + ASSERT_TRUE(if2_2.EncodesArtMethod()); - ASSERT_FALSE(if2.HasDexRegisterMapAtDepth(0)); + ASSERT_FALSE(if2_0.HasDexRegisterMap()); - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(1, if2, 1); + DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(1, sm3, 1); ASSERT_EQ(2, dex_registers1.GetMachineRegister(0)); - DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(2, if2, 2); + DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(2, sm3, 2); ASSERT_FALSE(dex_registers2.IsDexRegisterLive(0)); ASSERT_EQ(3, dex_registers2.GetMachineRegister(1)); } diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index f6b1c73230..12e1397311 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -46,22 +46,24 @@ namespace art { inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, const MethodInfo& method_info, - const InlineInfo& inline_info, + const CodeInfo& code_info, + const StackMap& stack_map, uint8_t inlining_depth) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(!outer_method->IsObsolete()); + InlineInfo inline_info = code_info.GetInlineInfoAtDepth(stack_map, inlining_depth); // This method is being used by artQuickResolutionTrampoline, before it sets up // the passed parameters in a GC friendly way. Therefore we must never be // suspended while executing it. ScopedAssertNoThreadSuspension sants(__FUNCTION__); - if (inline_info.EncodesArtMethodAtDepth(inlining_depth)) { - return inline_info.GetArtMethodAtDepth(inlining_depth); + if (inline_info.EncodesArtMethod()) { + return inline_info.GetArtMethod(); } - uint32_t method_index = inline_info.GetMethodIndexAtDepth(method_info, inlining_depth); - if (inline_info.GetDexPcAtDepth(inlining_depth) == static_cast(-1)) { + uint32_t method_index = inline_info.GetMethodIndex(method_info); + if (inline_info.GetDexPc() == static_cast(-1)) { // "charAt" special case. It is the only non-leaf method we inline across dex files. ArtMethod* inlined_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt); DCHECK_EQ(inlined_method->GetDexMethodIndex(), method_index); @@ -72,9 +74,10 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); ArtMethod* method = outer_method; for (uint32_t depth = 0, end = inlining_depth + 1u; depth != end; ++depth) { - DCHECK(!inline_info.EncodesArtMethodAtDepth(depth)); - DCHECK_NE(inline_info.GetDexPcAtDepth(depth), static_cast(-1)); - method_index = inline_info.GetMethodIndexAtDepth(method_info, depth); + inline_info = code_info.GetInlineInfoAtDepth(stack_map, depth); + DCHECK(!inline_info.EncodesArtMethod()); + DCHECK_NE(inline_info.GetDexPc(), static_cast(-1)); + method_index = inline_info.GetMethodIndex(method_info); ArtMethod* inlined_method = class_linker->LookupResolvedMethod(method_index, method->GetDexCache(), method->GetClassLoader()); diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 7f9b385a0a..e71d1fa38a 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -205,12 +205,9 @@ static inline ArtMethod* DoGetCalleeSaveMethodCaller(ArtMethod* outer_method, MethodInfo method_info = current_code->GetOptimizedMethodInfo(); StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); DCHECK(stack_map.IsValid()); - if (stack_map.HasInlineInfo()) { - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); - caller = GetResolvedMethod(outer_method, - method_info, - inline_info, - inline_info.GetDepth() - 1); + uint32_t depth = code_info.GetInlineDepthOf(stack_map); + if (depth != 0) { + caller = GetResolvedMethod(outer_method, method_info, code_info, stack_map, depth - 1); } } if (kIsDebugBuild && do_caller_check) { diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index a59faeae9b..af6a936d40 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -344,9 +344,10 @@ class QuickArgumentVisitor { CodeInfo code_info(current_code); StackMap stack_map = code_info.GetStackMapForNativePcOffset(outer_pc_offset); DCHECK(stack_map.IsValid()); - if (stack_map.HasInlineInfo()) { - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); - return inline_info.GetDexPcAtDepth(inline_info.GetDepth()-1); + uint32_t depth = code_info.GetInlineDepthOf(stack_map); + if (depth != 0) { + InlineInfo inline_info = code_info.GetInlineInfoAtDepth(stack_map, depth - 1); + return inline_info.GetDexPc(); } else { return stack_map.GetDexPc(); } @@ -1231,17 +1232,17 @@ static void DumpB74410240DebugData(ArtMethod** sp) REQUIRES_SHARED(Locks::mutato LOG(FATAL_WITHOUT_ABORT) << " instruction: " << DumpInstruction(outer_method, dex_pc); ArtMethod* caller = outer_method; - if (stack_map.HasInlineInfo()) { - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); - size_t depth = inline_info.GetDepth(); + uint32_t depth = code_info.GetInlineDepthOf(stack_map); + if (depth != 0) { for (size_t d = 0; d < depth; ++d) { + InlineInfo inline_info = code_info.GetInlineInfoAtDepth(stack_map, d); const char* tag = ""; - dex_pc = inline_info.GetDexPcAtDepth(d); - if (inline_info.EncodesArtMethodAtDepth(d)) { + dex_pc = inline_info.GetDexPc(); + if (inline_info.EncodesArtMethod()) { tag = "encoded "; - caller = inline_info.GetArtMethodAtDepth(d); + caller = inline_info.GetArtMethod(); } else { - uint32_t method_index = inline_info.GetMethodIndexAtDepth(method_info, d); + uint32_t method_index = inline_info.GetMethodIndex(method_info); if (dex_pc == static_cast(-1)) { tag = "special "; CHECK_EQ(d + 1u, depth); diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 4f4abf7f7f..23ccf6ad58 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -406,7 +406,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map); DexRegisterMap vreg_map = IsInInlinedFrame() ? code_info.GetDexRegisterMapAtDepth(GetCurrentInliningDepth() - 1, - code_info.GetInlineInfoOf(stack_map), + stack_map, number_of_vregs) : code_info.GetDexRegisterMapOf(stack_map, number_of_vregs); diff --git a/runtime/stack.cc b/runtime/stack.cc index 6da7dcb697..bd0d5d680e 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -76,14 +76,14 @@ StackVisitor::StackVisitor(Thread* thread, } } -static InlineInfo GetCurrentInlineInfo(CodeInfo& code_info, - const OatQuickMethodHeader* method_header, - uintptr_t cur_quick_frame_pc) +static StackMap GetCurrentStackMap(CodeInfo& code_info, + const OatQuickMethodHeader* method_header, + uintptr_t cur_quick_frame_pc) REQUIRES_SHARED(Locks::mutator_lock_) { uint32_t native_pc_offset = method_header->NativeQuickPcOffset(cur_quick_frame_pc); StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); DCHECK(stack_map.IsValid()); - return code_info.GetInlineInfoOf(stack_map); + return stack_map; } ArtMethod* StackVisitor::GetMethod() const { @@ -94,14 +94,13 @@ ArtMethod* StackVisitor::GetMethod() const { size_t depth_in_stack_map = current_inlining_depth_ - 1; const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); CodeInfo code_info(method_header); - InlineInfo inline_info = GetCurrentInlineInfo(code_info, - method_header, - cur_quick_frame_pc_); + StackMap stack_map = GetCurrentStackMap(code_info, method_header, cur_quick_frame_pc_); MethodInfo method_info = method_header->GetOptimizedMethodInfo(); DCHECK(walk_kind_ != StackWalkKind::kSkipInlinedFrames); return GetResolvedMethod(*GetCurrentQuickFrame(), method_info, - inline_info, + code_info, + stack_map, depth_in_stack_map); } else { return *cur_quick_frame_; @@ -118,8 +117,8 @@ uint32_t StackVisitor::GetDexPc(bool abort_on_failure) const { const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); CodeInfo code_info(method_header); size_t depth_in_stack_map = current_inlining_depth_ - 1; - return GetCurrentInlineInfo(code_info, method_header, cur_quick_frame_pc_). - GetDexPcAtDepth(depth_in_stack_map); + StackMap stack_map = GetCurrentStackMap(code_info, method_header, cur_quick_frame_pc_); + return code_info.GetInlineInfoAtDepth(stack_map, depth_in_stack_map).GetDexPc(); } else if (cur_oat_quick_method_header_ == nullptr) { return dex::kDexNoIndex; } else { @@ -237,9 +236,7 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin size_t depth_in_stack_map = current_inlining_depth_ - 1; DexRegisterMap dex_register_map = IsInInlinedFrame() - ? code_info.GetDexRegisterMapAtDepth(depth_in_stack_map, - code_info.GetInlineInfoOf(stack_map), - number_of_dex_registers) + ? code_info.GetDexRegisterMapAtDepth(depth_in_stack_map, stack_map, number_of_dex_registers) : code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); if (!dex_register_map.IsValid()) { @@ -825,9 +822,8 @@ void StackVisitor::WalkStack(bool include_transitions) { cur_oat_quick_method_header_->NativeQuickPcOffset(cur_quick_frame_pc_); StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); if (stack_map.IsValid() && stack_map.HasInlineInfo()) { - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); DCHECK_EQ(current_inlining_depth_, 0u); - for (current_inlining_depth_ = inline_info.GetDepth(); + for (current_inlining_depth_ = code_info.GetInlineDepthOf(stack_map); current_inlining_depth_ != 0; --current_inlining_depth_) { bool should_continue = VisitFrame(); diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index 923bb3559a..a5749b84a7 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -146,38 +146,39 @@ void StackMap::Dump(VariableIndentationOutputStream* vios, } vios->Stream() << ")\n"; DumpDexRegisterMap(vios, code_info.GetDexRegisterMapOf(*this, number_of_dex_registers)); - if (HasInlineInfo()) { - InlineInfo inline_info = code_info.GetInlineInfoOf(*this); + uint32_t depth = code_info.GetInlineDepthOf(*this); + for (size_t d = 0; d < depth; d++) { + InlineInfo inline_info = code_info.GetInlineInfoAtDepth(*this, d); // We do not know the length of the dex register maps of inlined frames // at this level, so we just pass null to `InlineInfo::Dump` to tell // it not to look at these maps. - inline_info.Dump(vios, code_info, method_info, nullptr); + inline_info.Dump(vios, code_info, *this, method_info, 0); } } void InlineInfo::Dump(VariableIndentationOutputStream* vios, const CodeInfo& code_info, + const StackMap& stack_map, const MethodInfo& method_info, - uint16_t number_of_dex_registers[]) const { - for (size_t i = 0; i < GetDepth(); ++i) { + uint16_t number_of_dex_registers) const { + uint32_t depth = Row() - stack_map.GetInlineInfoIndex(); + vios->Stream() + << "InlineInfo[" << Row() << "]" + << " (depth=" << depth + << std::hex + << ", dex_pc=0x" << GetDexPc(); + if (EncodesArtMethod()) { + ScopedObjectAccess soa(Thread::Current()); + vios->Stream() << ", method=" << GetArtMethod()->PrettyMethod(); + } else { vios->Stream() - << "InlineInfo[" << Row() + i << "]" - << " (depth=" << i - << std::hex - << ", dex_pc=0x" << GetDexPcAtDepth(i); - if (EncodesArtMethodAtDepth(i)) { - ScopedObjectAccess soa(Thread::Current()); - vios->Stream() << ", method=" << GetArtMethodAtDepth(i)->PrettyMethod(); - } else { - vios->Stream() - << std::dec - << ", method_index=" << GetMethodIndexAtDepth(method_info, i); - } - vios->Stream() << ")\n"; - if (number_of_dex_registers != nullptr) { - uint16_t vregs = number_of_dex_registers[i]; - DumpDexRegisterMap(vios, code_info.GetDexRegisterMapAtDepth(i, *this, vregs)); - } + << std::dec + << ", method_index=" << GetMethodIndex(method_info); + } + vios->Stream() << ")\n"; + if (number_of_dex_registers != 0) { + uint16_t vregs = number_of_dex_registers; + DumpDexRegisterMap(vios, code_info.GetDexRegisterMapAtDepth(depth, stack_map, vregs)); } } diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 9aac204e70..b04197e160 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -222,54 +222,47 @@ class InlineInfo : public BitTable<7>::Accessor { InlineInfo(const BitTable* table, uint32_t row) : BitTable::Accessor(table, row) {} - ALWAYS_INLINE InlineInfo AtDepth(uint32_t depth) const { - return InlineInfo(table_, this->row_ + depth); - } - - uint32_t GetDepth() const { - size_t depth = 0; - while (AtDepth(depth++).Get() == kMore) { } - return depth; - } + uint32_t GetIsLast() const { return Get(); } - uint32_t GetMethodIndexIdxAtDepth(uint32_t depth) const { - DCHECK(!EncodesArtMethodAtDepth(depth)); - return AtDepth(depth).Get(); + uint32_t GetMethodIndexIdx() const { + DCHECK(!EncodesArtMethod()); + return Get(); } - uint32_t GetMethodIndexAtDepth(const MethodInfo& method_info, uint32_t depth) const { - return method_info.GetMethodIndex(GetMethodIndexIdxAtDepth(depth)); + uint32_t GetMethodIndex(const MethodInfo& method_info) const { + return method_info.GetMethodIndex(GetMethodIndexIdx()); } - uint32_t GetDexPcAtDepth(uint32_t depth) const { - return AtDepth(depth).Get(); + uint32_t GetDexPc() const { + return Get(); } - bool EncodesArtMethodAtDepth(uint32_t depth) const { - return AtDepth(depth).Get() != kNoValue; + bool EncodesArtMethod() const { + return Get() != kNoValue; } - ArtMethod* GetArtMethodAtDepth(uint32_t depth) const { - uint64_t lo = AtDepth(depth).Get(); - uint64_t hi = AtDepth(depth).Get(); + ArtMethod* GetArtMethod() const { + uint64_t lo = Get(); + uint64_t hi = Get(); return reinterpret_cast((hi << 32) | lo); } - uint32_t GetDexRegisterMaskIndexAtDepth(uint32_t depth) const { - return AtDepth(depth).Get(); + uint32_t GetDexRegisterMaskIndex() const { + return Get(); } - uint32_t GetDexRegisterMapIndexAtDepth(uint32_t depth) const { - return AtDepth(depth).Get(); + uint32_t GetDexRegisterMapIndex() const { + return Get(); } - bool HasDexRegisterMapAtDepth(uint32_t depth) const { - return GetDexRegisterMapIndexAtDepth(depth) != kNoValue; + bool HasDexRegisterMap() const { + return GetDexRegisterMapIndex() != kNoValue; } void Dump(VariableIndentationOutputStream* vios, const CodeInfo& info, + const StackMap& stack_map, const MethodInfo& method_info, - uint16_t* number_of_dex_registers) const; + uint16_t number_of_dex_registers) const; }; class InvokeInfo : public BitTable<3>::Accessor { @@ -417,10 +410,11 @@ class CodeInfo { } ALWAYS_INLINE DexRegisterMap GetDexRegisterMapAtDepth(uint8_t depth, - InlineInfo inline_info, + StackMap stack_map, size_t num_dex_registers) const { - return DecodeDexRegisterMap(inline_info.GetDexRegisterMaskIndexAtDepth(depth), - inline_info.GetDexRegisterMapIndexAtDepth(depth), + InlineInfo inline_info = GetInlineInfoAtDepth(stack_map, depth); + return DecodeDexRegisterMap(inline_info.GetDexRegisterMaskIndex(), + inline_info.GetDexRegisterMapIndex(), num_dex_registers); } @@ -428,10 +422,19 @@ class CodeInfo { return InlineInfo(&inline_infos_, index); } - InlineInfo GetInlineInfoOf(StackMap stack_map) const { - DCHECK(stack_map.HasInlineInfo()); + uint32_t GetInlineDepthOf(StackMap stack_map) const { + uint32_t depth = 0; uint32_t index = stack_map.GetInlineInfoIndex(); - return GetInlineInfo(index); + if (index != StackMap::kNoValue) { + while (GetInlineInfo(index + depth++).GetIsLast() == InlineInfo::kMore) { } + } + return depth; + } + + InlineInfo GetInlineInfoAtDepth(StackMap stack_map, uint32_t depth) const { + DCHECK(stack_map.HasInlineInfo()); + DCHECK_LT(depth, GetInlineDepthOf(stack_map)); + return GetInlineInfo(stack_map.GetInlineInfoIndex() + depth); } StackMap GetStackMapForDexPc(uint32_t dex_pc) const { @@ -473,7 +476,6 @@ class CodeInfo { stack_map.GetNativePcOffset(kRuntimeISA)) { DCHECK_EQ(other.GetDexRegisterMapIndex(), stack_map.GetDexRegisterMapIndex()); - DCHECK(!stack_map.HasInlineInfo()); if (i < e - 2) { // Make sure there are not three identical stack maps following each other. DCHECK_NE( -- GitLab From 5af3f503f3ad5a24dc1acac3070f7dcda4b6593d Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 4 Jun 2018 16:17:56 -0700 Subject: [PATCH 534/749] Make adbconnection start automatically for debuggable apps (on target) With wrap.sh it is required that apps that wish to be debuggable declare that explicitly by adding the '-Xcompiler-option --debuggable' options. When we switched to the new debugger implementation we accidentally added a requirement that you pass -XjdwpProvider:adbconnection. This removes that requirement and returns to the earlier state. Bug: 109505014 Test: Build program with wrap.sh that adds -Xcompiler-option --debuggable Attach debugger to program. Change-Id: I9416ed3b156776439f5bfec032fee3f11fe4eb3c --- cmdline/cmdline_parser_test.cc | 2 +- runtime/jdwp_provider.h | 23 ++++++++++++++++++++++- runtime/runtime.cc | 8 +++++++- runtime/runtime_options.def | 2 +- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index 235a2aa90e..a52e16328a 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -371,7 +371,7 @@ TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) { */ TEST_F(CmdlineParserTest, TestJdwpProviderEmpty) { { - EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kNone, "", M::JdwpProvider); + EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kUnset, "", M::JdwpProvider); } } // TEST_F diff --git a/runtime/jdwp_provider.h b/runtime/jdwp_provider.h index 698fdc086d..c4f19899c9 100644 --- a/runtime/jdwp_provider.h +++ b/runtime/jdwp_provider.h @@ -19,6 +19,7 @@ #include +#include "base/globals.h" #include "base/macros.h" #include "base/logging.h" @@ -26,13 +27,33 @@ namespace art { enum class JdwpProvider { kNone, + // Special value only used to denote that no explicit choice has been made by the user. This + // should not be used and one should always call CanonicalizeJdwpProvider which will remove this + // value before using a JdwpProvider value. + kUnset, kInternal, kAdbConnection, - // The current default provider + // The current default provider. Used if you run -XjdwpProvider:default kDefaultJdwpProvider = kAdbConnection, + + // What we should use as provider with no options and debuggable. On host we always want to be + // none since there is no adbd on host. + kUnsetDebuggable = kIsTargetBuild ? kDefaultJdwpProvider : kNone, + // What we should use as provider with no options and non-debuggable + kUnsetNonDebuggable = kNone, }; +inline JdwpProvider CanonicalizeJdwpProvider(JdwpProvider p, bool debuggable) { + if (p != JdwpProvider::kUnset) { + return p; + } + if (debuggable) { + return JdwpProvider::kUnsetDebuggable; + } + return JdwpProvider::kUnsetNonDebuggable; +} + std::ostream& operator<<(std::ostream& os, const JdwpProvider& rhs); } // namespace art diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 14027493d8..64aab01176 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1270,7 +1270,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { dump_gc_performance_on_shutdown_ = runtime_options.Exists(Opt::DumpGCPerformanceOnShutdown); jdwp_options_ = runtime_options.GetOrDefault(Opt::JdwpOptions); - jdwp_provider_ = runtime_options.GetOrDefault(Opt::JdwpProvider); + jdwp_provider_ = CanonicalizeJdwpProvider(runtime_options.GetOrDefault(Opt::JdwpProvider), + IsJavaDebuggable()); switch (jdwp_provider_) { case JdwpProvider::kNone: { VLOG(jdwp) << "Disabling all JDWP support."; @@ -1304,6 +1305,11 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { constexpr const char* plugin_name = kIsDebugBuild ? "libadbconnectiond.so" : "libadbconnection.so"; plugins_.push_back(Plugin::Create(plugin_name)); + break; + } + case JdwpProvider::kUnset: { + LOG(FATAL) << "Illegal jdwp provider " << jdwp_provider_ << " was not filtered out!"; + break; } } callbacks_->AddThreadLifecycleCallback(Dbg::GetThreadLifecycleCallback()); diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index e647423b9c..3f9a3229ca 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -44,7 +44,7 @@ RUNTIME_OPTIONS_KEY (std::string, Image) RUNTIME_OPTIONS_KEY (Unit, CheckJni) RUNTIME_OPTIONS_KEY (Unit, JniOptsForceCopy) RUNTIME_OPTIONS_KEY (std::string, JdwpOptions, "") -RUNTIME_OPTIONS_KEY (JdwpProvider, JdwpProvider, JdwpProvider::kNone) +RUNTIME_OPTIONS_KEY (JdwpProvider, JdwpProvider, JdwpProvider::kUnset) RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryMaximumSize, gc::Heap::kDefaultMaximumSize) // -Xmx RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryInitialSize, gc::Heap::kDefaultInitialSize) // -Xms RUNTIME_OPTIONS_KEY (MemoryKiB, HeapGrowthLimit) // Default is 0 for unlimited -- GitLab From 2b69b9c65730638dd67d4376a7aee05a37e41198 Mon Sep 17 00:00:00 2001 From: Ivan Maidanski Date: Mon, 14 May 2018 13:50:48 +0300 Subject: [PATCH 535/749] Fix deadlock between ConcurrentCopying and thread holding dex_lock exclusively Similar to the issue addressed by https://android-review.googlesource.com/628465, we observe a deadlock between - a thread executing ConcurrentCopying::RevokeThreadLocalMarkStacks which has disabled weak references access and waiting for check_point barrier; - a thread which had acquired the mutator lock exclusively and was blocking on a condition variable regarding weak references access; and - a thread which cannot execute the checkpoint until it acquires the dex_lock. This change prevents garbage collection from occurring when dex_lock is held exclusively. Bug: 109720594 Test: m test-art-host Change-Id: I7a7e8e20aada4a9fad344b2f2f45a233639cda14 --- runtime/class_linker.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index b88aa5e07a..63366671fd 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3505,6 +3505,11 @@ void ClassLinker::RegisterExistingDexCache(ObjPtr dex_cache, WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); table = InsertClassTableForClassLoader(h_class_loader.Get()); } + // Avoid a deadlock between a garbage collecting thread running a checkpoint, + // a thread holding the dex lock and blocking on a condition variable regarding + // weak references access, and a thread blocking on the dex lock. + ScopedThreadStateChange tsc(self, kSuspended); + gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseClassLinker, gc::kCollectorTypeClassLinker); WriterMutexLock mu(self, *Locks::dex_lock_); RegisterDexFileLocked(*dex_file, h_dex_cache.Get(), h_class_loader.Get()); table->InsertStrongRoot(h_dex_cache.Get()); @@ -3545,6 +3550,11 @@ ObjPtr ClassLinker::RegisterDexFile(const DexFile& dex_file, dex_file))); Handle h_location(hs.NewHandle(location)); { + // Avoid a deadlock between a garbage collecting thread running a checkpoint, + // a thread holding the dex lock and blocking on a condition variable regarding + // weak references access, and a thread blocking on the dex lock. + ScopedThreadStateChange tsc(self, kSuspended); + gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseClassLinker, gc::kCollectorTypeClassLinker); WriterMutexLock mu(self, *Locks::dex_lock_); old_data = FindDexCacheDataLocked(dex_file); old_dex_cache = DecodeDexCache(self, old_data); -- GitLab From ed4ee4474b0f34d70ebb362eaa4be4ae9bca7f8e Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 5 Jun 2018 14:23:35 -0700 Subject: [PATCH 536/749] Remove unnecessary thread suspension Removed unnecessary thread suspension since it's already done by ScopedGCCriticalSection. This also fixes the access of handles without holding the mutator lock. Added scoped traces to RegisterDexFile to help detect any delays caused by blocking on GC from the ScopedGCCriticalSection. Bug: 109720594 Test: test-art-host Change-Id: I0bc7402eca7c8bff92d3522ccd30ca802bca2863 --- runtime/class_linker.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 0b955cadbf..be636d80a8 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3360,6 +3360,7 @@ ObjPtr ClassLinker::EnsureSameClassLoader( void ClassLinker::RegisterExistingDexCache(ObjPtr dex_cache, ObjPtr class_loader) { + SCOPED_TRACE << __FUNCTION__ << " " << dex_cache->GetDexFile()->GetLocation(); Thread* self = Thread::Current(); StackHandleScope<2> hs(self); Handle h_dex_cache(hs.NewHandle(dex_cache)); @@ -3384,7 +3385,6 @@ void ClassLinker::RegisterExistingDexCache(ObjPtr dex_cache, // Avoid a deadlock between a garbage collecting thread running a checkpoint, // a thread holding the dex lock and blocking on a condition variable regarding // weak references access, and a thread blocking on the dex lock. - ScopedThreadStateChange tsc(self, kSuspended); gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseClassLinker, gc::kCollectorTypeClassLinker); WriterMutexLock mu(self, *Locks::dex_lock_); RegisterDexFileLocked(*dex_file, h_dex_cache.Get(), h_class_loader.Get()); @@ -3408,6 +3408,7 @@ ObjPtr ClassLinker::RegisterDexFile(const DexFile& dex_file, if (old_dex_cache != nullptr) { return EnsureSameClassLoader(self, old_dex_cache, old_data, class_loader); } + SCOPED_TRACE << __FUNCTION__ << " " << dex_file.GetLocation(); LinearAlloc* const linear_alloc = GetOrCreateAllocatorForClassLoader(class_loader); DCHECK(linear_alloc != nullptr); ClassTable* table; @@ -3429,7 +3430,6 @@ ObjPtr ClassLinker::RegisterDexFile(const DexFile& dex_file, // Avoid a deadlock between a garbage collecting thread running a checkpoint, // a thread holding the dex lock and blocking on a condition variable regarding // weak references access, and a thread blocking on the dex lock. - ScopedThreadStateChange tsc(self, kSuspended); gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseClassLinker, gc::kCollectorTypeClassLinker); WriterMutexLock mu(self, *Locks::dex_lock_); old_data = FindDexCacheDataLocked(dex_file); -- GitLab From 3362d224c7f8db721d9fa60f8002cb221638a224 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 5 Jun 2018 16:02:10 -0700 Subject: [PATCH 537/749] ART: Run ExceptionDescribe on error When a classloader cannot be printed, print the exception. Bug: 109724219 Test: m test-art-host Change-Id: I32fbd241cb247a0e4cbe8f13a6120a8aa736995a --- runtime/jni/java_vm_ext.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/runtime/jni/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc index 8fe68bd318..44679a5afa 100644 --- a/runtime/jni/java_vm_ext.cc +++ b/runtime/jni/java_vm_ext.cc @@ -912,7 +912,11 @@ bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, return utf.c_str(); } } - env->ExceptionClear(); + if (env->ExceptionCheck()) { + // We can't do much better logging, really. So leave it with a Describe. + env->ExceptionDescribe(); + env->ExceptionClear(); + } return "(Error calling toString)"; } return "null"; -- GitLab From b2926c0cdbd30af7529b90fe20b24010f844ccc1 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 6 Jun 2018 05:49:44 +0000 Subject: [PATCH 538/749] Revert "Make adbconnection start automatically for debuggable apps (on target)" This reverts commit 5af3f503f3ad5a24dc1acac3070f7dcda4b6593d. Reason for revert: Breaks target tests Bug: 109505014 Change-Id: I43877a11c615637a152954e0c0d0b2b0f18bcc9e Test: none --- cmdline/cmdline_parser_test.cc | 2 +- runtime/jdwp_provider.h | 23 +---------------------- runtime/runtime.cc | 8 +------- runtime/runtime_options.def | 2 +- 4 files changed, 4 insertions(+), 31 deletions(-) diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index a52e16328a..235a2aa90e 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -371,7 +371,7 @@ TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) { */ TEST_F(CmdlineParserTest, TestJdwpProviderEmpty) { { - EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kUnset, "", M::JdwpProvider); + EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kNone, "", M::JdwpProvider); } } // TEST_F diff --git a/runtime/jdwp_provider.h b/runtime/jdwp_provider.h index c4f19899c9..698fdc086d 100644 --- a/runtime/jdwp_provider.h +++ b/runtime/jdwp_provider.h @@ -19,7 +19,6 @@ #include -#include "base/globals.h" #include "base/macros.h" #include "base/logging.h" @@ -27,33 +26,13 @@ namespace art { enum class JdwpProvider { kNone, - // Special value only used to denote that no explicit choice has been made by the user. This - // should not be used and one should always call CanonicalizeJdwpProvider which will remove this - // value before using a JdwpProvider value. - kUnset, kInternal, kAdbConnection, - // The current default provider. Used if you run -XjdwpProvider:default + // The current default provider kDefaultJdwpProvider = kAdbConnection, - - // What we should use as provider with no options and debuggable. On host we always want to be - // none since there is no adbd on host. - kUnsetDebuggable = kIsTargetBuild ? kDefaultJdwpProvider : kNone, - // What we should use as provider with no options and non-debuggable - kUnsetNonDebuggable = kNone, }; -inline JdwpProvider CanonicalizeJdwpProvider(JdwpProvider p, bool debuggable) { - if (p != JdwpProvider::kUnset) { - return p; - } - if (debuggable) { - return JdwpProvider::kUnsetDebuggable; - } - return JdwpProvider::kUnsetNonDebuggable; -} - std::ostream& operator<<(std::ostream& os, const JdwpProvider& rhs); } // namespace art diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 64aab01176..14027493d8 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1270,8 +1270,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { dump_gc_performance_on_shutdown_ = runtime_options.Exists(Opt::DumpGCPerformanceOnShutdown); jdwp_options_ = runtime_options.GetOrDefault(Opt::JdwpOptions); - jdwp_provider_ = CanonicalizeJdwpProvider(runtime_options.GetOrDefault(Opt::JdwpProvider), - IsJavaDebuggable()); + jdwp_provider_ = runtime_options.GetOrDefault(Opt::JdwpProvider); switch (jdwp_provider_) { case JdwpProvider::kNone: { VLOG(jdwp) << "Disabling all JDWP support."; @@ -1305,11 +1304,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { constexpr const char* plugin_name = kIsDebugBuild ? "libadbconnectiond.so" : "libadbconnection.so"; plugins_.push_back(Plugin::Create(plugin_name)); - break; - } - case JdwpProvider::kUnset: { - LOG(FATAL) << "Illegal jdwp provider " << jdwp_provider_ << " was not filtered out!"; - break; } } callbacks_->AddThreadLifecycleCallback(Dbg::GetThreadLifecycleCallback()); diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 3f9a3229ca..e647423b9c 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -44,7 +44,7 @@ RUNTIME_OPTIONS_KEY (std::string, Image) RUNTIME_OPTIONS_KEY (Unit, CheckJni) RUNTIME_OPTIONS_KEY (Unit, JniOptsForceCopy) RUNTIME_OPTIONS_KEY (std::string, JdwpOptions, "") -RUNTIME_OPTIONS_KEY (JdwpProvider, JdwpProvider, JdwpProvider::kUnset) +RUNTIME_OPTIONS_KEY (JdwpProvider, JdwpProvider, JdwpProvider::kNone) RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryMaximumSize, gc::Heap::kDefaultMaximumSize) // -Xmx RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryInitialSize, gc::Heap::kDefaultInitialSize) // -Xms RUNTIME_OPTIONS_KEY (MemoryKiB, HeapGrowthLimit) // Default is 0 for unlimited -- GitLab From b16e4a3c7563f83df0d6fd10c0aa4f1d36be8079 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Tue, 5 Jun 2018 13:20:48 +0100 Subject: [PATCH 539/749] Add check to CheckImageIdenticalToOriginalExceptForRelocation Add defensive check which fails gracefully if the relocation offset is larger than the image size. Add tests. Bug: 109677607 Test: test-art-host-gtest-patchoat_test SANITIZE_HOST=address Change-Id: Ic989d5b7c77fd66e77b9e8ba90df1bf490a46e43 --- patchoat/patchoat.cc | 4 + patchoat/patchoat_test.cc | 229 ++++++++++++++++++++++++-------------- 2 files changed, 150 insertions(+), 83 deletions(-) diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index a6d3903f19..73627b2302 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -363,6 +363,10 @@ static bool CheckImageIdenticalToOriginalExceptForRelocation( uint32_t offset_delta = 0; if (DecodeUnsignedLeb128Checked(&rel_ptr, rel_end, &offset_delta)) { offset += offset_delta; + if (static_cast(offset) + static_cast(sizeof(uint32_t)) > image_size) { + *error_msg = StringPrintf("Relocation out of bounds in %s", relocated_filename.c_str()); + return false; + } uint32_t *image_value = reinterpret_cast(image_start + offset); *image_value -= expected_diff; } else { diff --git a/patchoat/patchoat_test.cc b/patchoat/patchoat_test.cc index 69728ae051..934936d4b3 100644 --- a/patchoat/patchoat_test.cc +++ b/patchoat/patchoat_test.cc @@ -445,103 +445,166 @@ TEST_F(PatchoatTest, PatchoatRelocationSameAsDex2oatRelocation) { #endif } -TEST_F(PatchoatTest, RelFileVerification) { - // This test checks that a boot image relocated using patchoat can be unrelocated using the .rel - // file created by patchoat. +class PatchoatVerificationTest : public PatchoatTest { + protected: + virtual void SetUp() { + PatchoatTest::SetUp(); + + // This test checks that a boot image relocated using patchoat can be unrelocated using the .rel + // file created by patchoat. + + // This test doesn't work when heap poisoning is enabled because some of the + // references are negated. b/72117833 is tracking the effort to have patchoat + // and its tests support heap poisoning. + TEST_DISABLED_FOR_HEAP_POISONING(); + + // Compile boot image into a random directory using dex2oat + ScratchFile dex2oat_orig_scratch; + dex2oat_orig_scratch.Unlink(); + dex2oat_orig_dir_ = dex2oat_orig_scratch.GetFilename(); + ASSERT_EQ(0, mkdir(dex2oat_orig_dir_.c_str(), 0700)); + const uint32_t orig_base_addr = 0x60000000; + std::vector dex2oat_extra_args; + std::string error_msg; + if (!CompileBootImageToDir(dex2oat_orig_dir_, dex2oat_extra_args, orig_base_addr, &error_msg)) { + FAIL() << "CompileBootImage1 failed: " << error_msg; + } - // This test doesn't work when heap poisoning is enabled because some of the - // references are negated. b/72117833 is tracking the effort to have patchoat - // and its tests support heap poisoning. - TEST_DISABLED_FOR_HEAP_POISONING(); + // Generate image relocation file for the original boot image + std::string dex2oat_orig_with_arch_dir = + dex2oat_orig_dir_ + "/" + GetInstructionSetString(kRuntimeISA); + // The arch-including symlink is needed by patchoat + ASSERT_EQ(0, symlink(dex2oat_orig_dir_.c_str(), dex2oat_orig_with_arch_dir.c_str())); + base_addr_delta_ = 0x100000; + if (!GenerateBootImageRelFile( + dex2oat_orig_dir_ + "/boot.art", + dex2oat_orig_dir_, + base_addr_delta_, + &error_msg)) { + FAIL() << "RelocateBootImage failed: " << error_msg; + } - // Compile boot image into a random directory using dex2oat - ScratchFile dex2oat_orig_scratch; - dex2oat_orig_scratch.Unlink(); - std::string dex2oat_orig_dir = dex2oat_orig_scratch.GetFilename(); - ASSERT_EQ(0, mkdir(dex2oat_orig_dir.c_str(), 0700)); - const uint32_t orig_base_addr = 0x60000000; - std::vector dex2oat_extra_args; - std::string error_msg; - if (!CompileBootImageToDir(dex2oat_orig_dir, dex2oat_extra_args, orig_base_addr, &error_msg)) { - FAIL() << "CompileBootImage1 failed: " << error_msg; - } + // Relocate the original boot image using patchoat + ScratchFile relocated_scratch; + relocated_scratch.Unlink(); + relocated_dir_ = relocated_scratch.GetFilename(); + ASSERT_EQ(0, mkdir(relocated_dir_.c_str(), 0700)); + // Use a different relocation delta from the one used when generating .rel files above. This is + // to make sure .rel files are not specific to a particular relocation delta. + base_addr_delta_ -= 0x10000; + if (!RelocateBootImage( + dex2oat_orig_dir_ + "/boot.art", + relocated_dir_, + base_addr_delta_, + &error_msg)) { + FAIL() << "RelocateBootImage failed: " << error_msg; + } - // Generate image relocation file for the original boot image - std::string dex2oat_orig_with_arch_dir = - dex2oat_orig_dir + "/" + GetInstructionSetString(kRuntimeISA); - // The arch-including symlink is needed by patchoat - ASSERT_EQ(0, symlink(dex2oat_orig_dir.c_str(), dex2oat_orig_with_arch_dir.c_str())); - off_t base_addr_delta = 0x100000; - if (!GenerateBootImageRelFile( - dex2oat_orig_dir + "/boot.art", - dex2oat_orig_dir, - base_addr_delta, - &error_msg)) { - FAIL() << "RelocateBootImage failed: " << error_msg; + // Assert that patchoat created the same set of .art and .art.rel files + std::vector rel_basenames; + std::vector relocated_image_basenames; + if (!ListDirFilesEndingWith(dex2oat_orig_dir_, ".rel", &rel_basenames, &error_msg)) { + FAIL() << "Failed to list *.art.rel files in " << dex2oat_orig_dir_ << ": " << error_msg; + } + if (!ListDirFilesEndingWith(relocated_dir_, ".art", &relocated_image_basenames, &error_msg)) { + FAIL() << "Failed to list *.art files in " << relocated_dir_ << ": " << error_msg; + } + std::sort(rel_basenames.begin(), rel_basenames.end()); + std::sort(relocated_image_basenames.begin(), relocated_image_basenames.end()); + + // .art and .art.rel file names output by patchoat look like + // tmp@art-data--@boot*.art, encoding the name of the directory in their name. + // To compare these with each other, we retain only the part of the file name after the last @, + // and we also drop the extension. + std::vector rel_shortened_basenames(rel_basenames.size()); + std::vector relocated_image_shortened_basenames(relocated_image_basenames.size()); + for (size_t i = 0; i < rel_basenames.size(); i++) { + rel_shortened_basenames[i] = rel_basenames[i].substr(rel_basenames[i].find_last_of("@") + 1); + rel_shortened_basenames[i] = + rel_shortened_basenames[i].substr(0, rel_shortened_basenames[i].find(".")); + } + for (size_t i = 0; i < relocated_image_basenames.size(); i++) { + relocated_image_shortened_basenames[i] = + relocated_image_basenames[i].substr(relocated_image_basenames[i].find_last_of("@") + 1); + relocated_image_shortened_basenames[i] = + relocated_image_shortened_basenames[i].substr( + 0, relocated_image_shortened_basenames[i].find(".")); + } + ASSERT_EQ(rel_shortened_basenames, relocated_image_shortened_basenames); } - // Relocate the original boot image using patchoat - ScratchFile relocated_scratch; - relocated_scratch.Unlink(); - std::string relocated_dir = relocated_scratch.GetFilename(); - ASSERT_EQ(0, mkdir(relocated_dir.c_str(), 0700)); - // Use a different relocation delta from the one used when generating .rel files above. This is - // to make sure .rel files are not specific to a particular relocation delta. - base_addr_delta -= 0x10000; - if (!RelocateBootImage( - dex2oat_orig_dir + "/boot.art", - relocated_dir, - base_addr_delta, - &error_msg)) { - FAIL() << "RelocateBootImage failed: " << error_msg; - } + virtual void TearDown() { + ClearDirectory(dex2oat_orig_dir_.c_str(), /*recursive*/ true); + ClearDirectory(relocated_dir_.c_str(), /*recursive*/ true); - // Assert that patchoat created the same set of .art and .art.rel files - std::vector rel_basenames; - std::vector relocated_image_basenames; - if (!ListDirFilesEndingWith(dex2oat_orig_dir, ".rel", &rel_basenames, &error_msg)) { - FAIL() << "Failed to list *.art.rel files in " << dex2oat_orig_dir << ": " << error_msg; - } - if (!ListDirFilesEndingWith(relocated_dir, ".art", &relocated_image_basenames, &error_msg)) { - FAIL() << "Failed to list *.art files in " << relocated_dir << ": " << error_msg; - } - std::sort(rel_basenames.begin(), rel_basenames.end()); - std::sort(relocated_image_basenames.begin(), relocated_image_basenames.end()); - - // .art and .art.rel file names output by patchoat look like - // tmp@art-data--@boot*.art, encoding the name of the directory in their name. - // To compare these with each other, we retain only the part of the file name after the last @, - // and we also drop the extension. - std::vector rel_shortened_basenames(rel_basenames.size()); - std::vector relocated_image_shortened_basenames(relocated_image_basenames.size()); - for (size_t i = 0; i < rel_basenames.size(); i++) { - rel_shortened_basenames[i] = rel_basenames[i].substr(rel_basenames[i].find_last_of("@") + 1); - rel_shortened_basenames[i] = - rel_shortened_basenames[i].substr(0, rel_shortened_basenames[i].find(".")); - } - for (size_t i = 0; i < relocated_image_basenames.size(); i++) { - relocated_image_shortened_basenames[i] = - relocated_image_basenames[i].substr(relocated_image_basenames[i].find_last_of("@") + 1); - relocated_image_shortened_basenames[i] = - relocated_image_shortened_basenames[i].substr( - 0, relocated_image_shortened_basenames[i].find(".")); + rmdir(dex2oat_orig_dir_.c_str()); + rmdir(relocated_dir_.c_str()); + + PatchoatTest::TearDown(); } - ASSERT_EQ(rel_shortened_basenames, relocated_image_shortened_basenames); - // Assert that verification works with the .rel files. + std::string dex2oat_orig_dir_; + std::string relocated_dir_; + off_t base_addr_delta_; +}; + +// Assert that verification works with the .rel files. +TEST_F(PatchoatVerificationTest, Sucessful) { + std::string error_msg; if (!VerifyBootImage( - dex2oat_orig_dir + "/boot.art", - relocated_dir, - base_addr_delta, + dex2oat_orig_dir_ + "/boot.art", + relocated_dir_, + base_addr_delta_, &error_msg)) { FAIL() << "VerifyBootImage failed: " << error_msg; } +} - ClearDirectory(dex2oat_orig_dir.c_str(), /*recursive*/ true); - ClearDirectory(relocated_dir.c_str(), /*recursive*/ true); +// Corrupt the image file and check that the verification fails gracefully. +TEST_F(PatchoatVerificationTest, CorruptedImage) { + std::string error_msg; + std::string relocated_image_filename; + if (!GetDalvikCacheFilename((dex2oat_orig_dir_ + "/boot.art").c_str(), + relocated_dir_.c_str(), + &relocated_image_filename, + &error_msg)) { + FAIL() << "Failed to find relocated image file name: " << error_msg; + } + ASSERT_EQ(truncate(relocated_image_filename.c_str(), sizeof(ImageHeader)), 0) + << relocated_image_filename; - rmdir(dex2oat_orig_dir.c_str()); - rmdir(relocated_dir.c_str()); + if (VerifyBootImage( + dex2oat_orig_dir_ + "/boot.art", + relocated_dir_, + base_addr_delta_, + &error_msg)) { + FAIL() << "VerifyBootImage should have failed since the image was intentionally corrupted"; + } +} + +// Corrupt the relocation file and check that the verification fails gracefully. +TEST_F(PatchoatVerificationTest, CorruptedRelFile) { + std::string error_msg; + std::string art_filename = dex2oat_orig_dir_ + "/boot.art"; + std::string rel_filename = dex2oat_orig_dir_ + "/boot.art.rel"; + std::unique_ptr art_file(OS::OpenFileForReading(art_filename.c_str())); + std::unique_ptr rel_file(OS::OpenFileReadWrite(rel_filename.c_str())); + rel_file->ClearContent(); + uint8_t buffer[64] = {}; + ASSERT_TRUE(rel_file->WriteFully(&buffer, SHA256_DIGEST_LENGTH)); + // Encode single relocation which is just past the end of the image file. + size_t leb_size = EncodeUnsignedLeb128(buffer, art_file->GetLength()) - buffer; + ASSERT_TRUE(rel_file->WriteFully(&buffer, leb_size)); + ASSERT_EQ(rel_file->FlushClose(), 0); + ASSERT_EQ(art_file->Close(), 0); + + if (VerifyBootImage( + dex2oat_orig_dir_ + "/boot.art", + relocated_dir_, + base_addr_delta_, + &error_msg)) { + FAIL() << "VerifyBootImage should have failed since the rel file was intentionally corrupted"; + } } } // namespace art -- GitLab From d97e0828958fd14a962e37af2865bc2e628547ce Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Sun, 3 Jun 2018 12:00:24 +0100 Subject: [PATCH 540/749] Reduce code repetition in stack maps with macros. Simplifies the code somewhat. It also makes it possible to get column names as strings for the debugging code. Test: test-art-host-gtest-stack_map_test Change-Id: I1a2e146e7a4372c0752693313e1b881cb4a818bc --- compiler/optimizing/stack_map_stream.cc | 4 +- libartbase/base/bit_table.h | 56 +++++---- oatdump/oatdump.cc | 2 +- runtime/stack_map.h | 151 ++++++++---------------- 4 files changed, 83 insertions(+), 130 deletions(-) diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index d80e2fc0e3..3685ab2df4 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -129,7 +129,7 @@ void StackMapStream::AddInvoke(InvokeType invoke_type, uint32_t dex_method_index CHECK_EQ(invoke_info.GetNativePcOffset(instruction_set_), StackMap::UnpackNativePc(packed_native_pc, instruction_set_)); CHECK_EQ(invoke_info.GetInvokeType(), invoke_type); - CHECK_EQ(method_infos_[invoke_info.GetMethodIndexIdx()], dex_method_index); + CHECK_EQ(method_infos_[invoke_info.GetMethodInfoIndex()], dex_method_index); }); } } @@ -179,7 +179,7 @@ void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, if (encode_art_method) { CHECK_EQ(inline_info.GetArtMethod(), method); } else { - CHECK_EQ(method_infos_[inline_info.GetMethodIndexIdx()], + CHECK_EQ(method_infos_[inline_info.GetMethodInfoIndex()], method->GetDexMethodIndexUnchecked()); } CHECK_EQ(inline_info.HasDexRegisterMap(), (num_dex_registers != 0)); diff --git a/libartbase/base/bit_table.h b/libartbase/base/bit_table.h index bf3d3b032c..0ae60b9070 100644 --- a/libartbase/base/bit_table.h +++ b/libartbase/base/bit_table.h @@ -68,8 +68,10 @@ class BitTable { public: class Accessor { public: + static constexpr uint32_t kCount = kNumColumns; static constexpr uint32_t kNoValue = std::numeric_limits::max(); + Accessor() {} Accessor(const BitTable* table, uint32_t row) : table_(table), row_(row) {} ALWAYS_INLINE uint32_t Row() const { return row_; } @@ -86,14 +88,27 @@ class BitTable { return this->table_ == other.table_ && this->row_ == other.row_; } - Accessor& operator++() { - row_++; - return *this; - } +// Helper macro to create constructors and per-table utilities in derived class. +#define BIT_TABLE_HEADER() \ + using BitTable::Accessor::Accessor; /* inherit the constructors */ \ + template struct ColumnName; \ + +// Helper macro to create named column accessors in derived class. +#define BIT_TABLE_COLUMN(COLUMN, NAME) \ + static constexpr uint32_t k##NAME = COLUMN; \ + ALWAYS_INLINE uint32_t Get##NAME() const { \ + return table_->Get(row_, COLUMN); \ + } \ + ALWAYS_INLINE bool Has##NAME() const { \ + return table_->Get(row_, COLUMN) != kNoValue; \ + } \ + template struct ColumnName { \ + static constexpr const char* Value = #NAME; \ + }; \ protected: - const BitTable* table_; - uint32_t row_; + const BitTable* table_ = nullptr; + uint32_t row_ = -1; }; static constexpr uint32_t kValueBias = -1; @@ -152,11 +167,17 @@ class BitTable { uint16_t column_offset_[kNumColumns + 1] = {}; }; -template -constexpr uint32_t BitTable::Accessor::kNoValue; +// Template meta-programming helper. +template +static const char** GetBitTableColumnNamesImpl(std::index_sequence) { + static const char* names[] = { Accessor::template ColumnName::Value... }; + return names; +} -template -constexpr uint32_t BitTable::kValueBias; +template +static const char** GetBitTableColumnNames() { + return GetBitTableColumnNamesImpl(std::make_index_sequence()); +} // Helper class for encoding BitTable. It can optionally de-duplicate the inputs. // Type 'T' must be POD type consisting of uint32_t fields (one for each column). @@ -209,18 +230,6 @@ class BitTableBuilder { return index; } - // Check if the table already contains given values starting at the given index. - bool RangeEquals(uint32_t index, T* values, size_t count = 1) { - DCHECK_LE(index, size()); - DCHECK_LE(count, size() - index); - for (uint32_t i = 0; i < count; i++) { - if (memcmp(&values[i], &rows_[index + i], sizeof(T)) != 0) { - return false; - } - } - return true; - } - ALWAYS_INLINE uint32_t Get(uint32_t row, uint32_t column) const { DCHECK_LT(row, size()); DCHECK_LT(column, kNumColumns); @@ -290,9 +299,6 @@ class BitTableBuilder { ScopedArenaUnorderedMultimap dedup_; // Hash -> row index. }; -template -constexpr size_t BitTableBuilder::kNumColumns; - // Helper class for encoding single-column BitTable of bitmaps (allows more than 32 bits). class BitmapTableBuilder { public: diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 7ac9e984ff..3188087cfb 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1748,7 +1748,7 @@ class OatDumper { if (num_inline_infos > 0u) { stats_.AddBits( Stats::kByteKindInlineInfoMethodIndexIdx, - inline_infos.NumColumnBits(InlineInfo::kMethodIndexIdx) * num_inline_infos); + inline_infos.NumColumnBits(InlineInfo::kMethodInfoIndex) * num_inline_infos); stats_.AddBits( Stats::kByteKindInlineInfoDexPc, inline_infos.NumColumnBits(InlineInfo::kDexPc) * num_inline_infos); diff --git a/runtime/stack_map.h b/runtime/stack_map.h index b04197e160..6da002138c 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -147,38 +147,26 @@ class DexRegisterMap { */ class StackMap : public BitTable<7>::Accessor { public: - enum Field { - kPackedNativePc, - kDexPc, - kRegisterMaskIndex, - kStackMaskIndex, - kInlineInfoIndex, - kDexRegisterMaskIndex, - kDexRegisterMapIndex, - kCount, - }; - - StackMap() : BitTable::Accessor(nullptr, -1) {} - StackMap(const BitTable* table, uint32_t row) - : BitTable::Accessor(table, row) {} + BIT_TABLE_HEADER() + BIT_TABLE_COLUMN(0, PackedNativePc) + BIT_TABLE_COLUMN(1, DexPc) + BIT_TABLE_COLUMN(2, RegisterMaskIndex) + BIT_TABLE_COLUMN(3, StackMaskIndex) + BIT_TABLE_COLUMN(4, InlineInfoIndex) + BIT_TABLE_COLUMN(5, DexRegisterMaskIndex) + BIT_TABLE_COLUMN(6, DexRegisterMapIndex) ALWAYS_INLINE uint32_t GetNativePcOffset(InstructionSet instruction_set) const { return UnpackNativePc(Get(), instruction_set); } - uint32_t GetDexPc() const { return Get(); } - - uint32_t GetDexRegisterMaskIndex() const { return Get(); } - - uint32_t GetDexRegisterMapIndex() const { return Get(); } - bool HasDexRegisterMap() const { return GetDexRegisterMapIndex() != kNoValue; } - - uint32_t GetInlineInfoIndex() const { return Get(); } - bool HasInlineInfo() const { return GetInlineInfoIndex() != kNoValue; } - - uint32_t GetRegisterMaskIndex() const { return Get(); } + ALWAYS_INLINE bool HasInlineInfo() const { + return HasInlineInfoIndex(); + } - uint32_t GetStackMaskIndex() const { return Get(); } + ALWAYS_INLINE bool HasDexRegisterMap() const { + return HasDexRegisterMapIndex(); + } static uint32_t PackNativePc(uint32_t native_pc, InstructionSet isa) { DCHECK_ALIGNED_PARAM(native_pc, GetInstructionSetInstructionAlignment(isa)); @@ -206,56 +194,34 @@ class StackMap : public BitTable<7>::Accessor { */ class InlineInfo : public BitTable<7>::Accessor { public: - enum Field { - kIsLast, // Determines if there are further rows for further depths. - kDexPc, - kMethodIndexIdx, - kArtMethodHi, // High bits of ArtMethod*. - kArtMethodLo, // Low bits of ArtMethod*. - kDexRegisterMaskIndex, - kDexRegisterMapIndex, - kCount, - }; + BIT_TABLE_HEADER() + BIT_TABLE_COLUMN(0, IsLast) // Determines if there are further rows for further depths. + BIT_TABLE_COLUMN(1, DexPc) + BIT_TABLE_COLUMN(2, MethodInfoIndex) + BIT_TABLE_COLUMN(3, ArtMethodHi) // High bits of ArtMethod*. + BIT_TABLE_COLUMN(4, ArtMethodLo) // Low bits of ArtMethod*. + BIT_TABLE_COLUMN(5, DexRegisterMaskIndex) + BIT_TABLE_COLUMN(6, DexRegisterMapIndex) + static constexpr uint32_t kLast = -1; static constexpr uint32_t kMore = 0; - InlineInfo(const BitTable* table, uint32_t row) - : BitTable::Accessor(table, row) {} - - uint32_t GetIsLast() const { return Get(); } - - uint32_t GetMethodIndexIdx() const { - DCHECK(!EncodesArtMethod()); - return Get(); - } - uint32_t GetMethodIndex(const MethodInfo& method_info) const { - return method_info.GetMethodIndex(GetMethodIndexIdx()); - } - - uint32_t GetDexPc() const { - return Get(); + return method_info.GetMethodIndex(GetMethodInfoIndex()); } bool EncodesArtMethod() const { - return Get() != kNoValue; + return HasArtMethodLo(); } ArtMethod* GetArtMethod() const { - uint64_t lo = Get(); - uint64_t hi = Get(); + uint64_t lo = GetArtMethodLo(); + uint64_t hi = GetArtMethodHi(); return reinterpret_cast((hi << 32) | lo); } - uint32_t GetDexRegisterMaskIndex() const { - return Get(); - } - - uint32_t GetDexRegisterMapIndex() const { - return Get(); - } - bool HasDexRegisterMap() const { - return GetDexRegisterMapIndex() != kNoValue; + ALWAYS_INLINE bool HasDexRegisterMap() const { + return HasDexRegisterMapIndex(); } void Dump(VariableIndentationOutputStream* vios, @@ -267,43 +233,29 @@ class InlineInfo : public BitTable<7>::Accessor { class InvokeInfo : public BitTable<3>::Accessor { public: - enum Field { - kPackedNativePc, - kInvokeType, - kMethodIndexIdx, - kCount, - }; - - InvokeInfo(const BitTable* table, uint32_t row) - : BitTable::Accessor(table, row) {} + BIT_TABLE_HEADER() + BIT_TABLE_COLUMN(0, PackedNativePc) + BIT_TABLE_COLUMN(1, InvokeType) + BIT_TABLE_COLUMN(2, MethodInfoIndex) ALWAYS_INLINE uint32_t GetNativePcOffset(InstructionSet instruction_set) const { return StackMap::UnpackNativePc(Get(), instruction_set); } - uint32_t GetInvokeType() const { return Get(); } - - uint32_t GetMethodIndexIdx() const { return Get(); } - uint32_t GetMethodIndex(MethodInfo method_info) const { - return method_info.GetMethodIndex(GetMethodIndexIdx()); + return method_info.GetMethodIndex(GetMethodInfoIndex()); } }; class DexRegisterInfo : public BitTable<2>::Accessor { public: - enum Field { - kKind, - kPackedValue, - kCount, - }; - - DexRegisterInfo(const BitTable* table, uint32_t row) - : BitTable::Accessor(table, row) {} + BIT_TABLE_HEADER() + BIT_TABLE_COLUMN(0, Kind) + BIT_TABLE_COLUMN(1, PackedValue) ALWAYS_INLINE DexRegisterLocation GetLocation() const { - DexRegisterLocation::Kind kind = static_cast(Get()); - return DexRegisterLocation(kind, UnpackValue(kind, Get())); + DexRegisterLocation::Kind kind = static_cast(GetKind()); + return DexRegisterLocation(kind, UnpackValue(kind, GetPackedValue())); } static uint32_t PackValue(DexRegisterLocation::Kind kind, uint32_t value) { @@ -328,17 +280,12 @@ class DexRegisterInfo : public BitTable<2>::Accessor { // therefore it is worth encoding the mask as value+shift. class RegisterMask : public BitTable<2>::Accessor { public: - enum Field { - kValue, - kShift, - kCount, - }; - - RegisterMask(const BitTable* table, uint32_t row) - : BitTable::Accessor(table, row) {} + BIT_TABLE_HEADER() + BIT_TABLE_COLUMN(0, Value) + BIT_TABLE_COLUMN(1, Shift) ALWAYS_INLINE uint32_t GetMask() const { - return Get() << Get(); + return GetValue() << GetShift(); } }; @@ -509,7 +456,7 @@ class CodeInfo { return item; } } - return InvokeInfo(&invoke_infos_, -1); + return InvokeInfo(); } // Dump this CodeInfo object on `vios`. @@ -557,14 +504,14 @@ class CodeInfo { } size_t size_; - BitTable stack_maps_; - BitTable register_masks_; + BitTable stack_maps_; + BitTable register_masks_; BitTable<1> stack_masks_; - BitTable invoke_infos_; - BitTable inline_infos_; + BitTable invoke_infos_; + BitTable inline_infos_; BitTable<1> dex_register_masks_; BitTable<1> dex_register_maps_; - BitTable dex_register_catalog_; + BitTable dex_register_catalog_; friend class OatDumper; }; -- GitLab From 23ae5322a4e53d0fbe3ea514ec1a6941de472067 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Wed, 6 Jun 2018 16:02:21 +0100 Subject: [PATCH 541/749] Fix patchoat_test for heap poisoning The test was disabled for heap poisoning; keep it disabled. The TEST_DISABLED_FOR_HEAP_POISONING macro is in fact just a conditional return, and I didn't account for it properly. Bug: 109677607 Test: test-art-host-gtest-patchoat_test ART_HEAP_POISONING=true Change-Id: I40266d0a06102e4d8bc52357b884b0e7d9fd2630 --- patchoat/patchoat_test.cc | 41 +++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/patchoat/patchoat_test.cc b/patchoat/patchoat_test.cc index 934936d4b3..08bf31c4bd 100644 --- a/patchoat/patchoat_test.cc +++ b/patchoat/patchoat_test.cc @@ -445,19 +445,15 @@ TEST_F(PatchoatTest, PatchoatRelocationSameAsDex2oatRelocation) { #endif } +// These tests check that a boot image relocated using patchoat can be unrelocated +// using the .rel file created by patchoat. +// +// The tests don't work when heap poisoning is enabled because some of the +// references are negated. b/72117833 is tracking the effort to have patchoat +// and its tests support heap poisoning. class PatchoatVerificationTest : public PatchoatTest { protected: - virtual void SetUp() { - PatchoatTest::SetUp(); - - // This test checks that a boot image relocated using patchoat can be unrelocated using the .rel - // file created by patchoat. - - // This test doesn't work when heap poisoning is enabled because some of the - // references are negated. b/72117833 is tracking the effort to have patchoat - // and its tests support heap poisoning. - TEST_DISABLED_FOR_HEAP_POISONING(); - + void CreateRelocatedBootImage() { // Compile boot image into a random directory using dex2oat ScratchFile dex2oat_orig_scratch; dex2oat_orig_scratch.Unlink(); @@ -534,12 +530,14 @@ class PatchoatVerificationTest : public PatchoatTest { } virtual void TearDown() { - ClearDirectory(dex2oat_orig_dir_.c_str(), /*recursive*/ true); - ClearDirectory(relocated_dir_.c_str(), /*recursive*/ true); - - rmdir(dex2oat_orig_dir_.c_str()); - rmdir(relocated_dir_.c_str()); - + if (!dex2oat_orig_dir_.empty()) { + ClearDirectory(dex2oat_orig_dir_.c_str(), /*recursive*/ true); + rmdir(dex2oat_orig_dir_.c_str()); + } + if (!relocated_dir_.empty()) { + ClearDirectory(relocated_dir_.c_str(), /*recursive*/ true); + rmdir(relocated_dir_.c_str()); + } PatchoatTest::TearDown(); } @@ -550,6 +548,9 @@ class PatchoatVerificationTest : public PatchoatTest { // Assert that verification works with the .rel files. TEST_F(PatchoatVerificationTest, Sucessful) { + TEST_DISABLED_FOR_HEAP_POISONING(); + CreateRelocatedBootImage(); + std::string error_msg; if (!VerifyBootImage( dex2oat_orig_dir_ + "/boot.art", @@ -562,6 +563,9 @@ TEST_F(PatchoatVerificationTest, Sucessful) { // Corrupt the image file and check that the verification fails gracefully. TEST_F(PatchoatVerificationTest, CorruptedImage) { + TEST_DISABLED_FOR_HEAP_POISONING(); + CreateRelocatedBootImage(); + std::string error_msg; std::string relocated_image_filename; if (!GetDalvikCacheFilename((dex2oat_orig_dir_ + "/boot.art").c_str(), @@ -584,6 +588,9 @@ TEST_F(PatchoatVerificationTest, CorruptedImage) { // Corrupt the relocation file and check that the verification fails gracefully. TEST_F(PatchoatVerificationTest, CorruptedRelFile) { + TEST_DISABLED_FOR_HEAP_POISONING(); + CreateRelocatedBootImage(); + std::string error_msg; std::string art_filename = dex2oat_orig_dir_ + "/boot.art"; std::string rel_filename = dex2oat_orig_dir_ + "/boot.art.rel"; -- GitLab From 1b930cafa1c7e8f74f20d72ff1d0ff0c0fb6a875 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 6 Jun 2018 14:46:43 -0700 Subject: [PATCH 542/749] Follow rename from removing desugar support We always use d8 now, follow the rename of transform-classes-d8.jar-to-dex to transform-classes.jar-to-dex. Test: m out/host/linux-x86/obj/PACKAGING/veridex_intermediates/veridex.zip Change-Id: I84a36db0f7d04030f6f56929fccfa685519d51a7 --- tools/veridex/Android.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/veridex/Android.mk b/tools/veridex/Android.mk index f8463c1c33..2faa577262 100644 --- a/tools/veridex/Android.mk +++ b/tools/veridex/Android.mk @@ -22,13 +22,13 @@ LOCAL_PATH := $(call my-dir) system_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/core_dex_intermediates/classes.dex $(system_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000 $(system_stub_dex): $(call resolve-prebuilt-sdk-jar-path,system_current) | $(ZIP2ZIP) $(DX) - $(transform-classes-d8.jar-to-dex) + $(transform-classes.jar-to-dex) oahl_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/oahl_dex_intermediates/classes.dex $(oahl_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000 $(oahl_stub_dex): $(call get-prebuilt-sdk-dir,current)/org.apache.http.legacy.jar | $(ZIP2ZIP) $(DX) - $(transform-classes-d8.jar-to-dex) + $(transform-classes.jar-to-dex) app_compat_lists := \ $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \ -- GitLab From 6de8833fb64e59301eada4005ed04da995796170 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Sun, 3 Jun 2018 12:00:11 +0100 Subject: [PATCH 543/749] Delta-compress register maps in stack maps. The register maps tend to be similar from stack map to stack map, so instead of encoding them again, store only the modified ones. The dex register bitmap stores the delta now - if register has been modified since the previous stack map, the bit will be set. The decoding logic scans backwards through stack maps until it eventfully finds the most recent value of each register. This CL saves ~2.5% of .oat file size (~10% of stackmap size). Due to the scan, this makes dex register decoding slower by factor of 2.5, but that still beats the old algorithm before refactoring. Test: test-art-host-gtest-stack_map_test Change-Id: Id5217a329eb757954e0c9447f38b05ec34118f84 --- compiler/optimizing/stack_map_stream.cc | 92 ++++++++++++++----------- compiler/optimizing/stack_map_stream.h | 8 ++- compiler/optimizing/stack_map_test.cc | 21 +----- libartbase/base/bit_memory_region.h | 14 ++++ oatdump/oatdump.cc | 10 +-- runtime/dex_register_location.h | 6 +- runtime/stack.cc | 4 +- runtime/stack_map.cc | 65 +++++++++++++++++ runtime/stack_map.h | 85 ++++++++++++----------- 9 files changed, 198 insertions(+), 107 deletions(-) diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 3685ab2df4..094b75de69 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -46,6 +46,13 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, uint8_t inlining_depth) { DCHECK(!in_stack_map_) << "Mismatched Begin/End calls"; in_stack_map_ = true; + // num_dex_registers_ is the constant per-method number of registers. + // However we initially don't know what the value is, so lazily initialize it. + if (num_dex_registers_ == 0) { + num_dex_registers_ = num_dex_registers; + } else if (num_dex_registers > 0) { + DCHECK_EQ(num_dex_registers_, num_dex_registers) << "Inconsistent register count"; + } current_stack_map_ = StackMapEntry { .packed_native_pc = StackMap::PackNativePc(native_pc_offset, instruction_set_), @@ -85,7 +92,6 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, } CHECK_EQ(stack_map.HasInlineInfo(), (inlining_depth != 0)); CHECK_EQ(code_info.GetInlineDepthOf(stack_map), inlining_depth); - CHECK_EQ(stack_map.HasDexRegisterMap(), (num_dex_registers != 0)); }); } } @@ -102,16 +108,14 @@ void StackMapStream::EndStackMapEntry() { inline_infos_.Dedup(current_inline_infos_.data(), current_inline_infos_.size()); } + // Generate delta-compressed dex register map. + CreateDexRegisterMap(); + stack_maps_.Add(current_stack_map_); } void StackMapStream::AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value) { current_dex_registers_.push_back(DexRegisterLocation(kind, value)); - - // We have collected all the dex registers for StackMap/InlineInfo - create the map. - if (current_dex_registers_.size() == expected_num_dex_registers_) { - CreateDexRegisterMap(); - } } void StackMapStream::AddInvoke(InvokeType invoke_type, uint32_t dex_method_index) { @@ -142,14 +146,15 @@ void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, in_inline_info_ = true; DCHECK_EQ(expected_num_dex_registers_, current_dex_registers_.size()); + expected_num_dex_registers_ += num_dex_registers; + InlineInfoEntry entry = { .is_last = InlineInfo::kMore, .dex_pc = dex_pc, .method_info_index = kNoValue, .art_method_hi = kNoValue, .art_method_lo = kNoValue, - .dex_register_mask_index = kNoValue, - .dex_register_map_index = kNoValue, + .num_dex_registers = static_cast(expected_num_dex_registers_), }; if (EncodeArtMethodInInlineInfo(method)) { entry.art_method_hi = High32Bits(reinterpret_cast(method)); @@ -164,9 +169,6 @@ void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, } current_inline_infos_.push_back(entry); - current_dex_registers_.clear(); - expected_num_dex_registers_ = num_dex_registers; - if (kVerifyStackMaps) { size_t stack_map_index = stack_maps_.size(); size_t depth = current_inline_infos_.size() - 1; @@ -182,7 +184,6 @@ void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, CHECK_EQ(method_infos_[inline_info.GetMethodInfoIndex()], method->GetDexMethodIndexUnchecked()); } - CHECK_EQ(inline_info.HasDexRegisterMap(), (num_dex_registers != 0)); }); } } @@ -193,56 +194,68 @@ void StackMapStream::EndInlineInfoEntry() { DCHECK_EQ(expected_num_dex_registers_, current_dex_registers_.size()); } -// Create dex register map (bitmap + indices + catalogue entries) -// based on the currently accumulated list of DexRegisterLocations. +// Create delta-compressed dex register map based on the current list of DexRegisterLocations. +// All dex registers for a stack map are concatenated - inlined registers are just appended. void StackMapStream::CreateDexRegisterMap() { - // Create mask and map based on current registers. + // These are fields rather than local variables so that we can reuse the reserved memory. temp_dex_register_mask_.ClearAllBits(); temp_dex_register_map_.clear(); + + // Ensure that the arrays that hold previous state are big enough to be safely indexed below. + if (previous_dex_registers_.size() < current_dex_registers_.size()) { + previous_dex_registers_.resize(current_dex_registers_.size(), DexRegisterLocation::None()); + dex_register_timestamp_.resize(current_dex_registers_.size(), 0u); + } + + // Set bit in the mask for each register that has been changed since the previous stack map. + // Modified registers are stored in the catalogue and the catalogue index added to the list. for (size_t i = 0; i < current_dex_registers_.size(); i++) { DexRegisterLocation reg = current_dex_registers_[i]; - if (reg.IsLive()) { - DexRegisterEntry entry = DexRegisterEntry { + // Distance is difference between this index and the index of last modification. + uint32_t distance = stack_maps_.size() - dex_register_timestamp_[i]; + if (previous_dex_registers_[i] != reg || distance > kMaxDexRegisterMapSearchDistance) { + DexRegisterEntry entry = DexRegisterEntry{ .kind = static_cast(reg.GetKind()), .packed_value = DexRegisterInfo::PackValue(reg.GetKind(), reg.GetValue()), }; + uint32_t index = reg.IsLive() ? dex_register_catalog_.Dedup(&entry) : kNoValue; temp_dex_register_mask_.SetBit(i); - temp_dex_register_map_.push_back(dex_register_catalog_.Dedup(&entry)); + temp_dex_register_map_.push_back(index); + previous_dex_registers_[i] = reg; + dex_register_timestamp_[i] = stack_maps_.size(); } } - // Set the mask and map for the current StackMap/InlineInfo. - uint32_t mask_index = StackMap::kNoValue; // Represents mask with all zero bits. + // Set the mask and map for the current StackMap (which includes inlined registers). if (temp_dex_register_mask_.GetNumberOfBits() != 0) { - mask_index = dex_register_masks_.Dedup(temp_dex_register_mask_.GetRawStorage(), - temp_dex_register_mask_.GetNumberOfBits()); + current_stack_map_.dex_register_mask_index = + dex_register_masks_.Dedup(temp_dex_register_mask_.GetRawStorage(), + temp_dex_register_mask_.GetNumberOfBits()); } - uint32_t map_index = dex_register_maps_.Dedup(temp_dex_register_map_.data(), - temp_dex_register_map_.size()); - if (!current_inline_infos_.empty()) { - current_inline_infos_.back().dex_register_mask_index = mask_index; - current_inline_infos_.back().dex_register_map_index = map_index; - } else { - current_stack_map_.dex_register_mask_index = mask_index; - current_stack_map_.dex_register_map_index = map_index; + if (!current_dex_registers_.empty()) { + current_stack_map_.dex_register_map_index = + dex_register_maps_.Dedup(temp_dex_register_map_.data(), + temp_dex_register_map_.size()); } if (kVerifyStackMaps) { size_t stack_map_index = stack_maps_.size(); - int32_t depth = current_inline_infos_.size() - 1; + uint32_t depth = current_inline_infos_.size(); // We need to make copy of the current registers for later (when the check is run). - auto expected_dex_registers = std::make_shared>( + auto expected_dex_registers = std::make_shared>( current_dex_registers_.begin(), current_dex_registers_.end()); dchecks_.emplace_back([=](const CodeInfo& code_info) { StackMap stack_map = code_info.GetStackMapAt(stack_map_index); - size_t num_dex_registers = expected_dex_registers->size(); - DexRegisterMap map = (depth == -1) - ? code_info.GetDexRegisterMapOf(stack_map, num_dex_registers) - : code_info.GetDexRegisterMapAtDepth(depth, stack_map, num_dex_registers); - CHECK_EQ(map.size(), num_dex_registers); - for (size_t r = 0; r < num_dex_registers; r++) { - CHECK_EQ(expected_dex_registers->at(r), map.Get(r)); + uint32_t expected_reg = 0; + for (DexRegisterLocation reg : code_info.GetDexRegisterMapOf(stack_map)) { + CHECK_EQ((*expected_dex_registers)[expected_reg++], reg); + } + for (uint32_t d = 0; d < depth; d++) { + for (DexRegisterLocation reg : code_info.GetDexRegisterMapAtDepth(d, stack_map)) { + CHECK_EQ((*expected_dex_registers)[expected_reg++], reg); + } } + CHECK_EQ(expected_reg, expected_dex_registers->size()); }); } } @@ -290,6 +303,7 @@ size_t StackMapStream::PrepareForFillIn() { dex_register_masks_.Encode(&out_, &bit_offset); dex_register_maps_.Encode(&out_, &bit_offset); dex_register_catalog_.Encode(&out_, &bit_offset); + EncodeVarintBits(&out_, &bit_offset, num_dex_registers_); return UnsignedLeb128Size(out_.size()) + out_.size(); } diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index d634c703ff..02fb6cb434 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -55,6 +55,8 @@ class StackMapStream : public ValueObject { in_inline_info_(false), current_inline_infos_(allocator->Adapter(kArenaAllocStackMapStream)), current_dex_registers_(allocator->Adapter(kArenaAllocStackMapStream)), + previous_dex_registers_(allocator->Adapter(kArenaAllocStackMapStream)), + dex_register_timestamp_(allocator->Adapter(kArenaAllocStackMapStream)), temp_dex_register_mask_(allocator, 32, true, kArenaAllocStackMapStream), temp_dex_register_map_(allocator->Adapter(kArenaAllocStackMapStream)) { } @@ -113,8 +115,7 @@ class StackMapStream : public ValueObject { uint32_t method_info_index; uint32_t art_method_hi; uint32_t art_method_lo; - uint32_t dex_register_mask_index; - uint32_t dex_register_map_index; + uint32_t num_dex_registers; }; // The fields must be uint32_t and mirror the InvokeInfo accessor in stack_map.h! @@ -147,6 +148,7 @@ class StackMapStream : public ValueObject { BitmapTableBuilder dex_register_masks_; BitTableBuilder dex_register_maps_; BitTableBuilder dex_register_catalog_; + uint32_t num_dex_registers_ = 0; // TODO: Make this const and get the value in constructor. ScopedArenaVector out_; BitTableBuilder method_infos_; @@ -159,6 +161,8 @@ class StackMapStream : public ValueObject { StackMapEntry current_stack_map_; ScopedArenaVector current_inline_infos_; ScopedArenaVector current_dex_registers_; + ScopedArenaVector previous_dex_registers_; + ScopedArenaVector dex_register_timestamp_; // Stack map index of last change. size_t expected_num_dex_registers_; // Temporary variables used in CreateDexRegisterMap. diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index 77aa3ef965..0be276cfd6 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -358,13 +358,6 @@ TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { ASSERT_EQ(Kind::kConstant, location1.GetKind()); ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); - - // Test that the inline info dex register map deduplicated to the same offset as the stack map - // one. - ASSERT_TRUE(stack_map.HasInlineInfo()); - InlineInfo inline_info = code_info.GetInlineInfoAtDepth(stack_map, 0); - EXPECT_EQ(inline_info.GetDexRegisterMapIndex(), - stack_map.GetDexRegisterMapIndex()); } } @@ -466,13 +459,9 @@ TEST(StackMapTest, TestShareDexRegisterMap) { ASSERT_EQ(2, dex_registers2.GetMachineRegister(0)); ASSERT_EQ(-2, dex_registers2.GetConstant(1)); - // Verify dex register map offsets. - ASSERT_EQ(sm0.GetDexRegisterMapIndex(), - sm1.GetDexRegisterMapIndex()); - ASSERT_NE(sm0.GetDexRegisterMapIndex(), - sm2.GetDexRegisterMapIndex()); - ASSERT_NE(sm1.GetDexRegisterMapIndex(), - sm2.GetDexRegisterMapIndex()); + // Verify dex register mask offsets. + ASSERT_FALSE(sm1.HasDexRegisterMaskIndex()); // No delta. + ASSERT_TRUE(sm2.HasDexRegisterMaskIndex()); // Has delta. } TEST(StackMapTest, TestNoDexRegisterMap) { @@ -649,8 +638,6 @@ TEST(StackMapTest, InlineTest) { ASSERT_EQ(80, dex_registers2.GetStackOffsetInBytes(0)); ASSERT_EQ(10, dex_registers2.GetConstant(1)); ASSERT_EQ(5, dex_registers2.GetMachineRegister(2)); - - ASSERT_FALSE(if1_2.HasDexRegisterMap()); } { @@ -682,8 +669,6 @@ TEST(StackMapTest, InlineTest) { ASSERT_EQ(10u, if2_2.GetDexPc()); ASSERT_TRUE(if2_2.EncodesArtMethod()); - ASSERT_FALSE(if2_0.HasDexRegisterMap()); - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(1, sm3, 1); ASSERT_EQ(2, dex_registers1.GetMachineRegister(0)); diff --git a/libartbase/base/bit_memory_region.h b/libartbase/base/bit_memory_region.h index a3d3ee41d6..b4764fd5ce 100644 --- a/libartbase/base/bit_memory_region.h +++ b/libartbase/base/bit_memory_region.h @@ -151,6 +151,20 @@ class BitMemoryRegion FINAL : public ValueObject { StoreBits(bit_offset + bit, src.LoadBits(bit, num_bits), num_bits); } + // Count the number of set bits within the given bit range. + ALWAYS_INLINE size_t PopCount(size_t bit_offset, size_t bit_length) const { + DCHECK_LE(bit_offset, bit_size_); + DCHECK_LE(bit_length, bit_size_ - bit_offset); + size_t count = 0; + size_t bit = 0; + constexpr size_t kNumBits = BitSizeOf(); + for (; bit + kNumBits <= bit_length; bit += kNumBits) { + count += POPCOUNT(LoadBits(bit_offset + bit, kNumBits)); + } + count += POPCOUNT(LoadBits(bit_offset + bit, bit_length - bit)); + return count; + } + ALWAYS_INLINE bool Equals(const BitMemoryRegion& other) const { return data_ == other.data_ && bit_start_ == other.bit_start_ && diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 3188087cfb..3973edcf73 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -753,7 +753,7 @@ class OatDumper { kByteKindInlineInfoMethodIndexIdx, kByteKindInlineInfoDexPc, kByteKindInlineInfoArtMethod, - kByteKindInlineInfoDexRegisterMap, + kByteKindInlineInfoNumDexRegisters, kByteKindInlineInfoIsLast, kByteKindCount, // Special ranges for std::accumulate convenience. @@ -855,8 +855,8 @@ class OatDumper { inline_info_bits, "inline info"); Dump(os, - "InlineInfoDexRegisterMap ", - bits[kByteKindInlineInfoDexRegisterMap], + "InlineInfoNumDexRegisters ", + bits[kByteKindInlineInfoNumDexRegisters], inline_info_bits, "inline info"); Dump(os, @@ -1757,8 +1757,8 @@ class OatDumper { inline_infos.NumColumnBits(InlineInfo::kArtMethodHi) * num_inline_infos + inline_infos.NumColumnBits(InlineInfo::kArtMethodLo) * num_inline_infos); stats_.AddBits( - Stats::kByteKindInlineInfoDexRegisterMap, - inline_infos.NumColumnBits(InlineInfo::kDexRegisterMapIndex) * num_inline_infos); + Stats::kByteKindInlineInfoNumDexRegisters, + inline_infos.NumColumnBits(InlineInfo::kNumberOfDexRegisters) * num_inline_infos); stats_.AddBits(Stats::kByteKindInlineInfoIsLast, num_inline_infos); } } diff --git a/runtime/dex_register_location.h b/runtime/dex_register_location.h index c6d4ad2feb..a20dccbc12 100644 --- a/runtime/dex_register_location.h +++ b/runtime/dex_register_location.h @@ -29,6 +29,7 @@ namespace art { class DexRegisterLocation { public: enum class Kind : int32_t { + kInvalid = -2, // only used internally during register map decoding. kNone = -1, // vreg has not been set. kInStack, // vreg is on the stack, value holds the stack offset. kConstant, // vreg is a constant value. @@ -40,9 +41,8 @@ class DexRegisterLocation { DexRegisterLocation(Kind kind, int32_t value) : kind_(kind), value_(value) {} - static DexRegisterLocation None() { - return DexRegisterLocation(Kind::kNone, 0); - } + static DexRegisterLocation None() { return DexRegisterLocation(Kind::kNone, 0); } + static DexRegisterLocation Invalid() { return DexRegisterLocation(Kind::kInvalid, 0); } bool IsLive() const { return kind_ != Kind::kNone; } diff --git a/runtime/stack.cc b/runtime/stack.cc index bd0d5d680e..0b3441aa45 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -236,7 +236,9 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin size_t depth_in_stack_map = current_inlining_depth_ - 1; DexRegisterMap dex_register_map = IsInInlinedFrame() - ? code_info.GetDexRegisterMapAtDepth(depth_in_stack_map, stack_map, number_of_dex_registers) + ? code_info.GetDexRegisterMapAtDepth(depth_in_stack_map, + stack_map, + number_of_dex_registers) : code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); if (!dex_register_map.IsValid()) { diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index a5749b84a7..59a89e12b8 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -25,6 +25,69 @@ namespace art { +// Scan backward to determine dex register locations at given stack map. +// All registers for a stack map are combined - inlined registers are just appended, +// therefore 'first_dex_register' allows us to select a sub-range to decode. +void CodeInfo::DecodeDexRegisterMap(uint32_t stack_map_index, + uint32_t first_dex_register, + /*out*/ DexRegisterMap* map) const { + // Count remaining work so we know when we have finished. + uint32_t remaining_registers = map->size(); + + // Keep scanning backwards and collect the most recent location of each register. + for (int32_t s = stack_map_index; s >= 0 && remaining_registers != 0; s--) { + StackMap stack_map = GetStackMapAt(s); + DCHECK_LE(stack_map_index - s, kMaxDexRegisterMapSearchDistance) << "Unbounded search"; + + // The mask specifies which registers where modified in this stack map. + // NB: the mask can be shorter than expected if trailing zero bits were removed. + uint32_t mask_index = stack_map.GetDexRegisterMaskIndex(); + if (mask_index == StackMap::kNoValue) { + continue; // Nothing changed at this stack map. + } + BitMemoryRegion mask = dex_register_masks_.GetBitMemoryRegion(mask_index); + if (mask.size_in_bits() <= first_dex_register) { + continue; // Nothing changed after the first register we are interested in. + } + + // The map stores one catalogue index per each modified register location. + uint32_t map_index = stack_map.GetDexRegisterMapIndex(); + DCHECK_NE(map_index, StackMap::kNoValue); + + // Skip initial registers which we are not interested in (to get to inlined registers). + map_index += mask.PopCount(0, first_dex_register); + mask = mask.Subregion(first_dex_register, mask.size_in_bits() - first_dex_register); + + // Update registers that we see for first time (i.e. most recent value). + DexRegisterLocation* regs = map->data(); + const uint32_t end = std::min(map->size(), mask.size_in_bits()); + const size_t kNumBits = BitSizeOf(); + for (uint32_t reg = 0; reg < end; reg += kNumBits) { + // Process the mask in chunks of kNumBits for performance. + uint32_t bits = mask.LoadBits(reg, std::min(end - reg, kNumBits)); + while (bits != 0) { + uint32_t bit = CTZ(bits); + if (regs[reg + bit].GetKind() == DexRegisterLocation::Kind::kInvalid) { + regs[reg + bit] = GetDexRegisterCatalogEntry(dex_register_maps_.Get(map_index)); + remaining_registers--; + } + map_index++; + bits ^= 1u << bit; // Clear the bit. + } + } + } + + // Set any remaining registers to None (which is the default state at first stack map). + if (remaining_registers != 0) { + DexRegisterLocation* regs = map->data(); + for (uint32_t r = 0; r < map->size(); r++) { + if (regs[r].GetKind() == DexRegisterLocation::Kind::kInvalid) { + regs[r] = DexRegisterLocation::None(); + } + } + } +} + std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation& reg) { using Kind = DexRegisterLocation::Kind; switch (reg.GetKind()) { @@ -42,6 +105,8 @@ std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation& reg) { return stream << "f" << reg.GetValue() << "/hi"; case Kind::kConstant: return stream << "#" << reg.GetValue(); + case Kind::kInvalid: + return stream << "Invalid"; default: return stream << "DexRegisterLocation(" << static_cast(reg.GetKind()) << "," << reg.GetValue() << ")"; diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 6da002138c..ff70b6c759 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -39,6 +39,12 @@ class VariableIndentationOutputStream; // (signed) values. static constexpr ssize_t kFrameSlotSize = 4; +// The delta compression of dex register maps means we need to scan the stackmaps backwards. +// We compress the data in such a way so that there is an upper bound on the search distance. +// Max distance 0 means each stack map must be fully defined and no scanning back is allowed. +// If this value is changed, the oat file version should be incremented (for DCHECK to pass). +static constexpr size_t kMaxDexRegisterMapSearchDistance = 32; + class ArtMethod; class CodeInfo; @@ -49,12 +55,14 @@ std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation& reg); // If the size is small enough, it keeps the data on the stack. class DexRegisterMap { public: - // Create map for given number of registers and initialize all locations to None. - explicit DexRegisterMap(size_t count) : count_(count), regs_small_{} { + using iterator = DexRegisterLocation*; + + // Create map for given number of registers and initialize them to the given value. + DexRegisterMap(size_t count, DexRegisterLocation value) : count_(count), regs_small_{} { if (count_ <= kSmallCount) { - std::fill_n(regs_small_.begin(), count, DexRegisterLocation::None()); + std::fill_n(regs_small_.begin(), count, value); } else { - regs_large_.resize(count, DexRegisterLocation::None()); + regs_large_.resize(count, value); } } @@ -62,6 +70,9 @@ class DexRegisterMap { return count_ <= kSmallCount ? regs_small_.data() : regs_large_.data(); } + iterator begin() { return data(); } + iterator end() { return data() + count_; } + size_t size() const { return count_; } bool IsValid() const { return count_ != 0; } @@ -192,7 +203,7 @@ class StackMap : public BitTable<7>::Accessor { * The row referenced from the StackMap holds information at depth 0. * Following rows hold information for further depths. */ -class InlineInfo : public BitTable<7>::Accessor { +class InlineInfo : public BitTable<6>::Accessor { public: BIT_TABLE_HEADER() BIT_TABLE_COLUMN(0, IsLast) // Determines if there are further rows for further depths. @@ -200,7 +211,7 @@ class InlineInfo : public BitTable<7>::Accessor { BIT_TABLE_COLUMN(2, MethodInfoIndex) BIT_TABLE_COLUMN(3, ArtMethodHi) // High bits of ArtMethod*. BIT_TABLE_COLUMN(4, ArtMethodLo) // Low bits of ArtMethod*. - BIT_TABLE_COLUMN(5, DexRegisterMaskIndex) + BIT_TABLE_COLUMN(5, NumberOfDexRegisters) // Includes outer levels and the main method. BIT_TABLE_COLUMN(6, DexRegisterMapIndex) static constexpr uint32_t kLast = -1; @@ -220,10 +231,6 @@ class InlineInfo : public BitTable<7>::Accessor { return reinterpret_cast((hi << 32) | lo); } - ALWAYS_INLINE bool HasDexRegisterMap() const { - return HasDexRegisterMapIndex(); - } - void Dump(VariableIndentationOutputStream* vios, const CodeInfo& info, const StackMap& stack_map, @@ -338,7 +345,9 @@ class CodeInfo { } ALWAYS_INLINE DexRegisterLocation GetDexRegisterCatalogEntry(size_t index) const { - return DexRegisterInfo(&dex_register_catalog_, index).GetLocation(); + return (index == StackMap::kNoValue) + ? DexRegisterLocation::None() + : DexRegisterInfo(&dex_register_catalog_, index).GetLocation(); } uint32_t GetNumberOfStackMaps() const { @@ -350,19 +359,30 @@ class CodeInfo { } ALWAYS_INLINE DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, - size_t num_dex_registers) const { - return DecodeDexRegisterMap(stack_map.GetDexRegisterMaskIndex(), - stack_map.GetDexRegisterMapIndex(), - num_dex_registers); + size_t vregs ATTRIBUTE_UNUSED = 0) const { + if (stack_map.HasDexRegisterMap()) { + DexRegisterMap map(number_of_dex_registers_, DexRegisterLocation::Invalid()); + DecodeDexRegisterMap(stack_map.Row(), /* first_dex_register */ 0, &map); + return map; + } + return DexRegisterMap(0, DexRegisterLocation::None()); } ALWAYS_INLINE DexRegisterMap GetDexRegisterMapAtDepth(uint8_t depth, StackMap stack_map, - size_t num_dex_registers) const { - InlineInfo inline_info = GetInlineInfoAtDepth(stack_map, depth); - return DecodeDexRegisterMap(inline_info.GetDexRegisterMaskIndex(), - inline_info.GetDexRegisterMapIndex(), - num_dex_registers); + size_t vregs ATTRIBUTE_UNUSED = 0) const { + if (stack_map.HasDexRegisterMap()) { + // The register counts are commutative and include all outer levels. + // This allows us to determine the range [first, last) in just two lookups. + // If we are at depth 0 (the first inlinee), the count from the main method is used. + uint32_t first = (depth == 0) ? number_of_dex_registers_ + : GetInlineInfoAtDepth(stack_map, depth - 1).GetNumberOfDexRegisters(); + uint32_t last = GetInlineInfoAtDepth(stack_map, depth).GetNumberOfDexRegisters(); + DexRegisterMap map(last - first, DexRegisterLocation::Invalid()); + DecodeDexRegisterMap(stack_map.Row(), first, &map); + return map; + } + return DexRegisterMap(0, DexRegisterLocation::None()); } InlineInfo GetInlineInfo(size_t index) const { @@ -421,8 +441,6 @@ class CodeInfo { if (other.GetDexPc() == dex_pc && other.GetNativePcOffset(kRuntimeISA) == stack_map.GetNativePcOffset(kRuntimeISA)) { - DCHECK_EQ(other.GetDexRegisterMapIndex(), - stack_map.GetDexRegisterMapIndex()); if (i < e - 2) { // Make sure there are not three identical stack maps following each other. DCHECK_NE( @@ -469,23 +487,10 @@ class CodeInfo { const MethodInfo& method_info) const; private: - ALWAYS_INLINE DexRegisterMap DecodeDexRegisterMap(uint32_t mask_index, - uint32_t map_index, - uint32_t num_dex_registers) const { - DexRegisterMap map(map_index == StackMap::kNoValue ? 0 : num_dex_registers); - if (mask_index != StackMap::kNoValue) { - BitMemoryRegion mask = dex_register_masks_.GetBitMemoryRegion(mask_index); - num_dex_registers = std::min(num_dex_registers, mask.size_in_bits()); - DexRegisterLocation* regs = map.data(); - for (uint32_t r = 0; r < mask.size_in_bits(); r++) { - if (mask.LoadBit(r) /* is_live */) { - DCHECK_LT(r, map.size()); - regs[r] = GetDexRegisterCatalogEntry(dex_register_maps_.Get(map_index++)); - } - } - } - return map; - } + // Scan backward to determine dex register locations at given stack map. + void DecodeDexRegisterMap(uint32_t stack_map_index, + uint32_t first_dex_register, + /*out*/ DexRegisterMap* map) const; void Decode(const uint8_t* data) { size_t non_header_size = DecodeUnsignedLeb128(&data); @@ -500,6 +505,7 @@ class CodeInfo { dex_register_masks_.Decode(region, &bit_offset); dex_register_maps_.Decode(region, &bit_offset); dex_register_catalog_.Decode(region, &bit_offset); + number_of_dex_registers_ = DecodeVarintBits(region, &bit_offset); CHECK_EQ(non_header_size, BitsToBytesRoundUp(bit_offset)) << "Invalid CodeInfo"; } @@ -512,6 +518,7 @@ class CodeInfo { BitTable<1> dex_register_masks_; BitTable<1> dex_register_maps_; BitTable dex_register_catalog_; + uint32_t number_of_dex_registers_; // Excludes any inlined methods. friend class OatDumper; }; -- GitLab From fd89b0739d4000ea70f28bf53dea531027024f5a Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Sun, 3 Jun 2018 12:00:22 +0100 Subject: [PATCH 544/749] Remove unused GetDexRegisterMap arguments. They are no longer needed in the new encoding. I reuse the local variables in most places to DCHECK the size of the decoded register map. This has one catch though: We sometimes omit all dex registers, so the DCHECK should be done only after checking if the map is empty (if applicable). Test: test-art-host-gtest-stack_map_test Change-Id: I94b67029842374bc8eb7c9e5eac76fc93a651f24 --- compiler/debug/elf_debug_info_writer.h | 3 +- compiler/debug/elf_debug_loc_writer.h | 2 +- compiler/optimizing/stack_map_test.cc | 66 ++++++++++++++++---------- oatdump/oatdump.cc | 5 +- runtime/check_reference_map_visitor.h | 4 +- runtime/jit/jit.cc | 6 +-- runtime/quick_exception_handler.cc | 21 ++++---- runtime/stack.cc | 10 ++-- runtime/stack_map.cc | 21 +++----- runtime/stack_map.h | 14 ++---- runtime/thread.cc | 3 +- 11 files changed, 72 insertions(+), 83 deletions(-) diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index f2002a0af6..f2a942f34a 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -210,8 +210,7 @@ class ElfCompilationUnitWriter { code_info.reset(new CodeInfo(mi->code_info)); for (size_t s = 0; s < code_info->GetNumberOfStackMaps(); ++s) { const StackMap stack_map = code_info->GetStackMapAt(s); - dex_reg_maps.push_back(code_info->GetDexRegisterMapOf( - stack_map, accessor.RegistersSize())); + dex_reg_maps.push_back(code_info->GetDexRegisterMapOf(stack_map)); } } diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h index 8cb4e55bbc..4009acb992 100644 --- a/compiler/debug/elf_debug_loc_writer.h +++ b/compiler/debug/elf_debug_loc_writer.h @@ -147,7 +147,7 @@ static std::vector GetVariableLocations( DexRegisterLocation reg_hi = DexRegisterLocation::None(); DCHECK_LT(stack_map_index, dex_register_maps.size()); DexRegisterMap dex_register_map = dex_register_maps[stack_map_index]; - DCHECK(dex_register_map.IsValid()); + DCHECK(!dex_register_map.empty()); CodeItemDataAccessor accessor(*method_info->dex_file, method_info->code_item); reg_lo = dex_register_map.GetDexRegisterLocation(vreg); if (is64bitValue) { diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index 0be276cfd6..9adc4c5ba6 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -81,8 +81,8 @@ TEST(StackMapTest, Test1) { ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask)); ASSERT_TRUE(stack_map.HasDexRegisterMap()); - DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map); + ASSERT_EQ(number_of_dex_registers, dex_register_map.size()); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters()); @@ -170,8 +170,8 @@ TEST(StackMapTest, Test2) { ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask1)); ASSERT_TRUE(stack_map.HasDexRegisterMap()); - DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map); + ASSERT_EQ(number_of_dex_registers, dex_register_map.size()); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters()); @@ -210,8 +210,8 @@ TEST(StackMapTest, Test2) { ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask2)); ASSERT_TRUE(stack_map.HasDexRegisterMap()); - DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map); + ASSERT_EQ(number_of_dex_registers, dex_register_map.size()); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters()); @@ -243,8 +243,8 @@ TEST(StackMapTest, Test2) { ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask3)); ASSERT_TRUE(stack_map.HasDexRegisterMap()); - DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map); + ASSERT_EQ(number_of_dex_registers, dex_register_map.size()); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters()); @@ -276,8 +276,8 @@ TEST(StackMapTest, Test2) { ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask4)); ASSERT_TRUE(stack_map.HasDexRegisterMap()); - DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map); + ASSERT_EQ(number_of_dex_registers, dex_register_map.size()); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters()); @@ -342,7 +342,8 @@ TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { ASSERT_TRUE(CheckStackMask(code_info, stack_map, sp_mask1)); ASSERT_TRUE(stack_map.HasDexRegisterMap()); - DexRegisterMap map(code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers)); + DexRegisterMap map(code_info.GetDexRegisterMapOf(stack_map)); + ASSERT_EQ(number_of_dex_registers, map.size()); ASSERT_TRUE(map.IsDexRegisterLive(0)); ASSERT_TRUE(map.IsDexRegisterLive(1)); ASSERT_EQ(2u, map.GetNumberOfLiveDexRegisters()); @@ -393,8 +394,8 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(stack_map)); ASSERT_TRUE(stack_map.HasDexRegisterMap()); - DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map); + ASSERT_EQ(number_of_dex_registers, dex_register_map.size()); ASSERT_FALSE(dex_register_map.IsDexRegisterLive(0)); ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); ASSERT_EQ(1u, dex_register_map.GetNumberOfLiveDexRegisters()); @@ -443,19 +444,22 @@ TEST(StackMapTest, TestShareDexRegisterMap) { // Verify first stack map. StackMap sm0 = ci.GetStackMapAt(0); - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, number_of_dex_registers); + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0); + ASSERT_EQ(number_of_dex_registers, dex_registers0.size()); ASSERT_EQ(0, dex_registers0.GetMachineRegister(0)); ASSERT_EQ(-2, dex_registers0.GetConstant(1)); // Verify second stack map. StackMap sm1 = ci.GetStackMapAt(1); - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1, number_of_dex_registers); + DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1); + ASSERT_EQ(number_of_dex_registers, dex_registers1.size()); ASSERT_EQ(0, dex_registers1.GetMachineRegister(0)); ASSERT_EQ(-2, dex_registers1.GetConstant(1)); // Verify third stack map. StackMap sm2 = ci.GetStackMapAt(2); - DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2, number_of_dex_registers); + DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2); + ASSERT_EQ(number_of_dex_registers, dex_registers2.size()); ASSERT_EQ(2, dex_registers2.GetMachineRegister(0)); ASSERT_EQ(-2, dex_registers2.GetConstant(1)); @@ -591,7 +595,8 @@ TEST(StackMapTest, InlineTest) { // Verify first stack map. StackMap sm0 = ci.GetStackMapAt(0); - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, 2); + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0); + ASSERT_EQ(2u, dex_registers0.size()); ASSERT_EQ(0, dex_registers0.GetStackOffsetInBytes(0)); ASSERT_EQ(4, dex_registers0.GetConstant(1)); @@ -603,10 +608,12 @@ TEST(StackMapTest, InlineTest) { ASSERT_EQ(3u, if0_1.GetDexPc()); ASSERT_TRUE(if0_1.EncodesArtMethod()); - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, sm0, 1); + DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, sm0); + ASSERT_EQ(1u, dex_registers1.size()); ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0)); - DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, sm0, 3); + DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, sm0); + ASSERT_EQ(3u, dex_registers2.size()); ASSERT_EQ(16, dex_registers2.GetStackOffsetInBytes(0)); ASSERT_EQ(20, dex_registers2.GetConstant(1)); ASSERT_EQ(15, dex_registers2.GetMachineRegister(2)); @@ -616,7 +623,8 @@ TEST(StackMapTest, InlineTest) { // Verify second stack map. StackMap sm1 = ci.GetStackMapAt(1); - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm1, 2); + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm1); + ASSERT_EQ(2u, dex_registers0.size()); ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0)); ASSERT_EQ(0, dex_registers0.GetConstant(1)); @@ -631,10 +639,12 @@ TEST(StackMapTest, InlineTest) { ASSERT_EQ(5u, if1_2.GetDexPc()); ASSERT_TRUE(if1_2.EncodesArtMethod()); - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, sm1, 1); + DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, sm1); + ASSERT_EQ(1u, dex_registers1.size()); ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0)); - DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, sm1, 3); + DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, sm1); + ASSERT_EQ(3u, dex_registers2.size()); ASSERT_EQ(80, dex_registers2.GetStackOffsetInBytes(0)); ASSERT_EQ(10, dex_registers2.GetConstant(1)); ASSERT_EQ(5, dex_registers2.GetMachineRegister(2)); @@ -644,7 +654,8 @@ TEST(StackMapTest, InlineTest) { // Verify third stack map. StackMap sm2 = ci.GetStackMapAt(2); - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm2, 2); + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm2); + ASSERT_EQ(2u, dex_registers0.size()); ASSERT_FALSE(dex_registers0.IsDexRegisterLive(0)); ASSERT_EQ(4, dex_registers0.GetConstant(1)); ASSERT_FALSE(sm2.HasInlineInfo()); @@ -654,7 +665,8 @@ TEST(StackMapTest, InlineTest) { // Verify fourth stack map. StackMap sm3 = ci.GetStackMapAt(3); - DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm3, 2); + DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm3); + ASSERT_EQ(2u, dex_registers0.size()); ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0)); ASSERT_EQ(0, dex_registers0.GetConstant(1)); @@ -669,10 +681,12 @@ TEST(StackMapTest, InlineTest) { ASSERT_EQ(10u, if2_2.GetDexPc()); ASSERT_TRUE(if2_2.EncodesArtMethod()); - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(1, sm3, 1); + DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(1, sm3); + ASSERT_EQ(1u, dex_registers1.size()); ASSERT_EQ(2, dex_registers1.GetMachineRegister(0)); - DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(2, sm3, 2); + DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(2, sm3); + ASSERT_EQ(2u, dex_registers2.size()); ASSERT_FALSE(dex_registers2.IsDexRegisterLive(0)); ASSERT_EQ(3, dex_registers2.GetMachineRegister(1)); } diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 3973edcf73..b28c4d90d1 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1425,7 +1425,7 @@ class OatDumper { DCHECK(code_item_accessor.HasCodeItem()); ScopedIndentation indent1(vios); MethodInfo method_info = oat_method.GetOatQuickMethodHeader()->GetOptimizedMethodInfo(); - DumpCodeInfo(vios, code_info, oat_method, code_item_accessor, method_info); + DumpCodeInfo(vios, code_info, oat_method, method_info); } } else if (IsMethodGeneratedByDexToDexCompiler(oat_method, code_item_accessor)) { // We don't encode the size in the table, so just emit that we have quickened @@ -1441,11 +1441,9 @@ class OatDumper { void DumpCodeInfo(VariableIndentationOutputStream* vios, const CodeInfo& code_info, const OatFile::OatMethod& oat_method, - const CodeItemDataAccessor& code_item_accessor, const MethodInfo& method_info) { code_info.Dump(vios, oat_method.GetCodeOffset(), - code_item_accessor.RegistersSize(), options_.dump_code_info_stack_maps_, instruction_set_, method_info); @@ -1775,7 +1773,6 @@ class OatDumper { helper.GetCodeInfo(), method_info, oat_method.GetCodeOffset(), - code_item_accessor.RegistersSize(), instruction_set_); do { helper.Next(); diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h index acdb235f8c..8a2a70e7ab 100644 --- a/runtime/check_reference_map_visitor.h +++ b/runtime/check_reference_map_visitor.h @@ -68,8 +68,8 @@ class CheckReferenceMapVisitor : public StackVisitor { StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); CodeItemDataAccessor accessor(m->DexInstructionData()); uint16_t number_of_dex_registers = accessor.RegistersSize(); - DexRegisterMap dex_register_map = - code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map); + DCHECK_EQ(dex_register_map.size(), number_of_dex_registers); uint32_t register_mask = code_info.GetRegisterMaskOf(stack_map); BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map); for (int i = 0; i < number_of_references; ++i) { diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index b7b779ce31..5a5634ef62 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -493,8 +493,7 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, // We found a stack map, now fill the frame with dex register values from the interpreter's // shadow frame. - DexRegisterMap vreg_map = - code_info.GetDexRegisterMapOf(stack_map, number_of_vregs); + DexRegisterMap vreg_map = code_info.GetDexRegisterMapOf(stack_map); frame_size = osr_method->GetFrameSizeInBytes(); @@ -510,10 +509,11 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, memory[0] = method; shadow_frame = thread->PopShadowFrame(); - if (!vreg_map.IsValid()) { + if (vreg_map.empty()) { // If we don't have a dex register map, then there are no live dex registers at // this dex pc. } else { + DCHECK_EQ(vreg_map.size(), number_of_vregs); for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) { DexRegisterLocation::Kind location = vreg_map.GetLocationKind(vreg); if (location == DexRegisterLocation::Kind::kNone) { diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 23ccf6ad58..cf1cbe7f7b 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -230,19 +230,18 @@ void QuickExceptionHandler::SetCatchEnvironmentForOptimizedHandler(StackVisitor* // Find stack map of the catch block. StackMap catch_stack_map = code_info.GetCatchStackMapForDexPc(GetHandlerDexPc()); DCHECK(catch_stack_map.IsValid()); - DexRegisterMap catch_vreg_map = - code_info.GetDexRegisterMapOf(catch_stack_map, number_of_vregs); - if (!catch_vreg_map.IsValid() || !catch_vreg_map.HasAnyLiveDexRegisters()) { + DexRegisterMap catch_vreg_map = code_info.GetDexRegisterMapOf(catch_stack_map); + if (!catch_vreg_map.HasAnyLiveDexRegisters()) { return; } + DCHECK_EQ(catch_vreg_map.size(), number_of_vregs); // Find stack map of the throwing instruction. StackMap throw_stack_map = code_info.GetStackMapForNativePcOffset(stack_visitor->GetNativePcOffset()); DCHECK(throw_stack_map.IsValid()); - DexRegisterMap throw_vreg_map = - code_info.GetDexRegisterMapOf(throw_stack_map, number_of_vregs); - DCHECK(throw_vreg_map.IsValid()); + DexRegisterMap throw_vreg_map = code_info.GetDexRegisterMapOf(throw_stack_map); + DCHECK_EQ(throw_vreg_map.size(), number_of_vregs); // Copy values between them. for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) { @@ -405,14 +404,12 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { uint32_t register_mask = code_info.GetRegisterMaskOf(stack_map); BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map); DexRegisterMap vreg_map = IsInInlinedFrame() - ? code_info.GetDexRegisterMapAtDepth(GetCurrentInliningDepth() - 1, - stack_map, - number_of_vregs) - : code_info.GetDexRegisterMapOf(stack_map, number_of_vregs); - - if (!vreg_map.IsValid()) { + ? code_info.GetDexRegisterMapAtDepth(GetCurrentInliningDepth() - 1, stack_map) + : code_info.GetDexRegisterMapOf(stack_map); + if (vreg_map.empty()) { return; } + DCHECK_EQ(vreg_map.size(), number_of_vregs); for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) { if (updated_vregs != nullptr && updated_vregs[vreg]) { diff --git a/runtime/stack.cc b/runtime/stack.cc index 0b3441aa45..56e47b9c77 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -236,14 +236,12 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin size_t depth_in_stack_map = current_inlining_depth_ - 1; DexRegisterMap dex_register_map = IsInInlinedFrame() - ? code_info.GetDexRegisterMapAtDepth(depth_in_stack_map, - stack_map, - number_of_dex_registers) - : code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); - - if (!dex_register_map.IsValid()) { + ? code_info.GetDexRegisterMapAtDepth(depth_in_stack_map, stack_map) + : code_info.GetDexRegisterMapOf(stack_map); + if (dex_register_map.empty()) { return false; } + DCHECK_EQ(dex_register_map.size(), number_of_dex_registers); DexRegisterLocation::Kind location_kind = dex_register_map.GetLocationKind(vreg); switch (location_kind) { case DexRegisterLocation::Kind::kInStack: { diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index 59a89e12b8..a25c9fdee0 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -115,7 +115,7 @@ std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation& reg) { static void DumpDexRegisterMap(VariableIndentationOutputStream* vios, const DexRegisterMap& map) { - if (map.IsValid()) { + if (!map.empty()) { ScopedIndentation indent1(vios); for (size_t i = 0; i < map.size(); ++i) { if (map.IsDexRegisterLive(i)) { @@ -163,7 +163,6 @@ static void DumpTable(VariableIndentationOutputStream* vios, void CodeInfo::Dump(VariableIndentationOutputStream* vios, uint32_t code_offset, - uint16_t num_dex_registers, bool verbose, InstructionSet instruction_set, const MethodInfo& method_info) const { @@ -185,7 +184,7 @@ void CodeInfo::Dump(VariableIndentationOutputStream* vios, if (verbose) { for (size_t i = 0; i < GetNumberOfStackMaps(); ++i) { StackMap stack_map = GetStackMapAt(i); - stack_map.Dump(vios, *this, method_info, code_offset, num_dex_registers, instruction_set); + stack_map.Dump(vios, *this, method_info, code_offset, instruction_set); } } } @@ -194,7 +193,6 @@ void StackMap::Dump(VariableIndentationOutputStream* vios, const CodeInfo& code_info, const MethodInfo& method_info, uint32_t code_offset, - uint16_t number_of_dex_registers, InstructionSet instruction_set) const { const uint32_t pc_offset = GetNativePcOffset(instruction_set); vios->Stream() @@ -210,22 +208,18 @@ void StackMap::Dump(VariableIndentationOutputStream* vios, vios->Stream() << stack_mask.LoadBit(e - i - 1); } vios->Stream() << ")\n"; - DumpDexRegisterMap(vios, code_info.GetDexRegisterMapOf(*this, number_of_dex_registers)); + DumpDexRegisterMap(vios, code_info.GetDexRegisterMapOf(*this)); uint32_t depth = code_info.GetInlineDepthOf(*this); for (size_t d = 0; d < depth; d++) { InlineInfo inline_info = code_info.GetInlineInfoAtDepth(*this, d); - // We do not know the length of the dex register maps of inlined frames - // at this level, so we just pass null to `InlineInfo::Dump` to tell - // it not to look at these maps. - inline_info.Dump(vios, code_info, *this, method_info, 0); + inline_info.Dump(vios, code_info, *this, method_info); } } void InlineInfo::Dump(VariableIndentationOutputStream* vios, const CodeInfo& code_info, const StackMap& stack_map, - const MethodInfo& method_info, - uint16_t number_of_dex_registers) const { + const MethodInfo& method_info) const { uint32_t depth = Row() - stack_map.GetInlineInfoIndex(); vios->Stream() << "InlineInfo[" << Row() << "]" @@ -241,10 +235,7 @@ void InlineInfo::Dump(VariableIndentationOutputStream* vios, << ", method_index=" << GetMethodIndex(method_info); } vios->Stream() << ")\n"; - if (number_of_dex_registers != 0) { - uint16_t vregs = number_of_dex_registers; - DumpDexRegisterMap(vios, code_info.GetDexRegisterMapAtDepth(depth, stack_map, vregs)); - } + DumpDexRegisterMap(vios, code_info.GetDexRegisterMapAtDepth(depth, stack_map)); } } // namespace art diff --git a/runtime/stack_map.h b/runtime/stack_map.h index ff70b6c759..53f80e5203 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -75,7 +75,7 @@ class DexRegisterMap { size_t size() const { return count_; } - bool IsValid() const { return count_ != 0; } + bool empty() const { return count_ == 0; } DexRegisterLocation Get(size_t index) const { DCHECK_LT(index, count_); @@ -194,7 +194,6 @@ class StackMap : public BitTable<7>::Accessor { const CodeInfo& code_info, const MethodInfo& method_info, uint32_t code_offset, - uint16_t number_of_dex_registers, InstructionSet instruction_set) const; }; @@ -234,8 +233,7 @@ class InlineInfo : public BitTable<6>::Accessor { void Dump(VariableIndentationOutputStream* vios, const CodeInfo& info, const StackMap& stack_map, - const MethodInfo& method_info, - uint16_t number_of_dex_registers) const; + const MethodInfo& method_info) const; }; class InvokeInfo : public BitTable<3>::Accessor { @@ -358,8 +356,7 @@ class CodeInfo { return InvokeInfo(&invoke_infos_, index); } - ALWAYS_INLINE DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, - size_t vregs ATTRIBUTE_UNUSED = 0) const { + ALWAYS_INLINE DexRegisterMap GetDexRegisterMapOf(StackMap stack_map) const { if (stack_map.HasDexRegisterMap()) { DexRegisterMap map(number_of_dex_registers_, DexRegisterLocation::Invalid()); DecodeDexRegisterMap(stack_map.Row(), /* first_dex_register */ 0, &map); @@ -368,9 +365,7 @@ class CodeInfo { return DexRegisterMap(0, DexRegisterLocation::None()); } - ALWAYS_INLINE DexRegisterMap GetDexRegisterMapAtDepth(uint8_t depth, - StackMap stack_map, - size_t vregs ATTRIBUTE_UNUSED = 0) const { + ALWAYS_INLINE DexRegisterMap GetDexRegisterMapAtDepth(uint8_t depth, StackMap stack_map) const { if (stack_map.HasDexRegisterMap()) { // The register counts are commutative and include all outer levels. // This allows us to determine the range [first, last) in just two lookups. @@ -481,7 +476,6 @@ class CodeInfo { // `code_offset` is the (absolute) native PC of the compiled method. void Dump(VariableIndentationOutputStream* vios, uint32_t code_offset, - uint16_t number_of_dex_registers, bool verbose, InstructionSet instruction_set, const MethodInfo& method_info) const; diff --git a/runtime/thread.cc b/runtime/thread.cc index b59606a06b..bb4f186c95 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -3656,8 +3656,7 @@ class ReferenceMapVisitor : public StackVisitor { RootVisitor& _visitor) : number_of_dex_registers(method->DexInstructionData().RegistersSize()), code_info(_code_info), - dex_register_map(code_info.GetDexRegisterMapOf(map, - number_of_dex_registers)), + dex_register_map(code_info.GetDexRegisterMapOf(map)), visitor(_visitor) { } -- GitLab From f75613cb7c5cda6a4603ab7041d6f98ec62a80cd Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 5 Jun 2018 12:51:04 +0100 Subject: [PATCH 545/749] Keep objects for Integer.valueOf() intrinsic alive. Keep boot image objects referenced by the intrinsic alive through boot image roots, so that they can never be dead as the intrinsic would still be able to resurrect them later. Note that currently the GC considers all boot image objects live forever. That risks leaking memory and should be fixed. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Bug: 71526895 Change-Id: Iec25cc27e9c5c4bd3c5711991e3111bfb19ef182 --- compiler/optimizing/intrinsics.cc | 45 +++++++--------- dex2oat/linker/image_writer.cc | 87 ++++++++++++++++++++++++------- dex2oat/linker/image_writer.h | 2 + runtime/class_linker.cc | 8 ++- runtime/image.cc | 2 +- runtime/image.h | 12 +++-- runtime/runtime.cc | 1 + 7 files changed, 104 insertions(+), 53 deletions(-) diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 056f533398..02f736d775 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -272,34 +272,33 @@ IntrinsicVisitor::IntegerValueOfInfo IntrinsicVisitor::ComputeIntegerValueOfInfo ClassLinker* class_linker = runtime->GetClassLinker(); gc::Heap* heap = runtime->GetHeap(); IntegerValueOfInfo info; - info.integer_cache = - class_linker->FindSystemClass(self, "Ljava/lang/Integer$IntegerCache;").Ptr(); - if (info.integer_cache == nullptr) { - self->ClearException(); + info.integer_cache = class_linker->LookupClass(self, + "Ljava/lang/Integer$IntegerCache;", + /* class_loader */ nullptr).Ptr(); + if (info.integer_cache == nullptr || !info.integer_cache->IsInitialized()) { + // Optimization only works if the class is initialized. return info; } - if (!heap->ObjectIsInBootImageSpace(info.integer_cache) || !info.integer_cache->IsInitialized()) { - // Optimization only works if the class is initialized and in the boot image. + if (!heap->ObjectIsInBootImageSpace(info.integer_cache)) { + // Optimization only works if the class is in the boot image. + // TODO: Implement the intrinsic for boot image compilation. return info; } - info.integer = class_linker->FindSystemClass(self, "Ljava/lang/Integer;").Ptr(); - if (info.integer == nullptr) { - self->ClearException(); - return info; - } - if (!heap->ObjectIsInBootImageSpace(info.integer) || !info.integer->IsInitialized()) { - // Optimization only works if the class is initialized and in the boot image. + info.integer = + class_linker->LookupClass(self, "Ljava/lang/Integer;", /* class_loader */ nullptr).Ptr(); + DCHECK(info.integer != nullptr); + DCHECK(info.integer->IsInitialized()); // Must be initialized since IntegerCache is initialized. + if (!heap->ObjectIsInBootImageSpace(info.integer)) { + // Optimization only works if the class is in the boot image. return info; } ArtField* field = info.integer_cache->FindDeclaredStaticField("cache", "[Ljava/lang/Integer;"); - if (field == nullptr) { - return info; - } + CHECK(field != nullptr); info.cache = static_cast*>( field->GetObject(info.integer_cache).Ptr()); if (info.cache == nullptr) { - return info; + return info; // Did someone mess up the IntegerCache using reflection? } if (!heap->ObjectIsInBootImageSpace(info.cache)) { @@ -308,21 +307,15 @@ IntrinsicVisitor::IntegerValueOfInfo IntrinsicVisitor::ComputeIntegerValueOfInfo } field = info.integer->FindDeclaredInstanceField("value", "I"); - if (field == nullptr) { - return info; - } + CHECK(field != nullptr); info.value_offset = field->GetOffset().Int32Value(); field = info.integer_cache->FindDeclaredStaticField("low", "I"); - if (field == nullptr) { - return info; - } + CHECK(field != nullptr); info.low = field->GetInt(info.integer_cache); field = info.integer_cache->FindDeclaredStaticField("high", "I"); - if (field == nullptr) { - return info; - } + CHECK(field != nullptr); info.high = field->GetInt(info.integer_cache); DCHECK_EQ(info.cache->GetLength(), info.high - info.low + 1); diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index dc0709013c..f1cb068a38 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -1261,15 +1261,8 @@ mirror::String* ImageWriter::FindInternedString(mirror::String* string) { return nullptr; } - -ObjectArray* ImageWriter::CreateImageRoots(size_t oat_index) const { - Runtime* runtime = Runtime::Current(); - ClassLinker* class_linker = runtime->GetClassLinker(); - Thread* self = Thread::Current(); - StackHandleScope<3> hs(self); - Handle object_array_class(hs.NewHandle( - class_linker->FindSystemClass(self, "[Ljava/lang/Object;"))); - +ObjPtr> ImageWriter::CollectDexCaches(Thread* self, + size_t oat_index) const { std::unordered_set image_dex_files; for (auto& pair : dex_file_oat_index_map_) { const DexFile* image_dex_file = pair.first; @@ -1284,6 +1277,7 @@ ObjectArray* ImageWriter::CreateImageRoots(size_t oat_index) const { // ObjectArray, we lock the dex lock twice, first to get the number // of dex caches first and then lock it again to copy the dex // caches. We check that the number of dex caches does not change. + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); size_t dex_cache_count = 0; { ReaderMutexLock mu(self, *Locks::dex_lock_); @@ -1300,8 +1294,8 @@ ObjectArray* ImageWriter::CreateImageRoots(size_t oat_index) const { } } } - Handle> dex_caches( - hs.NewHandle(ObjectArray::Alloc(self, object_array_class.Get(), dex_cache_count))); + ObjPtr> dex_caches = ObjectArray::Alloc( + self, GetClassRoot>(class_linker), dex_cache_count); CHECK(dex_caches != nullptr) << "Failed to allocate a dex cache array."; { ReaderMutexLock mu(self, *Locks::dex_lock_); @@ -1335,11 +1329,62 @@ ObjectArray* ImageWriter::CreateImageRoots(size_t oat_index) const { } } } + return dex_caches; +} + +static ObjPtr> LookupIntegerCache(Thread* self, + ClassLinker* class_linker) + REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr integer_cache_class = class_linker->LookupClass( + self, "Ljava/lang/Integer$IntegerCache;", /* class_linker */ nullptr); + if (integer_cache_class == nullptr || !integer_cache_class->IsInitialized()) { + return nullptr; + } + ArtField* cache_field = + integer_cache_class->FindDeclaredStaticField("cache", "[Ljava/lang/Integer;"); + CHECK(cache_field != nullptr); + ObjPtr> integer_cache = + ObjPtr>::DownCast(cache_field->GetObject(integer_cache_class)); + CHECK(integer_cache != nullptr); + return integer_cache; +} + +static ObjPtr> CollectBootImageLiveObjects( + Thread* self, + ClassLinker* class_linker) REQUIRES_SHARED(Locks::mutator_lock_) { + // The objects used for the Integer.valueOf() intrinsic must remain live even if references + // to them are removed using reflection. Image roots are not accessible through reflection, + // so the array we construct here shall keep them alive. + StackHandleScope<1> hs(self); + Handle> integer_cache = + hs.NewHandle(LookupIntegerCache(self, class_linker)); + size_t live_objects_size = + (integer_cache != nullptr) ? (/* cache */ 1u + integer_cache->GetLength()) : 0u; + ObjPtr> live_objects = ObjectArray::Alloc( + self, GetClassRoot>(class_linker), live_objects_size); + int32_t index = 0; + if (integer_cache != nullptr) { + live_objects->Set(index++, integer_cache.Get()); + for (int32_t i = 0, length = integer_cache->GetLength(); i != length; ++i) { + live_objects->Set(index++, integer_cache->Get(i)); + } + } + CHECK_EQ(index, live_objects->GetLength()); + return live_objects; +} + +ObjectArray* ImageWriter::CreateImageRoots(size_t oat_index) const { + Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + Thread* self = Thread::Current(); + StackHandleScope<2> hs(self); + + Handle> dex_caches(hs.NewHandle(CollectDexCaches(self, oat_index))); // build an Object[] of the roots needed to restore the runtime int32_t image_roots_size = ImageHeader::NumberOfImageRoots(compile_app_image_); - auto image_roots(hs.NewHandle( - ObjectArray::Alloc(self, object_array_class.Get(), image_roots_size))); + Handle> image_roots(hs.NewHandle(ObjectArray::Alloc( + self, GetClassRoot>(class_linker), image_roots_size))); image_roots->Set(ImageHeader::kDexCaches, dex_caches.Get()); image_roots->Set(ImageHeader::kClassRoots, class_linker->GetClassRoots()); image_roots->Set(ImageHeader::kOomeWhenThrowingException, @@ -1350,10 +1395,16 @@ ObjectArray* ImageWriter::CreateImageRoots(size_t oat_index) const { runtime->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow()); image_roots->Set(ImageHeader::kNoClassDefFoundError, runtime->GetPreAllocatedNoClassDefFoundError()); - // image_roots[ImageHeader::kClassLoader] will be set later for app image. - static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax, - "Class loader should be the last image root."); - for (int32_t i = 0; i < ImageHeader::kImageRootsMax - 1; ++i) { + if (!compile_app_image_) { + ObjPtr> boot_image_live_objects = + CollectBootImageLiveObjects(self, class_linker); + image_roots->Set(ImageHeader::kBootImageLiveObjects, boot_image_live_objects); + } + for (int32_t i = 0, num = ImageHeader::NumberOfImageRoots(compile_app_image_); i != num; ++i) { + if (compile_app_image_ && i == ImageHeader::kAppImageClassLoader) { + // image_roots[ImageHeader::kAppImageClassLoader] will be set later for app image. + continue; + } CHECK(image_roots->Get(i) != nullptr); } return image_roots.Get(); @@ -1781,7 +1832,7 @@ void ImageWriter::CalculateNewObjectOffsets() { CHECK_EQ(class_loaders_.size(), 1u); CHECK_EQ(image_roots.size(), 1u); CHECK(*class_loaders_.begin() != nullptr); - image_roots[0]->Set(ImageHeader::kClassLoader, *class_loaders_.begin()); + image_roots[0]->Set(ImageHeader::kAppImageClassLoader, *class_loaders_.begin()); } // Verify that all objects have assigned image bin slots. diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h index 960d698689..c282a2ade8 100644 --- a/dex2oat/linker/image_writer.h +++ b/dex2oat/linker/image_writer.h @@ -450,6 +450,8 @@ class ImageWriter FINAL { REQUIRES_SHARED(Locks::mutator_lock_); void CreateHeader(size_t oat_index) REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr> CollectDexCaches(Thread* self, size_t oat_index) const + REQUIRES_SHARED(Locks::mutator_lock_); mirror::ObjectArray* CreateImageRoots(size_t oat_index) const REQUIRES_SHARED(Locks::mutator_lock_); void CalculateObjectBinSlots(mirror::Object* obj) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index be636d80a8..45332c8a1a 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -990,8 +990,7 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { class_roots_ = GcRoot>( ObjPtr>::DownCast(MakeObjPtr( spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)))); - DCHECK_EQ(GetClassRoot(ClassRoot::kJavaLangClass, this)->GetClassFlags(), - mirror::kClassFlagClass); + DCHECK_EQ(GetClassRoot(this)->GetClassFlags(), mirror::kClassFlagClass); ObjPtr java_lang_Object = GetClassRoot(this); java_lang_Object->SetObjectSize(sizeof(mirror::Object)); @@ -1610,10 +1609,9 @@ bool ClassLinker::AddImageSpace( hs.NewHandle(dex_caches_object->AsObjectArray())); Handle> class_roots(hs.NewHandle( header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray())); - static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax, - "Class loader should be the last image root."); MutableHandle image_class_loader(hs.NewHandle( - app_image ? header.GetImageRoot(ImageHeader::kClassLoader)->AsClassLoader() : nullptr)); + app_image ? header.GetImageRoot(ImageHeader::kAppImageClassLoader)->AsClassLoader() + : nullptr)); DCHECK(class_roots != nullptr); if (class_roots->GetLength() != static_cast(ClassRoot::kMax)) { *error_msg = StringPrintf("Expected %d class roots but got %d", diff --git a/runtime/image.cc b/runtime/image.cc index 17fc664bd7..7819c0bc00 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -26,7 +26,7 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '1', '\0' }; // Pre-allocated Throwables. +const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '2', '\0' }; // Boot image live objects. ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/image.h b/runtime/image.h index c6fc052a60..c1cde0a74a 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -211,8 +211,12 @@ class PACKED(4) ImageHeader { kOomeWhenThrowingOome, // Pre-allocated OOME when throwing OOME. kOomeWhenHandlingStackOverflow, // Pre-allocated OOME when handling StackOverflowError. kNoClassDefFoundError, // Pre-allocated NoClassDefFoundError. - kClassLoader, // App image only. + kSpecialRoots, // Different for boot image and app image, see aliases below. kImageRootsMax, + + // Aliases. + kAppImageClassLoader = kSpecialRoots, // The class loader used to build the app image. + kBootImageLiveObjects = kSpecialRoots, // Array of boot image objects that must be kept live. }; enum ImageSections { @@ -229,8 +233,10 @@ class PACKED(4) ImageHeader { kSectionCount, // Number of elements in enum. }; - static size_t NumberOfImageRoots(bool app_image) { - return app_image ? kImageRootsMax : kImageRootsMax - 1u; + static size_t NumberOfImageRoots(bool app_image ATTRIBUTE_UNUSED) { + // At the moment, boot image and app image have the same number of roots, + // though the meaning of the kSpecialRoots is different. + return kImageRootsMax; } ArtMethod* GetImageMethod(ImageMethod index) const; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 1e327fc8ed..b4dfc1195d 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -2045,6 +2045,7 @@ void Runtime::VisitNonThreadRoots(RootVisitor* visitor) { pre_allocated_OutOfMemoryError_when_handling_stack_overflow_ .VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); pre_allocated_NoClassDefFoundError_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); + VisitImageRoots(visitor); verifier::MethodVerifier::VisitStaticRoots(visitor); VisitTransactionRoots(visitor); } -- GitLab From 87547c967d58a465737375af0b5847d1085da6d3 Mon Sep 17 00:00:00 2001 From: Tamas Kenez Date: Thu, 7 Jun 2018 15:39:59 +0200 Subject: [PATCH 546/749] ART-tests: Remove DX-dependency from 565-checker-doublenegbitwise This test depended on DX code patterns like using xor-~0 instead of bitwise-not. The CL moves all java code to Smali and enables D8. Since all the test cases in the Java source had already existed as Smali test, too (with slightly different checks and different code then generated by DX) the names of the test cases moved from the Java source has been appended with 'V2'. Test: art/test.py -b --host -r -t 565-checker-doublenegbitwise Tested locally with --target and --gcstress. Bug: 65168732 Change-Id: I2e392c25f7a065a9dccd389866c63481c05eb501 --- test/565-checker-doublenegbitwise/build | 20 - .../smali/SmaliTests.smali | 588 ++++++++++++++++++ .../src/Main.java | 301 +-------- 3 files changed, 597 insertions(+), 312 deletions(-) delete mode 100755 test/565-checker-doublenegbitwise/build diff --git a/test/565-checker-doublenegbitwise/build b/test/565-checker-doublenegbitwise/build deleted file mode 100755 index 10ffcc537d..0000000000 --- a/test/565-checker-doublenegbitwise/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/565-checker-doublenegbitwise/smali/SmaliTests.smali b/test/565-checker-doublenegbitwise/smali/SmaliTests.smali index 2e0802276e..ce691549ce 100644 --- a/test/565-checker-doublenegbitwise/smali/SmaliTests.smali +++ b/test/565-checker-doublenegbitwise/smali/SmaliTests.smali @@ -403,3 +403,591 @@ sput-boolean v0, LSmaliTests;->doThrow:Z return-void .end method + + +# Test transformation of Not/Not/And into Or/Not. + +# Note: before the instruction_simplifier pass, Xor's are used instead of +# Not's (the simplification happens during the same pass). +## CHECK-START: int SmaliTests.$opt$noinline$andToOrV2(int, int) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant -1 +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: <> And [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests.$opt$noinline$andToOrV2(int, int) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> Or [<>,<>] +## CHECK-DAG: <> Not [<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests.$opt$noinline$andToOrV2(int, int) instruction_simplifier (after) +## CHECK-DAG: Not +## CHECK-NOT: Not + +## CHECK-START: int SmaliTests.$opt$noinline$andToOrV2(int, int) instruction_simplifier (after) +## CHECK-NOT: And + +# Original java source: +# +# public static int $opt$noinline$andToOr(int a, int b) { +# if (doThrow) throw new Error(); +# return ~a & ~b; +# } + +.method public static $opt$noinline$andToOrV2(II)I + .registers 4 + .param p0, "a" # I + .param p1, "b" # I + + .prologue + .line 85 + sget-boolean v0, LMain;->doThrow:Z + + if-eqz v0, :cond_a + + new-instance v0, Ljava/lang/Error; + + invoke-direct {v0}, Ljava/lang/Error;->()V + + throw v0 + + .line 86 + :cond_a + xor-int/lit8 v0, p0, -0x1 + + xor-int/lit8 v1, p1, -0x1 + + and-int/2addr v0, v1 + + return v0 +.end method + + +# Test transformation of Not/Not/And into Or/Not for boolean negations. +# Note that the graph before this instruction simplification pass does not +# contain `HBooleanNot` instructions. This is because this transformation +# follows the optimization of `HSelect` to `HBooleanNot` occurring in the +# same pass. + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOrV2(boolean, boolean) instruction_simplifier$after_gvn (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> Select [<>,<>,<>] +## CHECK-DAG: <> Select [<>,<>,<>] +## CHECK-DAG: <> And [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOrV2(boolean, boolean) instruction_simplifier$after_gvn (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> Or [<>,<>] +## CHECK-DAG: <> BooleanNot [<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOrV2(boolean, boolean) instruction_simplifier$after_bce (after) +## CHECK-DAG: BooleanNot +## CHECK-NOT: BooleanNot + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOrV2(boolean, boolean) instruction_simplifier$after_bce (after) +## CHECK-NOT: And + +# Original java source: +# +# public static boolean $opt$noinline$booleanAndToOr(boolean a, boolean b) { +# if (doThrow) throw new Error(); +# return !a & !b; +# } + +.method public static $opt$noinline$booleanAndToOrV2(ZZ)Z + .registers 5 + .param p0, "a" # Z + .param p1, "b" # Z + + .prologue + const/4 v0, 0x1 + + const/4 v1, 0x0 + + .line 122 + sget-boolean v2, LMain;->doThrow:Z + + if-eqz v2, :cond_c + + new-instance v0, Ljava/lang/Error; + + invoke-direct {v0}, Ljava/lang/Error;->()V + + throw v0 + + .line 123 + :cond_c + if-nez p0, :cond_13 + + move v2, v0 + + :goto_f + if-nez p1, :cond_15 + + :goto_11 + and-int/2addr v0, v2 + + return v0 + + :cond_13 + move v2, v1 + + goto :goto_f + + :cond_15 + move v0, v1 + + goto :goto_11 +.end method + + +# Test transformation of Not/Not/Or into And/Not. + +# See note above. +# The second Xor has its arguments reversed for no obvious reason. +## CHECK-START: long SmaliTests.$opt$noinline$orToAndV2(long, long) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> LongConstant -1 +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: <> Or [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: long SmaliTests.$opt$noinline$orToAndV2(long, long) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> And [<>,<>] +## CHECK-DAG: <> Not [<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: long SmaliTests.$opt$noinline$orToAndV2(long, long) instruction_simplifier (after) +## CHECK-DAG: Not +## CHECK-NOT: Not + +## CHECK-START: long SmaliTests.$opt$noinline$orToAndV2(long, long) instruction_simplifier (after) +## CHECK-NOT: Or + +# Original java source: +# +# public static long $opt$noinline$orToAnd(long a, long b) { +# if (doThrow) throw new Error(); +# return ~a | ~b; +# } + +.method public static $opt$noinline$orToAndV2(JJ)J + .registers 8 + .param p0, "a" # J + .param p2, "b" # J + + .prologue + const-wide/16 v2, -0x1 + + .line 156 + sget-boolean v0, LMain;->doThrow:Z + + if-eqz v0, :cond_c + + new-instance v0, Ljava/lang/Error; + + invoke-direct {v0}, Ljava/lang/Error;->()V + + throw v0 + + .line 157 + :cond_c + xor-long v0, p0, v2 + + xor-long/2addr v2, p2 + + or-long/2addr v0, v2 + + return-wide v0 +.end method + +# Test transformation of Not/Not/Or into Or/And for boolean negations. +# Note that the graph before this instruction simplification pass does not +# contain `HBooleanNot` instructions. This is because this transformation +# follows the optimization of `HSelect` to `HBooleanNot` occurring in the +# same pass. + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAndV2(boolean, boolean) instruction_simplifier$after_gvn (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> Select [<>,<>,<>] +## CHECK-DAG: <> Select [<>,<>,<>] +## CHECK-DAG: <> Or [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAndV2(boolean, boolean) instruction_simplifier$after_gvn (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> And [<>,<>] +## CHECK-DAG: <> BooleanNot [<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAndV2(boolean, boolean) instruction_simplifier$after_bce (after) +## CHECK-DAG: BooleanNot +## CHECK-NOT: BooleanNot + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAndV2(boolean, boolean) instruction_simplifier$after_bce (after) +## CHECK-NOT: Or + +# Original java source: +# +# public static boolean $opt$noinline$booleanOrToAnd(boolean a, boolean b) { +# if (doThrow) throw new Error(); +# return !a | !b; +# } + +.method public static $opt$noinline$booleanOrToAndV2(ZZ)Z + .registers 5 + .param p0, "a" # Z + .param p1, "b" # Z + + .prologue + const/4 v0, 0x1 + + const/4 v1, 0x0 + + .line 193 + sget-boolean v2, LMain;->doThrow:Z + + if-eqz v2, :cond_c + + new-instance v0, Ljava/lang/Error; + + invoke-direct {v0}, Ljava/lang/Error;->()V + + throw v0 + + .line 194 + :cond_c + if-nez p0, :cond_13 + + move v2, v0 + + :goto_f + if-nez p1, :cond_15 + + :goto_11 + or-int/2addr v0, v2 + + return v0 + + :cond_13 + move v2, v1 + + goto :goto_f + + :cond_15 + move v0, v1 + + goto :goto_11 +.end method + + +# Test that the transformation copes with inputs being separated from the +# bitwise operations. +# This is a regression test. The initial logic was inserting the new bitwise +# operation incorrectly. + +## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAwayV2(int, int) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> IntConstant -1 +## CHECK-DAG: <> Add [<>,<>] +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: <> Add [<>,<>] +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: <> Or [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAwayV2(int, int) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> Add [<>,<>] +## CHECK-DAG: <> Add [<>,<>] +## CHECK-DAG: <> And [<>,<>] +## CHECK-DAG: <> Not [<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAwayV2(int, int) instruction_simplifier (after) +## CHECK-DAG: Not +## CHECK-NOT: Not + +## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAwayV2(int, int) instruction_simplifier (after) +## CHECK-NOT: Or + +# Original java source: +# +# public static int $opt$noinline$regressInputsAway(int a, int b) { +# if (doThrow) throw new Error(); +# int a1 = a + 1; +# int not_a1 = ~a1; +# int b1 = b + 1; +# int not_b1 = ~b1; +# return not_a1 | not_b1; +# } + +.method public static $opt$noinline$regressInputsAwayV2(II)I + .registers 7 + .param p0, "a" # I + .param p1, "b" # I + + .prologue + .line 234 + sget-boolean v4, LMain;->doThrow:Z + + if-eqz v4, :cond_a + + new-instance v4, Ljava/lang/Error; + + invoke-direct {v4}, Ljava/lang/Error;->()V + + throw v4 + + .line 235 + :cond_a + add-int/lit8 v0, p0, 0x1 + + .line 236 + .local v0, "a1":I + xor-int/lit8 v2, v0, -0x1 + + .line 237 + .local v2, "not_a1":I + add-int/lit8 v1, p1, 0x1 + + .line 238 + .local v1, "b1":I + xor-int/lit8 v3, v1, -0x1 + + .line 239 + .local v3, "not_b1":I + or-int v4, v2, v3 + + return v4 +.end method + + +# Test transformation of Not/Not/Xor into Xor. + +# See first note above. +## CHECK-START: int SmaliTests.$opt$noinline$notXorToXorV2(int, int) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant -1 +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests.$opt$noinline$notXorToXorV2(int, int) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests.$opt$noinline$notXorToXorV2(int, int) instruction_simplifier (after) +## CHECK-NOT: Not + +# Original java source: +# +# public static int $opt$noinline$notXorToXor(int a, int b) { +# if (doThrow) throw new Error(); +# return ~a ^ ~b; +# } + +.method public static $opt$noinline$notXorToXorV2(II)I + .registers 4 + .param p0, "a" # I + .param p1, "b" # I + + .prologue + .line 266 + sget-boolean v0, LMain;->doThrow:Z + + if-eqz v0, :cond_a + + new-instance v0, Ljava/lang/Error; + + invoke-direct {v0}, Ljava/lang/Error;->()V + + throw v0 + + .line 267 + :cond_a + xor-int/lit8 v0, p0, -0x1 + + xor-int/lit8 v1, p1, -0x1 + + xor-int/2addr v0, v1 + + return v0 +.end method + + +# Test transformation of Not/Not/Xor into Xor for boolean negations. +# Note that the graph before this instruction simplification pass does not +# contain `HBooleanNot` instructions. This is because this transformation +# follows the optimization of `HSelect` to `HBooleanNot` occurring in the +# same pass. + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanNotXorToXorV2(boolean, boolean) instruction_simplifier$after_gvn (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> Select [<>,<>,<>] +## CHECK-DAG: <> Select [<>,<>,<>] +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanNotXorToXorV2(boolean, boolean) instruction_simplifier$after_gvn (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanNotXorToXorV2(boolean, boolean) instruction_simplifier$after_bce (after) +## CHECK-NOT: BooleanNot + +# Original java source: +# +# public static boolean $opt$noinline$booleanNotXorToXor(boolean a, boolean b) { +# if (doThrow) throw new Error(); +# return !a ^ !b; +# } + +.method public static $opt$noinline$booleanNotXorToXorV2(ZZ)Z + .registers 5 + .param p0, "a" # Z + .param p1, "b" # Z + + .prologue + const/4 v0, 0x1 + + const/4 v1, 0x0 + + .line 298 + sget-boolean v2, LMain;->doThrow:Z + + if-eqz v2, :cond_c + + new-instance v0, Ljava/lang/Error; + + invoke-direct {v0}, Ljava/lang/Error;->()V + + throw v0 + + .line 299 + :cond_c + if-nez p0, :cond_13 + + move v2, v0 + + :goto_f + if-nez p1, :cond_15 + + :goto_11 + xor-int/2addr v0, v2 + + return v0 + + :cond_13 + move v2, v1 + + goto :goto_f + + :cond_15 + move v0, v1 + + goto :goto_11 +.end method + + +# Check that no transformation is done when one Not has multiple uses. + +## CHECK-START: int SmaliTests.$opt$noinline$notMultipleUsesV2(int, int) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant -1 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: <> And [<>,<>] +## CHECK-DAG: <> Xor [<>,<>] +## CHECK-DAG: <> And [<>,<>] +## CHECK-DAG: <> Add [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests.$opt$noinline$notMultipleUsesV2(int, int) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> Not [<>] +## CHECK-DAG: <> And [<>,<>] +## CHECK-DAG: <> Not [<>] +## CHECK-DAG: <> And [<>,<>] +## CHECK-DAG: <> Add [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int SmaliTests.$opt$noinline$notMultipleUsesV2(int, int) instruction_simplifier (after) +## CHECK-NOT: Or + +# Original java source: +# +# public static int $opt$noinline$notMultipleUses(int a, int b) { +# if (doThrow) throw new Error(); +# int tmp = ~b; +# return (tmp & 0x1) + (~a & tmp); +# } + +.method public static $opt$noinline$notMultipleUsesV2(II)I + .registers 5 + .param p0, "a" # I + .param p1, "b" # I + + .prologue + .line 333 + sget-boolean v1, LMain;->doThrow:Z + + if-eqz v1, :cond_a + + new-instance v1, Ljava/lang/Error; + + invoke-direct {v1}, Ljava/lang/Error;->()V + + throw v1 + + .line 334 + :cond_a + xor-int/lit8 v0, p1, -0x1 + + .line 335 + .local v0, "tmp":I + and-int/lit8 v1, v0, 0x1 + + xor-int/lit8 v2, p0, -0x1 + + and-int/2addr v2, v0 + + add-int/2addr v1, v2 + + return v1 +.end method diff --git a/test/565-checker-doublenegbitwise/src/Main.java b/test/565-checker-doublenegbitwise/src/Main.java index e36a2bab40..5121569632 100644 --- a/test/565-checker-doublenegbitwise/src/Main.java +++ b/test/565-checker-doublenegbitwise/src/Main.java @@ -52,305 +52,22 @@ public class Main { } } - /** - * Test transformation of Not/Not/And into Or/Not. - */ - - // Note: before the instruction_simplifier pass, Xor's are used instead of - // Not's (the simplification happens during the same pass). - /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant -1 - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> And [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> Or [<>,<>] - /// CHECK-DAG: <> Not [<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after) - /// CHECK-DAG: Not - /// CHECK-NOT: Not - - /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after) - /// CHECK-NOT: And - - public static int $opt$noinline$andToOr(int a, int b) { - if (doThrow) throw new Error(); - return ~a & ~b; - } - - /** - * Test transformation of Not/Not/And into Or/Not for boolean negations. - * Note that the graph before this instruction simplification pass does not - * contain `HBooleanNot` instructions. This is because this transformation - * follows the optimization of `HSelect` to `HBooleanNot` occurring in the - * same pass. - */ - - /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_gvn (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> And [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_gvn (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> Or [<>,<>] - /// CHECK-DAG: <> BooleanNot [<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_bce (after) - /// CHECK-DAG: BooleanNot - /// CHECK-NOT: BooleanNot - - /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_bce (after) - /// CHECK-NOT: And - - public static boolean $opt$noinline$booleanAndToOr(boolean a, boolean b) { - if (doThrow) throw new Error(); - return !a & !b; - } - - /** - * Test transformation of Not/Not/Or into And/Not. - */ - - // See note above. - // The second Xor has its arguments reversed for no obvious reason. - /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> LongConstant -1 - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> Or [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> And [<>,<>] - /// CHECK-DAG: <> Not [<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after) - /// CHECK-DAG: Not - /// CHECK-NOT: Not - - /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after) - /// CHECK-NOT: Or - - public static long $opt$noinline$orToAnd(long a, long b) { - if (doThrow) throw new Error(); - return ~a | ~b; - } - - /** - * Test transformation of Not/Not/Or into Or/And for boolean negations. - * Note that the graph before this instruction simplification pass does not - * contain `HBooleanNot` instructions. This is because this transformation - * follows the optimization of `HSelect` to `HBooleanNot` occurring in the - * same pass. - */ - - /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_gvn (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> Or [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_gvn (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> And [<>,<>] - /// CHECK-DAG: <> BooleanNot [<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_bce (after) - /// CHECK-DAG: BooleanNot - /// CHECK-NOT: BooleanNot - - /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_bce (after) - /// CHECK-NOT: Or - - public static boolean $opt$noinline$booleanOrToAnd(boolean a, boolean b) { - if (doThrow) throw new Error(); - return !a | !b; - } - - /** - * Test that the transformation copes with inputs being separated from the - * bitwise operations. - * This is a regression test. The initial logic was inserting the new bitwise - * operation incorrectly. - */ - - /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> IntConstant -1 - /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> Or [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: <> And [<>,<>] - /// CHECK-DAG: <> Not [<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after) - /// CHECK-DAG: Not - /// CHECK-NOT: Not - - /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after) - /// CHECK-NOT: Or - - public static int $opt$noinline$regressInputsAway(int a, int b) { - if (doThrow) throw new Error(); - int a1 = a + 1; - int not_a1 = ~a1; - int b1 = b + 1; - int not_b1 = ~b1; - return not_a1 | not_b1; - } - - /** - * Test transformation of Not/Not/Xor into Xor. - */ - - // See first note above. - /// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant -1 - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after) - /// CHECK-NOT: Not - - public static int $opt$noinline$notXorToXor(int a, int b) { - if (doThrow) throw new Error(); - return ~a ^ ~b; - } - - /** - * Test transformation of Not/Not/Xor into Xor for boolean negations. - * Note that the graph before this instruction simplification pass does not - * contain `HBooleanNot` instructions. This is because this transformation - * follows the optimization of `HSelect` to `HBooleanNot` occurring in the - * same pass. - */ - - /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_gvn (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_gvn (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_bce (after) - /// CHECK-NOT: BooleanNot - - public static boolean $opt$noinline$booleanNotXorToXor(boolean a, boolean b) { - if (doThrow) throw new Error(); - return !a ^ !b; - } - - /** - * Check that no transformation is done when one Not has multiple uses. - */ - - /// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant -1 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> And [<>,<>] - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> And [<>,<>] - /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> Not [<>] - /// CHECK-DAG: <> And [<>,<>] - /// CHECK-DAG: <> Not [<>] - /// CHECK-DAG: <> And [<>,<>] - /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after) - /// CHECK-NOT: Or - - public static int $opt$noinline$notMultipleUses(int a, int b) { - if (doThrow) throw new Error(); - int tmp = ~b; - return (tmp & 0x1) + (~a & tmp); - } - - public static void main(String[] args) { - assertIntEquals(~0xff, $opt$noinline$andToOr(0xf, 0xff)); + public static void main(String[] args) throws Exception { + assertIntEquals(~0xff, $noinline$runSmaliTest("$opt$noinline$andToOrV2", int.class, 0xf, 0xff)); assertIntEquals(~0xff, $noinline$runSmaliTest("$opt$noinline$andToOr", int.class, 0xf, 0xff)); - assertEquals(true, $opt$noinline$booleanAndToOr(false, false)); + assertEquals(true, $noinline$runSmaliTest("$opt$noinline$booleanAndToOrV2", boolean.class, false, false)); assertEquals(true, $noinline$runSmaliTest("$opt$noinline$booleanAndToOr", boolean.class, false, false)); - assertLongEquals(~0xf, $opt$noinline$orToAnd(0xf, 0xff)); + assertLongEquals(~0xf, $noinline$runSmaliTest("$opt$noinline$orToAndV2", long.class, 0xfL, 0xffL)); assertLongEquals(~0xf, $noinline$runSmaliTest("$opt$noinline$orToAnd", long.class, 0xfL, 0xffL)); - assertEquals(false, $opt$noinline$booleanOrToAnd(true, true)); + assertEquals(false, $noinline$runSmaliTest("$opt$noinline$booleanOrToAndV2", boolean.class, true, true)); assertEquals(false, $noinline$runSmaliTest("$opt$noinline$booleanOrToAnd", boolean.class, true, true)); - assertIntEquals(-1, $opt$noinline$regressInputsAway(0xf, 0xff)); + assertIntEquals(-1, $noinline$runSmaliTest("$opt$noinline$regressInputsAwayV2", int.class, 0xf, 0xff)); assertIntEquals(-1, $noinline$runSmaliTest("$opt$noinline$regressInputsAway", int.class, 0xf, 0xff)); - assertIntEquals(0xf0, $opt$noinline$notXorToXor(0xf, 0xff)); + assertIntEquals(0xf0, $noinline$runSmaliTest("$opt$noinline$notXorToXorV2", int.class, 0xf, 0xff)); assertIntEquals(0xf0, $noinline$runSmaliTest("$opt$noinline$notXorToXor", int.class, 0xf, 0xff)); - assertEquals(true, $opt$noinline$booleanNotXorToXor(true, false)); + assertEquals(true, $noinline$runSmaliTest("$opt$noinline$booleanNotXorToXorV2", boolean.class, true, false)); assertEquals(true, $noinline$runSmaliTest("$opt$noinline$booleanNotXorToXor", boolean.class, true, false)); - assertIntEquals(~0xff, $opt$noinline$notMultipleUses(0xf, 0xff)); + assertIntEquals(~0xff, $noinline$runSmaliTest("$opt$noinline$notMultipleUsesV2", int.class, 0xf, 0xff)); assertIntEquals(~0xff, $noinline$runSmaliTest("$opt$noinline$notMultipleUses", int.class, 0xf, 0xff)); } } -- GitLab From d80668611e1982d50374da03377542a7863ba8e5 Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Thu, 7 Jun 2018 15:08:04 +0100 Subject: [PATCH 547/749] Add Throwable.UNASSIGNED_STACK to well known fields Replaces use of EmptyArray.STACK_TRACE_ELEMENT with Throwable.UNSASSIGNED_STACK to match changes in Java code. Bug: 35910877 Test: make checkbuild and run CtsLibcoreTestCases Change-Id: Idc14ce8610ec12ce776d87befdbb9a3cbfd9dbee --- runtime/common_throws.cc | 6 +++--- runtime/well_known_classes.cc | 9 +++------ runtime/well_known_classes.h | 3 +-- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index 657a78bd2f..2ffadb3ca2 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -787,7 +787,7 @@ void ThrowStackOverflowError(Thread* self) { // Object stackState; // StackTraceElement[] stackTrace; // Only Throwable has a non-empty constructor: - // this.stackTrace = EmptyArray.STACK_TRACE_ELEMENT; + // this.stackTrace = Throwable.UNASSIGNED_STACK; // fillInStackTrace(); // detailMessage. @@ -822,8 +822,8 @@ void ThrowStackOverflowError(Thread* self) { // stackTrace. ScopedLocalRef stack_trace_elem(env, env->GetStaticObjectField( - WellKnownClasses::libcore_util_EmptyArray, - WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT)); + WellKnownClasses::java_lang_Throwable, + WellKnownClasses::java_lang_Throwable_UNASSIGNED_STACK)); env->SetObjectField(exc.get(), WellKnownClasses::java_lang_Throwable_stackTrace, stack_trace_elem.get()); diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index c64e7bbca1..7b54b2fabc 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -76,7 +76,6 @@ jclass WellKnownClasses::java_util_Collections; jclass WellKnownClasses::java_util_function_Consumer; jclass WellKnownClasses::libcore_reflect_AnnotationFactory; jclass WellKnownClasses::libcore_reflect_AnnotationMember; -jclass WellKnownClasses::libcore_util_EmptyArray; jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk; jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer; @@ -137,6 +136,7 @@ jfieldID WellKnownClasses::java_lang_Throwable_detailMessage; jfieldID WellKnownClasses::java_lang_Throwable_stackTrace; jfieldID WellKnownClasses::java_lang_Throwable_stackState; jfieldID WellKnownClasses::java_lang_Throwable_suppressedExceptions; +jfieldID WellKnownClasses::java_lang_Throwable_UNASSIGNED_STACK; jfieldID WellKnownClasses::java_nio_ByteBuffer_address; jfieldID WellKnownClasses::java_nio_ByteBuffer_hb; jfieldID WellKnownClasses::java_nio_ByteBuffer_isReadOnly; @@ -145,7 +145,6 @@ jfieldID WellKnownClasses::java_nio_ByteBuffer_offset; jfieldID WellKnownClasses::java_nio_DirectByteBuffer_capacity; jfieldID WellKnownClasses::java_nio_DirectByteBuffer_effectiveDirectAddress; jfieldID WellKnownClasses::java_util_Collections_EMPTY_LIST; -jfieldID WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT; jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data; jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_length; jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_offset; @@ -331,7 +330,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_util_function_Consumer = CacheClass(env, "java/util/function/Consumer"); libcore_reflect_AnnotationFactory = CacheClass(env, "libcore/reflect/AnnotationFactory"); libcore_reflect_AnnotationMember = CacheClass(env, "libcore/reflect/AnnotationMember"); - libcore_util_EmptyArray = CacheClass(env, "libcore/util/EmptyArray"); org_apache_harmony_dalvik_ddmc_Chunk = CacheClass(env, "org/apache/harmony/dalvik/ddmc/Chunk"); org_apache_harmony_dalvik_ddmc_DdmServer = CacheClass(env, "org/apache/harmony/dalvik/ddmc/DdmServer"); @@ -385,6 +383,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Throwable_stackTrace = CacheField(env, java_lang_Throwable, false, "stackTrace", "[Ljava/lang/StackTraceElement;"); java_lang_Throwable_stackState = CacheField(env, java_lang_Throwable, false, "backtrace", "Ljava/lang/Object;"); java_lang_Throwable_suppressedExceptions = CacheField(env, java_lang_Throwable, false, "suppressedExceptions", "Ljava/util/List;"); + java_lang_Throwable_UNASSIGNED_STACK = CacheField(env, java_lang_Throwable, true, "UNASSIGNED_STACK", "[Ljava/lang/StackTraceElement;"); java_nio_ByteBuffer_address = CacheField(env, java_nio_ByteBuffer, false, "address", "J"); java_nio_ByteBuffer_hb = CacheField(env, java_nio_ByteBuffer, false, "hb", "[B"); java_nio_ByteBuffer_isReadOnly = CacheField(env, java_nio_ByteBuffer, false, "isReadOnly", "Z"); @@ -393,7 +392,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_nio_DirectByteBuffer_capacity = CacheField(env, java_nio_DirectByteBuffer, false, "capacity", "I"); java_nio_DirectByteBuffer_effectiveDirectAddress = CacheField(env, java_nio_DirectByteBuffer, false, "address", "J"); java_util_Collections_EMPTY_LIST = CacheField(env, java_util_Collections, true, "EMPTY_LIST", "Ljava/util/List;"); - libcore_util_EmptyArray_STACK_TRACE_ELEMENT = CacheField(env, libcore_util_EmptyArray, true, "STACK_TRACE_ELEMENT", "[Ljava/lang/StackTraceElement;"); org_apache_harmony_dalvik_ddmc_Chunk_data = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "data", "[B"); org_apache_harmony_dalvik_ddmc_Chunk_length = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "length", "I"); org_apache_harmony_dalvik_ddmc_Chunk_offset = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "offset", "I"); @@ -462,7 +460,6 @@ void WellKnownClasses::Clear() { java_nio_DirectByteBuffer = nullptr; libcore_reflect_AnnotationFactory = nullptr; libcore_reflect_AnnotationMember = nullptr; - libcore_util_EmptyArray = nullptr; org_apache_harmony_dalvik_ddmc_Chunk = nullptr; org_apache_harmony_dalvik_ddmc_DdmServer = nullptr; @@ -522,6 +519,7 @@ void WellKnownClasses::Clear() { java_lang_Throwable_stackTrace = nullptr; java_lang_Throwable_stackState = nullptr; java_lang_Throwable_suppressedExceptions = nullptr; + java_lang_Throwable_UNASSIGNED_STACK = nullptr; java_nio_ByteBuffer_address = nullptr; java_nio_ByteBuffer_hb = nullptr; java_nio_ByteBuffer_isReadOnly = nullptr; @@ -530,7 +528,6 @@ void WellKnownClasses::Clear() { java_nio_DirectByteBuffer_capacity = nullptr; java_nio_DirectByteBuffer_effectiveDirectAddress = nullptr; java_util_Collections_EMPTY_LIST = nullptr; - libcore_util_EmptyArray_STACK_TRACE_ELEMENT = nullptr; org_apache_harmony_dalvik_ddmc_Chunk_data = nullptr; org_apache_harmony_dalvik_ddmc_Chunk_length = nullptr; org_apache_harmony_dalvik_ddmc_Chunk_offset = nullptr; diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index c81062f594..bed8770c45 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -86,7 +86,6 @@ struct WellKnownClasses { static jclass java_nio_DirectByteBuffer; static jclass libcore_reflect_AnnotationFactory; static jclass libcore_reflect_AnnotationMember; - static jclass libcore_util_EmptyArray; static jclass org_apache_harmony_dalvik_ddmc_Chunk; static jclass org_apache_harmony_dalvik_ddmc_DdmServer; @@ -147,6 +146,7 @@ struct WellKnownClasses { static jfieldID java_lang_Throwable_stackTrace; static jfieldID java_lang_Throwable_stackState; static jfieldID java_lang_Throwable_suppressedExceptions; + static jfieldID java_lang_Throwable_UNASSIGNED_STACK; static jfieldID java_nio_ByteBuffer_address; static jfieldID java_nio_ByteBuffer_hb; static jfieldID java_nio_ByteBuffer_isReadOnly; @@ -156,7 +156,6 @@ struct WellKnownClasses { static jfieldID java_nio_DirectByteBuffer_effectiveDirectAddress; static jfieldID java_util_Collections_EMPTY_LIST; - static jfieldID libcore_util_EmptyArray_STACK_TRACE_ELEMENT; static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_data; static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_length; static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_offset; -- GitLab From 4459baee5d3042310cd2de94b7801f90ccf2504b Mon Sep 17 00:00:00 2001 From: Tamas Kenez Date: Thu, 7 Jun 2018 16:42:59 +0200 Subject: [PATCH 548/749] ART-tests: Remove DX-dependency from 551-checker-shifter-operand The merging of shift and add operations into DataProcWithShifterOp in the test case $opt$validateShiftLongAsserts was dependent on register-allocation patterns. With D8 sometimes the merge did not take place because the result of the shift was not overwritten by the subsequent add. This CL separates the intermittent method calls and the arithmetic operations so the environmental uses of the shift result does not block merging. Also, D8 is enabled. Test: art/test.py -r -b --target -t 551-checker-shifter-operand Tested locally with --host and --gcstress. Bug: 65168732 Change-Id: I3a8e71f3530211878879faae068acb7c7957ae19 --- test/551-checker-shifter-operand/build | 20 --- .../551-checker-shifter-operand/src/Main.java | 136 ++++++++++++------ 2 files changed, 92 insertions(+), 64 deletions(-) delete mode 100644 test/551-checker-shifter-operand/build diff --git a/test/551-checker-shifter-operand/build b/test/551-checker-shifter-operand/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/551-checker-shifter-operand/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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 -# -# 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java index b3e4a60e9a..8311b60df7 100644 --- a/test/551-checker-shifter-operand/src/Main.java +++ b/test/551-checker-shifter-operand/src/Main.java @@ -896,7 +896,7 @@ public class Main { } // Each test line below should see one merge. - /// CHECK-START-ARM: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm (after) + /// CHECK-START-ARM: long[] Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm (after) /// CHECK: DataProcWithShifterOp /// CHECK: DataProcWithShifterOp /// CHECK: DataProcWithShifterOp @@ -933,7 +933,7 @@ public class Main { /// CHECK-NOT: DataProcWithShifterOp // On ARM shifts by 1 are not merged. - /// CHECK-START-ARM: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm (after) + /// CHECK-START-ARM: long[] Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm (after) /// CHECK: Shl /// CHECK-NOT: Shl /// CHECK: Shr @@ -941,7 +941,7 @@ public class Main { /// CHECK: UShr /// CHECK-NOT: UShr - /// CHECK-START-ARM64: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm64 (after) + /// CHECK-START-ARM64: long[] Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm64 (after) /// CHECK: DataProcWithShifterOp /// CHECK: DataProcWithShifterOp /// CHECK: DataProcWithShifterOp @@ -980,50 +980,98 @@ public class Main { /// CHECK: DataProcWithShifterOp /// CHECK-NOT: DataProcWithShifterOp - /// CHECK-START-ARM64: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm64 (after) + /// CHECK-START-ARM64: long[] Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm64 (after) /// CHECK-NOT: Shl /// CHECK-NOT: Shr /// CHECK-NOT: UShr - public static void $opt$validateShiftLong(long a, long b) { - assertLongEquals(a + $noinline$LongShl(b, 1), a + (b << 1)); - assertLongEquals(a + $noinline$LongShl(b, 6), a + (b << 6)); - assertLongEquals(a + $noinline$LongShl(b, 7), a + (b << 7)); - assertLongEquals(a + $noinline$LongShl(b, 8), a + (b << 8)); - assertLongEquals(a + $noinline$LongShl(b, 14), a + (b << 14)); - assertLongEquals(a + $noinline$LongShl(b, 15), a + (b << 15)); - assertLongEquals(a + $noinline$LongShl(b, 16), a + (b << 16)); - assertLongEquals(a + $noinline$LongShl(b, 30), a + (b << 30)); - assertLongEquals(a + $noinline$LongShl(b, 31), a + (b << 31)); - assertLongEquals(a + $noinline$LongShl(b, 32), a + (b << 32)); - assertLongEquals(a + $noinline$LongShl(b, 62), a + (b << 62)); - assertLongEquals(a + $noinline$LongShl(b, 63), a + (b << 63)); - - assertLongEquals(a - $noinline$LongShr(b, 1), a - (b >> 1)); - assertLongEquals(a - $noinline$LongShr(b, 6), a - (b >> 6)); - assertLongEquals(a - $noinline$LongShr(b, 7), a - (b >> 7)); - assertLongEquals(a - $noinline$LongShr(b, 8), a - (b >> 8)); - assertLongEquals(a - $noinline$LongShr(b, 14), a - (b >> 14)); - assertLongEquals(a - $noinline$LongShr(b, 15), a - (b >> 15)); - assertLongEquals(a - $noinline$LongShr(b, 16), a - (b >> 16)); - assertLongEquals(a - $noinline$LongShr(b, 30), a - (b >> 30)); - assertLongEquals(a - $noinline$LongShr(b, 31), a - (b >> 31)); - assertLongEquals(a - $noinline$LongShr(b, 32), a - (b >> 32)); - assertLongEquals(a - $noinline$LongShr(b, 62), a - (b >> 62)); - assertLongEquals(a - $noinline$LongShr(b, 63), a - (b >> 63)); - - assertLongEquals(a ^ $noinline$LongUshr(b, 1), a ^ (b >>> 1)); - assertLongEquals(a ^ $noinline$LongUshr(b, 6), a ^ (b >>> 6)); - assertLongEquals(a ^ $noinline$LongUshr(b, 7), a ^ (b >>> 7)); - assertLongEquals(a ^ $noinline$LongUshr(b, 8), a ^ (b >>> 8)); - assertLongEquals(a ^ $noinline$LongUshr(b, 14), a ^ (b >>> 14)); - assertLongEquals(a ^ $noinline$LongUshr(b, 15), a ^ (b >>> 15)); - assertLongEquals(a ^ $noinline$LongUshr(b, 16), a ^ (b >>> 16)); - assertLongEquals(a ^ $noinline$LongUshr(b, 30), a ^ (b >>> 30)); - assertLongEquals(a ^ $noinline$LongUshr(b, 31), a ^ (b >>> 31)); - assertLongEquals(a ^ $noinline$LongUshr(b, 32), a ^ (b >>> 32)); - assertLongEquals(a ^ $noinline$LongUshr(b, 62), a ^ (b >>> 62)); - assertLongEquals(a ^ $noinline$LongUshr(b, 63), a ^ (b >>> 63)); + public static long[] $opt$validateShiftLong(long a, long b) { + long[] results = new long[36]; + + results[0] = a + (b << 1); + results[1] = a + (b << 6); + results[2] = a + (b << 7); + results[3] = a + (b << 8); + results[4] = a + (b << 14); + results[5] = a + (b << 15); + results[6] = a + (b << 16); + results[7] = a + (b << 30); + results[8] = a + (b << 31); + results[9] = a + (b << 32); + results[10] = a + (b << 62); + results[11] = a + (b << 63); + + results[12] = a - (b >> 1); + results[13] = a - (b >> 6); + results[14] = a - (b >> 7); + results[15] = a - (b >> 8); + results[16] = a - (b >> 14); + results[17] = a - (b >> 15); + results[18] = a - (b >> 16); + results[19] = a - (b >> 30); + results[20] = a - (b >> 31); + results[21] = a - (b >> 32); + results[22] = a - (b >> 62); + results[23] = a - (b >> 63); + + results[24] = a ^ (b >>> 1); + results[25] = a ^ (b >>> 6); + results[26] = a ^ (b >>> 7); + results[27] = a ^ (b >>> 8); + results[28] = a ^ (b >>> 14); + results[29] = a ^ (b >>> 15); + results[30] = a ^ (b >>> 16); + results[31] = a ^ (b >>> 30); + results[32] = a ^ (b >>> 31); + results[33] = a ^ (b >>> 32); + results[34] = a ^ (b >>> 62); + results[35] = a ^ (b >>> 63); + + return results; + } + + public static void $opt$validateShiftLongAsserts(long a, long b) { + long[] results = $opt$validateShiftLong(a, b); + assertIntEquals(3 * 12, results.length); + + assertLongEquals(a + $noinline$LongShl(b, 1), results[0]); + assertLongEquals(a + $noinline$LongShl(b, 6), results[1]); + assertLongEquals(a + $noinline$LongShl(b, 7), results[2]); + assertLongEquals(a + $noinline$LongShl(b, 8), results[3]); + assertLongEquals(a + $noinline$LongShl(b, 14), results[4]); + assertLongEquals(a + $noinline$LongShl(b, 15), results[5]); + assertLongEquals(a + $noinline$LongShl(b, 16), results[6]); + assertLongEquals(a + $noinline$LongShl(b, 30), results[7]); + assertLongEquals(a + $noinline$LongShl(b, 31), results[8]); + assertLongEquals(a + $noinline$LongShl(b, 32), results[9]); + assertLongEquals(a + $noinline$LongShl(b, 62), results[10]); + assertLongEquals(a + $noinline$LongShl(b, 63), results[11]); + + assertLongEquals(a - $noinline$LongShr(b, 1), results[12]); + assertLongEquals(a - $noinline$LongShr(b, 6), results[13]); + assertLongEquals(a - $noinline$LongShr(b, 7), results[14]); + assertLongEquals(a - $noinline$LongShr(b, 8), results[15]); + assertLongEquals(a - $noinline$LongShr(b, 14), results[16]); + assertLongEquals(a - $noinline$LongShr(b, 15), results[17]); + assertLongEquals(a - $noinline$LongShr(b, 16), results[18]); + assertLongEquals(a - $noinline$LongShr(b, 30), results[19]); + assertLongEquals(a - $noinline$LongShr(b, 31), results[20]); + assertLongEquals(a - $noinline$LongShr(b, 32), results[21]); + assertLongEquals(a - $noinline$LongShr(b, 62), results[22]); + assertLongEquals(a - $noinline$LongShr(b, 63), results[23]); + + assertLongEquals(a ^ $noinline$LongUshr(b, 1), results[24]); + assertLongEquals(a ^ $noinline$LongUshr(b, 6), results[25]); + assertLongEquals(a ^ $noinline$LongUshr(b, 7), results[26]); + assertLongEquals(a ^ $noinline$LongUshr(b, 8), results[27]); + assertLongEquals(a ^ $noinline$LongUshr(b, 14), results[28]); + assertLongEquals(a ^ $noinline$LongUshr(b, 15), results[29]); + assertLongEquals(a ^ $noinline$LongUshr(b, 16), results[30]); + assertLongEquals(a ^ $noinline$LongUshr(b, 30), results[31]); + assertLongEquals(a ^ $noinline$LongUshr(b, 31), results[32]); + assertLongEquals(a ^ $noinline$LongUshr(b, 32), results[33]); + assertLongEquals(a ^ $noinline$LongUshr(b, 62), results[34]); + assertLongEquals(a ^ $noinline$LongUshr(b, 63), results[35]); } @@ -1072,7 +1120,7 @@ public class Main { $opt$validateExtendLong(inputs[i], inputs[j]); $opt$validateShiftInt((int)inputs[i], (int)inputs[j]); - $opt$validateShiftLong(inputs[i], inputs[j]); + $opt$validateShiftLongAsserts(inputs[i], inputs[j]); } } -- GitLab From 7b39a39258311ad6c3da5934746a186c185c3f4f Mon Sep 17 00:00:00 2001 From: Alex Light Date: Wed, 6 Jun 2018 14:18:24 +0000 Subject: [PATCH 549/749] Revert^2 "Make adbconnection start automatically for debuggable apps (on target)" This reverts commit b2926c0cdbd30af7529b90fe20b24010f844ccc1. We were trying to start with adbconnection but that was not always available on devices. This could cause tests to fail. Bug: 109505014 Reason for revert: Fixed issue causing target tests to sometimes fail. Test: ./test.py --target Change-Id: I0bb9cd2dce87145a8a9c372e2f015e36a3276369 --- cmdline/cmdline_parser_test.cc | 2 +- runtime/jdwp_provider.h | 23 ++++++++++++++++++++++- runtime/runtime.cc | 8 +++++++- runtime/runtime_options.def | 2 +- test/etc/run-test-jar | 3 +++ 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index 235a2aa90e..a52e16328a 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -371,7 +371,7 @@ TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) { */ TEST_F(CmdlineParserTest, TestJdwpProviderEmpty) { { - EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kNone, "", M::JdwpProvider); + EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kUnset, "", M::JdwpProvider); } } // TEST_F diff --git a/runtime/jdwp_provider.h b/runtime/jdwp_provider.h index 698fdc086d..c4f19899c9 100644 --- a/runtime/jdwp_provider.h +++ b/runtime/jdwp_provider.h @@ -19,6 +19,7 @@ #include +#include "base/globals.h" #include "base/macros.h" #include "base/logging.h" @@ -26,13 +27,33 @@ namespace art { enum class JdwpProvider { kNone, + // Special value only used to denote that no explicit choice has been made by the user. This + // should not be used and one should always call CanonicalizeJdwpProvider which will remove this + // value before using a JdwpProvider value. + kUnset, kInternal, kAdbConnection, - // The current default provider + // The current default provider. Used if you run -XjdwpProvider:default kDefaultJdwpProvider = kAdbConnection, + + // What we should use as provider with no options and debuggable. On host we always want to be + // none since there is no adbd on host. + kUnsetDebuggable = kIsTargetBuild ? kDefaultJdwpProvider : kNone, + // What we should use as provider with no options and non-debuggable + kUnsetNonDebuggable = kNone, }; +inline JdwpProvider CanonicalizeJdwpProvider(JdwpProvider p, bool debuggable) { + if (p != JdwpProvider::kUnset) { + return p; + } + if (debuggable) { + return JdwpProvider::kUnsetDebuggable; + } + return JdwpProvider::kUnsetNonDebuggable; +} + std::ostream& operator<<(std::ostream& os, const JdwpProvider& rhs); } // namespace art diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 1e327fc8ed..7efd000f33 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1283,7 +1283,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { dump_gc_performance_on_shutdown_ = runtime_options.Exists(Opt::DumpGCPerformanceOnShutdown); jdwp_options_ = runtime_options.GetOrDefault(Opt::JdwpOptions); - jdwp_provider_ = runtime_options.GetOrDefault(Opt::JdwpProvider); + jdwp_provider_ = CanonicalizeJdwpProvider(runtime_options.GetOrDefault(Opt::JdwpProvider), + IsJavaDebuggable()); switch (jdwp_provider_) { case JdwpProvider::kNone: { VLOG(jdwp) << "Disabling all JDWP support."; @@ -1317,6 +1318,11 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { constexpr const char* plugin_name = kIsDebugBuild ? "libadbconnectiond.so" : "libadbconnection.so"; plugins_.push_back(Plugin::Create(plugin_name)); + break; + } + case JdwpProvider::kUnset: { + LOG(FATAL) << "Illegal jdwp provider " << jdwp_provider_ << " was not filtered out!"; + break; } } callbacks_->AddThreadLifecycleCallback(Dbg::GetThreadLifecycleCallback()); diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index e647423b9c..3f9a3229ca 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -44,7 +44,7 @@ RUNTIME_OPTIONS_KEY (std::string, Image) RUNTIME_OPTIONS_KEY (Unit, CheckJni) RUNTIME_OPTIONS_KEY (Unit, JniOptsForceCopy) RUNTIME_OPTIONS_KEY (std::string, JdwpOptions, "") -RUNTIME_OPTIONS_KEY (JdwpProvider, JdwpProvider, JdwpProvider::kNone) +RUNTIME_OPTIONS_KEY (JdwpProvider, JdwpProvider, JdwpProvider::kUnset) RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryMaximumSize, gc::Heap::kDefaultMaximumSize) // -Xmx RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryInitialSize, gc::Heap::kDefaultInitialSize) // -Xms RUNTIME_OPTIONS_KEY (MemoryKiB, HeapGrowthLimit) // Default is 0 for unlimited diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 1ba433e974..713fd35523 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -377,6 +377,9 @@ CHROOT_DEX_LOCATION="$CHROOT$DEX_LOCATION" if [ "$USE_JVM" = "n" ]; then FLAGS="${FLAGS} ${ANDROID_FLAGS}" + # we don't want to be trying to get adbconnections since the plugin might + # not have been built. + FLAGS="${FLAGS} -XjdwpProvider:none" for feature in ${EXPERIMENTAL}; do FLAGS="${FLAGS} -Xexperimental:${feature} -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:${feature}" COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xexperimental:${feature}" -- GitLab From 9cb13a66863411e6e885fd387d7908844270f42f Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 7 Jun 2018 13:02:02 -0700 Subject: [PATCH 550/749] Add debugging for b/62653020 We are seeing errors reading out/soong/build.ninja when running ART tests. Add a hack to copy out/soong/build.ninja to DIST_DIR on failure so we can see the contents. Test: Manually corrupt build.ninja, run testrunner.py with and without DIST_DIR set. Bug: 62653020 Change-Id: I6d83b87d56b6787a98fec8b244f224d44cb85bc1 --- test/testrunner/env.py | 3 +++ test/testrunner/testrunner.py | 4 ++++ tools/build/var_list | 3 +++ 3 files changed, 10 insertions(+) diff --git a/test/testrunner/env.py b/test/testrunner/env.py index 66ed0d0004..1f4b829989 100644 --- a/test/testrunner/env.py +++ b/test/testrunner/env.py @@ -140,3 +140,6 @@ ANDROID_JAVA_TOOLCHAIN = os.path.join(ANDROID_BUILD_TOP, # include platform prebuilt java, javac, etc in $PATH. os.environ['PATH'] = ANDROID_JAVA_TOOLCHAIN + ':' + os.environ['PATH'] + +DIST_DIR = _get_build_var('DIST_DIR') +SOONG_OUT_DIR = _get_build_var('SOONG_OUT_DIR') diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 2d1398e3fe..044e8dc12a 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -52,6 +52,7 @@ import json import multiprocessing import os import re +import shutil import subprocess import sys import tempfile @@ -1007,6 +1008,9 @@ def main(): build_command += ' -C ' + env.ANDROID_BUILD_TOP build_command += ' ' + build_targets if subprocess.call(build_command.split()): + # Debugging for b/62653020 + if env.DIST_DIR: + shutil.copyfile(env.SOONG_OUT_DIR + '/build.ninja', env.DIST_DIR + '/soong.ninja') sys.exit(1) if user_requested_tests: test_runner_thread = threading.Thread(target=run_tests, args=(user_requested_tests,)) diff --git a/tools/build/var_list b/tools/build/var_list index bb005cf77c..98a54725da 100644 --- a/tools/build/var_list +++ b/tools/build/var_list @@ -34,3 +34,6 @@ HOST_PREFER_32_BIT HOST_OUT_EXECUTABLES ANDROID_JAVA_TOOLCHAIN +# b/62653020 +DIST_DIR +SOONG_OUT_DIR -- GitLab From 18e2687c4126d66870d61d6548abc2c7661a66cc Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 4 Jun 2018 17:19:02 -0700 Subject: [PATCH 551/749] Refactor ClassAccessor to use an index instead of ClassDef pointer Removes a separate class_def_idx being required for getting the index of a ClassAccessor foreach loop. Bug: 79758018 Test: test-art-host-gtest Change-Id: Ie3010a17669f24cf492c678b55bdddba7ec62ea8 --- compiler/dex/dex_to_dex_decompiler_test.cc | 3 +-- compiler/driver/compiler_driver.cc | 20 ++++++++------------ dexdump/dexdump.cc | 2 +- libdexfile/dex/class_accessor-inl.h | 15 +++++++++++---- libdexfile/dex/class_accessor.h | 12 ++++++++---- libdexfile/dex/class_accessor_test.cc | 3 ++- oatdump/oatdump.cc | 6 ++---- profman/profman.cc | 4 +++- runtime/art_method.cc | 2 +- runtime/class_linker.cc | 6 +++--- runtime/verifier/method_verifier.cc | 3 +-- 11 files changed, 41 insertions(+), 35 deletions(-) diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc index 1fe42ad531..d4a9ba5491 100644 --- a/compiler/dex/dex_to_dex_decompiler_test.cc +++ b/compiler/dex/dex_to_dex_decompiler_test.cc @@ -82,9 +82,8 @@ class DexToDexDecompilerTest : public CommonCompilerTest { ASSERT_NE(0, cmp); // Unquicken the dex file. - for (uint32_t i = 0; i < updated_dex_file->NumClassDefs(); ++i) { + for (ClassAccessor accessor : updated_dex_file->GetClasses()) { // Unquicken each method. - ClassAccessor accessor(*updated_dex_file, updated_dex_file->GetClassDef(i)); for (const ClassAccessor::Method& method : accessor.GetMethods()) { CompiledMethod* compiled_method = compiler_driver_->GetCompiledMethod( method.GetReference()); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 653e9edb45..6cb3936f29 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1685,16 +1685,14 @@ static void CheckAndClearResolveException(Thread* self) bool CompilerDriver::RequiresConstructorBarrier(const DexFile& dex_file, uint16_t class_def_idx) const { - ClassAccessor accessor(dex_file, dex_file.GetClassDef(class_def_idx)); - bool has_is_final = false; + ClassAccessor accessor(dex_file, class_def_idx); // We require a constructor barrier if there are final instance fields. - accessor.VisitFields(/*static*/ VoidFunctor(), - [&](const ClassAccessor::Field& field) { + for (const ClassAccessor::Field& field : accessor.GetInstanceFields()) { if (field.IsFinal()) { - has_is_final = true; + return true; } - }); - return has_is_final; + } + return false; } class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { @@ -1744,7 +1742,7 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { // fields are assigned within the lock held for class initialization. bool requires_constructor_barrier = false; - ClassAccessor accessor(dex_file, class_def); + ClassAccessor accessor(dex_file, class_def_index); // Optionally resolve fields and methods and figure out if we need a constructor barrier. auto method_visitor = [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -1926,13 +1924,12 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, // Fetch the list of unverified classes. const std::set& unverified_classes = verifier_deps->GetUnverifiedClasses(*dex_file); - uint32_t class_def_idx = 0u; for (ClassAccessor accessor : dex_file->GetClasses()) { if (unverified_classes.find(accessor.GetClassIdx()) == unverified_classes.end()) { if (compiler_only_verifies) { // Just update the compiled_classes_ map. The compiler doesn't need to resolve // the type. - ClassReference ref(dex_file, class_def_idx); + ClassReference ref(dex_file, accessor.GetClassDefIndex()); const ClassStatus existing = ClassStatus::kNotReady; ClassStateTable::InsertResult result = compiled_classes_.Insert(ref, existing, ClassStatus::kVerified); @@ -1959,7 +1956,6 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, class_loader, soa.Self()); } - ++class_def_idx; } } return true; @@ -2700,7 +2696,7 @@ static void CompileDexFile(CompilerDriver* driver, jobject jclass_loader = context.GetClassLoader(); ClassReference ref(&dex_file, class_def_index); const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); - ClassAccessor accessor(dex_file, class_def); + ClassAccessor accessor(dex_file, class_def_index); // Skip compiling classes with generic verifier failures since they will still fail at runtime if (context.GetCompiler()->GetVerificationResults()->IsClassRejected(ref)) { return; diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 85778b6411..82610353b4 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -612,7 +612,7 @@ static void dumpClassDef(const DexFile* pDexFile, int idx) { pClassDef.class_data_off_, pClassDef.class_data_off_); // Fields and methods. - ClassAccessor accessor(*pDexFile, pClassDef); + ClassAccessor accessor(*pDexFile, idx); fprintf(gOutFile, "static_fields_size : %d\n", accessor.NumStaticFields()); fprintf(gOutFile, "instance_fields_size: %d\n", accessor.NumInstanceFields()); fprintf(gOutFile, "direct_methods_size : %d\n", accessor.NumDirectMethods()); diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h index 3bb9e93e5a..a335f088b0 100644 --- a/libdexfile/dex/class_accessor-inl.h +++ b/libdexfile/dex/class_accessor-inl.h @@ -26,12 +26,15 @@ namespace art { inline ClassAccessor::ClassAccessor(const ClassIteratorData& data) - : ClassAccessor(data.dex_file_, data.dex_file_.GetClassDef(data.class_def_idx_)) {} + : ClassAccessor(data.dex_file_, data.class_def_idx_) {} inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const DexFile::ClassDef& class_def) + : ClassAccessor(dex_file, dex_file.GetIndexForClassDef(class_def)) {} + +inline ClassAccessor::ClassAccessor(const DexFile& dex_file, uint32_t class_def_index) : dex_file_(dex_file), - descriptor_index_(class_def.class_idx_), - ptr_pos_(dex_file.GetClassData(class_def)), + class_def_index_(class_def_index), + ptr_pos_(dex_file.GetClassData(dex_file.GetClassDef(class_def_index))), num_static_fields_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u), num_instance_fields_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u), num_direct_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u), @@ -108,7 +111,7 @@ inline CodeItemInstructionAccessor ClassAccessor::Method::GetInstructions() cons } inline const char* ClassAccessor::GetDescriptor() const { - return dex_file_.StringByTypeIdx(descriptor_index_); + return dex_file_.StringByTypeIdx(GetClassIdx()); } inline const DexFile::CodeItem* ClassAccessor::Method::GetCodeItem() const { @@ -175,6 +178,10 @@ inline void ClassAccessor::Method::UnHideAccessFlags() const { DexFile::UnHideAccessFlags(const_cast(ptr_pos_), GetAccessFlags(), /*is_method*/ true); } +inline dex::TypeIndex ClassAccessor::GetClassIdx() const { + return dex_file_.GetClassDef(class_def_index_).class_idx_; +} + } // namespace art #endif // ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_ diff --git a/libdexfile/dex/class_accessor.h b/libdexfile/dex/class_accessor.h index 4f0fd32e31..34c7e0756b 100644 --- a/libdexfile/dex/class_accessor.h +++ b/libdexfile/dex/class_accessor.h @@ -248,6 +248,8 @@ class ClassAccessor { ClassAccessor(const DexFile& dex_file, const DexFile::ClassDef& class_def); + ClassAccessor(const DexFile& dex_file, uint32_t class_def_index); + // Return the code item for a method. const DexFile::CodeItem* GetCodeItem(const Method& method) const; @@ -315,9 +317,7 @@ class ClassAccessor { const char* GetDescriptor() const; - dex::TypeIndex GetClassIdx() const { - return descriptor_index_; - } + dex::TypeIndex GetClassIdx() const; const DexFile& GetDexFile() const { return dex_file_; @@ -327,6 +327,10 @@ class ClassAccessor { return ptr_pos_ != nullptr; } + uint32_t GetClassDefIndex() const { + return class_def_index_; + } + protected: // Template visitor to reduce copy paste for visiting elements. // No thread safety analysis since the visitor may require capabilities. @@ -341,7 +345,7 @@ class ClassAccessor { IterationRange> GetMethodsInternal(size_t count) const; const DexFile& dex_file_; - const dex::TypeIndex descriptor_index_ = {}; + const uint32_t class_def_index_; const uint8_t* ptr_pos_ = nullptr; // Pointer into stream of class_data_item. const uint32_t num_static_fields_ = 0u; const uint32_t num_instance_fields_ = 0u; diff --git a/libdexfile/dex/class_accessor_test.cc b/libdexfile/dex/class_accessor_test.cc index d0533c1811..1f30ae54d6 100644 --- a/libdexfile/dex/class_accessor_test.cc +++ b/libdexfile/dex/class_accessor_test.cc @@ -30,8 +30,9 @@ TEST_F(ClassAccessorTest, TestVisiting) { uint32_t class_def_idx = 0u; ASSERT_GT(dex_file->NumClassDefs(), 0u); for (ClassAccessor accessor : dex_file->GetClasses()) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx); + const DexFile::ClassDef& class_def = dex_file->GetClassDef(accessor.GetClassDefIndex()); EXPECT_EQ(accessor.GetDescriptor(), dex_file->StringByTypeIdx(class_def.class_idx_)); + EXPECT_EQ(class_def_idx, accessor.GetClassDefIndex()); ++class_def_idx; // Check iterators against visitors. auto methods = accessor.GetMethods(); diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 25676f736c..5c339b865d 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -274,7 +274,7 @@ class OatSymbolizer FINAL { void WalkOatClass(const OatFile::OatClass& oat_class, const DexFile& dex_file, uint32_t class_def_index) { - ClassAccessor accessor(dex_file, dex_file.GetClassDef(class_def_index)); + ClassAccessor accessor(dex_file, class_def_index); // Note: even if this is an interface or a native class, we still have to walk it, as there // might be a static initializer. uint32_t class_method_idx = 0; @@ -905,15 +905,13 @@ class OatDumper { continue; } offsets_.insert(reinterpret_cast(&dex_file->GetHeader())); - uint32_t class_def_index = 0u; for (ClassAccessor accessor : dex_file->GetClasses()) { - const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index); + const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(accessor.GetClassDefIndex()); for (uint32_t class_method_index = 0; class_method_index < accessor.NumMethods(); ++class_method_index) { AddOffsets(oat_class.GetOatMethod(class_method_index)); } - ++class_def_index; } } diff --git a/profman/profman.cc b/profman/profman.cc index 096e5dc3bd..5fbce66412 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -930,7 +930,9 @@ class ProfMan FINAL { dex_resolved_classes.first->AddClass(class_ref.TypeIndex()); std::vector methods; if (method_str == kClassAllMethods) { - ClassAccessor accessor(*dex_file, *dex_file->FindClassDef(class_ref.TypeIndex())); + ClassAccessor accessor( + *dex_file, + dex_file->GetIndexForClassDef(*dex_file->FindClassDef(class_ref.TypeIndex()))); for (const ClassAccessor::Method& method : accessor.GetMethods()) { if (method.GetCodeItemOffset() != 0) { // Add all of the methods that have code to the profile. diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 45bf66446a..5b4dcb73fd 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -425,7 +425,7 @@ bool ArtMethod::IsPolymorphicSignature() { static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx) { - ClassAccessor accessor(dex_file, dex_file.GetClassDef(class_def_idx)); + ClassAccessor accessor(dex_file, class_def_idx); uint32_t class_def_method_index = 0u; for (const ClassAccessor::Method& method : accessor.GetMethods()) { if (method.GetIndex() == method_idx) { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index be636d80a8..b1fd5f4788 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2863,9 +2863,9 @@ void ClassLinker::FixupStaticTrampolines(ObjPtr klass) { } const DexFile& dex_file = klass->GetDexFile(); - const DexFile::ClassDef* dex_class_def = klass->GetClassDef(); - CHECK(dex_class_def != nullptr); - ClassAccessor accessor(dex_file, *dex_class_def); + const uint16_t class_def_idx = klass->GetDexClassDefIndex(); + CHECK_NE(class_def_idx, DexFile::kDexNoIndex16); + ClassAccessor accessor(dex_file, class_def_idx); // There should always be class data if there were direct methods. CHECK(accessor.HasClassData()) << klass->PrettyDescriptor(); bool has_oat_class; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 59617481eb..a62271df77 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -212,8 +212,6 @@ FailureKind MethodVerifier::VerifyClass(Thread* self, bool allow_soft_failures, HardFailLogMode log_level, std::string* error) { - SCOPED_TRACE << "VerifyClass " << PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); - // A class must not be abstract and final. if ((class_def.access_flags_ & (kAccAbstract | kAccFinal)) == (kAccAbstract | kAccFinal)) { *error = "Verifier rejected class "; @@ -223,6 +221,7 @@ FailureKind MethodVerifier::VerifyClass(Thread* self, } ClassAccessor accessor(*dex_file, class_def); + SCOPED_TRACE << "VerifyClass " << PrettyDescriptor(accessor.GetDescriptor()); int64_t previous_method_idx[2] = { -1, -1 }; MethodVerifier::FailureData failure_data; -- GitLab From 26d465f1d8ca2b66b9c2766c9019aa2033d0e1be Mon Sep 17 00:00:00 2001 From: Richard Uhler Date: Thu, 7 Jun 2018 13:08:08 +0100 Subject: [PATCH 552/749] Add a tradefed config for ahat's unit tests. And a minor unrelated style cleanup. Bug: 38219110 Test: m ahat-test Test: tradefed.sh run AndroidTest.xml, without devices attached. Change-Id: Ifcb99ee08a43925ae79bb223a948522ab21c99c6 --- tools/ahat/Android.mk | 1 + tools/ahat/AndroidTest.xml | 21 ++++++++++ tools/ahat/etc/ahat-tests.mf | 2 +- .../ahat/src/main/com/android/ahat/Main.java | 4 +- .../ahat/{Tests.java => AhatTestSuite.java} | 42 ++++++++++--------- 5 files changed, 48 insertions(+), 22 deletions(-) create mode 100644 tools/ahat/AndroidTest.xml rename tools/ahat/src/test/com/android/ahat/{Tests.java => AhatTestSuite.java} (50%) diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk index ad33233159..9f423ba76d 100644 --- a/tools/ahat/Android.mk +++ b/tools/ahat/Android.mk @@ -147,6 +147,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := ahat junit-host LOCAL_IS_HOST_MODULE := true LOCAL_MODULE_TAGS := tests LOCAL_MODULE := ahat-tests +LOCAL_COMPATIBILITY_SUITE := general-tests include $(BUILD_HOST_JAVA_LIBRARY) AHAT_TEST_JAR := $(LOCAL_BUILT_MODULE) diff --git a/tools/ahat/AndroidTest.xml b/tools/ahat/AndroidTest.xml new file mode 100644 index 0000000000..867658cf5e --- /dev/null +++ b/tools/ahat/AndroidTest.xml @@ -0,0 +1,21 @@ + + + + diff --git a/tools/ahat/etc/ahat-tests.mf b/tools/ahat/etc/ahat-tests.mf index af17fadded..48fdeb3a01 100644 --- a/tools/ahat/etc/ahat-tests.mf +++ b/tools/ahat/etc/ahat-tests.mf @@ -1 +1 @@ -Main-Class: com.android.ahat.Tests +Main-Class: com.android.ahat.AhatTestSuite diff --git a/tools/ahat/src/main/com/android/ahat/Main.java b/tools/ahat/src/main/com/android/ahat/Main.java index 04a6012a61..af197d4862 100644 --- a/tools/ahat/src/main/com/android/ahat/Main.java +++ b/tools/ahat/src/main/com/android/ahat/Main.java @@ -102,7 +102,7 @@ public class Main { i++; try { map.readFromFile(new File(args[i])); - } catch (IOException|ParseException ex) { + } catch (IOException | ParseException ex) { System.out.println("Unable to read proguard map: " + ex); System.out.println("The proguard map will not be used."); } @@ -110,7 +110,7 @@ public class Main { i++; try { mapbase.readFromFile(new File(args[i])); - } catch (IOException|ParseException ex) { + } catch (IOException | ParseException ex) { System.out.println("Unable to read baseline proguard map: " + ex); System.out.println("The proguard map will not be used."); } diff --git a/tools/ahat/src/test/com/android/ahat/Tests.java b/tools/ahat/src/test/com/android/ahat/AhatTestSuite.java similarity index 50% rename from tools/ahat/src/test/com/android/ahat/Tests.java rename to tools/ahat/src/test/com/android/ahat/AhatTestSuite.java index 0e7043291d..bce1f053be 100644 --- a/tools/ahat/src/test/com/android/ahat/Tests.java +++ b/tools/ahat/src/test/com/android/ahat/AhatTestSuite.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * 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. @@ -17,28 +17,32 @@ package com.android.ahat; import org.junit.runner.JUnitCore; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; -public class Tests { +@RunWith(Suite.class) +@Suite.SuiteClasses({ + DiffFieldsTest.class, + DiffTest.class, + DominatorsTest.class, + HtmlEscaperTest.class, + InstanceTest.class, + NativeAllocationTest.class, + ObjectHandlerTest.class, + OverviewHandlerTest.class, + PerformanceTest.class, + ProguardMapTest.class, + RootedHandlerTest.class, + QueryTest.class, + SiteHandlerTest.class, + SiteTest.class +}) + +public class AhatTestSuite { public static void main(String[] args) { if (args.length == 0) { - args = new String[]{ - "com.android.ahat.DiffFieldsTest", - "com.android.ahat.DiffTest", - "com.android.ahat.DominatorsTest", - "com.android.ahat.HtmlEscaperTest", - "com.android.ahat.InstanceTest", - "com.android.ahat.NativeAllocationTest", - "com.android.ahat.ObjectHandlerTest", - "com.android.ahat.OverviewHandlerTest", - "com.android.ahat.PerformanceTest", - "com.android.ahat.ProguardMapTest", - "com.android.ahat.RootedHandlerTest", - "com.android.ahat.QueryTest", - "com.android.ahat.SiteHandlerTest", - "com.android.ahat.SiteTest", - }; + args = new String[]{"com.android.ahat.AhatTestSuite"}; } JUnitCore.main(args); } } - -- GitLab From fb57a6528d73e6ddbccf3049f08f6fa400e6dcd1 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 8 Jun 2018 09:40:19 +0000 Subject: [PATCH 553/749] Revert "Revert^2 "Make adbconnection start automatically for debuggable apps (on target)"" This reverts commit 7b39a39258311ad6c3da5934746a186c185c3f4f. Fails jdwp tests on target. Bug: 109505014 Change-Id: Ib8a278bc8c8137e1f1397e3e881d652504fea96f --- cmdline/cmdline_parser_test.cc | 2 +- runtime/jdwp_provider.h | 23 +---------------------- runtime/runtime.cc | 8 +------- runtime/runtime_options.def | 2 +- test/etc/run-test-jar | 3 --- 5 files changed, 4 insertions(+), 34 deletions(-) diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index a52e16328a..235a2aa90e 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -371,7 +371,7 @@ TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) { */ TEST_F(CmdlineParserTest, TestJdwpProviderEmpty) { { - EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kUnset, "", M::JdwpProvider); + EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kNone, "", M::JdwpProvider); } } // TEST_F diff --git a/runtime/jdwp_provider.h b/runtime/jdwp_provider.h index c4f19899c9..698fdc086d 100644 --- a/runtime/jdwp_provider.h +++ b/runtime/jdwp_provider.h @@ -19,7 +19,6 @@ #include -#include "base/globals.h" #include "base/macros.h" #include "base/logging.h" @@ -27,33 +26,13 @@ namespace art { enum class JdwpProvider { kNone, - // Special value only used to denote that no explicit choice has been made by the user. This - // should not be used and one should always call CanonicalizeJdwpProvider which will remove this - // value before using a JdwpProvider value. - kUnset, kInternal, kAdbConnection, - // The current default provider. Used if you run -XjdwpProvider:default + // The current default provider kDefaultJdwpProvider = kAdbConnection, - - // What we should use as provider with no options and debuggable. On host we always want to be - // none since there is no adbd on host. - kUnsetDebuggable = kIsTargetBuild ? kDefaultJdwpProvider : kNone, - // What we should use as provider with no options and non-debuggable - kUnsetNonDebuggable = kNone, }; -inline JdwpProvider CanonicalizeJdwpProvider(JdwpProvider p, bool debuggable) { - if (p != JdwpProvider::kUnset) { - return p; - } - if (debuggable) { - return JdwpProvider::kUnsetDebuggable; - } - return JdwpProvider::kUnsetNonDebuggable; -} - std::ostream& operator<<(std::ostream& os, const JdwpProvider& rhs); } // namespace art diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 7efd000f33..1e327fc8ed 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1283,8 +1283,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { dump_gc_performance_on_shutdown_ = runtime_options.Exists(Opt::DumpGCPerformanceOnShutdown); jdwp_options_ = runtime_options.GetOrDefault(Opt::JdwpOptions); - jdwp_provider_ = CanonicalizeJdwpProvider(runtime_options.GetOrDefault(Opt::JdwpProvider), - IsJavaDebuggable()); + jdwp_provider_ = runtime_options.GetOrDefault(Opt::JdwpProvider); switch (jdwp_provider_) { case JdwpProvider::kNone: { VLOG(jdwp) << "Disabling all JDWP support."; @@ -1318,11 +1317,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { constexpr const char* plugin_name = kIsDebugBuild ? "libadbconnectiond.so" : "libadbconnection.so"; plugins_.push_back(Plugin::Create(plugin_name)); - break; - } - case JdwpProvider::kUnset: { - LOG(FATAL) << "Illegal jdwp provider " << jdwp_provider_ << " was not filtered out!"; - break; } } callbacks_->AddThreadLifecycleCallback(Dbg::GetThreadLifecycleCallback()); diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 3f9a3229ca..e647423b9c 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -44,7 +44,7 @@ RUNTIME_OPTIONS_KEY (std::string, Image) RUNTIME_OPTIONS_KEY (Unit, CheckJni) RUNTIME_OPTIONS_KEY (Unit, JniOptsForceCopy) RUNTIME_OPTIONS_KEY (std::string, JdwpOptions, "") -RUNTIME_OPTIONS_KEY (JdwpProvider, JdwpProvider, JdwpProvider::kUnset) +RUNTIME_OPTIONS_KEY (JdwpProvider, JdwpProvider, JdwpProvider::kNone) RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryMaximumSize, gc::Heap::kDefaultMaximumSize) // -Xmx RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryInitialSize, gc::Heap::kDefaultInitialSize) // -Xms RUNTIME_OPTIONS_KEY (MemoryKiB, HeapGrowthLimit) // Default is 0 for unlimited diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 713fd35523..1ba433e974 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -377,9 +377,6 @@ CHROOT_DEX_LOCATION="$CHROOT$DEX_LOCATION" if [ "$USE_JVM" = "n" ]; then FLAGS="${FLAGS} ${ANDROID_FLAGS}" - # we don't want to be trying to get adbconnections since the plugin might - # not have been built. - FLAGS="${FLAGS} -XjdwpProvider:none" for feature in ${EXPERIMENTAL}; do FLAGS="${FLAGS} -Xexperimental:${feature} -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:${feature}" COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xexperimental:${feature}" -- GitLab From 92149d0a8e651a1d0f46bf4c94bdac809aa034ab Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 8 Jun 2018 10:36:45 +0100 Subject: [PATCH 554/749] Blacklist test. bug: 109791792 Test: m Change-Id: I1332794e8c1ee190ec5683ba5c3dbf519e02a487 --- test/knownfailures.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/knownfailures.json b/test/knownfailures.json index ed98d233c3..c680f53315 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -991,5 +991,11 @@ "variant": "jit", "bug": "b/77567088", "description": ["Test throws exception before or during OOME."] + }, + { + "tests": ["021-string2"], + "variant": "jit & debuggable", + "bug": "b/109791792", + "description": ["Stack too big."] } ] -- GitLab From cd260ebf53e0e05bd75c37c4139f32782eb4ad97 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Wed, 6 Jun 2018 09:04:17 +0100 Subject: [PATCH 555/749] ART: Simplify invoke-polymorphic entrypoints Moves to pattern used by the interpreter bridge and writes the result in both the regular return register and the floating point result register. Add return value tests to 956-method-handles. Test: art/test.py --host -r -t 956 Test: art/test.py --target --32 -r -t 956 Test: art/test.py --target --64 -r -t 956 Change-Id: I7389d04b70b88e149682f6d656ab185e48bcbf66 --- compiler/optimizing/code_generator.cc | 6 +- runtime/arch/arm/quick_entrypoints_arm.S | 79 +------------ runtime/arch/arm64/quick_entrypoints_arm64.S | 81 ++------------ runtime/arch/mips/quick_entrypoints_mips.S | 64 +++-------- .../arch/mips64/quick_entrypoints_mips64.S | 67 +++-------- runtime/arch/x86/quick_entrypoints_x86.S | 104 +++--------------- .../arch/x86_64/quick_entrypoints_x86_64.S | 75 +------------ .../quick/quick_trampoline_entrypoints.cc | 32 ++---- test/956-methodhandles/expected.txt | 1 + test/956-methodhandles/src/Main.java | 84 ++++++++++++++ 10 files changed, 167 insertions(+), 426 deletions(-) diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 4791fa3fba..9f2346db3c 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -516,7 +516,7 @@ void CodeGenerator::CreateCommonInvokeLocationSummary( locations->AddTemp(visitor->GetMethodLocation()); break; } - } else { + } else if (!invoke->IsInvokePolymorphic()) { locations->AddTemp(visitor->GetMethodLocation()); } } @@ -579,7 +579,9 @@ void CodeGenerator::GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invok } void CodeGenerator::GenerateInvokePolymorphicCall(HInvokePolymorphic* invoke) { - MoveConstant(invoke->GetLocations()->GetTemp(0), static_cast(invoke->GetType())); + // invoke-polymorphic does not use a temporary to convey any additional information (e.g. a + // method index) since it requires multiple info from the instruction (registers A, B, H). Not + // using the reservation has no effect on the registers used in the runtime call. QuickEntrypointEnum entrypoint = kQuickInvokePolymorphic; InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), nullptr); } diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 311e838fb3..2ef30c0d26 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -2686,82 +2686,15 @@ END art_quick_read_barrier_mark_introspection .extern artInvokePolymorphic ENTRY art_quick_invoke_polymorphic SETUP_SAVE_REFS_AND_ARGS_FRAME r2 - mov r2, rSELF @ pass Thread::Current - mov r3, sp @ pass SP - mov r0, #0 @ initialize 64-bit JValue as zero. - str r0, [sp, #-4]! - .cfi_adjust_cfa_offset 4 - str r0, [sp, #-4]! - .cfi_adjust_cfa_offset 4 - mov r0, sp @ pass JValue for return result as first argument. - bl artInvokePolymorphic @ artInvokePolymorphic(JValue, receiver, Thread*, SP) - sub r0, 'A' @ return value is descriptor of handle's return type. - cmp r0, 'Z' - 'A' @ check if value is in bounds of handler table - bgt .Lcleanup_and_return @ and clean-up if not. - adr r1, .Lhandler_table - tbb [r0, r1] @ branch to handler for return value based on return type. - -.Lstart_of_handlers: -.Lstore_boolean_result: - ldrb r0, [sp] @ Copy boolean value to return value of this function. - b .Lcleanup_and_return -.Lstore_char_result: - ldrh r0, [sp] @ Copy char value to return value of this function. - b .Lcleanup_and_return -.Lstore_float_result: - vldr s0, [sp] @ Copy float value from JValue result to the context restored by - vstr s0, [sp, #16] @ RESTORE_SAVE_REFS_AND_ARGS_FRAME. - b .Lcleanup_and_return -.Lstore_double_result: - vldr d0, [sp] @ Copy double value from JValue result to the context restored by - vstr d0, [sp, #16] @ RESTORE_SAVE_REFS_AND_ARGS_FRAME. - b .Lcleanup_and_return -.Lstore_long_result: - ldr r1, [sp, #4] @ Copy the upper bits from JValue result to the context restored by - str r1, [sp, #80] @ RESTORE_SAVE_REFS_AND_ARGS_FRAME. - // Fall-through for lower bits. -.Lstore_int_result: - ldr r0, [sp] @ Copy int value to return value of this function. - // Fall-through to clean up and return. -.Lcleanup_and_return: - add sp, #8 - .cfi_adjust_cfa_offset -8 + mov r0, r1 @ r0 := receiver + mov r1, rSELF @ r1 := Thread::Current + mov r2, sp @ r2 := SP + bl artInvokePolymorphic @ artInvokePolymorphic(receiver, Thread*, SP) + str r1, [sp, 72] @ r0:r1 := Result. Copy r1 to context. RESTORE_SAVE_REFS_AND_ARGS_FRAME REFRESH_MARKING_REGISTER + vmov d0, r0, r1 @ Put result r0:r1 into floating point return register. RETURN_OR_DELIVER_PENDING_EXCEPTION_REG r2 - -.macro HANDLER_TABLE_OFFSET handler_label - .byte (\handler_label - .Lstart_of_handlers) / 2 -.endm - -.Lhandler_table: - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // A - HANDLER_TABLE_OFFSET(.Lstore_int_result) // B (byte) - HANDLER_TABLE_OFFSET(.Lstore_char_result) // C (char) - HANDLER_TABLE_OFFSET(.Lstore_double_result) // D (double) - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // E - HANDLER_TABLE_OFFSET(.Lstore_float_result) // F (float) - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // G - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // H - HANDLER_TABLE_OFFSET(.Lstore_int_result) // I (int) - HANDLER_TABLE_OFFSET(.Lstore_long_result) // J (long) - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // K - HANDLER_TABLE_OFFSET(.Lstore_int_result) // L (object) - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // M - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // N - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // O - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // P - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Q - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // R - HANDLER_TABLE_OFFSET(.Lstore_int_result) // S (short) - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // T - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // U - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // V (void) - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // W - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // X - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Y - 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. diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 14d0cc7db4..5e540dd5e9 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -2844,82 +2844,15 @@ END art_quick_read_barrier_mark_introspection .extern artInvokePolymorphic ENTRY art_quick_invoke_polymorphic - SETUP_SAVE_REFS_AND_ARGS_FRAME // Save callee saves in case allocation triggers GC. - mov x2, xSELF - mov x3, sp - INCREASE_FRAME 16 // Reserve space for JValue result. - str xzr, [sp, #0] // Initialize result to zero. - mov x0, sp // Set r0 to point to result. - bl artInvokePolymorphic // artInvokePolymorphic(result, receiver, thread, save_area) - uxtb w0, w0 // Result is the return type descriptor as a char. - sub w0, w0, 'A' // Convert to zero based index. - cmp w0, 'Z' - 'A' - bhi .Lcleanup_and_return // Clean-up if out-of-bounds. - adrp x1, .Lhandler_table // Compute address of handler table. - add x1, x1, :lo12:.Lhandler_table - ldrb w0, [x1, w0, uxtw] // Lookup handler offset in handler table. - adr x1, .Lstart_of_handlers - add x0, x1, w0, sxtb #2 // Convert relative offset to absolute address. - br x0 // Branch to handler. - -.Lstart_of_handlers: -.Lstore_boolean_result: - ldrb w0, [sp] - b .Lcleanup_and_return -.Lstore_char_result: - ldrh w0, [sp] - b .Lcleanup_and_return -.Lstore_float_result: - ldr s0, [sp] - str s0, [sp, #32] - b .Lcleanup_and_return -.Lstore_double_result: - ldr d0, [sp] - str d0, [sp, #32] - b .Lcleanup_and_return -.Lstore_long_result: - ldr x0, [sp] - // Fall-through -.Lcleanup_and_return: - DECREASE_FRAME 16 + SETUP_SAVE_REFS_AND_ARGS_FRAME // Save callee saves in case allocation triggers GC. + mov x0, x1 // x0 := receiver + mov x1, xSELF // x1 := Thread::Current() + mov x2, sp // x2 := SP + bl artInvokePolymorphic // artInvokePolymorphic(receiver, thread, save_area) RESTORE_SAVE_REFS_AND_ARGS_FRAME REFRESH_MARKING_REGISTER - RETURN_OR_DELIVER_PENDING_EXCEPTION_X1 - - .section .rodata // Place handler table in read-only section away from text. - .align 2 -.macro HANDLER_TABLE_OFFSET handler_label - .byte (\handler_label - .Lstart_of_handlers) / 4 -.endm -.Lhandler_table: - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // A - HANDLER_TABLE_OFFSET(.Lstore_long_result) // B (byte) - HANDLER_TABLE_OFFSET(.Lstore_char_result) // C (char) - HANDLER_TABLE_OFFSET(.Lstore_double_result) // D (double) - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // E - HANDLER_TABLE_OFFSET(.Lstore_float_result) // F (float) - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // G - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // H - HANDLER_TABLE_OFFSET(.Lstore_long_result) // I (int) - HANDLER_TABLE_OFFSET(.Lstore_long_result) // J (long) - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // K - HANDLER_TABLE_OFFSET(.Lstore_long_result) // L (object - references are compressed and only 32-bits) - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // M - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // N - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // O - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // P - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Q - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // R - HANDLER_TABLE_OFFSET(.Lstore_long_result) // S (short) - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // T - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // U - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // V (void) - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // W - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // X - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Y - HANDLER_TABLE_OFFSET(.Lstore_boolean_result) // Z (boolean) - .text - + fmov d0, x0 // Result is in x0. Copy to floating return register. + RETURN_OR_DELIVER_PENDING_EXCEPTION END art_quick_invoke_polymorphic // Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding. diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index c367ea60c2..c9bdc969ee 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -3246,58 +3246,26 @@ art_quick_read_barrier_mark_introspection_end_of_entries: BRB_FIELD_EXIT_BREAK END art_quick_read_barrier_mark_introspection + /* + * Polymorphic method invocation. + * On entry: + * a0 = unused + * a1 = receiver + */ .extern artInvokePolymorphic ENTRY art_quick_invoke_polymorphic SETUP_SAVE_REFS_AND_ARGS_FRAME - move $a2, rSELF # Make $a2 an alias for the current Thread. - addiu $a3, $sp, ARG_SLOT_SIZE # Make $a3 a pointer to the saved frame context. - sw $zero, 20($sp) # Initialize JValue result. - sw $zero, 16($sp) - la $t9, artInvokePolymorphic - jalr $t9 # artInvokePolymorphic(result, receiver, Thread*, context) - addiu $a0, $sp, 16 # Make $a0 a pointer to the JValue result -.macro MATCH_RETURN_TYPE c, handler - li $t0, \c - beq $v0, $t0, \handler -.endm - MATCH_RETURN_TYPE 'V', .Lcleanup_and_return - MATCH_RETURN_TYPE 'L', .Lstore_int_result - MATCH_RETURN_TYPE 'I', .Lstore_int_result - MATCH_RETURN_TYPE 'J', .Lstore_long_result - MATCH_RETURN_TYPE 'B', .Lstore_int_result - MATCH_RETURN_TYPE 'C', .Lstore_char_result - MATCH_RETURN_TYPE 'D', .Lstore_double_result - MATCH_RETURN_TYPE 'F', .Lstore_float_result - MATCH_RETURN_TYPE 'S', .Lstore_int_result - MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result -.purgem MATCH_RETURN_TYPE - nop - b .Lcleanup_and_return - nop -.Lstore_boolean_result: - b .Lcleanup_and_return - lbu $v0, 16($sp) # Move byte from JValue result to return value register. -.Lstore_char_result: - b .Lcleanup_and_return - lhu $v0, 16($sp) # Move char from JValue result to return value register. -.Lstore_double_result: -.Lstore_float_result: - CHECK_ALIGNMENT $sp, $t0 - ldc1 $f0, 16($sp) # Move double/float from JValue result to return value register. - b .Lcleanup_and_return - nop -.Lstore_long_result: - lw $v1, 20($sp) # Move upper bits from JValue result to return value register. - // Fall-through for lower bits. -.Lstore_int_result: - lw $v0, 16($sp) # Move lower bits from JValue result to return value register. - // Fall-through to clean up and return. -.Lcleanup_and_return: - lw $t7, THREAD_EXCEPTION_OFFSET(rSELF) # Load Thread::Current()->exception_ + move $a0, $a1 # Make $a0 the receiver. + move $a1, rSELF # Make $a1 an alias for the current Thread. + la $t9, artInvokePolymorphic # Invoke artInvokePolymorphic + jalr $t9 # with args (receiver, Thread*, context). + addiu $a2, $sp, ARG_SLOT_SIZE # Make $a2 a pointer to the saved frame context. + lw $t7, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_ RESTORE_SAVE_REFS_AND_ARGS_FRAME - bnez $t7, 1f # Success if no exception is pending. - nop - jalr $zero, $ra + bnez $t7, 1f + # don't care if $v0 and/or $v1 are modified, when exception branch taken + MTD $v0, $v1, $f0, $f1 # move float value to return value + jalr $zero, $ra nop 1: DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 1f4f174e26..1800056f77 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -3046,59 +3046,26 @@ art_quick_read_barrier_mark_introspection_end_of_entries: BRB_FIELD_EXIT_BREAK END art_quick_read_barrier_mark_introspection + /* + * Polymorphic method invocation. + * On entry: + * a0 = unused + * a1 = receiver + */ .extern artInvokePolymorphic ENTRY art_quick_invoke_polymorphic SETUP_SAVE_REFS_AND_ARGS_FRAME - move $a2, rSELF # Make $a2 an alias for the current Thread. - move $a3, $sp # Make $a3 a pointer to the saved frame context. - daddiu $sp, $sp, -8 # Reserve space for JValue result. - .cfi_adjust_cfa_offset 8 - sd $zero, 0($sp) # Initialize JValue result. - jal artInvokePolymorphic # artInvokePolymorphic(result, receiver, Thread*, context) - move $a0, $sp # Make $a0 a pointer to the JValue result -.macro MATCH_RETURN_TYPE c, handler - li $t0, \c - beq $v0, $t0, \handler -.endm - MATCH_RETURN_TYPE 'V', .Lcleanup_and_return - MATCH_RETURN_TYPE 'L', .Lstore_ref_result - MATCH_RETURN_TYPE 'I', .Lstore_long_result - MATCH_RETURN_TYPE 'J', .Lstore_long_result - MATCH_RETURN_TYPE 'B', .Lstore_long_result - MATCH_RETURN_TYPE 'C', .Lstore_char_result - MATCH_RETURN_TYPE 'D', .Lstore_double_result - MATCH_RETURN_TYPE 'F', .Lstore_float_result - MATCH_RETURN_TYPE 'S', .Lstore_long_result - MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result -.purgem MATCH_RETURN_TYPE - nop - b .Lcleanup_and_return - nop -.Lstore_boolean_result: - b .Lcleanup_and_return - lbu $v0, 0($sp) # Move byte from JValue result to return value register. -.Lstore_char_result: - b .Lcleanup_and_return - lhu $v0, 0($sp) # Move char from JValue result to return value register. -.Lstore_double_result: -.Lstore_float_result: - b .Lcleanup_and_return - l.d $f0, 0($sp) # Move double/float from JValue result to return value register. -.Lstore_ref_result: - b .Lcleanup_and_return - lwu $v0, 0($sp) # Move zero extended lower 32-bits to return value register. -.Lstore_long_result: - ld $v0, 0($sp) # Move long from JValue result to return value register. - // Fall-through to clean up and return. -.Lcleanup_and_return: - daddiu $sp, $sp, 8 # Remove space for JValue result. - .cfi_adjust_cfa_offset -8 - ld $t0, THREAD_EXCEPTION_OFFSET(rSELF) # Load Thread::Current()->exception_ - RESTORE_SAVE_REFS_AND_ARGS_FRAME - bnez $t0, 1f # Success if no exception is pending. - nop - jalr $zero, $ra - nop + move $a0, $a1 # Make $a0 the receiver + move $a1, rSELF # Make $a1 an alias for the current Thread. + jal artInvokePolymorphic # artInvokePolymorphic(receiver, Thread*, context) + move $a2, $sp # Make $a3 a pointer to the saved frame context. + ld $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_ + daddiu $sp, $sp, REFS_AND_ARGS_MINUS_REFS_SIZE # skip a0-a7 and f12-f19 + RESTORE_SAVE_REFS_ONLY_FRAME + bne $t0, $zero, 1f + dmtc1 $v0, $f0 # place return value to FP return value + jalr $zero, $ra + dmtc1 $v1, $f1 # place return value to FP return value 1: DELIVER_PENDING_EXCEPTION END art_quick_invoke_polymorphic diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index b89d45f617..e392198d44 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -2434,97 +2434,25 @@ DEFINE_FUNCTION art_quick_osr_stub END_FUNCTION art_quick_osr_stub DEFINE_FUNCTION art_quick_invoke_polymorphic - SETUP_SAVE_REFS_AND_ARGS_FRAME ebx, ebx // Save frame. - mov %esp, %edx // Remember SP. - subl LITERAL(16), %esp // Make space for JValue result. - CFI_ADJUST_CFA_OFFSET(16) - movl LITERAL(0), (%esp) // Initialize result to zero. - movl LITERAL(0), 4(%esp) - mov %esp, %eax // Store pointer to JValue result in eax. - PUSH edx // pass SP - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() + // On entry: EAX := unused, ECX := receiver + SETUP_SAVE_REFS_AND_ARGS_FRAME ebx, ebx // Save frame. + mov %esp, %edx // Remember SP + sub LITERAL(4), %esp // Alignment padding CFI_ADJUST_CFA_OFFSET(4) - PUSH ecx // pass receiver (method handle) - PUSH eax // pass JResult - call SYMBOL(artInvokePolymorphic) // artInvokePolymorphic(result, receiver, Thread*, SP) - subl LITERAL('A'), %eax // Eliminate out of bounds options - cmpb LITERAL('Z' - 'A'), %al - ja .Lcleanup_and_return - movzbl %al, %eax - call .Lput_eip_in_ecx -.Lbranch_start: - movl %ecx, %edx - add $(.Lhandler_table - .Lbranch_start), %edx // Make EDX point to handler_table. - leal (%edx, %eax, 2), %eax // Calculate address of entry in table. - movzwl (%eax), %eax // Lookup relative branch in table. - addl %ecx, %eax // Add EIP relative offset. - jmp *%eax // Branch to handler. - - // Handlers for different return types. -.Lstore_boolean_result: - movzbl 16(%esp), %eax // Copy boolean result to the accumulator. - jmp .Lcleanup_and_return -.Lstore_char_result: - movzwl 16(%esp), %eax // Copy char result to the accumulator. - jmp .Lcleanup_and_return -.Lstore_float_result: - movd 16(%esp), %xmm0 // Copy float result to the context restored by - movd %xmm0, 36(%esp) // RESTORE_SAVE_REFS_ONLY_FRAME. - jmp .Lcleanup_and_return -.Lstore_double_result: - movsd 16(%esp), %xmm0 // Copy double result to the context restored by - movsd %xmm0, 36(%esp) // RESTORE_SAVE_REFS_ONLY_FRAME. - jmp .Lcleanup_and_return -.Lstore_long_result: - movl 20(%esp), %edx // Copy upper-word of result to the context restored by - movl %edx, 72(%esp) // RESTORE_SAVE_REFS_ONLY_FRAME. - // Fall-through for lower bits. -.Lstore_int_result: - movl 16(%esp), %eax // Copy int result to the accumulator. - // Fall-through to clean up and return. -.Lcleanup_and_return: - addl LITERAL(32), %esp // Pop arguments and stack allocated JValue result. - CFI_ADJUST_CFA_OFFSET(-32) + push %edx // Push SP + CFI_ADJUST_CFA_OFFSET(4) + pushl %fs:THREAD_SELF_OFFSET // Push Thread::Current() + CFI_ADJUST_CFA_OFFSET(4) + push %ecx // Push receiver (method handle) + CFI_ADJUST_CFA_OFFSET(4) + call SYMBOL(artInvokePolymorphic) // invoke with (receiver, thread, SP) + addl LITERAL(16), %esp // Pop arguments. + CFI_ADJUST_CFA_OFFSET(-16) + mov %eax, 4(%esp) // Result is in EAX:EDX. Copy to saved FP state. + mov %edx, 8(%esp) + mov %edx, 40(%esp) // Copy EDX to saved context RESTORE_SAVE_REFS_AND_ARGS_FRAME RETURN_OR_DELIVER_PENDING_EXCEPTION - -.Lput_eip_in_ecx: // Internal function that puts address of - movl 0(%esp), %ecx // next instruction into ECX when CALL - ret - - // Handler table to handlers for given type. -.Lhandler_table: -MACRO1(HANDLER_TABLE_ENTRY, handler_label) - // NB some tools require 16-bits for relocations. Shouldn't need adjusting. - .word RAW_VAR(handler_label) - .Lbranch_start -END_MACRO - HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // A - HANDLER_TABLE_ENTRY(.Lstore_int_result) // B (byte) - HANDLER_TABLE_ENTRY(.Lstore_char_result) // C (char) - HANDLER_TABLE_ENTRY(.Lstore_double_result) // D (double) - HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // E - HANDLER_TABLE_ENTRY(.Lstore_float_result) // F (float) - HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // G - HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // H - HANDLER_TABLE_ENTRY(.Lstore_int_result) // I (int) - HANDLER_TABLE_ENTRY(.Lstore_long_result) // J (long) - HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // K - HANDLER_TABLE_ENTRY(.Lstore_int_result) // L (object) - HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // M - HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // N - HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // O - HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // P - HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // Q - HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // R - HANDLER_TABLE_ENTRY(.Lstore_int_result) // S (short) - HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // T - HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // U - HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // V (void) - HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // W - HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // X - HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // Y - HANDLER_TABLE_ENTRY(.Lstore_boolean_result) // Z (boolean) - END_FUNCTION art_quick_invoke_polymorphic // Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding. diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index c179033e6b..3f5d4f669a 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -2418,78 +2418,15 @@ DEFINE_FUNCTION art_quick_osr_stub END_FUNCTION art_quick_osr_stub DEFINE_FUNCTION art_quick_invoke_polymorphic + // On entry: RDI := unused, RSI := receiver SETUP_SAVE_REFS_AND_ARGS_FRAME // save callee saves - movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread - movq %rsp, %rcx // pass SP - subq LITERAL(16), %rsp // make space for JValue result - CFI_ADJUST_CFA_OFFSET(16) - movq LITERAL(0), (%rsp) // initialize result - movq %rsp, %rdi // store pointer to JValue result - call SYMBOL(artInvokePolymorphic) // artInvokePolymorphic(result, receiver, Thread*, SP) + movq %rsi, %rdi // RDI := receiver + movq %gs:THREAD_SELF_OFFSET, %rsi // RSI := Thread (self) + movq %rsp, %rdx // RDX := pass SP + call SYMBOL(artInvokePolymorphic) // invoke with (receiver, self, SP) // save the code pointer - subq LITERAL('A'), %rax // Convert type descriptor character value to a zero based index. - cmpb LITERAL('Z' - 'A'), %al // Eliminate out of bounds options - ja .Lcleanup_and_return - movzbq %al, %rax - leaq .Lhandler_table(%rip), %rcx // Get the address of the handler table - movslq (%rcx, %rax, 4), %rax // Lookup handler offset relative to table - addq %rcx, %rax // Add table address to yield handler address. - jmpq *%rax // Jump to handler. - -.align 4 -.Lhandler_table: // Table of type descriptor to handlers. -MACRO1(HANDLER_TABLE_OFFSET, handle_label) - // NB some tools require 32-bits for relocations. Shouldn't need adjusting. - .long RAW_VAR(handle_label) - .Lhandler_table -END_MACRO - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // A - HANDLER_TABLE_OFFSET(.Lstore_long_result) // B (byte) - HANDLER_TABLE_OFFSET(.Lstore_char_result) // C (char) - HANDLER_TABLE_OFFSET(.Lstore_double_result) // D (double) - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // E - HANDLER_TABLE_OFFSET(.Lstore_float_result) // F (float) - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // G - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // H - HANDLER_TABLE_OFFSET(.Lstore_long_result) // I (int) - HANDLER_TABLE_OFFSET(.Lstore_long_result) // J (long) - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // K - HANDLER_TABLE_OFFSET(.Lstore_long_result) // L (object - references are compressed and only 32-bits) - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // M - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // N - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // O - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // P - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Q - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // R - HANDLER_TABLE_OFFSET(.Lstore_long_result) // S (short) - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // T - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // U - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // V (void) - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // W - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // X - HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Y - HANDLER_TABLE_OFFSET(.Lstore_boolean_result) // Z (boolean) - -.Lstore_boolean_result: - movzbq (%rsp), %rax // Copy boolean result to the accumulator - jmp .Lcleanup_and_return -.Lstore_char_result: - movzwq (%rsp), %rax // Copy char result to the accumulator - jmp .Lcleanup_and_return -.Lstore_float_result: - movd (%rsp), %xmm0 // Copy float result to the context restored by - movd %xmm0, 32(%rsp) // RESTORE_SAVE_REFS_AND_ARGS_FRAME. - jmp .Lcleanup_and_return -.Lstore_double_result: - movsd (%rsp), %xmm0 // Copy double result to the context restored by - movsd %xmm0, 32(%rsp) // RESTORE_SAVE_REFS_AND_ARGS_FRAME. - jmp .Lcleanup_and_return -.Lstore_long_result: - movq (%rsp), %rax // Copy long result to the accumulator. - // Fall-through -.Lcleanup_and_return: - addq LITERAL(16), %rsp // Pop space for JValue result. - CFI_ADJUST_CFA_OFFSET(16) RESTORE_SAVE_REFS_AND_ARGS_FRAME + movq %rax, %xmm0 // Result is in RAX. Copy to FP result register. RETURN_OR_DELIVER_PENDING_EXCEPTION END_FUNCTION art_quick_invoke_polymorphic diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index af6a936d40..d2b8a98de2 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -2722,18 +2722,11 @@ extern "C" TwoWordReturn artInvokeInterfaceTrampoline(ArtMethod* interface_metho reinterpret_cast(method)); } -// Returns shorty type so the caller can determine how to put |result| -// into expected registers. The shorty type is static so the compiler -// could call different flavors of this code path depending on the -// shorty type though this would require different entry points for -// each type. -extern "C" uintptr_t artInvokePolymorphic( - JValue* result, - mirror::Object* raw_receiver, - Thread* self, - ArtMethod** sp) +// Returns uint64_t representing raw bits from JValue. +extern "C" uint64_t artInvokePolymorphic(mirror::Object* raw_receiver, Thread* self, ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); + DCHECK(raw_receiver != nullptr); DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs)); // Start new JNI local reference state @@ -2766,18 +2759,12 @@ extern "C" uintptr_t artInvokePolymorphic( ArtMethod* resolved_method = linker->ResolveMethod( self, inst.VRegB(), caller_method, kVirtual); - if (UNLIKELY(receiver_handle.IsNull())) { - ThrowNullPointerExceptionForMethodAccess(resolved_method, InvokeType::kVirtual); - return static_cast('V'); - } - Handle method_type( hs.NewHandle(linker->ResolveMethodType(self, proto_idx, caller_method))); - - // This implies we couldn't resolve one or more types in this method handle. if (UNLIKELY(method_type.IsNull())) { + // This implies we couldn't resolve one or more types in this method handle. CHECK(self->IsExceptionPending()); - return static_cast('V'); + return 0UL; } DCHECK_EQ(ArtMethod::NumArgRegisters(shorty) + 1u, (uint32_t)inst.VRegA()); @@ -2811,6 +2798,7 @@ extern "C" uintptr_t artInvokePolymorphic( // consecutive order. RangeInstructionOperands operands(first_arg + 1, num_vregs - 1); Intrinsics intrinsic = static_cast(resolved_method->GetIntrinsic()); + JValue result; bool success = false; if (resolved_method->GetDeclaringClass() == GetClassRoot(linker)) { Handle method_handle(hs.NewHandle( @@ -2821,7 +2809,7 @@ extern "C" uintptr_t artInvokePolymorphic( method_handle, method_type, &operands, - result); + &result); } else { DCHECK_EQ(static_cast(intrinsic), static_cast(Intrinsics::kMethodHandleInvoke)); @@ -2830,7 +2818,7 @@ extern "C" uintptr_t artInvokePolymorphic( method_handle, method_type, &operands, - result); + &result); } } else { DCHECK_EQ(GetClassRoot(linker), resolved_method->GetDeclaringClass()); @@ -2844,7 +2832,7 @@ extern "C" uintptr_t artInvokePolymorphic( method_type, access_mode, &operands, - result); + &result); } DCHECK(success || self->IsExceptionPending()); @@ -2852,7 +2840,7 @@ extern "C" uintptr_t artInvokePolymorphic( // Pop transition record. self->PopManagedStackFragment(fragment); - return static_cast(shorty[0]); + return result.GetJ(); } } // namespace art diff --git a/test/956-methodhandles/expected.txt b/test/956-methodhandles/expected.txt index 6954c22ccb..a8b609bd21 100644 --- a/test/956-methodhandles/expected.txt +++ b/test/956-methodhandles/expected.txt @@ -15,6 +15,7 @@ H.chatter() Chatty.chatter() Chatty.chatter() String constructors done. +testReturnValues done. testReferenceReturnValueConversions done. testPrimitiveReturnValueConversions done. Hi diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java index dee818a4ee..11d6ead683 100644 --- a/test/956-methodhandles/src/Main.java +++ b/test/956-methodhandles/src/Main.java @@ -102,6 +102,7 @@ public class Main { testAsType(); testConstructors(); testStringConstructors(); + testReturnValues(); testReturnValueConversions(); testVariableArity(); testVariableArity_MethodHandles_bind(); @@ -873,6 +874,89 @@ public class Main { System.out.println("String constructors done."); } + private static void testReturnValues() throws Throwable { + Lookup lookup = MethodHandles.lookup(); + + // byte + MethodHandle mhByteValue = + lookup.findVirtual(Byte.class, "byteValue", MethodType.methodType(byte.class)); + assertEquals((byte) -77, (byte) mhByteValue.invokeExact(Byte.valueOf((byte) -77))); + assertEquals((byte) -77, (byte) mhByteValue.invoke(Byte.valueOf((byte) -77))); + + // char + MethodHandle mhCharacterValue = + lookup.findStaticGetter(Character.class, "MAX_SURROGATE", char.class); + assertEquals(Character.MAX_SURROGATE, (char) mhCharacterValue.invokeExact()); + assertEquals(Character.MAX_SURROGATE, (char) mhCharacterValue.invoke()); + + // double + MethodHandle mhSin = + lookup.findStatic( + Math.class, "sin", MethodType.methodType(double.class, double.class)); + for (double i = -Math.PI; i <= Math.PI; i += Math.PI / 8) { + assertEquals(Math.sin(i), (double) mhSin.invokeExact(i)); + assertEquals(Math.sin(i), (double) mhSin.invoke(i)); + } + + // float + MethodHandle mhAbsFloat = + lookup.findStatic( + Math.class, "abs", MethodType.methodType(float.class, float.class)); + assertEquals(Math.abs(-3.3e6f), (float) mhAbsFloat.invokeExact(-3.3e6f)); + assertEquals(Math.abs(-3.3e6f), (float) mhAbsFloat.invoke(-3.3e6f)); + + // int + MethodHandle mhAbsInt = + lookup.findStatic(Math.class, "abs", MethodType.methodType(int.class, int.class)); + assertEquals(Math.abs(-1000), (int) mhAbsInt.invokeExact(-1000)); + assertEquals(Math.abs(-1000), (int) mhAbsInt.invoke(-1000)); + + // long + MethodHandle mhMaxLong = + lookup.findStatic( + Math.class, + "max", + MethodType.methodType(long.class, long.class, long.class)); + assertEquals( + Long.MAX_VALUE, (long) mhMaxLong.invokeExact(Long.MAX_VALUE, Long.MAX_VALUE / 2)); + assertEquals(Long.MAX_VALUE, (long) mhMaxLong.invoke(Long.MAX_VALUE, Long.MAX_VALUE / 2)); + assertEquals(0x0123456789abcdefL, (long) mhMaxLong.invokeExact(0x0123456789abcdefL, 0L)); + assertEquals(0x0123456789abcdefL, (long) mhMaxLong.invoke(0x0123456789abcdefL, 0L)); + + // ref + MethodHandle mhShortValueOf = + lookup.findStatic( + Short.class, "valueOf", MethodType.methodType(Short.class, short.class)); + assertEquals( + (short) -7890, ((Short) mhShortValueOf.invokeExact((short) -7890)).shortValue()); + assertEquals((short) -7890, ((Short) mhShortValueOf.invoke((short) -7890)).shortValue()); + + // array + int [] array = {Integer.MIN_VALUE, -1, 0, +1, Integer.MAX_VALUE}; + MethodHandle mhCopyOf = + lookup.findStatic( + Arrays.class, "copyOf", MethodType.methodType(int[].class, int[].class, int.class)); + assertTrue(Arrays.equals(array, (int[]) mhCopyOf.invokeExact(array, array.length))); + assertTrue(Arrays.equals(array, (int[]) mhCopyOf.invoke(array, array.length))); + + // short + MethodHandle mhShortValue = + lookup.findVirtual(Short.class, "shortValue", MethodType.methodType(short.class)); + assertEquals((short) 12131, (short) mhShortValue.invokeExact(Short.valueOf((short) 12131))); + assertEquals((short) 12131, (short) mhShortValue.invoke(Short.valueOf((short) 12131))); + + // boolean + MethodHandle mhBooleanValue = + lookup.findVirtual( + Boolean.class, "booleanValue", MethodType.methodType(boolean.class)); + assertEquals(true, (boolean) mhBooleanValue.invokeExact(Boolean.valueOf(true))); + assertEquals(true, (boolean) mhBooleanValue.invoke(Boolean.valueOf(true))); + assertEquals(false, (boolean) mhBooleanValue.invokeExact(Boolean.valueOf(false))); + assertEquals(false, (boolean) mhBooleanValue.invoke(Boolean.valueOf(false))); + + System.out.println("testReturnValues done."); + } + private static void testReferenceReturnValueConversions() throws Throwable { MethodHandle mh = MethodHandles.lookup().findStatic( Float.class, "valueOf", MethodType.methodType(Float.class, String.class)); -- GitLab From 96f0ec14ba78e4d869065fdcf69b33008927505c Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 8 Jun 2018 15:30:20 +0100 Subject: [PATCH 556/749] Remove DCHECK while investigating. bug: 109666561 Test: m Change-Id: Ia10990d7caeb37d23d3096b087541aac9eea0c3d --- compiler/optimizing/instruction_builder.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 24dc2ee9b4..887f4f6e41 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1313,7 +1313,8 @@ bool HInstructionBuilder::HandleStringInit(HInvoke* invoke, // The only reason a HPhi can flow in a String. is when there is an // irreducible loop, which will create HPhi for all dex registers at loop entry. DCHECK(arg_this->IsPhi()); - DCHECK(graph_->HasIrreducibleLoops()); + // TODO(b/109666561): Re-enable. + // DCHECK(graph_->HasIrreducibleLoops()); // Don't bother compiling a method in that situation. While we could look at all // phis related to the HNewInstance, it's not worth the trouble. MaybeRecordStat(compilation_stats_, -- GitLab From baac7e44445286bb2a048ff24a14a65c537dd9b9 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Fri, 8 Jun 2018 15:30:11 +0000 Subject: [PATCH 557/749] "Revert^4 "Make adbconnection start automatically for debuggable apps (on target)" Needed to tell run-jdwp-tests.sh not to load adbconnection since it might not be present. This reverts commit fb57a6528d73e6ddbccf3049f08f6fa400e6dcd1. Reason for revert: Added -XjdwpProvider:none to run-jdwp-tests.sh Bug: 109505014 Test: ./art/tools/run-libjdwp-tests.sh Change-Id: I43941f7048ae5b64c6cbe7a8e7c273d47596192c --- cmdline/cmdline_parser_test.cc | 2 +- runtime/jdwp_provider.h | 23 ++++++++++++++++++++++- runtime/runtime.cc | 8 +++++++- runtime/runtime_options.def | 2 +- test/etc/run-test-jar | 3 +++ tools/run-jdwp-tests.sh | 7 +++++++ 6 files changed, 41 insertions(+), 4 deletions(-) diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index 235a2aa90e..a52e16328a 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -371,7 +371,7 @@ TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) { */ TEST_F(CmdlineParserTest, TestJdwpProviderEmpty) { { - EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kNone, "", M::JdwpProvider); + EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kUnset, "", M::JdwpProvider); } } // TEST_F diff --git a/runtime/jdwp_provider.h b/runtime/jdwp_provider.h index 698fdc086d..c4f19899c9 100644 --- a/runtime/jdwp_provider.h +++ b/runtime/jdwp_provider.h @@ -19,6 +19,7 @@ #include +#include "base/globals.h" #include "base/macros.h" #include "base/logging.h" @@ -26,13 +27,33 @@ namespace art { enum class JdwpProvider { kNone, + // Special value only used to denote that no explicit choice has been made by the user. This + // should not be used and one should always call CanonicalizeJdwpProvider which will remove this + // value before using a JdwpProvider value. + kUnset, kInternal, kAdbConnection, - // The current default provider + // The current default provider. Used if you run -XjdwpProvider:default kDefaultJdwpProvider = kAdbConnection, + + // What we should use as provider with no options and debuggable. On host we always want to be + // none since there is no adbd on host. + kUnsetDebuggable = kIsTargetBuild ? kDefaultJdwpProvider : kNone, + // What we should use as provider with no options and non-debuggable + kUnsetNonDebuggable = kNone, }; +inline JdwpProvider CanonicalizeJdwpProvider(JdwpProvider p, bool debuggable) { + if (p != JdwpProvider::kUnset) { + return p; + } + if (debuggable) { + return JdwpProvider::kUnsetDebuggable; + } + return JdwpProvider::kUnsetNonDebuggable; +} + std::ostream& operator<<(std::ostream& os, const JdwpProvider& rhs); } // namespace art diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 1e327fc8ed..7efd000f33 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1283,7 +1283,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { dump_gc_performance_on_shutdown_ = runtime_options.Exists(Opt::DumpGCPerformanceOnShutdown); jdwp_options_ = runtime_options.GetOrDefault(Opt::JdwpOptions); - jdwp_provider_ = runtime_options.GetOrDefault(Opt::JdwpProvider); + jdwp_provider_ = CanonicalizeJdwpProvider(runtime_options.GetOrDefault(Opt::JdwpProvider), + IsJavaDebuggable()); switch (jdwp_provider_) { case JdwpProvider::kNone: { VLOG(jdwp) << "Disabling all JDWP support."; @@ -1317,6 +1318,11 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { constexpr const char* plugin_name = kIsDebugBuild ? "libadbconnectiond.so" : "libadbconnection.so"; plugins_.push_back(Plugin::Create(plugin_name)); + break; + } + case JdwpProvider::kUnset: { + LOG(FATAL) << "Illegal jdwp provider " << jdwp_provider_ << " was not filtered out!"; + break; } } callbacks_->AddThreadLifecycleCallback(Dbg::GetThreadLifecycleCallback()); diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index e647423b9c..3f9a3229ca 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -44,7 +44,7 @@ RUNTIME_OPTIONS_KEY (std::string, Image) RUNTIME_OPTIONS_KEY (Unit, CheckJni) RUNTIME_OPTIONS_KEY (Unit, JniOptsForceCopy) RUNTIME_OPTIONS_KEY (std::string, JdwpOptions, "") -RUNTIME_OPTIONS_KEY (JdwpProvider, JdwpProvider, JdwpProvider::kNone) +RUNTIME_OPTIONS_KEY (JdwpProvider, JdwpProvider, JdwpProvider::kUnset) RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryMaximumSize, gc::Heap::kDefaultMaximumSize) // -Xmx RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryInitialSize, gc::Heap::kDefaultInitialSize) // -Xms RUNTIME_OPTIONS_KEY (MemoryKiB, HeapGrowthLimit) // Default is 0 for unlimited diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 1ba433e974..713fd35523 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -377,6 +377,9 @@ CHROOT_DEX_LOCATION="$CHROOT$DEX_LOCATION" if [ "$USE_JVM" = "n" ]; then FLAGS="${FLAGS} ${ANDROID_FLAGS}" + # we don't want to be trying to get adbconnections since the plugin might + # not have been built. + FLAGS="${FLAGS} -XjdwpProvider:none" for feature in ${EXPERIMENTAL}; do FLAGS="${FLAGS} -Xexperimental:${feature} -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:${feature}" COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xexperimental:${feature}" diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index 0796432f68..eb33da2267 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -251,6 +251,9 @@ else vm_args="${vm_args} --vm-arg -Djpda.settings.debuggeeAgentName=${with_jdwp_path}" fi vm_args="$vm_args --vm-arg -Xcompiler-option --vm-arg --debuggable" + # we don't want to be trying to connect to adbconnection which might not have + # been built. + vm_args="${vm_args} --vm-arg -XjdwpProvider:none" # Make sure the debuggee doesn't clean up what the debugger has generated. art_debugee="$art_debugee --no-clean" fi @@ -327,6 +330,10 @@ if [[ $mode != "ri" ]]; then if [[ "x$with_jdwp_path" == "x" ]]; then # Need to enable the internal jdwp implementation. art_debugee="${art_debugee} -XjdwpProvider:internal" + else + # need to disable the jdwpProvider since we give the agent explicitly on the + # cmdline. + art_debugee="${art_debugee} -XjdwpProvider:none" fi else toolchain_args="--toolchain javac --language CUR" -- GitLab From 4c8e12e66968929b36fac6a2237ca4b04160161e Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Fri, 18 May 2018 08:33:20 +0100 Subject: [PATCH 558/749] ART: Adds an entrypoint for invoke-custom Add support for the compiler to call into the runtime for invoke-custom bytecodes. Bug: 35337872 Test: art/test.py --host -r -t 952 Test: art/test.py --target --64 -r -t 952 Test: art/test.py --target --32 -r -t 952 Change-Id: I821432e7e5248c91b8e1d36c3112974c34171803 --- compiler/optimizing/code_generator.cc | 8 + compiler/optimizing/code_generator.h | 3 + compiler/optimizing/code_generator_arm64.cc | 9 + .../optimizing/code_generator_arm_vixl.cc | 35 +- compiler/optimizing/code_generator_mips.cc | 8 + compiler/optimizing/code_generator_mips64.cc | 8 + compiler/optimizing/code_generator_x86.cc | 8 + compiler/optimizing/code_generator_x86_64.cc | 8 + compiler/optimizing/inliner.cc | 7 +- compiler/optimizing/instruction_builder.cc | 109 +++--- compiler/optimizing/instruction_builder.h | 19 +- compiler/optimizing/intrinsics.cc | 1 + compiler/optimizing/nodes.h | 39 ++- .../assembler_thumb_test_expected.cc.inc | 2 +- dex2oat/linker/oat_writer_test.cc | 2 +- libdexfile/dex/dex_file.cc | 9 + libdexfile/dex/dex_file.h | 2 + libdexfile/dex/invoke_type.h | 3 +- runtime/arch/arm/quick_entrypoints_arm.S | 16 +- runtime/arch/arm64/quick_entrypoints_arm64.S | 13 + runtime/arch/mips/entrypoints_init_mips.cc | 3 + runtime/arch/mips/quick_entrypoints_mips.S | 21 ++ .../arch/mips64/quick_entrypoints_mips64.S | 21 ++ runtime/arch/x86/quick_entrypoints_x86.S | 22 ++ .../arch/x86_64/quick_entrypoints_x86_64.S | 11 + runtime/asm_support.h | 2 +- .../entrypoints/quick/quick_default_externs.h | 4 +- .../quick/quick_default_init_entrypoints.h | 1 + .../quick/quick_entrypoints_list.h | 1 + .../quick/quick_trampoline_entrypoints.cc | 59 ++++ runtime/entrypoints_order_test.cc | 4 +- runtime/interpreter/interpreter_common.cc | 115 +++--- runtime/interpreter/interpreter_common.h | 24 +- runtime/mirror/dex_cache-inl.h | 3 +- runtime/mirror/dex_cache.h | 4 +- runtime/oat.h | 4 +- runtime/verifier/method_verifier.cc | 46 ++- .../src/TestReturnValues.java | 330 ++++++++++++++++++ 38 files changed, 826 insertions(+), 158 deletions(-) create mode 100644 test/952-invoke-custom/src/TestReturnValues.java diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 9f2346db3c..b3feb787a9 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -544,6 +544,7 @@ void CodeGenerator::GenerateInvokeStaticOrDirectRuntimeCall( case kVirtual: case kInterface: case kPolymorphic: + case kCustom: LOG(FATAL) << "Unexpected invoke type: " << invoke->GetInvokeType(); UNREACHABLE(); } @@ -572,6 +573,7 @@ void CodeGenerator::GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invok entrypoint = kQuickInvokeInterfaceTrampolineWithAccessCheck; break; case kPolymorphic: + case kCustom: LOG(FATAL) << "Unexpected invoke type: " << invoke->GetInvokeType(); UNREACHABLE(); } @@ -586,6 +588,12 @@ void CodeGenerator::GenerateInvokePolymorphicCall(HInvokePolymorphic* invoke) { InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), nullptr); } +void CodeGenerator::GenerateInvokeCustomCall(HInvokeCustom* invoke) { + MoveConstant(invoke->GetLocations()->GetTemp(0), invoke->GetCallSiteIndex()); + QuickEntrypointEnum entrypoint = kQuickInvokeCustom; + InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), nullptr); +} + void CodeGenerator::CreateUnresolvedFieldLocationSummary( HInstruction* field_access, DataType::Type field_type, diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index a340446ac3..b3c29aa804 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -542,10 +542,13 @@ class CodeGenerator : public DeletableArenaObject { void GenerateInvokeStaticOrDirectRuntimeCall( HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path); + void GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invoke); void GenerateInvokePolymorphicCall(HInvokePolymorphic* invoke); + void GenerateInvokeCustomCall(HInvokeCustom* invoke); + void CreateUnresolvedFieldLocationSummary( HInstruction* field_access, DataType::Type field_type, diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 6f173e19f5..5f0533cbe9 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -4695,6 +4695,15 @@ void InstructionCodeGeneratorARM64::VisitInvokePolymorphic(HInvokePolymorphic* i codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__); } +void LocationsBuilderARM64::VisitInvokeCustom(HInvokeCustom* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorARM64::VisitInvokeCustom(HInvokeCustom* invoke) { + codegen_->GenerateInvokeCustomCall(invoke); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__); +} + vixl::aarch64::Label* CodeGeneratorARM64::NewBootImageRelRoPatch( uint32_t boot_image_offset, vixl::aarch64::Label* adrp_label) { diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 859e1597c6..91c13154bb 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -3742,6 +3742,15 @@ void InstructionCodeGeneratorARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 9); } +void LocationsBuilderARMVIXL::VisitInvokeCustom(HInvokeCustom* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorARMVIXL::VisitInvokeCustom(HInvokeCustom* invoke) { + codegen_->GenerateInvokeCustomCall(invoke); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 10); +} + void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall); @@ -5493,7 +5502,7 @@ void InstructionCodeGeneratorARMVIXL::VisitNewInstance(HNewInstance* instruction codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); CheckEntrypointTypes(); } - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 10); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 11); } void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) { @@ -5513,7 +5522,7 @@ void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) { codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc()); CheckEntrypointTypes(); DCHECK(!codegen_->IsLeafMethod()); - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 11); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 12); } void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) { @@ -7084,7 +7093,7 @@ void InstructionCodeGeneratorARMVIXL::VisitSuspendCheck(HSuspendCheck* instructi return; } GenerateSuspendCheck(instruction, nullptr); - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 12); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 13); } void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction, @@ -7437,7 +7446,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ HLoadClass::LoadKind load_kind = cls->GetLoadKind(); if (load_kind == HLoadClass::LoadKind::kRuntimeCall) { codegen_->GenerateLoadClassRuntimeCall(cls); - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 13); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 14); return; } DCHECK(!cls->NeedsAccessCheck()); @@ -7523,7 +7532,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ } else { __ Bind(slow_path->GetExitLabel()); } - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 14); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 15); } } @@ -7732,7 +7741,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE codegen_->AddSlowPath(slow_path); __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 15); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 16); return; } case HLoadString::LoadKind::kJitTableAddress: { @@ -7754,7 +7763,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_); codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc()); CheckEntrypointTypes(); - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 16); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 17); } static int32_t GetExceptionTlsOffset() { @@ -8384,7 +8393,7 @@ void InstructionCodeGeneratorARMVIXL::VisitMonitorOperation(HMonitorOperation* i } else { CheckEntrypointTypes(); } - codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 17); + codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 18); } void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) { @@ -8883,7 +8892,7 @@ void CodeGeneratorARMVIXL::GenerateGcRootFieldLoad( // Note that GC roots are not affected by heap poisoning, thus we // do not have to unpoison `root_reg` here. } - MaybeGenerateMarkingRegisterCheck(/* code */ 18); + MaybeGenerateMarkingRegisterCheck(/* code */ 19); } void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -8963,7 +8972,7 @@ void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* i narrow ? BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET : BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET); } - MaybeGenerateMarkingRegisterCheck(/* code */ 19, /* temp_loc */ LocationFrom(ip)); + MaybeGenerateMarkingRegisterCheck(/* code */ 20, /* temp_loc */ LocationFrom(ip)); return; } @@ -9041,7 +9050,7 @@ void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(HInstruction* i DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(), BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET); } - MaybeGenerateMarkingRegisterCheck(/* code */ 20, /* temp_loc */ LocationFrom(ip)); + MaybeGenerateMarkingRegisterCheck(/* code */ 21, /* temp_loc */ LocationFrom(ip)); return; } @@ -9095,7 +9104,7 @@ void CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier(HInstructio // Fast path: the GC is not marking: just load the reference. GenerateRawReferenceLoad(instruction, ref, obj, offset, index, scale_factor, needs_null_check); __ Bind(slow_path->GetExitLabel()); - MaybeGenerateMarkingRegisterCheck(/* code */ 21); + MaybeGenerateMarkingRegisterCheck(/* code */ 22); } void CodeGeneratorARMVIXL::UpdateReferenceFieldWithBakerReadBarrier(HInstruction* instruction, @@ -9150,7 +9159,7 @@ void CodeGeneratorARMVIXL::UpdateReferenceFieldWithBakerReadBarrier(HInstruction // Fast path: the GC is not marking: nothing to do (the field is // up-to-date, and we don't need to load the reference). __ Bind(slow_path->GetExitLabel()); - MaybeGenerateMarkingRegisterCheck(/* code */ 22); + MaybeGenerateMarkingRegisterCheck(/* code */ 23); } void CodeGeneratorARMVIXL::GenerateRawReferenceLoad(HInstruction* instruction, diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 8be84a15bd..507db364b5 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -7795,6 +7795,14 @@ void InstructionCodeGeneratorMIPS::VisitInvokePolymorphic(HInvokePolymorphic* in codegen_->GenerateInvokePolymorphicCall(invoke); } +void LocationsBuilderMIPS::VisitInvokeCustom(HInvokeCustom* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorMIPS::VisitInvokeCustom(HInvokeCustom* invoke) { + codegen_->GenerateInvokeCustomCall(invoke); +} + static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS* codegen) { if (invoke->GetLocations()->Intrinsified()) { IntrinsicCodeGeneratorMIPS intrinsic(codegen); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index cd9e0e521e..08a6512feb 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -5908,6 +5908,14 @@ void InstructionCodeGeneratorMIPS64::VisitInvokePolymorphic(HInvokePolymorphic* codegen_->GenerateInvokePolymorphicCall(invoke); } +void LocationsBuilderMIPS64::VisitInvokeCustom(HInvokeCustom* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorMIPS64::VisitInvokeCustom(HInvokeCustom* invoke) { + codegen_->GenerateInvokeCustomCall(invoke); +} + static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS64* codegen) { if (invoke->GetLocations()->Intrinsified()) { IntrinsicCodeGeneratorMIPS64 intrinsic(codegen); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 9e315381b1..9f42ac76f5 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -2311,6 +2311,14 @@ void InstructionCodeGeneratorX86::VisitInvokePolymorphic(HInvokePolymorphic* inv codegen_->GenerateInvokePolymorphicCall(invoke); } +void LocationsBuilderX86::VisitInvokeCustom(HInvokeCustom* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorX86::VisitInvokeCustom(HInvokeCustom* invoke) { + codegen_->GenerateInvokeCustomCall(invoke); +} + void LocationsBuilderX86::VisitNeg(HNeg* neg) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index f7397046d7..05194b15d5 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -2501,6 +2501,14 @@ void InstructionCodeGeneratorX86_64::VisitInvokePolymorphic(HInvokePolymorphic* codegen_->GenerateInvokePolymorphicCall(invoke); } +void LocationsBuilderX86_64::VisitInvokeCustom(HInvokeCustom* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorX86_64::VisitInvokeCustom(HInvokeCustom* invoke) { + codegen_->GenerateInvokeCustomCall(invoke); +} + void LocationsBuilderX86_64::VisitNeg(HNeg* neg) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall); diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 6900cd883a..7dd756e13a 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -460,9 +460,10 @@ static bool AlwaysThrows(CompilerDriver* const compiler_driver, ArtMethod* metho bool HInliner::TryInline(HInvoke* invoke_instruction) { if (invoke_instruction->IsInvokeUnresolved() || - invoke_instruction->IsInvokePolymorphic()) { - return false; // Don't bother to move further if we know the method is unresolved or an - // invoke-polymorphic. + invoke_instruction->IsInvokePolymorphic() || + invoke_instruction->IsInvokeCustom()) { + return false; // Don't bother to move further if we know the method is unresolved or the + // invocation is polymorphic (invoke-{polymorphic,custom}). } ScopedObjectAccess soa(Thread::Current()); diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 24dc2ee9b4..be8f2b1c74 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -449,11 +449,7 @@ void HInstructionBuilder::BuildIntrinsic(ArtMethod* method) { target_method, HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); RangeInstructionOperands operands(graph_->GetNumberOfVRegs() - in_vregs, in_vregs); - HandleInvoke(invoke, - operands, - dex_file_->GetMethodShorty(method_idx), - /* clinit_check */ nullptr, - /* is_unresolved */ false); + HandleInvoke(invoke, operands, dex_file_->GetMethodShorty(method_idx), /* is_unresolved */ false); // Add the return instruction. if (return_type_ == DataType::Type::kVoid) { @@ -916,11 +912,11 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, uint32_t method_idx, const InstructionOperands& operands) { InvokeType invoke_type = GetInvokeTypeFromOpCode(instruction.Opcode()); - const char* descriptor = dex_file_->GetMethodShorty(method_idx); - DataType::Type return_type = DataType::FromShorty(descriptor[0]); + const char* shorty = dex_file_->GetMethodShorty(method_idx); + DataType::Type return_type = DataType::FromShorty(shorty[0]); // Remove the return type from the 'proto'. - size_t number_of_arguments = strlen(descriptor) - 1; + size_t number_of_arguments = strlen(shorty) - 1; if (invoke_type != kStatic) { // instance call // One extra argument for 'this'. number_of_arguments++; @@ -937,11 +933,7 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, dex_pc, method_idx, invoke_type); - return HandleInvoke(invoke, - operands, - descriptor, - nullptr /* clinit_check */, - true /* is_unresolved */); + return HandleInvoke(invoke, operands, shorty, /* is_unresolved */ true); } // Replace calls to String. with StringFactory. @@ -968,7 +960,7 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, invoke_type, target_method, HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit); - return HandleStringInit(invoke, operands, descriptor); + return HandleStringInit(invoke, operands, shorty); } // Potential class initialization check, in the case of a static method call. @@ -1028,29 +1020,39 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, resolved_method, ImTable::GetImtIndex(resolved_method)); } - - return HandleInvoke(invoke, operands, descriptor, clinit_check, false /* is_unresolved */); + return HandleInvoke(invoke, operands, shorty, /* is_unresolved */ false, clinit_check); } -bool HInstructionBuilder::BuildInvokePolymorphic(const Instruction& instruction ATTRIBUTE_UNUSED, - uint32_t dex_pc, +bool HInstructionBuilder::BuildInvokePolymorphic(uint32_t dex_pc, uint32_t method_idx, dex::ProtoIndex proto_idx, const InstructionOperands& operands) { - const char* descriptor = dex_file_->GetShorty(proto_idx); - DCHECK_EQ(1 + ArtMethod::NumArgRegisters(descriptor), operands.GetNumberOfOperands()); - DataType::Type return_type = DataType::FromShorty(descriptor[0]); - size_t number_of_arguments = strlen(descriptor); + const char* shorty = dex_file_->GetShorty(proto_idx); + DCHECK_EQ(1 + ArtMethod::NumArgRegisters(shorty), operands.GetNumberOfOperands()); + DataType::Type return_type = DataType::FromShorty(shorty[0]); + size_t number_of_arguments = strlen(shorty); HInvoke* invoke = new (allocator_) HInvokePolymorphic(allocator_, number_of_arguments, return_type, dex_pc, method_idx); - return HandleInvoke(invoke, - operands, - descriptor, - nullptr /* clinit_check */, - false /* is_unresolved */); + return HandleInvoke(invoke, operands, shorty, /* is_unresolved */ false); +} + + +bool HInstructionBuilder::BuildInvokeCustom(uint32_t dex_pc, + uint32_t call_site_idx, + const InstructionOperands& operands) { + dex::ProtoIndex proto_idx = dex_file_->GetProtoIndexForCallSite(call_site_idx); + const char* shorty = dex_file_->GetShorty(proto_idx); + DataType::Type return_type = DataType::FromShorty(shorty[0]); + size_t number_of_arguments = strlen(shorty) - 1; + HInvoke* invoke = new (allocator_) HInvokeCustom(allocator_, + number_of_arguments, + call_site_idx, + return_type, + dex_pc); + return HandleInvoke(invoke, operands, shorty, /* is_unresolved */ false); } HNewInstance* HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, uint32_t dex_pc) { @@ -1197,10 +1199,10 @@ HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke( bool HInstructionBuilder::SetupInvokeArguments(HInvoke* invoke, const InstructionOperands& operands, - const char* descriptor, + const char* shorty, size_t start_index, size_t* argument_index) { - uint32_t descriptor_index = 1; // Skip the return type. + uint32_t shorty_index = 1; // Skip the return type. const size_t number_of_operands = operands.GetNumberOfOperands(); for (size_t i = start_index; // Make sure we don't go over the expected arguments or over the number of @@ -1208,7 +1210,7 @@ bool HInstructionBuilder::SetupInvokeArguments(HInvoke* invoke, // it hasn't been properly checked. (i < number_of_operands) && (*argument_index < invoke->GetNumberOfArguments()); i++, (*argument_index)++) { - DataType::Type type = DataType::FromShorty(descriptor[descriptor_index++]); + DataType::Type type = DataType::FromShorty(shorty[shorty_index++]); bool is_wide = (type == DataType::Type::kInt64) || (type == DataType::Type::kFloat64); if (is_wide && ((i + 1 == number_of_operands) || (operands.GetOperand(i) + 1 != operands.GetOperand(i + 1)))) { @@ -1250,9 +1252,9 @@ bool HInstructionBuilder::SetupInvokeArguments(HInvoke* invoke, bool HInstructionBuilder::HandleInvoke(HInvoke* invoke, const InstructionOperands& operands, - const char* descriptor, - HClinitCheck* clinit_check, - bool is_unresolved) { + const char* shorty, + bool is_unresolved, + HClinitCheck* clinit_check) { DCHECK(!invoke->IsInvokeStaticOrDirect() || !invoke->AsInvokeStaticOrDirect()->IsStringInit()); size_t start_index = 0; @@ -1267,7 +1269,7 @@ bool HInstructionBuilder::HandleInvoke(HInvoke* invoke, argument_index = 1; } - if (!SetupInvokeArguments(invoke, operands, descriptor, start_index, &argument_index)) { + if (!SetupInvokeArguments(invoke, operands, shorty, start_index, &argument_index)) { return false; } @@ -1288,13 +1290,13 @@ bool HInstructionBuilder::HandleInvoke(HInvoke* invoke, bool HInstructionBuilder::HandleStringInit(HInvoke* invoke, const InstructionOperands& operands, - const char* descriptor) { + const char* shorty) { DCHECK(invoke->IsInvokeStaticOrDirect()); DCHECK(invoke->AsInvokeStaticOrDirect()->IsStringInit()); size_t start_index = 1; size_t argument_index = 0; - if (!SetupInvokeArguments(invoke, operands, descriptor, start_index, &argument_index)) { + if (!SetupInvokeArguments(invoke, operands, shorty, start_index, &argument_index)) { return false; } @@ -2144,14 +2146,28 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, uint32_t args[5]; uint32_t number_of_vreg_arguments = instruction.GetVarArgs(args); VarArgsInstructionOperands operands(args, number_of_vreg_arguments); - return BuildInvokePolymorphic(instruction, dex_pc, method_idx, proto_idx, operands); + return BuildInvokePolymorphic(dex_pc, method_idx, proto_idx, operands); } case Instruction::INVOKE_POLYMORPHIC_RANGE: { uint16_t method_idx = instruction.VRegB_4rcc(); dex::ProtoIndex proto_idx(instruction.VRegH_4rcc()); RangeInstructionOperands operands(instruction.VRegC_4rcc(), instruction.VRegA_4rcc()); - return BuildInvokePolymorphic(instruction, dex_pc, method_idx, proto_idx, operands); + return BuildInvokePolymorphic(dex_pc, method_idx, proto_idx, operands); + } + + case Instruction::INVOKE_CUSTOM: { + uint16_t call_site_idx = instruction.VRegB_35c(); + uint32_t args[5]; + uint32_t number_of_vreg_arguments = instruction.GetVarArgs(args); + VarArgsInstructionOperands operands(args, number_of_vreg_arguments); + return BuildInvokeCustom(dex_pc, call_site_idx, operands); + } + + case Instruction::INVOKE_CUSTOM_RANGE: { + uint16_t call_site_idx = instruction.VRegB_3rc(); + RangeInstructionOperands operands(instruction.VRegC_3rc(), instruction.VRegA_3rc()); + return BuildInvokeCustom(dex_pc, call_site_idx, operands); } case Instruction::NEG_INT: { @@ -2933,7 +2949,21 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, break; } - default: + case Instruction::UNUSED_3E: + case Instruction::UNUSED_3F: + case Instruction::UNUSED_40: + case Instruction::UNUSED_41: + case Instruction::UNUSED_42: + case Instruction::UNUSED_43: + case Instruction::UNUSED_79: + case Instruction::UNUSED_7A: + case Instruction::UNUSED_F3: + case Instruction::UNUSED_F4: + case Instruction::UNUSED_F5: + case Instruction::UNUSED_F6: + case Instruction::UNUSED_F7: + case Instruction::UNUSED_F8: + case Instruction::UNUSED_F9: { VLOG(compiler) << "Did not compile " << dex_file_->PrettyMethod(dex_compilation_unit_->GetDexMethodIndex()) << " because of unhandled instruction " @@ -2941,6 +2971,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, MaybeRecordStat(compilation_stats_, MethodCompilationStat::kNotCompiledUnhandledInstruction); return false; + } } return true; } // NOLINT(readability/fn_size) diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index 2218a691ea..af7092a0cf 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -173,12 +173,17 @@ class HInstructionBuilder : public ValueObject { // Builds an invocation node for invoke-polymorphic and returns whether the // instruction is supported. - bool BuildInvokePolymorphic(const Instruction& instruction, - uint32_t dex_pc, + bool BuildInvokePolymorphic(uint32_t dex_pc, uint32_t method_idx, dex::ProtoIndex proto_idx, const InstructionOperands& operands); + // Builds an invocation node for invoke-custom and returns whether the + // instruction is supported. + bool BuildInvokeCustom(uint32_t dex_pc, + uint32_t call_site_idx, + const InstructionOperands& operands); + // Builds a new array node and the instructions that fill it. HNewArray* BuildFilledNewArray(uint32_t dex_pc, dex::TypeIndex type_index, @@ -253,19 +258,19 @@ class HInstructionBuilder : public ValueObject { bool SetupInvokeArguments(HInvoke* invoke, const InstructionOperands& operands, - const char* descriptor, + const char* shorty, size_t start_index, size_t* argument_index); bool HandleInvoke(HInvoke* invoke, const InstructionOperands& operands, - const char* descriptor, - HClinitCheck* clinit_check, - bool is_unresolved); + const char* shorty, + bool is_unresolved, + HClinitCheck* clinit_check = nullptr); bool HandleStringInit(HInvoke* invoke, const InstructionOperands& operands, - const char* descriptor); + const char* shorty); void HandleStringInitResult(HInvokeStaticOrDirect* invoke); HClinitCheck* ProcessClinitCheckForInvoke( diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 056f533398..4ffde7bc52 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -142,6 +142,7 @@ static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) case kSuper: case kInterface: case kPolymorphic: + case kCustom: return false; } LOG(FATAL) << "Unknown intrinsic invoke type: " << intrinsic_type; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 3fd5b6b02d..2037879726 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1380,6 +1380,7 @@ class HLoopInformationOutwardIterator : public ValueObject { M(InvokeStaticOrDirect, Invoke) \ M(InvokeVirtual, Invoke) \ M(InvokePolymorphic, Invoke) \ + M(InvokeCustom, Invoke) \ M(LessThan, Condition) \ M(LessThanOrEqual, Condition) \ M(LoadClass, Instruction) \ @@ -4382,6 +4383,38 @@ class HInvokePolymorphic FINAL : public HInvoke { DEFAULT_COPY_CONSTRUCTOR(InvokePolymorphic); }; +class HInvokeCustom FINAL : public HInvoke { + public: + HInvokeCustom(ArenaAllocator* allocator, + uint32_t number_of_arguments, + uint32_t call_site_index, + DataType::Type return_type, + uint32_t dex_pc) + : HInvoke(kInvokeCustom, + allocator, + number_of_arguments, + /* number_of_other_inputs */ 0u, + return_type, + dex_pc, + /* dex_method_index */ dex::kDexNoIndex, + /* resolved_method */ nullptr, + kStatic), + call_site_index_(call_site_index) { + } + + uint32_t GetCallSiteIndex() const { return call_site_index_; } + + bool IsClonable() const OVERRIDE { return true; } + + DECLARE_INSTRUCTION(InvokeCustom); + + protected: + DEFAULT_COPY_CONSTRUCTOR(InvokeCustom); + + private: + uint32_t call_site_index_; +}; + class HInvokeStaticOrDirect FINAL : public HInvoke { public: // Requirements of this method call regarding the class @@ -6510,9 +6543,9 @@ inline void HLoadString::AddSpecialInput(HInstruction* special_input) { class HLoadMethodHandle FINAL : public HInstruction { public: HLoadMethodHandle(HCurrentMethod* current_method, - uint16_t method_handle_idx, - const DexFile& dex_file, - uint32_t dex_pc) + uint16_t method_handle_idx, + const DexFile& dex_file, + uint32_t dex_pc) : HInstruction(kLoadMethodHandle, DataType::Type::kReference, SideEffectsForArchRuntimeCalls(), diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc index 19c405e517..e76e98a2a3 100644 --- a/compiler/utils/assembler_thumb_test_expected.cc.inc +++ b/compiler/utils/assembler_thumb_test_expected.cc.inc @@ -153,7 +153,7 @@ const char* const VixlJniHelpersResults[] = { " 21c: f8d9 8034 ldr.w r8, [r9, #52] ; 0x34\n", " 220: 4770 bx lr\n", " 222: 4660 mov r0, ip\n", - " 224: f8d9 c2cc ldr.w ip, [r9, #716] ; 0x2cc\n", + " 224: f8d9 c2d0 ldr.w ip, [r9, #720] ; 0x2d0\n", " 228: 47e0 blx ip\n", nullptr }; diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index d0a6eb9ff2..2bc286a7f4 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -497,7 +497,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(76U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(24U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(164 * static_cast(GetInstructionSetPointerSize(kRuntimeISA)), + EXPECT_EQ(165 * static_cast(GetInstructionSetPointerSize(kRuntimeISA)), sizeof(QuickEntryPoints)); } diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc index f570158dfb..f1f896058c 100644 --- a/libdexfile/dex/dex_file.cc +++ b/libdexfile/dex/dex_file.cc @@ -605,6 +605,15 @@ std::string DexFile::PrettyType(dex::TypeIndex type_idx) const { return PrettyDescriptor(GetTypeDescriptor(type_id)); } +dex::ProtoIndex DexFile::GetProtoIndexForCallSite(uint32_t call_site_idx) const { + const DexFile::CallSiteIdItem& csi = GetCallSiteId(call_site_idx); + CallSiteArrayValueIterator it(*this, csi); + it.Next(); + it.Next(); + DCHECK_EQ(EncodedArrayValueIterator::ValueType::kMethodType, it.GetValueType()); + return dex::ProtoIndex(it.GetJavaValue().i); +} + // Checks that visibility is as expected. Includes special behavior for M and // before to allow runtime and build visibility when expecting runtime. std::ostream& operator<<(std::ostream& os, const DexFile& dex_file) { diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index ed219808d2..a8cd1871b4 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -737,6 +737,8 @@ class DexFile { return DataBegin() + call_site_id.data_off_; } + dex::ProtoIndex GetProtoIndexForCallSite(uint32_t call_site_idx) const; + static const TryItem* GetTryItems(const DexInstructionIterator& code_item_end, uint32_t offset); // Get the base of the encoded data for the given DexCode. diff --git a/libdexfile/dex/invoke_type.h b/libdexfile/dex/invoke_type.h index 9b3af673a8..1740c079bb 100644 --- a/libdexfile/dex/invoke_type.h +++ b/libdexfile/dex/invoke_type.h @@ -28,7 +28,8 @@ enum InvokeType : uint32_t { kSuper, // <> kInterface, // <> kPolymorphic, // <> - kMaxInvokeType = kPolymorphic + kCustom, // <> + kMaxInvokeType = kCustom }; std::ostream& operator<<(std::ostream& os, const InvokeType& rhs); diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 2ef30c0d26..ccff9f6a7b 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -2693,10 +2693,24 @@ ENTRY art_quick_invoke_polymorphic str r1, [sp, 72] @ r0:r1 := Result. Copy r1 to context. RESTORE_SAVE_REFS_AND_ARGS_FRAME REFRESH_MARKING_REGISTER - vmov d0, r0, r1 @ Put result r0:r1 into floating point return register. + vmov d0, r0, r1 @ Put result r0:r1 into floating point return register. RETURN_OR_DELIVER_PENDING_EXCEPTION_REG r2 END art_quick_invoke_polymorphic +.extern artInvokeCustom +ENTRY art_quick_invoke_custom + SETUP_SAVE_REFS_AND_ARGS_FRAME r1 + @ r0 := call_site_idx + mov r1, rSELF @ r1 := Thread::Current + mov r2, sp @ r2 := SP + bl artInvokeCustom @ artInvokeCustom(call_site_idx, Thread*, SP) + str r1, [sp, #72] @ Save r1 to context (r0:r1 = result) + RESTORE_SAVE_REFS_AND_ARGS_FRAME + REFRESH_MARKING_REGISTER + vmov d0, r0, r1 @ Put result r0:r1 into floating point return register. + RETURN_OR_DELIVER_PENDING_EXCEPTION_REG r2 +END art_quick_invoke_custom + // 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. diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 5e540dd5e9..80d5fce423 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -2855,6 +2855,19 @@ ENTRY art_quick_invoke_polymorphic RETURN_OR_DELIVER_PENDING_EXCEPTION END art_quick_invoke_polymorphic +.extern artInvokeCustom +ENTRY art_quick_invoke_custom + SETUP_SAVE_REFS_AND_ARGS_FRAME // Save callee saves in case allocation triggers GC. + // x0 := call_site_idx + mov x1, xSELF // x1 := Thread::Current() + mov x2, sp // x2 := SP + bl artInvokeCustom // artInvokeCustom(call_site_idx, thread, save_area) + RESTORE_SAVE_REFS_AND_ARGS_FRAME + REFRESH_MARKING_REGISTER + fmov d0, x0 // Copy result to double result register. + RETURN_OR_DELIVER_PENDING_EXCEPTION +END art_quick_invoke_custom + // 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. diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 5d6e410101..2b69c1753b 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -410,6 +410,9 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { static_assert(!IsDirectEntrypoint(kQuickInvokeVirtualTrampolineWithAccessCheck), "Non-direct C stub marked direct."); qpoints->pInvokePolymorphic = art_quick_invoke_polymorphic; + static_assert(!IsDirectEntrypoint(kQuickInvokePolymorphic), "Non-direct C stub marked direct."); + qpoints->pInvokeCustom = art_quick_invoke_custom; + static_assert(!IsDirectEntrypoint(kQuickInvokeCustom), "Non-direct C stub marked direct."); // Thread qpoints->pTestSuspend = art_quick_test_suspend; diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index c9bdc969ee..508a2013b7 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -3270,3 +3270,24 @@ ENTRY art_quick_invoke_polymorphic 1: DELIVER_PENDING_EXCEPTION END art_quick_invoke_polymorphic + + /* + * InvokeCustom invocation. + * On entry: + * a0 = call_site_idx + */ +.extern artInvokeCustom +ENTRY art_quick_invoke_custom + SETUP_SAVE_REFS_AND_ARGS_FRAME + move $a1, rSELF # Make $a1 an alias for the current Thread. + la $t9, artInvokeCustom # Invoke artInvokeCustom + jalr $t9 # with args (call_site_idx, Thread*, context). + addiu $a2, $sp, ARG_SLOT_SIZE # Make $a2 a pointer to the saved frame context. + lw $t7, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_ + RESTORE_SAVE_REFS_AND_ARGS_FRAME + bnez $t7, 1f + # don't care if $v0 and/or $v1 are modified, when exception branch taken + MTD $v0, $v1, $f0, $f1 # move float value to return value + jalr $zero, $ra + nop +END art_quick_invoke_custom diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 1800056f77..258acddd47 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -3070,4 +3070,25 @@ ENTRY art_quick_invoke_polymorphic DELIVER_PENDING_EXCEPTION END art_quick_invoke_polymorphic + /* + * InvokeCustom invocation. + * On entry: + * a0 = call_site_idx + */ +.extern artInvokeCustom +ENTRY art_quick_invoke_custom + SETUP_SAVE_REFS_AND_ARGS_FRAME + move $a1, rSELF # Make $a1 an alias for the current Thread. + jal artInvokeCustom # Call artInvokeCustom(call_site_idx, Thread*, context). + move $a2, $sp # Make $a1 a pointer to the saved frame context. + ld $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_ + daddiu $sp, $sp, REFS_AND_ARGS_MINUS_REFS_SIZE # skip a0-a7 and f12-f19 + RESTORE_SAVE_REFS_ONLY_FRAME + bne $t0, $zero, 1f + dmtc1 $v0, $f0 # place return value to FP return value + jalr $zero, $ra + dmtc1 $v1, $f1 # place return value to FP return value +1: + DELIVER_PENDING_EXCEPTION +END art_quick_invoke_polymorphic .set pop diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index e392198d44..e1b3df8621 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -2455,6 +2455,28 @@ DEFINE_FUNCTION art_quick_invoke_polymorphic RETURN_OR_DELIVER_PENDING_EXCEPTION END_FUNCTION art_quick_invoke_polymorphic +DEFINE_FUNCTION art_quick_invoke_custom + SETUP_SAVE_REFS_AND_ARGS_FRAME ebx, ebx // Save frame. + // EAX := call_site_index + mov %esp, %ecx // Remember SP. + subl LITERAL(4), %esp // Alignment padding. + CFI_ADJUST_CFA_OFFSET(4) + push %ecx // pass SP + CFI_ADJUST_CFA_OFFSET(4) + pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() + CFI_ADJUST_CFA_OFFSET(4) + push %eax // pass call_site_index + CFI_ADJUST_CFA_OFFSET(4) + call SYMBOL(artInvokeCustom) // artInvokeCustom(call_site_index, Thread*, SP) + addl LITERAL(16), %esp // Pop arguments. + CFI_ADJUST_CFA_OFFSET(-16) + mov %eax, 4(%esp) // Result is in EAX:EDX. Copy to saved FP state. + mov %edx, 8(%esp) + mov %edx, 40(%esp) // Copy EDX to saved context + RESTORE_SAVE_REFS_AND_ARGS_FRAME + RETURN_OR_DELIVER_PENDING_EXCEPTION +END_FUNCTION art_quick_invoke_custom + // 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. diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 3f5d4f669a..9980966967 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -2430,6 +2430,17 @@ DEFINE_FUNCTION art_quick_invoke_polymorphic RETURN_OR_DELIVER_PENDING_EXCEPTION END_FUNCTION art_quick_invoke_polymorphic +DEFINE_FUNCTION art_quick_invoke_custom + SETUP_SAVE_REFS_AND_ARGS_FRAME // save callee saves + // RDI := call_site_index + movq %gs:THREAD_SELF_OFFSET, %rsi // RSI := Thread::Current() + movq %rsp, %rdx // RDX := SP + call SYMBOL(artInvokeCustom) // artInvokeCustom(Thread*, SP) + RESTORE_SAVE_REFS_AND_ARGS_FRAME + movq %rax, %xmm0 // Result is in RAX. Copy to FP result register. + RETURN_OR_DELIVER_PENDING_EXCEPTION +END_FUNCTION art_quick_invoke_custom + // 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. diff --git a/runtime/asm_support.h b/runtime/asm_support.h index 70ff40d32c..ffbff88421 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -73,7 +73,7 @@ ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, // Offset of field Thread::tlsPtr_.mterp_current_ibase. #define THREAD_CURRENT_IBASE_OFFSET \ - (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 164) * __SIZEOF_POINTER__) + (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 165) * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET, art::Thread::MterpCurrentIBaseOffset().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_default_ibase. diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h index 1804d9e64d..938489b730 100644 --- a/runtime/entrypoints/quick/quick_default_externs.h +++ b/runtime/entrypoints/quick/quick_default_externs.h @@ -114,9 +114,9 @@ extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, vo extern "C" void art_quick_invoke_virtual_trampoline_with_access_check(uint32_t, void*); -// Invoke polymorphic entrypoint. Return type is dynamic and may be void, a primitive value, or -// reference return type. +// Polymorphic invoke entrypoints. extern "C" void art_quick_invoke_polymorphic(uint32_t, void*); +extern "C" void art_quick_invoke_custom(uint32_t, void*); // Thread entrypoints. extern "C" void art_quick_test_suspend(); diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h index 3f66045576..5dcece4208 100644 --- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h +++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h @@ -106,6 +106,7 @@ static void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qp qpoints->pInvokeVirtualTrampolineWithAccessCheck = art_quick_invoke_virtual_trampoline_with_access_check; qpoints->pInvokePolymorphic = art_quick_invoke_polymorphic; + qpoints->pInvokeCustom = art_quick_invoke_custom; // Thread qpoints->pTestSuspend = art_quick_test_suspend; diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h index 3a8faca11d..415a158326 100644 --- a/runtime/entrypoints/quick/quick_entrypoints_list.h +++ b/runtime/entrypoints/quick/quick_entrypoints_list.h @@ -134,6 +134,7 @@ V(InvokeSuperTrampolineWithAccessCheck, void, uint32_t, void*) \ V(InvokeVirtualTrampolineWithAccessCheck, void, uint32_t, void*) \ V(InvokePolymorphic, void, uint32_t, void*) \ + V(InvokeCustom, void, uint32_t, void*) \ \ V(TestSuspend, void, void) \ \ diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index d2b8a98de2..2b1623ac5a 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -35,6 +35,7 @@ #include "index_bss_mapping.h" #include "instrumentation.h" #include "interpreter/interpreter.h" +#include "interpreter/interpreter_common.h" #include "interpreter/shadow_frame-inl.h" #include "jit/jit.h" #include "linear_alloc.h" @@ -2843,4 +2844,62 @@ extern "C" uint64_t artInvokePolymorphic(mirror::Object* raw_receiver, Thread* s return result.GetJ(); } +// Returns uint64_t representing raw bits from JValue. +extern "C" uint64_t artInvokeCustom(uint32_t call_site_idx, Thread* self, ArtMethod** sp) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); + DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs)); + + // invoke-custom is effectively a static call (no receiver). + static constexpr bool kMethodIsStatic = true; + + // Start new JNI local reference state + JNIEnvExt* env = self->GetJniEnv(); + ScopedObjectAccessUnchecked soa(env); + ScopedJniEnvLocalRefState env_state(env); + + const char* old_cause = self->StartAssertNoThreadSuspension("Making stack arguments safe."); + + // From the instruction, get the |callsite_shorty| and expose arguments on the stack to the GC. + ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp); + uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp); + const DexFile* dex_file = caller_method->GetDexFile(); + const dex::ProtoIndex proto_idx(dex_file->GetProtoIndexForCallSite(call_site_idx)); + const char* shorty = caller_method->GetDexFile()->GetShorty(proto_idx); + const uint32_t shorty_len = strlen(shorty); + + // Construct the shadow frame placing arguments consecutively from |first_arg|. + const size_t first_arg = 0; + const size_t num_vregs = ArtMethod::NumArgRegisters(shorty); + ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr = + CREATE_SHADOW_FRAME(num_vregs, /* link */ nullptr, caller_method, dex_pc); + ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get(); + ScopedStackedShadowFramePusher + frame_pusher(self, shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction); + BuildQuickShadowFrameVisitor shadow_frame_builder(sp, + kMethodIsStatic, + shorty, + shorty_len, + shadow_frame, + first_arg); + shadow_frame_builder.VisitArguments(); + + // Push a transition back into managed code onto the linked list in thread. + ManagedStack fragment; + self->PushManagedStackFragment(&fragment); + self->EndAssertNoThreadSuspension(old_cause); + + // Perform the invoke-custom operation. + RangeInstructionOperands operands(first_arg, num_vregs); + JValue result; + bool success = + interpreter::DoInvokeCustom(self, *shadow_frame, call_site_idx, &operands, &result); + DCHECK(success || self->IsExceptionPending()); + + // Pop transition record. + self->PopManagedStackFragment(fragment); + + return result.GetJ(); +} + } // namespace art diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index 1337cd5fb2..dda3ddeb76 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -287,8 +287,8 @@ class EntrypointsOrderTest : public CommonRuntimeTest { pInvokeVirtualTrampolineWithAccessCheck, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokeVirtualTrampolineWithAccessCheck, pInvokePolymorphic, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokePolymorphic, - pTestSuspend, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokePolymorphic, pInvokeCustom, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokeCustom, pTestSuspend, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pTestSuspend, pDeliverException, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pDeliverException, pThrowArrayBounds, sizeof(void*)); diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 27f761a144..92d4731480 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -1164,7 +1164,7 @@ static ObjPtr InvokeBootstrapMethod(Thread* self, ShadowFrame& shadow_frame, uint32_t call_site_idx) REQUIRES_SHARED(Locks::mutator_lock_) { - StackHandleScope<7> hs(self); + StackHandleScope<5> hs(self); // There are three mandatory arguments expected from the call site // value array in the DEX file: the bootstrap method handle, the // method name to pass to the bootstrap method, and the method type @@ -1358,75 +1358,80 @@ static ObjPtr InvokeBootstrapMethod(Thread* self, } // Check the call site target is not null as we're going to invoke it. - Handle call_site = - hs.NewHandle(ObjPtr::DownCast(ObjPtr(result.GetL()))); - Handle target = hs.NewHandle(call_site->GetTarget()); - if (UNLIKELY(target.IsNull())) { + ObjPtr call_site = + ObjPtr::DownCast(ObjPtr(result.GetL())); + ObjPtr target = call_site->GetTarget(); + if (UNLIKELY(target == nullptr)) { ThrowClassCastException("Bootstrap method returned a CallSite with a null target"); return nullptr; } - return call_site.Get(); + return call_site; } -template +namespace { + +ObjPtr DoResolveCallSite(Thread* self, + ShadowFrame& shadow_frame, + uint32_t call_site_idx) + REQUIRES_SHARED(Locks::mutator_lock_) { + StackHandleScope<1> hs(self); + Handle dex_cache(hs.NewHandle(shadow_frame.GetMethod()->GetDexCache())); + + // Get the call site from the DexCache if present. + ObjPtr call_site = dex_cache->GetResolvedCallSite(call_site_idx); + if (LIKELY(call_site != nullptr)) { + return call_site; + } + + // Invoke the bootstrap method to get a candidate call site. + call_site = InvokeBootstrapMethod(self, shadow_frame, call_site_idx); + if (UNLIKELY(call_site == nullptr)) { + if (!self->GetException()->IsError()) { + // Use a BootstrapMethodError if the exception is not an instance of java.lang.Error. + ThrowWrappedBootstrapMethodError("Exception from call site #%u bootstrap method", + call_site_idx); + } + return nullptr; + } + + // Attempt to place the candidate call site into the DexCache, return the winning call site. + return dex_cache->SetResolvedCallSite(call_site_idx, call_site); +} + +} // namespace + bool DoInvokeCustom(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) - REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t call_site_idx, + const InstructionOperands* operands, + JValue* result) { // Make sure to check for async exceptions if (UNLIKELY(self->ObserveAsyncException())) { return false; } + // invoke-custom is not supported in transactions. In transactions // there is a limited set of types supported. invoke-custom allows // running arbitrary code and instantiating arbitrary types. CHECK(!Runtime::Current()->IsActiveTransaction()); - StackHandleScope<4> hs(self); - Handle dex_cache(hs.NewHandle(shadow_frame.GetMethod()->GetDexCache())); - const uint32_t call_site_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); - MutableHandle - call_site(hs.NewHandle(dex_cache->GetResolvedCallSite(call_site_idx))); + + ObjPtr call_site = DoResolveCallSite(self, shadow_frame, call_site_idx); if (call_site.IsNull()) { - call_site.Assign(InvokeBootstrapMethod(self, shadow_frame, call_site_idx)); - if (UNLIKELY(call_site.IsNull())) { - CHECK(self->IsExceptionPending()); - if (!self->GetException()->IsError()) { - // Use a BootstrapMethodError if the exception is not an instance of java.lang.Error. - ThrowWrappedBootstrapMethodError("Exception from call site #%u bootstrap method", - call_site_idx); - } - result->SetJ(0); - return false; - } - mirror::CallSite* winning_call_site = - dex_cache->SetResolvedCallSite(call_site_idx, call_site.Get()); - call_site.Assign(winning_call_site); + DCHECK(self->IsExceptionPending()); + return false; } + StackHandleScope<2> hs(self); Handle target = hs.NewHandle(call_site->GetTarget()); Handle target_method_type = hs.NewHandle(target->GetMethodType()); - DCHECK_EQ(static_cast(inst->VRegA()), target_method_type->NumberOfVRegs()); - if (is_range) { - RangeInstructionOperands operands(inst->VRegC_3rc(), inst->VRegA_3rc()); - return MethodHandleInvokeExact(self, - shadow_frame, - target, - target_method_type, - &operands, - result); - } else { - uint32_t args[Instruction::kMaxVarArgRegs]; - inst->GetVarArgs(args, inst_data); - VarArgsInstructionOperands operands(args, inst->VRegA_35c()); - return MethodHandleInvokeExact(self, - shadow_frame, - target, - target_method_type, - &operands, - result); - } + DCHECK_EQ(operands->GetNumberOfOperands(), target_method_type->NumberOfVRegs()) + << " call_site_idx" << call_site_idx; + return MethodHandleInvokeExact(self, + shadow_frame, + target, + target_method_type, + operands, + result); } // Assign register 'src_reg' from shadow_frame to register 'dest_reg' into new_shadow_frame. @@ -1847,16 +1852,6 @@ EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false); EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true); #undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL -// Explicit DoInvokeCustom template function declarations. -#define EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(_is_range) \ - template REQUIRES_SHARED(Locks::mutator_lock_) \ - bool DoInvokeCustom<_is_range>( \ - Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, \ - uint16_t inst_data, JValue* result) -EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(false); -EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(true); -#undef EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL - // Explicit DoFilledNewArray template function declarations. #define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check, _transaction_active) \ template REQUIRES_SHARED(Locks::mutator_lock_) \ diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 60bf50546f..b324b4c99d 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -242,7 +242,15 @@ bool DoInvokePolymorphic(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data, - JValue* result); + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_); + +bool DoInvokeCustom(Thread* self, + ShadowFrame& shadow_frame, + uint32_t call_site_idx, + const InstructionOperands* operands, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_); // Performs a custom invoke (invoke-custom/invoke-custom-range). template @@ -250,7 +258,19 @@ bool DoInvokeCustom(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data, - JValue* result); + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + const uint32_t call_site_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); + if (is_range) { + RangeInstructionOperands operands(inst->VRegC_3rc(), inst->VRegA_3rc()); + return DoInvokeCustom(self, shadow_frame, call_site_idx, &operands, result); + } else { + uint32_t args[Instruction::kMaxVarArgRegs]; + inst->GetVarArgs(args, inst_data); + VarArgsInstructionOperands operands(args, inst->VRegA_35c()); + return DoInvokeCustom(self, shadow_frame, call_site_idx, &operands, result); + } +} // Handles invoke-virtual-quick and invoke-virtual-quick-range instructions. // Returns true on success, otherwise throws an exception and returns false. diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index 96778aa98d..e64a325024 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -157,7 +157,8 @@ inline CallSite* DexCache::GetResolvedCallSite(uint32_t call_site_idx) { return ref.load(std::memory_order_seq_cst).Read(); } -inline CallSite* DexCache::SetResolvedCallSite(uint32_t call_site_idx, CallSite* call_site) { +inline ObjPtr DexCache::SetResolvedCallSite(uint32_t call_site_idx, + ObjPtr call_site) { DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds()); diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index bb86004a90..941248edf7 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -320,8 +320,8 @@ class MANAGED DexCache FINAL : public Object { // because multiple threads can invoke the bootstrap method each // producing a call site, but the method handle invocation on the // call site must be on a common agreed value. - CallSite* SetResolvedCallSite(uint32_t call_site_idx, CallSite* resolved) WARN_UNUSED - REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr SetResolvedCallSite(uint32_t call_site_idx, ObjPtr resolved) + REQUIRES_SHARED(Locks::mutator_lock_) WARN_UNUSED; StringDexCacheType* GetStrings() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldPtr64(StringsOffset()); diff --git a/runtime/oat.h b/runtime/oat.h index 72eb27d69e..40f4edd8bb 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: Rewrite TypeLookupTable. - static constexpr uint8_t kOatVersion[] = { '1', '4', '7', '\0' }; + // Last oat version changed reason: compiler support invoke-custom + static constexpr uint8_t kOatVersion[] = { '1', '4', '8', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index a62271df77..61ddded9f2 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3053,10 +3053,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { // Step 2. Check the register arguments correspond to the expected arguments for the // method handle produced by step 1. The dex file verifier has checked ranges for // the first three arguments and CheckCallSite has checked the method handle type. - CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx)); - it.Next(); // Skip to name. - it.Next(); // Skip to method type of the method handle - const dex::ProtoIndex proto_idx(it.GetJavaValue().c); + const dex::ProtoIndex proto_idx = dex_file_->GetProtoIndexForCallSite(call_site_idx); const DexFile::ProtoId& proto_id = dex_file_->GetProtoId(proto_idx); DexFileParameterIterator param_it(*dex_file_, proto_id); // Treat method as static as it has yet to be determined. @@ -3072,8 +3069,6 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_)); } just_set_result = true; - // TODO: Add compiler support for invoke-custom (b/35337872). - Fail(VERIFY_ERROR_FORCE_INTERPRETER); break; } case Instruction::NEG_INT: @@ -4007,24 +4002,41 @@ bool MethodVerifier::CheckCallSite(uint32_t call_site_idx) { CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx)); // Check essential arguments are provided. The dex file verifier has verified indicies of the // main values (method handle, name, method_type). - if (it.Size() < 3) { + static const size_t kRequiredArguments = 3; + if (it.Size() < kRequiredArguments) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx << " has too few arguments: " - << it.Size() << " < 3"; + << it.Size() << " < " << kRequiredArguments; return false; } - // Get and check the first argument: the method handle (index range - // checked by the dex file verifier). - uint32_t method_handle_idx = static_cast(it.GetJavaValue().i); - if (method_handle_idx > dex_file_->NumMethodHandles()) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site id #" << call_site_idx - << " method handle index invalid " << method_handle_idx - << " >= " << dex_file_->NumMethodHandles(); - return false; + std::pair type_and_max[kRequiredArguments] = + { { EncodedArrayValueIterator::ValueType::kMethodHandle, dex_file_->NumMethodHandles() }, + { EncodedArrayValueIterator::ValueType::kString, dex_file_->NumStringIds() }, + { EncodedArrayValueIterator::ValueType::kMethodType, dex_file_->NumProtoIds() } + }; + uint32_t index[kRequiredArguments]; + + // Check arguments have expected types and are within permitted ranges. + for (size_t i = 0; i < kRequiredArguments; ++i) { + if (it.GetValueType() != type_and_max[i].first) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site id #" << call_site_idx + << " argument " << i << " has wrong type " + << it.GetValueType() << "!=" << type_and_max[i].first; + return false; + } + index[i] = static_cast(it.GetJavaValue().i); + if (index[i] >= type_and_max[i].second) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site id #" << call_site_idx + << " argument " << i << " bad index " + << index[i] << " >= " << type_and_max[i].second; + return false; + } + it.Next(); } - const DexFile::MethodHandleItem& mh = dex_file_->GetMethodHandle(method_handle_idx); + // Check method handle kind is valid. + const DexFile::MethodHandleItem& mh = dex_file_->GetMethodHandle(index[0]); if (mh.method_handle_type_ != static_cast(DexFile::MethodHandleType::kInvokeStatic)) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx << " argument 0 method handle type is not InvokeStatic: " diff --git a/test/952-invoke-custom/src/TestReturnValues.java b/test/952-invoke-custom/src/TestReturnValues.java new file mode 100644 index 0000000000..8450a44310 --- /dev/null +++ b/test/952-invoke-custom/src/TestReturnValues.java @@ -0,0 +1,330 @@ +/* + * 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.BootstrapMethod; +import annotations.CalledByIndy; +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +class TestReturnValues extends TestBase { + static CallSite bsm(MethodHandles.Lookup lookup, String name, MethodType methodType) + throws Throwable { + MethodHandle mh = lookup.findStatic(TestReturnValues.class, name, methodType); + return new ConstantCallSite(mh); + } + + // + // Methods that pass through a single argument. + // Used to check return path. + // + static byte passThrough(byte value) { + return value; + } + + static char passThrough(char value) { + return value; + } + + static double passThrough(double value) { + return value; + } + + static float passThrough(float value) { + return value; + } + + static int passThrough(int value) { + return value; + } + + static Object passThrough(Object value) { + return value; + } + + static Object[] passThrough(Object[] value) { + return value; + } + + static long passThrough(long value) { + return value; + } + + static short passThrough(short value) { + return value; + } + + static void passThrough() {} + + static boolean passThrough(boolean value) { + return value; + } + + // byte + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = byte.class, + parameterTypes = {byte.class}) + private static byte passThroughCallSite(byte value) { + assertNotReached(); + return (byte) 0; + } + + // char + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = char.class, + parameterTypes = {char.class}) + private static char passThroughCallSite(char value) { + assertNotReached(); + return 'Z'; + } + + // double + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = double.class, + parameterTypes = {double.class}) + private static double passThroughCallSite(double value) { + assertNotReached(); + return Double.NaN; + } + + // float + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = float.class, + parameterTypes = {float.class}) + private static float passThroughCallSite(float value) { + assertNotReached(); + return Float.NaN; + } + + // int + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = int.class, + parameterTypes = {int.class}) + private static int passThroughCallSite(int value) { + assertNotReached(); + return 0; + } + + // long + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = long.class, + parameterTypes = {long.class}) + private static long passThroughCallSite(long value) { + assertNotReached(); + return Long.MIN_VALUE; + } + + // Object + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = Object.class, + parameterTypes = {Object.class}) + private static Object passThroughCallSite(Object value) { + assertNotReached(); + return null; + } + + // Object[] + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = Object[].class, + parameterTypes = {Object[].class}) + private static Object[] passThroughCallSite(Object[] value) { + assertNotReached(); + return null; + } + + // short + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = short.class, + parameterTypes = {short.class}) + private static short passThroughCallSite(short value) { + assertNotReached(); + return (short) 0; + } + + // void + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = void.class, + parameterTypes = {}) + private static void passThroughCallSite() { + assertNotReached(); + } + + // boolean + @CalledByIndy( + bootstrapMethod = + @BootstrapMethod(enclosingType = TestReturnValues.class, name = "bsm"), + fieldOrMethodName = "passThrough", + returnType = boolean.class, + parameterTypes = {boolean.class}) + private static boolean passThroughCallSite(boolean value) { + assertNotReached(); + return false; + } + + private static void testByteReturnValues() { + byte[] values = {Byte.MIN_VALUE, Byte.MAX_VALUE}; + for (byte value : values) { + assertEquals(value, (byte) passThroughCallSite(value)); + } + } + + private static void testCharReturnValues() { + char[] values = { + Character.MIN_VALUE, + Character.MAX_HIGH_SURROGATE, + Character.MAX_LOW_SURROGATE, + Character.MAX_VALUE + }; + for (char value : values) { + assertEquals(value, (char) passThroughCallSite(value)); + } + } + + private static void testDoubleReturnValues() { + double[] values = { + Double.MIN_VALUE, + Double.MIN_NORMAL, + Double.NaN, + Double.POSITIVE_INFINITY, + Double.NEGATIVE_INFINITY, + Double.MAX_VALUE + }; + for (double value : values) { + assertEquals(value, (double) passThroughCallSite(value)); + } + } + + private static void testFloatReturnValues() { + float[] values = { + Float.MIN_VALUE, + Float.MIN_NORMAL, + Float.NaN, + Float.POSITIVE_INFINITY, + Float.NEGATIVE_INFINITY, + Float.MAX_VALUE + }; + for (float value : values) { + assertEquals(value, (float) passThroughCallSite(value)); + } + } + + private static void testIntReturnValues() { + int[] values = {Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.SIZE, -Integer.SIZE}; + for (int value : values) { + assertEquals(value, (int) passThroughCallSite(value)); + } + } + + private static void testLongReturnValues() { + long[] values = {Long.MIN_VALUE, Long.MAX_VALUE, (long) Long.SIZE, (long) -Long.SIZE}; + for (long value : values) { + assertEquals(value, (long) passThroughCallSite(value)); + } + } + + private static void testObjectReturnValues() { + Object[] values = {null, "abc", Integer.valueOf(123)}; + for (Object value : values) { + assertEquals(value, (Object) passThroughCallSite(value)); + } + + Object[] otherValues = (Object[]) passThroughCallSite(values); + assertEquals(values.length, otherValues.length); + for (int i = 0; i < otherValues.length; ++i) { + assertEquals(values[i], otherValues[i]); + } + } + + private static void testShortReturnValues() { + short[] values = { + Short.MIN_VALUE, Short.MAX_VALUE, (short) Short.SIZE, (short) -Short.SIZE + }; + for (short value : values) { + assertEquals(value, (short) passThroughCallSite(value)); + } + } + + private static void testVoidReturnValues() { + long l = Long.MIN_VALUE; + double d = Double.MIN_VALUE; + passThroughCallSite(); // Initializes call site + assertEquals(Long.MIN_VALUE, l); + assertEquals(Double.MIN_VALUE, d); + + l = Long.MAX_VALUE; + d = Double.MAX_VALUE; + passThroughCallSite(); // re-uses existing call site + assertEquals(Long.MAX_VALUE, l); + assertEquals(Double.MAX_VALUE, d); + } + + private static void testBooleanReturnValues() { + boolean[] values = {true, false, true, false, false}; + for (boolean value : values) { + assertEquals(value, (boolean) passThroughCallSite(value)); + } + } + + public static void test() { + System.out.println(TestReturnValues.class.getName()); + // Two passes here - the first is for the call site creation and invoke path, the second + // for the lookup and invoke path. + for (int pass = 0; pass < 2; ++pass) { + testByteReturnValues(); // B + testCharReturnValues(); // C + testDoubleReturnValues(); // D + testFloatReturnValues(); // F + testIntReturnValues(); // I + testLongReturnValues(); // J + testObjectReturnValues(); // L + testShortReturnValues(); // S + testVoidReturnValues(); // S + testBooleanReturnValues(); // Z + } + } +} -- GitLab From c0f02d4846846d644bfa4bb606aa26411c0be31d Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Mon, 11 Jun 2018 09:49:01 +0000 Subject: [PATCH 559/749] Revert "Add Throwable.UNASSIGNED_STACK to well known fields" This reverts commit d80668611e1982d50374da03377542a7863ba8e5. Reason for revert: Appears to break Serialization of some Throwable subclasses. Bug: 35910877 Bug: 109930347 Change-Id: Ic78e63229f946175c629ae76b82a277ed5f12e0f --- runtime/common_throws.cc | 6 +++--- runtime/well_known_classes.cc | 9 ++++++--- runtime/well_known_classes.h | 3 ++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index 2ffadb3ca2..657a78bd2f 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -787,7 +787,7 @@ void ThrowStackOverflowError(Thread* self) { // Object stackState; // StackTraceElement[] stackTrace; // Only Throwable has a non-empty constructor: - // this.stackTrace = Throwable.UNASSIGNED_STACK; + // this.stackTrace = EmptyArray.STACK_TRACE_ELEMENT; // fillInStackTrace(); // detailMessage. @@ -822,8 +822,8 @@ void ThrowStackOverflowError(Thread* self) { // stackTrace. ScopedLocalRef stack_trace_elem(env, env->GetStaticObjectField( - WellKnownClasses::java_lang_Throwable, - WellKnownClasses::java_lang_Throwable_UNASSIGNED_STACK)); + WellKnownClasses::libcore_util_EmptyArray, + WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT)); env->SetObjectField(exc.get(), WellKnownClasses::java_lang_Throwable_stackTrace, stack_trace_elem.get()); diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 7b54b2fabc..c64e7bbca1 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -76,6 +76,7 @@ jclass WellKnownClasses::java_util_Collections; jclass WellKnownClasses::java_util_function_Consumer; jclass WellKnownClasses::libcore_reflect_AnnotationFactory; jclass WellKnownClasses::libcore_reflect_AnnotationMember; +jclass WellKnownClasses::libcore_util_EmptyArray; jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk; jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer; @@ -136,7 +137,6 @@ jfieldID WellKnownClasses::java_lang_Throwable_detailMessage; jfieldID WellKnownClasses::java_lang_Throwable_stackTrace; jfieldID WellKnownClasses::java_lang_Throwable_stackState; jfieldID WellKnownClasses::java_lang_Throwable_suppressedExceptions; -jfieldID WellKnownClasses::java_lang_Throwable_UNASSIGNED_STACK; jfieldID WellKnownClasses::java_nio_ByteBuffer_address; jfieldID WellKnownClasses::java_nio_ByteBuffer_hb; jfieldID WellKnownClasses::java_nio_ByteBuffer_isReadOnly; @@ -145,6 +145,7 @@ jfieldID WellKnownClasses::java_nio_ByteBuffer_offset; jfieldID WellKnownClasses::java_nio_DirectByteBuffer_capacity; jfieldID WellKnownClasses::java_nio_DirectByteBuffer_effectiveDirectAddress; jfieldID WellKnownClasses::java_util_Collections_EMPTY_LIST; +jfieldID WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT; jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data; jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_length; jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_offset; @@ -330,6 +331,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_util_function_Consumer = CacheClass(env, "java/util/function/Consumer"); libcore_reflect_AnnotationFactory = CacheClass(env, "libcore/reflect/AnnotationFactory"); libcore_reflect_AnnotationMember = CacheClass(env, "libcore/reflect/AnnotationMember"); + libcore_util_EmptyArray = CacheClass(env, "libcore/util/EmptyArray"); org_apache_harmony_dalvik_ddmc_Chunk = CacheClass(env, "org/apache/harmony/dalvik/ddmc/Chunk"); org_apache_harmony_dalvik_ddmc_DdmServer = CacheClass(env, "org/apache/harmony/dalvik/ddmc/DdmServer"); @@ -383,7 +385,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Throwable_stackTrace = CacheField(env, java_lang_Throwable, false, "stackTrace", "[Ljava/lang/StackTraceElement;"); java_lang_Throwable_stackState = CacheField(env, java_lang_Throwable, false, "backtrace", "Ljava/lang/Object;"); java_lang_Throwable_suppressedExceptions = CacheField(env, java_lang_Throwable, false, "suppressedExceptions", "Ljava/util/List;"); - java_lang_Throwable_UNASSIGNED_STACK = CacheField(env, java_lang_Throwable, true, "UNASSIGNED_STACK", "[Ljava/lang/StackTraceElement;"); java_nio_ByteBuffer_address = CacheField(env, java_nio_ByteBuffer, false, "address", "J"); java_nio_ByteBuffer_hb = CacheField(env, java_nio_ByteBuffer, false, "hb", "[B"); java_nio_ByteBuffer_isReadOnly = CacheField(env, java_nio_ByteBuffer, false, "isReadOnly", "Z"); @@ -392,6 +393,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_nio_DirectByteBuffer_capacity = CacheField(env, java_nio_DirectByteBuffer, false, "capacity", "I"); java_nio_DirectByteBuffer_effectiveDirectAddress = CacheField(env, java_nio_DirectByteBuffer, false, "address", "J"); java_util_Collections_EMPTY_LIST = CacheField(env, java_util_Collections, true, "EMPTY_LIST", "Ljava/util/List;"); + libcore_util_EmptyArray_STACK_TRACE_ELEMENT = CacheField(env, libcore_util_EmptyArray, true, "STACK_TRACE_ELEMENT", "[Ljava/lang/StackTraceElement;"); org_apache_harmony_dalvik_ddmc_Chunk_data = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "data", "[B"); org_apache_harmony_dalvik_ddmc_Chunk_length = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "length", "I"); org_apache_harmony_dalvik_ddmc_Chunk_offset = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "offset", "I"); @@ -460,6 +462,7 @@ void WellKnownClasses::Clear() { java_nio_DirectByteBuffer = nullptr; libcore_reflect_AnnotationFactory = nullptr; libcore_reflect_AnnotationMember = nullptr; + libcore_util_EmptyArray = nullptr; org_apache_harmony_dalvik_ddmc_Chunk = nullptr; org_apache_harmony_dalvik_ddmc_DdmServer = nullptr; @@ -519,7 +522,6 @@ void WellKnownClasses::Clear() { java_lang_Throwable_stackTrace = nullptr; java_lang_Throwable_stackState = nullptr; java_lang_Throwable_suppressedExceptions = nullptr; - java_lang_Throwable_UNASSIGNED_STACK = nullptr; java_nio_ByteBuffer_address = nullptr; java_nio_ByteBuffer_hb = nullptr; java_nio_ByteBuffer_isReadOnly = nullptr; @@ -528,6 +530,7 @@ void WellKnownClasses::Clear() { java_nio_DirectByteBuffer_capacity = nullptr; java_nio_DirectByteBuffer_effectiveDirectAddress = nullptr; java_util_Collections_EMPTY_LIST = nullptr; + libcore_util_EmptyArray_STACK_TRACE_ELEMENT = nullptr; org_apache_harmony_dalvik_ddmc_Chunk_data = nullptr; org_apache_harmony_dalvik_ddmc_Chunk_length = nullptr; org_apache_harmony_dalvik_ddmc_Chunk_offset = nullptr; diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index bed8770c45..c81062f594 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -86,6 +86,7 @@ struct WellKnownClasses { static jclass java_nio_DirectByteBuffer; static jclass libcore_reflect_AnnotationFactory; static jclass libcore_reflect_AnnotationMember; + static jclass libcore_util_EmptyArray; static jclass org_apache_harmony_dalvik_ddmc_Chunk; static jclass org_apache_harmony_dalvik_ddmc_DdmServer; @@ -146,7 +147,6 @@ struct WellKnownClasses { static jfieldID java_lang_Throwable_stackTrace; static jfieldID java_lang_Throwable_stackState; static jfieldID java_lang_Throwable_suppressedExceptions; - static jfieldID java_lang_Throwable_UNASSIGNED_STACK; static jfieldID java_nio_ByteBuffer_address; static jfieldID java_nio_ByteBuffer_hb; static jfieldID java_nio_ByteBuffer_isReadOnly; @@ -156,6 +156,7 @@ struct WellKnownClasses { static jfieldID java_nio_DirectByteBuffer_effectiveDirectAddress; static jfieldID java_util_Collections_EMPTY_LIST; + static jfieldID libcore_util_EmptyArray_STACK_TRACE_ELEMENT; static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_data; static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_length; static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_offset; -- GitLab From 05e34f4cb8c9db165d1008721e874b9dd3db024b Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Thu, 24 May 2018 13:19:05 +0000 Subject: [PATCH 560/749] Revert^2 "Remove support for Valgrind in ART." - Disable test configuration art-gtest-valgrind64 (art-gtest-valgrind32 was already disabled). - Remove Makefile logic regarding testing with Valgrind. - Remove occurrences of `TEST_DISABLED_FOR_MEMORY_TOOL_VALGRIND`. - Replace occurrences of `TEST_DISABLED_FOR_MEMORY_TOOL_ASAN` with `TEST_DISABLED_FOR_MEMORY_TOOL`. - Replace the potentially dynamically evaluated `RUNNING_ON_MEMORY_TOOL` expression with constant `kRunningOnMemoryTool`. - Simplify and fold the logic of `art::ArenaAllocatorMemoryToolCheckImpl` and `art::ArenaAllocatorMemoryToolCheck` into `art::ArenaAllocatorMemoryTool`. - Adjust comments regarding memory tools. - Remove Valgrind suppression files. - Remove `--callgrind` option from tools/art. This reverts commit 8b362a87d52a6668ffd2283ef6ffc274315f41c8. Change-Id: I23c76845e6ccf766f19b22b58a0d5161f60842a9 Test: art/test.py Test: art/test/testrunner/run_build_test_target.py art-asan Bug: 77856586 Bug: 29282211 --- Android.mk | 26 --- build/Android.bp | 2 - build/Android.gtest.mk | 153 ++---------------- build/Android.oat.mk | 108 +++++-------- cmdline/unit.h | 5 +- compiler/dex/inline_method_analyser.cc | 3 +- compiler/optimizing/instruction_simplifier.cc | 8 +- dex2oat/dex2oat.cc | 8 +- dex2oat/dex2oat_test.cc | 6 +- dex2oat/linker/image_writer.cc | 3 +- libartbase/base/arena_allocator.h | 29 +--- libartbase/base/arena_allocator_test.cc | 8 +- libartbase/base/common_art_test.h | 14 +- libartbase/base/malloc_arena_pool.cc | 6 +- libartbase/base/mem_map.cc | 10 +- libartbase/base/mem_map_test.cc | 46 +++--- libartbase/base/memory_tool.h | 56 +++---- libartbase/base/scoped_arena_containers.h | 2 +- libdexfile/dex/dex_file_tracking_registrar.cc | 3 +- patchoat/patchoat.cc | 4 +- runtime/base/mem_map_arena_pool.cc | 2 +- runtime/exec_utils_test.cc | 24 ++- runtime/gc/allocator/rosalloc.h | 4 +- runtime/gc/heap-inl.h | 4 +- runtime/gc/heap.cc | 3 +- runtime/gc/space/large_object_space.cc | 5 +- .../gc/space/memory_tool_malloc_space-inl.h | 141 +++++++++------- runtime/gc/space/rosalloc_space.cc | 6 +- runtime/gc/space/rosalloc_space.h | 4 +- runtime/interpreter/unstarted_runtime_test.cc | 5 - runtime/jit/jit.cc | 2 +- runtime/native_stack_dump.cc | 6 +- runtime/runtime.cc | 10 +- runtime/runtime_callbacks_test.cc | 4 +- runtime/thread.cc | 13 +- test/137-cfi/cfi.cc | 3 - test/testrunner/target_config.py | 6 +- test/valgrind-suppressions.txt | 87 ---------- test/valgrind-target-suppressions.txt | 76 --------- tools/art | 4 - 40 files changed, 274 insertions(+), 635 deletions(-) delete mode 100644 test/valgrind-suppressions.txt delete mode 100644 test/valgrind-target-suppressions.txt diff --git a/Android.mk b/Android.mk index 08a1a105b8..1c946292ef 100644 --- a/Android.mk +++ b/Android.mk @@ -245,19 +245,6 @@ endif test-art-host-dexdump: $(addprefix $(HOST_OUT_EXECUTABLES)/, dexdump2 dexlist) ANDROID_HOST_OUT=$(realpath $(HOST_OUT)) art/test/dexdump/run-all-tests -# Valgrind. -.PHONY: valgrind-test-art-host -valgrind-test-art-host: valgrind-test-art-host-gtest - $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) - -.PHONY: valgrind-test-art-host32 -valgrind-test-art-host32: valgrind-test-art-host-gtest32 - $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) - -.PHONY: valgrind-test-art-host64 -valgrind-test-art-host64: valgrind-test-art-host-gtest64 - $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) - ######################################################################## # target test rules @@ -332,19 +319,6 @@ test-art-target-jit$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) endif -# Valgrind. -.PHONY: valgrind-test-art-target -valgrind-test-art-target: valgrind-test-art-target-gtest - $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) - -.PHONY: valgrind-test-art-target32 -valgrind-test-art-target32: valgrind-test-art-target-gtest32 - $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) - -.PHONY: valgrind-test-art-target64 -valgrind-test-art-target64: valgrind-test-art-target-gtest64 - $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) - ####################### # Fake packages for ART diff --git a/build/Android.bp b/build/Android.bp index 2a5598fb7a..3a1d5839f5 100644 --- a/build/Android.bp +++ b/build/Android.bp @@ -127,8 +127,6 @@ art_global_defaults { }, include_dirs: [ - "external/valgrind/include", - "external/valgrind", "external/vixl/src", ], diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 7272661860..9f42727a2f 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -408,15 +408,9 @@ endif ART_TEST_HOST_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_GTEST_RULES := -ART_TEST_HOST_VALGRIND_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := -ART_TEST_HOST_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := -ART_TEST_HOST_VALGRIND_GTEST_RULES := ART_TEST_TARGET_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := ART_TEST_TARGET_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := ART_TEST_TARGET_GTEST_RULES := -ART_TEST_TARGET_VALGRIND_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := -ART_TEST_TARGET_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := -ART_TEST_TARGET_VALGRIND_GTEST_RULES := ART_TEST_HOST_GTEST_DEPENDENCIES := ART_GTEST_TARGET_ANDROID_ROOT := '/system' @@ -424,40 +418,6 @@ ifneq ($(ART_TEST_ANDROID_ROOT),) ART_GTEST_TARGET_ANDROID_ROOT := $(ART_TEST_ANDROID_ROOT) endif -ART_VALGRIND_TARGET_DEPENDENCIES := - -# Has to match list in external/valgrind/Android.build_one.mk -ART_VALGRIND_SUPPORTED_ARCH := arm arm64 x86_64 - -# Valgrind is not supported for x86 -ifneq (,$(filter $(ART_VALGRIND_SUPPORTED_ARCH),$(TARGET_ARCH))) -art_vg_arch := $(if $(filter x86_64,$(TARGET_ARCH)),amd64,$(TARGET_ARCH)) -ART_VALGRIND_TARGET_DEPENDENCIES += \ - $(TARGET_OUT_EXECUTABLES)/valgrind \ - $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/memcheck-$(art_vg_arch)-linux \ - $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_core-$(art_vg_arch)-linux.so \ - $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_memcheck-$(art_vg_arch)-linux.so \ - $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/default.supp -art_vg_arch := -endif - -ifdef TARGET_2ND_ARCH -ifneq (,$(filter $(ART_VALGRIND_SUPPORTED_ARCH),$(TARGET_2ND_ARCH))) -ART_VALGRIND_TARGET_DEPENDENCIES += \ - $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/memcheck-$(TARGET_2ND_ARCH)-linux \ - $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_core-$(TARGET_2ND_ARCH)-linux.so \ - $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_memcheck-$(TARGET_2ND_ARCH)-linux.so -endif -endif - -include $(CLEAR_VARS) -LOCAL_MODULE := valgrind-target-suppressions.txt -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := test/valgrind-target-suppressions.txt -LOCAL_MODULE_PATH := $(ART_TARGET_TEST_OUT) -include $(BUILD_PREBUILT) - # Define a make rule for a target device gtest. # $(1): gtest name - the name of the test we're building such as leb128_test. # $(2): path relative to $OUT to the test binary @@ -487,11 +447,10 @@ define define-art-gtest-rule-target $$($(3)TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so \ $$($(3)TARGET_OUT_SHARED_LIBRARIES)/libopenjdkd.so \ $$(TARGET_OUT_JAVA_LIBRARIES)/core-libart-testdex.jar \ - $$(TARGET_OUT_JAVA_LIBRARIES)/core-oj-testdex.jar \ - $$(ART_TARGET_TEST_OUT)/valgrind-target-suppressions.txt + $$(TARGET_OUT_JAVA_LIBRARIES)/core-oj-testdex.jar -$$(gtest_rule) valgrind-$$(gtest_rule): PRIVATE_TARGET_EXE := $$(gtest_target_exe) -$$(gtest_rule) valgrind-$$(gtest_rule): PRIVATE_MAYBE_CHROOT_COMMAND := $$(maybe_chroot_command) +$$(gtest_rule): PRIVATE_TARGET_EXE := $$(gtest_target_exe) +$$(gtest_rule): PRIVATE_MAYBE_CHROOT_COMMAND := $$(maybe_chroot_command) # File witnessing the success of the gtest, the presence of which means the gtest's success. gtest_witness := \ @@ -516,37 +475,7 @@ $$(gtest_rule): test-art-target-sync ART_TEST_TARGET_GTEST_RULES += $$(gtest_rule) ART_TEST_TARGET_GTEST_$(1)_RULES += $$(gtest_rule) -# File witnessing the success of the Valgrind gtest, the presence of which means the gtest's -# success. -valgrind_gtest_witness := \ - $$(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/valgrind-$$(gtest_rule)-$$$$PPID - -valgrind-$$(gtest_rule): PRIVATE_VALGRIND_GTEST_WITNESS := $$(valgrind_gtest_witness) - -.PHONY: valgrind-$$(gtest_rule) -valgrind-$$(gtest_rule): $(ART_VALGRIND_TARGET_DEPENDENCIES) test-art-target-sync - $(hide) adb shell touch $$(PRIVATE_VALGRIND_GTEST_WITNESS) - $(hide) adb shell rm $$(PRIVATE_VALGRIND_GTEST_WITNESS) - $(hide) adb shell $$(PRIVATE_MAYBE_CHROOT_COMMAND) chmod 755 $$(PRIVATE_TARGET_EXE) - $(hide) $$(call ART_TEST_SKIP,$$@) && \ - (adb shell "$$(PRIVATE_MAYBE_CHROOT_COMMAND) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ - ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \ - $(ART_GTEST_TARGET_ANDROID_ROOT)/bin/valgrind \ - --leak-check=full --error-exitcode=1 --workaround-gcc296-bugs=yes \ - --suppressions=$(ART_TARGET_TEST_DIR)/valgrind-target-suppressions.txt \ - --num-callers=50 --show-mismatched-frees=no $$(PRIVATE_TARGET_EXE) \ - && touch $$(PRIVATE_VALGRIND_GTEST_WITNESS)" \ - && (adb pull $$(PRIVATE_VALGRIND_GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ - || $$(call ART_TEST_FAILED,$$@)) - $(hide) rm -f /tmp/$$@-$$$$PPID - - ART_TEST_TARGET_VALGRIND_GTEST$$($(3)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += \ - valgrind-$$(gtest_rule) - ART_TEST_TARGET_VALGRIND_GTEST_RULES += valgrind-$$(gtest_rule) - ART_TEST_TARGET_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule) - # Clear locally defined variables. - valgrind_gtest_witness := gtest_witness := maybe_chroot_command := maybe_art_test_chroot := @@ -555,16 +484,6 @@ valgrind-$$(gtest_rule): $(ART_VALGRIND_TARGET_DEPENDENCIES) test-art-target-syn gtest_rule := endef # define-art-gtest-rule-target -ART_VALGRIND_DEPENDENCIES := \ - $(HOST_OUT_EXECUTABLES)/valgrind \ - $(HOST_OUT)/lib64/valgrind/memcheck-amd64-linux \ - $(HOST_OUT)/lib64/valgrind/memcheck-x86-linux \ - $(HOST_OUT)/lib64/valgrind/default.supp \ - $(HOST_OUT)/lib64/valgrind/vgpreload_core-amd64-linux.so \ - $(HOST_OUT)/lib64/valgrind/vgpreload_core-x86-linux.so \ - $(HOST_OUT)/lib64/valgrind/vgpreload_memcheck-amd64-linux.so \ - $(HOST_OUT)/lib64/valgrind/vgpreload_memcheck-x86-linux.so - # Define make rules for a host gtests. # $(1): gtest name - the name of the test we're building such as leb128_test. # $(2): path relative to $OUT to the test binary @@ -616,19 +535,6 @@ endif ART_TEST_HOST_GTEST_$(1)_RULES += $$(gtest_rule) -.PHONY: valgrind-$$(gtest_rule) -valgrind-$$(gtest_rule): $$(gtest_exe) $$(gtest_deps) $(ART_VALGRIND_DEPENDENCIES) - $(hide) $$(call ART_TEST_SKIP,$$@) && \ - VALGRIND_LIB=$(HOST_OUT)/lib64/valgrind \ - $(HOST_OUT_EXECUTABLES)/valgrind --leak-check=full --error-exitcode=1 \ - --suppressions=art/test/valgrind-suppressions.txt --num-callers=50 \ - $$< && \ - $$(call ART_TEST_PASSED,$$@) || $$(call ART_TEST_FAILED,$$@) - - ART_TEST_HOST_VALGRIND_GTEST$$($(3)ART_PHONY_TEST_HOST_SUFFIX)_RULES += valgrind-$$(gtest_rule) - ART_TEST_HOST_VALGRIND_GTEST_RULES += valgrind-$$(gtest_rule) - ART_TEST_HOST_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule) - # Clear locally defined variables. gtest_deps := gtest_exe := @@ -661,7 +567,6 @@ define define-art-gtest-target ifndef ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES := - ART_TEST_TARGET_VALGRIND_GTEST_$$(art_gtest_name)_RULES := endif $$(eval $$(call define-art-gtest-rule-target,$$(art_gtest_name),$$(art_gtest_filename),$(2),$$($(2)library_path))) @@ -681,7 +586,6 @@ define define-art-gtest-host art_gtest_name := $$(notdir $$(basename $$(art_gtest_filename))) ifndef ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES := - ART_TEST_HOST_VALGRIND_GTEST_$$(art_gtest_name)_RULES := endif $$(eval $$(call define-art-gtest-rule-host,$$(art_gtest_name),$$(art_gtest_filename),$(2))) @@ -700,13 +604,8 @@ define define-art-gtest-target-both test-art-target-gtest-$$(art_gtest_name): $$(ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES) $$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) -.PHONY: valgrind-test-art-target-gtest-$$(art_gtest_name) -valgrind-test-art-target-gtest-$$(art_gtest_name): $$(ART_TEST_TARGET_VALGRIND_GTEST_$$(art_gtest_name)_RULES) - $$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) - # Clear now unused variables. ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES := - ART_TEST_TARGET_VALGRIND_GTEST_$$(art_gtest_name)_RULES := art_gtest_name := endef # define-art-gtest-target-both @@ -719,13 +618,8 @@ define define-art-gtest-host-both test-art-host-gtest-$$(art_gtest_name): $$(ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES) $$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) -.PHONY: valgrind-test-art-host-gtest-$$(art_gtest_name) -valgrind-test-art-host-gtest-$$(art_gtest_name): $$(ART_TEST_HOST_VALGRIND_GTEST_$$(art_gtest_name)_RULES) - $$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) - # Clear now unused variables. ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES := - ART_TEST_HOST_VALGRIND_GTEST_$$(art_gtest_name)_RULES := art_gtest_name := endef # define-art-gtest-host-both @@ -751,12 +645,11 @@ RUNTIME_TARGET_GTEST_MAKE_TARGETS := $(foreach file, $(ART_TARGET_GTEST_FILES), $(eval RUNTIME_TARGET_GTEST_MAKE_TARGETS += $$(notdir $$(patsubst %/,%,$$(dir $$(file))))_$$(notdir $$(basename $$(file))))) COMPILER_TARGET_GTEST_MAKE_TARGETS := -# Define all the combinations of host/target, valgrind and suffix such as: -# test-art-host-gtest or valgrind-test-art-host-gtest64 +# Define all the combinations of host/target and suffix such as: +# test-art-host-gtest or test-art-host-gtest64 # $(1): host or target # $(2): HOST or TARGET -# $(3): valgrind- or undefined -# $(4): undefined, 32 or 64 +# $(3): undefined, 32 or 64 define define-test-art-gtest-combination ifeq ($(1),host) ifneq ($(2),HOST) @@ -771,12 +664,8 @@ define define-test-art-gtest-combination endif endif - rule_name := $(3)test-art-$(1)-gtest$(4) - ifeq ($(3),valgrind-) - dependencies := $$(ART_TEST_$(2)_VALGRIND_GTEST$(4)_RULES) - else - dependencies := $$(ART_TEST_$(2)_GTEST$(4)_RULES) - endif + rule_name := test-art-$(1)-gtest$(3) + dependencies := $$(ART_TEST_$(2)_GTEST$(3)_RULES) .PHONY: $$(rule_name) $$(rule_name): $$(dependencies) dx d8-compat-dx desugar @@ -787,21 +676,15 @@ $$(rule_name): $$(dependencies) dx d8-compat-dx desugar dependencies := endef # define-test-art-gtest-combination -$(eval $(call define-test-art-gtest-combination,target,TARGET,,)) -$(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,)) -$(eval $(call define-test-art-gtest-combination,target,TARGET,,$(ART_PHONY_TEST_TARGET_SUFFIX))) -$(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,$(ART_PHONY_TEST_TARGET_SUFFIX))) +$(eval $(call define-test-art-gtest-combination,target,TARGET,)) +$(eval $(call define-test-art-gtest-combination,target,TARGET,$(ART_PHONY_TEST_TARGET_SUFFIX))) ifdef 2ND_ART_PHONY_TEST_TARGET_SUFFIX -$(eval $(call define-test-art-gtest-combination,target,TARGET,,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX))) -$(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX))) +$(eval $(call define-test-art-gtest-combination,target,TARGET,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX))) endif -$(eval $(call define-test-art-gtest-combination,host,HOST,,)) -$(eval $(call define-test-art-gtest-combination,host,HOST,valgrind-,)) -$(eval $(call define-test-art-gtest-combination,host,HOST,,$(ART_PHONY_TEST_HOST_SUFFIX))) -$(eval $(call define-test-art-gtest-combination,host,HOST,valgrind-,$(ART_PHONY_TEST_HOST_SUFFIX))) +$(eval $(call define-test-art-gtest-combination,host,HOST,)) +$(eval $(call define-test-art-gtest-combination,host,HOST,$(ART_PHONY_TEST_HOST_SUFFIX))) ifneq ($(HOST_PREFER_32_BIT),true) -$(eval $(call define-test-art-gtest-combination,host,HOST,,$(2ND_ART_PHONY_TEST_HOST_SUFFIX))) -$(eval $(call define-test-art-gtest-combination,host,HOST,valgrind-,$(2ND_ART_PHONY_TEST_HOST_SUFFIX))) +$(eval $(call define-test-art-gtest-combination,host,HOST,$(2ND_ART_PHONY_TEST_HOST_SUFFIX))) endif # Clear locally defined variables. @@ -818,15 +701,9 @@ COMPILER_GTEST_HOST_SRC_FILES := ART_TEST_HOST_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_GTEST_RULES := -ART_TEST_HOST_VALGRIND_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := -ART_TEST_HOST_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := -ART_TEST_HOST_VALGRIND_GTEST_RULES := ART_TEST_TARGET_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := ART_TEST_TARGET_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := ART_TEST_TARGET_GTEST_RULES := -ART_TEST_TARGET_VALGRIND_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES := -ART_TEST_TARGET_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := -ART_TEST_TARGET_VALGRIND_GTEST_RULES := ART_GTEST_TARGET_ANDROID_ROOT := ART_GTEST_class_linker_test_DEX_DEPS := ART_GTEST_class_table_test_DEX_DEPS := @@ -865,8 +742,6 @@ ART_GTEST_transaction_test_DEX_DEPS := ART_GTEST_dex2oat_environment_tests_DEX_DEPS := ART_GTEST_heap_verification_test_DEX_DEPS := ART_GTEST_verifier_deps_test_DEX_DEPS := -ART_VALGRIND_DEPENDENCIES := -ART_VALGRIND_TARGET_DEPENDENCIES := $(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_TARGET_GTEST_$(dir)_DEX :=)) $(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_HOST_GTEST_$(dir)_DEX :=)) ART_TEST_HOST_GTEST_MainStripped_DEX := diff --git a/build/Android.oat.mk b/build/Android.oat.mk index 517ac5c28d..ba3ef053de 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -37,11 +37,9 @@ else endif # Use dex2oat debug version for better error reporting -# $(1): compiler - optimizing, interpreter or interpreter-access-checks. +# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks). # $(2): 2ND_ or undefined, 2ND_ for 32-bit host builds. -# $(3): wrapper, e.g., valgrind. -# $(4): dex2oat suffix, e.g, valgrind requires 32 right now. -# $(5): multi-image. +# $(3): multi-image. # NB depending on HOST_CORE_DEX_LOCATIONS so we are sure to have the dex files in frameworks for # run-test --no-image define create-core-oat-host-rules @@ -65,11 +63,11 @@ define create-core-oat-host-rules endif ifneq ($(filter-out interpreter interp-ac optimizing,$(1)),) #Technically this test is not precise, but hopefully good enough. - $$(error found $(1) expected interpreter, interpreter-access-checks, or optimizing) + $$(error found $(1) expected interpreter, interp-ac, or optimizing) endif - # If $(5) is true, generate a multi-image. - ifeq ($(5),true) + # If $(3) is true, generate a multi-image. + ifeq ($(3),true) core_multi_infix := -multi core_multi_param := --multi-image --no-inline-from=core-oj-hostdex.jar core_multi_group := _multi @@ -79,22 +77,18 @@ define create-core-oat-host-rules core_multi_group := endif - core_image_name := $($(2)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(3)$(CORE_IMG_SUFFIX) - core_oat_name := $($(2)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(3)$(CORE_OAT_SUFFIX) + core_image_name := $($(2)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(CORE_IMG_SUFFIX) + core_oat_name := $($(2)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(CORE_OAT_SUFFIX) # Using the bitness suffix makes it easier to add as a dependency for the run-test mk. ifeq ($(2),) - $(3)HOST_CORE_IMAGE_$(1)$$(core_multi_group)_64 := $$(core_image_name) + HOST_CORE_IMAGE_$(1)$$(core_multi_group)_64 := $$(core_image_name) else - $(3)HOST_CORE_IMAGE_$(1)$$(core_multi_group)_32 := $$(core_image_name) + HOST_CORE_IMAGE_$(1)$$(core_multi_group)_32 := $$(core_image_name) endif - $(3)HOST_CORE_IMG_OUTS += $$(core_image_name) - $(3)HOST_CORE_OAT_OUTS += $$(core_oat_name) + HOST_CORE_IMG_OUTS += $$(core_image_name) + HOST_CORE_OAT_OUTS += $$(core_oat_name) - # If we have a wrapper, make the target phony. - ifneq ($(3),) -.PHONY: $$(core_image_name) - endif $$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options) $$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name) $$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name) @@ -102,7 +96,7 @@ $$(core_image_name): PRIVATE_CORE_MULTI_PARAM := $$(core_multi_param) $$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency) @echo "host dex2oat: $$@" @mkdir -p $$(dir $$@) - $$(hide) $(3) $$(DEX2OAT)$(4) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \ + $$(hide) $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \ --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \ --image-classes=$$(PRELOADED_CLASSES) $$(addprefix --dex-file=,$$(HOST_CORE_DEX_FILES)) \ $$(addprefix --dex-location=,$$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$$(PRIVATE_CORE_OAT_NAME) \ @@ -124,35 +118,27 @@ $$(core_oat_name): $$(core_image_name) core_infix := endef # create-core-oat-host-rules -# $(1): compiler - optimizing, interpreter or interpreter-access-checks. -# $(2): wrapper. -# $(3): dex2oat suffix. -# $(4): multi-image. +# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks). +# $(2): multi-image. define create-core-oat-host-rule-combination - $(call create-core-oat-host-rules,$(1),,$(2),$(3),$(4)) + $(call create-core-oat-host-rules,$(1),,$(2)) ifneq ($(HOST_PREFER_32_BIT),true) - $(call create-core-oat-host-rules,$(1),2ND_,$(2),$(3),$(4)) + $(call create-core-oat-host-rules,$(1),2ND_,$(2)) endif endef -$(eval $(call create-core-oat-host-rule-combination,optimizing,,,false)) -$(eval $(call create-core-oat-host-rule-combination,interpreter,,,false)) -$(eval $(call create-core-oat-host-rule-combination,interp-ac,,,false)) -$(eval $(call create-core-oat-host-rule-combination,optimizing,,,true)) -$(eval $(call create-core-oat-host-rule-combination,interpreter,,,true)) -$(eval $(call create-core-oat-host-rule-combination,interp-ac,,,true)) - -valgrindHOST_CORE_IMG_OUTS := -valgrindHOST_CORE_OAT_OUTS := -$(eval $(call create-core-oat-host-rule-combination,optimizing,valgrind,32,false)) -$(eval $(call create-core-oat-host-rule-combination,interpreter,valgrind,32,false)) -$(eval $(call create-core-oat-host-rule-combination,interp-ac,valgrind,32,false)) - -valgrind-test-art-host-dex2oat-host: $(valgrindHOST_CORE_IMG_OUTS) +$(eval $(call create-core-oat-host-rule-combination,optimizing,false)) +$(eval $(call create-core-oat-host-rule-combination,interpreter,false)) +$(eval $(call create-core-oat-host-rule-combination,interp-ac,false)) +$(eval $(call create-core-oat-host-rule-combination,optimizing,true)) +$(eval $(call create-core-oat-host-rule-combination,interpreter,true)) +$(eval $(call create-core-oat-host-rule-combination,interp-ac,true)) test-art-host-dex2oat-host: $(HOST_CORE_IMG_OUTS) +# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks). +# $(2): 2ND_ or undefined define create-core-oat-target-rules core_compile_options := core_image_name := @@ -176,36 +162,32 @@ define create-core-oat-target-rules endif ifneq ($(filter-out interpreter interp-ac optimizing,$(1)),) # Technically this test is not precise, but hopefully good enough. - $$(error found $(1) expected interpreter, interpreter-access-checks, or optimizing) + $$(error found $(1) expected interpreter, interp-ac, or optimizing) endif - core_image_name := $($(2)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$(3)$(CORE_IMG_SUFFIX) - core_oat_name := $($(2)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$(3)$(CORE_OAT_SUFFIX) + core_image_name := $($(2)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$(CORE_IMG_SUFFIX) + core_oat_name := $($(2)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$(CORE_OAT_SUFFIX) # Using the bitness suffix makes it easier to add as a dependency for the run-test mk. ifeq ($(2),) ifdef TARGET_2ND_ARCH - $(3)TARGET_CORE_IMAGE_$(1)_64 := $$(core_image_name) + TARGET_CORE_IMAGE_$(1)_64 := $$(core_image_name) else - $(3)TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name) + TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name) endif else - $(3)TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name) + TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name) endif - $(3)TARGET_CORE_IMG_OUTS += $$(core_image_name) - $(3)TARGET_CORE_OAT_OUTS += $$(core_oat_name) + TARGET_CORE_IMG_OUTS += $$(core_image_name) + TARGET_CORE_OAT_OUTS += $$(core_oat_name) - # If we have a wrapper, make the target phony. - ifneq ($(3),) -.PHONY: $$(core_image_name) - endif $$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options) $$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name) $$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name) $$(core_image_name): $$(TARGET_CORE_DEX_FILES) $$(core_dex2oat_dependency) @echo "target dex2oat: $$@" @mkdir -p $$(dir $$@) - $$(hide) $(4) $$(DEX2OAT)$(5) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \ + $$(hide) $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \ --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \ --image-classes=$$(PRELOADED_CLASSES) $$(addprefix --dex-file=,$$(TARGET_CORE_DEX_FILES)) \ $$(addprefix --dex-location=,$$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$$(PRIVATE_CORE_OAT_NAME) \ @@ -228,30 +210,18 @@ $$(core_oat_name): $$(core_image_name) core_infix := endef # create-core-oat-target-rules -# $(1): compiler - optimizing, interpreter or interpreter-access-checks. -# $(2): wrapper. -# $(3): dex2oat suffix. +# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks). define create-core-oat-target-rule-combination - $(call create-core-oat-target-rules,$(1),,$(2),$(3)) + $(call create-core-oat-target-rules,$(1),) ifdef TARGET_2ND_ARCH - $(call create-core-oat-target-rules,$(1),2ND_,$(2),$(3)) + $(call create-core-oat-target-rules,$(1),2ND_) endif endef -$(eval $(call create-core-oat-target-rule-combination,optimizing,,)) -$(eval $(call create-core-oat-target-rule-combination,interpreter,,)) -$(eval $(call create-core-oat-target-rule-combination,interp-ac,,)) - -valgrindTARGET_CORE_IMG_OUTS := -valgrindTARGET_CORE_OAT_OUTS := -$(eval $(call create-core-oat-target-rule-combination,optimizing,valgrind,32)) -$(eval $(call create-core-oat-target-rule-combination,interpreter,valgrind,32)) -$(eval $(call create-core-oat-target-rule-combination,interp-ac,valgrind,32)) - -valgrind-test-art-host-dex2oat-target: $(valgrindTARGET_CORE_IMG_OUTS) - -valgrind-test-art-host-dex2oat: valgrind-test-art-host-dex2oat-host valgrind-test-art-host-dex2oat-target +$(eval $(call create-core-oat-target-rule-combination,optimizing)) +$(eval $(call create-core-oat-target-rule-combination,interpreter)) +$(eval $(call create-core-oat-target-rule-combination,interp-ac)) # Define a default core image that can be used for things like gtests that # need some image to run, but don't otherwise care which image is used. diff --git a/cmdline/unit.h b/cmdline/unit.h index ad6a03d12f..f73981fbd3 100644 --- a/cmdline/unit.h +++ b/cmdline/unit.h @@ -21,8 +21,9 @@ namespace art { // Used for arguments that simply indicate presence (e.g. "-help") without any values. struct Unit { - // Avoid 'Conditional jump or move depends on uninitialised value(s)' errors - // when running valgrind by specifying a user-defined constructor. + // Historical note: We specified a user-defined constructor to avoid + // 'Conditional jump or move depends on uninitialised value(s)' errors + // when running Valgrind. Unit() {} Unit(const Unit&) = default; ~Unit() {} diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc index dc044c1210..fe8b766d0f 100644 --- a/compiler/dex/inline_method_analyser.cc +++ b/compiler/dex/inline_method_analyser.cc @@ -724,7 +724,8 @@ bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(ArtMethod* method, return false; } DCHECK_GE(field->GetOffset().Int32Value(), 0); - // Do not interleave function calls with bit field writes to placate valgrind. Bug: 27552451. + // Historical note: We made sure not to interleave function calls with bit field writes to + // placate Valgrind. Bug: 27552451. uint32_t field_offset = field->GetOffset().Uint32Value(); bool is_volatile = field->IsVolatile(); result->field_idx = field_idx; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 63704a470e..d532eeeb52 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -637,8 +637,8 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { return; } - // Note: The `outcome` is initialized to please valgrind - the compiler can reorder - // the return value check with the `outcome` check, b/27651442 . + // Historical note: The `outcome` was initialized to please Valgrind - the compiler can reorder + // the return value check with the `outcome` check, b/27651442. bool outcome = false; if (TypeCheckHasKnownOutcome(check_cast->GetTargetClassRTI(), object, &outcome)) { if (outcome) { @@ -683,8 +683,8 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { return; } - // Note: The `outcome` is initialized to please valgrind - the compiler can reorder - // the return value check with the `outcome` check, b/27651442 . + // Historical note: The `outcome` was initialized to please Valgrind - the compiler can reorder + // the return value check with the `outcome` check, b/27651442. bool outcome = false; if (TypeCheckHasKnownOutcome(instruction->GetTargetClassRTI(), object, &outcome)) { MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf); diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 6b65aca943..00c893a8b5 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -657,7 +657,7 @@ class Dex2Oat FINAL { // the runtime. LogCompletionTime(); - if (!kIsDebugBuild && !(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { + if (!kIsDebugBuild && !(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { // We want to just exit on non-debug builds, not bringing the runtime down // in an orderly fashion. So release the following fields. driver_.release(); @@ -3119,9 +3119,9 @@ static dex2oat::ReturnCode Dex2oat(int argc, char** argv) { int main(int argc, char** argv) { int result = static_cast(art::Dex2oat(argc, argv)); // Everything was done, do an explicit exit here to avoid running Runtime destructors that take - // time (bug 10645725) unless we're a debug or instrumented build or running on valgrind. Note: - // The Dex2Oat class should not destruct the runtime in this case. - if (!art::kIsDebugBuild && !art::kIsPGOInstrumentation && (RUNNING_ON_MEMORY_TOOL == 0)) { + // time (bug 10645725) unless we're a debug or instrumented build or running on a memory tool. + // Note: The Dex2Oat class should not destruct the runtime in this case. + if (!art::kIsDebugBuild && !art::kIsPGOInstrumentation && !art::kRunningOnMemoryTool) { _exit(result); } return result; diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 96d7dba225..a060fd2e36 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -472,8 +472,8 @@ class Dex2oatSwapUseTest : public Dex2oatSwapTest { }; TEST_F(Dex2oatSwapUseTest, CheckSwapUsage) { - // Native memory usage isn't correctly tracked under sanitization. - TEST_DISABLED_FOR_MEMORY_TOOL_ASAN(); + // Native memory usage isn't correctly tracked when running under ASan. + TEST_DISABLED_FOR_MEMORY_TOOL(); // The `native_alloc_2_ >= native_alloc_1_` assertion below may not // hold true on some x86 systems; disable this test while we @@ -1054,8 +1054,6 @@ TEST_F(Dex2oatWatchdogTest, TestWatchdogOK) { } TEST_F(Dex2oatWatchdogTest, TestWatchdogTrigger) { - TEST_DISABLED_FOR_MEMORY_TOOL_VALGRIND(); // b/63052624 - // The watchdog is independent of dex2oat and will not delete intermediates. It is possible // that the compilation succeeds and the file is completely written by the time the watchdog // kills dex2oat (but the dex2oat threads must have been scheduled pretty badly). diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index dc0709013c..5f5930fdb6 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -2094,7 +2094,8 @@ void ImageWriter::CopyAndFixupNativeData(size_t oat_index) { size_t size = ArtMethod::Size(target_ptr_size_); size_t alignment = ArtMethod::Alignment(target_ptr_size_); memcpy(dest, pair.first, LengthPrefixedArray::ComputeSize(0, size, alignment)); - // Clear padding to avoid non-deterministic data in the image (and placate valgrind). + // Clear padding to avoid non-deterministic data in the image. + // Historical note: We also did that to placate Valgrind. reinterpret_cast*>(dest)->ClearPadding(size, alignment); break; } diff --git a/libartbase/base/arena_allocator.h b/libartbase/base/arena_allocator.h index 4dccd033d6..a9ccae1b07 100644 --- a/libartbase/base/arena_allocator.h +++ b/libartbase/base/arena_allocator.h @@ -148,34 +148,9 @@ class ArenaAllocatorStatsImpl { typedef ArenaAllocatorStatsImpl ArenaAllocatorStats; -template -class ArenaAllocatorMemoryToolCheckImpl { - // This is the generic template but since there is a partial specialization - // for kValgrind == false, this can be instantiated only for kValgrind == true. - static_assert(kValgrind, "This template can be instantiated only for Valgrind."); - static_assert(kAvailable, "Valgrind implies memory tool availability."); - - public: - ArenaAllocatorMemoryToolCheckImpl() : is_running_on_valgrind_(RUNNING_ON_MEMORY_TOOL) { } - bool IsRunningOnMemoryTool() { return is_running_on_valgrind_; } - - private: - const bool is_running_on_valgrind_; -}; - -template -class ArenaAllocatorMemoryToolCheckImpl { - public: - ArenaAllocatorMemoryToolCheckImpl() { } - bool IsRunningOnMemoryTool() { return kAvailable; } -}; - -typedef ArenaAllocatorMemoryToolCheckImpl - ArenaAllocatorMemoryToolCheck; - -class ArenaAllocatorMemoryTool : private ArenaAllocatorMemoryToolCheck { +class ArenaAllocatorMemoryTool { public: - using ArenaAllocatorMemoryToolCheck::IsRunningOnMemoryTool; + bool IsRunningOnMemoryTool() { return kMemoryToolIsAvailable; } void MakeDefined(void* ptr, size_t size) { if (UNLIKELY(IsRunningOnMemoryTool())) { diff --git a/libartbase/base/arena_allocator_test.cc b/libartbase/base/arena_allocator_test.cc index e358710ca6..6323a2b97c 100644 --- a/libartbase/base/arena_allocator_test.cc +++ b/libartbase/base/arena_allocator_test.cc @@ -16,6 +16,7 @@ #include "arena_allocator-inl.h" #include "arena_bit_vector.h" +#include "base/common_art_test.h" #include "gtest/gtest.h" #include "malloc_arena_pool.h" #include "memory_tool.h" @@ -146,11 +147,8 @@ TEST_F(ArenaAllocatorTest, AllocAlignment) { } TEST_F(ArenaAllocatorTest, ReallocReuse) { - // Realloc does not reuse arenas when running under sanitization. So we cannot do those - if (RUNNING_ON_MEMORY_TOOL != 0) { - printf("WARNING: TEST DISABLED FOR MEMORY_TOOL\n"); - return; - } + // Realloc does not reuse arenas when running under sanitization. + TEST_DISABLED_FOR_MEMORY_TOOL(); { // Case 1: small aligned allocation, aligned extend inside arena. diff --git a/libartbase/base/common_art_test.h b/libartbase/base/common_art_test.h index 3998be516d..d9bea3d97a 100644 --- a/libartbase/base/common_art_test.h +++ b/libartbase/base/common_art_test.h @@ -210,23 +210,11 @@ using CommonArtTestWithParam = CommonArtTestBase>; } #define TEST_DISABLED_FOR_MEMORY_TOOL() \ - if (RUNNING_ON_MEMORY_TOOL > 0) { \ + if (kRunningOnMemoryTool) { \ printf("WARNING: TEST DISABLED FOR MEMORY TOOL\n"); \ return; \ } -#define TEST_DISABLED_FOR_MEMORY_TOOL_VALGRIND() \ - if (RUNNING_ON_MEMORY_TOOL > 0 && kMemoryToolIsValgrind) { \ - printf("WARNING: TEST DISABLED FOR MEMORY TOOL VALGRIND\n"); \ - return; \ - } - -#define TEST_DISABLED_FOR_MEMORY_TOOL_ASAN() \ - if (RUNNING_ON_MEMORY_TOOL > 0 && !kMemoryToolIsValgrind) { \ - printf("WARNING: TEST DISABLED FOR MEMORY TOOL ASAN\n"); \ - return; \ - } - #define TEST_DISABLED_FOR_HEAP_POISONING() \ if (kPoisonHeapReferences) { \ printf("WARNING: TEST DISABLED FOR HEAP POISONING\n"); \ diff --git a/libartbase/base/malloc_arena_pool.cc b/libartbase/base/malloc_arena_pool.cc index 144b06ceb9..15a5d71a6b 100644 --- a/libartbase/base/malloc_arena_pool.cc +++ b/libartbase/base/malloc_arena_pool.cc @@ -53,7 +53,7 @@ MallocArena::MallocArena(size_t size) { memory_ = unaligned_memory_; } else { memory_ = AlignUp(unaligned_memory_, ArenaAllocator::kArenaAlignment); - if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { + if (kRunningOnMemoryTool) { size_t head = memory_ - unaligned_memory_; size_t tail = overallocation - head; MEMORY_TOOL_MAKE_NOACCESS(unaligned_memory_, head); @@ -66,7 +66,7 @@ MallocArena::MallocArena(size_t size) { MallocArena::~MallocArena() { constexpr size_t overallocation = RequiredOverallocation(); - if (overallocation != 0u && UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { + if (overallocation != 0u && kRunningOnMemoryTool) { size_t head = memory_ - unaligned_memory_; size_t tail = overallocation - head; MEMORY_TOOL_MAKE_UNDEFINED(unaligned_memory_, head); @@ -132,7 +132,7 @@ size_t MallocArenaPool::GetBytesAllocated() const { } void MallocArenaPool::FreeArenaChain(Arena* first) { - if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { + if (kRunningOnMemoryTool) { for (Arena* arena = first; arena != nullptr; arena = arena->next_) { MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_); } diff --git a/libartbase/base/mem_map.cc b/libartbase/base/mem_map.cc index c455fed829..9ba1d6c139 100644 --- a/libartbase/base/mem_map.cc +++ b/libartbase/base/mem_map.cc @@ -460,7 +460,7 @@ MemMap* MemMap::MapFileAtAddress(uint8_t* expected_ptr, (expected_ptr == nullptr) ? nullptr : (expected_ptr - page_offset); size_t redzone_size = 0; - if (RUNNING_ON_MEMORY_TOOL && kMemoryToolAddsRedzones && expected_ptr == nullptr) { + if (kRunningOnMemoryTool && kMemoryToolAddsRedzones && expected_ptr == nullptr) { redzone_size = kPageSize; page_aligned_byte_count += redzone_size; } @@ -649,9 +649,11 @@ void MemMap::MadviseDontNeedAndZero() { bool MemMap::Sync() { bool result; if (redzone_size_ != 0) { - // To avoid valgrind errors, temporarily lift the lower-end noaccess protection before passing - // it to msync() as it only accepts page-aligned base address, and exclude the higher-end - // noaccess protection from the msync range. b/27552451. + // To avoid errors when running on a memory tool, temporarily lift the lower-end noaccess + // protection before passing it to msync() as it only accepts page-aligned base address, + // and exclude the higher-end noaccess protection from the msync range. b/27552451. + // TODO: Valgrind is no longer supported, but Address Sanitizer is: + // check whether this special case is needed for ASan. uint8_t* base_begin = reinterpret_cast(base_begin_); MEMORY_TOOL_MAKE_DEFINED(base_begin, begin_ - base_begin); result = msync(BaseBegin(), End() - base_begin, MS_SYNC) == 0; diff --git a/libartbase/base/mem_map_test.cc b/libartbase/base/mem_map_test.cc index d956126df1..4a78bdcabe 100644 --- a/libartbase/base/mem_map_test.cc +++ b/libartbase/base/mem_map_test.cc @@ -471,31 +471,33 @@ TEST_F(MemMapTest, MapAnonymousExactAddr32bitHighAddr) { // cannot allocate in the 2GB-4GB region. TEST_DISABLED_FOR_MIPS(); + // This test may not work under Valgrind. + // TODO: Valgrind is no longer supported, but Address Sanitizer is: + // check whether this test works with ASan. + TEST_DISABLED_FOR_MEMORY_TOOL(); + CommonInit(); - // This test may not work under valgrind. - if (RUNNING_ON_MEMORY_TOOL == 0) { - constexpr size_t size = 0x100000; - // Try all addresses starting from 2GB to 4GB. - size_t start_addr = 2 * GB; - std::string error_msg; - std::unique_ptr map; - for (; start_addr <= std::numeric_limits::max() - size; start_addr += size) { - map.reset(MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr", - reinterpret_cast(start_addr), - size, - PROT_READ | PROT_WRITE, - /*low_4gb*/true, - false, - &error_msg)); - if (map != nullptr) { - break; - } + constexpr size_t size = 0x100000; + // Try all addresses starting from 2GB to 4GB. + size_t start_addr = 2 * GB; + std::string error_msg; + std::unique_ptr map; + for (; start_addr <= std::numeric_limits::max() - size; start_addr += size) { + map.reset(MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr", + reinterpret_cast(start_addr), + size, + PROT_READ | PROT_WRITE, + /*low_4gb*/true, + false, + &error_msg)); + if (map != nullptr) { + break; } - ASSERT_TRUE(map.get() != nullptr) << error_msg; - ASSERT_GE(reinterpret_cast(map->End()), 2u * GB); - ASSERT_TRUE(error_msg.empty()); - ASSERT_EQ(BaseBegin(map.get()), reinterpret_cast(start_addr)); } + ASSERT_TRUE(map.get() != nullptr) << error_msg; + ASSERT_GE(reinterpret_cast(map->End()), 2u * GB); + ASSERT_TRUE(error_msg.empty()); + ASSERT_EQ(BaseBegin(map.get()), reinterpret_cast(start_addr)); } TEST_F(MemMapTest, MapAnonymousOverflow) { diff --git a/libartbase/base/memory_tool.h b/libartbase/base/memory_tool.h index e1df99fed4..d381f010f5 100644 --- a/libartbase/base/memory_tool.h +++ b/libartbase/base/memory_tool.h @@ -19,53 +19,53 @@ #include +namespace art { + #if !defined(__has_feature) -#define __has_feature(x) 0 +# define __has_feature(x) 0 #endif #if __has_feature(address_sanitizer) -#include -#define ADDRESS_SANITIZER +# include +# define ADDRESS_SANITIZER -#ifdef ART_ENABLE_ADDRESS_SANITIZER -#define MEMORY_TOOL_MAKE_NOACCESS(p, s) __asan_poison_memory_region(p, s) -#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) __asan_unpoison_memory_region(p, s) -#define MEMORY_TOOL_MAKE_DEFINED(p, s) __asan_unpoison_memory_region(p, s) +# ifdef ART_ENABLE_ADDRESS_SANITIZER +# define MEMORY_TOOL_MAKE_NOACCESS(p, s) __asan_poison_memory_region(p, s) +# define MEMORY_TOOL_MAKE_UNDEFINED(p, s) __asan_unpoison_memory_region(p, s) +# define MEMORY_TOOL_MAKE_DEFINED(p, s) __asan_unpoison_memory_region(p, s) constexpr bool kMemoryToolIsAvailable = true; -#else -#define MEMORY_TOOL_MAKE_NOACCESS(p, s) do { (void)(p); (void)(s); } while (0) -#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) do { (void)(p); (void)(s); } while (0) -#define MEMORY_TOOL_MAKE_DEFINED(p, s) do { (void)(p); (void)(s); } while (0) +# else +# define MEMORY_TOOL_MAKE_NOACCESS(p, s) do { (void)(p); (void)(s); } while (0) +# define MEMORY_TOOL_MAKE_UNDEFINED(p, s) do { (void)(p); (void)(s); } while (0) +# define MEMORY_TOOL_MAKE_DEFINED(p, s) do { (void)(p); (void)(s); } while (0) constexpr bool kMemoryToolIsAvailable = false; -#endif +# endif extern "C" void __asan_handle_no_return(); -#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) -#define MEMORY_TOOL_HANDLE_NO_RETURN __asan_handle_no_return() -#define RUNNING_ON_MEMORY_TOOL 1U -constexpr bool kMemoryToolIsValgrind = false; +# define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) +# define MEMORY_TOOL_HANDLE_NO_RETURN __asan_handle_no_return() +constexpr bool kRunningOnMemoryTool = true; constexpr bool kMemoryToolDetectsLeaks = true; constexpr bool kMemoryToolAddsRedzones = true; constexpr size_t kMemoryToolStackGuardSizeScale = 2; #else -#include -#include -#define MEMORY_TOOL_MAKE_NOACCESS(p, s) VALGRIND_MAKE_MEM_NOACCESS(p, s) -#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) VALGRIND_MAKE_MEM_UNDEFINED(p, s) -#define MEMORY_TOOL_MAKE_DEFINED(p, s) VALGRIND_MAKE_MEM_DEFINED(p, s) -#define ATTRIBUTE_NO_SANITIZE_ADDRESS -#define MEMORY_TOOL_HANDLE_NO_RETURN do { } while (0) -#define RUNNING_ON_MEMORY_TOOL RUNNING_ON_VALGRIND -constexpr bool kMemoryToolIsAvailable = true; -constexpr bool kMemoryToolIsValgrind = true; -constexpr bool kMemoryToolDetectsLeaks = true; -constexpr bool kMemoryToolAddsRedzones = true; +# define MEMORY_TOOL_MAKE_NOACCESS(p, s) do { (void)(p); (void)(s); } while (0) +# define MEMORY_TOOL_MAKE_UNDEFINED(p, s) do { (void)(p); (void)(s); } while (0) +# define MEMORY_TOOL_MAKE_DEFINED(p, s) do { (void)(p); (void)(s); } while (0) +# define ATTRIBUTE_NO_SANITIZE_ADDRESS +# define MEMORY_TOOL_HANDLE_NO_RETURN do { } while (0) +constexpr bool kRunningOnMemoryTool = false; +constexpr bool kMemoryToolIsAvailable = false; +constexpr bool kMemoryToolDetectsLeaks = false; +constexpr bool kMemoryToolAddsRedzones = false; constexpr size_t kMemoryToolStackGuardSizeScale = 1; #endif +} // namespace art + #endif // ART_LIBARTBASE_BASE_MEMORY_TOOL_H_ diff --git a/libartbase/base/scoped_arena_containers.h b/libartbase/base/scoped_arena_containers.h index 44d7ebbc96..6c78bad21b 100644 --- a/libartbase/base/scoped_arena_containers.h +++ b/libartbase/base/scoped_arena_containers.h @@ -236,7 +236,7 @@ class ArenaDelete { protected: // Used for variable sized objects such as RegisterLine. ALWAYS_INLINE void ProtectMemory(T* ptr, size_t size) const { - if (RUNNING_ON_MEMORY_TOOL > 0) { + if (kRunningOnMemoryTool) { // Writing to the memory will fail ift we already destroyed the pointer with // DestroyOnlyDelete since we make it no access. memset(ptr, kMagicFill, size); diff --git a/libdexfile/dex/dex_file_tracking_registrar.cc b/libdexfile/dex/dex_file_tracking_registrar.cc index 78ea9c16cb..551bea108c 100644 --- a/libdexfile/dex/dex_file_tracking_registrar.cc +++ b/libdexfile/dex/dex_file_tracking_registrar.cc @@ -130,7 +130,8 @@ inline void SetRegistrationRange(const void* begin, size_t size, bool should_poi MEMORY_TOOL_MAKE_NOACCESS(begin, size); } else { // Note: MEMORY_TOOL_MAKE_UNDEFINED has the same functionality with Address - // Sanitizer. The difference has not been tested with Valgrind + // Sanitizer. + // Historical note: The difference has not been tested with Valgrind. MEMORY_TOOL_MAKE_DEFINED(begin, size); } } diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 3c0b3e42c9..108c753c12 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -611,7 +611,7 @@ bool PatchOat::Patch(const std::string& image_location, } } - if (!kIsDebugBuild && !(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { + if (!kIsDebugBuild && !(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { // We want to just exit on non-debug builds, not bringing the runtime down // in an orderly fashion. So release the following fields. runtime.release(); @@ -691,7 +691,7 @@ bool PatchOat::Verify(const std::string& image_location, } } - if (!kIsDebugBuild && !(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { + if (!kIsDebugBuild && !(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { // We want to just exit on non-debug builds, not bringing the runtime down // in an orderly fashion. So release the following fields. runtime.release(); diff --git a/runtime/base/mem_map_arena_pool.cc b/runtime/base/mem_map_arena_pool.cc index 9ac7886e5d..702f0e453b 100644 --- a/runtime/base/mem_map_arena_pool.cc +++ b/runtime/base/mem_map_arena_pool.cc @@ -125,7 +125,7 @@ size_t MemMapArenaPool::GetBytesAllocated() const { } void MemMapArenaPool::FreeArenaChain(Arena* first) { - if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { + if (kRunningOnMemoryTool) { for (Arena* arena = first; arena != nullptr; arena = arena->next_) { MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_); } diff --git a/runtime/exec_utils_test.cc b/runtime/exec_utils_test.cc index 68edfa8b72..a9c1ea2ae0 100644 --- a/runtime/exec_utils_test.cc +++ b/runtime/exec_utils_test.cc @@ -36,8 +36,10 @@ TEST_F(ExecUtilsTest, ExecSuccess) { command.push_back("/usr/bin/id"); } std::string error_msg; - if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { - // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks. + if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { + // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks. + // TODO: Valgrind is no longer supported, but Address Sanitizer is: + // check whether the following code works with ASan. EXPECT_TRUE(Exec(command, &error_msg)); } EXPECT_EQ(0U, error_msg.size()) << error_msg; @@ -50,8 +52,10 @@ TEST_F(ExecUtilsTest, ExecError) { std::vector command; command.push_back("bogus"); std::string error_msg; - if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { - // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks. + if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { + // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks. + // TODO: Valgrind is no longer supported, but Address Sanitizer is: + // check whether the following code works with ASan. EXPECT_FALSE(Exec(command, &error_msg)); EXPECT_FALSE(error_msg.empty()); } @@ -72,8 +76,10 @@ TEST_F(ExecUtilsTest, EnvSnapshotAdditionsAreNotVisible) { } command.push_back(kModifiedVariable); std::string error_msg; - if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { - // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks. + if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { + // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks. + // TODO: Valgrind is no longer supported, but Address Sanitizer is: + // check whether the following code works with ASan. EXPECT_FALSE(Exec(command, &error_msg)); EXPECT_NE(0U, error_msg.size()) << error_msg; } @@ -97,8 +103,10 @@ TEST_F(ExecUtilsTest, EnvSnapshotDeletionsAreNotVisible) { } command.push_back(kDeletedVariable); std::string error_msg; - if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) { - // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks. + if (!(kRunningOnMemoryTool && kMemoryToolDetectsLeaks)) { + // Running on Valgrind fails due to some memory that leaks in thread alternate signal stacks. + // TODO: Valgrind is no longer supported, but Address Sanitizer is: + // check whether the following code works with ASan. EXPECT_TRUE(Exec(command, &error_msg)); EXPECT_EQ(0U, error_msg.size()) << error_msg; } diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h index 150fe956ae..30213d55c5 100644 --- a/runtime/gc/allocator/rosalloc.h +++ b/runtime/gc/allocator/rosalloc.h @@ -625,7 +625,7 @@ class RosAlloc { // If true, check that the returned memory is actually zero. static constexpr bool kCheckZeroMemory = kIsDebugBuild; - // Valgrind protects memory, so do not check memory when running under valgrind. In a normal + // Do not check memory when running under a memory tool. In a normal // build with kCheckZeroMemory the whole test should be optimized away. // TODO: Unprotect before checks. ALWAYS_INLINE bool ShouldCheckZeroMemory(); @@ -768,7 +768,7 @@ class RosAlloc { // greater than or equal to this value, release pages. const size_t page_release_size_threshold_; - // Whether this allocator is running under Valgrind. + // Whether this allocator is running on a memory tool. bool is_running_on_memory_tool_; // The base address of the memory region that's managed by this allocator. diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 948d23303c..675686830e 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -272,7 +272,7 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, } case kAllocatorTypeRosAlloc: { if (kInstrumented && UNLIKELY(is_running_on_memory_tool_)) { - // If running on valgrind or asan, we should be using the instrumented path. + // If running on ASan, we should be using the instrumented path. size_t max_bytes_tl_bulk_allocated = rosalloc_space_->MaxBytesBulkAllocatedFor(alloc_size); if (UNLIKELY(IsOutOfMemoryOnAllocation(allocator_type, max_bytes_tl_bulk_allocated, @@ -303,7 +303,7 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, } case kAllocatorTypeDlMalloc: { if (kInstrumented && UNLIKELY(is_running_on_memory_tool_)) { - // If running on valgrind, we should be using the instrumented path. + // If running on ASan, we should be using the instrumented path. ret = dlmalloc_space_->Alloc(self, alloc_size, bytes_allocated, diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 25ed652b41..8e3bbde224 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -2248,7 +2248,8 @@ class ZygoteCompactingCollector FINAL : public collector::SemiSpace { // Add a new bin with the remaining space. AddBin(size - alloc_size, pos + alloc_size); } - // Copy the object over to its new location. Don't use alloc_size to avoid valgrind error. + // Copy the object over to its new location. + // Historical note: We did not use `alloc_size` to avoid a Valgrind error. memcpy(reinterpret_cast(forward_address), obj, obj_size); if (kUseBakerReadBarrier) { obj->AssertReadBarrierState(); diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc index 512cde484d..a24ca32314 100644 --- a/runtime/gc/space/large_object_space.cc +++ b/runtime/gc/space/large_object_space.cc @@ -45,8 +45,9 @@ class MemoryToolLargeObjectMapSpace FINAL : public LargeObjectMapSpace { } ~MemoryToolLargeObjectMapSpace() OVERRIDE { - // Keep valgrind happy if there is any large objects such as dex cache arrays which aren't - // freed since they are held live by the class linker. + // Historical note: We were deleting large objects to keep Valgrind happy if there were + // any large objects such as Dex cache arrays which aren't freed since they are held live + // by the class linker. MutexLock mu(Thread::Current(), lock_); for (auto& m : large_objects_) { delete m.second.mem_map; diff --git a/runtime/gc/space/memory_tool_malloc_space-inl.h b/runtime/gc/space/memory_tool_malloc_space-inl.h index 8282f3dda7..c022171082 100644 --- a/runtime/gc/space/memory_tool_malloc_space-inl.h +++ b/runtime/gc/space/memory_tool_malloc_space-inl.h @@ -30,11 +30,14 @@ namespace space { namespace memory_tool_details { template -inline mirror::Object* AdjustForValgrind(void* obj_with_rdz, size_t num_bytes, - size_t bytes_allocated, size_t usable_size, - size_t bytes_tl_bulk_allocated, - size_t* bytes_allocated_out, size_t* usable_size_out, - size_t* bytes_tl_bulk_allocated_out) { +inline mirror::Object* AdjustForMemoryTool(void* obj_with_rdz, + size_t num_bytes, + size_t bytes_allocated, + size_t usable_size, + size_t bytes_tl_bulk_allocated, + size_t* bytes_allocated_out, + size_t* usable_size_out, + size_t* bytes_tl_bulk_allocated_out) { if (bytes_allocated_out != nullptr) { *bytes_allocated_out = bytes_allocated; } @@ -84,24 +87,31 @@ template mirror::Object* MemoryToolMallocSpace::AllocWithGrowth( - Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out, + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::AllocWithGrowth( + Thread* self, + size_t num_bytes, + size_t* bytes_allocated_out, + size_t* usable_size_out, size_t* bytes_tl_bulk_allocated_out) { size_t bytes_allocated; size_t usable_size; size_t bytes_tl_bulk_allocated; - void* obj_with_rdz = S::AllocWithGrowth(self, num_bytes + 2 * kMemoryToolRedZoneBytes, - &bytes_allocated, &usable_size, + void* obj_with_rdz = S::AllocWithGrowth(self, + num_bytes + 2 * kMemoryToolRedZoneBytes, + &bytes_allocated, + &usable_size, &bytes_tl_bulk_allocated); if (obj_with_rdz == nullptr) { return nullptr; } - return memory_tool_details::AdjustForValgrind( - obj_with_rdz, num_bytes, - bytes_allocated, usable_size, + return memory_tool_details::AdjustForMemoryTool( + obj_with_rdz, + num_bytes, + bytes_allocated, + usable_size, bytes_tl_bulk_allocated, bytes_allocated_out, usable_size_out, @@ -113,27 +123,35 @@ template mirror::Object* MemoryToolMallocSpace::Alloc( - Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out, + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::Alloc( + Thread* self, + size_t num_bytes, + size_t* bytes_allocated_out, + size_t* usable_size_out, size_t* bytes_tl_bulk_allocated_out) { size_t bytes_allocated; size_t usable_size; size_t bytes_tl_bulk_allocated; - void* obj_with_rdz = S::Alloc(self, num_bytes + 2 * kMemoryToolRedZoneBytes, - &bytes_allocated, &usable_size, &bytes_tl_bulk_allocated); + void* obj_with_rdz = S::Alloc(self, + num_bytes + 2 * kMemoryToolRedZoneBytes, + &bytes_allocated, + &usable_size, + &bytes_tl_bulk_allocated); if (obj_with_rdz == nullptr) { return nullptr; } - return memory_tool_details::AdjustForValgrind(obj_with_rdz, num_bytes, - bytes_allocated, usable_size, - bytes_tl_bulk_allocated, - bytes_allocated_out, - usable_size_out, - bytes_tl_bulk_allocated_out); + return memory_tool_details::AdjustForMemoryTool( + obj_with_rdz, + num_bytes, + bytes_allocated, + usable_size, + bytes_tl_bulk_allocated, + bytes_allocated_out, + usable_size_out, + bytes_tl_bulk_allocated_out); } template mirror::Object* MemoryToolMallocSpace::AllocThreadUnsafe( - Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out, + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::AllocThreadUnsafe( + Thread* self, + size_t num_bytes, + size_t* bytes_allocated_out, + size_t* usable_size_out, size_t* bytes_tl_bulk_allocated_out) { size_t bytes_allocated; size_t usable_size; size_t bytes_tl_bulk_allocated; - void* obj_with_rdz = S::AllocThreadUnsafe(self, num_bytes + 2 * kMemoryToolRedZoneBytes, - &bytes_allocated, &usable_size, + void* obj_with_rdz = S::AllocThreadUnsafe(self, + num_bytes + 2 * kMemoryToolRedZoneBytes, + &bytes_allocated, + &usable_size, &bytes_tl_bulk_allocated); if (obj_with_rdz == nullptr) { return nullptr; } - return memory_tool_details::AdjustForValgrind( - obj_with_rdz, num_bytes, - bytes_allocated, usable_size, + return memory_tool_details::AdjustForMemoryTool( + obj_with_rdz, + num_bytes, + bytes_allocated, + usable_size, bytes_tl_bulk_allocated, bytes_allocated_out, usable_size_out, @@ -170,12 +195,14 @@ template size_t MemoryToolMallocSpace::AllocationSize( + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::AllocationSize( mirror::Object* obj, size_t* usable_size) { - size_t result = S::AllocationSize(reinterpret_cast( - reinterpret_cast(obj) - (kAdjustForRedzoneInAllocSize ? kMemoryToolRedZoneBytes : 0)), + size_t result = S::AllocationSize( + reinterpret_cast( + reinterpret_cast(obj) + - (kAdjustForRedzoneInAllocSize ? kMemoryToolRedZoneBytes : 0)), usable_size); if (usable_size != nullptr) { if (kUseObjSizeForUsable) { @@ -192,10 +219,9 @@ template size_t MemoryToolMallocSpace::Free( - Thread* self, mirror::Object* ptr) { + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::Free(Thread* self, mirror::Object* ptr) { void* obj_after_rdz = reinterpret_cast(ptr); uint8_t* obj_with_rdz = reinterpret_cast(obj_after_rdz) - kMemoryToolRedZoneBytes; @@ -220,10 +246,10 @@ template size_t MemoryToolMallocSpace::FreeList( - Thread* self, size_t num_ptrs, mirror::Object** ptrs) { + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::FreeList( + Thread* self, size_t num_ptrs, mirror::Object** ptrs) { size_t freed = 0; for (size_t i = 0; i < num_ptrs; i++) { freed += Free(self, ptrs[i]); @@ -238,11 +264,12 @@ template template MemoryToolMallocSpace::MemoryToolMallocSpace( - MemMap* mem_map, size_t initial_size, Params... params) : S(mem_map, initial_size, params...) { - // Don't want to change the valgrind states of the mem map here as the allocator is already + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::MemoryToolMallocSpace( + MemMap* mem_map, size_t initial_size, Params... params) + : S(mem_map, initial_size, params...) { + // Don't want to change the memory tool states of the mem map here as the allocator is already // initialized at this point and that may interfere with what the allocator does internally. Note // that the tail beyond the initial size is mprotected. } @@ -252,9 +279,9 @@ template size_t MemoryToolMallocSpace::MaxBytesBulkAllocatedFor(size_t num_bytes) { + kMemoryToolRedZoneBytes, + kAdjustForRedzoneInAllocSize, + kUseObjSizeForUsable>::MaxBytesBulkAllocatedFor(size_t num_bytes) { return S::MaxBytesBulkAllocatedFor(num_bytes + 2 * kMemoryToolRedZoneBytes); } diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc index e7865363a1..b0402e4b83 100644 --- a/runtime/gc/space/rosalloc_space.cc +++ b/runtime/gc/space/rosalloc_space.cc @@ -77,7 +77,7 @@ RosAllocSpace* RosAllocSpace::CreateFromMemMap(MemMap* mem_map, const std::strin // Everything is set so record in immutable structure and leave uint8_t* begin = mem_map->Begin(); - // TODO: Fix RosAllocSpace to support Valgrind/ASan. There is currently some issues with + // TODO: Fix RosAllocSpace to support ASan. There is currently some issues with // AllocationSize caused by redzones. b/12944686 if (running_on_memory_tool) { return new MemoryToolMallocSpace( @@ -382,12 +382,12 @@ size_t RosAllocSpace::AllocationSizeNonvirtual(mirror::Object* obj, size_t* usab size_t size = obj->SizeOf(); bool add_redzones = false; if (kMaybeIsRunningOnMemoryTool) { - add_redzones = RUNNING_ON_MEMORY_TOOL ? kMemoryToolAddsRedzones : 0; + add_redzones = kRunningOnMemoryTool && kMemoryToolAddsRedzones; if (add_redzones) { size += 2 * kDefaultMemoryToolRedZoneBytes; } } else { - DCHECK_EQ(RUNNING_ON_MEMORY_TOOL, 0U); + DCHECK(!kRunningOnMemoryTool); } size_t size_by_size = rosalloc_->UsableSize(size); if (kIsDebugBuild) { diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h index 9d16b87b7d..4c17233360 100644 --- a/runtime/gc/space/rosalloc_space.h +++ b/runtime/gc/space/rosalloc_space.h @@ -159,8 +159,8 @@ class RosAllocSpace : public MallocSpace { void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size, size_t maximum_size, bool low_memory_mode) OVERRIDE { - return CreateRosAlloc(base, morecore_start, initial_size, maximum_size, low_memory_mode, - RUNNING_ON_MEMORY_TOOL != 0); + return CreateRosAlloc( + base, morecore_start, initial_size, maximum_size, low_memory_mode, kRunningOnMemoryTool); } static allocator::RosAlloc* CreateRosAlloc(void* base, size_t morecore_start, size_t initial_size, size_t maximum_size, bool low_memory_mode, diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc index 655713e8c6..01e74962ba 100644 --- a/runtime/interpreter/unstarted_runtime_test.cc +++ b/runtime/interpreter/unstarted_runtime_test.cc @@ -867,11 +867,6 @@ TEST_F(UnstartedRuntimeTest, Cos) { } TEST_F(UnstartedRuntimeTest, Pow) { - // Valgrind seems to get this wrong, actually. Disable for valgrind. - if (RUNNING_ON_MEMORY_TOOL != 0 && kMemoryToolIsValgrind) { - return; - } - Thread* self = Thread::Current(); ScopedObjectAccess soa(self); diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index b7b779ce31..f31a24ec2a 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -333,7 +333,7 @@ void Jit::DeleteThreadPool() { } // When running sanitized, let all tasks finish to not leak. Otherwise just clear the queue. - if (!RUNNING_ON_MEMORY_TOOL) { + if (!kRunningOnMemoryTool) { pool->StopWorkers(self); pool->RemoveAllTasks(self); } diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc index 14f3f45f9e..b3a47c3053 100644 --- a/runtime/native_stack_dump.cc +++ b/runtime/native_stack_dump.cc @@ -289,8 +289,10 @@ void DumpNativeStack(std::ostream& os, ArtMethod* current_method, void* ucontext_ptr, bool skip_frames) { - // b/18119146 - if (RUNNING_ON_MEMORY_TOOL != 0) { + // Historical note: This was disabled when running under Valgrind (b/18119146). + // TODO: Valgrind is no longer supported, but Address Sanitizer is: + // check whether this test works with ASan. + if (kRunningOnMemoryTool) { return; } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 1e327fc8ed..6d10a224e9 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -240,7 +240,7 @@ Runtime::Runtime() exit_(nullptr), abort_(nullptr), stats_enabled_(false), - is_running_on_memory_tool_(RUNNING_ON_MEMORY_TOOL), + is_running_on_memory_tool_(kRunningOnMemoryTool), instrumentation_(), main_thread_group_(nullptr), system_thread_group_(nullptr), @@ -1362,8 +1362,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { case InstructionSet::kMips: case InstructionSet::kMips64: implicit_null_checks_ = true; - // Installing stack protection does not play well with valgrind. - implicit_so_checks_ = !(RUNNING_ON_MEMORY_TOOL && kMemoryToolIsValgrind); + // Historical note: Installing stack protection was not playing well with Valgrind. + implicit_so_checks_ = true; break; default: // Keep the defaults. @@ -1378,8 +1378,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // These need to be in a specific order. The null point check handler must be // after the suspend check and stack overflow check handlers. // - // Note: the instances attach themselves to the fault manager and are handled by it. The manager - // will delete the instance on Shutdown(). + // Note: the instances attach themselves to the fault manager and are handled by it. The + // manager will delete the instance on Shutdown(). if (implicit_suspend_checks_) { new SuspensionHandler(&fault_manager); } diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc index 72d9919971..54769f9c49 100644 --- a/runtime/runtime_callbacks_test.cc +++ b/runtime/runtime_callbacks_test.cc @@ -339,8 +339,8 @@ class RuntimeSigQuitCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest { }; TEST_F(RuntimeSigQuitCallbackRuntimeCallbacksTest, SigQuit) { - // SigQuit induces a dump. ASAN isn't happy with libunwind reading memory. - TEST_DISABLED_FOR_MEMORY_TOOL_ASAN(); + // SigQuit induces a dump. ASan isn't happy with libunwind reading memory. + TEST_DISABLED_FOR_MEMORY_TOOL(); // The runtime needs to be started for the signal handler. Thread* self = Thread::Current(); diff --git a/runtime/thread.cc b/runtime/thread.cc index a8133a1fda..210e1b0c51 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1116,21 +1116,10 @@ bool Thread::InitStackHwm() { Runtime* runtime = Runtime::Current(); bool implicit_stack_check = !runtime->ExplicitStackOverflowChecks() && !runtime->IsAotCompiler(); - // Valgrind on arm doesn't give the right values here. Do not install the guard page, and - // effectively disable stack overflow checks (we'll get segfaults, potentially) by setting - // stack_begin to 0. - const bool valgrind_on_arm = - (kRuntimeISA == InstructionSet::kArm || kRuntimeISA == InstructionSet::kArm64) && - kMemoryToolIsValgrind && - RUNNING_ON_MEMORY_TOOL != 0; - if (valgrind_on_arm) { - tlsPtr_.stack_begin = nullptr; - } - ResetDefaultStackEnd(); // Install the protected region if we are doing implicit overflow checks. - if (implicit_stack_check && !valgrind_on_arm) { + if (implicit_stack_check) { // The thread might have protected region at the bottom. We need // to install our own region so we need to move the limits // of the stack to make room for it. diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc index 7ada47d304..a91d348441 100644 --- a/test/137-cfi/cfi.cc +++ b/test/137-cfi/cfi.cc @@ -114,8 +114,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess( jint, jboolean) { #if __linux__ - // TODO: What to do on Valgrind? - std::unique_ptr bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid())); if (!bt->Unwind(0, nullptr)) { printf("Cannot unwind in process.\n"); @@ -191,7 +189,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess( jboolean, jint pid_int) { #if __linux__ - // TODO: What to do on Valgrind? pid_t pid = static_cast(pid_int); // OK, this is painful. debuggerd uses ptrace to unwind other processes. diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index 71f4cc0731..68efcaf5a3 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -266,14 +266,16 @@ target_config = { } }, 'art-gtest-valgrind32': { - # Disabled: x86 valgrind does not understand SSE4.x + # Disabled: Valgrind is no longer supported. + # Historical note: This was already disabled, as x86 valgrind did not understand SSE4.x # 'make' : 'valgrind-test-art-host32', 'env': { 'ART_USE_READ_BARRIER' : 'false' } }, 'art-gtest-valgrind64': { - 'make' : 'valgrind-test-art-host64', + # Disabled: Valgrind is no longer supported. + # 'make' : 'valgrind-test-art-host64', 'env': { 'ART_USE_READ_BARRIER' : 'false' } diff --git a/test/valgrind-suppressions.txt b/test/valgrind-suppressions.txt deleted file mode 100644 index a97d03c2d4..0000000000 --- a/test/valgrind-suppressions.txt +++ /dev/null @@ -1,87 +0,0 @@ -{ - b/27596582 - Memcheck:Cond - fun:index - fun:expand_dynamic_string_token - fun:_dl_map_object - fun:map_doit - fun:_dl_catch_error - fun:do_preload - fun:dl_main - fun:_dl_sysdep_start - fun:_dl_start_final - fun:_dl_start - obj:/lib/x86_64-linux-gnu/ld-2.19.so -} - -{ - b/31275764 - Memcheck:Leak - match-leak-kinds: definite - fun:malloc - ... - fun:_ZN3art7Runtime17InitNativeMethodsEv -} - -# SigQuit runs libbacktrace -{ - BackTraceReading64 - Memcheck:Addr8 - fun:access_mem_unrestricted - fun:_Uelf64_memory_read - fun:_Uelf64_valid_object_memory - fun:map_create_list - fun:unw_map_local_create - fun:_ZN14UnwindMapLocal5BuildEv - fun:_ZN12BacktraceMap6CreateEib -} -{ - BackTraceReading32 - Memcheck:Addr4 - fun:access_mem_unrestricted - fun:_Uelf32_memory_read - fun:_Uelf32_valid_object_memory - fun:map_create_list - fun:unw_map_local_create - fun:_ZN14UnwindMapLocal5BuildEv - fun:_ZN12BacktraceMap6CreateEib -} -{ - BackTraceReading64 - Memcheck:Addr8 - fun:access_mem_unrestricted - fun:_Uelf64_memory_read - fun:_Uelf64_get_load_base - fun:map_create_list - fun:unw_map_local_create - fun:_ZN14UnwindMapLocal5BuildEv - fun:_ZN12BacktraceMap6CreateEib -} -{ - BackTraceReading32 - Memcheck:Addr4 - fun:access_mem_unrestricted - fun:_Uelf32_memory_read - fun:_Uelf32_get_load_base - fun:map_create_list - fun:unw_map_local_create - fun:_ZN14UnwindMapLocal5BuildEv - fun:_ZN12BacktraceMap6CreateEib -} - -{ - process_vm_readv - Memcheck:Param - process_vm_readv(lvec[...]) - fun:process_vm_readv -} - -# Suppressions for IsAddressMapped check in MemMapTest -{ - MemMapTest_IsAddressMapped - Memcheck:Param - msync(start) - ... - fun:_ZN3art10MemMapTest15IsAddressMappedEPv - ... -} diff --git a/test/valgrind-target-suppressions.txt b/test/valgrind-target-suppressions.txt deleted file mode 100644 index 0d63a1c7aa..0000000000 --- a/test/valgrind-target-suppressions.txt +++ /dev/null @@ -1,76 +0,0 @@ -# Valgrind does not recognize the ashmen ioctl() calls on ARM64, so it assumes that a size -# parameter is a pointer. -{ - ashmem ioctl - Memcheck:Param - ioctl(generic) - ... - fun:ioctl - fun:ashmem_create_region -} - -# It seems that on ARM64 Valgrind considers the canary value used by the Clang stack protector to -# be an uninitialized value. -{ - jemalloc chunk_alloc_cache - Memcheck:Cond - fun:je_chunk_alloc_cache -} - -# The VectorImpl class does not hold a pointer to the allocated SharedBuffer structure, but to the -# beginning of the data, which is effectively an interior pointer. Valgrind has limitations when -# dealing with interior pointers. -{ - VectorImpl - Memcheck:Leak - match-leak-kinds:possible - fun:malloc - # The wildcards make this rule work both for 32-bit and 64-bit environments. - fun:_ZN7android12SharedBuffer5allocE? - fun:_ZN7android10VectorImpl5_growE?? -} - -# Clang/LLVM uses memcpy for *x = *y, even though x == y (which is undefined behavior). Ignore. -# b/29279679, https://llvm.org/bugs/show_bug.cgi?id=11763 -{ - MemCpySelfAssign - Memcheck:Overlap - fun:memcpy - ... - fun:je_malloc_tsd_boot0 -} - -# Setenv is known-leaking when overwriting mappings. This is triggered by re-initializing -# ANDROID_DATA. Ignore all setenv leaks. -{ - SetenvAndroidDataReinit - Memcheck:Leak - match-leak-kinds: definite - fun:malloc - fun:setenv -} - -{ - b/31275764 - Memcheck:Leak - match-leak-kinds: definite - fun:malloc - ... - fun:_ZN3art7Runtime17InitNativeMethodsEv -} - -# art::MemMap::MapInternal() uses msync() to check for the existence of memory mappings. -{ - art::MemMap::MapInternal() - Memcheck:Param - msync(start) - fun:msync - fun:_ZN3art6MemMap11MapInternalEPvmiiilb -} - -{ - process_vm_readv - Memcheck:Param - process_vm_readv(lvec[...]) - fun:process_vm_readv -} diff --git a/tools/art b/tools/art index 1c603d4fa7..781ee2f9cb 100644 --- a/tools/art +++ b/tools/art @@ -77,7 +77,6 @@ Usage: art [OPTIONS] [--] [ART_OPTIONS] CLASS Supported OPTIONS include: --32 Use the 32-bit Android Runtime. --64 Use the 64-bit Android Runtime. - --callgrind Launch the Android Runtime in callgrind. -d Use the debug ART library (libartd.so). --debug Equivalent to -d. --gdb Launch the Android Runtime in gdb. @@ -269,9 +268,6 @@ while [[ "$1" = "-"* ]]; do --64) ART_BINARY=dalvikvm64 ;; - --callgrind) - LAUNCH_WRAPPER="valgrind --tool=callgrind" - ;; -d) ;& # Fallthrough --debug) -- GitLab From 917c80f148ea8cef57f7c206c8c7954f2f0d13d6 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Fri, 8 Jun 2018 16:27:23 +0100 Subject: [PATCH 561/749] Simplify the ART Buildbot clean-up script regarding chroot support. The ART Buildbot is now executing the "setup device" step after the "device pre-run cleanup" step, and the "tear down device" step before the "device post-run cleanup" step, so we no longer need to preserve any part of the chroot directory when cleaning up. Therefore, try to remove the entire chroot directory during cleanup. Test: Rely on the ART Buildbot Bug: 34729697 Change-Id: I6a8c2017bc81f6cb5ed1781d2f97c0169d803532 --- tools/cleanup-buildbot-device.sh | 35 ++++++++------------------------ 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/tools/cleanup-buildbot-device.sh b/tools/cleanup-buildbot-device.sh index ca5219aa25..694c739e07 100755 --- a/tools/cleanup-buildbot-device.sh +++ b/tools/cleanup-buildbot-device.sh @@ -28,33 +28,16 @@ if [[ -n "$ART_TEST_CHROOT" ]]; then exit 1 fi - echo -e "${green}Clean up /system in chroot${nc}" - # Remove all files under /system except the potential property_contexts file. - # - # The current ART Buildbot set-up runs the "setup device" step - # (performed by script tools/setup-buildbot-device.sh) before the - # "device cleanup" step (implemented by this script). As - # property_contexts file aliases are created during the former step, - # we need this exception to prevent the property_contexts file under - # /system in the chroot from being removed by the latter step. - # - # TODO: Reorder ART Buildbot steps so that "device cleanup" happens - # before "setup device" and remove this special case. - adb shell test -d "$ART_TEST_CHROOT/system" \ - "&&" find "$ART_TEST_CHROOT/system" \ - ! -path "$ART_TEST_CHROOT/system/etc/selinux/plat_property_contexts" \ - ! -type d \ - -exec rm -f \{\} + + if adb shell test -d "$ART_TEST_CHROOT"; then + echo -e "${green}Remove entire /system directory from chroot directory${nc}" + adb shell rm -rf "$ART_TEST_CHROOT/system" - echo -e "${green}Clean up some subdirs in /data in chroot${nc}" - adb shell rm -rf \ - "$ART_TEST_CHROOT/data/local/tmp/*" \ - "$ART_TEST_CHROOT/data/art-test" \ - "$ART_TEST_CHROOT/data/nativetest" \ - "$ART_TEST_CHROOT/data/nativetest64" \ - "$ART_TEST_CHROOT/data/run-test" \ - "$ART_TEST_CHROOT/data/dalvik-cache/*" \ - "$ART_TEST_CHROOT/data/misc/trace/*" + echo -e "${green}Remove entire /data directory from chroot directory${nc}" + adb shell rm -rf "$ART_TEST_CHROOT/data" + + echo -e "${green}Remove entire chroot directory${nc}" + adb shell rmdir "$ART_TEST_CHROOT" || adb shell ls -la "$ART_TEST_CHROOT" + fi else adb shell rm -rf \ /data/local/tmp /data/art-test /data/nativetest /data/nativetest64 '/data/misc/trace/*' -- GitLab From 7a1815414f0f992558de3ee552143e9fc72096a6 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Wed, 8 Mar 2017 17:34:46 -0800 Subject: [PATCH 562/749] GC-local moved/objects_moved counters for CC. To reduce the number of CAS. Bug: 33419211 Test: test-art-host Change-Id: Ia03d9a521983e0a8426a527fff4ddd4f61f68a25 --- runtime/gc/collector/concurrent_copying-inl.h | 33 ++-- runtime/gc/collector/concurrent_copying.cc | 145 ++++++++++-------- runtime/gc/collector/concurrent_copying.h | 38 +++-- 3 files changed, 127 insertions(+), 89 deletions(-) diff --git a/runtime/gc/collector/concurrent_copying-inl.h b/runtime/gc/collector/concurrent_copying-inl.h index b331e975fd..36fefbdbc3 100644 --- a/runtime/gc/collector/concurrent_copying-inl.h +++ b/runtime/gc/collector/concurrent_copying-inl.h @@ -32,7 +32,9 @@ namespace gc { namespace collector { inline mirror::Object* ConcurrentCopying::MarkUnevacFromSpaceRegion( - mirror::Object* ref, accounting::ContinuousSpaceBitmap* bitmap) { + Thread* const self, + mirror::Object* ref, + accounting::ContinuousSpaceBitmap* bitmap) { // For the Baker-style RB, in a rare case, we could incorrectly change the object from white // to gray even though the object has already been marked through. This happens if a mutator // thread gets preempted before the AtomicSetReadBarrierState below, GC marks through the @@ -63,20 +65,21 @@ inline mirror::Object* ConcurrentCopying::MarkUnevacFromSpaceRegion( if (kUseBakerReadBarrier) { DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::GrayState()); } - PushOntoMarkStack(ref); + PushOntoMarkStack(self, ref); } return ref; } template -inline mirror::Object* ConcurrentCopying::MarkImmuneSpace(mirror::Object* ref) { +inline mirror::Object* ConcurrentCopying::MarkImmuneSpace(Thread* const self, + mirror::Object* ref) { if (kUseBakerReadBarrier) { // The GC-running thread doesn't (need to) gray immune objects except when updating thread roots // in the thread flip on behalf of suspended threads (when gc_grays_immune_objects_ is // true). Also, a mutator doesn't (need to) gray an immune object after GC has updated all // immune space objects (when updated_all_immune_objects_ is true). if (kIsDebugBuild) { - if (Thread::Current() == thread_running_gc_) { + if (self == thread_running_gc_) { DCHECK(!kGrayImmuneObject || updated_all_immune_objects_.load(std::memory_order_relaxed) || gc_grays_immune_objects_); @@ -91,7 +94,7 @@ inline mirror::Object* ConcurrentCopying::MarkImmuneSpace(mirror::Object* ref) { bool success = ref->AtomicSetReadBarrierState(/* expected_rb_state */ ReadBarrier::WhiteState(), /* rb_state */ ReadBarrier::GrayState()); if (success) { - MutexLock mu(Thread::Current(), immune_gray_stack_lock_); + MutexLock mu(self, immune_gray_stack_lock_); immune_gray_stack_.push_back(ref); } } @@ -99,7 +102,8 @@ inline mirror::Object* ConcurrentCopying::MarkImmuneSpace(mirror::Object* ref) { } template -inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref, +inline mirror::Object* ConcurrentCopying::Mark(Thread* const self, + mirror::Object* from_ref, mirror::Object* holder, MemberOffset offset) { if (from_ref == nullptr) { @@ -108,7 +112,7 @@ inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref, DCHECK(heap_->collector_type_ == kCollectorTypeCC); if (kFromGCThread) { DCHECK(is_active_); - DCHECK_EQ(Thread::Current(), thread_running_gc_); + DCHECK_EQ(self, thread_running_gc_); } else if (UNLIKELY(kUseBakerReadBarrier && !is_active_)) { // In the lock word forward address state, the read barrier bits // in the lock word are part of the stored forwarding address and @@ -134,7 +138,7 @@ inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref, mirror::Object* to_ref = GetFwdPtr(from_ref); if (to_ref == nullptr) { // It isn't marked yet. Mark it by copying it to the to-space. - to_ref = Copy(from_ref, holder, offset); + to_ref = Copy(self, from_ref, holder, offset); } // The copy should either be in a to-space region, or in the // non-moving space, if it could not fit in a to-space region. @@ -143,7 +147,7 @@ inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref, return to_ref; } case space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace: - return MarkUnevacFromSpaceRegion(from_ref, region_space_bitmap_); + return MarkUnevacFromSpaceRegion(self, from_ref, region_space_bitmap_); default: // The reference is in an unused region. LOG(FATAL_WITHOUT_ABORT) << DumpHeapReference(holder, offset, from_ref); @@ -153,24 +157,25 @@ inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref, } } else { if (immune_spaces_.ContainsObject(from_ref)) { - return MarkImmuneSpace(from_ref); + return MarkImmuneSpace(self, from_ref); } else { - return MarkNonMoving(from_ref, holder, offset); + return MarkNonMoving(self, from_ref, holder, offset); } } } inline mirror::Object* ConcurrentCopying::MarkFromReadBarrier(mirror::Object* from_ref) { mirror::Object* ret; + Thread* const self = Thread::Current(); // We can get here before marking starts since we gray immune objects before the marking phase. - if (from_ref == nullptr || !Thread::Current()->GetIsGcMarking()) { + if (from_ref == nullptr || !self->GetIsGcMarking()) { return from_ref; } // TODO: Consider removing this check when we are done investigating slow paths. b/30162165 if (UNLIKELY(mark_from_read_barrier_measurements_)) { - ret = MarkFromReadBarrierWithMeasurements(from_ref); + ret = MarkFromReadBarrierWithMeasurements(self, from_ref); } else { - ret = Mark(from_ref); + ret = Mark(self, from_ref); } // Only set the mark bit for baker barrier. if (kUseBakerReadBarrier && LIKELY(!rb_mark_bit_stack_full_ && ret->AtomicSetMarkBit(0, 1))) { diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index 2c2c437365..c4d2fdda0b 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -129,13 +129,14 @@ ConcurrentCopying::ConcurrentCopying(Heap* heap, void ConcurrentCopying::MarkHeapReference(mirror::HeapReference* field, bool do_atomic_update) { + Thread* const self = Thread::Current(); if (UNLIKELY(do_atomic_update)) { // Used to mark the referent in DelayReferenceReferent in transaction mode. mirror::Object* from_ref = field->AsMirrorPtr(); if (from_ref == nullptr) { return; } - mirror::Object* to_ref = Mark(from_ref); + mirror::Object* to_ref = Mark(self, from_ref); if (from_ref != to_ref) { do { if (field->AsMirrorPtr() != from_ref) { @@ -148,7 +149,7 @@ void ConcurrentCopying::MarkHeapReference(mirror::HeapReference* // Used for preserving soft references, should be OK to not have a CAS here since there should be // no other threads which can trigger read barriers on the same referent during reference // processing. - field->Assign(Mark(field->AsMirrorPtr())); + field->Assign(Mark(self, field->AsMirrorPtr())); } } @@ -300,6 +301,8 @@ void ConcurrentCopying::InitializePhase() { immune_spaces_.Reset(); bytes_moved_.store(0, std::memory_order_relaxed); objects_moved_.store(0, std::memory_order_relaxed); + bytes_moved_gc_thread_ = 0; + objects_moved_gc_thread_ = 0; GcCause gc_cause = GetCurrentIteration()->GetGcCause(); if (gc_cause == kGcCauseExplicit || gc_cause == kGcCauseCollectorTransition || @@ -370,11 +373,12 @@ class ConcurrentCopying::ThreadFlipVisitor : public Closure, public RootVisitor size_t count, const RootInfo& info ATTRIBUTE_UNUSED) REQUIRES_SHARED(Locks::mutator_lock_) { + Thread* self = Thread::Current(); for (size_t i = 0; i < count; ++i) { mirror::Object** root = roots[i]; mirror::Object* ref = *root; if (ref != nullptr) { - mirror::Object* to_ref = concurrent_copying_->Mark(ref); + mirror::Object* to_ref = concurrent_copying_->Mark(self, ref); if (to_ref != ref) { *root = to_ref; } @@ -386,11 +390,12 @@ class ConcurrentCopying::ThreadFlipVisitor : public Closure, public RootVisitor size_t count, const RootInfo& info ATTRIBUTE_UNUSED) REQUIRES_SHARED(Locks::mutator_lock_) { + Thread* self = Thread::Current(); for (size_t i = 0; i < count; ++i) { mirror::CompressedReference* const root = roots[i]; if (!root->IsNull()) { mirror::Object* ref = root->AsMirrorPtr(); - mirror::Object* to_ref = concurrent_copying_->Mark(ref); + mirror::Object* to_ref = concurrent_copying_->Mark(self, ref); if (to_ref != ref) { root->Assign(to_ref); } @@ -452,7 +457,7 @@ class ConcurrentCopying::FlipCallback : public Closure { // This is safe since single threaded behavior should mean FillDummyObject does not // happen when java_lang_Object_ is null. if (WellKnownClasses::java_lang_Object != nullptr) { - cc->java_lang_Object_ = down_cast(cc->Mark( + cc->java_lang_Object_ = down_cast(cc->Mark(thread, WellKnownClasses::ToClass(WellKnownClasses::java_lang_Object).Ptr())); } else { cc->java_lang_Object_ = nullptr; @@ -1024,10 +1029,10 @@ void ConcurrentCopying::DisableMarking() { mark_stack_mode_.store(kMarkStackModeOff, std::memory_order_seq_cst); } -void ConcurrentCopying::PushOntoFalseGrayStack(mirror::Object* ref) { +void ConcurrentCopying::PushOntoFalseGrayStack(Thread* const self, mirror::Object* ref) { CHECK(kUseBakerReadBarrier); DCHECK(ref != nullptr); - MutexLock mu(Thread::Current(), mark_stack_lock_); + MutexLock mu(self, mark_stack_lock_); false_gray_stack_.push_back(ref); } @@ -1070,10 +1075,9 @@ void ConcurrentCopying::ExpandGcMarkStack() { DCHECK(!gc_mark_stack_->IsFull()); } -void ConcurrentCopying::PushOntoMarkStack(mirror::Object* to_ref) { +void ConcurrentCopying::PushOntoMarkStack(Thread* const self, mirror::Object* to_ref) { CHECK_EQ(is_mark_stack_push_disallowed_.load(std::memory_order_relaxed), 0) << " " << to_ref << " " << mirror::Object::PrettyTypeOf(to_ref); - Thread* self = Thread::Current(); // TODO: pass self as an argument from call sites? CHECK(thread_running_gc_ != nullptr); MarkStackMode mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed); if (LIKELY(mark_stack_mode == kMarkStackModeThreadLocal)) { @@ -1409,10 +1413,10 @@ void ConcurrentCopying::ProcessMarkStack() { } bool ConcurrentCopying::ProcessMarkStackOnce() { - Thread* self = Thread::Current(); - CHECK(thread_running_gc_ != nullptr); - CHECK(self == thread_running_gc_); - CHECK(self->GetThreadLocalMarkStack() == nullptr); + DCHECK(thread_running_gc_ != nullptr); + Thread* const self = Thread::Current(); + DCHECK(self == thread_running_gc_); + DCHECK(thread_running_gc_->GetThreadLocalMarkStack() == nullptr); size_t count = 0; MarkStackMode mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed); if (mark_stack_mode == kMarkStackModeThreadLocal) { @@ -1432,14 +1436,14 @@ bool ConcurrentCopying::ProcessMarkStackOnce() { IssueEmptyCheckpoint(); // Process the shared GC mark stack with a lock. { - MutexLock mu(self, mark_stack_lock_); + MutexLock mu(thread_running_gc_, mark_stack_lock_); CHECK(revoked_mark_stacks_.empty()); } while (true) { std::vector refs; { // Copy refs with lock. Note the number of refs should be small. - MutexLock mu(self, mark_stack_lock_); + MutexLock mu(thread_running_gc_, mark_stack_lock_); if (gc_mark_stack_->IsEmpty()) { break; } @@ -1458,7 +1462,7 @@ bool ConcurrentCopying::ProcessMarkStackOnce() { CHECK_EQ(static_cast(mark_stack_mode), static_cast(kMarkStackModeGcExclusive)); { - MutexLock mu(self, mark_stack_lock_); + MutexLock mu(thread_running_gc_, mark_stack_lock_); CHECK(revoked_mark_stacks_.empty()); } // Process the GC mark stack in the exclusive mode. No need to take the lock. @@ -1481,7 +1485,7 @@ size_t ConcurrentCopying::ProcessThreadLocalMarkStacks(bool disable_weak_ref_acc size_t count = 0; std::vector*> mark_stacks; { - MutexLock mu(Thread::Current(), mark_stack_lock_); + MutexLock mu(thread_running_gc_, mark_stack_lock_); // Make a copy of the mark stack vector. mark_stacks = revoked_mark_stacks_; revoked_mark_stacks_.clear(); @@ -1493,7 +1497,7 @@ size_t ConcurrentCopying::ProcessThreadLocalMarkStacks(bool disable_weak_ref_acc ++count; } { - MutexLock mu(Thread::Current(), mark_stack_lock_); + MutexLock mu(thread_running_gc_, mark_stack_lock_); if (pooled_mark_stacks_.size() >= kMarkStackPoolSize) { // The pool has enough. Delete it. delete mark_stack; @@ -1596,9 +1600,9 @@ class ConcurrentCopying::DisableWeakRefAccessCallback : public Closure { void ConcurrentCopying::SwitchToSharedMarkStackMode() { Thread* self = Thread::Current(); - CHECK(thread_running_gc_ != nullptr); - CHECK_EQ(self, thread_running_gc_); - CHECK(self->GetThreadLocalMarkStack() == nullptr); + DCHECK(thread_running_gc_ != nullptr); + DCHECK(self == thread_running_gc_); + DCHECK(thread_running_gc_->GetThreadLocalMarkStack() == nullptr); MarkStackMode before_mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed); CHECK_EQ(static_cast(before_mark_stack_mode), static_cast(kMarkStackModeThreadLocal)); @@ -1614,9 +1618,9 @@ void ConcurrentCopying::SwitchToSharedMarkStackMode() { void ConcurrentCopying::SwitchToGcExclusiveMarkStackMode() { Thread* self = Thread::Current(); - CHECK(thread_running_gc_ != nullptr); - CHECK_EQ(self, thread_running_gc_); - CHECK(self->GetThreadLocalMarkStack() == nullptr); + DCHECK(thread_running_gc_ != nullptr); + DCHECK(self == thread_running_gc_); + DCHECK(thread_running_gc_->GetThreadLocalMarkStack() == nullptr); MarkStackMode before_mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed); CHECK_EQ(static_cast(before_mark_stack_mode), static_cast(kMarkStackModeShared)); @@ -1629,14 +1633,14 @@ void ConcurrentCopying::SwitchToGcExclusiveMarkStackMode() { void ConcurrentCopying::CheckEmptyMarkStack() { Thread* self = Thread::Current(); - CHECK(thread_running_gc_ != nullptr); - CHECK_EQ(self, thread_running_gc_); - CHECK(self->GetThreadLocalMarkStack() == nullptr); + DCHECK(thread_running_gc_ != nullptr); + DCHECK(self == thread_running_gc_); + DCHECK(thread_running_gc_->GetThreadLocalMarkStack() == nullptr); MarkStackMode mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed); if (mark_stack_mode == kMarkStackModeThreadLocal) { // Thread-local mark stack mode. RevokeThreadLocalMarkStacks(false, nullptr); - MutexLock mu(Thread::Current(), mark_stack_lock_); + MutexLock mu(thread_running_gc_, mark_stack_lock_); if (!revoked_mark_stacks_.empty()) { for (accounting::AtomicStack* mark_stack : revoked_mark_stacks_) { while (!mark_stack->IsEmpty()) { @@ -1655,7 +1659,7 @@ void ConcurrentCopying::CheckEmptyMarkStack() { } } else { // Shared, GC-exclusive, or off. - MutexLock mu(Thread::Current(), mark_stack_lock_); + MutexLock mu(thread_running_gc_, mark_stack_lock_); CHECK(gc_mark_stack_->IsEmpty()); CHECK(revoked_mark_stacks_.empty()); } @@ -1755,9 +1759,9 @@ void ConcurrentCopying::ReclaimPhase() { const uint64_t from_objects = region_space_->GetObjectsAllocatedInFromSpace(); const uint64_t unevac_from_bytes = region_space_->GetBytesAllocatedInUnevacFromSpace(); const uint64_t unevac_from_objects = region_space_->GetObjectsAllocatedInUnevacFromSpace(); - uint64_t to_bytes = bytes_moved_.load(std::memory_order_seq_cst); + uint64_t to_bytes = bytes_moved_.load(std::memory_order_seq_cst) + bytes_moved_gc_thread_; cumulative_bytes_moved_.fetch_add(to_bytes, std::memory_order_relaxed); - uint64_t to_objects = objects_moved_.load(std::memory_order_seq_cst); + uint64_t to_objects = objects_moved_.load(std::memory_order_seq_cst) + objects_moved_gc_thread_; cumulative_objects_moved_.fetch_add(to_objects, std::memory_order_relaxed); if (kEnableFromSpaceAccountingCheck) { CHECK_EQ(from_space_num_objects_at_first_pause_, from_objects + unevac_from_objects); @@ -2075,8 +2079,8 @@ void ConcurrentCopying::AssertToSpaceInvariantInNonMovingSpace(mirror::Object* o // Used to scan ref fields of an object. class ConcurrentCopying::RefFieldsVisitor { public: - explicit RefFieldsVisitor(ConcurrentCopying* collector) - : collector_(collector) {} + explicit RefFieldsVisitor(ConcurrentCopying* collector, Thread* const thread) + : collector_(collector), thread_(thread) {} void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */) const ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) @@ -2101,11 +2105,12 @@ class ConcurrentCopying::RefFieldsVisitor { void VisitRoot(mirror::CompressedReference* root) const ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) { - collector_->MarkRoot(root); + collector_->MarkRoot(thread_, root); } private: ConcurrentCopying* const collector_; + Thread* const thread_; }; inline void ConcurrentCopying::Scan(mirror::Object* to_ref) { @@ -2117,12 +2122,12 @@ inline void ConcurrentCopying::Scan(mirror::Object* to_ref) { } DCHECK(!region_space_->IsInFromSpace(to_ref)); DCHECK_EQ(Thread::Current(), thread_running_gc_); - RefFieldsVisitor visitor(this); + RefFieldsVisitor visitor(this, thread_running_gc_); // Disable the read barrier for a performance reason. to_ref->VisitReferences( visitor, visitor); if (kDisallowReadBarrierDuringScan && !Runtime::Current()->IsActiveTransaction()) { - Thread::Current()->ModifyDebugDisallowReadBarrier(-1); + thread_running_gc_->ModifyDebugDisallowReadBarrier(-1); } } @@ -2131,6 +2136,7 @@ inline void ConcurrentCopying::Process(mirror::Object* obj, MemberOffset offset) mirror::Object* ref = obj->GetFieldObject< mirror::Object, kVerifyNone, kWithoutReadBarrier, false>(offset); mirror::Object* to_ref = Mark( + thread_running_gc_, ref, /*holder*/ obj, offset); @@ -2156,10 +2162,11 @@ inline void ConcurrentCopying::Process(mirror::Object* obj, MemberOffset offset) // Process some roots. inline void ConcurrentCopying::VisitRoots( mirror::Object*** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED) { + Thread* const self = Thread::Current(); for (size_t i = 0; i < count; ++i) { mirror::Object** root = roots[i]; mirror::Object* ref = *root; - mirror::Object* to_ref = Mark(ref); + mirror::Object* to_ref = Mark(self, ref); if (to_ref == ref) { continue; } @@ -2176,10 +2183,11 @@ inline void ConcurrentCopying::VisitRoots( } template -inline void ConcurrentCopying::MarkRoot(mirror::CompressedReference* root) { +inline void ConcurrentCopying::MarkRoot(Thread* const self, + mirror::CompressedReference* root) { DCHECK(!root->IsNull()); mirror::Object* const ref = root->AsMirrorPtr(); - mirror::Object* to_ref = Mark(ref); + mirror::Object* to_ref = Mark(self, ref); if (to_ref != ref) { auto* addr = reinterpret_cast>*>(root); auto expected_ref = mirror::CompressedReference::FromMirrorPtr(ref); @@ -2197,11 +2205,12 @@ inline void ConcurrentCopying::MarkRoot(mirror::CompressedReference** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED) { + Thread* const self = Thread::Current(); for (size_t i = 0; i < count; ++i) { mirror::CompressedReference* const root = roots[i]; if (!root->IsNull()) { // kGrayImmuneObject is true because this is used for the thread flip. - MarkRoot(root); + MarkRoot(self, root); } } } @@ -2235,7 +2244,9 @@ class ConcurrentCopying::ScopedGcGraysImmuneObjects { // Fill the given memory block with a dummy object. Used to fill in a // copy of objects that was lost in race. -void ConcurrentCopying::FillWithDummyObject(mirror::Object* dummy_obj, size_t byte_size) { +void ConcurrentCopying::FillWithDummyObject(Thread* const self, + mirror::Object* dummy_obj, + size_t byte_size) { // GC doesn't gray immune objects while scanning immune objects. But we need to trigger the read // barriers here because we need the updated reference to the int array class, etc. Temporary set // gc_grays_immune_objects_ to true so that we won't cause a DCHECK failure in MarkImmuneSpace(). @@ -2245,7 +2256,7 @@ void ConcurrentCopying::FillWithDummyObject(mirror::Object* dummy_obj, size_t by // Avoid going through read barrier for since kDisallowReadBarrierDuringScan may be enabled. // Explicitly mark to make sure to get an object in the to-space. mirror::Class* int_array_class = down_cast( - Mark(GetClassRoot().Ptr())); + Mark(self, GetClassRoot().Ptr())); CHECK(int_array_class != nullptr); if (ReadBarrier::kEnableToSpaceInvariantChecks) { AssertToSpaceInvariant(nullptr, MemberOffset(0), int_array_class); @@ -2279,10 +2290,9 @@ void ConcurrentCopying::FillWithDummyObject(mirror::Object* dummy_obj, size_t by } // Reuse the memory blocks that were copy of objects that were lost in race. -mirror::Object* ConcurrentCopying::AllocateInSkippedBlock(size_t alloc_size) { +mirror::Object* ConcurrentCopying::AllocateInSkippedBlock(Thread* const self, size_t alloc_size) { // Try to reuse the blocks that were unused due to CAS failures. CHECK_ALIGNED(alloc_size, space::RegionSpace::kAlignment); - Thread* self = Thread::Current(); size_t min_object_size = RoundUp(sizeof(mirror::Object), space::RegionSpace::kAlignment); size_t byte_size; uint8_t* addr; @@ -2326,7 +2336,8 @@ mirror::Object* ConcurrentCopying::AllocateInSkippedBlock(size_t alloc_size) { // FillWithDummyObject may mark an object, avoid holding skipped_blocks_lock_ to prevent lock // violation and possible deadlock. The deadlock case is a recursive case: // FillWithDummyObject -> Mark(IntArray.class) -> Copy -> AllocateInSkippedBlock. - FillWithDummyObject(reinterpret_cast(addr + alloc_size), + FillWithDummyObject(self, + reinterpret_cast(addr + alloc_size), byte_size - alloc_size); CHECK(region_space_->IsInToSpace(reinterpret_cast(addr + alloc_size))); { @@ -2337,7 +2348,8 @@ mirror::Object* ConcurrentCopying::AllocateInSkippedBlock(size_t alloc_size) { return reinterpret_cast(addr); } -mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, +mirror::Object* ConcurrentCopying::Copy(Thread* const self, + mirror::Object* from_ref, mirror::Object* holder, MemberOffset offset) { DCHECK(region_space_->IsInFromSpace(from_ref)); @@ -2366,7 +2378,7 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, DCHECK_EQ(region_space_alloc_size, region_space_bytes_allocated); } else { // Failed to allocate in the region space. Try the skipped blocks. - to_ref = AllocateInSkippedBlock(region_space_alloc_size); + to_ref = AllocateInSkippedBlock(self, region_space_alloc_size); if (to_ref != nullptr) { // Succeeded to allocate in a skipped block. if (heap_->use_tlab_) { @@ -2386,7 +2398,7 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, << " skipped_objects=" << to_space_objects_skipped_.load(std::memory_order_seq_cst); } - to_ref = heap_->non_moving_space_->Alloc(Thread::Current(), obj_size, + to_ref = heap_->non_moving_space_->Alloc(self, obj_size, &non_moving_space_bytes_allocated, nullptr, &dummy); if (UNLIKELY(to_ref == nullptr)) { LOG(FATAL_WITHOUT_ABORT) << "Fall-back non-moving space allocation failed for a " @@ -2427,7 +2439,7 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, // the forwarding pointer first. Make the lost copy (to_ref) // look like a valid but dead (dummy) object and keep it for // future reuse. - FillWithDummyObject(to_ref, bytes_allocated); + FillWithDummyObject(self, to_ref, bytes_allocated); if (!fall_back_to_non_moving) { DCHECK(region_space_->IsInToSpace(to_ref)); if (bytes_allocated > space::RegionSpace::kRegionSize) { @@ -2438,7 +2450,7 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, heap_->num_bytes_allocated_.fetch_add(bytes_allocated, std::memory_order_seq_cst); to_space_bytes_skipped_.fetch_add(bytes_allocated, std::memory_order_seq_cst); to_space_objects_skipped_.fetch_add(1, std::memory_order_seq_cst); - MutexLock mu(Thread::Current(), skipped_blocks_lock_); + MutexLock mu(self, skipped_blocks_lock_); skipped_blocks_map_.insert(std::make_pair(bytes_allocated, reinterpret_cast(to_ref))); } @@ -2450,7 +2462,7 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, heap_mark_bitmap_->GetContinuousSpaceBitmap(to_ref); CHECK(mark_bitmap != nullptr); CHECK(mark_bitmap->Clear(to_ref)); - heap_->non_moving_space_->Free(Thread::Current(), to_ref); + heap_->non_moving_space_->Free(self, to_ref); } // Get the winner's forward ptr. @@ -2481,8 +2493,15 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, bool success = from_ref->CasLockWordWeakRelaxed(old_lock_word, new_lock_word); if (LIKELY(success)) { // The CAS succeeded. - objects_moved_.fetch_add(1, std::memory_order_relaxed); - bytes_moved_.fetch_add(region_space_alloc_size, std::memory_order_relaxed); + DCHECK(thread_running_gc_ != nullptr); + if (LIKELY(self == thread_running_gc_)) { + objects_moved_gc_thread_ += 1; + bytes_moved_gc_thread_ += region_space_alloc_size; + } else { + objects_moved_.fetch_add(1, std::memory_order_relaxed); + bytes_moved_.fetch_add(region_space_alloc_size, std::memory_order_relaxed); + } + if (LIKELY(!fall_back_to_non_moving)) { DCHECK(region_space_->IsInToSpace(to_ref)); } else { @@ -2494,7 +2513,7 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, } DCHECK(GetFwdPtr(from_ref) == to_ref); CHECK_NE(to_ref->GetLockWord(false).GetState(), LockWord::kForwardingAddress); - PushOntoMarkStack(to_ref); + PushOntoMarkStack(self, to_ref); return to_ref; } else { // The CAS failed. It may have lost the race or may have failed @@ -2573,7 +2592,8 @@ bool ConcurrentCopying::IsOnAllocStack(mirror::Object* ref) { return alloc_stack->Contains(ref); } -mirror::Object* ConcurrentCopying::MarkNonMoving(mirror::Object* ref, +mirror::Object* ConcurrentCopying::MarkNonMoving(Thread* const self, + mirror::Object* ref, mirror::Object* holder, MemberOffset offset) { // ref is in a non-moving space (from_ref == to_ref). @@ -2636,20 +2656,20 @@ mirror::Object* ConcurrentCopying::MarkNonMoving(mirror::Object* ref, // Already marked. if (kUseBakerReadBarrier && cas_success && ref->GetReadBarrierState() == ReadBarrier::GrayState()) { - PushOntoFalseGrayStack(ref); + PushOntoFalseGrayStack(self, ref); } } else if (is_los && los_bitmap->AtomicTestAndSet(ref)) { // Already marked in LOS. if (kUseBakerReadBarrier && cas_success && ref->GetReadBarrierState() == ReadBarrier::GrayState()) { - PushOntoFalseGrayStack(ref); + PushOntoFalseGrayStack(self, ref); } } else { // Newly marked. if (kUseBakerReadBarrier) { DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::GrayState()); } - PushOntoMarkStack(ref); + PushOntoMarkStack(self, ref); } } } @@ -2742,7 +2762,7 @@ bool ConcurrentCopying::IsNullOrMarkedHeapReference(mirror::HeapReference klass, @@ -2763,15 +2783,16 @@ void ConcurrentCopying::RevokeAllThreadLocalBuffers() { region_space_->RevokeAllThreadLocalBuffers(); } -mirror::Object* ConcurrentCopying::MarkFromReadBarrierWithMeasurements(mirror::Object* from_ref) { - if (Thread::Current() != thread_running_gc_) { +mirror::Object* ConcurrentCopying::MarkFromReadBarrierWithMeasurements(Thread* const self, + mirror::Object* from_ref) { + if (self != thread_running_gc_) { rb_slow_path_count_.fetch_add(1u, std::memory_order_relaxed); } else { rb_slow_path_count_gc_.fetch_add(1u, std::memory_order_relaxed); } ScopedTrace tr(__FUNCTION__); const uint64_t start_time = measure_read_barrier_slow_path_ ? NanoTime() : 0u; - mirror::Object* ret = Mark(from_ref); + mirror::Object* ret = Mark(self, from_ref); if (measure_read_barrier_slow_path_) { rb_slow_path_ns_.fetch_add(NanoTime() - start_time, std::memory_order_relaxed); } diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h index a00dbb58d0..f1e7e2fd23 100644 --- a/runtime/gc/collector/concurrent_copying.h +++ b/runtime/gc/collector/concurrent_copying.h @@ -112,7 +112,8 @@ class ConcurrentCopying : public GarbageCollector { } template // Mark object `from_ref`, copying it to the to-space if needed. - ALWAYS_INLINE mirror::Object* Mark(mirror::Object* from_ref, + ALWAYS_INLINE mirror::Object* Mark(Thread* const self, + mirror::Object* from_ref, mirror::Object* holder = nullptr, MemberOffset offset = MemberOffset(0)) REQUIRES_SHARED(Locks::mutator_lock_) @@ -144,9 +145,11 @@ class ConcurrentCopying : public GarbageCollector { REQUIRES_SHARED(Locks::mutator_lock_); private: - void PushOntoMarkStack(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) + void PushOntoMarkStack(Thread* const self, mirror::Object* obj) + REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_); - mirror::Object* Copy(mirror::Object* from_ref, + mirror::Object* Copy(Thread* const self, + mirror::Object* from_ref, mirror::Object* holder, MemberOffset offset) REQUIRES_SHARED(Locks::mutator_lock_) @@ -162,7 +165,7 @@ class ConcurrentCopying : public GarbageCollector { OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_); template - void MarkRoot(mirror::CompressedReference* root) + void MarkRoot(Thread* const self, mirror::CompressedReference* root) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_); virtual void VisitRoots(mirror::CompressedReference** roots, size_t count, @@ -220,10 +223,10 @@ class ConcurrentCopying : public GarbageCollector { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_); void MarkZygoteLargeObjects() REQUIRES_SHARED(Locks::mutator_lock_); - void FillWithDummyObject(mirror::Object* dummy_obj, size_t byte_size) + void FillWithDummyObject(Thread* const self, mirror::Object* dummy_obj, size_t byte_size) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - mirror::Object* AllocateInSkippedBlock(size_t alloc_size) + mirror::Object* AllocateInSkippedBlock(Thread* const self, size_t alloc_size) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_) REQUIRES_SHARED(Locks::mutator_lock_); void CheckEmptyMarkStack() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_); @@ -253,25 +256,30 @@ class ConcurrentCopying : public GarbageCollector { void DisableMarking() REQUIRES_SHARED(Locks::mutator_lock_); void IssueDisableMarkingCheckpoint() REQUIRES_SHARED(Locks::mutator_lock_); void ExpandGcMarkStack() REQUIRES_SHARED(Locks::mutator_lock_); - mirror::Object* MarkNonMoving(mirror::Object* from_ref, + mirror::Object* MarkNonMoving(Thread* const self, + mirror::Object* from_ref, mirror::Object* holder = nullptr, MemberOffset offset = MemberOffset(0)) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_); - ALWAYS_INLINE mirror::Object* MarkUnevacFromSpaceRegion(mirror::Object* from_ref, + ALWAYS_INLINE mirror::Object* MarkUnevacFromSpaceRegion(Thread* const self, + mirror::Object* from_ref, accounting::SpaceBitmap* bitmap) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_); template - ALWAYS_INLINE mirror::Object* MarkImmuneSpace(mirror::Object* from_ref) + ALWAYS_INLINE mirror::Object* MarkImmuneSpace(Thread* const self, + mirror::Object* from_ref) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!immune_gray_stack_lock_); - void PushOntoFalseGrayStack(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) + void PushOntoFalseGrayStack(Thread* const self, mirror::Object* obj) + REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_); void ProcessFalseGrayStack() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_); void ScanImmuneObject(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_); - mirror::Object* MarkFromReadBarrierWithMeasurements(mirror::Object* from_ref) + mirror::Object* MarkFromReadBarrierWithMeasurements(Thread* const self, + mirror::Object* from_ref) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_); void DumpPerformanceInfo(std::ostream& os) OVERRIDE REQUIRES(!rb_slow_path_histogram_lock_); @@ -330,8 +338,12 @@ class ConcurrentCopying : public GarbageCollector { bool weak_ref_access_enabled_ GUARDED_BY(Locks::thread_list_lock_); // How many objects and bytes we moved. Used for accounting. - Atomic bytes_moved_; - Atomic objects_moved_; + // GC thread moves many more objects than mutators. + // Therefore, we separate the two to avoid CAS. + Atomic bytes_moved_; // Used by mutators + Atomic objects_moved_; // Used by mutators + size_t bytes_moved_gc_thread_; // Used by GC + size_t objects_moved_gc_thread_; // Used by GC Atomic cumulative_bytes_moved_; Atomic cumulative_objects_moved_; -- GitLab From 41349055f9f7bbe86a93ef59343525731a0b2e1a Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 11 Jun 2018 13:28:17 -0700 Subject: [PATCH 563/749] Make ti-stress work on RI In some cases the RI will send JVMTI agents events with some of the required reference arguments null. This could cause the ti-stress agent to crash. Fix this by explicitly null checking before calling DeleteLocalRef. Test: java -agentpath:$ANDROID_HOST_OUT/nativetest64/libtistress.so=jvmti-stress,trace \ HelloWorld Change-Id: I29f4144e2e773253b76f5fa8b38d57ae1deab03b --- test/ti-stress/stress.cc | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc index 0eba7426c0..bd320c66cc 100644 --- a/test/ti-stress/stress.cc +++ b/test/ti-stress/stress.cc @@ -56,6 +56,12 @@ struct StressData { bool step_stress; }; +static void DeleteLocalRef(JNIEnv* env, jobject obj) { + if (obj != nullptr) { + env->DeleteLocalRef(obj); + } +} + static bool DoExtractClassFromData(jvmtiEnv* env, const std::string& descriptor, jint in_len, @@ -130,8 +136,8 @@ class ScopedThreadInfo { if (free_name_) { jvmtienv_->Deallocate(reinterpret_cast(info_.name)); } - env_->DeleteLocalRef(info_.thread_group); - env_->DeleteLocalRef(info_.context_class_loader); + DeleteLocalRef(env_, info_.thread_group); + DeleteLocalRef(env_, info_.context_class_loader); } const char* GetName() const { @@ -227,7 +233,7 @@ class ScopedMethodInfo { first_line_(-1) {} ~ScopedMethodInfo() { - env_->DeleteLocalRef(declaring_class_); + DeleteLocalRef(env_, declaring_class_); jvmtienv_->Deallocate(reinterpret_cast(name_)); jvmtienv_->Deallocate(reinterpret_cast(signature_)); jvmtienv_->Deallocate(reinterpret_cast(generic_)); @@ -389,7 +395,7 @@ static std::string GetName(jvmtiEnv* jvmtienv, JNIEnv* jnienv, jobject obj) { char *cname, *cgen; if (jvmtienv->GetClassSignature(klass, &cname, &cgen) != JVMTI_ERROR_NONE) { LOG(ERROR) << "Unable to get class name!"; - jnienv->DeleteLocalRef(klass); + DeleteLocalRef(jnienv, klass); return ""; } std::string name(cname); @@ -407,7 +413,7 @@ static std::string GetName(jvmtiEnv* jvmtienv, JNIEnv* jnienv, jobject obj) { } jvmtienv->Deallocate(reinterpret_cast(cname)); jvmtienv->Deallocate(reinterpret_cast(cgen)); - jnienv->DeleteLocalRef(klass); + DeleteLocalRef(jnienv, klass); return name; } @@ -468,7 +474,7 @@ void JNICALL FieldAccessHook(jvmtiEnv* jvmtienv, << "type \"" << obj_class_info.GetName() << "\" in method \"" << method_info << "\" at location 0x" << std::hex << location << ". Thread is \"" << info.GetName() << "\"."; - env->DeleteLocalRef(oklass); + DeleteLocalRef(env, oklass); } static std::string PrintJValue(jvmtiEnv* jvmtienv, JNIEnv* env, char type, jvalue new_value) { @@ -486,7 +492,7 @@ static std::string PrintJValue(jvmtiEnv* jvmtienv, JNIEnv* env, char type, jvalu } else { oss << "of type \"" << nv_class_info.GetName() << "\""; } - env->DeleteLocalRef(nv_klass); + DeleteLocalRef(env, nv_klass); } break; } @@ -539,7 +545,7 @@ void JNICALL FieldModificationHook(jvmtiEnv* jvmtienv, << "\" at location 0x" << std::hex << location << std::dec << ". New value is " << PrintJValue(jvmtienv, env, type, new_value) << ". Thread is \"" << info.GetName() << "\"."; - env->DeleteLocalRef(oklass); + DeleteLocalRef(env, oklass); } void JNICALL MethodExitHook(jvmtiEnv* jvmtienv, JNIEnv* env, @@ -714,7 +720,7 @@ static void JNICALL PerformFinalSetupVMInit(jvmtiEnv *jvmti_env, } else { // GetMethodID is spec'd to cause the class to be initialized. jni_env->GetMethodID(klass, "hashCode", "()I"); - jni_env->DeleteLocalRef(klass); + DeleteLocalRef(jni_env, klass); data->vm_class_loader_initialized = true; } } @@ -761,7 +767,7 @@ static bool WatchAllFields(JavaVM* vm, jvmtiEnv* jvmti) { return false; } jvmti->Deallocate(reinterpret_cast(fields)); - jni->DeleteLocalRef(k); + DeleteLocalRef(jni, k); } jvmti->Deallocate(reinterpret_cast(klasses)); return true; -- GitLab From 2682918b0168d3f47294676547efc0ee1c7a7249 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 11 Jun 2018 11:36:24 -0700 Subject: [PATCH 564/749] Add support for a --run-test-option to testrunner. Sometimes it is useful to send specific run-test options to every test. This adds a --run-test-option argument to testrunner that will let one pass any run-test options down to run-test with no modification. Test: ./test/testrunner/testrunner.py \ --host \ --run-test-option='--with-agent libtifast.so=log,MethodEntry,MethodExit' \ -t 001-HelloWorld Change-Id: Ice8e5b83fce4389b7461f5284c093dd144969640 --- test/testrunner/testrunner.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 044e8dc12a..067e678ca1 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -52,6 +52,7 @@ import json import multiprocessing import os import re +import shlex import shutil import subprocess import sys @@ -116,6 +117,7 @@ build = False gdb = False gdb_arg = '' runtime_option = '' +run_test_option = [] stop_testrunner = False dex2oat_jobs = -1 # -1 corresponds to default threads for dex2oat run_all_configs = False @@ -326,6 +328,8 @@ def run_tests(tests): if gdb_arg: options_all += ' --gdb-arg ' + gdb_arg + options_all += ' '.join(run_test_option) + if runtime_option: for opt in runtime_option: options_all += ' --runtime-option ' + opt @@ -905,6 +909,7 @@ def parse_option(): global gdb global gdb_arg global runtime_option + global run_test_option global timeout global dex2oat_jobs global run_all_configs @@ -934,6 +939,12 @@ def parse_option(): global_group.set_defaults(build = env.ART_TEST_RUN_TEST_BUILD) global_group.add_argument('--gdb', action='store_true', dest='gdb') global_group.add_argument('--gdb-arg', dest='gdb_arg') + global_group.add_argument('--run-test-option', action='append', dest='run_test_option', + default=[], + help="""Pass an option, unaltered, to the run-test script. + This should be enclosed in single-quotes to allow for spaces. The option + will be split using shlex.split() prior to invoking run-test. + Example \"--run-test-option='--with-agent libtifast.so=MethodExit'\"""") global_group.add_argument('--runtime-option', action='append', dest='runtime_option', help="""Pass an option to the runtime. Runtime options starting with a '-' must be separated by a '=', for @@ -982,6 +993,8 @@ def parse_option(): if options['gdb_arg']: gdb_arg = options['gdb_arg'] runtime_option = options['runtime_option']; + run_test_option = sum(map(shlex.split, options['run_test_option']), []) + timeout = options['timeout'] if options['dex2oat_jobs']: dex2oat_jobs = options['dex2oat_jobs'] -- GitLab From 4914d157d24f0ff2e0c0963e0441222138f29081 Mon Sep 17 00:00:00 2001 From: Tamas Kenez Date: Tue, 12 Jun 2018 10:40:19 +0200 Subject: [PATCH 565/749] ART-tests: remove DX-dependency from 566-checker-signum One test case converts a boolean to 0/1 with a conditional. This construct is optimized away by D8. This CL moves this test case to smali and enables D8. Test: art/test.py -b -r --host -t 566-checker-signum Tested locally with --target/--gcstress Bug: 65168732 Change-Id: Ic1f901dea486692174d38281413fe06539a41a34 --- test/566-checker-signum/build | 20 -- test/566-checker-signum/smali/Main2.smali | 83 ++++++++ test/566-checker-signum/src-art/Main.java | 196 +++++++++++++++++++ test/566-checker-signum/src/Main.java | 219 +--------------------- 4 files changed, 282 insertions(+), 236 deletions(-) delete mode 100644 test/566-checker-signum/build create mode 100644 test/566-checker-signum/smali/Main2.smali create mode 100644 test/566-checker-signum/src-art/Main.java diff --git a/test/566-checker-signum/build b/test/566-checker-signum/build deleted file mode 100644 index 10ffcc537d..0000000000 --- a/test/566-checker-signum/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/566-checker-signum/smali/Main2.smali b/test/566-checker-signum/smali/Main2.smali new file mode 100644 index 0000000000..d99ad8662b --- /dev/null +++ b/test/566-checker-signum/smali/Main2.smali @@ -0,0 +1,83 @@ +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class public LMain2; +.super Ljava/lang/Object; + +## CHECK-START: int Main2.signBoolean(boolean) intrinsics_recognition (after) +## CHECK-DAG: <> CurrentMethod +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> Phi [<>,<>] +## CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:IntegerSignum +## CHECK-DAG: Return [<>] + +## CHECK-START: int Main2.signBoolean(boolean) instruction_simplifier (after) +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> Phi [<>,<>] +## CHECK-DAG: <> Compare [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int Main2.signBoolean(boolean) instruction_simplifier (after) +## CHECK-NOT: InvokeStaticOrDirect + +## CHECK-START: int Main2.signBoolean(boolean) select_generator (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> Select [<>,<>,<>] +## CHECK-DAG: <> Compare [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int Main2.signBoolean(boolean) select_generator (after) +## CHECK-NOT: Phi + +## CHECK-START: int Main2.signBoolean(boolean) instruction_simplifier$after_bce (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> Compare [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int Main2.signBoolean(boolean) instruction_simplifier$after_bce (after) +## CHECK-NOT: Select + +# Original java source: +# +# private static int signBoolean(boolean x) { +# return Integer.signum(x ? 1 : 0); +# } + +.method public static signBoolean(Z)I + .registers 2 + .param p0, "x" # Z + + .prologue + .line 58 + if-eqz p0, :cond_8 + + const/4 v0, 0x1 + + :goto_3 + invoke-static {v0}, Ljava/lang/Integer;->signum(I)I + + move-result v0 + + return v0 + + :cond_8 + const/4 v0, 0x0 + + goto :goto_3 +.end method diff --git a/test/566-checker-signum/src-art/Main.java b/test/566-checker-signum/src-art/Main.java new file mode 100644 index 0000000000..f1e1e1bf6e --- /dev/null +++ b/test/566-checker-signum/src-art/Main.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; + +public class Main { + + /// CHECK-START: int Main.signByte(byte) intrinsics_recognition (after) + /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:IntegerSignum + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.signByte(byte) instruction_simplifier (after) + /// CHECK-DAG: <> Compare + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.signByte(byte) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + + private static int signByte(byte x) { + return Integer.signum(x); + } + + /// CHECK-START: int Main.signShort(short) intrinsics_recognition (after) + /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:IntegerSignum + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.signShort(short) instruction_simplifier (after) + /// CHECK-DAG: <> Compare + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.signShort(short) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + + private static int signShort(short x) { + return Integer.signum(x); + } + + /// CHECK-START: int Main.signChar(char) intrinsics_recognition (after) + /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:IntegerSignum + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.signChar(char) instruction_simplifier (after) + /// CHECK-DAG: <> Compare + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.signChar(char) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + + private static int signChar(char x) { + return Integer.signum(x); + } + + /// CHECK-START: int Main.signInt(int) intrinsics_recognition (after) + /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:IntegerSignum + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.signInt(int) instruction_simplifier (after) + /// CHECK-DAG: <> Compare + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.signInt(int) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + + private static int signInt(int x) { + return Integer.signum(x); + } + + /// CHECK-START: int Main.signLong(long) intrinsics_recognition (after) + /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:LongSignum + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.signLong(long) instruction_simplifier (after) + /// CHECK-DAG: <> Compare + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.signLong(long) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + + private static int signLong(long x) { + return Long.signum(x); + } + + + public static void testSignBoolean() throws Exception { + Method signBoolean = Class.forName("Main2").getMethod("signBoolean", boolean.class); + expectEquals(0, (int)signBoolean.invoke(null, false)); + expectEquals(1, (int)signBoolean.invoke(null, true)); + } + + public static void testSignByte() { + expectEquals(-1, signByte((byte)Byte.MIN_VALUE)); + expectEquals(-1, signByte((byte)-64)); + expectEquals(-1, signByte((byte)-1)); + expectEquals(0, signByte((byte)0)); + expectEquals(1, signByte((byte)1)); + expectEquals(1, signByte((byte)64)); + expectEquals(1, signByte((byte)Byte.MAX_VALUE)); + } + + public static void testSignShort() { + expectEquals(-1, signShort((short)Short.MIN_VALUE)); + expectEquals(-1, signShort((short)-12345)); + expectEquals(-1, signShort((short)-1)); + expectEquals(0, signShort((short)0)); + expectEquals(1, signShort((short)1)); + expectEquals(1, signShort((short)12345)); + expectEquals(1, signShort((short)Short.MAX_VALUE)); + } + + public static void testSignChar() { + expectEquals(0, signChar((char)0)); + expectEquals(1, signChar((char)1)); + expectEquals(1, signChar((char)12345)); + expectEquals(1, signChar((char)Character.MAX_VALUE)); + } + + public static void testSignInt() { + expectEquals(-1, signInt(Integer.MIN_VALUE)); + expectEquals(-1, signInt(-12345)); + expectEquals(-1, signInt(-1)); + expectEquals(0, signInt(0)); + expectEquals(1, signInt(1)); + expectEquals(1, signInt(12345)); + expectEquals(1, signInt(Integer.MAX_VALUE)); + + for (int i = -11; i <= 11; i++) { + int expected = 0; + if (i < 0) expected = -1; + else if (i > 0) expected = 1; + expectEquals(expected, signInt(i)); + } + } + + public static void testSignLong() { + expectEquals(-1, signLong(Long.MIN_VALUE)); + expectEquals(-1, signLong(-12345L)); + expectEquals(-1, signLong(-1L)); + expectEquals(0, signLong(0L)); + expectEquals(1, signLong(1L)); + expectEquals(1, signLong(12345L)); + expectEquals(1, signLong(Long.MAX_VALUE)); + + expectEquals(-1, signLong(0x800000007FFFFFFFL)); + expectEquals(-1, signLong(0x80000000FFFFFFFFL)); + expectEquals(1, signLong(0x000000007FFFFFFFL)); + expectEquals(1, signLong(0x00000000FFFFFFFFL)); + expectEquals(1, signLong(0x7FFFFFFF7FFFFFFFL)); + expectEquals(1, signLong(0x7FFFFFFFFFFFFFFFL)); + + for (long i = -11L; i <= 11L; i++) { + int expected = 0; + if (i < 0) expected = -1; + else if (i > 0) expected = 1; + expectEquals(expected, signLong(i)); + } + + for (long i = Long.MIN_VALUE; i <= Long.MIN_VALUE + 11L; i++) { + expectEquals(-1, signLong(i)); + } + + for (long i = Long.MAX_VALUE; i >= Long.MAX_VALUE - 11L; i--) { + expectEquals(1, signLong(i)); + } + } + + + public static void main(String args[]) throws Exception { + testSignBoolean(); + testSignByte(); + testSignShort(); + testSignChar(); + testSignInt(); + testSignLong(); + + 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/566-checker-signum/src/Main.java b/test/566-checker-signum/src/Main.java index 7fc9e84055..fa8e5cd1fe 100644 --- a/test/566-checker-signum/src/Main.java +++ b/test/566-checker-signum/src/Main.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * 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. @@ -14,222 +14,9 @@ * limitations under the License. */ +// This file is just for running on the RI as the test is ART specific. public class Main { - - /// CHECK-START: int Main.signBoolean(boolean) intrinsics_recognition (after) - /// CHECK-DAG: <> CurrentMethod - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> Phi [<>,<>] - /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:IntegerSignum - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.signBoolean(boolean) instruction_simplifier (after) - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> Phi [<>,<>] - /// CHECK-DAG: <> Compare [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.signBoolean(boolean) instruction_simplifier (after) - /// CHECK-NOT: InvokeStaticOrDirect - - /// CHECK-START: int Main.signBoolean(boolean) select_generator (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: <> Compare [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.signBoolean(boolean) select_generator (after) - /// CHECK-NOT: Phi - - /// CHECK-START: int Main.signBoolean(boolean) instruction_simplifier$after_bce (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> Compare [<>,<>] - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.signBoolean(boolean) instruction_simplifier$after_bce (after) - /// CHECK-NOT: Select - - private static int signBoolean(boolean x) { - return Integer.signum(x ? 1 : 0); - } - - /// CHECK-START: int Main.signByte(byte) intrinsics_recognition (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:IntegerSignum - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.signByte(byte) instruction_simplifier (after) - /// CHECK-DAG: <> Compare - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.signByte(byte) instruction_simplifier (after) - /// CHECK-NOT: InvokeStaticOrDirect - - private static int signByte(byte x) { - return Integer.signum(x); - } - - /// CHECK-START: int Main.signShort(short) intrinsics_recognition (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:IntegerSignum - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.signShort(short) instruction_simplifier (after) - /// CHECK-DAG: <> Compare - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.signShort(short) instruction_simplifier (after) - /// CHECK-NOT: InvokeStaticOrDirect - - private static int signShort(short x) { - return Integer.signum(x); - } - - /// CHECK-START: int Main.signChar(char) intrinsics_recognition (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:IntegerSignum - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.signChar(char) instruction_simplifier (after) - /// CHECK-DAG: <> Compare - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.signChar(char) instruction_simplifier (after) - /// CHECK-NOT: InvokeStaticOrDirect - - private static int signChar(char x) { - return Integer.signum(x); - } - - /// CHECK-START: int Main.signInt(int) intrinsics_recognition (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:IntegerSignum - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.signInt(int) instruction_simplifier (after) - /// CHECK-DAG: <> Compare - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.signInt(int) instruction_simplifier (after) - /// CHECK-NOT: InvokeStaticOrDirect - - private static int signInt(int x) { - return Integer.signum(x); - } - - /// CHECK-START: int Main.signLong(long) intrinsics_recognition (after) - /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:LongSignum - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.signLong(long) instruction_simplifier (after) - /// CHECK-DAG: <> Compare - /// CHECK-DAG: Return [<>] - - /// CHECK-START: int Main.signLong(long) instruction_simplifier (after) - /// CHECK-NOT: InvokeStaticOrDirect - - private static int signLong(long x) { - return Long.signum(x); - } - - - public static void testSignBoolean() { - expectEquals(0, signBoolean(false)); - expectEquals(1, signBoolean(true)); - } - - public static void testSignByte() { - expectEquals(-1, signByte((byte)Byte.MIN_VALUE)); - expectEquals(-1, signByte((byte)-64)); - expectEquals(-1, signByte((byte)-1)); - expectEquals(0, signByte((byte)0)); - expectEquals(1, signByte((byte)1)); - expectEquals(1, signByte((byte)64)); - expectEquals(1, signByte((byte)Byte.MAX_VALUE)); - } - - public static void testSignShort() { - expectEquals(-1, signShort((short)Short.MIN_VALUE)); - expectEquals(-1, signShort((short)-12345)); - expectEquals(-1, signShort((short)-1)); - expectEquals(0, signShort((short)0)); - expectEquals(1, signShort((short)1)); - expectEquals(1, signShort((short)12345)); - expectEquals(1, signShort((short)Short.MAX_VALUE)); - } - - public static void testSignChar() { - expectEquals(0, signChar((char)0)); - expectEquals(1, signChar((char)1)); - expectEquals(1, signChar((char)12345)); - expectEquals(1, signChar((char)Character.MAX_VALUE)); - } - - public static void testSignInt() { - expectEquals(-1, signInt(Integer.MIN_VALUE)); - expectEquals(-1, signInt(-12345)); - expectEquals(-1, signInt(-1)); - expectEquals(0, signInt(0)); - expectEquals(1, signInt(1)); - expectEquals(1, signInt(12345)); - expectEquals(1, signInt(Integer.MAX_VALUE)); - - for (int i = -11; i <= 11; i++) { - int expected = 0; - if (i < 0) expected = -1; - else if (i > 0) expected = 1; - expectEquals(expected, signInt(i)); - } - } - - public static void testSignLong() { - expectEquals(-1, signLong(Long.MIN_VALUE)); - expectEquals(-1, signLong(-12345L)); - expectEquals(-1, signLong(-1L)); - expectEquals(0, signLong(0L)); - expectEquals(1, signLong(1L)); - expectEquals(1, signLong(12345L)); - expectEquals(1, signLong(Long.MAX_VALUE)); - - expectEquals(-1, signLong(0x800000007FFFFFFFL)); - expectEquals(-1, signLong(0x80000000FFFFFFFFL)); - expectEquals(1, signLong(0x000000007FFFFFFFL)); - expectEquals(1, signLong(0x00000000FFFFFFFFL)); - expectEquals(1, signLong(0x7FFFFFFF7FFFFFFFL)); - expectEquals(1, signLong(0x7FFFFFFFFFFFFFFFL)); - - for (long i = -11L; i <= 11L; i++) { - int expected = 0; - if (i < 0) expected = -1; - else if (i > 0) expected = 1; - expectEquals(expected, signLong(i)); - } - - for (long i = Long.MIN_VALUE; i <= Long.MIN_VALUE + 11L; i++) { - expectEquals(-1, signLong(i)); - } - - for (long i = Long.MAX_VALUE; i >= Long.MAX_VALUE - 11L; i--) { - expectEquals(1, signLong(i)); - } - } - - - public static void main(String args[]) { - testSignBoolean(); - testSignByte(); - testSignShort(); - testSignChar(); - testSignInt(); - testSignLong(); - + public static void main(String[] args) { System.out.println("passed"); } - - private static void expectEquals(int expected, int result) { - if (expected != result) { - throw new Error("Expected: " + expected + ", found: " + result); - } - } } -- GitLab From 8bdbd3726073dd6956b734fe7fbe46a811968510 Mon Sep 17 00:00:00 2001 From: Tamas Kenez Date: Tue, 12 Jun 2018 11:37:27 +0200 Subject: [PATCH 566/749] ART-tests: Remove DX-dependency from 570-checker-osr. This test has already been working with D8, this CL only enables D8. Test: art/test.py -r -b --host -t 570-checker-osr Tested locally with --target/--gcstress. Bug: 65168732 Change-Id: I3d8094f50cc765efb57b59a96aff972f9d04441f --- test/570-checker-osr/build | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 test/570-checker-osr/build diff --git a/test/570-checker-osr/build b/test/570-checker-osr/build deleted file mode 100644 index 10ffcc537d..0000000000 --- a/test/570-checker-osr/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" -- GitLab From f83f83dfe3c5168a3bf191723933be662785e108 Mon Sep 17 00:00:00 2001 From: Richard Uhler Date: Fri, 1 Jun 2018 13:17:08 +0100 Subject: [PATCH 567/749] Improve ahat dominators computation. Improve the space complexity of the ahat dominators computation algorithm to be linear in the size of the heap dump being processed. Performance of the algorithm is on par with the previous version of the algorithm, verified by running it on a collection of 150 or so Android heap dumps laying around from old memory investigations. For example: old new 0m1.161s --> 0m1.333s 0m2.800s --> 0m3.350s 0m2.805s --> 0m2.429s 0m4.161s --> 0m5.547s 0m4.553s --> 0m3.482s 0m5.714s --> 0m6.373s 0m6.254s --> 0m5.439s 0m11.615s --> 0m11.456s 0m14.377s --> 0m14.543s The new algorithm performs considerably better in those cases where the old algorithm has excessive memory requirements. For example: old new 0m11.135s --> 0m5.915s 0m11.596s --> 0m7.805s 0m31.886s --> 0m10.569s 1m10.972s --> 0m14.350s For the heap dump in b/79200800, the old algorithm would be killed after trying to use more than 64GB of memory. The new algorithm takes roughly 2m30s and 5GB of memory to process that same heap dump. Bug: 79200800 Test: m ahat-test Test: Open a non-trivial heap dump and compare retained sizes for the top 30 or so rooted instances between the old and new version of the algorithm. Change-Id: I8f939b35cf3867d3062863fd9dedeee487db9591 --- .../dominators/DominatorsComputation.java | 372 ++++++++++++------ .../test/com/android/ahat/DominatorsTest.java | 31 ++ 2 files changed, 288 insertions(+), 115 deletions(-) diff --git a/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java b/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java index d3fea4869a..6185deed04 100644 --- a/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java +++ b/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java @@ -17,9 +17,8 @@ package com.android.ahat.dominators; import java.util.ArrayDeque; -import java.util.ArrayList; +import java.util.Arrays; import java.util.Deque; -import java.util.List; import java.util.Queue; /** @@ -106,43 +105,133 @@ public class DominatorsComputation { // dominator of B. public long id; - // Upper bound on the id of this node's dominator. - // The true immediate dominator of this node must have id <= domid. - // This upper bound is slowly tightened as part of the dominators - // computation. - public long domid; + // The largest id of all nodes reachable from this node. + // If foo.id > this.maxReachableId, then foo is not reachable from this + // node. + public long maxReachableId; + + // The set of ids of nodes that have references to this node. + public IdSet inRefIds = new IdSet(); // The current candidate dominator for this node. - // Invariant: (domid < domS.id) implies this node is on the queue of - // nodes to be revisited. + // The true immediate dominator of this node must have id <= domS.id. public NodeS domS; - // A node with a reference to this node that is one step closer to the - // root than this node. - // Invariant: srcS.id < this.id - public NodeS srcS; - - // The largest id of the nodes we have seen so far on a path from the root - // to this node. Used to keep track of which nodes we have already seen - // and avoid processing them again. - public long seenid; - - // The set of nodes X reachable by 'this' on a path of nodes from the - // root with increasing ids (possibly excluding X) that this node does not - // dominate (this.id > X.domid). - // We can use a List instead of a Set for this because we guarentee based - // on seenid that we don't add the same node more than once to the list. - public List undom = new ArrayList(); + // The previous candidate dominator for this node. + // Invariant: + // * There are no nodes xS reachable from this node on a path of nodes + // with increasing ids (not counting xS.id) for which + // this.id > xS.domS.id > this.oldDomS.id. + // This ensures that when all nodes xS satisfy xS.domS == xS.oldDomS, we + // have found the true immediate dominator of each node. + // + // Note: We only use this field to tell if this node is scheduled to be + // revisited. We could replace it with a boolean to save space, but it + // probably doesn't save that much space and it's easier to explain the + // algorithm if we can refer to this field. + public NodeS oldDomS; + + // The set of nodes that this node is the candidate immediate dominator + // of. More precisely, the set of nodes xS such that xS.domS == this. + public NodeSet dominated = new NodeSet(); + + // The set of nodes that this node is the old candidate immediate + // dominator of that need to be revisited. Specifically, the set of nodes + // xS such that: + // xS.oldDomS == this && xS.oldDomS != xS.domS. + // + // The empty set is represented as null instead of an empty NodeSet to + // save memory. + // Invariant: + // If revisit != null, this node is on the global list of nodes to be + // revisited. + public NodeSet revisit = null; + } + + // A collection of node ids. + private static class IdSet { + private int size = 0; + private long[] ids = new long[4]; + + // Adds an id to the set. + public void add(long id) { + if (size == ids.length) { + ids = Arrays.copyOf(ids, size * 2); + } + ids[size++] = id; + } + + // Returns the most recent id added to the set. Behavior is undefined if + // the set is empty. + public long last() { + assert size != 0; + return ids[size - 1]; + } + + // Returns true if the set contains an id in the range [low, high] + // inclusive, false otherwise. + public boolean hasIdInRange(long low, long high) { + for (int i = 0; i < size; ++i) { + if (low <= ids[i] && ids[i] <= high) { + return true; + } + } + return false; + } } + // An unordered set of nodes data structure supporting efficient iteration + // over elements. The bulk of the time spent in the dominators algorithm is + // iterating over these sets. Using an array to store the set provides + // noticable performance improvements over ArrayList or a linked list. + private static class NodeSet { + public int size = 0; + public NodeS[] nodes = new NodeS[4]; + + public void add(NodeS nodeS) { + if (size == nodes.length) { + nodes = Arrays.copyOf(nodes, size * 2); + } + nodes[size++] = nodeS; + } + + public void remove(NodeS nodeS) { + for (int i = 0; i < size; ++i) { + if (nodes[i] == nodeS) { + remove(i); + break; + } + } + } + + public void remove(int index) { + nodes[index] = nodes[--size]; + nodes[size] = null; + } + } + + // A reference from a source node to a destination node to be processed + // during the initial depth-first traversal of nodes. + // + // Also used as a marker to indicate when the depth-first traversal has been + // completed for a node. In that case, srcS is the node depth-first + // traversal has been completed for, and dst will be set to null. private static class Link { - public NodeS srcS; - public Node dst; + public final NodeS srcS; + public final Node dst; + // Constructor for a reference from srcS to dst. public Link(NodeS srcS, Node dst) { this.srcS = srcS; this.dst = dst; } + + // Constructor for a marker indicating depth-first traversal has been + // completed for srcS. + public Link(NodeS srcS) { + this.srcS = srcS; + this.dst = null; + } } /** @@ -158,16 +247,10 @@ public class DominatorsComputation { public static void computeDominators(Node root) { long id = 0; - // List of all nodes seen. We keep track of this here to update all the - // dominators once we are done. - List nodes = new ArrayList(); - - // The set of nodes N such that N.domid < N.domS.id. These nodes need - // to be revisisted because their dominator is clearly wrong. + // The set of nodes xS such that xS.revisit != null. // Use a Queue instead of a Set because performance will be better. We - // avoid adding nodes already on the queue by checking whether it was - // already true that N.domid < N.domS.id, in which case the node is - // already on the queue. + // avoid adding nodes already on the queue by checking + // xS == null before adding the node to the queue. Queue revisit = new ArrayDeque(); // Set up the root node specially. @@ -176,107 +259,166 @@ public class DominatorsComputation { rootS.id = id++; root.setDominatorsComputationState(rootS); - // 1. Do a depth first search of the nodes, label them with ids and come - // up with intial candidate dominators for them. Deque dfs = new ArrayDeque(); + dfs.push(new Link(rootS)); for (Node child : root.getReferencesForDominators()) { dfs.push(new Link(rootS, child)); } + // 1. Do a depth first search of the nodes, label them with ids and come + // up with initial candidate dominators for them. while (!dfs.isEmpty()) { Link link = dfs.pop(); - NodeS dstS = (NodeS)link.dst.getDominatorsComputationState(); - if (dstS == null) { - // This is the first time we have seen the node. The candidate - // dominator is link src. - dstS = new NodeS(); - dstS.node = link.dst; - dstS.id = id++; - dstS.domid = link.srcS.id; - dstS.domS = link.srcS; - dstS.srcS = link.srcS; - dstS.seenid = dstS.domid; - nodes.add(dstS); - link.dst.setDominatorsComputationState(dstS); - - for (Node child : link.dst.getReferencesForDominators()) { - dfs.push(new Link(dstS, child)); - } + + if (link.dst == null) { + // This is the marker link indicating we have now visited all + // nodes reachable from link.srcS. + link.srcS.maxReachableId = id - 1; } else { - // We have seen the node already. Update the state based on the new - // potential dominator. - NodeS srcS = link.srcS; - boolean revisiting = dstS.domid < dstS.domS.id; - - while (srcS.id > dstS.seenid) { - srcS.undom.add(dstS); - srcS = srcS.srcS; - } - dstS.seenid = link.srcS.id; - - if (srcS.id < dstS.domid) { - // In this case, dstS.domid must be wrong, because we just found a - // path to dstS that does not go through dstS.domid: - // All nodes from root to srcS have id < domid, and all nodes from - // srcS to dstS had id > domid, so dstS.domid cannot be on this path - // from root to dstS. - dstS.domid = srcS.id; - if (!revisiting) { - revisit.add(dstS); + NodeS dstS = (NodeS)link.dst.getDominatorsComputationState(); + if (dstS == null) { + // We are seeing the destination node for the first time. + // The candidate dominator is the source node. + dstS = new NodeS(); + link.dst.setDominatorsComputationState(dstS); + + dstS.node = link.dst; + dstS.id = id++; + dstS.inRefIds.add(link.srcS.id); + dstS.domS = link.srcS; + dstS.domS.dominated.add(dstS); + dstS.oldDomS = link.srcS; + + dfs.push(new Link(dstS)); + for (Node child : link.dst.getReferencesForDominators()) { + dfs.push(new Link(dstS, child)); + } + } else { + // We have seen the destination node before. Update the state based + // on the new potential dominator. + long seenid = dstS.inRefIds.last(); + dstS.inRefIds.add(link.srcS.id); + + // Go up the dominator chain until we reach a node we haven't already + // seen with a path to dstS. + NodeS xS = link.srcS; + while (xS.id > seenid) { + xS = xS.domS; + } + + // The new dominator for dstS must have an id less than the node we + // just reached. Pull the dominator for dstS up its dominator + // chain until we find a suitable new dominator for dstS. + long domid = xS.id; + if (dstS.domS.id > domid) { + // Mark the node as needing to be revisited. + if (dstS.domS == dstS.oldDomS) { + if (dstS.oldDomS.revisit == null) { + dstS.oldDomS.revisit = new NodeSet(); + revisit.add(dstS.oldDomS); + } + dstS.oldDomS.revisit.add(dstS); + } + + // Update the node's candidate dominator. + dstS.domS.dominated.remove(dstS); + do { + dstS.domS = dstS.domS.domS; + } while (dstS.domS.id > domid); + dstS.domS.dominated.add(dstS); } } } } - // 2. Continue revisiting nodes until they all satisfy the requirement - // that domS.id <= domid. + // 2. Continue revisiting nodes until every node satisfies the requirement + // that domS.id == oldDomS.id. while (!revisit.isEmpty()) { - NodeS nodeS = revisit.poll(); - NodeS domS = nodeS.domS; - assert nodeS.domid < domS.id; - while (domS.id > nodeS.domid) { - if (domS.domS.id < nodeS.domid) { - // In this case, nodeS.domid must be wrong, because there is a path - // from root to nodeS that does not go through nodeS.domid: - // * We can go from root to domS without going through nodeS.domid, - // because otherwise nodeS.domid would dominate domS, not - // domS.domS. - // * We can go from domS to nodeS without going through nodeS.domid - // because we know nodeS is reachable from domS on a path of nodes - // with increases ids, which cannot include nodeS.domid, which - // has a smaller id than domS. - nodeS.domid = domS.domS.id; + NodeS oldDomS = revisit.poll(); + assert oldDomS.revisit != null; + + NodeSet nodes = oldDomS.revisit; + oldDomS.revisit = null; + + // Search for pairs of nodes nodeS, xS for which + // nodeS.id > xS.domS.id > nodeS.oldDomS.id + // and there is a path of nodes with increasing ids from nodeS to xS. + // In that case, xS.domS must be wrong, because there is a path to xS + // from the root that does not go through xS.domS: + // * There is a path from the root to nodeS.oldDomS that doesn't go + // through xS.domS. Otherwise xS.domS would be a dominator of + // nodeS.oldDomS, but it can't be because xS.domS.id > nodeS.oldDomS.id. + // * There is a path from nodeS.oldDomS to nodeS that doesn't go through + // xS.domS, because xS.domS is not a dominator of nodeS. + // * There is a path from nodeS to xS that doesn't go through xS.domS, + // because we have a path of increasing ids from nodeS to xS, none of + // which can have an id smaller than nodeS as xS.domS does. + for (int i = 0; i < oldDomS.dominated.size; ++i) { + NodeS xS = oldDomS.dominated.nodes[i]; + for (int j = 0; j < nodes.size; ++j) { + NodeS nodeS = nodes.nodes[j]; + assert nodeS.oldDomS == oldDomS; + if (isReachableAscending(nodeS, xS)) { + // Update the dominator for xS. + if (xS.domS == xS.oldDomS) { + if (xS.oldDomS.revisit == null) { + xS.oldDomS.revisit = new NodeSet(); + revisit.add(xS.oldDomS); + } + xS.oldDomS.revisit.add(xS); + } + oldDomS.dominated.remove(i--); + xS.domS = nodeS.domS; + xS.domS.dominated.add(xS); + break; + } } - domS.undom.add(nodeS); - domS = domS.srcS; } - nodeS.domS = domS; - nodeS.domid = domS.id; - - for (NodeS xS : nodeS.undom) { - if (domS.id < xS.domid) { - // In this case, xS.domid must be wrong, because there is a path - // from the root to xX that does not go through xS.domid: - // * We can go from root to nodeS without going through xS.domid, - // because otherwise xS.domid would dominate nodeS, not domS. - // * We can go from nodeS to xS without going through xS.domid - // because we know xS is reachable from nodeS on a path of nodes - // with increasing ids, which cannot include xS.domid, which has - // a smaller id than nodeS. - boolean revisiting = xS.domid < xS.domS.id; - xS.domid = domS.id; - if (!revisiting) { - revisit.add(xS); + + // We can now safely update oldDomS for each of the nodes nodeS while + // preserving the oldDomS invariant. + for (int i = 0; i < nodes.size; ++i) { + NodeS nodeS = nodes.nodes[i]; + nodeS.oldDomS = oldDomS.oldDomS; + if (nodeS.oldDomS != nodeS.domS) { + if (nodeS.oldDomS.revisit == null) { + nodeS.oldDomS.revisit = new NodeSet(); + revisit.add(nodeS.oldDomS); } + nodeS.oldDomS.revisit.add(nodeS); } } } - // 3. Update the dominators of the nodes. - root.setDominatorsComputationState(null); - for (NodeS nodeS : nodes) { - nodeS.node.setDominator(nodeS.domS.node); + // 3. We have figured out the correct dominator for each node. Notify the + // user of the results by doing one last traversal of the nodes. + assert revisit.isEmpty(); + revisit.add(rootS); + while (!revisit.isEmpty()) { + NodeS nodeS = revisit.poll(); + assert nodeS.domS == nodeS.oldDomS; + assert nodeS.revisit == null; nodeS.node.setDominatorsComputationState(null); + for (int i = 0; i < nodeS.dominated.size; ++i) { + NodeS xS = nodeS.dominated.nodes[i]; + xS.node.setDominator(nodeS.node); + revisit.add(xS); + } + } + } + + // Returns true if there is a path from srcS to dstS of nodes with ascending + // ids (not including dstS.id). + private static boolean isReachableAscending(NodeS srcS, NodeS dstS) { + if (dstS.id < srcS.id) { + // The first time we saw dstS was before we saw srcS. See if srcS is on + // the source chain for any nodes with direct references to dstS. + return dstS.inRefIds.hasIdInRange(srcS.id, srcS.maxReachableId); } + + // Otherwise dstS is only reachable from srcS on a node with ascending ids + // if it was visited for the first time while performing the depth-first + // traversal of srcS. + return dstS.id <= srcS.maxReachableId; } } diff --git a/tools/ahat/src/test/com/android/ahat/DominatorsTest.java b/tools/ahat/src/test/com/android/ahat/DominatorsTest.java index 0424e10dc8..d9af363659 100644 --- a/tools/ahat/src/test/com/android/ahat/DominatorsTest.java +++ b/tools/ahat/src/test/com/android/ahat/DominatorsTest.java @@ -295,4 +295,35 @@ public class DominatorsTest { assertEquals(p, d.dominator); assertEquals(p, e.dominator); } + + @Test + public void twiceRevisit() { + // /---->---\ + // / /--> f -->-\ + // --> a --> b -->--x---> c --> d + // \----------->----/ + // A regression test for a bug where we failed to ever revisit a node more + // than once. The node c is revisited a first time to bring its dominator + // up to b. c needs to be revisited again after the dominator for f is + // pulled up to a, and that revisit of c is necessary to ensure the + // dominator for d is pulled up to a. + Node a = new Node("a"); + Node b = new Node("b"); + Node x = new Node("x"); + Node c = new Node("c"); + Node d = new Node("d"); + Node f = new Node("f"); + a.depends = Arrays.asList(f, b); + b.depends = Arrays.asList(f, d, x); + x.depends = Arrays.asList(c); + c.depends = Arrays.asList(d); + f.depends = Arrays.asList(c); + + a.computeDominators(); + assertEquals(a, b.dominator); + assertEquals(b, x.dominator); + assertEquals(a, c.dominator); + assertEquals(a, d.dominator); + assertEquals(a, f.dominator); + } } -- GitLab From 6f17b57948e04486b145b5cdf7325bd87f5e03f8 Mon Sep 17 00:00:00 2001 From: Tamas Kenez Date: Tue, 12 Jun 2018 15:05:19 +0200 Subject: [PATCH 568/749] ART-tests: Remove DX-dependency from 618-checker-induction. In some test cases D8 generates PHI nodes in different order than DX. This CL updates the numbering of PHI nodes in the CHECK conditions in those cases and enables D8. Test: art/test.py -r -b --host -t 618-checker-induction Tested locally with --target/--gcstress. Bug: 65168732 Change-Id: Ibef8ae37a7924aa12ac496ab5db5d8088e94617a --- test/618-checker-induction/build | 20 -------------------- test/618-checker-induction/src/Main.java | 20 ++++++++++---------- 2 files changed, 10 insertions(+), 30 deletions(-) delete mode 100644 test/618-checker-induction/build diff --git a/test/618-checker-induction/build b/test/618-checker-induction/build deleted file mode 100644 index 10ffcc537d..0000000000 --- a/test/618-checker-induction/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/618-checker-induction/src/Main.java b/test/618-checker-induction/src/Main.java index 0080ffa464..1460725e10 100644 --- a/test/618-checker-induction/src/Main.java +++ b/test/618-checker-induction/src/Main.java @@ -290,7 +290,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:<> /// CHECK-DAG: <> Phi loop:<> outer_loop:<> - /// CHECK-DAG: Return [<>] loop:none + /// CHECK-DAG: Return [<>] loop:none // /// CHECK-START: int Main.closedFormNested() loop_optimization (after) /// CHECK-NOT: Phi @@ -313,7 +313,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:<> /// CHECK-DAG: <> Phi loop:<> outer_loop:<> - /// CHECK-DAG: Return [<>] loop:none + /// CHECK-DAG: Return [<>] loop:none // /// CHECK-START: int Main.closedFormNestedAlt() loop_optimization (after) /// CHECK-NOT: Phi @@ -411,7 +411,7 @@ public class Main { /// CHECK-START: int Main.periodicReturned9() loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: Return [<>] loop:none + /// CHECK-DAG: Return [<>] loop:none // /// CHECK-START: int Main.periodicReturned9() loop_optimization (after) /// CHECK-NOT: Phi @@ -430,7 +430,7 @@ public class Main { /// CHECK-START: int Main.periodicReturned10() loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: Return [<>] loop:none + /// CHECK-DAG: Return [<>] loop:none // /// CHECK-START: int Main.periodicReturned10() loop_optimization (after) /// CHECK-NOT: Phi @@ -450,7 +450,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: Return [<>] loop:none + /// CHECK-DAG: Return [<>] loop:none // /// CHECK-START: int Main.getSum21() loop_optimization (after) /// CHECK-NOT: Phi @@ -505,7 +505,7 @@ public class Main { /// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: Return [<>] loop:none + /// CHECK-DAG: Return [<>] loop:none // /// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (after) /// CHECK-NOT: Phi @@ -547,7 +547,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: Return [<>] loop:none + /// CHECK-DAG: Return [<>] loop:none /// CHECK-EVAL: "<>" != "<>" // /// CHECK-START: int Main.closedFeed() loop_optimization (after) @@ -691,7 +691,7 @@ public class Main { /// CHECK-START: boolean Main.periodicBoolIdiom1N(boolean, int) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: Return [<>] loop:none + /// CHECK-DAG: Return [<>] loop:none // /// CHECK-START: boolean Main.periodicBoolIdiom1N(boolean, int) loop_optimization (after) /// CHECK-NOT: Phi @@ -705,7 +705,7 @@ public class Main { /// CHECK-START: boolean Main.periodicBoolIdiom2N(boolean, int) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: Return [<>] loop:none + /// CHECK-DAG: Return [<>] loop:none // /// CHECK-START: boolean Main.periodicBoolIdiom2N(boolean, int) loop_optimization (after) /// CHECK-NOT: Phi @@ -719,7 +719,7 @@ public class Main { /// CHECK-START: boolean Main.periodicBoolIdiom3N(boolean, int) loop_optimization (before) /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: Return [<>] loop:none + /// CHECK-DAG: Return [<>] loop:none // /// CHECK-START: boolean Main.periodicBoolIdiom3N(boolean, int) loop_optimization (after) /// CHECK-NOT: Phi -- GitLab From 6fa4404f501cf0fb5a87074baec9673cedde25d4 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 19 Mar 2018 18:42:49 +0000 Subject: [PATCH 569/749] Simplify const-string.indexOf(). Simplify String.indexOf() called on empty or single-char constant string. We see these patterns in inlined java.net.URI$Parser.scan(int, int, String, String) called with constant strings from other URI$Parser methods. The empty string simplification allows constant folding and DCE to remove some of the code. The single-character string simplification avoids entrypoint call overhead. Test: New tests in 458-checker-instruct-simplification Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Change-Id: I4d3c997a8d6220202d481bbf8cbf280832c27cd7 --- compiler/optimizing/instruction_simplifier.cc | 42 ++++++++++ .../src/Main.java | 84 +++++++++++++++++++ 2 files changed, 126 insertions(+) diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index d532eeeb52..c979a5a56c 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -117,6 +117,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void SimplifyFP2Int(HInvoke* invoke); void SimplifyStringCharAt(HInvoke* invoke); void SimplifyStringIsEmptyOrLength(HInvoke* invoke); + void SimplifyStringIndexOf(HInvoke* invoke); void SimplifyNPEOnArgN(HInvoke* invoke, size_t); void SimplifyReturnThis(HInvoke* invoke); void SimplifyAllocationIntrinsic(HInvoke* invoke); @@ -2417,6 +2418,43 @@ void InstructionSimplifierVisitor::SimplifyStringIsEmptyOrLength(HInvoke* invoke invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, replacement); } +void InstructionSimplifierVisitor::SimplifyStringIndexOf(HInvoke* invoke) { + DCHECK(invoke->GetIntrinsic() == Intrinsics::kStringIndexOf || + invoke->GetIntrinsic() == Intrinsics::kStringIndexOfAfter); + if (invoke->InputAt(0)->IsLoadString()) { + HLoadString* load_string = invoke->InputAt(0)->AsLoadString(); + const DexFile& dex_file = load_string->GetDexFile(); + uint32_t utf16_length; + const char* data = + dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), &utf16_length); + if (utf16_length == 0) { + invoke->ReplaceWith(GetGraph()->GetIntConstant(-1)); + invoke->GetBlock()->RemoveInstruction(invoke); + RecordSimplification(); + return; + } + if (utf16_length == 1 && invoke->GetIntrinsic() == Intrinsics::kStringIndexOf) { + // Simplify to HSelect(HEquals(., load_string.charAt(0)), 0, -1). + // If the sought character is supplementary, this gives the correct result, i.e. -1. + uint32_t c = GetUtf16FromUtf8(&data); + DCHECK_EQ(GetTrailingUtf16Char(c), 0u); + DCHECK_EQ(GetLeadingUtf16Char(c), c); + uint32_t dex_pc = invoke->GetDexPc(); + ArenaAllocator* allocator = GetGraph()->GetAllocator(); + HEqual* equal = + new (allocator) HEqual(invoke->InputAt(1), GetGraph()->GetIntConstant(c), dex_pc); + invoke->GetBlock()->InsertInstructionBefore(equal, invoke); + HSelect* result = new (allocator) HSelect(equal, + GetGraph()->GetIntConstant(0), + GetGraph()->GetIntConstant(-1), + dex_pc); + invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, result); + RecordSimplification(); + return; + } + } +} + // This method should only be used on intrinsics whose sole way of throwing an // exception is raising a NPE when the nth argument is null. If that argument // is provably non-null, we can clear the flag. @@ -2554,6 +2592,10 @@ void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { case Intrinsics::kStringLength: SimplifyStringIsEmptyOrLength(instruction); break; + case Intrinsics::kStringIndexOf: + case Intrinsics::kStringIndexOfAfter: + SimplifyStringIndexOf(instruction); + break; case Intrinsics::kStringStringIndexOf: case Intrinsics::kStringStringIndexOfAfter: SimplifyNPEOnArgN(instruction, 1); // 0th has own NullCheck diff --git a/test/458-checker-instruct-simplification/src/Main.java b/test/458-checker-instruct-simplification/src/Main.java index 40e3778109..9e714f5111 100644 --- a/test/458-checker-instruct-simplification/src/Main.java +++ b/test/458-checker-instruct-simplification/src/Main.java @@ -2458,6 +2458,77 @@ public class Main { return (byte)((int)(((long)(b & 0xff)) & 255L)); } + /// CHECK-START: int Main.$noinline$emptyStringIndexOf(int) instruction_simplifier (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> LoadString + /// CHECK-DAG: <> InvokeVirtual [<>,<>] intrinsic:StringIndexOf + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.$noinline$emptyStringIndexOf(int) instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual + + /// CHECK-START: int Main.$noinline$emptyStringIndexOf(int) instruction_simplifier (after) + /// CHECK-DAG: <> IntConstant -1 + /// CHECK-DAG: Return [<>] + public static int $noinline$emptyStringIndexOf(int ch) { + return "".indexOf(ch); + } + + /// CHECK-START: int Main.$noinline$emptyStringIndexOfAfter(int, int) instruction_simplifier (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> LoadString + /// CHECK-DAG: <> InvokeVirtual [<>,<>,<>] intrinsic:StringIndexOfAfter + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.$noinline$emptyStringIndexOfAfter(int, int) instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual + + /// CHECK-START: int Main.$noinline$emptyStringIndexOfAfter(int, int) instruction_simplifier (after) + /// CHECK-DAG: <> IntConstant -1 + /// CHECK-DAG: Return [<>] + public static int $noinline$emptyStringIndexOfAfter(int ch, int fromIndex) { + return "".indexOf(ch, fromIndex); + } + + /// CHECK-START: int Main.$noinline$singleCharStringIndexOf(int) instruction_simplifier (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> LoadString + /// CHECK-DAG: <> InvokeVirtual [<>,<>] intrinsic:StringIndexOf + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.$noinline$singleCharStringIndexOf(int) instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual + + /// CHECK-START: int Main.$noinline$singleCharStringIndexOf(int) instruction_simplifier (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 120 + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant -1 + /// CHECK-DAG: <> Equal [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<